diff options
author | The Android Open Source Project <initial-contribution@android.com> | 2009-03-03 18:28:41 -0800 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2009-03-03 18:28:41 -0800 |
commit | 648161bb0edfc3d43db63caed5cc5213bc6cb78f (patch) | |
tree | 4b825dc642cb6eb9a060e54bf8d69288fbee4904 /WebCore/editing | |
parent | a65af38181ac7d34544586bdb5cd004de93897ad (diff) | |
download | external_webkit-648161bb0edfc3d43db63caed5cc5213bc6cb78f.zip external_webkit-648161bb0edfc3d43db63caed5cc5213bc6cb78f.tar.gz external_webkit-648161bb0edfc3d43db63caed5cc5213bc6cb78f.tar.bz2 |
auto import from //depot/cupcake/@135843
Diffstat (limited to 'WebCore/editing')
103 files changed, 0 insertions, 23492 deletions
diff --git a/WebCore/editing/AppendNodeCommand.cpp b/WebCore/editing/AppendNodeCommand.cpp deleted file mode 100644 index 6641877..0000000 --- a/WebCore/editing/AppendNodeCommand.cpp +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (C) 2005, 2006, 2008 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 "AppendNodeCommand.h" -#include "htmlediting.h" - -namespace WebCore { - -AppendNodeCommand::AppendNodeCommand(PassRefPtr<Node> parentNode, PassRefPtr<Node> childToAppend) - : SimpleEditCommand(parentNode->document()), m_parentNode(parentNode), m_childToAppend(childToAppend) -{ - ASSERT(m_childToAppend); - ASSERT(m_parentNode); -} - -void AppendNodeCommand::doApply() -{ - ASSERT(m_childToAppend); - ASSERT(m_parentNode); - // If the child to append is already in a tree, appending it will remove it from it's old location - // in an non-undoable way. We might eventually find it useful to do an undoable remove in this case. - ASSERT(!m_childToAppend->parent()); - ASSERT(enclosingNodeOfType(Position(m_parentNode.get(), 0), &isContentEditable) || !m_parentNode->attached()); - - ExceptionCode ec = 0; - m_parentNode->appendChild(m_childToAppend.get(), ec); - ASSERT(ec == 0); -} - -void AppendNodeCommand::doUnapply() -{ - ASSERT(m_childToAppend); - ASSERT(m_parentNode); - - ExceptionCode ec = 0; - m_parentNode->removeChild(m_childToAppend.get(), ec); - ASSERT(ec == 0); -} - -} // namespace WebCore diff --git a/WebCore/editing/AppendNodeCommand.h b/WebCore/editing/AppendNodeCommand.h deleted file mode 100644 index 3210512..0000000 --- a/WebCore/editing/AppendNodeCommand.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (C) 2005, 2006, 2008 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 AppendNodeCommand_h -#define AppendNodeCommand_h - -#include "EditCommand.h" - -namespace WebCore { - -class AppendNodeCommand : public SimpleEditCommand { -public: - static PassRefPtr<AppendNodeCommand> create(PassRefPtr<Node> parentNode, PassRefPtr<Node> childToAppend) - { - return adoptRef(new AppendNodeCommand(parentNode, childToAppend)); - } - -private: - AppendNodeCommand(PassRefPtr<Node> parentNode, PassRefPtr<Node> childToAppend); - - virtual void doApply(); - virtual void doUnapply(); - - RefPtr<Node> m_parentNode; - RefPtr<Node> m_childToAppend; -}; - -} // namespace WebCore - -#endif // AppendNodeCommand_h diff --git a/WebCore/editing/ApplyStyleCommand.cpp b/WebCore/editing/ApplyStyleCommand.cpp deleted file mode 100644 index 887ae3c..0000000 --- a/WebCore/editing/ApplyStyleCommand.cpp +++ /dev/null @@ -1,1349 +0,0 @@ -/* - * Copyright (C) 2005, 2006 Apple Computer, 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 "ApplyStyleCommand.h" - -#include "CSSComputedStyleDeclaration.h" -#include "CSSParser.h" -#include "CSSProperty.h" -#include "CSSPropertyNames.h" -#include "Document.h" -#include "HTMLElement.h" -#include "HTMLInterchange.h" -#include "HTMLNames.h" -#include "NodeList.h" -#include "Range.h" -#include "RenderObject.h" -#include "Text.h" -#include "TextIterator.h" -#include "htmlediting.h" -#include "visible_units.h" - -namespace WebCore { - -using namespace HTMLNames; - -class StyleChange { -public: - enum ELegacyHTMLStyles { DoNotUseLegacyHTMLStyles, UseLegacyHTMLStyles }; - - explicit StyleChange(CSSStyleDeclaration *, ELegacyHTMLStyles usesLegacyStyles=UseLegacyHTMLStyles); - StyleChange(CSSStyleDeclaration *, const Position &, ELegacyHTMLStyles usesLegacyStyles=UseLegacyHTMLStyles); - - static ELegacyHTMLStyles styleModeForParseMode(bool); - - String cssStyle() const { return m_cssStyle; } - bool applyBold() const { return m_applyBold; } - bool applyItalic() const { return m_applyItalic; } - bool applyFontColor() const { return m_applyFontColor.length() > 0; } - bool applyFontFace() const { return m_applyFontFace.length() > 0; } - bool applyFontSize() const { return m_applyFontSize.length() > 0; } - - String fontColor() { return m_applyFontColor; } - String fontFace() { return m_applyFontFace; } - String fontSize() { return m_applyFontSize; } - - bool usesLegacyStyles() const { return m_usesLegacyStyles; } - -private: - void init(PassRefPtr<CSSStyleDeclaration>, const Position &); - bool checkForLegacyHTMLStyleChange(const CSSProperty *); - static bool currentlyHasStyle(const Position &, const CSSProperty *); - - String m_cssStyle; - bool m_applyBold; - bool m_applyItalic; - String m_applyFontColor; - String m_applyFontFace; - String m_applyFontSize; - bool m_usesLegacyStyles; -}; - - - -StyleChange::StyleChange(CSSStyleDeclaration *style, ELegacyHTMLStyles usesLegacyStyles) - : m_applyBold(false), m_applyItalic(false), m_usesLegacyStyles(usesLegacyStyles) -{ - init(style, Position()); -} - -StyleChange::StyleChange(CSSStyleDeclaration *style, const Position &position, ELegacyHTMLStyles usesLegacyStyles) - : m_applyBold(false), m_applyItalic(false), m_usesLegacyStyles(usesLegacyStyles) -{ - init(style, position); -} - -void StyleChange::init(PassRefPtr<CSSStyleDeclaration> style, const Position &position) -{ - RefPtr<CSSMutableStyleDeclaration> mutableStyle = style->makeMutable(); - - String styleText(""); - - DeprecatedValueListConstIterator<CSSProperty> end; - for (DeprecatedValueListConstIterator<CSSProperty> it = mutableStyle->valuesIterator(); it != end; ++it) { - const CSSProperty *property = &*it; - - // If position is empty or the position passed in already has the - // style, just move on. - if (position.isNotNull() && currentlyHasStyle(position, property)) - continue; - - // Changing the whitespace style in a tab span would collapse the tab into a space. - if (property->id() == CSSPropertyWhiteSpace && (isTabSpanTextNode(position.node()) || isTabSpanNode((position.node())))) - continue; - - // If needed, figure out if this change is a legacy HTML style change. - if (m_usesLegacyStyles && checkForLegacyHTMLStyleChange(property)) - continue; - - // Add this property - - if (property->id() == CSSPropertyWebkitTextDecorationsInEffect) { - // we have to special-case text decorations - CSSProperty alteredProperty = CSSProperty(CSSPropertyTextDecoration, property->value(), property->isImportant()); - styleText += alteredProperty.cssText(); - } else - styleText += property->cssText(); - } - - // Save the result for later - m_cssStyle = styleText.stripWhiteSpace(); -} - -StyleChange::ELegacyHTMLStyles StyleChange::styleModeForParseMode(bool isQuirksMode) -{ - return isQuirksMode ? UseLegacyHTMLStyles : DoNotUseLegacyHTMLStyles; -} - -bool StyleChange::checkForLegacyHTMLStyleChange(const CSSProperty *property) -{ - if (!property || !property->value()) { - return false; - } - - String valueText(property->value()->cssText()); - switch (property->id()) { - case CSSPropertyFontWeight: - if (equalIgnoringCase(valueText, "bold")) { - m_applyBold = true; - return true; - } - break; - case CSSPropertyFontStyle: - if (equalIgnoringCase(valueText, "italic") || equalIgnoringCase(valueText, "oblique")) { - m_applyItalic = true; - return true; - } - break; - case CSSPropertyColor: { - RGBA32 rgba = 0; - CSSParser::parseColor(rgba, valueText); - Color color(rgba); - m_applyFontColor = color.name(); - return true; - } - case CSSPropertyFontFamily: - m_applyFontFace = valueText; - return true; - case CSSPropertyFontSize: - if (property->value()->cssValueType() == CSSValue::CSS_PRIMITIVE_VALUE) { - CSSPrimitiveValue *value = static_cast<CSSPrimitiveValue *>(property->value()); - - if (value->primitiveType() < CSSPrimitiveValue::CSS_PX || value->primitiveType() > CSSPrimitiveValue::CSS_PC) - // Size keyword or relative unit. - return false; - - float number = value->getFloatValue(CSSPrimitiveValue::CSS_PX); - if (number <= 9) - m_applyFontSize = "1"; - else if (number <= 10) - m_applyFontSize = "2"; - else if (number <= 13) - m_applyFontSize = "3"; - else if (number <= 16) - m_applyFontSize = "4"; - else if (number <= 18) - m_applyFontSize = "5"; - else if (number <= 24) - m_applyFontSize = "6"; - else - m_applyFontSize = "7"; - // Huge quirk in Microsft Entourage is that they understand CSS font-size, but also write - // out legacy 1-7 values in font tags (I guess for mailers that are not CSS-savvy at all, - // like Eudora). Yes, they write out *both*. We need to write out both as well. Return false. - return false; - } - else { - // Can't make sense of the number. Put no font size. - return true; - } - } - return false; -} - -bool StyleChange::currentlyHasStyle(const Position &pos, const CSSProperty *property) -{ - ASSERT(pos.isNotNull()); - RefPtr<CSSComputedStyleDeclaration> style = pos.computedStyle(); - RefPtr<CSSValue> value = style->getPropertyCSSValue(property->id(), DoNotUpdateLayout); - if (!value) - return false; - return equalIgnoringCase(value->cssText(), property->value()->cssText()); -} - -static String &styleSpanClassString() -{ - static String styleSpanClassString = AppleStyleSpanClass; - return styleSpanClassString; -} - -bool isStyleSpan(const Node *node) -{ - if (!node || !node->isHTMLElement()) - return false; - - const HTMLElement *elem = static_cast<const HTMLElement *>(node); - return elem->hasLocalName(spanAttr) && elem->getAttribute(classAttr) == styleSpanClassString(); -} - -static bool isUnstyledStyleSpan(const Node *node) -{ - if (!node || !node->isHTMLElement() || !node->hasTagName(spanTag)) - return false; - - const HTMLElement *elem = static_cast<const HTMLElement *>(node); - CSSMutableStyleDeclaration *inlineStyleDecl = elem->inlineStyleDecl(); - return (!inlineStyleDecl || inlineStyleDecl->length() == 0) && elem->getAttribute(classAttr) == styleSpanClassString(); -} - -static bool isEmptyFontTag(const Node *node) -{ - if (!node || !node->hasTagName(fontTag)) - return false; - - const Element *elem = static_cast<const Element *>(node); - NamedAttrMap *map = elem->attributes(true); // true for read-only - return (!map || map->length() == 1) && elem->getAttribute(classAttr) == styleSpanClassString(); -} - -static PassRefPtr<Element> createFontElement(Document* document) -{ - ExceptionCode ec = 0; - RefPtr<Element> fontNode = document->createElementNS(xhtmlNamespaceURI, "font", ec); - ASSERT(ec == 0); - fontNode->setAttribute(classAttr, styleSpanClassString()); - return fontNode.release(); -} - -PassRefPtr<HTMLElement> createStyleSpanElement(Document* document) -{ - ExceptionCode ec = 0; - RefPtr<Element> styleElement = document->createElementNS(xhtmlNamespaceURI, "span", ec); - ASSERT(ec == 0); - styleElement->setAttribute(classAttr, styleSpanClassString()); - return static_pointer_cast<HTMLElement>(styleElement.release()); -} - -ApplyStyleCommand::ApplyStyleCommand(Document* document, CSSStyleDeclaration* style, EditAction editingAction, EPropertyLevel propertyLevel) - : CompositeEditCommand(document) - , m_style(style->makeMutable()) - , m_editingAction(editingAction) - , m_propertyLevel(propertyLevel) - , m_start(endingSelection().start().downstream()) - , m_end(endingSelection().end().upstream()) - , m_useEndingSelection(true) - , m_styledInlineElement(0) - , m_removeOnly(false) -{ -} - -ApplyStyleCommand::ApplyStyleCommand(Document* document, CSSStyleDeclaration* style, const Position& start, const Position& end, EditAction editingAction, EPropertyLevel propertyLevel) - : CompositeEditCommand(document) - , m_style(style->makeMutable()) - , m_editingAction(editingAction) - , m_propertyLevel(propertyLevel) - , m_start(start) - , m_end(end) - , m_useEndingSelection(false) - , m_styledInlineElement(0) - , m_removeOnly(false) -{ -} - -ApplyStyleCommand::ApplyStyleCommand(Element* element, bool removeOnly, EditAction editingAction) - : CompositeEditCommand(element->document()) - , m_style(CSSMutableStyleDeclaration::create()) - , m_editingAction(editingAction) - , m_propertyLevel(PropertyDefault) - , m_start(endingSelection().start().downstream()) - , m_end(endingSelection().end().upstream()) - , m_useEndingSelection(true) - , m_styledInlineElement(element) - , m_removeOnly(removeOnly) -{ -} - -void ApplyStyleCommand::updateStartEnd(const Position& newStart, const Position& newEnd) -{ - ASSERT(Range::compareBoundaryPoints(newEnd, newStart) >= 0); - - if (!m_useEndingSelection && (newStart != m_start || newEnd != m_end)) - m_useEndingSelection = true; - - setEndingSelection(Selection(newStart, newEnd, VP_DEFAULT_AFFINITY)); - m_start = newStart; - m_end = newEnd; -} - -Position ApplyStyleCommand::startPosition() -{ - if (m_useEndingSelection) - return endingSelection().start(); - - return m_start; -} - -Position ApplyStyleCommand::endPosition() -{ - if (m_useEndingSelection) - return endingSelection().end(); - - return m_end; -} - -void ApplyStyleCommand::doApply() -{ - switch (m_propertyLevel) { - case PropertyDefault: { - // apply the block-centric properties of the style - RefPtr<CSSMutableStyleDeclaration> blockStyle = m_style->copyBlockProperties(); - if (blockStyle->length()) - applyBlockStyle(blockStyle.get()); - // apply any remaining styles to the inline elements - // NOTE: hopefully, this string comparison is the same as checking for a non-null diff - if (blockStyle->length() < m_style->length() || m_styledInlineElement) { - RefPtr<CSSMutableStyleDeclaration> inlineStyle = m_style->copy(); - applyRelativeFontStyleChange(inlineStyle.get()); - blockStyle->diff(inlineStyle.get()); - applyInlineStyle(inlineStyle.get()); - } - break; - } - case ForceBlockProperties: - // Force all properties to be applied as block styles. - applyBlockStyle(m_style.get()); - break; - } -} - -EditAction ApplyStyleCommand::editingAction() const -{ - return m_editingAction; -} - -void ApplyStyleCommand::applyBlockStyle(CSSMutableStyleDeclaration *style) -{ - // 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 - updateLayout(); - - // get positions we want to use for applying style - Position start = startPosition(); - Position end = endPosition(); - if (Range::compareBoundaryPoints(end, start) < 0) { - Position swap = start; - start = end; - end = swap; - } - - VisiblePosition visibleStart(start); - VisiblePosition visibleEnd(end); - // 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()); - Position rangeStart(scope, 0); - RefPtr<Range> startRange = Range::create(document(), rangeStart, rangeCompliantEquivalent(visibleStart.deepEquivalent())); - RefPtr<Range> endRange = Range::create(document(), rangeStart, rangeCompliantEquivalent(visibleEnd.deepEquivalent())); - int startIndex = TextIterator::rangeLength(startRange.get(), true); - int endIndex = TextIterator::rangeLength(endRange.get(), true); - - VisiblePosition paragraphStart(startOfParagraph(visibleStart)); - VisiblePosition nextParagraphStart(endOfParagraph(paragraphStart).next()); - VisiblePosition beyondEnd(endOfParagraph(visibleEnd).next()); - while (paragraphStart.isNotNull() && paragraphStart != beyondEnd) { - StyleChange styleChange(style, paragraphStart.deepEquivalent(), StyleChange::styleModeForParseMode(document()->inCompatMode())); - if (styleChange.cssStyle().length() > 0 || m_removeOnly) { - RefPtr<Node> block = enclosingBlock(paragraphStart.deepEquivalent().node()); - RefPtr<Node> newBlock = moveParagraphContentsToNewBlockIfNecessary(paragraphStart.deepEquivalent()); - if (newBlock) - block = newBlock; - ASSERT(block->isHTMLElement()); - if (block->isHTMLElement()) { - removeCSSStyle(style, static_cast<HTMLElement*>(block.get())); - if (!m_removeOnly) - addBlockStyle(styleChange, static_cast<HTMLElement*>(block.get())); - } - } - paragraphStart = nextParagraphStart; - nextParagraphStart = endOfParagraph(paragraphStart).next(); - } - - startRange = TextIterator::rangeFromLocationAndLength(static_cast<Element*>(scope), startIndex, 0, true); - endRange = TextIterator::rangeFromLocationAndLength(static_cast<Element*>(scope), endIndex, 0, true); - if (startRange && endRange) - updateStartEnd(startRange->startPosition(), endRange->startPosition()); -} - -#define NoFontDelta (0.0f) -#define MinimumFontSize (0.1f) - -void ApplyStyleCommand::applyRelativeFontStyleChange(CSSMutableStyleDeclaration *style) -{ - RefPtr<CSSValue> value = style->getPropertyCSSValue(CSSPropertyFontSize); - if (value) { - // Explicit font size overrides any delta. - style->removeProperty(CSSPropertyWebkitFontSizeDelta); - return; - } - - // Get the adjustment amount out of the style. - value = style->getPropertyCSSValue(CSSPropertyWebkitFontSizeDelta); - if (!value) - return; - float adjustment = NoFontDelta; - if (value->cssValueType() == CSSValue::CSS_PRIMITIVE_VALUE) { - CSSPrimitiveValue *primitiveValue = static_cast<CSSPrimitiveValue *>(value.get()); - if (primitiveValue->primitiveType() == CSSPrimitiveValue::CSS_PX) { - // Only PX handled now. If we handle more types in the future, perhaps - // a switch statement here would be more appropriate. - adjustment = primitiveValue->getFloatValue(); - } - } - style->removeProperty(CSSPropertyWebkitFontSizeDelta); - if (adjustment == NoFontDelta) - return; - - Position start = startPosition(); - Position end = endPosition(); - if (Range::compareBoundaryPoints(end, start) < 0) { - Position swap = start; - start = end; - end = swap; - } - - // Join up any adjacent text nodes. - if (start.node()->isTextNode()) { - joinChildTextNodes(start.node()->parentNode(), start, end); - start = startPosition(); - end = endPosition(); - } - if (end.node()->isTextNode() && start.node()->parentNode() != end.node()->parentNode()) { - joinChildTextNodes(end.node()->parentNode(), start, end); - start = startPosition(); - end = endPosition(); - } - - // Split the start text nodes if needed to apply style. - bool splitStart = splitTextAtStartIfNeeded(start, end); - if (splitStart) { - start = startPosition(); - end = endPosition(); - } - bool splitEnd = splitTextAtEndIfNeeded(start, end); - if (splitEnd) { - start = startPosition(); - end = endPosition(); - } - - // Calculate loop end point. - // 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(); - else - beyondEnd = end.node()->traverseNextNode(); - - start = start.upstream(); // Move upstream to ensure we do not add redundant spans. - Node *startNode = start.node(); - if (startNode->isTextNode() && start.offset() >= caretMaxOffset(startNode)) // Move out of text node if range does not include its characters. - startNode = startNode->traverseNextNode(); - - // Store away font size before making any changes to the document. - // This ensures that changes to one node won't effect another. - HashMap<Node*, float> startingFontSizes; - for (Node *node = startNode; node != beyondEnd; node = node->traverseNextNode()) - startingFontSizes.set(node, computedFontSize(node)); - - // These spans were added by us. If empty after font size changes, they can be removed. - DeprecatedPtrList<Node> unstyledSpans; - - Node *lastStyledNode = 0; - for (Node *node = startNode; node != beyondEnd; node = node->traverseNextNode()) { - HTMLElement *elem = 0; - if (node->isHTMLElement()) { - // Only work on fully selected nodes. - if (!nodeFullySelected(node, start, end)) - continue; - elem = static_cast<HTMLElement *>(node); - } else if (node->isTextNode() && node->renderer() && node->parentNode() != lastStyledNode) { - // Last styled node was not parent node of this text node, but we wish to style this - // text node. To make this possible, add a style span to surround this text node. - RefPtr<HTMLElement> span = createStyleSpanElement(document()); - insertNodeBefore(span.get(), node); - surroundNodeRangeWithElement(node, node, span.get()); - elem = span.get(); - } else { - // Only handle HTML elements and text nodes. - continue; - } - lastStyledNode = node; - - CSSMutableStyleDeclaration* inlineStyleDecl = elem->getInlineStyleDecl(); - float currentFontSize = computedFontSize(node); - float desiredFontSize = max(MinimumFontSize, startingFontSizes.get(node) + adjustment); - RefPtr<CSSValue> value = inlineStyleDecl->getPropertyCSSValue(CSSPropertyFontSize); - if (value) { - inlineStyleDecl->removeProperty(CSSPropertyFontSize, true); - currentFontSize = computedFontSize(node); - } - if (currentFontSize != desiredFontSize) { - inlineStyleDecl->setProperty(CSSPropertyFontSize, String::number(desiredFontSize) + "px", false, false); - setNodeAttribute(elem, styleAttr, inlineStyleDecl->cssText()); - } - if (inlineStyleDecl->length() == 0) { - removeNodeAttribute(elem, styleAttr); - if (isUnstyledStyleSpan(elem)) - unstyledSpans.append(elem); - } - } - - for (DeprecatedPtrListIterator<Node> it(unstyledSpans); it.current(); ++it) - removeNodePreservingChildren(it.current()); -} - -#undef NoFontDelta -#undef MinimumFontSize - -static Node* dummySpanAncestorForNode(const Node* node) -{ - while (node && !isStyleSpan(node)) - node = node->parent(); - - return node ? node->parent() : 0; -} - -void ApplyStyleCommand::cleanupUnstyledAppleStyleSpans(Node* dummySpanAncestor) -{ - if (!dummySpanAncestor) - return; - - // Dummy spans are created when text node is split, so that style information - // can be propagated, which can result in more splitting. If a dummy span gets - // cloned/split, the new node is always a sibling of it. Therefore, we scan - // all the children of the dummy's parent - Node* next; - for (Node* node = dummySpanAncestor->firstChild(); node; node = next) { - next = node->nextSibling(); - if (isUnstyledStyleSpan(node)) - removeNodePreservingChildren(node); - node = next; - } -} - -void ApplyStyleCommand::applyInlineStyle(CSSMutableStyleDeclaration *style) -{ - Node* startDummySpanAncestor = 0; - Node* endDummySpanAncestor = 0; - - // 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 - updateLayout(); - - // adjust to the positions we want to use for applying style - Position start = startPosition(); - Position end = endPosition(); - if (Range::compareBoundaryPoints(end, start) < 0) { - Position swap = start; - start = end; - end = swap; - } - - // split the start node and containing element if the selection starts inside of it - bool splitStart = splitTextElementAtStartIfNeeded(start, end); - if (splitStart) { - start = startPosition(); - end = endPosition(); - startDummySpanAncestor = dummySpanAncestorForNode(start.node()); - } - - // split the end node and containing element if the selection ends inside of it - bool splitEnd = splitTextElementAtEndIfNeeded(start, end); - if (splitEnd) { - start = startPosition(); - end = endPosition(); - endDummySpanAncestor = dummySpanAncestorForNode(end.node()); - } - - // Remove style from the selection. - // Use the upstream position of the start for removing style. - // This will ensure we remove all traces of the relevant styles from the selection - // and prevent us from adding redundant ones, as described in: - // <rdar://problem/3724344> Bolding and unbolding creates extraneous tags - removeInlineStyle(style, start.upstream(), end); - start = startPosition(); - end = endPosition(); - - if (splitStart) { - bool mergedStart = mergeStartWithPreviousIfIdentical(start, end); - if (mergedStart) { - start = startPosition(); - end = endPosition(); - } - } - - if (splitEnd) { - mergeEndWithNextIfIdentical(start, end); - start = startPosition(); - end = endPosition(); - } - - // update document layout once before running the rest of the function - // so that we avoid the expense of updating before each and every call - // to check a computed style - updateLayout(); - - Node* node = start.node(); - - bool rangeIsEmpty = false; - - if (start.offset() >= caretMaxOffset(start.node())) { - node = node->traverseNextNode(); - Position newStart = Position(node, 0); - if (Range::compareBoundaryPoints(end, newStart) < 0) - rangeIsEmpty = true; - } - - if (!rangeIsEmpty) { - // 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)) - end = positionAfterNode(start.node()); - // Add the style to selected inline runs. - Node* pastLast = Range::create(document(), rangeCompliantEquivalent(start), rangeCompliantEquivalent(end))->pastLastNode(); - for (Node* next; node && node != pastLast; node = next) { - - next = node->traverseNextNode(); - - if (!node->renderer() || !node->isContentEditable()) - continue; - - if (!node->isContentRichlyEditable() && node->isHTMLElement()) { - // This is a plaintext-only region. Only proceed if it's fully selected. - if (end.node()->isDescendantOf(node)) - break; - // Add to this element's inline style and skip over its contents. - HTMLElement* element = static_cast<HTMLElement*>(node); - RefPtr<CSSMutableStyleDeclaration> inlineStyle = element->getInlineStyleDecl()->copy(); - inlineStyle->merge(style); - setNodeAttribute(element, styleAttr, inlineStyle->cssText()); - next = node->traverseNextSibling(); - continue; - } - - if (isBlock(node)) - continue; - - if (node->childNodeCount()) { - if (editingIgnoresContent(node)) { - next = node->traverseNextSibling(); - continue; - } - continue; - } - - Node* runStart = node; - // Find the end of the run. - Node* sibling = node->nextSibling(); - while (sibling && sibling != pastLast && (!sibling->isElementNode() || sibling->hasTagName(brTag)) && !isBlock(sibling)) { - node = sibling; - sibling = node->nextSibling(); - } - // Recompute next, since node has changed. - next = node->traverseNextNode(); - // Apply the style to the run. - addInlineStyleIfNeeded(style, runStart, node); - } - } - - // Remove dummy style spans created by splitting text elements. - cleanupUnstyledAppleStyleSpans(startDummySpanAncestor); - if (endDummySpanAncestor != startDummySpanAncestor) - cleanupUnstyledAppleStyleSpans(endDummySpanAncestor); -} - -bool ApplyStyleCommand::isHTMLStyleNode(CSSMutableStyleDeclaration *style, HTMLElement *elem) -{ - DeprecatedValueListConstIterator<CSSProperty> end; - for (DeprecatedValueListConstIterator<CSSProperty> it = style->valuesIterator(); it != end; ++it) { - switch ((*it).id()) { - case CSSPropertyFontWeight: - if (elem->hasLocalName(bTag)) - return true; - break; - case CSSPropertyFontStyle: - if (elem->hasLocalName(iTag)) - return true; - } - } - - return false; -} - -void ApplyStyleCommand::removeHTMLStyleNode(HTMLElement *elem) -{ - // This node can be removed. - // EDIT FIXME: This does not handle the case where the node - // has attributes. But how often do people add attributes to <B> tags? - // Not so often I think. - ASSERT(elem); - removeNodePreservingChildren(elem); -} - -void ApplyStyleCommand::removeHTMLFontStyle(CSSMutableStyleDeclaration *style, HTMLElement *elem) -{ - ASSERT(style); - ASSERT(elem); - - if (!elem->hasLocalName(fontTag)) - return; - - DeprecatedValueListConstIterator<CSSProperty> end; - for (DeprecatedValueListConstIterator<CSSProperty> it = style->valuesIterator(); it != end; ++it) { - switch ((*it).id()) { - case CSSPropertyColor: - removeNodeAttribute(elem, colorAttr); - break; - case CSSPropertyFontFamily: - removeNodeAttribute(elem, faceAttr); - break; - case CSSPropertyFontSize: - removeNodeAttribute(elem, sizeAttr); - break; - } - } - - if (isEmptyFontTag(elem)) - removeNodePreservingChildren(elem); -} - -void ApplyStyleCommand::removeCSSStyle(CSSMutableStyleDeclaration *style, HTMLElement *elem) -{ - ASSERT(style); - ASSERT(elem); - - CSSMutableStyleDeclaration *decl = elem->inlineStyleDecl(); - if (!decl) - return; - - DeprecatedValueListConstIterator<CSSProperty> end; - for (DeprecatedValueListConstIterator<CSSProperty> it = style->valuesIterator(); it != end; ++it) { - int propertyID = (*it).id(); - RefPtr<CSSValue> value = decl->getPropertyCSSValue(propertyID); - if (value && (propertyID != CSSPropertyWhiteSpace || !isTabSpanNode(elem))) - removeCSSProperty(decl, propertyID); - } - - if (isUnstyledStyleSpan(elem)) - removeNodePreservingChildren(elem); -} - -void ApplyStyleCommand::removeBlockStyle(CSSMutableStyleDeclaration *style, const Position &start, const Position &end) -{ - ASSERT(start.isNotNull()); - ASSERT(end.isNotNull()); - ASSERT(start.node()->inDocument()); - ASSERT(end.node()->inDocument()); - ASSERT(Range::compareBoundaryPoints(start, end) <= 0); - -} - -static bool hasTextDecorationProperty(Node *node) -{ - if (!node->isElementNode()) - return false; - - RefPtr<CSSValue> value = computedStyle(node)->getPropertyCSSValue(CSSPropertyTextDecoration, DoNotUpdateLayout); - return value && !equalIgnoringCase(value->cssText(), "none"); -} - -static Node* highestAncestorWithTextDecoration(Node *node) -{ - Node *result = NULL; - - for (Node *n = node; n; n = n->parentNode()) { - if (hasTextDecorationProperty(n)) - result = n; - } - - return result; -} - -PassRefPtr<CSSMutableStyleDeclaration> ApplyStyleCommand::extractTextDecorationStyle(Node* node) -{ - ASSERT(node); - ASSERT(node->isElementNode()); - - // non-html elements not handled yet - if (!node->isHTMLElement()) - return 0; - - HTMLElement *element = static_cast<HTMLElement *>(node); - RefPtr<CSSMutableStyleDeclaration> style = element->inlineStyleDecl(); - if (!style) - return 0; - - int properties[1] = { CSSPropertyTextDecoration }; - RefPtr<CSSMutableStyleDeclaration> textDecorationStyle = style->copyPropertiesInSet(properties, 1); - - RefPtr<CSSValue> property = style->getPropertyCSSValue(CSSPropertyTextDecoration); - if (property && !equalIgnoringCase(property->cssText(), "none")) - removeCSSProperty(style.get(), CSSPropertyTextDecoration); - - return textDecorationStyle.release(); -} - -PassRefPtr<CSSMutableStyleDeclaration> ApplyStyleCommand::extractAndNegateTextDecorationStyle(Node* node) -{ - ASSERT(node); - ASSERT(node->isElementNode()); - - // non-html elements not handled yet - if (!node->isHTMLElement()) - return 0; - - RefPtr<CSSComputedStyleDeclaration> nodeStyle = computedStyle(node); - ASSERT(nodeStyle); - - int properties[1] = { CSSPropertyTextDecoration }; - RefPtr<CSSMutableStyleDeclaration> textDecorationStyle = nodeStyle->copyPropertiesInSet(properties, 1); - - RefPtr<CSSValue> property = nodeStyle->getPropertyCSSValue(CSSPropertyTextDecoration); - if (property && !equalIgnoringCase(property->cssText(), "none")) { - RefPtr<CSSMutableStyleDeclaration> newStyle = textDecorationStyle->copy(); - newStyle->setProperty(CSSPropertyTextDecoration, "none"); - applyTextDecorationStyle(node, newStyle.get()); - } - - return textDecorationStyle.release(); -} - -void ApplyStyleCommand::applyTextDecorationStyle(Node *node, CSSMutableStyleDeclaration *style) -{ - ASSERT(node); - - if (!style || !style->cssText().length()) - return; - - if (node->isTextNode()) { - RefPtr<HTMLElement> styleSpan = createStyleSpanElement(document()); - insertNodeBefore(styleSpan.get(), node); - surroundNodeRangeWithElement(node, node, styleSpan.get()); - node = styleSpan.get(); - } - - if (!node->isElementNode()) - return; - - HTMLElement *element = static_cast<HTMLElement *>(node); - - StyleChange styleChange(style, Position(element, 0), StyleChange::styleModeForParseMode(document()->inCompatMode())); - if (styleChange.cssStyle().length() > 0) { - String cssText = styleChange.cssStyle(); - CSSMutableStyleDeclaration *decl = element->inlineStyleDecl(); - if (decl) - cssText += decl->cssText(); - setNodeAttribute(element, styleAttr, cssText); - } -} - -void ApplyStyleCommand::pushDownTextDecorationStyleAroundNode(Node *node, const Position &start, const Position &end, bool force) -{ - Node *highestAncestor = highestAncestorWithTextDecoration(node); - - if (highestAncestor) { - Node *nextCurrent; - Node *nextChild; - for (Node *current = highestAncestor; current != node; current = nextCurrent) { - ASSERT(current); - - nextCurrent = NULL; - - RefPtr<CSSMutableStyleDeclaration> decoration = force ? extractAndNegateTextDecorationStyle(current) : extractTextDecorationStyle(current); - - for (Node *child = current->firstChild(); child; child = nextChild) { - nextChild = child->nextSibling(); - - if (node == child) { - nextCurrent = child; - } else if (node->isDescendantOf(child)) { - applyTextDecorationStyle(child, decoration.get()); - nextCurrent = child; - } else { - applyTextDecorationStyle(child, decoration.get()); - } - } - } - } -} - -void ApplyStyleCommand::pushDownTextDecorationStyleAtBoundaries(const Position &start, const Position &end) -{ - // We need to work in two passes. First we push down any inline - // styles that set text decoration. Then we look for any remaining - // styles (caused by stylesheets) and explicitly negate text - // decoration while pushing down. - - pushDownTextDecorationStyleAroundNode(start.node(), start, end, false); - updateLayout(); - pushDownTextDecorationStyleAroundNode(start.node(), start, end, true); - - pushDownTextDecorationStyleAroundNode(end.node(), start, end, false); - updateLayout(); - pushDownTextDecorationStyleAroundNode(end.node(), start, end, true); -} - -static int maxRangeOffset(Node *n) -{ - if (n->offsetInCharacters()) - return n->maxCharacterOffset(); - - if (n->isElementNode()) - return n->childNodeCount(); - - return 1; -} - -void ApplyStyleCommand::removeInlineStyle(PassRefPtr<CSSMutableStyleDeclaration> style, const Position &start, const Position &end) -{ - ASSERT(start.isNotNull()); - ASSERT(end.isNotNull()); - ASSERT(start.node()->inDocument()); - ASSERT(end.node()->inDocument()); - ASSERT(Range::compareBoundaryPoints(start, end) <= 0); - - RefPtr<CSSValue> textDecorationSpecialProperty = style->getPropertyCSSValue(CSSPropertyWebkitTextDecorationsInEffect); - - if (textDecorationSpecialProperty) { - pushDownTextDecorationStyleAtBoundaries(start.downstream(), end.upstream()); - style = style->copy(); - style->setProperty(CSSPropertyTextDecoration, textDecorationSpecialProperty->cssText(), style->getPropertyPriority(CSSPropertyWebkitTextDecorationsInEffect)); - } - - // 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. - Position s = start; - Position e = end; - - Node *node = start.node(); - while (node) { - Node *next = node->traverseNextNode(); - if (node->isHTMLElement() && nodeFullySelected(node, start, end)) { - HTMLElement *elem = static_cast<HTMLElement *>(node); - Node *prev = elem->traversePreviousNodePostOrder(); - Node *next = elem->traverseNextNode(); - if (m_styledInlineElement && elem->hasTagName(m_styledInlineElement->tagQName())) - removeNodePreservingChildren(elem); - if (isHTMLStyleNode(style.get(), elem)) - removeHTMLStyleNode(elem); - else { - removeHTMLFontStyle(style.get(), elem); - removeCSSStyle(style.get(), elem); - } - if (!elem->inDocument()) { - if (s.node() == 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.offset() <= caretMinOffset(s.node())); - s = Position(next, 0); - } - if (e.node() == 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. - ASSERT(e.offset() >= maxRangeOffset(e.node())); - e = Position(prev, maxRangeOffset(prev)); - } - } - } - if (node == end.node()) - break; - node = next; - } - - ASSERT(s.node()->inDocument()); - ASSERT(e.node()->inDocument()); - updateStartEnd(s, e); -} - -bool ApplyStyleCommand::nodeFullySelected(Node *node, const Position &start, const Position &end) const -{ - ASSERT(node); - ASSERT(node->isElementNode()); - - Position pos = Position(node, node->childNodeCount()).upstream(); - return Range::compareBoundaryPoints(node, 0, start.node(), start.offset()) >= 0 && - Range::compareBoundaryPoints(pos, end) <= 0; -} - -bool ApplyStyleCommand::nodeFullyUnselected(Node *node, const Position &start, const Position &end) const -{ - ASSERT(node); - ASSERT(node->isElementNode()); - - Position pos = Position(node, node->childNodeCount()).upstream(); - bool isFullyBeforeStart = Range::compareBoundaryPoints(pos, start) < 0; - bool isFullyAfterEnd = Range::compareBoundaryPoints(node, 0, end.node(), end.offset()) > 0; - - return isFullyBeforeStart || isFullyAfterEnd; -} - - -bool ApplyStyleCommand::splitTextAtStartIfNeeded(const Position &start, const Position &end) -{ - if (start.node()->isTextNode() && start.offset() > caretMinOffset(start.node()) && start.offset() < caretMaxOffset(start.node())) { - int endOffsetAdjustment = start.node() == end.node() ? start.offset() : 0; - Text *text = static_cast<Text *>(start.node()); - splitTextNode(text, start.offset()); - updateStartEnd(Position(start.node(), 0), Position(end.node(), end.offset() - endOffsetAdjustment)); - return true; - } - return false; -} - -bool ApplyStyleCommand::splitTextAtEndIfNeeded(const Position &start, const Position &end) -{ - if (end.node()->isTextNode() && end.offset() > caretMinOffset(end.node()) && end.offset() < caretMaxOffset(end.node())) { - Text *text = static_cast<Text *>(end.node()); - splitTextNode(text, end.offset()); - - Node *prevNode = text->previousSibling(); - ASSERT(prevNode); - Node *startNode = start.node() == end.node() ? prevNode : start.node(); - ASSERT(startNode); - updateStartEnd(Position(startNode, start.offset()), Position(prevNode, caretMaxOffset(prevNode))); - return true; - } - return false; -} - -bool ApplyStyleCommand::splitTextElementAtStartIfNeeded(const Position &start, const Position &end) -{ - if (start.node()->isTextNode() && start.offset() > caretMinOffset(start.node()) && start.offset() < caretMaxOffset(start.node())) { - int endOffsetAdjustment = start.node() == end.node() ? start.offset() : 0; - Text *text = static_cast<Text *>(start.node()); - splitTextNodeContainingElement(text, start.offset()); - - updateStartEnd(Position(start.node()->parentNode(), start.node()->nodeIndex()), Position(end.node(), end.offset() - endOffsetAdjustment)); - return true; - } - return false; -} - -bool ApplyStyleCommand::splitTextElementAtEndIfNeeded(const Position &start, const Position &end) -{ - if (end.node()->isTextNode() && end.offset() > caretMinOffset(end.node()) && end.offset() < caretMaxOffset(end.node())) { - Text *text = static_cast<Text *>(end.node()); - splitTextNodeContainingElement(text, end.offset()); - - Node *prevNode = text->parent()->previousSibling()->lastChild(); - ASSERT(prevNode); - Node *startNode = start.node() == end.node() ? prevNode : start.node(); - ASSERT(startNode); - updateStartEnd(Position(startNode, start.offset()), Position(prevNode->parent(), prevNode->nodeIndex() + 1)); - return true; - } - return false; -} - -static bool areIdenticalElements(Node *first, Node *second) -{ - // check that tag name and all attribute names and values are identical - - if (!first->isElementNode()) - return false; - - if (!second->isElementNode()) - return false; - - Element *firstElement = static_cast<Element *>(first); - Element *secondElement = static_cast<Element *>(second); - - if (!firstElement->tagQName().matches(secondElement->tagQName())) - return false; - - NamedAttrMap *firstMap = firstElement->attributes(); - NamedAttrMap *secondMap = secondElement->attributes(); - - unsigned firstLength = firstMap->length(); - - if (firstLength != secondMap->length()) - return false; - - for (unsigned i = 0; i < firstLength; i++) { - Attribute *attribute = firstMap->attributeItem(i); - Attribute *secondAttribute = secondMap->getAttributeItem(attribute->name()); - - if (!secondAttribute || attribute->value() != secondAttribute->value()) - return false; - } - - return true; -} - -bool ApplyStyleCommand::mergeStartWithPreviousIfIdentical(const Position &start, const Position &end) -{ - Node *startNode = start.node(); - int startOffset = start.offset(); - - if (isAtomicNode(start.node())) { - if (start.offset() != 0) - return false; - - // note: prior siblings could be unrendered elements. it's silly to miss the - // merge opportunity just for that. - if (start.node()->previousSibling()) - return false; - - startNode = start.node()->parent(); - startOffset = 0; - } - - if (!startNode->isElementNode()) - return false; - - if (startOffset != 0) - return false; - - Node *previousSibling = startNode->previousSibling(); - - if (previousSibling && areIdenticalElements(startNode, previousSibling)) { - Element *previousElement = static_cast<Element *>(previousSibling); - Element *element = static_cast<Element *>(startNode); - Node *startChild = element->firstChild(); - ASSERT(startChild); - mergeIdenticalElements(previousElement, element); - - int startOffsetAdjustment = startChild->nodeIndex(); - int endOffsetAdjustment = startNode == end.node() ? startOffsetAdjustment : 0; - updateStartEnd(Position(startNode, startOffsetAdjustment), Position(end.node(), end.offset() + endOffsetAdjustment)); - return true; - } - - return false; -} - -bool ApplyStyleCommand::mergeEndWithNextIfIdentical(const Position &start, const Position &end) -{ - Node *endNode = end.node(); - int endOffset = end.offset(); - - if (isAtomicNode(endNode)) { - if (endOffset < caretMaxOffset(endNode)) - return false; - - unsigned parentLastOffset = end.node()->parent()->childNodes()->length() - 1; - if (end.node()->nextSibling()) - return false; - - endNode = end.node()->parent(); - endOffset = parentLastOffset; - } - - if (!endNode->isElementNode() || endNode->hasTagName(brTag)) - return false; - - Node *nextSibling = endNode->nextSibling(); - - if (nextSibling && areIdenticalElements(endNode, nextSibling)) { - Element *nextElement = static_cast<Element *>(nextSibling); - Element *element = static_cast<Element *>(endNode); - Node *nextChild = nextElement->firstChild(); - - mergeIdenticalElements(element, nextElement); - - Node *startNode = start.node() == endNode ? nextElement : start.node(); - ASSERT(startNode); - - int endOffset = nextChild ? nextChild->nodeIndex() : nextElement->childNodes()->length(); - updateStartEnd(Position(startNode, start.offset()), Position(nextElement, endOffset)); - return true; - } - - return false; -} - -void ApplyStyleCommand::surroundNodeRangeWithElement(Node *startNode, Node *endNode, Element *element) -{ - ASSERT(startNode); - ASSERT(endNode); - ASSERT(element); - - Node *node = startNode; - while (1) { - Node *next = node->traverseNextNode(); - if (node->childNodeCount() == 0 && node->renderer() && node->renderer()->isInline()) { - removeNode(node); - appendNode(node, element); - } - if (node == endNode) - break; - node = next; - } - // FIXME: We should probably call updateStartEnd if the start or end was in the node - // range so that the endingSelection() is canonicalized. See the comments at the end of - // Selection::validate(). -} - -void ApplyStyleCommand::addBlockStyle(const StyleChange& styleChange, HTMLElement* block) -{ - // Do not check for legacy styles here. Those styles, like <B> and <I>, only apply for - // inline content. - if (!block) - return; - - String cssText = styleChange.cssStyle(); - CSSMutableStyleDeclaration* decl = block->inlineStyleDecl(); - if (decl) - cssText += decl->cssText(); - setNodeAttribute(block, styleAttr, cssText); -} - -void ApplyStyleCommand::addInlineStyleIfNeeded(CSSMutableStyleDeclaration *style, Node *startNode, Node *endNode) -{ - if (m_removeOnly) - return; - - StyleChange styleChange(style, Position(startNode, 0), StyleChange::styleModeForParseMode(document()->inCompatMode())); - ExceptionCode ec = 0; - - // - // Font tags need to go outside of CSS so that CSS font sizes override leagcy font sizes. - // - if (styleChange.applyFontColor() || styleChange.applyFontFace() || styleChange.applyFontSize()) { - RefPtr<Element> fontElement = createFontElement(document()); - ASSERT(ec == 0); - insertNodeBefore(fontElement.get(), startNode); - if (styleChange.applyFontColor()) - fontElement->setAttribute(colorAttr, styleChange.fontColor()); - if (styleChange.applyFontFace()) - fontElement->setAttribute(faceAttr, styleChange.fontFace()); - if (styleChange.applyFontSize()) - fontElement->setAttribute(sizeAttr, styleChange.fontSize()); - surroundNodeRangeWithElement(startNode, endNode, fontElement.get()); - } - - if (styleChange.cssStyle().length() > 0) { - RefPtr<Element> styleElement = createStyleSpanElement(document()); - styleElement->setAttribute(styleAttr, styleChange.cssStyle()); - insertNodeBefore(styleElement.get(), startNode); - surroundNodeRangeWithElement(startNode, endNode, styleElement.get()); - } - - if (styleChange.applyBold()) { - RefPtr<Element> boldElement = document()->createElementNS(xhtmlNamespaceURI, "b", ec); - ASSERT(ec == 0); - insertNodeBefore(boldElement.get(), startNode); - surroundNodeRangeWithElement(startNode, endNode, boldElement.get()); - } - - if (styleChange.applyItalic()) { - RefPtr<Element> italicElement = document()->createElementNS(xhtmlNamespaceURI, "i", ec); - ASSERT(ec == 0); - insertNodeBefore(italicElement.get(), startNode); - surroundNodeRangeWithElement(startNode, endNode, italicElement.get()); - } - - if (m_styledInlineElement) { - RefPtr<Element> clonedElement = static_pointer_cast<Element>(m_styledInlineElement->cloneNode(false)); - insertNodeBefore(clonedElement.get(), startNode); - surroundNodeRangeWithElement(startNode, endNode, clonedElement.get()); - } -} - -float ApplyStyleCommand::computedFontSize(const Node *node) -{ - if (!node) - return 0; - - Position pos(const_cast<Node *>(node), 0); - RefPtr<CSSComputedStyleDeclaration> computedStyle = pos.computedStyle(); - if (!computedStyle) - return 0; - - RefPtr<CSSPrimitiveValue> value = static_pointer_cast<CSSPrimitiveValue>(computedStyle->getPropertyCSSValue(CSSPropertyFontSize)); - if (!value) - return 0; - - return value->getFloatValue(CSSPrimitiveValue::CSS_PX); -} - -void ApplyStyleCommand::joinChildTextNodes(Node *node, const Position &start, const Position &end) -{ - if (!node) - return; - - Position newStart = start; - Position newEnd = end; - - Node *child = node->firstChild(); - while (child) { - Node *next = child->nextSibling(); - if (child->isTextNode() && next && next->isTextNode()) { - Text *childText = static_cast<Text *>(child); - Text *nextText = static_cast<Text *>(next); - if (next == start.node()) - newStart = Position(childText, childText->length() + start.offset()); - if (next == end.node()) - newEnd = Position(childText, childText->length() + end.offset()); - String textToMove = nextText->data(); - insertTextIntoNode(childText, childText->length(), textToMove); - removeNode(next); - // don't move child node pointer. it may want to merge with more text nodes. - } - else { - child = child->nextSibling(); - } - } - - updateStartEnd(newStart, newEnd); -} - -} diff --git a/WebCore/editing/ApplyStyleCommand.h b/WebCore/editing/ApplyStyleCommand.h deleted file mode 100644 index 8df76a0..0000000 --- a/WebCore/editing/ApplyStyleCommand.h +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright (C) 2005, 2006, 2008 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 ApplyStyleCommand_h -#define ApplyStyleCommand_h - -#include "CompositeEditCommand.h" - -namespace WebCore { - -class HTMLElement; -class StyleChange; - -class ApplyStyleCommand : public CompositeEditCommand { -public: - enum EPropertyLevel { PropertyDefault, ForceBlockProperties }; - - static PassRefPtr<ApplyStyleCommand> create(Document* document, CSSStyleDeclaration* style, EditAction action = EditActionChangeAttributes, EPropertyLevel level = PropertyDefault) - { - return adoptRef(new ApplyStyleCommand(document, style, action, level)); - } - static PassRefPtr<ApplyStyleCommand> create(Document* document, CSSStyleDeclaration* style, const Position& start, const Position& end, EditAction action = EditActionChangeAttributes, EPropertyLevel level = PropertyDefault) - { - return adoptRef(new ApplyStyleCommand(document, style, start, end, action, level)); - } - static PassRefPtr<ApplyStyleCommand> create(Element* element, bool removeOnly = false, EditAction action = EditActionChangeAttributes) - { - return adoptRef(new ApplyStyleCommand(element, removeOnly, action)); - } - -private: - ApplyStyleCommand(Document*, CSSStyleDeclaration*, EditAction, EPropertyLevel); - ApplyStyleCommand(Document*, CSSStyleDeclaration*, const Position& start, const Position& end, EditAction, EPropertyLevel); - ApplyStyleCommand(Element*, bool removeOnly, EditAction); - - virtual void doApply(); - virtual EditAction editingAction() const; - - CSSMutableStyleDeclaration* style() const { return m_style.get(); } - - // style-removal helpers - bool isHTMLStyleNode(CSSMutableStyleDeclaration*, HTMLElement*); - void removeHTMLStyleNode(HTMLElement*); - void removeHTMLFontStyle(CSSMutableStyleDeclaration*, HTMLElement*); - void removeCSSStyle(CSSMutableStyleDeclaration*, HTMLElement*); - void removeBlockStyle(CSSMutableStyleDeclaration*, const Position& start, const Position& end); - void removeInlineStyle(PassRefPtr<CSSMutableStyleDeclaration>, 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; - PassRefPtr<CSSMutableStyleDeclaration> extractTextDecorationStyle(Node*); - PassRefPtr<CSSMutableStyleDeclaration> extractAndNegateTextDecorationStyle(Node*); - void applyTextDecorationStyle(Node*, CSSMutableStyleDeclaration *style); - void pushDownTextDecorationStyleAroundNode(Node*, const Position& start, const Position& end, bool force); - void pushDownTextDecorationStyleAtBoundaries(const Position& start, const Position& end); - - // style-application helpers - void applyBlockStyle(CSSMutableStyleDeclaration*); - void applyRelativeFontStyleChange(CSSMutableStyleDeclaration*); - void applyInlineStyle(CSSMutableStyleDeclaration*); - void addBlockStyle(const StyleChange&, HTMLElement*); - void addInlineStyleIfNeeded(CSSMutableStyleDeclaration*, Node* start, Node* end); - bool splitTextAtStartIfNeeded(const Position& start, const Position& end); - bool splitTextAtEndIfNeeded(const Position& start, const Position& end); - bool splitTextElementAtStartIfNeeded(const Position& start, const Position& end); - bool splitTextElementAtEndIfNeeded(const Position& start, const Position& end); - bool mergeStartWithPreviousIfIdentical(const Position& start, const Position& end); - bool mergeEndWithNextIfIdentical(const Position& start, const Position& end); - void cleanupUnstyledAppleStyleSpans(Node* dummySpanAncestor); - - void surroundNodeRangeWithElement(Node* start, Node* end, Element* element); - float computedFontSize(const Node*); - void joinChildTextNodes(Node*, const Position& start, const Position& end); - - void updateStartEnd(const Position& newStart, const Position& newEnd); - Position startPosition(); - Position endPosition(); - - RefPtr<CSSMutableStyleDeclaration> m_style; - EditAction m_editingAction; - EPropertyLevel m_propertyLevel; - Position m_start; - Position m_end; - bool m_useEndingSelection; - RefPtr<Element> m_styledInlineElement; - bool m_removeOnly; -}; - -bool isStyleSpan(const Node*); -PassRefPtr<HTMLElement> createStyleSpanElement(Document*); - -} // namespace WebCore - -#endif diff --git a/WebCore/editing/BreakBlockquoteCommand.cpp b/WebCore/editing/BreakBlockquoteCommand.cpp deleted file mode 100644 index a172953..0000000 --- a/WebCore/editing/BreakBlockquoteCommand.cpp +++ /dev/null @@ -1,175 +0,0 @@ -/* - * Copyright (C) 2005 Apple Computer, 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 "BreakBlockquoteCommand.h" - -#include "Element.h" -#include "HTMLNames.h" -#include "Text.h" -#include "VisiblePosition.h" -#include "htmlediting.h" -#include "RenderListItem.h" - -namespace WebCore { - -using namespace HTMLNames; - -BreakBlockquoteCommand::BreakBlockquoteCommand(Document *document) - : CompositeEditCommand(document) -{ -} - -void BreakBlockquoteCommand::doApply() -{ - if (endingSelection().isNone()) - return; - - // Delete the current selection. - if (endingSelection().isRange()) - deleteSelection(false, false); - - VisiblePosition visiblePos = endingSelection().visibleStart(); - // pos is a position equivalent to the caret. We use downstream() so that pos will - // be in the first node that we need to move (there are a few exceptions to this, see below). - Position pos = endingSelection().start().downstream(); - - // startNode is the first node that we need to move to the new blockquote. - Node* startNode = pos.node(); - // Find the top-most blockquote from the start. - Node* topBlockquote = 0; - for (Node *node = startNode->parentNode(); node; node = node->parentNode()) { - if (isMailBlockquote(node)) - topBlockquote = node; - } - if (!topBlockquote || !topBlockquote->parentNode()) - return; - - // Insert a break after the top blockquote. - RefPtr<Element> breakNode = createBreakElement(document()); - insertNodeAfter(breakNode.get(), topBlockquote); - - if (isLastVisiblePositionInNode(visiblePos, topBlockquote)) { - setEndingSelection(Selection(Position(breakNode.get(), 0), DOWNSTREAM)); - rebalanceWhitespace(); - return; - } - - // Don't move a line break just after the caret. Doing so would create an extra, empty paragraph - // in the new blockquote. - if (lineBreakExistsAtPosition(visiblePos)) - pos = pos.next(); - - // Split at pos if in the middle of a text node. - if (startNode->isTextNode()) { - Text* textNode = static_cast<Text*>(startNode); - if ((unsigned)pos.offset() >= textNode->length()) { - startNode = startNode->traverseNextNode(); - ASSERT(startNode); - } else if (pos.offset() > 0) - splitTextNode(textNode, pos.offset()); - } else if (pos.offset() > 0) { - startNode = startNode->traverseNextNode(); - ASSERT(startNode); - } - - // If there's nothing inside topBlockquote to move, we're finished. - if (!startNode->isDescendantOf(topBlockquote)) { - setEndingSelection(Selection(VisiblePosition(Position(startNode, 0)))); - return; - } - - // Build up list of ancestors in between the start node and the top blockquote. - Vector<Node*> ancestors; - for (Node* node = startNode->parentNode(); node != topBlockquote; node = node->parentNode()) - ancestors.append(node); - - // Insert a clone of the top blockquote after the break. - RefPtr<Node> clonedBlockquote = topBlockquote->cloneNode(false); - insertNodeAfter(clonedBlockquote.get(), breakNode.get()); - - // Clone startNode's ancestors into the cloned blockquote. - // On exiting this loop, clonedAncestor is the lowest ancestor - // that was cloned (i.e. the clone of either ancestors.last() - // or clonedBlockquote if ancestors is empty). - RefPtr<Node> clonedAncestor = clonedBlockquote; - for (size_t i = ancestors.size(); i != 0; --i) { - RefPtr<Node> clonedChild = ancestors[i - 1]->cloneNode(false); // shallow clone - // Preserve list item numbering in cloned lists. - if (clonedChild->isElementNode() && clonedChild->hasTagName(olTag)) { - Node* listChildNode = i > 1 ? ancestors[i - 2] : startNode; - // The first child of the cloned list might not be a list item element, - // find the first one so that we know where to start numbering. - while (listChildNode && !listChildNode->hasTagName(liTag)) - listChildNode = listChildNode->nextSibling(); - if (listChildNode && listChildNode->renderer()) - setNodeAttribute(static_cast<Element*>(clonedChild.get()), startAttr, String::number(static_cast<RenderListItem*>(listChildNode->renderer())->value())); - } - - appendNode(clonedChild.get(), clonedAncestor.get()); - clonedAncestor = clonedChild; - } - - // Move the startNode and its siblings. - Node *moveNode = startNode; - while (moveNode) { - Node *next = moveNode->nextSibling(); - removeNode(moveNode); - appendNode(moveNode, clonedAncestor.get()); - moveNode = next; - } - - // Hold open startNode's original parent if we emptied it - if (!ancestors.isEmpty()) { - addBlockPlaceholderIfNeeded(ancestors.first()); - - // Split the tree up the ancestor chain until the topBlockquote - // Throughout this loop, clonedParent is the clone of ancestor's parent. - // This is so we can clone ancestor's siblings and place the clones - // into the clone corresponding to the ancestor's parent. - Node* ancestor; - Node* clonedParent; - for (ancestor = ancestors.first(), clonedParent = clonedAncestor->parentNode(); - ancestor && ancestor != topBlockquote; - ancestor = ancestor->parentNode(), clonedParent = clonedParent->parentNode()) { - moveNode = ancestor->nextSibling(); - while (moveNode) { - Node *next = moveNode->nextSibling(); - removeNode(moveNode); - appendNode(moveNode, clonedParent); - moveNode = next; - } - } - } - - // Make sure the cloned block quote renders. - addBlockPlaceholderIfNeeded(clonedBlockquote.get()); - - // Put the selection right before the break. - setEndingSelection(Selection(Position(breakNode.get(), 0), DOWNSTREAM)); - rebalanceWhitespace(); -} - -} // namespace WebCore diff --git a/WebCore/editing/BreakBlockquoteCommand.h b/WebCore/editing/BreakBlockquoteCommand.h deleted file mode 100644 index 885e5d6..0000000 --- a/WebCore/editing/BreakBlockquoteCommand.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (C) 2005, 2006, 2008 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 BreakBlockquoteCommand_h -#define BreakBlockquoteCommand_h - -#include "CompositeEditCommand.h" - -namespace WebCore { - -class BreakBlockquoteCommand : public CompositeEditCommand { -public: - static PassRefPtr<BreakBlockquoteCommand> create(Document* document) - { - return adoptRef(new BreakBlockquoteCommand(document)); - } - -private: - BreakBlockquoteCommand(Document*); - virtual void doApply(); -}; - -} // namespace WebCore - -#endif diff --git a/WebCore/editing/CompositeEditCommand.cpp b/WebCore/editing/CompositeEditCommand.cpp deleted file mode 100644 index 3700023..0000000 --- a/WebCore/editing/CompositeEditCommand.cpp +++ /dev/null @@ -1,960 +0,0 @@ -/* - * Copyright (C) 2005, 2006, 2007, 2008 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 "CompositeEditCommand.h" - -#include "AppendNodeCommand.h" -#include "ApplyStyleCommand.h" -#include "CSSComputedStyleDeclaration.h" -#include "CSSMutableStyleDeclaration.h" -#include "CharacterNames.h" -#include "DeleteFromTextNodeCommand.h" -#include "DeleteSelectionCommand.h" -#include "Document.h" -#include "DocumentFragment.h" -#include "EditorInsertAction.h" -#include "Element.h" -#include "HTMLNames.h" -#include "InlineTextBox.h" -#include "InsertIntoTextNodeCommand.h" -#include "InsertLineBreakCommand.h" -#include "InsertNodeBeforeCommand.h" -#include "InsertParagraphSeparatorCommand.h" -#include "InsertTextCommand.h" -#include "JoinTextNodesCommand.h" -#include "MergeIdenticalElementsCommand.h" -#include "Range.h" -#include "RemoveCSSPropertyCommand.h" -#include "RemoveNodeAttributeCommand.h" -#include "RemoveNodeCommand.h" -#include "RemoveNodePreservingChildrenCommand.h" -#include "ReplaceSelectionCommand.h" -#include "SetNodeAttributeCommand.h" -#include "SplitElementCommand.h" -#include "SplitTextNodeCommand.h" -#include "SplitTextNodeContainingElementCommand.h" -#include "Text.h" -#include "TextIterator.h" -#include "WrapContentsInDummySpanCommand.h" -#include "htmlediting.h" -#include "markup.h" -#include "visible_units.h" - -using namespace std; - -namespace WebCore { - -using namespace HTMLNames; - -CompositeEditCommand::CompositeEditCommand(Document *document) - : EditCommand(document) -{ -} - -void CompositeEditCommand::doUnapply() -{ - size_t size = m_commands.size(); - for (size_t i = size; i != 0; --i) - m_commands[i - 1]->unapply(); -} - -void CompositeEditCommand::doReapply() -{ - size_t size = m_commands.size(); - for (size_t i = 0; i != size; ++i) - m_commands[i]->reapply(); -} - -// -// sugary-sweet convenience functions to help create and apply edit commands in composite commands -// -void CompositeEditCommand::applyCommandToComposite(PassRefPtr<EditCommand> cmd) -{ - cmd->setParent(this); - cmd->apply(); - m_commands.append(cmd); -} - -void CompositeEditCommand::applyStyle(CSSStyleDeclaration* style, EditAction editingAction) -{ - applyCommandToComposite(ApplyStyleCommand::create(document(), style, editingAction)); -} - -void CompositeEditCommand::applyStyle(CSSStyleDeclaration* style, const Position& start, const Position& end, EditAction editingAction) -{ - applyCommandToComposite(ApplyStyleCommand::create(document(), style, start, end, editingAction)); -} - -void CompositeEditCommand::applyStyledElement(Element* element) -{ - applyCommandToComposite(ApplyStyleCommand::create(element, false)); -} - -void CompositeEditCommand::removeStyledElement(Element* element) -{ - applyCommandToComposite(ApplyStyleCommand::create(element, true)); -} - -void CompositeEditCommand::insertParagraphSeparator(bool useDefaultParagraphElement) -{ - applyCommandToComposite(InsertParagraphSeparatorCommand::create(document(), useDefaultParagraphElement)); -} - -void CompositeEditCommand::insertLineBreak() -{ - applyCommandToComposite(InsertLineBreakCommand::create(document())); -} - -void CompositeEditCommand::insertNodeBefore(Node* insertChild, Node* refChild) -{ - ASSERT(!refChild->hasTagName(bodyTag)); - applyCommandToComposite(InsertNodeBeforeCommand::create(insertChild, refChild)); -} - -void CompositeEditCommand::insertNodeAfter(Node* insertChild, Node* refChild) -{ - ASSERT(!refChild->hasTagName(bodyTag)); - if (refChild->parentNode()->lastChild() == refChild) - appendNode(insertChild, refChild->parentNode()); - else { - ASSERT(refChild->nextSibling()); - insertNodeBefore(insertChild, refChild->nextSibling()); - } -} - -void CompositeEditCommand::insertNodeAt(Node* insertChild, const Position& editingPosition) -{ - ASSERT(isEditablePosition(editingPosition)); - // For editing positions like [table, 0], insert before the table, - // likewise for replaced elements, brs, etc. - Position p = rangeCompliantEquivalent(editingPosition); - Node* refChild = p.node(); - int offset = p.offset(); - - if (canHaveChildrenForEditing(refChild)) { - Node* child = refChild->firstChild(); - for (int i = 0; child && i < offset; i++) - child = child->nextSibling(); - if (child) - insertNodeBefore(insertChild, child); - else - appendNode(insertChild, refChild); - } else if (caretMinOffset(refChild) >= offset) { - insertNodeBefore(insertChild, refChild); - } else if (refChild->isTextNode() && caretMaxOffset(refChild) > offset) { - splitTextNode(static_cast<Text *>(refChild), offset); - insertNodeBefore(insertChild, refChild); - } else { - insertNodeAfter(insertChild, refChild); - } -} - -void CompositeEditCommand::appendNode(Node* newChild, Node* parent) -{ - ASSERT(canHaveChildrenForEditing(parent)); - applyCommandToComposite(AppendNodeCommand::create(parent, newChild)); -} - -void CompositeEditCommand::removeChildrenInRange(Node* node, int from, int to) -{ - Node* nodeToRemove = node->childNode(from); - for (int i = from; i < to; i++) { - ASSERT(nodeToRemove); - Node* next = nodeToRemove->nextSibling(); - removeNode(nodeToRemove); - nodeToRemove = next; - } -} - -void CompositeEditCommand::removeNode(Node* removeChild) -{ - applyCommandToComposite(RemoveNodeCommand::create(removeChild)); -} - -void CompositeEditCommand::removeNodePreservingChildren(Node* removeChild) -{ - applyCommandToComposite(RemoveNodePreservingChildrenCommand::create(removeChild)); -} - -void CompositeEditCommand::removeNodeAndPruneAncestors(Node* node) -{ - RefPtr<Node> parent = node->parentNode(); - removeNode(node); - prune(parent); -} - -static bool hasARenderedDescendant(Node* node) -{ - Node* n = node->firstChild(); - while (n) { - if (n->renderer()) - return true; - n = n->traverseNextNode(node); - } - return false; -} - -void CompositeEditCommand::prune(PassRefPtr<Node> node) -{ - while (node) { - // If you change this rule you may have to add an updateLayout() here. - RenderObject* renderer = node->renderer(); - if (renderer && (!renderer->canHaveChildren() || hasARenderedDescendant(node.get()) || node->rootEditableElement() == node)) - return; - - RefPtr<Node> next = node->parentNode(); - removeNode(node.get()); - node = next; - } -} - -void CompositeEditCommand::splitTextNode(Text *text, int offset) -{ - applyCommandToComposite(SplitTextNodeCommand::create(text, offset)); -} - -void CompositeEditCommand::splitElement(Element* element, Node* atChild) -{ - applyCommandToComposite(SplitElementCommand::create(element, atChild)); -} - -void CompositeEditCommand::mergeIdenticalElements(Element* first, Element* second) -{ - ASSERT(!first->isDescendantOf(second) && second != first); - if (first->nextSibling() != second) { - removeNode(second); - insertNodeAfter(second, first); - } - applyCommandToComposite(MergeIdenticalElementsCommand::create(first, second)); -} - -void CompositeEditCommand::wrapContentsInDummySpan(Element* element) -{ - applyCommandToComposite(WrapContentsInDummySpanCommand::create(element)); -} - -void CompositeEditCommand::splitTextNodeContainingElement(Text *text, int offset) -{ - applyCommandToComposite(SplitTextNodeContainingElementCommand::create(text, offset)); -} - -void CompositeEditCommand::joinTextNodes(Text *text1, Text *text2) -{ - applyCommandToComposite(JoinTextNodesCommand::create(text1, text2)); -} - -void CompositeEditCommand::inputText(const String &text, bool selectInsertedText) -{ - int offset = 0; - int length = text.length(); - RefPtr<Range> startRange = Range::create(document(), Position(document()->documentElement(), 0), endingSelection().start()); - int startIndex = TextIterator::rangeLength(startRange.get()); - int newline; - do { - newline = text.find('\n', offset); - if (newline != offset) { - RefPtr<InsertTextCommand> command = InsertTextCommand::create(document()); - applyCommandToComposite(command); - int substringLength = newline == -1 ? length - offset : newline - offset; - command->input(text.substring(offset, substringLength), false); - } - if (newline != -1) - insertLineBreak(); - - offset = newline + 1; - } while (newline != -1 && offset != length); - - if (selectInsertedText) { - RefPtr<Range> selectedRange = TextIterator::rangeFromLocationAndLength(document()->documentElement(), startIndex, length); - setEndingSelection(Selection(selectedRange.get())); - } -} - -void CompositeEditCommand::insertTextIntoNode(Text *node, int offset, const String &text) -{ - applyCommandToComposite(InsertIntoTextNodeCommand::create(node, offset, text)); -} - -void CompositeEditCommand::deleteTextFromNode(Text *node, int offset, int count) -{ - applyCommandToComposite(DeleteFromTextNodeCommand::create(node, offset, count)); -} - -void CompositeEditCommand::replaceTextInNode(Text *node, int offset, int count, const String &replacementText) -{ - applyCommandToComposite(DeleteFromTextNodeCommand::create(node, offset, count)); - applyCommandToComposite(InsertIntoTextNodeCommand::create(node, offset, replacementText)); -} - -Position CompositeEditCommand::positionOutsideTabSpan(const Position& pos) -{ - if (!isTabSpanTextNode(pos.node())) - return pos; - - Node* tabSpan = tabSpanNode(pos.node()); - - if (pos.offset() <= caretMinOffset(pos.node())) - return positionBeforeNode(tabSpan); - - if (pos.offset() >= caretMaxOffset(pos.node())) - return positionAfterNode(tabSpan); - - splitTextNodeContainingElement(static_cast<Text *>(pos.node()), pos.offset()); - return positionBeforeNode(tabSpan); -} - -void CompositeEditCommand::insertNodeAtTabSpanPosition(Node* node, const Position& pos) -{ - // insert node before, after, or at split of tab span - Position insertPos = positionOutsideTabSpan(pos); - insertNodeAt(node, insertPos); -} - -void CompositeEditCommand::deleteSelection(bool smartDelete, bool mergeBlocksAfterDelete, bool replace, bool expandForSpecialElements) -{ - if (endingSelection().isRange()) - applyCommandToComposite(DeleteSelectionCommand::create(document(), smartDelete, mergeBlocksAfterDelete, replace, expandForSpecialElements)); -} - -void CompositeEditCommand::deleteSelection(const Selection &selection, bool smartDelete, bool mergeBlocksAfterDelete, bool replace, bool expandForSpecialElements) -{ - if (selection.isRange()) - applyCommandToComposite(DeleteSelectionCommand::create(selection, smartDelete, mergeBlocksAfterDelete, replace, expandForSpecialElements)); -} - -void CompositeEditCommand::removeCSSProperty(CSSStyleDeclaration *decl, int property) -{ - applyCommandToComposite(RemoveCSSPropertyCommand::create(document(), decl, property)); -} - -void CompositeEditCommand::removeNodeAttribute(Element* element, const QualifiedName& attribute) -{ - if (element->getAttribute(attribute).isNull()) - return; - applyCommandToComposite(RemoveNodeAttributeCommand::create(element, attribute)); -} - -void CompositeEditCommand::setNodeAttribute(Element* element, const QualifiedName& attribute, const String &value) -{ - applyCommandToComposite(SetNodeAttributeCommand::create(element, attribute, value)); -} - -static inline bool isWhitespace(UChar c) -{ - return c == noBreakSpace || c == ' ' || c == '\n' || c == '\t'; -} - -// FIXME: Doesn't go into text nodes that contribute adjacent text (siblings, cousins, etc). -void CompositeEditCommand::rebalanceWhitespaceAt(const Position& position) -{ - Node* node = position.node(); - if (!node || !node->isTextNode()) - return; - Text* textNode = static_cast<Text*>(node); - - if (textNode->length() == 0) - return; - RenderObject* renderer = textNode->renderer(); - if (renderer && !renderer->style()->collapseWhiteSpace()) - return; - - String text = textNode->data(); - ASSERT(!text.isEmpty()); - - int offset = position.offset(); - // If neither text[offset] nor text[offset - 1] are some form of whitespace, do nothing. - if (!isWhitespace(text[offset])) { - offset--; - if (offset < 0 || !isWhitespace(text[offset])) - return; - } - - // Set upstream and downstream to define the extent of the whitespace surrounding text[offset]. - int upstream = offset; - while (upstream > 0 && isWhitespace(text[upstream - 1])) - upstream--; - - int downstream = offset; - while ((unsigned)downstream + 1 < text.length() && isWhitespace(text[downstream + 1])) - downstream++; - - int length = downstream - upstream + 1; - ASSERT(length > 0); - - VisiblePosition visibleUpstreamPos(Position(position.node(), upstream)); - VisiblePosition visibleDownstreamPos(Position(position.node(), downstream + 1)); - - String string = text.substring(upstream, length); - String rebalancedString = stringWithRebalancedWhitespace(string, - // FIXME: Because of the problem mentioned at the top of this function, we must also use nbsps at the start/end of the string because - // this function doesn't get all surrounding whitespace, just the whitespace in the current text node. - isStartOfParagraph(visibleUpstreamPos) || upstream == 0, - isEndOfParagraph(visibleDownstreamPos) || (unsigned)downstream == text.length() - 1); - - if (string != rebalancedString) - replaceTextInNode(textNode, upstream, length, rebalancedString); -} - -void CompositeEditCommand::prepareWhitespaceAtPositionForSplit(Position& position) -{ - Node* node = position.node(); - if (!node || !node->isTextNode()) - return; - Text* textNode = static_cast<Text*>(node); - - if (textNode->length() == 0) - return; - RenderObject* renderer = textNode->renderer(); - if (renderer && !renderer->style()->collapseWhiteSpace()) - return; - - // Delete collapsed whitespace so that inserting nbsps doesn't uncollapse it. - Position upstreamPos = position.upstream(); - deleteInsignificantText(position.upstream(), position.downstream()); - position = upstreamPos.downstream(); - - VisiblePosition visiblePos(position); - 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.offset(), 1, nonBreakingSpaceString()); - if (isCollapsibleWhitespace(visiblePos.characterAfter()) && position.node()->isTextNode() && !position.node()->hasTagName(brTag)) - replaceTextInNode(static_cast<Text*>(position.node()), position.offset(), 1, nonBreakingSpaceString()); -} - -void CompositeEditCommand::rebalanceWhitespace() -{ - Selection selection = endingSelection(); - if (selection.isNone()) - return; - - rebalanceWhitespaceAt(selection.start()); - if (selection.isRange()) - rebalanceWhitespaceAt(selection.end()); -} - -void CompositeEditCommand::deleteInsignificantText(Text* textNode, int start, int end) -{ - if (!textNode || !textNode->renderer() || start >= end) - return; - - RenderText* textRenderer = static_cast<RenderText*>(textNode->renderer()); - InlineTextBox* box = textRenderer->firstTextBox(); - if (!box) { - // whole text node is empty - removeNode(textNode); - return; - } - - int length = textNode->length(); - if (start >= length || end > length) - return; - - int removed = 0; - InlineTextBox* prevBox = 0; - String str; - - // This loop structure works to process all gaps preceding a box, - // and also will look at the gap after the last box. - while (prevBox || box) { - int gapStart = prevBox ? prevBox->m_start + prevBox->m_len : 0; - if (end < gapStart) - // No more chance for any intersections - break; - - int gapEnd = box ? box->m_start : length; - bool indicesIntersect = start <= gapEnd && end >= gapStart; - int gapLen = gapEnd - gapStart; - if (indicesIntersect && gapLen > 0) { - gapStart = max(gapStart, start); - gapEnd = min(gapEnd, end); - if (str.isNull()) - str = textNode->string()->substring(start, end - start); - // remove text in the gap - str.remove(gapStart - start - removed, gapLen); - removed += gapLen; - } - - prevBox = box; - if (box) - box = box->nextTextBox(); - } - - if (!str.isNull()) { - // Replace the text between start and end with our pruned version. - if (!str.isEmpty()) - replaceTextInNode(textNode, start, end - start, str); - else { - // Assert that we are not going to delete all of the text in the node. - // If we were, that should have been done above with the call to - // removeNode and return. - ASSERT(start > 0 || (unsigned)end - start < textNode->length()); - deleteTextFromNode(textNode, start, end - start); - } - } -} - -void CompositeEditCommand::deleteInsignificantText(const Position& start, const Position& end) -{ - if (start.isNull() || end.isNull()) - return; - - if (Range::compareBoundaryPoints(start, end) >= 0) - return; - - Node* next; - for (Node* node = start.node(); node; node = next) { - next = node->traverseNextNode(); - if (node->isTextNode()) { - Text* textNode = static_cast<Text*>(node); - int startOffset = node == start.node() ? start.offset() : 0; - int endOffset = node == end.node() ? end.offset() : textNode->length(); - deleteInsignificantText(textNode, startOffset, endOffset); - } - if (node == end.node()) - break; - } -} - -void CompositeEditCommand::deleteInsignificantTextDownstream(const Position& pos) -{ - Position end = VisiblePosition(pos, VP_DEFAULT_AFFINITY).next().deepEquivalent().downstream(); - deleteInsignificantText(pos, end); -} - -PassRefPtr<Node> CompositeEditCommand::appendBlockPlaceholder(Node* node) -{ - if (!node) - return 0; - - // Should assert isBlockFlow || isInlineFlow when deletion improves. See 4244964. - ASSERT(node->renderer()); - - RefPtr<Node> placeholder = createBlockPlaceholderElement(document()); - appendNode(placeholder.get(), node); - return placeholder.release(); -} - -PassRefPtr<Node> CompositeEditCommand::insertBlockPlaceholder(const Position& pos) -{ - if (pos.isNull()) - return 0; - - // Should assert isBlockFlow || isInlineFlow when deletion improves. See 4244964. - ASSERT(pos.node()->renderer()); - - RefPtr<Node> placeholder = createBlockPlaceholderElement(document()); - insertNodeAt(placeholder.get(), pos); - return placeholder.release(); -} - -PassRefPtr<Node> CompositeEditCommand::addBlockPlaceholderIfNeeded(Node* node) -{ - if (!node) - return 0; - - updateLayout(); - - RenderObject *renderer = node->renderer(); - if (!renderer || !renderer->isBlockFlow()) - return 0; - - // append the placeholder to make sure it follows - // any unrendered blocks - if (renderer->height() == 0 || (renderer->isListItem() && renderer->isEmpty())) - return appendBlockPlaceholder(node); - - return 0; -} - -// Removes '\n's and brs that will collapse when content is inserted just before them. -// FIXME: We shouldn't really have to remove placeholders, but removing them is a workaround for 9661. -void CompositeEditCommand::removePlaceholderAt(const VisiblePosition& visiblePosition) -{ - if (visiblePosition.isNull()) - return; - - Position p = visiblePosition.deepEquivalent().downstream(); - // If a br or '\n' is at the end of a block and not at the start of a paragraph, - // then it is superfluous, so adding content before a br or '\n' that is at - // the start of a paragraph will render it superfluous. - // FIXME: This doesn't remove placeholders at the end of anonymous blocks. - if (isEndOfBlock(visiblePosition) && isStartOfParagraph(visiblePosition)) { - if (p.node()->hasTagName(brTag) && p.offset() == 0) - removeNode(p.node()); - else if (lineBreakExistsAtPosition(visiblePosition)) - deleteTextFromNode(static_cast<Text*>(p.node()), p.offset(), 1); - } -} - -PassRefPtr<Node> CompositeEditCommand::moveParagraphContentsToNewBlockIfNecessary(const Position& pos) -{ - if (pos.isNull()) - return 0; - - updateLayout(); - - VisiblePosition visiblePos(pos, VP_DEFAULT_AFFINITY); - VisiblePosition visibleParagraphStart(startOfParagraph(visiblePos)); - VisiblePosition visibleParagraphEnd = endOfParagraph(visiblePos); - VisiblePosition next = visibleParagraphEnd.next(); - VisiblePosition visibleEnd = next.isNotNull() ? next : visibleParagraphEnd; - - Position paragraphStart = visibleParagraphStart.deepEquivalent().upstream(); - Position end = visibleEnd.deepEquivalent().upstream(); - - // If there are no VisiblePositions in the same block as pos then - // paragraphStart will be outside the paragraph - if (Range::compareBoundaryPoints(pos, paragraphStart) < 0) - return 0; - - // Perform some checks to see if we need to perform work in this function. - if (isBlock(paragraphStart.node())) { - if (isBlock(end.node())) { - if (!end.node()->isDescendantOf(paragraphStart.node())) { - // 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(end.node()) != paragraphStart.node()) { - // The visibleEnd. It must be an ancestor of the paragraph start. - // We can bail as we have a full block to work with. - ASSERT(paragraphStart.node()->isDescendantOf(enclosingBlock(end.node()))); - return 0; - } - else if (isEndOfDocument(visibleEnd)) { - // At the end of the document. We can bail here as well. - return 0; - } - } - - RefPtr<Node> newBlock = createDefaultParagraphElement(document()); - appendNode(createBreakElement(document()).get(), newBlock.get()); - insertNodeAt(newBlock.get(), paragraphStart); - - moveParagraphs(visibleParagraphStart, visibleParagraphEnd, VisiblePosition(Position(newBlock.get(), 0))); - - return newBlock.get(); -} - -void CompositeEditCommand::pushAnchorElementDown(Node* anchorNode) -{ - if (!anchorNode) - return; - - ASSERT(anchorNode->isLink()); - - setEndingSelection(Selection::selectionFromContentsOfNode(anchorNode)); - applyStyledElement(static_cast<Element*>(anchorNode)); - // Clones of anchorNode have been pushed down, now remove it. - if (anchorNode->inDocument()) - removeNodePreservingChildren(anchorNode); -} - -// We must push partially selected anchors down before creating or removing -// links from a selection to create fully selected chunks that can be removed. -// ApplyStyleCommand doesn't do this for us because styles can be nested. -// Anchors cannot be nested. -void CompositeEditCommand::pushPartiallySelectedAnchorElementsDown() -{ - Selection originalSelection = endingSelection(); - VisiblePosition visibleStart(originalSelection.start()); - VisiblePosition visibleEnd(originalSelection.end()); - - Node* startAnchor = enclosingAnchorElement(originalSelection.start()); - VisiblePosition startOfStartAnchor(Position(startAnchor, 0)); - if (startAnchor && startOfStartAnchor != visibleStart) - pushAnchorElementDown(startAnchor); - - Node* endAnchor = enclosingAnchorElement(originalSelection.end()); - VisiblePosition endOfEndAnchor(Position(endAnchor, 0)); - if (endAnchor && endOfEndAnchor != visibleEnd) - pushAnchorElementDown(endAnchor); - - ASSERT(originalSelection.start().node()->inDocument() && originalSelection.end().node()->inDocument()); - setEndingSelection(originalSelection); -} - -// This moves a paragraph preserving its style. -void CompositeEditCommand::moveParagraph(const VisiblePosition& startOfParagraphToMove, const VisiblePosition& endOfParagraphToMove, const VisiblePosition& destination, bool preserveSelection, bool preserveStyle) -{ - ASSERT(isStartOfParagraph(startOfParagraphToMove)); - ASSERT(isEndOfParagraph(endOfParagraphToMove)); - moveParagraphs(startOfParagraphToMove, endOfParagraphToMove, destination, preserveSelection, preserveStyle); -} - -void CompositeEditCommand::moveParagraphs(const VisiblePosition& startOfParagraphToMove, const VisiblePosition& endOfParagraphToMove, const VisiblePosition& destination, bool preserveSelection, bool preserveStyle) -{ - if (startOfParagraphToMove == destination) - return; - - int startIndex = -1; - int endIndex = -1; - int destinationIndex = -1; - if (preserveSelection && !endingSelection().isNone()) { - VisiblePosition visibleStart = endingSelection().visibleStart(); - VisiblePosition visibleEnd = endingSelection().visibleEnd(); - - bool startAfterParagraph = Range::compareBoundaryPoints(visibleStart.deepEquivalent(), endOfParagraphToMove.deepEquivalent()) > 0; - bool endBeforeParagraph = Range::compareBoundaryPoints(visibleEnd.deepEquivalent(), startOfParagraphToMove.deepEquivalent()) < 0; - - if (!startAfterParagraph && !endBeforeParagraph) { - bool startInParagraph = Range::compareBoundaryPoints(visibleStart.deepEquivalent(), startOfParagraphToMove.deepEquivalent()) >= 0; - bool endInParagraph = Range::compareBoundaryPoints(visibleEnd.deepEquivalent(), endOfParagraphToMove.deepEquivalent()) <= 0; - - startIndex = 0; - if (startInParagraph) { - RefPtr<Range> startRange = Range::create(document(), rangeCompliantEquivalent(startOfParagraphToMove.deepEquivalent()), rangeCompliantEquivalent(visibleStart.deepEquivalent())); - startIndex = TextIterator::rangeLength(startRange.get(), true); - } - - endIndex = 0; - if (endInParagraph) { - RefPtr<Range> endRange = Range::create(document(), rangeCompliantEquivalent(startOfParagraphToMove.deepEquivalent()), rangeCompliantEquivalent(visibleEnd.deepEquivalent())); - endIndex = TextIterator::rangeLength(endRange.get(), true); - } - } - } - - VisiblePosition beforeParagraph = startOfParagraphToMove.previous(); - VisiblePosition afterParagraph(endOfParagraphToMove.next()); - - // We upstream() the end and downstream() the start so that we don't include collapsed whitespace in the move. - // When we paste a fragment, spaces after the end and before the start are treated as though they were rendered. - Position start = startOfParagraphToMove.deepEquivalent().downstream(); - Position end = endOfParagraphToMove.deepEquivalent().upstream(); - - // start and end can't be used directly to create a Range; they are "editing positions" - Position startRangeCompliant = rangeCompliantEquivalent(start); - Position endRangeCompliant = rangeCompliantEquivalent(end); - RefPtr<Range> range = Range::create(document(), startRangeCompliant.node(), startRangeCompliant.offset(), endRangeCompliant.node(), endRangeCompliant.offset()); - - // 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. - RefPtr<DocumentFragment> fragment = startOfParagraphToMove != endOfParagraphToMove ? createFragmentFromMarkup(document(), createMarkup(range.get(), 0, DoNotAnnotateForInterchange, true), "") : 0; - - // A non-empty paragraph's style is moved when we copy and move it. We don't move - // anything if we're given an empty paragraph, but an empty paragraph can have style - // too, <div><b><br></b></div> for example. Save it so that we can preserve it later. - RefPtr<CSSMutableStyleDeclaration> styleInEmptyParagraph; - if (startOfParagraphToMove == endOfParagraphToMove && preserveStyle) { - styleInEmptyParagraph = styleAtPosition(startOfParagraphToMove.deepEquivalent()); - // The moved paragraph should assume the block style of the destination. - styleInEmptyParagraph->removeBlockProperties(); - } - - // FIXME (5098931): We should add a new insert action "WebViewInsertActionMoved" and call shouldInsertFragment here. - - setEndingSelection(Selection(start, end, DOWNSTREAM)); - deleteSelection(false, false, false, false); - - ASSERT(destination.deepEquivalent().node()->inDocument()); - - // There are bugs in deletion when it removes a fully selected table/list. - // It expands and removes the entire table/list, but will let content - // before and after the table/list collapse onto one line. - - // Deleting a paragraph will leave a placeholder. Remove it (and prune - // empty or unrendered parents). - VisiblePosition caretAfterDelete = endingSelection().visibleStart(); - if (isStartOfParagraph(caretAfterDelete) && isEndOfParagraph(caretAfterDelete)) { - // Note: We want the rightmost candidate. - Position position = caretAfterDelete.deepEquivalent().downstream(); - Node* node = position.node(); - // Normally deletion will leave a br as a placeholder. - if (node->hasTagName(brTag)) - removeNodeAndPruneAncestors(node); - // If the selection to move was empty and in an empty block that - // doesn't require a placeholder to prop itself open (like a bordered - // div or an li), remove it during the move (the list removal code - // expects this behavior). - else if (isBlock(node)) - removeNodeAndPruneAncestors(node); - else if (lineBreakExistsAtPosition(caretAfterDelete)) { - // There is a preserved '\n' at caretAfterDelete. - Text* textNode = static_cast<Text*>(node); - if (textNode->length() == 1) - removeNodeAndPruneAncestors(node); - else - deleteTextFromNode(textNode, position.offset(), 1); - } - } - - // Add a br if pruning an empty block level element caused a collapse. For example: - // foo^ - // <div>bar</div> - // baz - // Imagine moving 'bar' to ^. 'bar' will be deleted and its div pruned. That would - // cause 'baz' to collapse onto the line with 'foobar' unless we insert a br. - // Must recononicalize these two VisiblePositions after the pruning above. - beforeParagraph = VisiblePosition(beforeParagraph.deepEquivalent()); - afterParagraph = VisiblePosition(afterParagraph.deepEquivalent()); - if (beforeParagraph.isNotNull() && (!isEndOfParagraph(beforeParagraph) || beforeParagraph == afterParagraph)) { - // FIXME: Trim text between beforeParagraph and afterParagraph if they aren't equal. - insertNodeAt(createBreakElement(document()).get(), beforeParagraph.deepEquivalent()); - // Need an updateLayout here in case inserting the br has split a text node. - updateLayout(); - } - - RefPtr<Range> startToDestinationRange(Range::create(document(), Position(document(), 0), rangeCompliantEquivalent(destination.deepEquivalent()))); - destinationIndex = TextIterator::rangeLength(startToDestinationRange.get(), true); - - setEndingSelection(destination); - applyCommandToComposite(ReplaceSelectionCommand::create(document(), fragment.get(), true, false, !preserveStyle, false, true)); - // Restore styles from an empty paragraph to the new empty paragraph. - if (styleInEmptyParagraph) - applyStyle(styleInEmptyParagraph.get()); - - if (preserveSelection && startIndex != -1) { - // Fragment creation (using createMarkup) incorrectly uses regular - // spaces instead of nbsps for some spaces that were rendered (11475), which - // causes spaces to be collapsed during the move operation. This results - // in a call to rangeFromLocationAndLength with a location past the end - // of the document (which will return null). - RefPtr<Range> start = TextIterator::rangeFromLocationAndLength(document()->documentElement(), destinationIndex + startIndex, 0, true); - RefPtr<Range> end = TextIterator::rangeFromLocationAndLength(document()->documentElement(), destinationIndex + endIndex, 0, true); - if (start && end) - setEndingSelection(Selection(start->startPosition(), end->startPosition(), DOWNSTREAM)); - } -} - -// FIXME: Send an appropriate shouldDeleteRange call. -bool CompositeEditCommand::breakOutOfEmptyListItem() -{ - Node* emptyListItem = enclosingEmptyListItem(endingSelection().visibleStart()); - if (!emptyListItem) - return false; - - RefPtr<CSSMutableStyleDeclaration> style = styleAtPosition(endingSelection().start()); - - Node* listNode = emptyListItem->parentNode(); - - if (!listNode->isContentEditable()) - return false; - - RefPtr<Node> newBlock = isListElement(listNode->parentNode()) ? createListItemElement(document()) : createDefaultParagraphElement(document()); - - if (emptyListItem->renderer()->nextSibling()) { - if (emptyListItem->renderer()->previousSibling()) - splitElement(static_cast<Element*>(listNode), emptyListItem); - insertNodeBefore(newBlock.get(), listNode); - removeNode(emptyListItem); - } else { - insertNodeAfter(newBlock.get(), listNode); - removeNode(emptyListItem->renderer()->previousSibling() ? emptyListItem : listNode); - } - - appendBlockPlaceholder(newBlock.get()); - setEndingSelection(Selection(Position(newBlock.get(), 0), DOWNSTREAM)); - - computedStyle(endingSelection().start().node())->diff(style.get()); - if (style->length() > 0) - applyStyle(style.get()); - - return true; -} - -// Operations use this function to avoid inserting content into an anchor when at the start or the end of -// that anchor, as in NSTextView. -// FIXME: This is only an approximation of NSTextViews insertion behavior, which varies depending on how -// the caret was made. -Position CompositeEditCommand::positionAvoidingSpecialElementBoundary(const Position& original, bool alwaysAvoidAnchors) -{ - if (original.isNull()) - return original; - - VisiblePosition visiblePos(original); - Node* enclosingAnchor = enclosingAnchorElement(original); - Position result = original; - // Don't avoid block level anchors, because that would insert content into the wrong paragraph. - if (enclosingAnchor && !isBlock(enclosingAnchor)) { - VisiblePosition firstInAnchor(Position(enclosingAnchor, 0)); - VisiblePosition lastInAnchor(Position(enclosingAnchor, maxDeepOffset(enclosingAnchor))); - // If visually just after the anchor, insert *inside* the anchor unless it's the last - // VisiblePosition in the document, to match NSTextView. - if (visiblePos == lastInAnchor && (isEndOfDocument(visiblePos) || alwaysAvoidAnchors)) { - // 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) { - pushAnchorElementDown(enclosingAnchor); - enclosingAnchor = enclosingAnchorElement(original); - if (!enclosingAnchor) - return original; - } - // 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 (lineBreakExistsAtPosition(visiblePos) && downstream.node()->isDescendantOf(enclosingAnchor)) - return original; - - result = positionAfterNode(enclosingAnchor); - } - // If visually just before an anchor, insert *outside* the anchor unless it's the first - // VisiblePosition in a paragraph, to match NSTextView. - if (visiblePos == firstInAnchor && (!isStartOfParagraph(visiblePos) || alwaysAvoidAnchors)) { - // 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) { - pushAnchorElementDown(enclosingAnchor); - enclosingAnchor = enclosingAnchorElement(original); - } - result = positionBeforeNode(enclosingAnchor); - } - } - - if (result.isNull() || !editableRootForPosition(result)) - result = original; - - return result; -} - -// Splits the tree parent by parent until we reach the specified ancestor. We use VisiblePositions -// to determine if the split is necessary. Returns the last split node. -PassRefPtr<Node> CompositeEditCommand::splitTreeToNode(Node* start, Node* end, bool splitAncestor) -{ - RefPtr<Node> node; - for (node = start; node && node->parent() != end; node = node->parent()) { - VisiblePosition positionInParent(Position(node->parent(), 0), DOWNSTREAM); - VisiblePosition positionInNode(Position(node, 0), DOWNSTREAM); - if (positionInParent != positionInNode) - applyCommandToComposite(SplitElementCommand::create(static_cast<Element*>(node->parent()), node)); - } - if (splitAncestor) - return splitTreeToNode(end, end->parent()); - return node.release(); -} - -PassRefPtr<Element> createBlockPlaceholderElement(Document* document) -{ - ExceptionCode ec = 0; - RefPtr<Element> breakNode = document->createElementNS(xhtmlNamespaceURI, "br", ec); - ASSERT(ec == 0); - return breakNode.release(); -} - -} // namespace WebCore diff --git a/WebCore/editing/CompositeEditCommand.h b/WebCore/editing/CompositeEditCommand.h deleted file mode 100644 index bbf8b59..0000000 --- a/WebCore/editing/CompositeEditCommand.h +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright (C) 2005, 2006, 2008 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 CompositeEditCommand_h -#define CompositeEditCommand_h - -#include "EditCommand.h" -#include <wtf/Vector.h> - -namespace WebCore { - -class CSSStyleDeclaration; -class Text; - -class CompositeEditCommand : public EditCommand { -public: - bool isFirstCommand(EditCommand* command) { return !m_commands.isEmpty() && m_commands.first() == command; } - -protected: - CompositeEditCommand(Document*); - - // - // sugary-sweet convenience functions to help create and apply edit commands in composite commands - // - void appendNode(Node* appendChild, Node* parentNode); - void applyCommandToComposite(PassRefPtr<EditCommand>); - void applyStyle(CSSStyleDeclaration*, EditAction = EditActionChangeAttributes); - void applyStyle(CSSStyleDeclaration*, const Position& start, const Position& end, EditAction = EditActionChangeAttributes); - void applyStyledElement(Element*); - void removeStyledElement(Element*); - void deleteSelection(bool smartDelete = false, bool mergeBlocksAfterDelete = true, bool replace = false, bool expandForSpecialElements = true); - void deleteSelection(const Selection&, bool smartDelete = false, bool mergeBlocksAfterDelete = true, bool replace = false, bool expandForSpecialElements = true); - virtual void deleteTextFromNode(Text* node, int offset, int count); - void inputText(const String&, bool selectInsertedText = false); - void insertNodeAfter(Node* insertChild, Node* refChild); - void insertNodeAt(Node* insertChild, const Position&); - void insertNodeBefore(Node* insertChild, Node* refChild); - void insertParagraphSeparator(bool useDefaultParagraphElement = false); - void insertLineBreak(); - void insertTextIntoNode(Text* node, int offset, const String& text); - void joinTextNodes(Text*, Text*); - void rebalanceWhitespace(); - void rebalanceWhitespaceAt(const Position&); - void prepareWhitespaceAtPositionForSplit(Position& position); - void removeCSSProperty(CSSStyleDeclaration*, int property); - void removeNodeAttribute(Element*, const QualifiedName& attribute); - void removeChildrenInRange(Node*, int from, int to); - virtual void removeNode(Node*); - void removeNodePreservingChildren(Node*); - void removeNodeAndPruneAncestors(Node*); - void prune(PassRefPtr<Node>); - void replaceTextInNode(Text* node, int offset, int count, const String& replacementText); - Position positionOutsideTabSpan(const Position&); - void insertNodeAtTabSpanPosition(Node*, const Position&); - void setNodeAttribute(Element*, const QualifiedName& attribute, const String& value); - void splitTextNode(Text*, int offset); - void splitElement(Element*, Node* atChild); - void mergeIdenticalElements(Element*, Element*); - void wrapContentsInDummySpan(Element*); - void splitTextNodeContainingElement(Text*, int offset); - - void deleteInsignificantText(Text*, int start, int end); - void deleteInsignificantText(const Position& start, const Position& end); - void deleteInsignificantTextDownstream(const Position&); - - PassRefPtr<Node> appendBlockPlaceholder(Node*); - PassRefPtr<Node> insertBlockPlaceholder(const Position&); - PassRefPtr<Node> addBlockPlaceholderIfNeeded(Node*); - void removePlaceholderAt(const VisiblePosition&); - - PassRefPtr<Node> moveParagraphContentsToNewBlockIfNecessary(const Position&); - - void pushAnchorElementDown(Node*); - void pushPartiallySelectedAnchorElementsDown(); - - void moveParagraph(const VisiblePosition&, const VisiblePosition&, const VisiblePosition&, bool preserveSelection = false, bool preserveStyle = true); - void moveParagraphs(const VisiblePosition&, const VisiblePosition&, const VisiblePosition&, bool preserveSelection = false, bool preserveStyle = true); - - bool breakOutOfEmptyListItem(); - - Position positionAvoidingSpecialElementBoundary(const Position&, bool alwaysAvoidAnchors = true); - - PassRefPtr<Node> splitTreeToNode(Node*, Node*, bool splitAncestor = false); - - Vector<RefPtr<EditCommand> > m_commands; - -private: - virtual void doUnapply(); - virtual void doReapply(); -}; - -} // namespace WebCore - -#endif // CompositeEditCommand_h diff --git a/WebCore/editing/CreateLinkCommand.cpp b/WebCore/editing/CreateLinkCommand.cpp deleted file mode 100644 index c5d68dd..0000000 --- a/WebCore/editing/CreateLinkCommand.cpp +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (C) 2006 Apple Computer, 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 "CreateLinkCommand.h" -#include "htmlediting.h" -#include "Text.h" - -#include "HTMLAnchorElement.h" - -namespace WebCore { - -CreateLinkCommand::CreateLinkCommand(Document* document, const String& url) - : CompositeEditCommand(document) -{ - m_url = url; -} - -void CreateLinkCommand::doApply() -{ - if (endingSelection().isNone()) - return; - - RefPtr<HTMLAnchorElement> anchorElement = new HTMLAnchorElement(document()); - anchorElement->setHref(m_url); - - if (endingSelection().isRange()) { - pushPartiallySelectedAnchorElementsDown(); - applyStyledElement(anchorElement.get()); - } else { - insertNodeAt(anchorElement.get(), endingSelection().start()); - RefPtr<Text> textNode = new Text(document(), m_url); - appendNode(textNode.get(), anchorElement.get()); - setEndingSelection(Selection(positionBeforeNode(anchorElement.get()), positionAfterNode(anchorElement.get()), DOWNSTREAM)); - } -} - -} diff --git a/WebCore/editing/CreateLinkCommand.h b/WebCore/editing/CreateLinkCommand.h deleted file mode 100644 index ba5fe6f..0000000 --- a/WebCore/editing/CreateLinkCommand.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (C) 2006, 2008 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 CreateLinkCommand_h -#define CreateLinkCommand_h - -#include "CompositeEditCommand.h" - -namespace WebCore { - -class CreateLinkCommand : public CompositeEditCommand { -public: - static PassRefPtr<CreateLinkCommand> create(Document* document, const String& linkURL) - { - return adoptRef(new CreateLinkCommand(document, linkURL)); - } - -private: - CreateLinkCommand(Document*, const String& linkURL); - - virtual void doApply(); - virtual EditAction editingAction() const { return EditActionCreateLink; } - - String m_url; -}; - -} // namespace WebCore - -#endif // CreateLinkCommand_h diff --git a/WebCore/editing/DeleteButton.cpp b/WebCore/editing/DeleteButton.cpp deleted file mode 100644 index 449b5b2..0000000 --- a/WebCore/editing/DeleteButton.cpp +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (C) 2006 Apple Computer, 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 "DeleteButton.h" - -#include "DeleteButtonController.h" -#include "Document.h" -#include "Editor.h" -#include "Event.h" -#include "EventNames.h" -#include "Frame.h" - -namespace WebCore { - -DeleteButton::DeleteButton(Document* document) - : HTMLImageElement(document) -{ -} - -void DeleteButton::defaultEventHandler(Event* event) -{ - if (event->isMouseEvent()) { - if (event->type() == eventNames().clickEvent) { - document()->frame()->editor()->deleteButtonController()->deleteTarget(); - event->setDefaultHandled(); - } - } - - HTMLImageElement::defaultEventHandler(event); -} - -} // namespace diff --git a/WebCore/editing/DeleteButton.h b/WebCore/editing/DeleteButton.h deleted file mode 100644 index ac3cdac..0000000 --- a/WebCore/editing/DeleteButton.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2006 Apple Computer, 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 DeleteButton_h -#define DeleteButton_h - -#include "HTMLImageElement.h" - -namespace WebCore { - -class DeleteButton : public HTMLImageElement { -public: - DeleteButton(Document*); - - virtual void defaultEventHandler(Event*); -}; - -} // namespace - -#endif diff --git a/WebCore/editing/DeleteButtonController.cpp b/WebCore/editing/DeleteButtonController.cpp deleted file mode 100644 index d00220d..0000000 --- a/WebCore/editing/DeleteButtonController.cpp +++ /dev/null @@ -1,308 +0,0 @@ -/* - * Copyright (C) 2006, 2008 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 "DeleteButtonController.h" - -#include "CachedImage.h" -#include "CSSMutableStyleDeclaration.h" -#include "CSSPrimitiveValue.h" -#include "CSSPropertyNames.h" -#include "CSSValueKeywords.h" -#include "DeleteButton.h" -#include "Document.h" -#include "Editor.h" -#include "Frame.h" -#include "htmlediting.h" -#include "HTMLDivElement.h" -#include "HTMLNames.h" -#include "Image.h" -#include "Node.h" -#include "Range.h" -#include "RemoveNodeCommand.h" -#include "RenderObject.h" -#include "SelectionController.h" - -namespace WebCore { - -using namespace HTMLNames; - -const char* const DeleteButtonController::containerElementIdentifier = "WebKit-Editing-Delete-Container"; -const char* const DeleteButtonController::buttonElementIdentifier = "WebKit-Editing-Delete-Button"; -const char* const DeleteButtonController::outlineElementIdentifier = "WebKit-Editing-Delete-Outline"; - -DeleteButtonController::DeleteButtonController(Frame* frame) - : m_frame(frame) - , m_wasStaticPositioned(false) - , m_wasAutoZIndex(false) - , m_disableStack(0) -{ -} - -static bool isDeletableElement(const Node* node) -{ - if (!node || !node->isHTMLElement() || !node->inDocument() || !node->isContentEditable()) - return false; - - const int minimumWidth = 25; - const int minimumHeight = 25; - const unsigned minimumVisibleBorders = 3; - - RenderObject* renderer = node->renderer(); - if (!renderer || renderer->width() < minimumWidth || renderer->height() < minimumHeight) - return false; - - if (renderer->isTable()) - return true; - - if (node->hasTagName(ulTag) || node->hasTagName(olTag)) - return true; - - if (renderer->isPositioned()) - return true; - - // allow block elements (excluding table cells) that have some non-transparent borders - if (renderer->isRenderBlock() && !renderer->isTableCell()) { - RenderStyle* style = renderer->style(); - if (style && style->hasBorder()) { - unsigned visibleBorders = style->borderTop().isVisible() + style->borderBottom().isVisible() + style->borderLeft().isVisible() + style->borderRight().isVisible(); - if (visibleBorders >= minimumVisibleBorders) - return true; - } - } - - return false; -} - -static HTMLElement* enclosingDeletableElement(const Selection& selection) -{ - if (!selection.isContentEditable()) - return 0; - - RefPtr<Range> range = selection.toRange(); - if (!range) - return 0; - - ExceptionCode ec = 0; - Node* container = range->commonAncestorContainer(ec); - ASSERT(container); - ASSERT(ec == 0); - - // The enclosingNodeOfType function only works on nodes that are editable - // (which is strange, given its name). - if (!container->isContentEditable()) - return 0; - - Node* element = enclosingNodeOfType(Position(container, 0), &isDeletableElement); - if (!element) - return 0; - - ASSERT(element->isHTMLElement()); - return static_cast<HTMLElement*>(element); -} - -void DeleteButtonController::respondToChangedSelection(const Selection& oldSelection) -{ - if (!enabled()) - return; - - HTMLElement* oldElement = enclosingDeletableElement(oldSelection); - HTMLElement* newElement = enclosingDeletableElement(m_frame->selection()->selection()); - if (oldElement == newElement) - return; - - // If the base is inside a deletable element, give the element a delete widget. - if (newElement) - show(newElement); - else - hide(); -} - -void DeleteButtonController::createDeletionUI() -{ - RefPtr<HTMLDivElement> container = new HTMLDivElement(m_target->document()); - container->setId(containerElementIdentifier); - - CSSMutableStyleDeclaration* style = container->getInlineStyleDecl(); - style->setProperty(CSSPropertyWebkitUserDrag, CSSValueNone); - style->setProperty(CSSPropertyWebkitUserSelect, CSSValueNone); - style->setProperty(CSSPropertyWebkitUserModify, CSSValueNone); - - RefPtr<HTMLDivElement> outline = new HTMLDivElement(m_target->document()); - outline->setId(outlineElementIdentifier); - - const int borderWidth = 4; - const int borderRadius = 6; - - style = outline->getInlineStyleDecl(); - style->setProperty(CSSPropertyPosition, CSSValueAbsolute); - style->setProperty(CSSPropertyCursor, CSSValueDefault); - style->setProperty(CSSPropertyWebkitUserDrag, CSSValueNone); - style->setProperty(CSSPropertyWebkitUserSelect, CSSValueNone); - style->setProperty(CSSPropertyWebkitUserModify, CSSValueNone); - style->setProperty(CSSPropertyZIndex, String::number(-1000000)); - style->setProperty(CSSPropertyTop, String::number(-borderWidth - m_target->renderer()->borderTop()) + "px"); - style->setProperty(CSSPropertyRight, String::number(-borderWidth - m_target->renderer()->borderRight()) + "px"); - style->setProperty(CSSPropertyBottom, String::number(-borderWidth - m_target->renderer()->borderBottom()) + "px"); - style->setProperty(CSSPropertyLeft, String::number(-borderWidth - m_target->renderer()->borderLeft()) + "px"); - style->setProperty(CSSPropertyBorder, String::number(borderWidth) + "px solid rgba(0, 0, 0, 0.6)"); - style->setProperty(CSSPropertyWebkitBorderRadius, String::number(borderRadius) + "px"); - - ExceptionCode ec = 0; - container->appendChild(outline.get(), ec); - ASSERT(ec == 0); - if (ec) - return; - - RefPtr<DeleteButton> button = new DeleteButton(m_target->document()); - button->setId(buttonElementIdentifier); - - const int buttonWidth = 30; - const int buttonHeight = 30; - const int buttonBottomShadowOffset = 2; - - style = button->getInlineStyleDecl(); - style->setProperty(CSSPropertyPosition, CSSValueAbsolute); - style->setProperty(CSSPropertyCursor, CSSValueDefault); - style->setProperty(CSSPropertyWebkitUserDrag, CSSValueNone); - style->setProperty(CSSPropertyWebkitUserSelect, CSSValueNone); - style->setProperty(CSSPropertyWebkitUserModify, CSSValueNone); - style->setProperty(CSSPropertyZIndex, String::number(1000000)); - style->setProperty(CSSPropertyTop, String::number((-buttonHeight / 2) - m_target->renderer()->borderTop() - (borderWidth / 2) + buttonBottomShadowOffset) + "px"); - style->setProperty(CSSPropertyLeft, String::number((-buttonWidth / 2) - m_target->renderer()->borderLeft() - (borderWidth / 2)) + "px"); - style->setProperty(CSSPropertyWidth, String::number(buttonWidth) + "px"); - style->setProperty(CSSPropertyHeight, String::number(buttonHeight) + "px"); - - RefPtr<Image> buttonImage = Image::loadPlatformResource("deleteButton"); - if (buttonImage->isNull()) - return; - - button->setCachedImage(new CachedImage(buttonImage.get())); - - container->appendChild(button.get(), ec); - ASSERT(ec == 0); - if (ec) - return; - - m_containerElement = container.release(); - m_outlineElement = outline.release(); - m_buttonElement = button.release(); -} - -void DeleteButtonController::show(HTMLElement* element) -{ - hide(); - - if (!enabled() || !element || !element->inDocument() || !isDeletableElement(element)) - return; - - if (!m_frame->editor()->shouldShowDeleteInterface(static_cast<HTMLElement*>(element))) - return; - - // we rely on the renderer having current information, so we should update the layout if needed - m_frame->document()->updateLayoutIgnorePendingStylesheets(); - - m_target = element; - - if (!m_containerElement) { - createDeletionUI(); - if (!m_containerElement) { - hide(); - return; - } - } - - ExceptionCode ec = 0; - m_target->appendChild(m_containerElement.get(), ec); - ASSERT(ec == 0); - if (ec) { - hide(); - return; - } - - if (m_target->renderer()->style()->position() == StaticPosition) { - m_target->getInlineStyleDecl()->setProperty(CSSPropertyPosition, CSSValueRelative); - m_wasStaticPositioned = true; - } - - if (m_target->renderer()->style()->hasAutoZIndex()) { - m_target->getInlineStyleDecl()->setProperty(CSSPropertyZIndex, "0"); - m_wasAutoZIndex = true; - } -} - -void DeleteButtonController::hide() -{ - m_outlineElement = 0; - m_buttonElement = 0; - - ExceptionCode ec = 0; - if (m_containerElement && m_containerElement->parentNode()) - m_containerElement->parentNode()->removeChild(m_containerElement.get(), ec); - - if (m_target) { - if (m_wasStaticPositioned) - m_target->getInlineStyleDecl()->setProperty(CSSPropertyPosition, CSSValueStatic); - if (m_wasAutoZIndex) - m_target->getInlineStyleDecl()->setProperty(CSSPropertyZIndex, CSSValueAuto); - } - - m_wasStaticPositioned = false; - m_wasAutoZIndex = false; -} - -void DeleteButtonController::enable() -{ - ASSERT(m_disableStack > 0); - if (m_disableStack > 0) - m_disableStack--; - if (enabled()) - show(enclosingDeletableElement(m_frame->selection()->selection())); -} - -void DeleteButtonController::disable() -{ - if (enabled()) - hide(); - m_disableStack++; -} - -void DeleteButtonController::deleteTarget() -{ - if (!enabled() || !m_target) - return; - - RefPtr<Node> element = m_target; - hide(); - - // Because the deletion UI only appears when the selection is entirely - // within the target, we unconditionally update the selection to be - // a caret where the target had been. - Position pos = positionBeforeNode(element.get()); - applyCommand(RemoveNodeCommand::create(element.release())); - m_frame->selection()->setSelection(VisiblePosition(pos)); -} - -} // namespace WebCore diff --git a/WebCore/editing/DeleteButtonController.h b/WebCore/editing/DeleteButtonController.h deleted file mode 100644 index ab2d0b0..0000000 --- a/WebCore/editing/DeleteButtonController.h +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (C) 2006, 2007 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 DeleteButtonController_h -#define DeleteButtonController_h - -#include "DeleteButton.h" - -namespace WebCore { - -class DeleteButton; -class Frame; -class HTMLElement; -class RenderObject; -class Selection; - -class DeleteButtonController { -public: - DeleteButtonController(Frame*); - - static const char* const containerElementIdentifier; - - HTMLElement* target() const { return m_target.get(); } - HTMLElement* containerElement() const { return m_containerElement.get(); } - - void respondToChangedSelection(const Selection& oldSelection); - - void show(HTMLElement*); - void hide(); - - bool enabled() const { return (m_disableStack == 0); } - void enable(); - void disable(); - - void deleteTarget(); - -private: - static const char* const buttonElementIdentifier; - static const char* const outlineElementIdentifier; - - void createDeletionUI(); - - Frame* m_frame; - RefPtr<HTMLElement> m_target; - RefPtr<HTMLElement> m_containerElement; - RefPtr<HTMLElement> m_outlineElement; - RefPtr<DeleteButton> m_buttonElement; - bool m_wasStaticPositioned; - bool m_wasAutoZIndex; - unsigned m_disableStack; -}; - -} // namespace WebCore - -#endif diff --git a/WebCore/editing/DeleteFromTextNodeCommand.cpp b/WebCore/editing/DeleteFromTextNodeCommand.cpp deleted file mode 100644 index 93a974f..0000000 --- a/WebCore/editing/DeleteFromTextNodeCommand.cpp +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (C) 2005, 2008 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 "DeleteFromTextNodeCommand.h" - -#include "Text.h" - -namespace WebCore { - -DeleteFromTextNodeCommand::DeleteFromTextNodeCommand(PassRefPtr<Text> node, int offset, int count) - : SimpleEditCommand(node->document()), m_node(node), m_offset(offset), m_count(count) -{ - ASSERT(m_node); - ASSERT(m_offset >= 0); - ASSERT(m_offset < (int)m_node->length()); - ASSERT(m_count >= 0); -} - -void DeleteFromTextNodeCommand::doApply() -{ - ASSERT(m_node); - - ExceptionCode ec = 0; - m_text = m_node->substringData(m_offset, m_count, ec); - ASSERT(ec == 0); - - m_node->deleteData(m_offset, m_count, ec); - ASSERT(ec == 0); -} - -void DeleteFromTextNodeCommand::doUnapply() -{ - ASSERT(m_node); - ASSERT(!m_text.isEmpty()); - - ExceptionCode ec = 0; - m_node->insertData(m_offset, m_text, ec); - ASSERT(ec == 0); -} - -} // namespace WebCore diff --git a/WebCore/editing/DeleteFromTextNodeCommand.h b/WebCore/editing/DeleteFromTextNodeCommand.h deleted file mode 100644 index b3bf10b..0000000 --- a/WebCore/editing/DeleteFromTextNodeCommand.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (C) 2005, 2006, 2008 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 DeleteFromTextNodeCommand_h -#define DeleteFromTextNodeCommand_h - -#include "EditCommand.h" - -namespace WebCore { - -class Text; - -class DeleteFromTextNodeCommand : public SimpleEditCommand { -public: - static PassRefPtr<DeleteFromTextNodeCommand> create(PassRefPtr<Text> node, int offset, int count) - { - return adoptRef(new DeleteFromTextNodeCommand(node, offset, count)); - } - -private: - DeleteFromTextNodeCommand(PassRefPtr<Text>, int offset, int count); - - virtual void doApply(); - virtual void doUnapply(); - - RefPtr<Text> m_node; - int m_offset; - int m_count; - String m_text; -}; - -} // namespace WebCore - -#endif // DeleteFromTextNodeCommand_h diff --git a/WebCore/editing/DeleteSelectionCommand.cpp b/WebCore/editing/DeleteSelectionCommand.cpp deleted file mode 100644 index 0c9d68a..0000000 --- a/WebCore/editing/DeleteSelectionCommand.cpp +++ /dev/null @@ -1,790 +0,0 @@ -/* - * Copyright (C) 2005 Apple Computer, 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 "DeleteSelectionCommand.h" - -#include "Document.h" -#include "DocumentFragment.h" -#include "Editor.h" -#include "EditorClient.h" -#include "Element.h" -#include "Frame.h" -#include "Logging.h" -#include "CSSComputedStyleDeclaration.h" -#include "htmlediting.h" -#include "HTMLInputElement.h" -#include "HTMLNames.h" -#include "markup.h" -#include "ReplaceSelectionCommand.h" -#include "Text.h" -#include "TextIterator.h" -#include "visible_units.h" - -namespace WebCore { - -using namespace HTMLNames; - -static bool isTableRow(const Node* node) -{ - return node && node->hasTagName(trTag); -} - -static bool isTableCellEmpty(Node* cell) -{ - ASSERT(isTableCell(cell)); - VisiblePosition firstInCell(Position(cell, 0)); - VisiblePosition lastInCell(Position(cell, maxDeepOffset(cell))); - return firstInCell == lastInCell; -} - -static bool isTableRowEmpty(Node* row) -{ - if (!isTableRow(row)) - return false; - - for (Node* child = row->firstChild(); child; child = child->nextSibling()) - if (isTableCell(child) && !isTableCellEmpty(child)) - return false; - - return true; -} - -DeleteSelectionCommand::DeleteSelectionCommand(Document *document, bool smartDelete, bool mergeBlocksAfterDelete, bool replace, bool expandForSpecialElements) - : CompositeEditCommand(document), - m_hasSelectionToDelete(false), - m_smartDelete(smartDelete), - m_mergeBlocksAfterDelete(mergeBlocksAfterDelete), - m_replace(replace), - m_expandForSpecialElements(expandForSpecialElements), - m_startBlock(0), - m_endBlock(0), - m_typingStyle(0), - m_deleteIntoBlockquoteStyle(0) -{ -} - -DeleteSelectionCommand::DeleteSelectionCommand(const Selection& selection, bool smartDelete, bool mergeBlocksAfterDelete, bool replace, bool expandForSpecialElements) - : CompositeEditCommand(selection.start().node()->document()), - m_hasSelectionToDelete(true), - m_smartDelete(smartDelete), - m_mergeBlocksAfterDelete(mergeBlocksAfterDelete), - m_replace(replace), - m_expandForSpecialElements(expandForSpecialElements), - m_selectionToDelete(selection), - m_startBlock(0), - m_endBlock(0), - m_typingStyle(0), - m_deleteIntoBlockquoteStyle(0) -{ -} - -void DeleteSelectionCommand::initializeStartEnd(Position& start, Position& end) -{ - Node* startSpecialContainer = 0; - Node* endSpecialContainer = 0; - - start = m_selectionToDelete.start(); - end = m_selectionToDelete.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 = Position(start.node(), 0); - else if (end.node()->hasTagName(hrTag)) - end = Position(end.node(), 1); - - // FIXME: This is only used so that moveParagraphs can avoid the bugs in special element expanion. - if (!m_expandForSpecialElements) - return; - - while (1) { - startSpecialContainer = 0; - endSpecialContainer = 0; - - Position s = positionBeforeContainingSpecialElement(start, &startSpecialContainer); - Position e = positionAfterContainingSpecialElement(end, &endSpecialContainer); - - if (!startSpecialContainer && !endSpecialContainer) - break; - - if (VisiblePosition(start) != m_selectionToDelete.visibleStart() || VisiblePosition(end) != m_selectionToDelete.visibleEnd()) - break; - - // If we're going to expand to include the startSpecialContainer, it must be fully selected. - if (startSpecialContainer && !endSpecialContainer && Range::compareBoundaryPoints(positionAfterNode(startSpecialContainer), end) > -1) - break; - - // If we're going to expand to include the endSpecialContainer, it must be fully selected. - if (endSpecialContainer && !startSpecialContainer && Range::compareBoundaryPoints(start, positionBeforeNode(endSpecialContainer)) > -1) - break; - - if (startSpecialContainer && startSpecialContainer->isDescendantOf(endSpecialContainer)) - // Don't adjust the end yet, it is the end of a special element that contains the start - // special element (which may or may not be fully selected). - start = s; - else if (endSpecialContainer && endSpecialContainer->isDescendantOf(startSpecialContainer)) - // Don't adjust the start yet, it is the start of a special element that contains the end - // special element (which may or may not be fully selected). - end = e; - else { - start = s; - end = e; - } - } -} - -void DeleteSelectionCommand::initializePositionData() -{ - Position start, end; - initializeStartEnd(start, end); - - m_upstreamStart = start.upstream(); - m_downstreamStart = start.downstream(); - m_upstreamEnd = end.upstream(); - m_downstreamEnd = end.downstream(); - - m_startRoot = editableRootForPosition(start); - m_endRoot = editableRootForPosition(end); - - m_startTableRow = enclosingNodeOfType(start, &isTableRow); - m_endTableRow = enclosingNodeOfType(end, &isTableRow); - - // Don't move content out of a table cell. - // If the cell is non-editable, enclosingNodeOfType won't return it by default, so - // tell that function that we don't care if it returns non-editable nodes. - Node* startCell = enclosingNodeOfType(m_upstreamStart, &isTableCell, false); - Node* endCell = enclosingNodeOfType(m_downstreamEnd, &isTableCell, false); - // FIXME: This isn't right. A borderless table with two rows and a single column would appear as two paragraphs. - if (endCell && endCell != startCell) - m_mergeBlocksAfterDelete = false; - - // Usually the start and the end of the selection to delete are pulled together as a result of the deletion. - // Sometimes they aren't (like when no merge is requested), so we must choose one position to hold the caret - // and receive the placeholder after deletion. - VisiblePosition visibleEnd(m_downstreamEnd); - if (m_mergeBlocksAfterDelete && !isEndOfParagraph(visibleEnd)) - m_endingPosition = m_downstreamEnd; - else - m_endingPosition = m_downstreamStart; - - // Handle leading and trailing whitespace, as well as smart delete adjustments to the selection - m_leadingWhitespace = m_upstreamStart.leadingWhitespacePosition(m_selectionToDelete.affinity()); - m_trailingWhitespace = m_downstreamEnd.trailingWhitespacePosition(VP_DEFAULT_AFFINITY); - - if (m_smartDelete) { - - // skip smart delete if the selection to delete already starts or ends with whitespace - Position pos = VisiblePosition(m_upstreamStart, m_selectionToDelete.affinity()).deepEquivalent(); - bool skipSmartDelete = pos.trailingWhitespacePosition(VP_DEFAULT_AFFINITY, true).isNotNull(); - if (!skipSmartDelete) - skipSmartDelete = m_downstreamEnd.leadingWhitespacePosition(VP_DEFAULT_AFFINITY, true).isNotNull(); - - // extend selection upstream if there is whitespace there - bool hasLeadingWhitespaceBeforeAdjustment = m_upstreamStart.leadingWhitespacePosition(m_selectionToDelete.affinity(), true).isNotNull(); - if (!skipSmartDelete && hasLeadingWhitespaceBeforeAdjustment) { - VisiblePosition visiblePos = VisiblePosition(m_upstreamStart, VP_DEFAULT_AFFINITY).previous(); - pos = visiblePos.deepEquivalent(); - // Expand out one character upstream for smart delete and recalculate - // positions based on this change. - m_upstreamStart = pos.upstream(); - m_downstreamStart = pos.downstream(); - m_leadingWhitespace = m_upstreamStart.leadingWhitespacePosition(visiblePos.affinity()); - } - - // trailing whitespace is only considered for smart delete if there is no leading - // whitespace, as in the case where you double-click the first word of a paragraph. - if (!skipSmartDelete && !hasLeadingWhitespaceBeforeAdjustment && m_downstreamEnd.trailingWhitespacePosition(VP_DEFAULT_AFFINITY, true).isNotNull()) { - // Expand out one character downstream for smart delete and recalculate - // positions based on this change. - pos = VisiblePosition(m_downstreamEnd, VP_DEFAULT_AFFINITY).next().deepEquivalent(); - m_upstreamEnd = pos.upstream(); - m_downstreamEnd = pos.downstream(); - m_trailingWhitespace = m_downstreamEnd.trailingWhitespacePosition(VP_DEFAULT_AFFINITY); - } - } - - // We must pass the positions through rangeCompliantEquivalent, since some editing positions - // that appear inside their nodes aren't really inside them. [hr, 0] is one example. - // FIXME: rangeComplaintEquivalent should eventually be moved into enclosing element getters - // like the one below, since editing functions should obviously accept editing positions. - // FIXME: Passing false to enclosingNodeOfType tells it that it's OK to return a non-editable - // node. This was done to match existing behavior, but it seems wrong. - m_startBlock = enclosingNodeOfType(rangeCompliantEquivalent(m_downstreamStart), &isBlock, false); - m_endBlock = enclosingNodeOfType(rangeCompliantEquivalent(m_upstreamEnd), &isBlock, false); -} - -void DeleteSelectionCommand::saveTypingStyleState() -{ - // A common case is deleting characters that are all from the same text node. In - // that case, the style at the start of the selection before deletion will be the - // same as the style at the start of the selection after deletion (since those - // two positions will be identical). Therefore there is no need to save the - // 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()) - return; - - // Figure out the typing style in effect before the delete is done. - RefPtr<CSSComputedStyleDeclaration> computedStyle = positionBeforeTabSpan(m_selectionToDelete.start()).computedStyle(); - m_typingStyle = computedStyle->copyInheritableProperties(); - - // 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())) { - computedStyle = m_selectionToDelete.end().computedStyle(); - m_deleteIntoBlockquoteStyle = computedStyle->copyInheritableProperties(); - } else - m_deleteIntoBlockquoteStyle = 0; -} - -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(); - if (isBROnLineByItself) { - removeNode(m_downstreamStart.node()); - return true; - } - - // Not a special-case delete per se, but we can detect that the merging of content between blocks - // should not be done. - if (upstreamStartIsBR && downstreamStartIsBR) { - m_mergeBlocksAfterDelete = false; - m_endingPosition = m_downstreamEnd; - } - - return false; -} - -static void updatePositionForNodeRemoval(Node* node, Position& position) -{ - if (position.isNull()) - return; - if (node->parent() == position.node() && node->nodeIndex() < (unsigned)position.offset()) - position = Position(position.node(), position.offset() - 1); - if (position.node() == node || position.node()->isDescendantOf(node)) - position = positionBeforeNode(node); -} - -void DeleteSelectionCommand::removeNode(Node *node) -{ - if (!node) - return; - - if (m_startRoot != m_endRoot && !(node->isDescendantOf(m_startRoot.get()) && node->isDescendantOf(m_endRoot.get()))) { - // If a node is not in both the start and end editable roots, remove it only if its inside an editable region. - if (!node->parentNode()->isContentEditable()) { - // Don't remove non-editable atomic nodes. - if (!node->firstChild()) - return; - // Search this non-editable region for editable regions to empty. - RefPtr<Node> child = node->firstChild(); - while (child) { - RefPtr<Node> nextChild = child->nextSibling(); - removeNode(child.get()); - // Bail if nextChild is no longer node's child. - if (nextChild && nextChild->parentNode() != node) - return; - child = nextChild; - } - - // Don't remove editable regions that are inside non-editable ones, just clear them. - return; - } - } - - if (isTableStructureNode(node) || node == node->rootEditableElement()) { - // Do not remove an element of table structure; remove its contents. - // Likewise for the root editable element. - Node *child = node->firstChild(); - while (child) { - Node *remove = child; - child = child->nextSibling(); - removeNode(remove); - } - - // make sure empty cell has some height - updateLayout(); - RenderObject *r = node->renderer(); - if (r && r->isTableCell() && r->contentHeight() <= 0) - insertBlockPlaceholder(Position(node,0)); - return; - } - - if (node == m_startBlock && !isEndOfBlock(VisiblePosition(m_startBlock.get(), 0, DOWNSTREAM).previous())) - m_needPlaceholder = true; - else if (node == m_endBlock && !isStartOfBlock(VisiblePosition(m_endBlock.get(), maxDeepOffset(m_endBlock.get()), DOWNSTREAM).next())) - m_needPlaceholder = true; - - // FIXME: Update the endpoints of the range being deleted. - updatePositionForNodeRemoval(node, m_endingPosition); - updatePositionForNodeRemoval(node, m_leadingWhitespace); - updatePositionForNodeRemoval(node, m_trailingWhitespace); - - CompositeEditCommand::removeNode(node); -} - - -void updatePositionForTextRemoval(Node* node, int offset, int count, Position& position) -{ - if (position.node() == node) { - if (position.offset() > offset + count) - position = Position(position.node(), position.offset() - count); - else if (position.offset() > offset) - position = Position(position.node(), offset); - } -} - -void DeleteSelectionCommand::deleteTextFromNode(Text *node, int offset, int count) -{ - // FIXME: Update the endpoints of the range being deleted. - updatePositionForTextRemoval(node, offset, count, m_endingPosition); - updatePositionForTextRemoval(node, offset, count, m_leadingWhitespace); - updatePositionForTextRemoval(node, offset, count, m_trailingWhitespace); - - CompositeEditCommand::deleteTextFromNode(node, offset, count); -} - -void DeleteSelectionCommand::handleGeneralDelete() -{ - int startOffset = m_upstreamStart.offset(); - Node* startNode = m_upstreamStart.node(); - - // 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)) { - startOffset = 0; - startNode = startNode->traverseNextNode(); - } - - if (startOffset >= caretMaxOffset(startNode) && startNode->isTextNode()) { - Text *text = static_cast<Text *>(startNode); - if (text->length() > (unsigned)caretMaxOffset(startNode)) - deleteTextFromNode(text, caretMaxOffset(startNode), text->length() - caretMaxOffset(startNode)); - } - - if (startOffset >= maxDeepOffset(startNode)) { - startNode = startNode->traverseNextSibling(); - startOffset = 0; - } - - // Done adjusting the start. See if we're all done. - if (!startNode) - return; - - if (startNode == m_downstreamEnd.node()) { - // The selection to delete is all in one node. - if (!startNode->renderer() || - (startOffset == 0 && m_downstreamEnd.offset() >= maxDeepOffset(startNode))) { - // just delete - removeNode(startNode); - } else if (m_downstreamEnd.offset() - startOffset > 0) { - if (startNode->isTextNode()) { - // in a text node that needs to be trimmed - Text *text = static_cast<Text *>(startNode); - deleteTextFromNode(text, startOffset, m_downstreamEnd.offset() - startOffset); - } else { - removeChildrenInRange(startNode, startOffset, m_downstreamEnd.offset()); - m_endingPosition = m_upstreamStart; - } - } - } - else { - bool startNodeWasDescendantOfEndNode = m_upstreamStart.node()->isDescendantOf(m_downstreamEnd.node()); - // The selection to delete spans more than one node. - RefPtr<Node> node(startNode); - - if (startOffset > 0) { - if (startNode->isTextNode()) { - // in a text node that needs to be trimmed - Text *text = static_cast<Text *>(node.get()); - deleteTextFromNode(text, startOffset, text->length() - startOffset); - node = node->traverseNextNode(); - } else { - node = startNode->childNode(startOffset); - } - } - - // handle deleting all nodes that are completely selected - while (node && node != m_downstreamEnd.node()) { - if (Range::compareBoundaryPoints(Position(node.get(), 0), m_downstreamEnd) >= 0) { - // traverseNextSibling just blew past the end position, so stop deleting - node = 0; - } else if (!m_downstreamEnd.node()->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()) { - ASSERT(node->nodeIndex() < (unsigned)m_downstreamEnd.offset()); - m_downstreamEnd = Position(m_downstreamEnd.node(), m_downstreamEnd.offset() - 1); - } - removeNode(node.get()); - node = nextNode.get(); - } else { - Node* n = node->lastDescendant(); - if (m_downstreamEnd.node() == n && m_downstreamEnd.offset() >= caretMaxOffset(n)) { - removeNode(node.get()); - node = 0; - } else - node = node->traverseNextNode(); - } - } - - if (m_downstreamEnd.node() != startNode && !m_upstreamStart.node()->isDescendantOf(m_downstreamEnd.node()) && m_downstreamEnd.node()->inDocument() && m_downstreamEnd.offset() >= caretMinOffset(m_downstreamEnd.node())) { - if (m_downstreamEnd.offset() >= maxDeepOffset(m_downstreamEnd.node()) && !canHaveChildrenForEditing(m_downstreamEnd.node())) { - // The node itself is fully selected, not just its contents. Delete it. - removeNode(m_downstreamEnd.node()); - } else { - if (m_downstreamEnd.node()->isTextNode()) { - // in a text node that needs to be trimmed - Text *text = static_cast<Text *>(m_downstreamEnd.node()); - if (m_downstreamEnd.offset() > 0) { - deleteTextFromNode(text, 0, m_downstreamEnd.offset()); - m_downstreamEnd = Position(text, 0); - } - // 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() - // 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())) { - int offset = 0; - if (m_upstreamStart.node()->isDescendantOf(m_downstreamEnd.node())) { - Node *n = m_upstreamStart.node(); - while (n && n->parentNode() != m_downstreamEnd.node()) - n = n->parentNode(); - if (n) - offset = n->nodeIndex() + 1; - } - removeChildrenInRange(m_downstreamEnd.node(), offset, m_downstreamEnd.offset()); - m_downstreamEnd = Position(m_downstreamEnd.node(), offset); - } - } - } - } -} - -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()) { - Text* textNode = static_cast<Text*>(m_leadingWhitespace.node()); - ASSERT(!textNode->renderer() || textNode->renderer()->style()->collapseWhiteSpace()); - replaceTextInNode(textNode, m_leadingWhitespace.offset(), 1, nonBreakingSpaceString()); - } - if (m_trailingWhitespace.isNotNull() && !m_trailingWhitespace.isRenderedCharacter()) { - Text* textNode = static_cast<Text*>(m_trailingWhitespace.node()); - ASSERT(!textNode->renderer() ||textNode->renderer()->style()->collapseWhiteSpace()); - replaceTextInNode(textNode, m_trailingWhitespace.offset(), 1, nonBreakingSpaceString()); - } -} - -// If a selection starts in one block and ends in another, we have to merge to bring content before the -// start together with content after the end. -void DeleteSelectionCommand::mergeParagraphs() -{ - if (!m_mergeBlocksAfterDelete) - return; - - // 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()) - return; - - // FIXME: The deletion algorithm shouldn't let this happen. - if (Range::compareBoundaryPoints(m_upstreamStart, m_downstreamEnd) > 0) - return; - - // FIXME: Merging will always be unnecessary in this case, but we really bail here because this is a case where - // deletion commonly fails to adjust its endpoints, which would cause the visible position comparison below to false negative. - if (m_endBlock == m_startBlock) - return; - - VisiblePosition startOfParagraphToMove(m_downstreamEnd); - VisiblePosition mergeDestination(m_upstreamStart); - - // 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())); - 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())) { - insertNodeAt(createBreakElement(document()).get(), m_upstreamStart); - mergeDestination = VisiblePosition(m_upstreamStart); - } - - if (mergeDestination == startOfParagraphToMove) - return; - - VisiblePosition endOfParagraphToMove = endOfParagraph(startOfParagraphToMove); - - if (mergeDestination == endOfParagraphToMove) - return; - - // The rule for merging into an empty block is: only do so if its farther to the right. - // FIXME: Consider RTL. - // FIXME: handleSpecialCaseBRDelete prevents us from getting here in a case like <ul><li>foo<br><br></li></ul>^foo - if (isStartOfParagraph(mergeDestination) && startOfParagraphToMove.caretRect().x() > mergeDestination.caretRect().x()) { - ASSERT(mergeDestination.deepEquivalent().downstream().node()->hasTagName(brTag)); - removeNodeAndPruneAncestors(mergeDestination.deepEquivalent().downstream().node()); - m_endingPosition = startOfParagraphToMove.deepEquivalent(); - return; - } - - RefPtr<Range> range = Range::create(document(), rangeCompliantEquivalent(startOfParagraphToMove.deepEquivalent()), rangeCompliantEquivalent(endOfParagraphToMove.deepEquivalent())); - RefPtr<Range> rangeToBeReplaced = Range::create(document(), rangeCompliantEquivalent(mergeDestination.deepEquivalent()), rangeCompliantEquivalent(mergeDestination.deepEquivalent())); - if (!document()->frame()->editor()->client()->shouldMoveRangeAfterDelete(range.get(), rangeToBeReplaced.get())) - return; - - // moveParagraphs will insert placeholders if it removes blocks that would require their use, don't let block - // removals that it does cause the insertion of *another* placeholder. - bool needPlaceholder = m_needPlaceholder; - moveParagraph(startOfParagraphToMove, endOfParagraphToMove, mergeDestination); - m_needPlaceholder = needPlaceholder; - // The endingPosition was likely clobbered by the move, so recompute it (moveParagraph selects the moved paragraph). - m_endingPosition = endingSelection().start(); -} - -void DeleteSelectionCommand::removePreviouslySelectedEmptyTableRows() -{ - if (m_endTableRow && m_endTableRow->inDocument() && m_endTableRow != m_startTableRow) { - Node* row = m_endTableRow->previousSibling(); - while (row && row != m_startTableRow) { - RefPtr<Node> previousRow = row->previousSibling(); - if (isTableRowEmpty(row)) - // Use a raw removeNode, instead of DeleteSelectionCommand's, because - // that won't remove rows, it only empties them in preparation for this function. - CompositeEditCommand::removeNode(row); - row = previousRow.get(); - } - } - - // Remove empty rows after the start row. - if (m_startTableRow && m_startTableRow->inDocument() && m_startTableRow != m_endTableRow) { - Node* row = m_startTableRow->nextSibling(); - while (row && row != m_endTableRow) { - RefPtr<Node> nextRow = row->nextSibling(); - if (isTableRowEmpty(row)) - CompositeEditCommand::removeNode(row); - row = nextRow.get(); - } - } - - 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())) { - // 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. - CompositeEditCommand::removeNode(m_endTableRow.get()); - } - } -} - -void DeleteSelectionCommand::calculateTypingStyleAfterDelete() -{ - if (!m_typingStyle) - return; - - // Compute the difference between the style before the delete and the style now - // after the delete has been done. Set this style on the frame, so other editing - // commands being composed with this one will work, and also cache it on the command, - // so the Frame::appliedEditing can set it after the whole composite command - // 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())) - m_typingStyle = m_deleteIntoBlockquoteStyle; - m_deleteIntoBlockquoteStyle = 0; - - RefPtr<CSSComputedStyleDeclaration> endingStyle = computedStyle(m_endingPosition.node()); - endingStyle->diff(m_typingStyle.get()); - if (!m_typingStyle->length()) - m_typingStyle = 0; - VisiblePosition visibleEnd(m_endingPosition); - if (m_typingStyle && - isStartOfParagraph(visibleEnd) && - isEndOfParagraph(visibleEnd) && - lineBreakExistsAtPosition(visibleEnd)) { - // Apply style to the placeholder that is now holding open the empty paragraph. - // This makes sure that the paragraph has the right height, and that the paragraph - // takes on the right style and retains it even if you move the selection away and - // then move it back (which will clear typing style). - - setEndingSelection(visibleEnd); - applyStyle(m_typingStyle.get(), EditActionUnspecified); - // applyStyle can destroy the placeholder that was at m_endingPosition if it needs to - // move it, but it will set an endingSelection() at [movedPlaceholder, 0] if it does so. - m_endingPosition = endingSelection().start(); - m_typingStyle = 0; - } - // This is where we've deleted all traces of a style but not a whole paragraph (that's handled above). - // In this case if we start typing, the new characters should have the same style as the just deleted ones, - // but, if we change the selection, come back and start typing that style should be lost. Also see - // preserveTypingStyle() below. - document()->frame()->setTypingStyle(m_typingStyle.get()); -} - -void DeleteSelectionCommand::clearTransientState() -{ - m_selectionToDelete = Selection(); - m_upstreamStart.clear(); - m_downstreamStart.clear(); - m_upstreamEnd.clear(); - m_downstreamEnd.clear(); - m_endingPosition.clear(); - m_leadingWhitespace.clear(); - m_trailingWhitespace.clear(); -} - -void DeleteSelectionCommand::saveFullySelectedAnchor() -{ - // If deleting an anchor element, save it away so that it can be restored - // when the user begins entering text. - - Position start = m_selectionToDelete.start(); - Node* startAnchor = enclosingNodeWithTag(start.downstream(), aTag); - if (!startAnchor) - return; - - Position end = m_selectionToDelete.end(); - Node* endAnchor = enclosingNodeWithTag(end.upstream(), aTag); - if (startAnchor != endAnchor) - return; - - VisiblePosition visibleStart(m_selectionToDelete.visibleStart()); - VisiblePosition visibleEnd(m_selectionToDelete.visibleEnd()); - - Node* beforeStartAnchor = enclosingNodeWithTag(visibleStart.previous().deepEquivalent().downstream(), aTag); - Node* afterEndAnchor = enclosingNodeWithTag(visibleEnd.next().deepEquivalent().upstream(), aTag); - - if (startAnchor && startAnchor == endAnchor && startAnchor != beforeStartAnchor && endAnchor != afterEndAnchor) - document()->frame()->editor()->setRemovedAnchor(startAnchor->cloneNode(false)); -} - -void DeleteSelectionCommand::doApply() -{ - // If selection has not been set to a custom selection when the command was created, - // use the current ending selection. - if (!m_hasSelectionToDelete) - m_selectionToDelete = endingSelection(); - - if (!m_selectionToDelete.isRange()) - return; - - // 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* ancestorNode = startNode ? startNode->shadowAncestorNode() : 0; - if (ancestorNode && ancestorNode->hasTagName(inputTag) - && static_cast<HTMLInputElement*>(ancestorNode)->isTextField() - && ancestorNode->focused()) - document()->frame()->textWillBeDeletedInTextField(static_cast<Element*>(ancestorNode)); - } - - // save this to later make the selection with - EAffinity affinity = m_selectionToDelete.affinity(); - - Position downstreamEnd = m_selectionToDelete.end().downstream(); - m_needPlaceholder = isStartOfParagraph(m_selectionToDelete.visibleStart()) && - isEndOfParagraph(m_selectionToDelete.visibleEnd()) && - !lineBreakExistsAtPosition(m_selectionToDelete.visibleEnd()); - if (m_needPlaceholder) { - // Don't need a placeholder when deleting a selection that starts just before a table - // 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)) - m_needPlaceholder = false; - } - - - // set up our state - initializePositionData(); - - // Delete any text that may hinder our ability to fixup whitespace after the delete - deleteInsignificantTextDownstream(m_trailingWhitespace); - - saveTypingStyleState(); - - saveFullySelectedAnchor(); - - // deleting just a BR is handled specially, at least because we do not - // want to replace it with a placeholder BR! - if (handleSpecialCaseBRDelete()) { - calculateTypingStyleAfterDelete(); - setEndingSelection(Selection(m_endingPosition, affinity)); - clearTransientState(); - rebalanceWhitespace(); - return; - } - - handleGeneralDelete(); - - fixupWhitespace(); - - mergeParagraphs(); - - removePreviouslySelectedEmptyTableRows(); - - RefPtr<Node> placeholder = m_needPlaceholder ? createBreakElement(document()).get() : 0; - - if (placeholder) - insertNodeAt(placeholder.get(), m_endingPosition); - - rebalanceWhitespaceAt(m_endingPosition); - - calculateTypingStyleAfterDelete(); - - setEndingSelection(Selection(m_endingPosition, affinity)); - clearTransientState(); -} - -EditAction DeleteSelectionCommand::editingAction() const -{ - // Note that DeleteSelectionCommand is also used when the user presses the Delete key, - // but in that case there's a TypingCommand that supplies the editingAction(), so - // the Undo menu correctly shows "Undo Typing" - return EditActionCut; -} - -// Normally deletion doesn't preserve the typing style that was present before it. For example, -// type a character, Bold, then delete the character and start typing. The Bold typing style shouldn't -// stick around. Deletion should preserve a typing style that *it* sets, however. -bool DeleteSelectionCommand::preservesTypingStyle() const -{ - return m_typingStyle; -} - -} // namespace WebCore diff --git a/WebCore/editing/DeleteSelectionCommand.h b/WebCore/editing/DeleteSelectionCommand.h deleted file mode 100644 index 4a1a817..0000000 --- a/WebCore/editing/DeleteSelectionCommand.h +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright (C) 2005, 2006, 2008 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 DeleteSelectionCommand_h -#define DeleteSelectionCommand_h - -#include "CompositeEditCommand.h" - -namespace WebCore { - -class DeleteSelectionCommand : public CompositeEditCommand { -public: - static PassRefPtr<DeleteSelectionCommand> create(Document* document, bool smartDelete = false, bool mergeBlocksAfterDelete = true, bool replace = false, bool expandForSpecialElements = false) - { - return adoptRef(new DeleteSelectionCommand(document, smartDelete, mergeBlocksAfterDelete, replace, expandForSpecialElements)); - } - static PassRefPtr<DeleteSelectionCommand> create(const Selection& selection, bool smartDelete = false, bool mergeBlocksAfterDelete = true, bool replace = false, bool expandForSpecialElements = false) - { - return adoptRef(new DeleteSelectionCommand(selection, smartDelete, mergeBlocksAfterDelete, replace, expandForSpecialElements)); - } - -private: - DeleteSelectionCommand(Document*, bool smartDelete, bool mergeBlocksAfterDelete, bool replace, bool expandForSpecialElements); - DeleteSelectionCommand(const Selection&, bool smartDelete, bool mergeBlocksAfterDelete, bool replace, bool expandForSpecialElements); - - virtual void doApply(); - virtual EditAction editingAction() const; - - virtual bool preservesTypingStyle() const; - - void initializeStartEnd(Position&, Position&); - void initializePositionData(); - void saveTypingStyleState(); - void saveFullySelectedAnchor(); - void insertPlaceholderForAncestorBlockContent(); - bool handleSpecialCaseBRDelete(); - void handleGeneralDelete(); - void fixupWhitespace(); - void mergeParagraphs(); - void removePreviouslySelectedEmptyTableRows(); - void calculateEndingPosition(); - void calculateTypingStyleAfterDelete(); - void clearTransientState(); - virtual void removeNode(Node*); - virtual void deleteTextFromNode(Text*, int, int); - - bool m_hasSelectionToDelete; - bool m_smartDelete; - bool m_mergeBlocksAfterDelete; - bool m_needPlaceholder; - bool m_replace; - bool m_expandForSpecialElements; - - // This data is transient and should be cleared at the end of the doApply function. - Selection m_selectionToDelete; - Position m_upstreamStart; - Position m_downstreamStart; - Position m_upstreamEnd; - Position m_downstreamEnd; - Position m_endingPosition; - Position m_leadingWhitespace; - Position m_trailingWhitespace; - RefPtr<Node> m_startBlock; - RefPtr<Node> m_endBlock; - RefPtr<CSSMutableStyleDeclaration> m_typingStyle; - RefPtr<CSSMutableStyleDeclaration> m_deleteIntoBlockquoteStyle; - RefPtr<Node> m_startRoot; - RefPtr<Node> m_endRoot; - RefPtr<Node> m_startTableRow; - RefPtr<Node> m_endTableRow; -}; - -} // namespace WebCore - -#endif // DeleteSelectionCommand_h diff --git a/WebCore/editing/EditAction.h b/WebCore/editing/EditAction.h deleted file mode 100644 index 8046f3c..0000000 --- a/WebCore/editing/EditAction.h +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (C) 2004 Apple Computer, 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 EditAction_h -#define EditAction_h - -namespace WebCore { - typedef enum { - EditActionUnspecified, - EditActionSetColor, - EditActionSetBackgroundColor, - EditActionTurnOffKerning, - EditActionTightenKerning, - EditActionLoosenKerning, - EditActionUseStandardKerning, - EditActionTurnOffLigatures, - EditActionUseStandardLigatures, - EditActionUseAllLigatures, - EditActionRaiseBaseline, - EditActionLowerBaseline, - EditActionSetTraditionalCharacterShape, - EditActionSetFont, - EditActionChangeAttributes, - EditActionAlignLeft, - EditActionAlignRight, - EditActionCenter, - EditActionJustify, - EditActionSetWritingDirection, - EditActionSubscript, - EditActionSuperscript, - EditActionUnderline, - EditActionOutline, - EditActionUnscript, - EditActionDrag, - EditActionCut, - EditActionPaste, - EditActionPasteFont, - EditActionPasteRuler, - EditActionTyping, - EditActionCreateLink, - EditActionUnlink, - EditActionFormatBlock, - EditActionInsertList, - EditActionIndent, - EditActionOutdent - } EditAction; -} - -#endif diff --git a/WebCore/editing/EditCommand.cpp b/WebCore/editing/EditCommand.cpp deleted file mode 100644 index f9f8b80..0000000 --- a/WebCore/editing/EditCommand.cpp +++ /dev/null @@ -1,237 +0,0 @@ -/* - * Copyright (C) 2005, 2006, 2007 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 "EditCommand.h" - -#include "CompositeEditCommand.h" -#include "CSSComputedStyleDeclaration.h" -#include "CSSMutableStyleDeclaration.h" -#include "DeleteButtonController.h" -#include "Document.h" -#include "Editor.h" -#include "Element.h" -#include "EventNames.h" -#include "Frame.h" -#include "SelectionController.h" -#include "VisiblePosition.h" -#include "htmlediting.h" - -namespace WebCore { - -EditCommand::EditCommand(Document* document) - : m_document(document) - , m_parent(0) -{ - ASSERT(m_document); - ASSERT(m_document->frame()); - DeleteButtonController* deleteButton = m_document->frame()->editor()->deleteButtonController(); - setStartingSelection(avoidIntersectionWithNode(m_document->frame()->selection()->selection(), deleteButton ? deleteButton->containerElement() : 0)); - setEndingSelection(m_startingSelection); -} - -EditCommand::~EditCommand() -{ -} - -void EditCommand::apply() -{ - ASSERT(m_document); - ASSERT(m_document->frame()); - - Frame* frame = m_document->frame(); - - if (!m_parent) { - if (!endingSelection().isContentRichlyEditable()) { - switch (editingAction()) { - case EditActionTyping: - case EditActionPaste: - case EditActionDrag: - case EditActionSetWritingDirection: - case EditActionCut: - case EditActionUnspecified: - break; - default: - ASSERT_NOT_REACHED(); - return; - } - } - } - - // Changes to the document may have been made since the last editing operation that - // require a layout, as in <rdar://problem/5658603>. Low level operations, like - // RemoveNodeCommand, don't require a layout because the high level operations that - // use them perform one if one is necessary (like for the creation of VisiblePositions). - if (!m_parent) - updateLayout(); - - // All high level commands, and all commands that a TypingCommand spawns, except for - // text insertions, which should restore a removed anchor, should clear it. - if (!m_parent && !isTypingCommand()) - frame->editor()->setRemovedAnchor(0); - if (m_parent && m_parent->isTypingCommand() && !isInsertTextCommand()) - frame->editor()->setRemovedAnchor(0); - - DeleteButtonController* deleteButtonController = frame->editor()->deleteButtonController(); - deleteButtonController->disable(); - doApply(); - deleteButtonController->enable(); - - if (!m_parent) { - updateLayout(); - frame->editor()->appliedEditing(this); - } -} - -void EditCommand::unapply() -{ - ASSERT(m_document); - ASSERT(m_document->frame()); - - Frame* frame = m_document->frame(); - - // Changes to the document may have been made since the last editing operation that - // require a layout, as in <rdar://problem/5658603>. Low level operations, like - // RemoveNodeCommand, don't require a layout because the high level operations that - // use them perform one if one is necessary (like for the creation of VisiblePositions). - if (!m_parent) - updateLayout(); - - DeleteButtonController* deleteButtonController = frame->editor()->deleteButtonController(); - deleteButtonController->disable(); - doUnapply(); - deleteButtonController->enable(); - - if (!m_parent) { - updateLayout(); - frame->editor()->unappliedEditing(this); - } -} - -void EditCommand::reapply() -{ - ASSERT(m_document); - ASSERT(m_document->frame()); - - Frame* frame = m_document->frame(); - - // Changes to the document may have been made since the last editing operation that - // require a layout, as in <rdar://problem/5658603>. Low level operations, like - // RemoveNodeCommand, don't require a layout because the high level operations that - // use them perform one if one is necessary (like for the creation of VisiblePositions). - if (!m_parent) - updateLayout(); - - DeleteButtonController* deleteButtonController = frame->editor()->deleteButtonController(); - deleteButtonController->disable(); - doReapply(); - deleteButtonController->enable(); - - if (!m_parent) { - updateLayout(); - frame->editor()->reappliedEditing(this); - } -} - -void EditCommand::doReapply() -{ - doApply(); -} - -EditAction EditCommand::editingAction() const -{ - return EditActionUnspecified; -} - -void EditCommand::setStartingSelection(const Selection& s) -{ - Element* root = s.rootEditableElement(); - for (EditCommand* cmd = this; ; cmd = cmd->m_parent) { - cmd->m_startingSelection = s; - cmd->m_startingRootEditableElement = root; - if (!cmd->m_parent || cmd->m_parent->isFirstCommand(cmd)) - break; - } -} - -void EditCommand::setEndingSelection(const Selection &s) -{ - Element* root = s.rootEditableElement(); - for (EditCommand* cmd = this; cmd; cmd = cmd->m_parent) { - cmd->m_endingSelection = s; - cmd->m_endingRootEditableElement = root; - } -} - -bool EditCommand::preservesTypingStyle() const -{ - return false; -} - -bool EditCommand::isInsertTextCommand() const -{ - return false; -} - -bool EditCommand::isTypingCommand() const -{ - return false; -} - -PassRefPtr<CSSMutableStyleDeclaration> EditCommand::styleAtPosition(const Position &pos) -{ - RefPtr<CSSMutableStyleDeclaration> style = positionBeforeTabSpan(pos).computedStyle()->copyInheritableProperties(); - - // FIXME: It seems misleading to also include the typing style when returning the style at some arbitrary - // position in the document. - CSSMutableStyleDeclaration* typingStyle = document()->frame()->typingStyle(); - if (typingStyle) - style->merge(typingStyle); - - return style.release(); -} - -void EditCommand::updateLayout() const -{ - document()->updateLayoutIgnorePendingStylesheets(); -} - -void EditCommand::setParent(CompositeEditCommand* parent) -{ - ASSERT(parent); - ASSERT(!m_parent); - m_parent = parent; - m_startingSelection = parent->m_endingSelection; - m_endingSelection = parent->m_endingSelection; - m_startingRootEditableElement = parent->m_endingRootEditableElement; - m_endingRootEditableElement = parent->m_endingRootEditableElement; -} - -void applyCommand(PassRefPtr<EditCommand> command) -{ - command->apply(); -} - -} // namespace WebCore diff --git a/WebCore/editing/EditCommand.h b/WebCore/editing/EditCommand.h deleted file mode 100644 index 78490f8..0000000 --- a/WebCore/editing/EditCommand.h +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright (C) 2005, 2006, 2008 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 EditCommand_h -#define EditCommand_h - -#include "EditAction.h" -#include "Element.h" -#include "Selection.h" - -namespace WebCore { - -class CompositeEditCommand; -class CSSMutableStyleDeclaration; - -class EditCommand : public RefCounted<EditCommand> { -public: - virtual ~EditCommand(); - - void setParent(CompositeEditCommand*); - - void apply(); - void unapply(); - void reapply(); - - virtual EditAction editingAction() const; - - const Selection& startingSelection() const { return m_startingSelection; } - const Selection& endingSelection() const { return m_endingSelection; } - - Element* startingRootEditableElement() const { return m_startingRootEditableElement.get(); } - Element* endingRootEditableElement() const { return m_endingRootEditableElement.get(); } - - virtual bool isInsertTextCommand() const; - virtual bool isTypingCommand() const; - - virtual bool preservesTypingStyle() const; - -protected: - EditCommand(Document*); - - Document* document() const { return m_document.get(); } - - void setStartingSelection(const Selection&); - void setEndingSelection(const Selection&); - - PassRefPtr<CSSMutableStyleDeclaration> styleAtPosition(const Position&); - void updateLayout() const; - -private: - virtual void doApply() = 0; - virtual void doUnapply() = 0; - virtual void doReapply(); // calls doApply() - - RefPtr<Document> m_document; - Selection m_startingSelection; - Selection m_endingSelection; - RefPtr<Element> m_startingRootEditableElement; - RefPtr<Element> m_endingRootEditableElement; - CompositeEditCommand* m_parent; - - friend void applyCommand(PassRefPtr<EditCommand>); -}; - -class SimpleEditCommand : public EditCommand { -protected: - SimpleEditCommand(Document* document) : EditCommand(document) { } -}; - -void applyCommand(PassRefPtr<EditCommand>); - -} // namespace WebCore - -#endif // EditCommand_h diff --git a/WebCore/editing/Editor.cpp b/WebCore/editing/Editor.cpp deleted file mode 100644 index 1890338..0000000 --- a/WebCore/editing/Editor.cpp +++ /dev/null @@ -1,2074 +0,0 @@ -/* - * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. - * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) - * - * 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 "Editor.h" - -#include "AXObjectCache.h" -#include "ApplyStyleCommand.h" -#include "CSSComputedStyleDeclaration.h" -#include "CSSProperty.h" -#include "CSSPropertyNames.h" -#include "ClipboardEvent.h" -#include "DeleteButtonController.h" -#include "DeleteSelectionCommand.h" -#include "DocLoader.h" -#include "DocumentFragment.h" -#include "EditorClient.h" -#include "EventHandler.h" -#include "EventNames.h" -#include "FocusController.h" -#include "Frame.h" -#include "FrameTree.h" -#include "FrameView.h" -#include "HTMLInputElement.h" -#include "HTMLTextAreaElement.h" -#include "HitTestResult.h" -#include "IndentOutdentCommand.h" -#include "InsertListCommand.h" -#include "KeyboardEvent.h" -#include "ModifySelectionListLevel.h" -#include "Page.h" -#include "Pasteboard.h" -#include "RemoveFormatCommand.h" -#include "RenderBlock.h" -#include "RenderPart.h" -#include "ReplaceSelectionCommand.h" -#include "Sound.h" -#include "Text.h" -#include "TextIterator.h" -#include "TypingCommand.h" -#include "htmlediting.h" -#include "markup.h" -#include "visible_units.h" - -namespace WebCore { - -using namespace std; -using namespace HTMLNames; - -// When an event handler has moved the selection outside of a text control -// we should use the target control's selection for this editing operation. -Selection Editor::selectionForCommand(Event* event) -{ - Selection selection = m_frame->selection()->selection(); - if (!event) - return selection; - // 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(); - if (target && (!selectionStart || target->shadowAncestorNode() != selectionStart->shadowAncestorNode())) { - if (target->hasTagName(inputTag) && static_cast<HTMLInputElement*>(target)->isTextField()) - return static_cast<HTMLInputElement*>(target)->selection(); - if (target->hasTagName(textareaTag)) - return static_cast<HTMLTextAreaElement*>(target)->selection(); - } - return selection; -} - -EditorClient* Editor::client() const -{ - if (Page* page = m_frame->page()) - return page->editorClient(); - return 0; -} - -void Editor::handleKeyboardEvent(KeyboardEvent* event) -{ - if (EditorClient* c = client()) - if (selectionForCommand(event).isContentEditable()) - c->handleKeyboardEvent(event); -} - -void Editor::handleInputMethodKeydown(KeyboardEvent* event) -{ - if (EditorClient* c = client()) - if (selectionForCommand(event).isContentEditable()) - c->handleInputMethodKeydown(event); -} - -bool Editor::canEdit() const -{ - return m_frame->selection()->isContentEditable(); -} - -bool Editor::canEditRichly() const -{ - return m_frame->selection()->isContentRichlyEditable(); -} - -// WinIE uses onbeforecut and onbeforepaste to enables the cut and paste menu items. They -// also send onbeforecopy, apparently for symmetry, but it doesn't affect the menu items. -// We need to use onbeforecopy as a real menu enabler because we allow elements that are not -// normally selectable to implement copy/paste (like divs, or a document body). - -bool Editor::canDHTMLCut() -{ - return !m_frame->selection()->isInPasswordField() && !dispatchCPPEvent(eventNames().beforecutEvent, ClipboardNumb); -} - -bool Editor::canDHTMLCopy() -{ - return !m_frame->selection()->isInPasswordField() && !dispatchCPPEvent(eventNames().beforecopyEvent, ClipboardNumb); -} - -bool Editor::canDHTMLPaste() -{ - return !dispatchCPPEvent(eventNames().beforepasteEvent, ClipboardNumb); -} - -bool Editor::canCut() const -{ - return canCopy() && canDelete(); -} - -static HTMLImageElement* imageElementFromImageDocument(Document* document) -{ - if (!document) - return 0; - if (!document->isImageDocument()) - return 0; - - HTMLElement* body = document->body(); - if (!body) - return 0; - - Node* node = body->firstChild(); - if (!node) - return 0; - if (!node->hasTagName(imgTag)) - return 0; - return static_cast<HTMLImageElement*>(node); -} - -bool Editor::canCopy() const -{ - if (imageElementFromImageDocument(m_frame->document())) - return true; - SelectionController* selection = m_frame->selection(); - return selection->isRange() && !selection->isInPasswordField(); -} - -bool Editor::canPaste() const -{ - return canEdit(); -} - -bool Editor::canDelete() const -{ - SelectionController* selection = m_frame->selection(); - return selection->isRange() && selection->isContentEditable(); -} - -bool Editor::canDeleteRange(Range* range) const -{ - ExceptionCode ec = 0; - Node* startContainer = range->startContainer(ec); - Node* endContainer = range->endContainer(ec); - if (!startContainer || !endContainer) - return false; - - if (!startContainer->isContentEditable() || !endContainer->isContentEditable()) - return false; - - if (range->collapsed(ec)) { - VisiblePosition start(startContainer, range->startOffset(ec), 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()) - return false; - } - return true; -} - -bool Editor::smartInsertDeleteEnabled() -{ - return client() && client()->smartInsertDeleteEnabled(); -} - -bool Editor::canSmartCopyOrDelete() -{ - return client() && client()->smartInsertDeleteEnabled() && m_frame->selectionGranularity() == WordGranularity; -} - -bool Editor::deleteWithDirection(SelectionController::EDirection direction, TextGranularity granularity, bool killRing, bool isTypingAction) -{ - // Delete the selection, if there is one. - // If not, make a selection using the passed-in direction and granularity. - - if (!canEdit()) - return false; - - if (m_frame->selection()->isRange()) { - if (killRing) - addToKillRing(selectedRange().get(), false); - if (isTypingAction) { - if (m_frame->document()) { - TypingCommand::deleteKeyPressed(m_frame->document(), canSmartCopyOrDelete(), granularity); - revealSelectionAfterEditingOperation(); - } - } else { - deleteSelectionWithSmartDelete(canSmartCopyOrDelete()); - // Implicitly calls revealSelectionAfterEditingOperation(). - } - } else { - SelectionController selectionToDelete; - selectionToDelete.setSelection(m_frame->selection()->selection()); - selectionToDelete.modify(SelectionController::EXTEND, direction, granularity); - if (killRing && selectionToDelete.isCaret() && granularity != CharacterGranularity) - selectionToDelete.modify(SelectionController::EXTEND, direction, CharacterGranularity); - - RefPtr<Range> range = selectionToDelete.toRange(); - - if (killRing) - addToKillRing(range.get(), false); - - if (!m_frame->selection()->setSelectedRange(range.get(), DOWNSTREAM, (granularity != CharacterGranularity))) - return true; - - switch (direction) { - case SelectionController::FORWARD: - case SelectionController::RIGHT: - if (m_frame->document()) - TypingCommand::forwardDeleteKeyPressed(m_frame->document(), false, granularity); - break; - case SelectionController::BACKWARD: - case SelectionController::LEFT: - if (m_frame->document()) - TypingCommand::deleteKeyPressed(m_frame->document(), false, granularity); - break; - } - revealSelectionAfterEditingOperation(); - } - - // clear the "start new kill ring sequence" setting, because it was set to true - // when the selection was updated by deleting the range - if (killRing) - setStartNewKillRingSequence(false); - - return true; -} - -void Editor::deleteSelectionWithSmartDelete(bool smartDelete) -{ - if (m_frame->selection()->isNone()) - return; - - applyCommand(DeleteSelectionCommand::create(m_frame->document(), smartDelete)); -} - -void Editor::pasteAsPlainTextWithPasteboard(Pasteboard* pasteboard) -{ - String text = pasteboard->plainText(m_frame); - if (client() && client()->shouldInsertText(text, selectedRange().get(), EditorInsertActionPasted)) - replaceSelectionWithText(text, false, canSmartReplaceWithPasteboard(pasteboard)); -} - -void Editor::pasteWithPasteboard(Pasteboard* pasteboard, bool allowPlainText) -{ - RefPtr<Range> range = selectedRange(); - bool chosePlainText; - RefPtr<DocumentFragment> fragment = pasteboard->documentFragment(m_frame, range, allowPlainText, chosePlainText); - if (fragment && shouldInsertFragment(fragment, range, EditorInsertActionPasted)) - replaceSelectionWithFragment(fragment, false, canSmartReplaceWithPasteboard(pasteboard), chosePlainText); -} - -bool Editor::canSmartReplaceWithPasteboard(Pasteboard* pasteboard) -{ - return client() && client()->smartInsertDeleteEnabled() && pasteboard->canSmartReplace(); -} - -bool Editor::shouldInsertFragment(PassRefPtr<DocumentFragment> fragment, PassRefPtr<Range> replacingDOMRange, EditorInsertAction givenAction) -{ - if (!client()) - return false; - - Node* child = fragment->firstChild(); - if (child && fragment->lastChild() == child && child->isCharacterDataNode()) - return client()->shouldInsertText(static_cast<CharacterData*>(child)->data(), replacingDOMRange.get(), givenAction); - - return client()->shouldInsertNode(fragment.get(), replacingDOMRange.get(), givenAction); -} - -void Editor::replaceSelectionWithFragment(PassRefPtr<DocumentFragment> fragment, bool selectReplacement, bool smartReplace, bool matchStyle) -{ - if (m_frame->selection()->isNone() || !fragment) - return; - - applyCommand(ReplaceSelectionCommand::create(m_frame->document(), fragment, selectReplacement, smartReplace, matchStyle)); - revealSelectionAfterEditingOperation(); -} - -void Editor::replaceSelectionWithText(const String& text, bool selectReplacement, bool smartReplace) -{ - replaceSelectionWithFragment(createFragmentFromText(selectedRange().get(), text), selectReplacement, smartReplace, true); -} - -PassRefPtr<Range> Editor::selectedRange() -{ - if (!m_frame) - return 0; - return m_frame->selection()->toRange(); -} - -bool Editor::shouldDeleteRange(Range* range) const -{ - ExceptionCode ec; - if (!range || range->collapsed(ec)) - return false; - - if (!canDeleteRange(range)) - return false; - - return client() && client()->shouldDeleteRange(range); -} - -bool Editor::tryDHTMLCopy() -{ - if (m_frame->selection()->isInPasswordField()) - return false; - - // Must be done before oncopy adds types and data to the pboard, - // also done for security, as it erases data from the last copy/paste. - Pasteboard::generalPasteboard()->clear(); - - return !dispatchCPPEvent(eventNames().copyEvent, ClipboardWritable); -} - -bool Editor::tryDHTMLCut() -{ - if (m_frame->selection()->isInPasswordField()) - return false; - - // Must be done before oncut adds types and data to the pboard, - // also done for security, as it erases data from the last copy/paste. - Pasteboard::generalPasteboard()->clear(); - - return !dispatchCPPEvent(eventNames().cutEvent, ClipboardWritable); -} - -bool Editor::tryDHTMLPaste() -{ - return !dispatchCPPEvent(eventNames().pasteEvent, ClipboardReadable); -} - -void Editor::writeSelectionToPasteboard(Pasteboard* pasteboard) -{ - pasteboard->writeSelection(selectedRange().get(), canSmartCopyOrDelete(), m_frame); -} - -bool Editor::shouldInsertText(const String& text, Range* range, EditorInsertAction action) const -{ - return client() && client()->shouldInsertText(text, range, action); -} - -bool Editor::shouldShowDeleteInterface(HTMLElement* element) const -{ - return client() && client()->shouldShowDeleteInterface(element); -} - -void Editor::respondToChangedSelection(const Selection& oldSelection) -{ - if (client()) - client()->respondToChangedSelection(); - m_deleteButtonController->respondToChangedSelection(oldSelection); -} - -void Editor::respondToChangedContents(const Selection& endingSelection) -{ - if (AXObjectCache::accessibilityEnabled()) { - Node* node = endingSelection.start().node(); - if (node) - m_frame->document()->axObjectCache()->postNotification(node->renderer(), "AXValueChanged"); - } - - if (client()) - client()->respondToChangedContents(); -} - -const SimpleFontData* Editor::fontForSelection(bool& hasMultipleFonts) const -{ -#if !PLATFORM(QT) - hasMultipleFonts = false; - - if (!m_frame->selection()->isRange()) { - Node* nodeToRemove; - RenderStyle* style = m_frame->styleForSelectionStart(nodeToRemove); // sets nodeToRemove - - const SimpleFontData* result = 0; - if (style) - result = style->font().primaryFont(); - - if (nodeToRemove) { - ExceptionCode ec; - nodeToRemove->remove(ec); - ASSERT(ec == 0); - } - - return result; - } - - const SimpleFontData* font = 0; - - RefPtr<Range> range = m_frame->selection()->toRange(); - Node* startNode = range->editingStartPosition().node(); - 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 - // unreproducible case where this didn't happen, so check for nil also. - for (Node* n = startNode; n && n != pastEnd; n = n->traverseNextNode()) { - RenderObject *renderer = n->renderer(); - if (!renderer) - continue; - // FIXME: Are there any node types that have renderers, but that we should be skipping? - const SimpleFontData* f = renderer->style()->font().primaryFont(); - if (!font) - font = f; - else if (font != f) { - hasMultipleFonts = true; - break; - } - } - } - - return font; -#else - return 0; -#endif -} - -TriState Editor::selectionUnorderedListState() const -{ - if (m_frame->selection()->isCaret()) { - if (enclosingNodeWithTag(m_frame->selection()->selection().start(), ulTag)) - return TrueTriState; - } else if (m_frame->selection()->isRange()) { - Node* startNode = enclosingNodeWithTag(m_frame->selection()->selection().start(), ulTag); - Node* endNode = enclosingNodeWithTag(m_frame->selection()->selection().end(), ulTag); - if (startNode && endNode && startNode == endNode) - return TrueTriState; - } - - return FalseTriState; -} - -TriState Editor::selectionOrderedListState() const -{ - if (m_frame->selection()->isCaret()) { - if (enclosingNodeWithTag(m_frame->selection()->selection().start(), olTag)) - return TrueTriState; - } else if (m_frame->selection()->isRange()) { - Node* startNode = enclosingNodeWithTag(m_frame->selection()->selection().start(), olTag); - Node* endNode = enclosingNodeWithTag(m_frame->selection()->selection().end(), olTag); - if (startNode && endNode && startNode == endNode) - return TrueTriState; - } - - return FalseTriState; -} - -PassRefPtr<Node> Editor::insertOrderedList() -{ - if (!canEditRichly()) - return 0; - - RefPtr<Node> newList = InsertListCommand::insertList(m_frame->document(), InsertListCommand::OrderedList); - revealSelectionAfterEditingOperation(); - return newList; -} - -PassRefPtr<Node> Editor::insertUnorderedList() -{ - if (!canEditRichly()) - return 0; - - RefPtr<Node> newList = InsertListCommand::insertList(m_frame->document(), InsertListCommand::UnorderedList); - revealSelectionAfterEditingOperation(); - return newList; -} - -bool Editor::canIncreaseSelectionListLevel() -{ - return canEditRichly() && IncreaseSelectionListLevelCommand::canIncreaseSelectionListLevel(m_frame->document()); -} - -bool Editor::canDecreaseSelectionListLevel() -{ - return canEditRichly() && DecreaseSelectionListLevelCommand::canDecreaseSelectionListLevel(m_frame->document()); -} - -PassRefPtr<Node> Editor::increaseSelectionListLevel() -{ - if (!canEditRichly() || m_frame->selection()->isNone()) - return 0; - - RefPtr<Node> newList = IncreaseSelectionListLevelCommand::increaseSelectionListLevel(m_frame->document()); - revealSelectionAfterEditingOperation(); - return newList; -} - -PassRefPtr<Node> Editor::increaseSelectionListLevelOrdered() -{ - if (!canEditRichly() || m_frame->selection()->isNone()) - return 0; - - PassRefPtr<Node> newList = IncreaseSelectionListLevelCommand::increaseSelectionListLevelOrdered(m_frame->document()); - revealSelectionAfterEditingOperation(); - return newList; -} - -PassRefPtr<Node> Editor::increaseSelectionListLevelUnordered() -{ - if (!canEditRichly() || m_frame->selection()->isNone()) - return 0; - - PassRefPtr<Node> newList = IncreaseSelectionListLevelCommand::increaseSelectionListLevelUnordered(m_frame->document()); - revealSelectionAfterEditingOperation(); - return newList; -} - -void Editor::decreaseSelectionListLevel() -{ - if (!canEditRichly() || m_frame->selection()->isNone()) - return; - - DecreaseSelectionListLevelCommand::decreaseSelectionListLevel(m_frame->document()); - revealSelectionAfterEditingOperation(); -} - -void Editor::removeFormattingAndStyle() -{ - applyCommand(RemoveFormatCommand::create(m_frame->document())); -} - -void Editor::clearLastEditCommand() -{ - m_lastEditCommand.clear(); -} - -// Returns whether caller should continue with "the default processing", which is the same as -// the event handler NOT setting the return value to false -bool Editor::dispatchCPPEvent(const AtomicString &eventType, ClipboardAccessPolicy policy) -{ - Node* target = m_frame->selection()->start().element(); - if (!target && m_frame->document()) - target = m_frame->document()->body(); - if (!target) - return true; - target = target->shadowAncestorNode(); - - RefPtr<Clipboard> clipboard = newGeneralClipboard(policy); - - ExceptionCode ec = 0; - RefPtr<Event> evt = ClipboardEvent::create(eventType, true, true, clipboard); - EventTargetNodeCast(target)->dispatchEvent(evt, ec); - bool noDefaultProcessing = evt->defaultPrevented(); - - // invalidate clipboard here for security - clipboard->setAccessPolicy(ClipboardNumb); - - return !noDefaultProcessing; -} - -void Editor::applyStyle(CSSStyleDeclaration* style, EditAction editingAction) -{ - switch (m_frame->selection()->state()) { - case Selection::NONE: - // do nothing - break; - case Selection::CARET: - m_frame->computeAndSetTypingStyle(style, editingAction); - break; - case Selection::RANGE: - if (m_frame->document() && style) - applyCommand(ApplyStyleCommand::create(m_frame->document(), style, editingAction)); - break; - } -} - -bool Editor::shouldApplyStyle(CSSStyleDeclaration* style, Range* range) -{ - return client()->shouldApplyStyle(style, range); -} - -void Editor::applyParagraphStyle(CSSStyleDeclaration* style, EditAction editingAction) -{ - switch (m_frame->selection()->state()) { - case Selection::NONE: - // do nothing - break; - case Selection::CARET: - case Selection::RANGE: - if (m_frame->document() && style) - applyCommand(ApplyStyleCommand::create(m_frame->document(), style, editingAction, ApplyStyleCommand::ForceBlockProperties)); - break; - } -} - -void Editor::applyStyleToSelection(CSSStyleDeclaration* style, EditAction editingAction) -{ - if (!style || style->length() == 0 || !canEditRichly()) - return; - - if (client() && client()->shouldApplyStyle(style, m_frame->selection()->toRange().get())) - applyStyle(style, editingAction); -} - -void Editor::applyParagraphStyleToSelection(CSSStyleDeclaration* style, EditAction editingAction) -{ - if (!style || style->length() == 0 || !canEditRichly()) - return; - - if (client() && client()->shouldApplyStyle(style, m_frame->selection()->toRange().get())) - applyParagraphStyle(style, editingAction); -} - -bool Editor::clientIsEditable() const -{ - return client() && client()->isEditable(); -} - -bool Editor::selectionStartHasStyle(CSSStyleDeclaration* style) const -{ - Node* nodeToRemove; - RefPtr<CSSComputedStyleDeclaration> selectionStyle = m_frame->selectionComputedStyle(nodeToRemove); - if (!selectionStyle) - return false; - - RefPtr<CSSMutableStyleDeclaration> mutableStyle = style->makeMutable(); - - bool match = true; - DeprecatedValueListConstIterator<CSSProperty> end; - for (DeprecatedValueListConstIterator<CSSProperty> it = mutableStyle->valuesIterator(); it != end; ++it) { - int propertyID = (*it).id(); - if (!equalIgnoringCase(mutableStyle->getPropertyValue(propertyID), selectionStyle->getPropertyValue(propertyID))) { - match = false; - break; - } - } - - if (nodeToRemove) { - ExceptionCode ec = 0; - nodeToRemove->remove(ec); - ASSERT(ec == 0); - } - - return match; -} - -static void updateState(CSSMutableStyleDeclaration* desiredStyle, CSSComputedStyleDeclaration* computedStyle, bool& atStart, TriState& state) -{ - DeprecatedValueListConstIterator<CSSProperty> end; - for (DeprecatedValueListConstIterator<CSSProperty> it = desiredStyle->valuesIterator(); it != end; ++it) { - int propertyID = (*it).id(); - String desiredProperty = desiredStyle->getPropertyValue(propertyID); - String computedProperty = computedStyle->getPropertyValue(propertyID); - TriState propertyState = equalIgnoringCase(desiredProperty, computedProperty) - ? TrueTriState : FalseTriState; - if (atStart) { - state = propertyState; - atStart = false; - } else if (state != propertyState) { - state = MixedTriState; - break; - } - } -} - -TriState Editor::selectionHasStyle(CSSStyleDeclaration* style) const -{ - bool atStart = true; - TriState state = FalseTriState; - - RefPtr<CSSMutableStyleDeclaration> mutableStyle = style->makeMutable(); - - if (!m_frame->selection()->isRange()) { - Node* nodeToRemove; - RefPtr<CSSComputedStyleDeclaration> selectionStyle = m_frame->selectionComputedStyle(nodeToRemove); - if (!selectionStyle) - return FalseTriState; - updateState(mutableStyle.get(), selectionStyle.get(), atStart, state); - if (nodeToRemove) { - ExceptionCode ec = 0; - nodeToRemove->remove(ec); - ASSERT(ec == 0); - } - } else { - for (Node* node = m_frame->selection()->start().node(); node; node = node->traverseNextNode()) { - RefPtr<CSSComputedStyleDeclaration> nodeStyle = computedStyle(node); - if (nodeStyle) - updateState(mutableStyle.get(), nodeStyle.get(), atStart, state); - if (state == MixedTriState) - break; - if (node == m_frame->selection()->end().node()) - break; - } - } - - return state; -} -void Editor::indent() -{ - applyCommand(IndentOutdentCommand::create(m_frame->document(), IndentOutdentCommand::Indent)); -} - -void Editor::outdent() -{ - applyCommand(IndentOutdentCommand::create(m_frame->document(), IndentOutdentCommand::Outdent)); -} - -static void dispatchEditableContentChangedEvents(const EditCommand& command) -{ - Element* startRoot = command.startingRootEditableElement(); - Element* endRoot = command.endingRootEditableElement(); - ExceptionCode ec; - if (startRoot) - startRoot->dispatchEvent(Event::create(eventNames().webkitEditableContentChangedEvent, false, false), ec); - if (endRoot && endRoot != startRoot) - endRoot->dispatchEvent(Event::create(eventNames().webkitEditableContentChangedEvent, false, false), ec); -} - -void Editor::appliedEditing(PassRefPtr<EditCommand> cmd) -{ - dispatchEditableContentChangedEvents(*cmd); - - Selection newSelection(cmd->endingSelection()); - // If there is no selection change, don't bother sending shouldChangeSelection, but still call setSelection, - // because there is work that it must do in this situation. - // 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 - // Don't clear the typing style or removedAnchor with this selection change. We do those things elsewhere if necessary. - if (newSelection == m_frame->selection()->selection() || m_frame->shouldChangeSelection(newSelection)) - m_frame->selection()->setSelection(newSelection, false, false); - - if (!cmd->preservesTypingStyle()) - m_frame->setTypingStyle(0); - - // Command will be equal to last edit command only in the case of typing - if (m_lastEditCommand.get() == cmd) - ASSERT(cmd->isTypingCommand()); - else { - // Only register a new undo command if the command passed in is - // different from the last command - m_lastEditCommand = cmd; - if (client()) - client()->registerCommandForUndo(m_lastEditCommand); - } - respondToChangedContents(newSelection); -} - -void Editor::unappliedEditing(PassRefPtr<EditCommand> cmd) -{ - dispatchEditableContentChangedEvents(*cmd); - - Selection newSelection(cmd->startingSelection()); - // If there is no selection change, don't bother sending shouldChangeSelection, but still call setSelection, - // because there is work that it must do in this situation. - // 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 - if (newSelection == m_frame->selection()->selection() || m_frame->shouldChangeSelection(newSelection)) - m_frame->selection()->setSelection(newSelection, true); - - m_lastEditCommand = 0; - if (client()) - client()->registerCommandForRedo(cmd); - respondToChangedContents(newSelection); -} - -void Editor::reappliedEditing(PassRefPtr<EditCommand> cmd) -{ - dispatchEditableContentChangedEvents(*cmd); - - Selection newSelection(cmd->endingSelection()); - // If there is no selection change, don't bother sending shouldChangeSelection, but still call setSelection, - // because there is work that it must do in this situation. - // 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 - if (newSelection == m_frame->selection()->selection() || m_frame->shouldChangeSelection(newSelection)) - m_frame->selection()->setSelection(newSelection, true); - - m_lastEditCommand = 0; - if (client()) - client()->registerCommandForUndo(cmd); - respondToChangedContents(newSelection); -} - -Editor::Editor(Frame* frame) - : m_frame(frame) - , m_deleteButtonController(new DeleteButtonController(frame)) - , m_ignoreCompositionSelectionChange(false) - , m_shouldStartNewKillRingSequence(false) -{ -} - -Editor::~Editor() -{ -} - -void Editor::clear() -{ - m_compositionNode = 0; - m_customCompositionUnderlines.clear(); -} - -bool Editor::insertText(const String& text, Event* triggeringEvent) -{ - return m_frame->eventHandler()->handleTextInputEvent(text, triggeringEvent); -} - -bool Editor::insertTextWithoutSendingTextEvent(const String& text, bool selectInsertedText, Event* triggeringEvent) -{ - if (text.isEmpty()) - return false; - - Selection selection = selectionForCommand(triggeringEvent); - if (!selection.isContentEditable()) - return false; - RefPtr<Range> range = selection.toRange(); - - if (!shouldInsertText(text, range.get(), EditorInsertActionTyped)) - return true; - - // 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()) { - RefPtr<Document> document = selectionStart->document(); - - // Insert the text - TypingCommand::insertText(document.get(), text, selection, selectInsertedText); - - // Reveal the current selection - if (Frame* editedFrame = document->frame()) - if (Page* page = editedFrame->page()) - page->focusController()->focusedOrMainFrame()->revealSelection(RenderLayer::gAlignToEdgeIfNeeded); - } - } - - return true; -} - -bool Editor::insertLineBreak() -{ - if (!canEdit()) - return false; - - if (!shouldInsertText("\n", m_frame->selection()->toRange().get(), EditorInsertActionTyped)) - return true; - - TypingCommand::insertLineBreak(m_frame->document()); - revealSelectionAfterEditingOperation(); - return true; -} - -bool Editor::insertParagraphSeparator() -{ - if (!canEdit()) - return false; - - if (!canEditRichly()) - return insertLineBreak(); - - if (!shouldInsertText("\n", m_frame->selection()->toRange().get(), EditorInsertActionTyped)) - return true; - - TypingCommand::insertParagraphSeparator(m_frame->document()); - revealSelectionAfterEditingOperation(); - return true; -} - -void Editor::cut() -{ - if (tryDHTMLCut()) - return; // DHTML did the whole operation - if (!canCut()) { - systemBeep(); - return; - } - RefPtr<Range> selection = selectedRange(); - if (shouldDeleteRange(selection.get())) { - Pasteboard::generalPasteboard()->writeSelection(selection.get(), canSmartCopyOrDelete(), m_frame); - didWriteSelectionToPasteboard(); - deleteSelectionWithSmartDelete(canSmartCopyOrDelete()); - } -} - -void Editor::copy() -{ - if (tryDHTMLCopy()) - return; // DHTML did the whole operation - if (!canCopy()) { - systemBeep(); - return; - } - - Document* document = m_frame->document(); - if (HTMLImageElement* imageElement = imageElementFromImageDocument(document)) - Pasteboard::generalPasteboard()->writeImage(imageElement, document->url(), document->title()); - else - Pasteboard::generalPasteboard()->writeSelection(selectedRange().get(), canSmartCopyOrDelete(), m_frame); - - didWriteSelectionToPasteboard(); -} - -#if !PLATFORM(MAC) - -void Editor::paste() -{ - ASSERT(m_frame->document()); - if (tryDHTMLPaste()) - return; // DHTML did the whole operation - if (!canPaste()) - return; - DocLoader* loader = m_frame->document()->docLoader(); - loader->setAllowStaleResources(true); - if (m_frame->selection()->isContentRichlyEditable()) - pasteWithPasteboard(Pasteboard::generalPasteboard(), true); - else - pasteAsPlainTextWithPasteboard(Pasteboard::generalPasteboard()); - loader->setAllowStaleResources(false); -} - -#endif - -void Editor::pasteAsPlainText() -{ - if (!canPaste()) - return; - pasteAsPlainTextWithPasteboard(Pasteboard::generalPasteboard()); -} - -void Editor::performDelete() -{ - if (!canDelete()) { - systemBeep(); - return; - } - - addToKillRing(selectedRange().get(), false); - deleteSelectionWithSmartDelete(canSmartCopyOrDelete()); - - // clear the "start new kill ring sequence" setting, because it was set to true - // when the selection was updated by deleting the range - setStartNewKillRingSequence(false); -} - -void Editor::copyURL(const KURL& url, const String& title) -{ - Pasteboard::generalPasteboard()->writeURL(url, title, m_frame); -} - -void Editor::copyImage(const HitTestResult& result) -{ - KURL url = result.absoluteLinkURL(); - if (url.isEmpty()) - url = result.absoluteImageURL(); - - Pasteboard::generalPasteboard()->writeImage(result.innerNonSharedNode(), url, result.altDisplayString()); -} - -bool Editor::isContinuousSpellCheckingEnabled() -{ - return client() && client()->isContinuousSpellCheckingEnabled(); -} - -void Editor::toggleContinuousSpellChecking() -{ - if (client()) - client()->toggleContinuousSpellChecking(); -} - -bool Editor::isGrammarCheckingEnabled() -{ - return client() && client()->isGrammarCheckingEnabled(); -} - -void Editor::toggleGrammarChecking() -{ - if (client()) - client()->toggleGrammarChecking(); -} - -int Editor::spellCheckerDocumentTag() -{ - return client() ? client()->spellCheckerDocumentTag() : 0; -} - -bool Editor::shouldEndEditing(Range* range) -{ - return client() && client()->shouldEndEditing(range); -} - -bool Editor::shouldBeginEditing(Range* range) -{ - return client() && client()->shouldBeginEditing(range); -} - -void Editor::clearUndoRedoOperations() -{ - if (client()) - client()->clearUndoRedoOperations(); -} - -bool Editor::canUndo() -{ - return client() && client()->canUndo(); -} - -void Editor::undo() -{ - if (client()) - client()->undo(); -} - -bool Editor::canRedo() -{ - return client() && client()->canRedo(); -} - -void Editor::redo() -{ - if (client()) - client()->redo(); -} - -void Editor::didBeginEditing() -{ - if (client()) - client()->didBeginEditing(); -} - -void Editor::didEndEditing() -{ - if (client()) - client()->didEndEditing(); -} - -void Editor::didWriteSelectionToPasteboard() -{ - if (client()) - client()->didWriteSelectionToPasteboard(); -} - -void Editor::toggleBold() -{ - command("ToggleBold").execute(); -} - -void Editor::toggleUnderline() -{ - command("ToggleUnderline").execute(); -} - -void Editor::setBaseWritingDirection(WritingDirection direction) -{ - Node* focusedNode = frame()->document()->focusedNode(); - if (focusedNode && (focusedNode->hasTagName(textareaTag) - || focusedNode->hasTagName(inputTag) && (static_cast<HTMLInputElement*>(focusedNode)->inputType() == HTMLInputElement::TEXT - || static_cast<HTMLInputElement*>(focusedNode)->inputType() == HTMLInputElement::SEARCH))) { - if (direction == NaturalWritingDirection) - return; - static_cast<HTMLElement*>(focusedNode)->setAttribute(dirAttr, direction == LeftToRightWritingDirection ? "ltr" : "rtl"); - frame()->document()->updateRendering(); - return; - } - - RefPtr<CSSMutableStyleDeclaration> style = CSSMutableStyleDeclaration::create(); - style->setProperty(CSSPropertyDirection, direction == LeftToRightWritingDirection ? "ltr" : direction == RightToLeftWritingDirection ? "rtl" : "inherit", false); - applyParagraphStyleToSelection(style.get(), EditActionSetWritingDirection); -} - -void Editor::selectComposition() -{ - RefPtr<Range> range = compositionRange(); - if (!range) - return; - - // The composition can start inside a composed character sequence, so we have to override checks. - // See <http://bugs.webkit.org/show_bug.cgi?id=15781> - Selection selection; - selection.setWithoutValidation(range->startPosition(), range->endPosition()); - m_frame->selection()->setSelection(selection, false, false); -} - -void Editor::confirmComposition() -{ - if (!m_compositionNode) - return; - confirmComposition(m_compositionNode->data().substring(m_compositionStart, m_compositionEnd - m_compositionStart), false); -} - -void Editor::confirmCompositionWithoutDisturbingSelection() -{ - if (!m_compositionNode) - return; - confirmComposition(m_compositionNode->data().substring(m_compositionStart, m_compositionEnd - m_compositionStart), true); -} - -void Editor::confirmComposition(const String& text) -{ - confirmComposition(text, false); -} - -void Editor::confirmComposition(const String& text, bool preserveSelection) -{ - setIgnoreCompositionSelectionChange(true); - - Selection oldSelection = m_frame->selection()->selection(); - - selectComposition(); - - if (m_frame->selection()->isNone()) { - setIgnoreCompositionSelectionChange(false); - return; - } - - // If text is empty, then delete the old composition here. If text is non-empty, InsertTextCommand::input - // will delete the old composition with an optimized replace operation. - if (text.isEmpty()) - TypingCommand::deleteSelection(m_frame->document(), false); - - m_compositionNode = 0; - m_customCompositionUnderlines.clear(); - - insertText(text, 0); - - if (preserveSelection) - m_frame->selection()->setSelection(oldSelection, false, false); - - setIgnoreCompositionSelectionChange(false); -} - -void Editor::setComposition(const String& text, const Vector<CompositionUnderline>& underlines, unsigned selectionStart, unsigned selectionEnd) -{ - setIgnoreCompositionSelectionChange(true); - - selectComposition(); - - if (m_frame->selection()->isNone()) { - setIgnoreCompositionSelectionChange(false); - return; - } - - // If text is empty, then delete the old composition here. If text is non-empty, InsertTextCommand::input - // will delete the old composition with an optimized replace operation. - if (text.isEmpty()) - TypingCommand::deleteSelection(m_frame->document(), false); - - m_compositionNode = 0; - m_customCompositionUnderlines.clear(); - - if (!text.isEmpty()) { - TypingCommand::insertText(m_frame->document(), text, true, true); - - Node* baseNode = m_frame->selection()->base().node(); - unsigned baseOffset = m_frame->selection()->base().offset(); - Node* extentNode = m_frame->selection()->extent().node(); - unsigned extentOffset = m_frame->selection()->extent().offset(); - - if (baseNode && baseNode == extentNode && baseNode->isTextNode() && baseOffset + text.length() == extentOffset) { - m_compositionNode = static_cast<Text*>(baseNode); - m_compositionStart = baseOffset; - m_compositionEnd = extentOffset; - m_customCompositionUnderlines = underlines; - size_t numUnderlines = m_customCompositionUnderlines.size(); - for (size_t i = 0; i < numUnderlines; ++i) { - m_customCompositionUnderlines[i].startOffset += baseOffset; - m_customCompositionUnderlines[i].endOffset += baseOffset; - } - if (baseNode->renderer()) - baseNode->renderer()->repaint(); - - unsigned start = min(baseOffset + selectionStart, extentOffset); - unsigned end = min(max(start, baseOffset + selectionEnd), extentOffset); - RefPtr<Range> selectedRange = Range::create(baseNode->document(), baseNode, start, baseNode, end); - m_frame->selection()->setSelectedRange(selectedRange.get(), DOWNSTREAM, false); - } - } - - setIgnoreCompositionSelectionChange(false); -} - -void Editor::ignoreSpelling() -{ - if (!client()) - return; - - RefPtr<Range> selectedRange = frame()->selection()->toRange(); - if (selectedRange) - frame()->document()->removeMarkers(selectedRange.get(), DocumentMarker::Spelling); - - String text = frame()->selectedText(); - ASSERT(text.length() != 0); - client()->ignoreWordInSpellDocument(text); -} - -void Editor::learnSpelling() -{ - if (!client()) - return; - - // FIXME: We don't call this on the Mac, and it should remove misppelling markers around the - // learned word, see <rdar://problem/5396072>. - - String text = frame()->selectedText(); - ASSERT(text.length() != 0); - client()->learnWord(text); -} - -static String findFirstMisspellingInRange(EditorClient* client, Range* searchRange, int& firstMisspellingOffset, bool markAll) -{ - ASSERT_ARG(client, client); - ASSERT_ARG(searchRange, searchRange); - - WordAwareIterator it(searchRange); - firstMisspellingOffset = 0; - - String firstMisspelling; - int currentChunkOffset = 0; - - while (!it.atEnd()) { - const UChar* chars = it.characters(); - int len = it.length(); - - // Skip some work for one-space-char hunks - if (!(len == 1 && chars[0] == ' ')) { - - int misspellingLocation = -1; - int misspellingLength = 0; - client->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 - // more robust against bad input on release builds. - ASSERT(misspellingLength >= 0); - ASSERT(misspellingLocation >= -1); - ASSERT(misspellingLength == 0 || misspellingLocation >= 0); - ASSERT(misspellingLocation < len); - ASSERT(misspellingLength <= len); - ASSERT(misspellingLocation + misspellingLength <= len); - - if (misspellingLocation >= 0 && misspellingLength > 0 && misspellingLocation < len && misspellingLength <= len && misspellingLocation + misspellingLength <= len) { - - // Remember first-encountered misspelling and its offset - if (!firstMisspelling) { - firstMisspellingOffset = currentChunkOffset + misspellingLocation; - firstMisspelling = String(chars + misspellingLocation, misspellingLength); - } - - // Mark this instance if we're marking all instances. Otherwise bail out because we found the first one. - if (!markAll) - break; - - // Compute range of misspelled word - RefPtr<Range> misspellingRange = TextIterator::subrange(searchRange, currentChunkOffset + misspellingLocation, misspellingLength); - - // Store marker for misspelled word - ExceptionCode ec = 0; - misspellingRange->startContainer(ec)->document()->addMarker(misspellingRange.get(), DocumentMarker::Spelling); - ASSERT(ec == 0); - } - } - - currentChunkOffset += len; - it.advance(); - } - - return firstMisspelling; -} - -#ifndef BUILDING_ON_TIGER - -static PassRefPtr<Range> paragraphAlignedRangeForRange(Range* arbitraryRange, int& offsetIntoParagraphAlignedRange, String& paragraphString) -{ - ASSERT_ARG(arbitraryRange, arbitraryRange); - - ExceptionCode ec = 0; - - // Expand range to paragraph boundaries - RefPtr<Range> paragraphRange = arbitraryRange->cloneRange(ec); - setStart(paragraphRange.get(), startOfParagraph(arbitraryRange->startPosition())); - setEnd(paragraphRange.get(), endOfParagraph(arbitraryRange->endPosition())); - - // Compute offset from start of expanded range to start of original range - RefPtr<Range> offsetAsRange = Range::create(paragraphRange->startContainer(ec)->document(), paragraphRange->startPosition(), arbitraryRange->startPosition()); - offsetIntoParagraphAlignedRange = TextIterator::rangeLength(offsetAsRange.get()); - - // Fill in out parameter with string representing entire paragraph range. - // Someday we might have a caller that doesn't use this, but for now all callers do. - paragraphString = plainText(paragraphRange.get()); - - return paragraphRange; -} - -static int findFirstGrammarDetailInRange(const Vector<GrammarDetail>& grammarDetails, int badGrammarPhraseLocation, int badGrammarPhraseLength, Range *searchRange, int startOffset, int endOffset, bool markAll) -{ - // Found some bad grammar. Find the earliest detail range that starts in our search range (if any). - // Optionally add a DocumentMarker for each detail in the range. - int earliestDetailLocationSoFar = -1; - int earliestDetailIndex = -1; - for (unsigned i = 0; i < grammarDetails.size(); i++) { - const GrammarDetail* detail = &grammarDetails[i]; - ASSERT(detail->length > 0 && detail->location >= 0); - - int detailStartOffsetInParagraph = badGrammarPhraseLocation + detail->location; - - // Skip this detail if it starts before the original search range - if (detailStartOffsetInParagraph < startOffset) - continue; - - // Skip this detail if it starts after the original search range - if (detailStartOffsetInParagraph >= endOffset) - continue; - - if (markAll) { - RefPtr<Range> badGrammarRange = TextIterator::subrange(searchRange, badGrammarPhraseLocation - startOffset + detail->location, detail->length); - ExceptionCode ec = 0; - badGrammarRange->startContainer(ec)->document()->addMarker(badGrammarRange.get(), DocumentMarker::Grammar, detail->userDescription); - ASSERT(ec == 0); - } - - // Remember this detail only if it's earlier than our current candidate (the details aren't in a guaranteed order) - if (earliestDetailIndex < 0 || earliestDetailLocationSoFar > detail->location) { - earliestDetailIndex = i; - earliestDetailLocationSoFar = detail->location; - } - } - - return earliestDetailIndex; -} - -static String findFirstBadGrammarInRange(EditorClient* client, Range* searchRange, GrammarDetail& outGrammarDetail, int& outGrammarPhraseOffset, bool markAll) -{ - ASSERT_ARG(client, client); - ASSERT_ARG(searchRange, searchRange); - - // Initialize out parameters; these will be updated if we find something to return. - outGrammarDetail.location = -1; - outGrammarDetail.length = 0; - outGrammarDetail.guesses.clear(); - outGrammarDetail.userDescription = ""; - outGrammarPhraseOffset = 0; - - String firstBadGrammarPhrase; - - // Expand the search range to encompass entire paragraphs, since grammar checking needs that much context. - // Determine the character offset from the start of the paragraph to the start of the original search range, - // since we will want to ignore results in this area. - int searchRangeStartOffset; - String paragraphString; - RefPtr<Range> paragraphRange = paragraphAlignedRangeForRange(searchRange, searchRangeStartOffset, paragraphString); - - // Determine the character offset from the start of the paragraph to the end of the original search range, - // since we will want to ignore results in this area also. - int searchRangeEndOffset = searchRangeStartOffset + TextIterator::rangeLength(searchRange); - - // Start checking from beginning of paragraph, but skip past results that occur before the start of the original search range. - int startOffset = 0; - while (startOffset < searchRangeEndOffset) { - Vector<GrammarDetail> grammarDetails; - int badGrammarPhraseLocation = -1; - int badGrammarPhraseLength = 0; - client->checkGrammarOfString(paragraphString.characters() + startOffset, paragraphString.length() - startOffset, grammarDetails, &badGrammarPhraseLocation, &badGrammarPhraseLength); - - if (badGrammarPhraseLength == 0) { - ASSERT(badGrammarPhraseLocation == -1); - return String(); - } - - ASSERT(badGrammarPhraseLocation >= 0); - badGrammarPhraseLocation += startOffset; - - - // Found some bad grammar. Find the earliest detail range that starts in our search range (if any). - int badGrammarIndex = findFirstGrammarDetailInRange(grammarDetails, badGrammarPhraseLocation, badGrammarPhraseLength, searchRange, searchRangeStartOffset, searchRangeEndOffset, markAll); - if (badGrammarIndex >= 0) { - ASSERT(static_cast<unsigned>(badGrammarIndex) < grammarDetails.size()); - outGrammarDetail = grammarDetails[badGrammarIndex]; - } - - // If we found a detail in range, then we have found the first bad phrase (unless we found one earlier but - // kept going so we could mark all instances). - if (badGrammarIndex >= 0 && firstBadGrammarPhrase.isEmpty()) { - outGrammarPhraseOffset = badGrammarPhraseLocation - searchRangeStartOffset; - firstBadGrammarPhrase = paragraphString.substring(badGrammarPhraseLocation, badGrammarPhraseLength); - - // Found one. We're done now, unless we're marking each instance. - if (!markAll) - break; - } - - // These results were all between the start of the paragraph and the start of the search range; look - // beyond this phrase. - startOffset = badGrammarPhraseLocation + badGrammarPhraseLength; - } - - return firstBadGrammarPhrase; -} - -#endif /* not BUILDING_ON_TIGER */ - -void Editor::advanceToNextMisspelling(bool startBeforeSelection) -{ - ExceptionCode ec = 0; - - // The basic approach is to search in two phases - from the selection end to the end of the doc, and - // then we wrap and search from the doc start to (approximately) where we started. - - // Start at the end of the selection, search to edge of document. Starting at the selection end makes - // repeated "check spelling" commands work. - Selection selection(frame()->selection()->selection()); - RefPtr<Range> spellingSearchRange(rangeOfContents(frame()->document())); - bool startedWithSelection = false; - if (selection.start().node()) { - startedWithSelection = true; - if (startBeforeSelection) { - VisiblePosition start(selection.visibleStart()); - // We match AppKit's rule: Start 1 character before the selection. - VisiblePosition oneBeforeStart = start.previous(); - setStart(spellingSearchRange.get(), oneBeforeStart.isNotNull() ? oneBeforeStart : start); - } else - setStart(spellingSearchRange.get(), selection.visibleEnd()); - } - - Position position = spellingSearchRange->startPosition(); - if (!isEditablePosition(position)) { - // This shouldn't happen in very often because the Spelling menu items aren't enabled unless the - // selection is editable. - // This can happen in Mail for a mix of non-editable and editable content (like Stationary), - // when spell checking the whole document before sending the message. - // In that case the document might not be editable, but there are editable pockets that need to be spell checked. - - position = firstEditablePositionAfterPositionInRoot(position, frame()->document()->documentElement()).deepEquivalent(); - if (position.isNull()) - return; - - Position rangeCompliantPosition = rangeCompliantEquivalent(position); - spellingSearchRange->setStart(rangeCompliantPosition.node(), rangeCompliantPosition.offset(), ec); - startedWithSelection = false; // won't need to wrap - } - - // topNode defines the whole range we want to operate on - Node* topNode = highestEditableRoot(position); - spellingSearchRange->setEnd(topNode, maxDeepOffset(topNode), ec); - - // If spellingSearchRange starts in the middle of a word, advance to the next word so we start checking - // at a word boundary. Going back by one char and then forward by a word does the trick. - if (startedWithSelection) { - VisiblePosition oneBeforeStart = startVisiblePosition(spellingSearchRange.get(), DOWNSTREAM).previous(); - if (oneBeforeStart.isNotNull()) { - setStart(spellingSearchRange.get(), endOfWord(oneBeforeStart)); - } // else we were already at the start of the editable node - } - - if (spellingSearchRange->collapsed(ec)) - return; // nothing to search in - - // Get the spell checker if it is available - if (!client()) - return; - - // We go to the end of our first range instead of the start of it, just to be sure - // we don't get foiled by any word boundary problems at the start. It means we might - // do a tiny bit more searching. - Node *searchEndNodeAfterWrap = spellingSearchRange->endContainer(ec); - int searchEndOffsetAfterWrap = spellingSearchRange->endOffset(ec); - - int misspellingOffset; - String misspelledWord = findFirstMisspellingInRange(client(), spellingSearchRange.get(), misspellingOffset, false); - - String badGrammarPhrase; - -#ifndef BUILDING_ON_TIGER - int grammarPhraseOffset = 0; - GrammarDetail grammarDetail; - - // Search for bad grammar that occurs prior to the next misspelled word (if any) - RefPtr<Range> grammarSearchRange = spellingSearchRange->cloneRange(ec); - if (!misspelledWord.isEmpty()) { - // Stop looking at start of next misspelled word - CharacterIterator chars(grammarSearchRange.get()); - chars.advance(misspellingOffset); - grammarSearchRange->setEnd(chars.range()->startContainer(ec), chars.range()->startOffset(ec), ec); - } - - if (isGrammarCheckingEnabled()) - badGrammarPhrase = findFirstBadGrammarInRange(client(), grammarSearchRange.get(), grammarDetail, grammarPhraseOffset, false); -#endif - - // If we found neither bad grammar nor a misspelled word, wrap and try again (but don't bother if we started at the beginning of the - // block rather than at a selection). - if (startedWithSelection && !misspelledWord && !badGrammarPhrase) { - spellingSearchRange->setStart(topNode, 0, ec); - // going until the end of the very first chunk we tested is far enough - spellingSearchRange->setEnd(searchEndNodeAfterWrap, searchEndOffsetAfterWrap, ec); - - misspelledWord = findFirstMisspellingInRange(client(), spellingSearchRange.get(), misspellingOffset, false); - -#ifndef BUILDING_ON_TIGER - grammarSearchRange = spellingSearchRange->cloneRange(ec); - if (!misspelledWord.isEmpty()) { - // Stop looking at start of next misspelled word - CharacterIterator chars(grammarSearchRange.get()); - chars.advance(misspellingOffset); - grammarSearchRange->setEnd(chars.range()->startContainer(ec), chars.range()->startOffset(ec), ec); - } - if (isGrammarCheckingEnabled()) - badGrammarPhrase = findFirstBadGrammarInRange(client(), grammarSearchRange.get(), grammarDetail, grammarPhraseOffset, false); -#endif - } - - if (!badGrammarPhrase.isEmpty()) { -#ifdef BUILDING_ON_TIGER - ASSERT_NOT_REACHED(); -#else - // We found bad grammar. Since we only searched for bad grammar up to the first misspelled word, the bad grammar - // takes precedence and we ignore any potential misspelled word. Select the grammar detail, update the spelling - // panel, and store a marker so we draw the green squiggle later. - - ASSERT(badGrammarPhrase.length() > 0); - ASSERT(grammarDetail.location != -1 && grammarDetail.length > 0); - - // FIXME 4859190: This gets confused with doubled punctuation at the end of a paragraph - RefPtr<Range> badGrammarRange = TextIterator::subrange(grammarSearchRange.get(), grammarPhraseOffset + grammarDetail.location, grammarDetail.length); - frame()->selection()->setSelection(Selection(badGrammarRange.get(), SEL_DEFAULT_AFFINITY)); - frame()->revealSelection(); - - client()->updateSpellingUIWithGrammarString(badGrammarPhrase, grammarDetail); - frame()->document()->addMarker(badGrammarRange.get(), DocumentMarker::Grammar, grammarDetail.userDescription); -#endif - } else if (!misspelledWord.isEmpty()) { - // We found a misspelling, but not any earlier bad grammar. Select the misspelling, update the spelling panel, and store - // a marker so we draw the red squiggle later. - - RefPtr<Range> misspellingRange = TextIterator::subrange(spellingSearchRange.get(), misspellingOffset, misspelledWord.length()); - frame()->selection()->setSelection(Selection(misspellingRange.get(), DOWNSTREAM)); - frame()->revealSelection(); - - client()->updateSpellingUIWithMisspelledWord(misspelledWord); - frame()->document()->addMarker(misspellingRange.get(), DocumentMarker::Spelling); - } -} - -bool Editor::isSelectionMisspelled() -{ - String selectedString = frame()->selectedText(); - int length = selectedString.length(); - if (length == 0) - return false; - - if (!client()) - return false; - - int misspellingLocation = -1; - int misspellingLength = 0; - client()->checkSpellingOfString(selectedString.characters(), length, &misspellingLocation, &misspellingLength); - - // The selection only counts as misspelled if the selected text is exactly one misspelled word - if (misspellingLength != length) - return false; - - // Update the spelling panel to be displaying this error (whether or not the spelling panel is on screen). - // This is necessary to make a subsequent call to [NSSpellChecker ignoreWord:inSpellDocumentWithTag:] work - // correctly; that call behaves differently based on whether the spelling panel is displaying a misspelling - // or a grammar error. - client()->updateSpellingUIWithMisspelledWord(selectedString); - - return true; -} - -#ifndef BUILDING_ON_TIGER -static bool isRangeUngrammatical(EditorClient* client, Range *range, Vector<String>& guessesVector) -{ - if (!client) - return false; - - ExceptionCode ec; - if (!range || range->collapsed(ec)) - return false; - - // Returns true only if the passed range exactly corresponds to a bad grammar detail range. This is analogous - // to isSelectionMisspelled. It's not good enough for there to be some bad grammar somewhere in the range, - // or overlapping the range; the ranges must exactly match. - guessesVector.clear(); - int grammarPhraseOffset; - - GrammarDetail grammarDetail; - String badGrammarPhrase = findFirstBadGrammarInRange(client, range, grammarDetail, grammarPhraseOffset, false); - - // No bad grammar in these parts at all. - if (badGrammarPhrase.isEmpty()) - return false; - - // Bad grammar, but phrase (e.g. sentence) starts beyond start of range. - if (grammarPhraseOffset > 0) - return false; - - ASSERT(grammarDetail.location >= 0 && grammarDetail.length > 0); - - // Bad grammar, but start of detail (e.g. ungrammatical word) doesn't match start of range - if (grammarDetail.location + grammarPhraseOffset != 0) - return false; - - // Bad grammar at start of range, but end of bad grammar is before or after end of range - if (grammarDetail.length != TextIterator::rangeLength(range)) - return false; - - // Update the spelling panel to be displaying this error (whether or not the spelling panel is on screen). - // This is necessary to make a subsequent call to [NSSpellChecker ignoreWord:inSpellDocumentWithTag:] work - // correctly; that call behaves differently based on whether the spelling panel is displaying a misspelling - // or a grammar error. - client->updateSpellingUIWithGrammarString(badGrammarPhrase, grammarDetail); - - return true; -} -#endif - -bool Editor::isSelectionUngrammatical() -{ -#ifdef BUILDING_ON_TIGER - return false; -#else - Vector<String> ignoredGuesses; - return isRangeUngrammatical(client(), frame()->selection()->toRange().get(), ignoredGuesses); -#endif -} - -Vector<String> Editor::guessesForUngrammaticalSelection() -{ -#ifdef BUILDING_ON_TIGER - return Vector<String>(); -#else - Vector<String> guesses; - // Ignore the result of isRangeUngrammatical; we just want the guesses, whether or not there are any - isRangeUngrammatical(client(), frame()->selection()->toRange().get(), guesses); - return guesses; -#endif -} - -Vector<String> Editor::guessesForMisspelledSelection() -{ - String selectedString = frame()->selectedText(); - ASSERT(selectedString.length() != 0); - - Vector<String> guesses; - if (client()) - client()->getGuessesForWord(selectedString, guesses); - return guesses; -} - -void Editor::showSpellingGuessPanel() -{ - if (!client()) { - LOG_ERROR("No NSSpellChecker"); - return; - } - -#ifndef BUILDING_ON_TIGER - // Post-Tiger, this menu item is a show/hide toggle, to match AppKit. Leave Tiger behavior alone - // to match rest of OS X. - if (client()->spellingUIIsShowing()) { - client()->showSpellingUI(false); - return; - } -#endif - - advanceToNextMisspelling(true); - client()->showSpellingUI(true); -} - -bool Editor::spellingPanelIsShowing() -{ - if (!client()) - return false; - return client()->spellingUIIsShowing(); -} - -void Editor::markMisspellingsAfterTypingToPosition(const VisiblePosition &p) -{ - if (!isContinuousSpellCheckingEnabled()) - return; - - // Check spelling of one word - markMisspellings(Selection(startOfWord(p, LeftWordIfOnBoundary), endOfWord(p, RightWordIfOnBoundary))); - - if (!isGrammarCheckingEnabled()) - return; - - // Check grammar of entire sentence - markBadGrammar(Selection(startOfSentence(p), endOfSentence(p))); -} - -static void markAllMisspellingsInRange(EditorClient* client, Range* searchRange) -{ - // Use the "markAll" feature of findFirstMisspellingInRange. Ignore the return value and the "out parameter"; - // all we need to do is mark every instance. - int ignoredOffset; - findFirstMisspellingInRange(client, searchRange, ignoredOffset, true); -} - -#ifndef BUILDING_ON_TIGER -static void markAllBadGrammarInRange(EditorClient* client, Range* searchRange) -{ - // Use the "markAll" feature of findFirstBadGrammarInRange. Ignore the return value and "out parameters"; all we need to - // do is mark every instance. - GrammarDetail ignoredGrammarDetail; - int ignoredOffset; - findFirstBadGrammarInRange(client, searchRange, ignoredGrammarDetail, ignoredOffset, true); -} -#endif - -static void markMisspellingsOrBadGrammar(Editor* editor, const Selection& selection, bool checkSpelling) -{ - // This function is called with a selection already expanded to word boundaries. - // Might be nice to assert that here. - - // This function is used only for as-you-type checking, so if that's off we do nothing. Note that - // grammar checking can only be on if spell checking is also on. - if (!editor->isContinuousSpellCheckingEnabled()) - return; - - RefPtr<Range> searchRange(selection.toRange()); - if (!searchRange) - return; - - // If we're not in an editable node, bail. - Node* editableNode = searchRange->startContainer(); - if (!editableNode || !editableNode->isContentEditable()) - return; - - // Get the spell checker if it is available - if (!editor->client()) - return; - - if (checkSpelling) - markAllMisspellingsInRange(editor->client(), searchRange.get()); - else { -#ifdef BUILDING_ON_TIGER - ASSERT_NOT_REACHED(); -#else - if (editor->isGrammarCheckingEnabled()) - markAllBadGrammarInRange(editor->client(), searchRange.get()); -#endif - } -} - -void Editor::markMisspellings(const Selection& selection) -{ - markMisspellingsOrBadGrammar(this, selection, true); -} - -void Editor::markBadGrammar(const Selection& selection) -{ -#ifndef BUILDING_ON_TIGER - markMisspellingsOrBadGrammar(this, selection, false); -#endif -} - -PassRefPtr<Range> Editor::rangeForPoint(const IntPoint& windowPoint) -{ - Document* document = m_frame->documentAtPoint(windowPoint); - if (!document) - return 0; - - Frame* frame = document->frame(); - ASSERT(frame); - FrameView* frameView = frame->view(); - if (!frameView) - return 0; - IntPoint framePoint = frameView->windowToContents(windowPoint); - Selection selection(frame->visiblePositionForPoint(framePoint)); - return avoidIntersectionWithNode(selection.toRange().get(), deleteButtonController() ? deleteButtonController()->containerElement() : 0); -} - -void Editor::revealSelectionAfterEditingOperation() -{ - if (m_ignoreCompositionSelectionChange) - return; - - m_frame->revealSelection(RenderLayer::gAlignToEdgeIfNeeded); -} - -void Editor::setIgnoreCompositionSelectionChange(bool ignore) -{ - if (m_ignoreCompositionSelectionChange == ignore) - return; - - m_ignoreCompositionSelectionChange = ignore; - if (!ignore) - revealSelectionAfterEditingOperation(); -} - -PassRefPtr<Range> Editor::compositionRange() const -{ - if (!m_compositionNode) - return 0; - unsigned length = m_compositionNode->length(); - unsigned start = min(m_compositionStart, length); - unsigned end = min(max(start, m_compositionEnd), length); - if (start >= end) - return 0; - return Range::create(m_compositionNode->document(), m_compositionNode.get(), start, m_compositionNode.get(), end); -} - -bool Editor::getCompositionSelection(unsigned& selectionStart, unsigned& selectionEnd) const -{ - if (!m_compositionNode) - return false; - Position start = m_frame->selection()->start(); - if (start.node() != m_compositionNode) - return false; - Position end = m_frame->selection()->end(); - if (end.node() != m_compositionNode) - return false; - - if (static_cast<unsigned>(start.offset()) < m_compositionStart) - return false; - if (static_cast<unsigned>(end.offset()) > m_compositionEnd) - return false; - - selectionStart = start.offset() - m_compositionStart; - selectionEnd = start.offset() - m_compositionEnd; - return true; -} - -void Editor::transpose() -{ - if (!canEdit()) - return; - - Selection selection = m_frame->selection()->selection(); - if (!selection.isCaret()) - return; - - // Make a selection that goes back one character and forward two characters. - VisiblePosition caret = selection.visibleStart(); - VisiblePosition next = isEndOfParagraph(caret) ? caret : caret.next(); - VisiblePosition previous = next.previous(); - if (next == previous) - return; - previous = previous.previous(); - if (!inSameParagraph(next, previous)) - return; - RefPtr<Range> range = makeRange(previous, next); - if (!range) - return; - Selection newSelection(range.get(), DOWNSTREAM); - - // Transpose the two characters. - String text = plainText(range.get()); - if (text.length() != 2) - return; - String transposed = text.right(1) + text.left(1); - - // Select the two characters. - if (newSelection != m_frame->selection()->selection()) { - if (!m_frame->shouldChangeSelection(newSelection)) - return; - m_frame->selection()->setSelection(newSelection); - } - - // Insert the transposed characters. - if (!shouldInsertText(transposed, range.get(), EditorInsertActionTyped)) - return; - replaceSelectionWithText(transposed, false, false); -} - -void Editor::addToKillRing(Range* range, bool prepend) -{ - if (m_shouldStartNewKillRingSequence) - startNewKillRingSequence(); - - String text = plainText(range); - text.replace('\\', m_frame->backslashAsCurrencySymbol()); - if (prepend) - prependToKillRing(text); - else - appendToKillRing(text); - m_shouldStartNewKillRingSequence = false; -} - -#if !PLATFORM(MAC) - -void Editor::appendToKillRing(const String&) -{ -} - -void Editor::prependToKillRing(const String&) -{ -} - -String Editor::yankFromKillRing() -{ - return String(); -} - -void Editor::startNewKillRingSequence() -{ -} - -void Editor::setKillRingToYankedState() -{ -} - -#endif - -bool Editor::insideVisibleArea(const IntPoint& point) const -{ - if (m_frame->excludeFromTextSearch()) - return false; - - // Right now, we only check the visibility of a point for disconnected frames. For all other - // frames, we assume visibility. - Frame* frame = m_frame->isDisconnected() ? m_frame : m_frame->tree()->top(true); - if (!frame->isDisconnected()) - return true; - - RenderPart* renderer = frame->ownerRenderer(); - RenderBlock* container = renderer->containingBlock(); - if (!(container->style()->overflowX() == OHIDDEN || container->style()->overflowY() == OHIDDEN)) - return true; - - IntRect rectInPageCoords = container->getOverflowClipRect(0, 0); - IntRect rectInFrameCoords = IntRect(renderer->xPos() * -1, renderer->yPos() * -1, - rectInPageCoords.width(), rectInPageCoords.height()); - - return rectInFrameCoords.contains(point); -} - -bool Editor::insideVisibleArea(Range* range) const -{ - if (!range) - return true; - - if (m_frame->excludeFromTextSearch()) - return false; - - // Right now, we only check the visibility of a range for disconnected frames. For all other - // frames, we assume visibility. - Frame* frame = m_frame->isDisconnected() ? m_frame : m_frame->tree()->top(true); - if (!frame->isDisconnected()) - return true; - - RenderPart* renderer = frame->ownerRenderer(); - RenderBlock* container = renderer->containingBlock(); - if (!(container->style()->overflowX() == OHIDDEN || container->style()->overflowY() == OHIDDEN)) - return true; - - IntRect rectInPageCoords = container->getOverflowClipRect(0, 0); - IntRect rectInFrameCoords = IntRect(renderer->xPos() * -1, renderer->yPos() * -1, - rectInPageCoords.width(), rectInPageCoords.height()); - IntRect resultRect = range->boundingBox(); - - return rectInFrameCoords.contains(resultRect); -} - -PassRefPtr<Range> Editor::firstVisibleRange(const String& target, bool caseFlag) -{ - RefPtr<Range> searchRange(rangeOfContents(m_frame->document())); - RefPtr<Range> resultRange = findPlainText(searchRange.get(), target, true, caseFlag); - ExceptionCode ec = 0; - - while (!insideVisibleArea(resultRange.get())) { - searchRange->setStartAfter(resultRange->endContainer(), ec); - if (searchRange->startContainer() == searchRange->endContainer()) - return Range::create(m_frame->document()); - resultRange = findPlainText(searchRange.get(), target, true, caseFlag); - } - - return resultRange; -} - -PassRefPtr<Range> Editor::lastVisibleRange(const String& target, bool caseFlag) -{ - RefPtr<Range> searchRange(rangeOfContents(m_frame->document())); - RefPtr<Range> resultRange = findPlainText(searchRange.get(), target, false, caseFlag); - ExceptionCode ec = 0; - - while (!insideVisibleArea(resultRange.get())) { - searchRange->setEndBefore(resultRange->startContainer(), ec); - if (searchRange->startContainer() == searchRange->endContainer()) - return Range::create(m_frame->document()); - resultRange = findPlainText(searchRange.get(), target, false, caseFlag); - } - - return resultRange; -} - -PassRefPtr<Range> Editor::nextVisibleRange(Range* currentRange, const String& target, bool forward, bool caseFlag, bool wrapFlag) -{ - if (m_frame->excludeFromTextSearch()) - return Range::create(m_frame->document()); - - RefPtr<Range> resultRange = currentRange; - RefPtr<Range> searchRange(rangeOfContents(m_frame->document())); - ExceptionCode ec = 0; - - for ( ; !insideVisibleArea(resultRange.get()); resultRange = findPlainText(searchRange.get(), target, forward, caseFlag)) { - if (resultRange->collapsed(ec)) { - if (!resultRange->startContainer()->isInShadowTree()) - break; - searchRange = rangeOfContents(m_frame->document()); - if (forward) - searchRange->setStartAfter(resultRange->startContainer()->shadowAncestorNode(), ec); - else - searchRange->setEndBefore(resultRange->startContainer()->shadowAncestorNode(), ec); - continue; - } - - if (forward) - searchRange->setStartAfter(resultRange->endContainer(), ec); - else - searchRange->setEndBefore(resultRange->startContainer(), ec); - - Node* shadowTreeRoot = searchRange->shadowTreeRootNode(); - if (searchRange->collapsed(ec) && shadowTreeRoot) { - if (forward) - searchRange->setEnd(shadowTreeRoot, shadowTreeRoot->childNodeCount(), ec); - else - searchRange->setStartBefore(shadowTreeRoot, ec); - } - - if (searchRange->startContainer()->isDocumentNode() && searchRange->endContainer()->isDocumentNode()) - break; - } - - if (insideVisibleArea(resultRange.get())) - return resultRange; - - if (!wrapFlag) - return Range::create(m_frame->document()); - - if (forward) - return firstVisibleRange(target, caseFlag); - - return lastVisibleRange(target, caseFlag); -} - -} // namespace WebCore diff --git a/WebCore/editing/Editor.h b/WebCore/editing/Editor.h deleted file mode 100644 index 7478ff9..0000000 --- a/WebCore/editing/Editor.h +++ /dev/null @@ -1,302 +0,0 @@ -/* - * Copyright (C) 2006, 2007, 2008 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 Editor_h -#define Editor_h - -#include "ClipboardAccessPolicy.h" -#include "Color.h" -#include "EditAction.h" -#include "EditorDeleteAction.h" -#include "EditorInsertAction.h" -#include "SelectionController.h" - -namespace WebCore { - -class CSSStyleDeclaration; -class Clipboard; -class DeleteButtonController; -class EditCommand; -class EditorClient; -class EditorInternalCommand; -class HTMLElement; -class HitTestResult; -class Pasteboard; -class SimpleFontData; -class Text; - -struct CompositionUnderline { - CompositionUnderline() - : startOffset(0), endOffset(0), thick(false) { } - CompositionUnderline(unsigned s, unsigned e, const Color& c, bool t) - : startOffset(s), endOffset(e), color(c), thick(t) { } - unsigned startOffset; - unsigned endOffset; - Color color; - bool thick; -}; - -enum TriState { FalseTriState, TrueTriState, MixedTriState }; -enum EditorCommandSource { CommandFromMenuOrKeyBinding, CommandFromDOM, CommandFromDOMWithUserInterface }; -enum WritingDirection { NaturalWritingDirection, LeftToRightWritingDirection, RightToLeftWritingDirection }; - -class Editor { -public: - Editor(Frame*); - ~Editor(); - - EditorClient* client() const; - Frame* frame() const { return m_frame; } - DeleteButtonController* deleteButtonController() const { return m_deleteButtonController.get(); } - EditCommand* lastEditCommand() { return m_lastEditCommand.get(); } - - void handleKeyboardEvent(KeyboardEvent*); - void handleInputMethodKeydown(KeyboardEvent*); - - bool canEdit() const; - bool canEditRichly() const; - - bool canDHTMLCut(); - bool canDHTMLCopy(); - bool canDHTMLPaste(); - bool tryDHTMLCopy(); - bool tryDHTMLCut(); - bool tryDHTMLPaste(); - - bool canCut() const; - bool canCopy() const; - bool canPaste() const; - bool canDelete() const; - bool canSmartCopyOrDelete(); - - void cut(); - void copy(); - void paste(); - void pasteAsPlainText(); - void performDelete(); - - void copyURL(const KURL&, const String&); - void copyImage(const HitTestResult&); - - void indent(); - void outdent(); - void transpose(); - - bool shouldInsertFragment(PassRefPtr<DocumentFragment>, PassRefPtr<Range>, EditorInsertAction); - bool shouldInsertText(const String&, Range*, EditorInsertAction) const; - bool shouldShowDeleteInterface(HTMLElement*) const; - bool shouldDeleteRange(Range*) const; - bool shouldApplyStyle(CSSStyleDeclaration*, Range*); - - void respondToChangedSelection(const Selection& oldSelection); - void respondToChangedContents(const Selection& endingSelection); - - TriState selectionHasStyle(CSSStyleDeclaration*) const; - const SimpleFontData* fontForSelection(bool&) const; - - TriState selectionUnorderedListState() const; - TriState selectionOrderedListState() const; - PassRefPtr<Node> insertOrderedList(); - PassRefPtr<Node> insertUnorderedList(); - bool canIncreaseSelectionListLevel(); - bool canDecreaseSelectionListLevel(); - PassRefPtr<Node> increaseSelectionListLevel(); - PassRefPtr<Node> increaseSelectionListLevelOrdered(); - PassRefPtr<Node> increaseSelectionListLevelUnordered(); - void decreaseSelectionListLevel(); - - void removeFormattingAndStyle(); - - void clearLastEditCommand(); - - bool deleteWithDirection(SelectionController::EDirection, TextGranularity, bool killRing, bool isTypingAction); - void deleteSelectionWithSmartDelete(bool smartDelete); - bool dispatchCPPEvent(const AtomicString&, ClipboardAccessPolicy); - - Node* removedAnchor() const { return m_removedAnchor.get(); } - void setRemovedAnchor(PassRefPtr<Node> n) { m_removedAnchor = n; } - - void applyStyle(CSSStyleDeclaration*, EditAction = EditActionUnspecified); - void applyParagraphStyle(CSSStyleDeclaration*, EditAction = EditActionUnspecified); - void applyStyleToSelection(CSSStyleDeclaration*, EditAction); - void applyParagraphStyleToSelection(CSSStyleDeclaration*, EditAction); - - void appliedEditing(PassRefPtr<EditCommand>); - void unappliedEditing(PassRefPtr<EditCommand>); - void reappliedEditing(PassRefPtr<EditCommand>); - - bool selectionStartHasStyle(CSSStyleDeclaration*) const; - - bool clientIsEditable() const; - - class Command { - public: - Command(); - Command(PassRefPtr<Frame>, const EditorInternalCommand*, EditorCommandSource); - - bool execute(const String& parameter = String(), Event* triggeringEvent = 0) const; - bool execute(Event* triggeringEvent) const; - - bool isSupported() const; - bool isEnabled(Event* triggeringEvent = 0) const; - - TriState state(Event* triggeringEvent = 0) const; - String value(Event* triggeringEvent = 0) const; - - bool isTextInsertion() const; - - private: - RefPtr<Frame> m_frame; - const EditorInternalCommand* m_command; - EditorCommandSource m_source; - }; - Command command(const String& commandName); // Default is CommandFromMenuOrKeyBinding. - Command command(const String& commandName, EditorCommandSource); - - bool insertText(const String&, Event* triggeringEvent); - bool insertTextWithoutSendingTextEvent(const String&, bool selectInsertedText, Event* triggeringEvent); - bool insertLineBreak(); - bool insertParagraphSeparator(); - - bool isContinuousSpellCheckingEnabled(); - void toggleContinuousSpellChecking(); - bool isGrammarCheckingEnabled(); - void toggleGrammarChecking(); - void ignoreSpelling(); - void learnSpelling(); - int spellCheckerDocumentTag(); - bool isSelectionUngrammatical(); - bool isSelectionMisspelled(); - Vector<String> guessesForMisspelledSelection(); - Vector<String> guessesForUngrammaticalSelection(); - void markMisspellingsAfterTypingToPosition(const VisiblePosition&); - void markMisspellings(const Selection&); - void markBadGrammar(const Selection&); - void advanceToNextMisspelling(bool startBeforeSelection = false); - void showSpellingGuessPanel(); - bool spellingPanelIsShowing(); - - bool shouldBeginEditing(Range*); - bool shouldEndEditing(Range*); - - void clearUndoRedoOperations(); - bool canUndo(); - void undo(); - bool canRedo(); - void redo(); - - void didBeginEditing(); - void didEndEditing(); - void didWriteSelectionToPasteboard(); - - void showFontPanel(); - void showStylesPanel(); - void showColorPanel(); - void toggleBold(); - void toggleUnderline(); - void setBaseWritingDirection(WritingDirection); - - bool smartInsertDeleteEnabled(); - - // international text input composition - bool hasComposition() const { return m_compositionNode; } - void setComposition(const String&, const Vector<CompositionUnderline>&, unsigned selectionStart, unsigned selectionEnd); - void confirmComposition(); - void confirmComposition(const String&); // if no existing composition, replaces selection - void confirmCompositionWithoutDisturbingSelection(); - PassRefPtr<Range> compositionRange() const; - bool getCompositionSelection(unsigned& selectionStart, unsigned& selectionEnd) const; - - // getting international text input composition state (for use by InlineTextBox) - Text* compositionNode() const { return m_compositionNode.get(); } - unsigned compositionStart() const { return m_compositionStart; } - unsigned compositionEnd() const { return m_compositionEnd; } - bool compositionUsesCustomUnderlines() const { return !m_customCompositionUnderlines.isEmpty(); } - const Vector<CompositionUnderline>& customCompositionUnderlines() const { return m_customCompositionUnderlines; } - - bool ignoreCompositionSelectionChange() const { return m_ignoreCompositionSelectionChange; } - - void setStartNewKillRingSequence(bool); - - PassRefPtr<Range> rangeForPoint(const IntPoint& windowPoint); - - void clear(); - - Selection selectionForCommand(Event*); - - void appendToKillRing(const String&); - void prependToKillRing(const String&); - String yankFromKillRing(); - void startNewKillRingSequence(); - void setKillRingToYankedState(); - - PassRefPtr<Range> selectedRange(); - - // We should make these functions private when their callers in Frame are moved over here to Editor - bool insideVisibleArea(const IntPoint&) const; - bool insideVisibleArea(Range*) const; - PassRefPtr<Range> nextVisibleRange(Range*, const String&, bool forward, bool caseFlag, bool wrapFlag); - -private: - Frame* m_frame; - OwnPtr<DeleteButtonController> m_deleteButtonController; - RefPtr<EditCommand> m_lastEditCommand; - RefPtr<Node> m_removedAnchor; - - RefPtr<Text> m_compositionNode; - unsigned m_compositionStart; - unsigned m_compositionEnd; - Vector<CompositionUnderline> m_customCompositionUnderlines; - bool m_ignoreCompositionSelectionChange; - bool m_shouldStartNewKillRingSequence; - - bool canDeleteRange(Range*) const; - bool canSmartReplaceWithPasteboard(Pasteboard*); - PassRefPtr<Clipboard> newGeneralClipboard(ClipboardAccessPolicy); - void pasteAsPlainTextWithPasteboard(Pasteboard*); - void pasteWithPasteboard(Pasteboard*, bool allowPlainText); - void replaceSelectionWithFragment(PassRefPtr<DocumentFragment>, bool selectReplacement, bool smartReplace, bool matchStyle); - void replaceSelectionWithText(const String&, bool selectReplacement, bool smartReplace); - void writeSelectionToPasteboard(Pasteboard*); - void revealSelectionAfterEditingOperation(); - - void selectComposition(); - void confirmComposition(const String&, bool preserveSelection); - void setIgnoreCompositionSelectionChange(bool ignore); - - void addToKillRing(Range*, bool prepend); - - PassRefPtr<Range> firstVisibleRange(const String&, bool caseFlag); - PassRefPtr<Range> lastVisibleRange(const String&, bool caseFlag); -}; - -inline void Editor::setStartNewKillRingSequence(bool flag) -{ - m_shouldStartNewKillRingSequence = flag; -} - -} // namespace WebCore - -#endif // Editor_h diff --git a/WebCore/editing/EditorCommand.cpp b/WebCore/editing/EditorCommand.cpp deleted file mode 100644 index bd896ad..0000000 --- a/WebCore/editing/EditorCommand.cpp +++ /dev/null @@ -1,1418 +0,0 @@ -/* - * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. - * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) - * - * 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 "AtomicString.h" -#include "CSSPropertyNames.h" -#include "CreateLinkCommand.h" -#include "DocumentFragment.h" -#include "Editor.h" -#include "EditorClient.h" -#include "Event.h" -#include "EventHandler.h" -#include "Frame.h" -#include "FormatBlockCommand.h" -#include "HTMLFontElement.h" -#include "HTMLImageElement.h" -#include "IndentOutdentCommand.h" -#include "InsertListCommand.h" -#include "Page.h" -#include "ReplaceSelectionCommand.h" -#include "Scrollbar.h" -#include "Settings.h" -#include "Sound.h" -#include "TypingCommand.h" -#include "UnlinkCommand.h" -#include "htmlediting.h" -#include "markup.h" - -namespace WebCore { - -using namespace HTMLNames; - -class EditorInternalCommand { -public: - bool (*execute)(Frame*, Event*, EditorCommandSource, const String&); - bool (*isSupported)(Frame*, EditorCommandSource); - bool (*isEnabled)(Frame*, Event*, EditorCommandSource); - TriState (*state)(Frame*, Event*); - String (*value)(Frame*, Event*); - bool isTextInsertion; - bool allowExecutionWhenDisabled; -}; - -typedef HashMap<String, const EditorInternalCommand*, CaseFoldingHash> CommandMap; - -static const bool notTextInsertion = false; -static const bool isTextInsertion = true; - -static const bool allowExecutionWhenDisabled = true; -static const bool doNotAllowExecutionWhenDisabled = false; - -// Related to Editor::selectionForCommand. -// Certain operations continue to use the target control's selection even if the event handler -// already moved the selection outside of the text control. -static Frame* targetFrame(Frame* frame, Event* event) -{ - if (!event) - return frame; - Node* node = event->target()->toNode(); - if (!node) - return frame; - return node->document()->frame(); -} - -static bool executeApplyStyle(Frame* frame, EditorCommandSource source, EditAction action, int propertyID, const String& propertyValue) -{ - RefPtr<CSSMutableStyleDeclaration> style = CSSMutableStyleDeclaration::create(); - style->setProperty(propertyID, propertyValue); - // FIXME: We don't call shouldApplyStyle when the source is DOM; is there a good reason for that? - switch (source) { - case CommandFromMenuOrKeyBinding: - frame->editor()->applyStyleToSelection(style.get(), action); - return true; - case CommandFromDOM: - case CommandFromDOMWithUserInterface: - frame->editor()->applyStyle(style.get()); - return true; - } - ASSERT_NOT_REACHED(); - return false; -} - -static bool executeApplyStyle(Frame* frame, EditorCommandSource source, EditAction action, int propertyID, const char* propertyValue) -{ - return executeApplyStyle(frame, source, action, propertyID, String(propertyValue)); -} - -static bool executeApplyStyle(Frame* frame, EditorCommandSource source, EditAction action, int propertyID, int propertyValue) -{ - RefPtr<CSSMutableStyleDeclaration> style = CSSMutableStyleDeclaration::create(); - style->setProperty(propertyID, propertyValue); - // FIXME: We don't call shouldApplyStyle when the source is DOM; is there a good reason for that? - switch (source) { - case CommandFromMenuOrKeyBinding: - frame->editor()->applyStyleToSelection(style.get(), action); - return true; - case CommandFromDOM: - case CommandFromDOMWithUserInterface: - frame->editor()->applyStyle(style.get()); - return true; - } - ASSERT_NOT_REACHED(); - return false; -} - -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); - style->setProperty(propertyID, frame->editor()->selectionStartHasStyle(style.get()) ? offValue : onValue); - // FIXME: We don't call shouldApplyStyle when the source is DOM; is there a good reason for that? - switch (source) { - case CommandFromMenuOrKeyBinding: - frame->editor()->applyStyleToSelection(style.get(), action); - return true; - case CommandFromDOM: - case CommandFromDOMWithUserInterface: - frame->editor()->applyStyle(style.get()); - return true; - } - ASSERT_NOT_REACHED(); - return false; -} - -static bool executeApplyParagraphStyle(Frame* frame, EditorCommandSource source, EditAction action, int propertyID, const String& propertyValue) -{ - RefPtr<CSSMutableStyleDeclaration> style = CSSMutableStyleDeclaration::create(); - style->setProperty(propertyID, propertyValue); - // FIXME: We don't call shouldApplyStyle when the source is DOM; is there a good reason for that? - switch (source) { - case CommandFromMenuOrKeyBinding: - frame->editor()->applyParagraphStyleToSelection(style.get(), action); - return true; - case CommandFromDOM: - case CommandFromDOMWithUserInterface: - frame->editor()->applyParagraphStyle(style.get()); - return true; - } - ASSERT_NOT_REACHED(); - return false; -} - -static bool executeInsertFragment(Frame* frame, PassRefPtr<DocumentFragment> fragment) -{ - applyCommand(ReplaceSelectionCommand::create(frame->document(), fragment, - false, false, false, true, false, EditActionUnspecified)); - return true; -} - -static bool executeInsertNode(Frame* frame, PassRefPtr<Node> content) -{ - RefPtr<DocumentFragment> fragment = new DocumentFragment(frame->document()); - ExceptionCode ec = 0; - fragment->appendChild(content, ec); - if (ec) - return false; - return executeInsertFragment(frame, fragment.release()); -} - -static bool expandSelectionToGranularity(Frame* frame, TextGranularity granularity) -{ - Selection selection = frame->selection()->selection(); - selection.expandUsingGranularity(granularity); - RefPtr<Range> newRange = selection.toRange(); - if (!newRange) - return false; - ExceptionCode ec = 0; - if (newRange->collapsed(ec)) - return false; - RefPtr<Range> oldRange = frame->selection()->selection().toRange(); - EAffinity affinity = frame->selection()->affinity(); - if (!frame->editor()->client()->shouldChangeSelectedRange(oldRange.get(), newRange.get(), affinity, false)) - return false; - frame->selection()->setSelectedRange(newRange.get(), affinity, true); - return true; -} - -static TriState stateStyle(Frame* frame, int propertyID, const char* desiredValue) -{ - RefPtr<CSSMutableStyleDeclaration> style = CSSMutableStyleDeclaration::create(); - style->setProperty(propertyID, desiredValue); - return frame->editor()->selectionHasStyle(style.get()); -} - -static String valueStyle(Frame* frame, int propertyID) -{ - return frame->selectionStartStylePropertyValue(propertyID); -} - -static int verticalScrollDistance(Frame* frame) -{ - Node* focusedNode = frame->document()->focusedNode(); - if (!focusedNode) - return 0; - RenderObject* renderer = focusedNode->renderer(); - if (!renderer) - return 0; - RenderStyle* style = renderer->style(); - if (!style) - return 0; - if (!(style->overflowY() == OSCROLL || style->overflowY() == OAUTO || renderer->isTextArea())) - return 0; - int height = renderer->clientHeight(); - return max((height + 1) / 2, height - cAmountToKeepWhenPaging); -} - -static RefPtr<Range> unionDOMRanges(Range* a, Range* b) -{ - ExceptionCode ec = 0; - Range* start = a->compareBoundaryPoints(Range::START_TO_START, b, ec) <= 0 ? a : b; - ASSERT(!ec); - Range* end = a->compareBoundaryPoints(Range::END_TO_END, b, ec) <= 0 ? b : a; - ASSERT(!ec); - - return Range::create(a->startContainer(ec)->ownerDocument(), start->startContainer(ec), start->startOffset(ec), end->endContainer(ec), end->endOffset(ec)); -} - -// Execute command functions - -static bool executeBackColor(Frame* frame, Event*, EditorCommandSource source, const String& value) -{ - return executeApplyStyle(frame, source, EditActionSetBackgroundColor, CSSPropertyBackgroundColor, value); -} - -static bool executeCopy(Frame* frame, Event*, EditorCommandSource, const String&) -{ - frame->editor()->copy(); - return true; -} - -static bool executeCreateLink(Frame* frame, Event*, EditorCommandSource, const String& value) -{ - // FIXME: If userInterface is true, we should display a dialog box to let the user enter a URL. - if (value.isEmpty()) - return false; - applyCommand(CreateLinkCommand::create(frame->document(), value)); - return true; -} - -static bool executeCut(Frame* frame, Event*, EditorCommandSource, const String&) -{ - frame->editor()->cut(); - return true; -} - -static bool executeDelete(Frame* frame, Event*, EditorCommandSource source, const String&) -{ - switch (source) { - case CommandFromMenuOrKeyBinding: - // Doesn't modify the text if the current selection isn't a range. - frame->editor()->performDelete(); - return true; - case CommandFromDOM: - case CommandFromDOMWithUserInterface: - // If the current selection is a caret, delete the preceding character. IE performs forwardDelete, but we currently side with Firefox. - // Doesn't scroll to make the selection visible, or modify the kill ring (this time, siding with IE, not Firefox). - TypingCommand::deleteKeyPressed(frame->document(), frame->selectionGranularity() == WordGranularity); - return true; - } - ASSERT_NOT_REACHED(); - return false; -} - -static bool executeDeleteBackward(Frame* frame, Event*, EditorCommandSource, const String&) -{ - frame->editor()->deleteWithDirection(SelectionController::BACKWARD, CharacterGranularity, false, true); - return true; -} - -static bool executeDeleteBackwardByDecomposingPreviousCharacter(Frame* frame, Event*, EditorCommandSource, const String&) -{ - LOG_ERROR("DeleteBackwardByDecomposingPreviousCharacter is not implemented, doing DeleteBackward instead"); - frame->editor()->deleteWithDirection(SelectionController::BACKWARD, CharacterGranularity, false, true); - return true; -} - -static bool executeDeleteForward(Frame* frame, Event*, EditorCommandSource source, const String&) -{ - frame->editor()->deleteWithDirection(SelectionController::FORWARD, CharacterGranularity, false, true); - return true; -} - -static bool executeDeleteToBeginningOfLine(Frame* frame, Event*, EditorCommandSource, const String&) -{ - frame->editor()->deleteWithDirection(SelectionController::BACKWARD, LineBoundary, true, false); - return true; -} - -static bool executeDeleteToBeginningOfParagraph(Frame* frame, Event*, EditorCommandSource, const String&) -{ - frame->editor()->deleteWithDirection(SelectionController::BACKWARD, ParagraphBoundary, true, false); - return true; -} - -static bool executeDeleteToEndOfLine(Frame* frame, Event*, EditorCommandSource, const String&) -{ - // Despite its name, this command should delete the newline at the end of - // a paragraph if you are at the end of a paragraph (like DeleteToEndOfParagraph). - frame->editor()->deleteWithDirection(SelectionController::FORWARD, LineBoundary, true, false); - return true; -} - -static bool executeDeleteToEndOfParagraph(Frame* frame, Event*, EditorCommandSource, const String&) -{ - // Despite its name, this command should delete the newline at the end of - // a paragraph if you are at the end of a paragraph. - frame->editor()->deleteWithDirection(SelectionController::FORWARD, ParagraphBoundary, true, false); - return true; -} - -static bool executeDeleteToMark(Frame* frame, Event*, EditorCommandSource, const String&) -{ - RefPtr<Range> mark = frame->mark().toRange(); - if (mark) { - SelectionController* selection = frame->selection(); - bool selected = selection->setSelectedRange(unionDOMRanges(mark.get(), frame->editor()->selectedRange().get()).get(), DOWNSTREAM, true); - ASSERT(selected); - if (!selected) - return false; - } - frame->editor()->performDelete(); - frame->setMark(frame->selection()->selection()); - return true; -} - -static bool executeDeleteWordBackward(Frame* frame, Event*, EditorCommandSource, const String&) -{ - frame->editor()->deleteWithDirection(SelectionController::BACKWARD, WordGranularity, true, false); - return true; -} - -static bool executeDeleteWordForward(Frame* frame, Event*, EditorCommandSource, const String&) -{ - frame->editor()->deleteWithDirection(SelectionController::FORWARD, WordGranularity, true, false); - return true; -} - -static bool executeFindString(Frame* frame, Event*, EditorCommandSource, const String& value) -{ - return frame->findString(value, true, false, true, false); -} - -static bool executeFontName(Frame* frame, Event*, EditorCommandSource source, const String& value) -{ - return executeApplyStyle(frame, source, EditActionSetFont, CSSPropertyFontFamily, value); -} - -static bool executeFontSize(Frame* frame, Event*, EditorCommandSource source, const String& value) -{ - int size; - if (!HTMLFontElement::cssValueFromFontSizeNumber(value, size)) - return false; - return executeApplyStyle(frame, source, EditActionChangeAttributes, CSSPropertyFontSize, size); -} - -static bool executeFontSizeDelta(Frame* frame, Event*, EditorCommandSource source, const String& value) -{ - return executeApplyStyle(frame, source, EditActionChangeAttributes, CSSPropertyWebkitFontSizeDelta, value); -} - -static bool executeForeColor(Frame* frame, Event*, EditorCommandSource source, const String& value) -{ - return executeApplyStyle(frame, source, EditActionSetColor, CSSPropertyColor, value); -} - -static bool executeFormatBlock(Frame* frame, Event*, EditorCommandSource, const String& value) -{ - String tagName = value.lower(); - if (tagName[0] == '<' && tagName[tagName.length() - 1] == '>') - tagName = tagName.substring(1, tagName.length() - 2); - if (!validBlockTag(tagName)) - return false; - applyCommand(FormatBlockCommand::create(frame->document(), tagName)); - return true; -} - -static bool executeForwardDelete(Frame* frame, Event*, EditorCommandSource source, const String&) -{ - switch (source) { - case CommandFromMenuOrKeyBinding: - frame->editor()->deleteWithDirection(SelectionController::FORWARD, CharacterGranularity, false, true); - return true; - case CommandFromDOM: - case CommandFromDOMWithUserInterface: - // Doesn't scroll to make the selection visible, or modify the kill ring. - // ForwardDelete is not implemented in IE or Firefox, so this behavior is only needed for - // backward compatibility with ourselves, and for consistency with Delete. - TypingCommand::forwardDeleteKeyPressed(frame->document()); - return true; - } - ASSERT_NOT_REACHED(); - return false; -} - -static bool executeIgnoreSpelling(Frame* frame, Event*, EditorCommandSource, const String&) -{ - frame->editor()->ignoreSpelling(); - return true; -} - -static bool executeIndent(Frame* frame, Event*, EditorCommandSource, const String&) -{ - applyCommand(IndentOutdentCommand::create(frame->document(), IndentOutdentCommand::Indent)); - return true; -} - -static bool executeInsertBacktab(Frame* frame, Event* event, EditorCommandSource, const String&) -{ - return targetFrame(frame, event)->eventHandler()->handleTextInputEvent("\t", event, false, true); -} - -static bool executeInsertHorizontalRule(Frame* frame, Event*, EditorCommandSource, const String& value) -{ - RefPtr<HTMLElement> hr = new HTMLElement(hrTag, frame->document()); - if (!value.isEmpty()) - hr->setId(value); - return executeInsertNode(frame, hr.release()); -} - -static bool executeInsertHTML(Frame* frame, Event*, EditorCommandSource, const String& value) -{ - return executeInsertFragment(frame, createFragmentFromMarkup(frame->document(), value, "")); -} - -static bool executeInsertImage(Frame* frame, Event*, EditorCommandSource, const String& value) -{ - // FIXME: If userInterface is true, we should display a dialog box and let the user choose a local image. - RefPtr<HTMLImageElement> image = new HTMLImageElement(imgTag, frame->document()); - image->setSrc(value); - return executeInsertNode(frame, image.release()); -} - -static bool executeInsertLineBreak(Frame* frame, Event* event, EditorCommandSource source, const String&) -{ - switch (source) { - case CommandFromMenuOrKeyBinding: - return targetFrame(frame, event)->eventHandler()->handleTextInputEvent("\n", event, true); - case CommandFromDOM: - case CommandFromDOMWithUserInterface: - // 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()); - return true; - } - ASSERT_NOT_REACHED(); - return false; -} - -static bool executeInsertNewline(Frame* frame, Event* event, EditorCommandSource, const String&) -{ - Frame* targetFrame = WebCore::targetFrame(frame, event); - return targetFrame->eventHandler()->handleTextInputEvent("\n", event, !targetFrame->editor()->canEditRichly()); -} - -static bool executeInsertNewlineInQuotedContent(Frame* frame, Event*, EditorCommandSource, const String&) -{ - TypingCommand::insertParagraphSeparatorInQuotedContent(frame->document()); - return true; -} - -static bool executeInsertOrderedList(Frame* frame, Event*, EditorCommandSource, const String& value) -{ - applyCommand(InsertListCommand::create(frame->document(), InsertListCommand::OrderedList, value)); - return true; -} - -static bool executeInsertParagraph(Frame* frame, Event*, EditorCommandSource, const String&) -{ - TypingCommand::insertParagraphSeparator(frame->document()); - return true; -} - -static bool executeInsertTab(Frame* frame, Event* event, EditorCommandSource, const String&) -{ - return targetFrame(frame, event)->eventHandler()->handleTextInputEvent("\t", event, false, false); -} - -static bool executeInsertText(Frame* frame, Event*, EditorCommandSource, const String& value) -{ - TypingCommand::insertText(frame->document(), value); - return true; -} - -static bool executeInsertUnorderedList(Frame* frame, Event*, EditorCommandSource, const String& value) -{ - applyCommand(InsertListCommand::create(frame->document(), InsertListCommand::UnorderedList, value)); - return true; -} - -static bool executeJustifyCenter(Frame* frame, Event*, EditorCommandSource source, const String&) -{ - return executeApplyParagraphStyle(frame, source, EditActionCenter, CSSPropertyTextAlign, "center"); -} - -static bool executeJustifyFull(Frame* frame, Event*, EditorCommandSource source, const String&) -{ - return executeApplyParagraphStyle(frame, source, EditActionJustify, CSSPropertyTextAlign, "justify"); -} - -static bool executeJustifyLeft(Frame* frame, Event*, EditorCommandSource source, const String&) -{ - return executeApplyParagraphStyle(frame, source, EditActionAlignLeft, CSSPropertyTextAlign, "left"); -} - -static bool executeJustifyRight(Frame* frame, Event*, EditorCommandSource source, const String&) -{ - return executeApplyParagraphStyle(frame, source, EditActionAlignRight, CSSPropertyTextAlign, "right"); -} - -static bool executeMoveBackward(Frame* frame, Event*, EditorCommandSource, const String&) -{ - frame->selection()->modify(SelectionController::MOVE, SelectionController::BACKWARD, CharacterGranularity, true); - return true; -} - -static bool executeMoveBackwardAndModifySelection(Frame* frame, Event*, EditorCommandSource, const String&) -{ - frame->selection()->modify(SelectionController::EXTEND, SelectionController::BACKWARD, CharacterGranularity, true); - return true; -} - -static bool executeMoveDown(Frame* frame, Event*, EditorCommandSource, const String&) -{ - frame->selection()->modify(SelectionController::MOVE, SelectionController::FORWARD, LineGranularity, true); - return true; -} - -static bool executeMoveDownAndModifySelection(Frame* frame, Event*, EditorCommandSource, const String&) -{ - frame->selection()->modify(SelectionController::EXTEND, SelectionController::FORWARD, LineGranularity, true); - return true; -} - -static bool executeMoveForward(Frame* frame, Event*, EditorCommandSource, const String&) -{ - frame->selection()->modify(SelectionController::MOVE, SelectionController::FORWARD, CharacterGranularity, true); - return true; -} - -static bool executeMoveForwardAndModifySelection(Frame* frame, Event*, EditorCommandSource, const String&) -{ - frame->selection()->modify(SelectionController::EXTEND, SelectionController::FORWARD, CharacterGranularity, true); - return true; -} - -static bool executeMoveLeft(Frame* frame, Event*, EditorCommandSource, const String&) -{ - frame->selection()->modify(SelectionController::MOVE, SelectionController::LEFT, CharacterGranularity, true); - return true; -} - -static bool executeMoveLeftAndModifySelection(Frame* frame, Event*, EditorCommandSource, const String&) -{ - frame->selection()->modify(SelectionController::EXTEND, SelectionController::LEFT, CharacterGranularity, true); - return true; -} - -static bool executeMovePageDown(Frame* frame, Event*, EditorCommandSource, const String&) -{ - int distance = verticalScrollDistance(frame); - if (!distance) - return false; - return frame->selection()->modify(SelectionController::MOVE, distance, true); -} - -static bool executeMovePageDownAndModifySelection(Frame* frame, Event*, EditorCommandSource, const String&) -{ - int distance = verticalScrollDistance(frame); - if (!distance) - return false; - return frame->selection()->modify(SelectionController::EXTEND, distance, true); -} - -static bool executeMovePageUp(Frame* frame, Event*, EditorCommandSource, const String&) -{ - int distance = verticalScrollDistance(frame); - if (!distance) - return false; - return frame->selection()->modify(SelectionController::MOVE, -distance, true); -} - -static bool executeMovePageUpAndModifySelection(Frame* frame, Event*, EditorCommandSource, const String&) -{ - int distance = verticalScrollDistance(frame); - if (!distance) - return false; - return frame->selection()->modify(SelectionController::EXTEND, -distance, true); -} - -static bool executeMoveRight(Frame* frame, Event*, EditorCommandSource, const String&) -{ - frame->selection()->modify(SelectionController::MOVE, SelectionController::RIGHT, CharacterGranularity, true); - return true; -} - -static bool executeMoveRightAndModifySelection(Frame* frame, Event*, EditorCommandSource, const String&) -{ - frame->selection()->modify(SelectionController::EXTEND, SelectionController::RIGHT, CharacterGranularity, true); - return true; -} - -static bool executeMoveToBeginningOfDocument(Frame* frame, Event*, EditorCommandSource, const String&) -{ - frame->selection()->modify(SelectionController::MOVE, SelectionController::BACKWARD, DocumentBoundary, true); - return true; -} - -static bool executeMoveToBeginningOfDocumentAndModifySelection(Frame* frame, Event*, EditorCommandSource, const String&) -{ - frame->selection()->modify(SelectionController::EXTEND, SelectionController::BACKWARD, DocumentBoundary, true); - return true; -} - -static bool executeMoveToBeginningOfLine(Frame* frame, Event*, EditorCommandSource, const String&) -{ - frame->selection()->modify(SelectionController::MOVE, SelectionController::BACKWARD, LineBoundary, true); - return true; -} - -static bool executeMoveToBeginningOfLineAndModifySelection(Frame* frame, Event*, EditorCommandSource, const String&) -{ - frame->selection()->modify(SelectionController::EXTEND, SelectionController::BACKWARD, LineBoundary, true); - return true; -} - -static bool executeMoveToBeginningOfParagraph(Frame* frame, Event*, EditorCommandSource, const String&) -{ - frame->selection()->modify(SelectionController::MOVE, SelectionController::BACKWARD, ParagraphBoundary, true); - return true; -} - -static bool executeMoveToBeginningOfParagraphAndModifySelection(Frame* frame, Event*, EditorCommandSource, const String&) -{ - frame->selection()->modify(SelectionController::EXTEND, SelectionController::BACKWARD, ParagraphBoundary, true); - return true; -} - -static bool executeMoveToBeginningOfSentence(Frame* frame, Event*, EditorCommandSource, const String&) -{ - frame->selection()->modify(SelectionController::MOVE, SelectionController::BACKWARD, SentenceBoundary, true); - return true; -} - -static bool executeMoveToBeginningOfSentenceAndModifySelection(Frame* frame, Event*, EditorCommandSource, const String&) -{ - frame->selection()->modify(SelectionController::EXTEND, SelectionController::BACKWARD, SentenceBoundary, true); - return true; -} - -static bool executeMoveToEndOfDocument(Frame* frame, Event*, EditorCommandSource, const String&) -{ - frame->selection()->modify(SelectionController::MOVE, SelectionController::FORWARD, DocumentBoundary, true); - return true; -} - -static bool executeMoveToEndOfDocumentAndModifySelection(Frame* frame, Event*, EditorCommandSource, const String&) -{ - frame->selection()->modify(SelectionController::EXTEND, SelectionController::FORWARD, DocumentBoundary, true); - return true; -} - -static bool executeMoveToEndOfSentence(Frame* frame, Event*, EditorCommandSource, const String&) -{ - frame->selection()->modify(SelectionController::MOVE, SelectionController::FORWARD, SentenceBoundary, true); - return true; -} - -static bool executeMoveToEndOfSentenceAndModifySelection(Frame* frame, Event*, EditorCommandSource, const String&) -{ - frame->selection()->modify(SelectionController::EXTEND, SelectionController::FORWARD, SentenceBoundary, true); - return true; -} - -static bool executeMoveToEndOfLine(Frame* frame, Event*, EditorCommandSource, const String&) -{ - frame->selection()->modify(SelectionController::MOVE, SelectionController::FORWARD, LineBoundary, true); - return true; -} - -static bool executeMoveToEndOfLineAndModifySelection(Frame* frame, Event*, EditorCommandSource, const String&) -{ - frame->selection()->modify(SelectionController::EXTEND, SelectionController::FORWARD, LineBoundary, true); - return true; -} - -static bool executeMoveToEndOfParagraph(Frame* frame, Event*, EditorCommandSource, const String&) -{ - frame->selection()->modify(SelectionController::MOVE, SelectionController::FORWARD, ParagraphBoundary, true); - return true; -} - -static bool executeMoveToEndOfParagraphAndModifySelection(Frame* frame, Event*, EditorCommandSource, const String&) -{ - frame->selection()->modify(SelectionController::EXTEND, SelectionController::FORWARD, ParagraphBoundary, true); - return true; -} - -static bool executeMoveParagraphBackwardAndModifySelection(Frame* frame, Event*, EditorCommandSource, const String&) -{ - frame->selection()->modify(SelectionController::EXTEND, SelectionController::BACKWARD, ParagraphGranularity, true); - return true; -} - -static bool executeMoveParagraphForwardAndModifySelection(Frame* frame, Event*, EditorCommandSource, const String&) -{ - frame->selection()->modify(SelectionController::EXTEND, SelectionController::FORWARD, ParagraphGranularity, true); - return true; -} - -static bool executeMoveUp(Frame* frame, Event*, EditorCommandSource, const String&) -{ - frame->selection()->modify(SelectionController::MOVE, SelectionController::BACKWARD, LineGranularity, true); - return true; -} - -static bool executeMoveUpAndModifySelection(Frame* frame, Event*, EditorCommandSource, const String&) -{ - frame->selection()->modify(SelectionController::EXTEND, SelectionController::BACKWARD, LineGranularity, true); - return true; -} - -static bool executeMoveWordBackward(Frame* frame, Event*, EditorCommandSource, const String&) -{ - frame->selection()->modify(SelectionController::MOVE, SelectionController::BACKWARD, WordGranularity, true); - return true; -} - -static bool executeMoveWordBackwardAndModifySelection(Frame* frame, Event*, EditorCommandSource, const String&) -{ - frame->selection()->modify(SelectionController::EXTEND, SelectionController::BACKWARD, WordGranularity, true); - return true; -} - -static bool executeMoveWordForward(Frame* frame, Event*, EditorCommandSource, const String&) -{ - frame->selection()->modify(SelectionController::MOVE, SelectionController::FORWARD, WordGranularity, true); - return true; -} - -static bool executeMoveWordForwardAndModifySelection(Frame* frame, Event*, EditorCommandSource, const String&) -{ - frame->selection()->modify(SelectionController::EXTEND, SelectionController::FORWARD, WordGranularity, true); - return true; -} - -static bool executeMoveWordLeft(Frame* frame, Event*, EditorCommandSource, const String&) -{ - frame->selection()->modify(SelectionController::MOVE, SelectionController::LEFT, WordGranularity, true); - return true; -} - -static bool executeMoveWordLeftAndModifySelection(Frame* frame, Event*, EditorCommandSource, const String&) -{ - frame->selection()->modify(SelectionController::EXTEND, SelectionController::LEFT, WordGranularity, true); - return true; -} - -static bool executeMoveWordRight(Frame* frame, Event*, EditorCommandSource, const String&) -{ - frame->selection()->modify(SelectionController::MOVE, SelectionController::RIGHT, WordGranularity, true); - return true; -} - -static bool executeMoveWordRightAndModifySelection(Frame* frame, Event*, EditorCommandSource, const String&) -{ - frame->selection()->modify(SelectionController::EXTEND, SelectionController::RIGHT, WordGranularity, true); - return true; -} - -static bool executeOutdent(Frame* frame, Event*, EditorCommandSource, const String&) -{ - applyCommand(IndentOutdentCommand::create(frame->document(), IndentOutdentCommand::Outdent)); - return true; -} - -static bool executePaste(Frame* frame, Event*, EditorCommandSource, const String&) -{ - frame->editor()->paste(); - return true; -} - -static bool executePasteAndMatchStyle(Frame* frame, Event*, EditorCommandSource, const String&) -{ - frame->editor()->pasteAsPlainText(); - return true; -} - -static bool executePrint(Frame* frame, Event*, EditorCommandSource, const String&) -{ - Page* page = frame->page(); - if (!page) - return false; - page->chrome()->print(frame); - return true; -} - -static bool executeRedo(Frame* frame, Event*, EditorCommandSource, const String&) -{ - frame->editor()->redo(); - return true; -} - -static bool executeRemoveFormat(Frame* frame, Event*, EditorCommandSource, const String&) -{ - frame->editor()->removeFormattingAndStyle(); - return true; -} - -static bool executeSelectAll(Frame* frame, Event*, EditorCommandSource, const String&) -{ - frame->selection()->selectAll(); - return true; -} - -static bool executeSelectLine(Frame* frame, Event*, EditorCommandSource, const String&) -{ - return expandSelectionToGranularity(frame, LineGranularity); -} - -static bool executeSelectParagraph(Frame* frame, Event*, EditorCommandSource, const String&) -{ - return expandSelectionToGranularity(frame, ParagraphGranularity); -} - -static bool executeSelectSentence(Frame* frame, Event*, EditorCommandSource, const String&) -{ - return expandSelectionToGranularity(frame, SentenceGranularity); -} - -static bool executeSelectToMark(Frame* frame, Event*, EditorCommandSource, const String&) -{ - RefPtr<Range> mark = frame->mark().toRange(); - RefPtr<Range> selection = frame->editor()->selectedRange(); - if (!mark || !selection) { - systemBeep(); - return false; - } - frame->selection()->setSelectedRange(unionDOMRanges(mark.get(), selection.get()).get(), DOWNSTREAM, true); - return true; -} - -static bool executeSelectWord(Frame* frame, Event*, EditorCommandSource, const String&) -{ - return expandSelectionToGranularity(frame, WordGranularity); -} - -static bool executeSetMark(Frame* frame, Event*, EditorCommandSource, const String&) -{ - frame->setMark(frame->selection()->selection()); - return true; -} - -static bool executeStrikethrough(Frame* frame, Event*, EditorCommandSource source, const String&) -{ - return executeToggleStyle(frame, source, EditActionChangeAttributes, CSSPropertyWebkitTextDecorationsInEffect, "none", "line-through"); -} - -static bool executeSubscript(Frame* frame, Event*, EditorCommandSource source, const String&) -{ - return executeApplyStyle(frame, source, EditActionSubscript, CSSPropertyVerticalAlign, "sub"); -} - -static bool executeSuperscript(Frame* frame, Event*, EditorCommandSource source, const String&) -{ - return executeApplyStyle(frame, source, EditActionSuperscript, CSSPropertyVerticalAlign, "super"); -} - -static bool executeSwapWithMark(Frame* frame, Event*, EditorCommandSource, const String&) -{ - const Selection& mark = frame->mark(); - const Selection& selection = frame->selection()->selection(); - if (mark.isNone() || selection.isNone()) { - systemBeep(); - return false; - } - frame->selection()->setSelection(mark); - frame->setMark(selection); - return true; -} - -static bool executeToggleBold(Frame* frame, Event*, EditorCommandSource source, const String&) -{ - return executeToggleStyle(frame, source, EditActionChangeAttributes, CSSPropertyFontWeight, "normal", "bold"); -} - -static bool executeToggleItalic(Frame* frame, Event*, EditorCommandSource source, const String&) -{ - return executeToggleStyle(frame, source, EditActionChangeAttributes, CSSPropertyFontStyle, "normal", "italic"); -} - -static bool executeTranspose(Frame* frame, Event*, EditorCommandSource, const String&) -{ - frame->editor()->transpose(); - return true; -} - -static bool executeUnderline(Frame* frame, Event*, EditorCommandSource source, const String&) -{ - // FIXME: This currently clears overline, line-through, and blink as an unwanted side effect. - return executeToggleStyle(frame, source, EditActionUnderline, CSSPropertyWebkitTextDecorationsInEffect, "none", "underline"); -} - -static bool executeUndo(Frame* frame, Event*, EditorCommandSource, const String&) -{ - frame->editor()->undo(); - return true; -} - -static bool executeUnlink(Frame* frame, Event*, EditorCommandSource, const String&) -{ - applyCommand(UnlinkCommand::create(frame->document())); - return true; -} - -static bool executeUnscript(Frame* frame, Event*, EditorCommandSource source, const String&) -{ - return executeApplyStyle(frame, source, EditActionUnscript, CSSPropertyVerticalAlign, "baseline"); -} - -static bool executeUnselect(Frame* frame, Event*, EditorCommandSource, const String&) -{ - frame->selection()->clear(); - return true; -} - -static bool executeYank(Frame* frame, Event*, EditorCommandSource, const String&) -{ - frame->editor()->insertTextWithoutSendingTextEvent(frame->editor()->yankFromKillRing(), false, 0); - frame->editor()->setKillRingToYankedState(); - return true; -} - -static bool executeYankAndSelect(Frame* frame, Event*, EditorCommandSource, const String&) -{ - frame->editor()->insertTextWithoutSendingTextEvent(frame->editor()->yankFromKillRing(), true, 0); - frame->editor()->setKillRingToYankedState(); - return true; -} - -// Supported functions - -static bool supported(Frame*, EditorCommandSource) -{ - return true; -} - -static bool supportedFromMenuOrKeyBinding(Frame*, EditorCommandSource source) -{ - return source == CommandFromMenuOrKeyBinding; -} - -static bool supportedPaste(Frame* frame, EditorCommandSource source) -{ - switch (source) { - case CommandFromMenuOrKeyBinding: - return true; - case CommandFromDOM: - case CommandFromDOMWithUserInterface: { - Settings* settings = frame ? frame->settings() : 0; - return settings && settings->isDOMPasteAllowed(); - } - } - ASSERT_NOT_REACHED(); - return false; -} - -// Enabled functions - -static bool enabled(Frame*, Event*, EditorCommandSource) -{ - return true; -} - -static bool enabledAnySelection(Frame* frame, Event*, EditorCommandSource) -{ - return frame->selection()->isCaretOrRange(); -} - -static bool enabledAnySelectionAndMark(Frame* frame, Event*, EditorCommandSource) -{ - return frame->selection()->isCaretOrRange() && frame->mark().isCaretOrRange(); -} - -static bool enableCaretInEditableText(Frame* frame, Event* event, EditorCommandSource) -{ - const Selection& selection = frame->editor()->selectionForCommand(event); - return selection.isCaret() && selection.isContentEditable(); -} - -static bool enabledCopy(Frame* frame, Event*, EditorCommandSource) -{ - return frame->editor()->canDHTMLCopy() || frame->editor()->canCopy(); -} - -static bool enabledCut(Frame* frame, Event*, EditorCommandSource) -{ - return frame->editor()->canDHTMLCut() || frame->editor()->canCut(); -} - -static bool enabledDelete(Frame* frame, Event* event, EditorCommandSource source) -{ - switch (source) { - case CommandFromMenuOrKeyBinding: - // "Delete" from menu only affects selected range, just like Cut but without affecting pasteboard - return frame->editor()->canDHTMLCut() || frame->editor()->canCut(); - case CommandFromDOM: - case CommandFromDOMWithUserInterface: - // "Delete" from DOM is like delete/backspace keypress, affects selected range if non-empty, - // otherwise removes a character - return frame->editor()->selectionForCommand(event).isContentEditable(); - } - ASSERT_NOT_REACHED(); - return false; -} - -static bool enabledInEditableText(Frame* frame, Event* event, EditorCommandSource) -{ - return frame->editor()->selectionForCommand(event).isContentEditable(); -} - -static bool enabledInRichlyEditableText(Frame* frame, Event*, EditorCommandSource) -{ - return frame->selection()->isCaretOrRange() && frame->selection()->isContentRichlyEditable(); -} - -static bool enabledPaste(Frame* frame, Event*, EditorCommandSource) -{ - return frame->editor()->canPaste(); -} - -static bool enabledRangeInEditableText(Frame* frame, Event*, EditorCommandSource) -{ - return frame->selection()->isRange() && frame->selection()->isContentEditable(); -} - -static bool enabledRangeInRichlyEditableText(Frame* frame, Event*, EditorCommandSource) -{ - return frame->selection()->isRange() && frame->selection()->isContentRichlyEditable(); -} - -static bool enabledRedo(Frame* frame, Event*, EditorCommandSource) -{ - return frame->editor()->canRedo(); -} - -static bool enabledUndo(Frame* frame, Event*, EditorCommandSource) -{ - return frame->editor()->canUndo(); -} - -// State functions - -static TriState stateNone(Frame*, Event*) -{ - return FalseTriState; -} - -static TriState stateBold(Frame* frame, Event*) -{ - return stateStyle(frame, CSSPropertyFontWeight, "bold"); -} - -static TriState stateItalic(Frame* frame, Event*) -{ - return stateStyle(frame, CSSPropertyFontStyle, "italic"); -} - -static TriState stateOrderedList(Frame* frame, Event*) -{ - return frame->editor()->selectionOrderedListState(); -} - -static TriState stateStrikethrough(Frame* frame, Event*) -{ - return stateStyle(frame, CSSPropertyTextDecoration, "line-through"); -} - -static TriState stateSubscript(Frame* frame, Event*) -{ - return stateStyle(frame, CSSPropertyVerticalAlign, "sub"); -} - -static TriState stateSuperscript(Frame* frame, Event*) -{ - return stateStyle(frame, CSSPropertyVerticalAlign, "super"); -} - -static TriState stateUnderline(Frame* frame, Event*) -{ - return stateStyle(frame, CSSPropertyTextDecoration, "underline"); -} - -static TriState stateUnorderedList(Frame* frame, Event*) -{ - return frame->editor()->selectionUnorderedListState(); -} - -// Value functions - -static String valueNull(Frame*, Event*) -{ - return String(); -} - -String valueBackColor(Frame* frame, Event*) -{ - return valueStyle(frame, CSSPropertyBackgroundColor); -} - -String valueFontName(Frame* frame, Event*) -{ - return valueStyle(frame, CSSPropertyFontFamily); -} - -String valueFontSize(Frame* frame, Event*) -{ - return valueStyle(frame, CSSPropertyFontSize); -} - -String valueFontSizeDelta(Frame* frame, Event*) -{ - return valueStyle(frame, CSSPropertyWebkitFontSizeDelta); -} - -String valueForeColor(Frame* frame, Event*) -{ - return valueStyle(frame, CSSPropertyColor); -} - -// Map of functions - -static const CommandMap& createCommandMap() -{ - struct CommandEntry { const char* name; EditorInternalCommand command; }; - - static const CommandEntry commands[] = { - { "AlignCenter", { executeJustifyCenter, supportedFromMenuOrKeyBinding, enabledInRichlyEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "AlignJustified", { executeJustifyFull, supportedFromMenuOrKeyBinding, enabledInRichlyEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "AlignLeft", { executeJustifyLeft, supportedFromMenuOrKeyBinding, enabledInRichlyEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "AlignRight", { executeJustifyRight, supportedFromMenuOrKeyBinding, enabledInRichlyEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "BackColor", { executeBackColor, supported, enabledInRichlyEditableText, stateNone, valueBackColor, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "BackwardDelete", { executeDeleteBackward, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, // FIXME: remove BackwardDelete when Safari for Windows stops using it. - { "Bold", { executeToggleBold, supported, enabledInRichlyEditableText, stateBold, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "Copy", { executeCopy, supported, enabledCopy, stateNone, valueNull, notTextInsertion, allowExecutionWhenDisabled } }, - { "CreateLink", { executeCreateLink, supported, enabledInRichlyEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "Cut", { executeCut, supported, enabledCut, stateNone, valueNull, notTextInsertion, allowExecutionWhenDisabled } }, - { "Delete", { executeDelete, supported, enabledDelete, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "DeleteBackward", { executeDeleteBackward, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "DeleteBackwardByDecomposingPreviousCharacter", { executeDeleteBackwardByDecomposingPreviousCharacter, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "DeleteForward", { executeDeleteForward, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "DeleteToBeginningOfLine", { executeDeleteToBeginningOfLine, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "DeleteToBeginningOfParagraph", { executeDeleteToBeginningOfParagraph, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "DeleteToEndOfLine", { executeDeleteToEndOfLine, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "DeleteToEndOfParagraph", { executeDeleteToEndOfParagraph, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "DeleteToMark", { executeDeleteToMark, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "DeleteWordBackward", { executeDeleteWordBackward, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "DeleteWordForward", { executeDeleteWordForward, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "FindString", { executeFindString, supported, enabled, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "FontName", { executeFontName, supported, enabledInEditableText, stateNone, valueFontName, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "FontSize", { executeFontSize, supported, enabledInEditableText, stateNone, valueFontSize, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "FontSizeDelta", { executeFontSizeDelta, supported, enabledInEditableText, stateNone, valueFontSizeDelta, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "ForeColor", { executeForeColor, supported, enabledInRichlyEditableText, stateNone, valueForeColor, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "FormatBlock", { executeFormatBlock, supported, enabledInRichlyEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "ForwardDelete", { executeForwardDelete, supported, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "HiliteColor", { executeBackColor, supported, enabledInRichlyEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "IgnoreSpelling", { executeIgnoreSpelling, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "Indent", { executeIndent, supported, enabledInRichlyEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "InsertBacktab", { executeInsertBacktab, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, isTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "InsertHorizontalRule", { executeInsertHorizontalRule, supported, enabledInRichlyEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "InsertHTML", { executeInsertHTML, supported, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "InsertImage", { executeInsertImage, supported, enabledInRichlyEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "InsertLineBreak", { executeInsertLineBreak, supported, enabledInEditableText, stateNone, valueNull, isTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "InsertNewline", { executeInsertNewline, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, isTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "InsertNewlineInQuotedContent", { executeInsertNewlineInQuotedContent, supported, enabledInRichlyEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "InsertOrderedList", { executeInsertOrderedList, supported, enabledInRichlyEditableText, stateOrderedList, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "InsertParagraph", { executeInsertParagraph, supported, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "InsertTab", { executeInsertTab, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, isTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "InsertText", { executeInsertText, supported, enabledInEditableText, stateNone, valueNull, isTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "InsertUnorderedList", { executeInsertUnorderedList, supported, enabledInRichlyEditableText, stateUnorderedList, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "Italic", { executeToggleItalic, supported, enabledInRichlyEditableText, stateItalic, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "JustifyCenter", { executeJustifyCenter, supported, enabledInRichlyEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "JustifyFull", { executeJustifyFull, supported, enabledInRichlyEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "JustifyLeft", { executeJustifyLeft, supported, enabledInRichlyEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "JustifyNone", { executeJustifyLeft, supported, enabledInRichlyEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "JustifyRight", { executeJustifyRight, supported, enabledInRichlyEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "MoveBackward", { executeMoveBackward, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "MoveBackwardAndModifySelection", { executeMoveBackwardAndModifySelection, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "MoveDown", { executeMoveDown, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "MoveDownAndModifySelection", { executeMoveDownAndModifySelection, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "MoveForward", { executeMoveForward, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "MoveForwardAndModifySelection", { executeMoveForwardAndModifySelection, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "MoveLeft", { executeMoveLeft, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "MoveLeftAndModifySelection", { executeMoveLeftAndModifySelection, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "MovePageDown", { executeMovePageDown, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "MovePageDownAndModifySelection", { executeMovePageDownAndModifySelection, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "MovePageUp", { executeMovePageUp, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "MovePageUpAndModifySelection", { executeMovePageUpAndModifySelection, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "MoveParagraphBackwardAndModifySelection", { executeMoveParagraphBackwardAndModifySelection, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "MoveParagraphForwardAndModifySelection", { executeMoveParagraphForwardAndModifySelection, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "MoveRight", { executeMoveRight, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "MoveRightAndModifySelection", { executeMoveRightAndModifySelection, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "MoveToBeginningOfDocument", { executeMoveToBeginningOfDocument, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "MoveToBeginningOfDocumentAndModifySelection", { executeMoveToBeginningOfDocumentAndModifySelection, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "MoveToBeginningOfLine", { executeMoveToBeginningOfLine, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "MoveToBeginningOfLineAndModifySelection", { executeMoveToBeginningOfLineAndModifySelection, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "MoveToBeginningOfParagraph", { executeMoveToBeginningOfParagraph, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "MoveToBeginningOfParagraphAndModifySelection", { executeMoveToBeginningOfParagraphAndModifySelection, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "MoveToBeginningOfSentence", { executeMoveToBeginningOfSentence, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "MoveToBeginningOfSentenceAndModifySelection", { executeMoveToBeginningOfSentenceAndModifySelection, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "MoveToEndOfDocument", { executeMoveToEndOfDocument, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "MoveToEndOfDocumentAndModifySelection", { executeMoveToEndOfDocumentAndModifySelection, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "MoveToEndOfLine", { executeMoveToEndOfLine, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "MoveToEndOfLineAndModifySelection", { executeMoveToEndOfLineAndModifySelection, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "MoveToEndOfParagraph", { executeMoveToEndOfParagraph, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "MoveToEndOfParagraphAndModifySelection", { executeMoveToEndOfParagraphAndModifySelection, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "MoveToEndOfSentence", { executeMoveToEndOfSentence, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "MoveToEndOfSentenceAndModifySelection", { executeMoveToEndOfSentenceAndModifySelection, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "MoveUp", { executeMoveUp, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "MoveUpAndModifySelection", { executeMoveUpAndModifySelection, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "MoveWordBackward", { executeMoveWordBackward, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "MoveWordBackwardAndModifySelection", { executeMoveWordBackwardAndModifySelection, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "MoveWordForward", { executeMoveWordForward, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "MoveWordForwardAndModifySelection", { executeMoveWordForwardAndModifySelection, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "MoveWordLeft", { executeMoveWordLeft, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "MoveWordLeftAndModifySelection", { executeMoveWordLeftAndModifySelection, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "MoveWordRight", { executeMoveWordRight, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "MoveWordRightAndModifySelection", { executeMoveWordRightAndModifySelection, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "Outdent", { executeOutdent, supported, enabledInRichlyEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "Paste", { executePaste, supportedPaste, enabledPaste, stateNone, valueNull, notTextInsertion, allowExecutionWhenDisabled } }, - { "PasteAndMatchStyle", { executePasteAndMatchStyle, supportedPaste, enabledPaste, stateNone, valueNull, notTextInsertion, allowExecutionWhenDisabled } }, - { "Print", { executePrint, supported, enabled, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "Redo", { executeRedo, supported, enabledRedo, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "RemoveFormat", { executeRemoveFormat, supported, enabledRangeInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "SelectAll", { executeSelectAll, supported, enabled, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "SelectLine", { executeSelectLine, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "SelectParagraph", { executeSelectParagraph, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "SelectSentence", { executeSelectSentence, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "SelectToMark", { executeSelectToMark, supportedFromMenuOrKeyBinding, enabledAnySelectionAndMark, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "SelectWord", { executeSelectWord, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "SetMark", { executeSetMark, supportedFromMenuOrKeyBinding, enabledAnySelection, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "Strikethrough", { executeStrikethrough, supported, enabledInRichlyEditableText, stateStrikethrough, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "Subscript", { executeSubscript, supported, enabledInRichlyEditableText, stateSubscript, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "Superscript", { executeSuperscript, supported, enabledInRichlyEditableText, stateSuperscript, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "SwapWithMark", { executeSwapWithMark, supportedFromMenuOrKeyBinding, enabledAnySelectionAndMark, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "ToggleBold", { executeToggleBold, supportedFromMenuOrKeyBinding, enabledInRichlyEditableText, stateBold, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "ToggleItalic", { executeToggleItalic, supportedFromMenuOrKeyBinding, enabledInRichlyEditableText, stateItalic, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "ToggleUnderline", { executeUnderline, supportedFromMenuOrKeyBinding, enabledInRichlyEditableText, stateUnderline, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "Transpose", { executeTranspose, supported, enableCaretInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "Underline", { executeUnderline, supported, enabledInRichlyEditableText, stateUnderline, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "Undo", { executeUndo, supported, enabledUndo, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "Unlink", { executeUnlink, supported, enabledRangeInRichlyEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "Unscript", { executeUnscript, supportedFromMenuOrKeyBinding, enabledInRichlyEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "Unselect", { executeUnselect, supported, enabledAnySelection, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "Yank", { executeYank, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - { "YankAndSelect", { executeYankAndSelect, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } }, - }; - - // These unsupported commands are listed here since they appear in the Microsoft - // documentation used as the starting point for our DOM executeCommand support. - // - // 2D-Position (not supported) - // AbsolutePosition (not supported) - // BlockDirLTR (not supported) - // BlockDirRTL (not supported) - // BrowseMode (not supported) - // ClearAuthenticationCache (not supported) - // CreateBookmark (not supported) - // DirLTR (not supported) - // DirRTL (not supported) - // EditMode (not supported) - // InlineDirLTR (not supported) - // InlineDirRTL (not supported) - // InsertButton (not supported) - // InsertFieldSet (not supported) - // InsertIFrame (not supported) - // InsertInputButton (not supported) - // InsertInputCheckbox (not supported) - // InsertInputFileUpload (not supported) - // InsertInputHidden (not supported) - // InsertInputImage (not supported) - // InsertInputPassword (not supported) - // InsertInputRadio (not supported) - // InsertInputReset (not supported) - // InsertInputSubmit (not supported) - // InsertInputText (not supported) - // InsertMarquee (not supported) - // InsertSelectDropDown (not supported) - // InsertSelectListBox (not supported) - // InsertTextArea (not supported) - // LiveResize (not supported) - // MultipleSelection (not supported) - // Open (not supported) - // Overwrite (not supported) - // PlayImage (not supported) - // Refresh (not supported) - // RemoveParaFormat (not supported) - // SaveAs (not supported) - // SizeToControl (not supported) - // SizeToControlHeight (not supported) - // SizeToControlWidth (not supported) - // Stop (not supported) - // StopImage (not supported) - // Unbookmark (not supported) - - CommandMap& commandMap = *new CommandMap; - - const unsigned numCommands = sizeof(commands) / sizeof(commands[0]); - for (unsigned i = 0; i < numCommands; i++) { - ASSERT(!commandMap.get(commands[i].name)); - commandMap.set(commands[i].name, &commands[i].command); - } - - return commandMap; -} - -Editor::Command Editor::command(const String& commandName) -{ - return command(commandName, CommandFromMenuOrKeyBinding); -} - -Editor::Command Editor::command(const String& commandName, EditorCommandSource source) -{ - if (commandName.isEmpty()) - return Command(); - - static const CommandMap& commandMap = createCommandMap(); - const EditorInternalCommand* internalCommand = commandMap.get(commandName); - return internalCommand ? Command(m_frame, internalCommand, source) : Command(); -} - -Editor::Command::Command() - : m_command(0) - , m_source() -{ -} - -Editor::Command::Command(PassRefPtr<Frame> frame, const EditorInternalCommand* command, EditorCommandSource source) - : m_frame(frame) - , m_command(command) - , m_source(source) -{ - ASSERT(m_frame); - ASSERT(m_command); -} - -bool Editor::Command::execute(const String& parameter, Event* triggeringEvent) const -{ - if (!isEnabled(triggeringEvent)) { - // Let certain commands be executed when performed explicitly even if they are disabled. - if (!isSupported() || !m_frame || !m_frame->document() || !m_command->allowExecutionWhenDisabled) - return false; - } - m_frame->document()->updateLayoutIgnorePendingStylesheets(); - return m_command->execute(m_frame.get(), triggeringEvent, m_source, parameter); -} - -bool Editor::Command::execute(Event* triggeringEvent) const -{ - return execute(String(), triggeringEvent); -} - -bool Editor::Command::isSupported() const -{ - return m_command && m_command->isSupported(m_frame.get(), m_source); -} - -bool Editor::Command::isEnabled(Event* triggeringEvent) const -{ - if (!isSupported() || !m_frame || !m_frame->document()) - return false; - return m_command->isEnabled(m_frame.get(), triggeringEvent, m_source); -} - -TriState Editor::Command::state(Event* triggeringEvent) const -{ - if (!isSupported() || !m_frame || !m_frame->document()) - return FalseTriState; - return m_command->state(m_frame.get(), triggeringEvent); -} - -String Editor::Command::value(Event* triggeringEvent) const -{ - if (!isSupported() || !m_frame || !m_frame->document()) - return String(); - return m_command->value(m_frame.get(), triggeringEvent); -} - -bool Editor::Command::isTextInsertion() const -{ - return m_command && m_command->isTextInsertion; -} - -} // namespace WebCore diff --git a/WebCore/editing/EditorDeleteAction.h b/WebCore/editing/EditorDeleteAction.h deleted file mode 100644 index 00bf683..0000000 --- a/WebCore/editing/EditorDeleteAction.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (C) 2006 Apple Computer, 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 EditorDeleteAction_h -#define EditorDeleteAction_h - -namespace WebCore { - -enum EditorDeleteAction { - deleteSelectionAction, - deleteKeyAction, - forwardDeleteKeyAction -}; - -} // namespace - -#endif - diff --git a/WebCore/editing/EditorInsertAction.h b/WebCore/editing/EditorInsertAction.h deleted file mode 100644 index 5b732dc..0000000 --- a/WebCore/editing/EditorInsertAction.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (C) 2006 Apple Computer, 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 EditorInsertAction_h -#define EditorInsertAction_h - -namespace WebCore { - -// This must be kept in sync with WebViewInsertAction defined in WebEditingDelegate.h -enum EditorInsertAction { - EditorInsertActionTyped, - EditorInsertActionPasted, - EditorInsertActionDropped, -}; - -} // namespace - -#endif diff --git a/WebCore/editing/FormatBlockCommand.cpp b/WebCore/editing/FormatBlockCommand.cpp deleted file mode 100644 index 64f7c2d..0000000 --- a/WebCore/editing/FormatBlockCommand.cpp +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Copyright (C) 2006 Apple Computer, 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 "Element.h" -#include "FormatBlockCommand.h" -#include "Document.h" -#include "htmlediting.h" -#include "HTMLElement.h" -#include "HTMLNames.h" -#include "visible_units.h" - -namespace WebCore { - -using namespace HTMLNames; - -FormatBlockCommand::FormatBlockCommand(Document* document, const String& tagName) - : CompositeEditCommand(document), m_tagName(tagName) -{ -} - -bool FormatBlockCommand::modifyRange() -{ - ASSERT(endingSelection().isRange()); - VisiblePosition visibleStart = endingSelection().visibleStart(); - VisiblePosition visibleEnd = endingSelection().visibleEnd(); - VisiblePosition startOfLastParagraph = startOfParagraph(visibleEnd); - - if (startOfParagraph(visibleStart) == startOfLastParagraph) - return false; - - setEndingSelection(visibleStart); - doApply(); - visibleStart = endingSelection().visibleStart(); - VisiblePosition nextParagraph = endOfParagraph(visibleStart).next(); - while (nextParagraph.isNotNull() && nextParagraph != startOfLastParagraph) { - setEndingSelection(nextParagraph); - doApply(); - nextParagraph = endOfParagraph(endingSelection().visibleStart()).next(); - } - setEndingSelection(visibleEnd); - doApply(); - visibleEnd = endingSelection().visibleEnd(); - setEndingSelection(Selection(visibleStart.deepEquivalent(), visibleEnd.deepEquivalent(), DOWNSTREAM)); - - return true; -} - -void FormatBlockCommand::doApply() -{ - if (endingSelection().isNone()) - return; - - if (!endingSelection().rootEditableElement()) - return; - - VisiblePosition visibleEnd = endingSelection().visibleEnd(); - VisiblePosition visibleStart = endingSelection().visibleStart(); - // When a selection ends at the start of a paragraph, we rarely paint - // the selection gap before that paragraph, because there often is no gap. - // In a case like this, it's not obvious to the user that the selection - // ends "inside" that paragraph, so it would be confusing if FormatBlock - // operated on that paragraph. - // FIXME: We paint the gap before some paragraphs that are indented with left - // margin/padding, but not others. We should make the gap painting more consistent and - // then use a left margin/padding rule here. - if (visibleEnd != visibleStart && isStartOfParagraph(visibleEnd)) - setEndingSelection(Selection(visibleStart, visibleEnd.previous(true))); - - if (endingSelection().isRange() && modifyRange()) - return; - - ExceptionCode ec; - String localName, prefix; - if (!Document::parseQualifiedName(m_tagName, prefix, localName, ec)) - return; - QualifiedName qTypeOfBlock(prefix, localName, xhtmlNamespaceURI); - - Node* refNode = enclosingBlockFlowElement(endingSelection().visibleStart()); - if (refNode->hasTagName(qTypeOfBlock)) - // We're already in a block with the format we want, so we don't have to do anything - return; - - VisiblePosition paragraphStart = startOfParagraph(endingSelection().visibleStart()); - VisiblePosition paragraphEnd = endOfParagraph(endingSelection().visibleStart()); - VisiblePosition blockStart = startOfBlock(endingSelection().visibleStart()); - VisiblePosition blockEnd = endOfBlock(endingSelection().visibleStart()); - RefPtr<Node> blockNode = createElement(document(), m_tagName); - RefPtr<Node> placeholder = createBreakElement(document()); - - Node* root = endingSelection().start().node()->rootEditableElement(); - if (validBlockTag(refNode->nodeName().lower()) && - paragraphStart == blockStart && paragraphEnd == blockEnd && - refNode != root && !root->isDescendantOf(refNode)) - // Already in a valid block tag that only contains the current paragraph, so we can swap with the new tag - insertNodeBefore(blockNode.get(), refNode); - else { - // Avoid inserting inside inline elements that surround paragraphStart with upstream(). - // This is only to avoid creating bloated markup. - insertNodeAt(blockNode.get(), paragraphStart.deepEquivalent().upstream()); - } - appendNode(placeholder.get(), blockNode.get()); - - VisiblePosition destination(Position(placeholder.get(), 0)); - if (paragraphStart == paragraphEnd && !lineBreakExistsAtPosition(paragraphStart)) { - setEndingSelection(destination); - return; - } - moveParagraph(paragraphStart, paragraphEnd, destination, true, false); -} - -} diff --git a/WebCore/editing/FormatBlockCommand.h b/WebCore/editing/FormatBlockCommand.h deleted file mode 100644 index 0e84bc1..0000000 --- a/WebCore/editing/FormatBlockCommand.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (C) 2006, 2008 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 FormatBlockCommand_h -#define FormatBlockCommand_h - -#include "CompositeEditCommand.h" - -namespace WebCore { - -class FormatBlockCommand : public CompositeEditCommand { -public: - static PassRefPtr<FormatBlockCommand> create(Document* document, const String& tagName) - { - return adoptRef(new FormatBlockCommand(document, tagName)); - } - -private: - FormatBlockCommand(Document*, const String& tagName); - - virtual void doApply(); - virtual EditAction editingAction() const { return EditActionFormatBlock; } - - bool modifyRange(); - String m_tagName; -}; - -} // namespace WebCore - -#endif // FormatBlockCommand_h diff --git a/WebCore/editing/HTMLInterchange.cpp b/WebCore/editing/HTMLInterchange.cpp deleted file mode 100644 index 024ac9f..0000000 --- a/WebCore/editing/HTMLInterchange.cpp +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright (C) 2004, 2008 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 "HTMLInterchange.h" - -#include "CharacterNames.h" -#include "Text.h" -#include "TextIterator.h" - -namespace WebCore { - -namespace { - -String convertedSpaceString() -{ - static String convertedSpaceString; - if (convertedSpaceString.isNull()) { - convertedSpaceString = "<span class=\""; - convertedSpaceString += AppleConvertedSpace; - convertedSpaceString += "\">"; - convertedSpaceString.append(noBreakSpace); - convertedSpaceString += "</span>"; - } - return convertedSpaceString; -} - -} // end anonymous namespace - -String convertHTMLTextToInterchangeFormat(const String& in, const Text* node) -{ - // Assume all the text comes from node. - if (node->renderer() && node->renderer()->style()->preserveNewline()) - return in; - - Vector<UChar> s; - - unsigned i = 0; - unsigned consumed = 0; - while (i < in.length()) { - consumed = 1; - if (isCollapsibleWhitespace(in[i])) { - // count number of adjoining spaces - unsigned j = i + 1; - while (j < in.length() && isCollapsibleWhitespace(in[j])) - j++; - unsigned count = j - i; - consumed = count; - while (count) { - unsigned add = count % 3; - switch (add) { - case 0: - append(s, convertedSpaceString()); - s.append(' '); - append(s, convertedSpaceString()); - add = 3; - break; - case 1: - if (i == 0 || i + 1 == in.length()) // at start or end of string - append(s, convertedSpaceString()); - else - s.append(' '); - break; - case 2: - if (i == 0) { - // at start of string - append(s, convertedSpaceString()); - s.append(' '); - } else if (i + 2 == in.length()) { - // at end of string - append(s, convertedSpaceString()); - append(s, convertedSpaceString()); - } else { - append(s, convertedSpaceString()); - s.append(' '); - } - break; - } - count -= add; - } - } else - s.append(in[i]); - i += consumed; - } - - return String::adopt(s); -} - -} // namespace WebCore diff --git a/WebCore/editing/HTMLInterchange.h b/WebCore/editing/HTMLInterchange.h deleted file mode 100644 index 3b68efb..0000000 --- a/WebCore/editing/HTMLInterchange.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (C) 2004, 2008 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 HTMLInterchange_h -#define HTMLInterchange_h - -namespace WebCore { - -class String; -class Text; - -#define AppleInterchangeNewline "Apple-interchange-newline" -#define AppleConvertedSpace "Apple-converted-space" -#define ApplePasteAsQuotation "Apple-paste-as-quotation" -#define AppleStyleSpanClass "Apple-style-span" -#define AppleTabSpanClass "Apple-tab-span" - -enum EAnnotateForInterchange { DoNotAnnotateForInterchange, AnnotateForInterchange }; - -String convertHTMLTextToInterchangeFormat(const String&, const Text*); - -} - -#endif diff --git a/WebCore/editing/IndentOutdentCommand.cpp b/WebCore/editing/IndentOutdentCommand.cpp deleted file mode 100644 index 385f1b7..0000000 --- a/WebCore/editing/IndentOutdentCommand.cpp +++ /dev/null @@ -1,298 +0,0 @@ -/* - * Copyright (C) 2006, 2008 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 (IndentOutdentCommandINCLUDING, 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 "Element.h" -#include "IndentOutdentCommand.h" -#include "InsertListCommand.h" -#include "Document.h" -#include "htmlediting.h" -#include "HTMLElement.h" -#include "HTMLNames.h" -#include "InsertLineBreakCommand.h" -#include "Range.h" -#include "SplitElementCommand.h" -#include "TextIterator.h" -#include "visible_units.h" - -namespace WebCore { - -using namespace HTMLNames; - -static String indentBlockquoteString() -{ - static String string = "webkit-indent-blockquote"; - return string; -} - -static PassRefPtr<Element> createIndentBlockquoteElement(Document* document) -{ - RefPtr<Element> indentBlockquoteElement = createElement(document, "blockquote"); - indentBlockquoteElement->setAttribute(classAttr, indentBlockquoteString()); - indentBlockquoteElement->setAttribute(styleAttr, "margin: 0 0 0 40px; border: none; padding: 0px;"); - return indentBlockquoteElement.release(); -} - -static bool isIndentBlockquote(const Node* node) -{ - if (!node || !node->hasTagName(blockquoteTag) || !node->isElementNode()) - return false; - - const Element* elem = static_cast<const Element*>(node); - return elem->getAttribute(classAttr) == indentBlockquoteString(); -} - -static bool isListOrIndentBlockquote(const Node* node) -{ - return node && (node->hasTagName(ulTag) || node->hasTagName(olTag) || isIndentBlockquote(node)); -} - -IndentOutdentCommand::IndentOutdentCommand(Document* document, EIndentType typeOfAction, int marginInPixels) - : CompositeEditCommand(document), m_typeOfAction(typeOfAction), m_marginInPixels(marginInPixels) -{} - -// This function is a workaround for moveParagraph's tendency to strip blockquotes. It updates lastBlockquote to point to the -// correct level for the current paragraph, and returns a pointer to a placeholder br where the insertion should be performed. -Node* IndentOutdentCommand::prepareBlockquoteLevelForInsertion(VisiblePosition& currentParagraph, Node** lastBlockquote) -{ - int currentBlockquoteLevel = 0; - int lastBlockquoteLevel = 0; - Node* node = currentParagraph.deepEquivalent().node(); - while ((node = enclosingNodeOfType(Position(node->parentNode(), 0), &isIndentBlockquote))) - currentBlockquoteLevel++; - node = *lastBlockquote; - while ((node = enclosingNodeOfType(Position(node->parentNode(), 0), &isIndentBlockquote))) - lastBlockquoteLevel++; - while (currentBlockquoteLevel > lastBlockquoteLevel) { - RefPtr<Node> newBlockquote = createIndentBlockquoteElement(document()); - appendNode(newBlockquote.get(), *lastBlockquote); - *lastBlockquote = newBlockquote.get(); - lastBlockquoteLevel++; - } - while (currentBlockquoteLevel < lastBlockquoteLevel) { - *lastBlockquote = enclosingNodeOfType(Position((*lastBlockquote)->parentNode(), 0), &isIndentBlockquote); - lastBlockquoteLevel--; - } - RefPtr<Node> placeholder = createBreakElement(document()); - appendNode(placeholder.get(), *lastBlockquote); - // Add another br before the placeholder if it collapsed. - VisiblePosition visiblePos(Position(placeholder.get(), 0)); - if (!isStartOfParagraph(visiblePos)) - insertNodeBefore(createBreakElement(document()).get(), placeholder.get()); - return placeholder.get(); -} - -void IndentOutdentCommand::indentRegion() -{ - Selection selection = selectionForParagraphIteration(endingSelection()); - VisiblePosition startOfSelection = selection.visibleStart(); - VisiblePosition endOfSelection = selection.visibleEnd(); - int startIndex = indexForVisiblePosition(startOfSelection); - int endIndex = indexForVisiblePosition(endOfSelection); - - ASSERT(!startOfSelection.isNull()); - ASSERT(!endOfSelection.isNull()); - - // Special case empty root editable elements because there's nothing to split - // and there's nothing to move. - Position start = startOfSelection.deepEquivalent().downstream(); - if (start.node() == editableRootForPosition(start)) { - RefPtr<Node> blockquote = createIndentBlockquoteElement(document()); - insertNodeAt(blockquote.get(), start); - RefPtr<Node> placeholder = createBreakElement(document()); - appendNode(placeholder.get(), blockquote.get()); - setEndingSelection(Selection(Position(placeholder.get(), 0), DOWNSTREAM)); - return; - } - - Node* previousListNode = 0; - Node* newListNode = 0; - Node* newBlockquote = 0; - VisiblePosition endOfCurrentParagraph = endOfParagraph(startOfSelection); - VisiblePosition endAfterSelection = endOfParagraph(endOfParagraph(endOfSelection).next()); - while (endOfCurrentParagraph != endAfterSelection) { - // Iterate across the selected paragraphs... - VisiblePosition endOfNextParagraph = endOfParagraph(endOfCurrentParagraph.next()); - Node* listNode = enclosingList(endOfCurrentParagraph.deepEquivalent().node()); - Node* insertionPoint; - if (listNode) { - RefPtr<Node> placeholder = createBreakElement(document()); - insertionPoint = placeholder.get(); - newBlockquote = 0; - RefPtr<Node> listItem = createListItemElement(document()); - if (listNode == previousListNode) { - // The previous paragraph was inside the same list, so add this list item to the list we already created - appendNode(listItem.get(), newListNode); - appendNode(placeholder.get(), listItem.get()); - } else { - // Clone the list element, insert it before the current paragraph, and move the paragraph into it. - RefPtr<Node> clonedList = static_cast<Element*>(listNode)->cloneNode(false); - insertNodeBefore(clonedList.get(), enclosingListChild(endOfCurrentParagraph.deepEquivalent().node())); - appendNode(listItem.get(), clonedList.get()); - appendNode(placeholder.get(), listItem.get()); - newListNode = clonedList.get(); - previousListNode = listNode; - } - } else if (newBlockquote) - // The previous paragraph was put into a new blockquote, so move this paragraph there as well - insertionPoint = prepareBlockquoteLevelForInsertion(endOfCurrentParagraph, &newBlockquote); - else { - // Create a new blockquote and insert it as a child of the root editable element. We accomplish - // this by splitting all parents of the current paragraph up to that point. - RefPtr<Node> blockquote = createIndentBlockquoteElement(document()); - Position start = startOfParagraph(endOfCurrentParagraph).deepEquivalent(); - - Node* enclosingCell = enclosingNodeOfType(start, &isTableCell); - Node* nodeToSplitTo = enclosingCell ? enclosingCell : editableRootForPosition(start); - RefPtr<Node> startOfNewBlock = splitTreeToNode(start.node(), nodeToSplitTo); - insertNodeBefore(blockquote.get(), startOfNewBlock.get()); - newBlockquote = blockquote.get(); - insertionPoint = prepareBlockquoteLevelForInsertion(endOfCurrentParagraph, &newBlockquote); - // Don't put the next paragraph in the blockquote we just created for this paragraph unless - // the next paragraph is in the same cell. - if (enclosingCell && enclosingCell != enclosingNodeOfType(endOfNextParagraph.deepEquivalent(), &isTableCell)) - newBlockquote = 0; - } - moveParagraph(startOfParagraph(endOfCurrentParagraph), endOfCurrentParagraph, VisiblePosition(Position(insertionPoint, 0)), true); - // moveParagraph should not destroy content that contains endOfNextParagraph, but if it does, return here - // to avoid a crash. - if (endOfNextParagraph.isNotNull() && !endOfNextParagraph.deepEquivalent().node()->inDocument()) { - ASSERT_NOT_REACHED(); - return; - } - endOfCurrentParagraph = endOfNextParagraph; - } - - RefPtr<Range> startRange = TextIterator::rangeFromLocationAndLength(document()->documentElement(), startIndex, 0, true); - RefPtr<Range> endRange = TextIterator::rangeFromLocationAndLength(document()->documentElement(), endIndex, 0, true); - if (startRange && endRange) - setEndingSelection(Selection(startRange->startPosition(), endRange->startPosition(), DOWNSTREAM)); -} - -void IndentOutdentCommand::outdentParagraph() -{ - VisiblePosition visibleStartOfParagraph = startOfParagraph(endingSelection().visibleStart()); - VisiblePosition visibleEndOfParagraph = endOfParagraph(visibleStartOfParagraph); - - Node* enclosingNode = enclosingNodeOfType(visibleStartOfParagraph.deepEquivalent(), &isListOrIndentBlockquote); - if (!enclosingNode) - return; - - // Use InsertListCommand to remove the selection from the list - if (enclosingNode->hasTagName(olTag)) { - applyCommandToComposite(InsertListCommand::create(document(), InsertListCommand::OrderedList, "")); - return; - } - if (enclosingNode->hasTagName(ulTag)) { - applyCommandToComposite(InsertListCommand::create(document(), InsertListCommand::UnorderedList, "")); - return; - } - - // The selection is inside a blockquote - VisiblePosition positionInEnclosingBlock = VisiblePosition(Position(enclosingNode, 0)); - VisiblePosition startOfEnclosingBlock = startOfBlock(positionInEnclosingBlock); - VisiblePosition endOfEnclosingBlock = endOfBlock(positionInEnclosingBlock); - if (visibleStartOfParagraph == startOfEnclosingBlock && - visibleEndOfParagraph == endOfEnclosingBlock) { - // The blockquote doesn't contain anything outside the paragraph, so it can be totally removed. - removeNodePreservingChildren(enclosingNode); - updateLayout(); - visibleStartOfParagraph = VisiblePosition(visibleStartOfParagraph.deepEquivalent()); - visibleEndOfParagraph = VisiblePosition(visibleEndOfParagraph.deepEquivalent()); - if (visibleStartOfParagraph.isNotNull() && !isStartOfParagraph(visibleStartOfParagraph)) - insertNodeAt(createBreakElement(document()).get(), visibleStartOfParagraph.deepEquivalent()); - if (visibleEndOfParagraph.isNotNull() && !isEndOfParagraph(visibleEndOfParagraph)) - insertNodeAt(createBreakElement(document()).get(), visibleEndOfParagraph.deepEquivalent()); - return; - } - Node* enclosingBlockFlow = enclosingBlockFlowElement(visibleStartOfParagraph); - RefPtr<Node> splitBlockquoteNode = enclosingNode; - if (enclosingBlockFlow != enclosingNode) - splitBlockquoteNode = splitTreeToNode(enclosingBlockFlowElement(visibleStartOfParagraph), enclosingNode, true); - RefPtr<Node> placeholder = createBreakElement(document()); - insertNodeBefore(placeholder.get(), splitBlockquoteNode.get()); - moveParagraph(startOfParagraph(visibleStartOfParagraph), endOfParagraph(visibleEndOfParagraph), VisiblePosition(Position(placeholder.get(), 0)), true); -} - -void IndentOutdentCommand::outdentRegion() -{ - VisiblePosition startOfSelection = endingSelection().visibleStart(); - VisiblePosition endOfSelection = endingSelection().visibleEnd(); - VisiblePosition endOfLastParagraph = endOfParagraph(endOfSelection); - - ASSERT(!startOfSelection.isNull()); - ASSERT(!endOfSelection.isNull()); - - if (endOfParagraph(startOfSelection) == endOfLastParagraph) { - outdentParagraph(); - return; - } - - Position originalSelectionEnd = endingSelection().end(); - setEndingSelection(endingSelection().visibleStart()); - outdentParagraph(); - Position originalSelectionStart = endingSelection().start(); - VisiblePosition endOfCurrentParagraph = endOfParagraph(endOfParagraph(endingSelection().visibleStart()).next(true)); - VisiblePosition endAfterSelection = endOfParagraph(endOfParagraph(endOfSelection).next()); - while (endOfCurrentParagraph != endAfterSelection) { - VisiblePosition endOfNextParagraph = endOfParagraph(endOfCurrentParagraph.next()); - if (endOfCurrentParagraph == endOfLastParagraph) - setEndingSelection(Selection(originalSelectionEnd, DOWNSTREAM)); - else - setEndingSelection(endOfCurrentParagraph); - outdentParagraph(); - endOfCurrentParagraph = endOfNextParagraph; - } - setEndingSelection(Selection(originalSelectionStart, endingSelection().end(), DOWNSTREAM)); -} - -void IndentOutdentCommand::doApply() -{ - if (endingSelection().isNone()) - return; - - if (!endingSelection().rootEditableElement()) - return; - - VisiblePosition visibleEnd = endingSelection().visibleEnd(); - VisiblePosition visibleStart = endingSelection().visibleStart(); - // When a selection ends at the start of a paragraph, we rarely paint - // the selection gap before that paragraph, because there often is no gap. - // In a case like this, it's not obvious to the user that the selection - // ends "inside" that paragraph, so it would be confusing if Indent/Outdent - // operated on that paragraph. - // FIXME: We paint the gap before some paragraphs that are indented with left - // margin/padding, but not others. We should make the gap painting more consistent and - // then use a left margin/padding rule here. - if (visibleEnd != visibleStart && isStartOfParagraph(visibleEnd)) - setEndingSelection(Selection(visibleStart, visibleEnd.previous(true))); - - if (m_typeOfAction == Indent) - indentRegion(); - else - outdentRegion(); -} - -} diff --git a/WebCore/editing/IndentOutdentCommand.h b/WebCore/editing/IndentOutdentCommand.h deleted file mode 100644 index c2ba1ea..0000000 --- a/WebCore/editing/IndentOutdentCommand.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (C) 2006, 2008 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 IndentOutdentCommand_h -#define IndentOutdentCommand_h - -#include "CompositeEditCommand.h" - -namespace WebCore { - -class IndentOutdentCommand : public CompositeEditCommand { -public: - enum EIndentType { Indent, Outdent }; - static PassRefPtr<IndentOutdentCommand> create(Document* document, EIndentType type, int marginInPixels = 0) - { - return adoptRef(new IndentOutdentCommand(document, type, marginInPixels)); - } - - virtual bool preservesTypingStyle() const { return true; } - -private: - IndentOutdentCommand(Document*, EIndentType, int marginInPixels); - - virtual void doApply(); - virtual EditAction editingAction() const { return m_typeOfAction == Indent ? EditActionIndent : EditActionOutdent; } - - void indentRegion(); - void outdentRegion(); - void outdentParagraph(); - Node* prepareBlockquoteLevelForInsertion(VisiblePosition&, Node**); - - EIndentType m_typeOfAction; - int m_marginInPixels; -}; - -} // namespace WebCore - -#endif // IndentOutdentCommand_h diff --git a/WebCore/editing/InsertIntoTextNodeCommand.cpp b/WebCore/editing/InsertIntoTextNodeCommand.cpp deleted file mode 100644 index 9d5ea6e..0000000 --- a/WebCore/editing/InsertIntoTextNodeCommand.cpp +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (C) 2005, 2008 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 "InsertIntoTextNodeCommand.h" - -#include "Text.h" - -namespace WebCore { - -InsertIntoTextNodeCommand::InsertIntoTextNodeCommand(PassRefPtr<Text> node, int offset, const String& text) - : SimpleEditCommand(node->document()) - , m_node(node) - , m_offset(offset) - , m_text(text) -{ - ASSERT(m_node); - ASSERT(offset >= 0); - ASSERT(!text.isEmpty()); -} - -void InsertIntoTextNodeCommand::doApply() -{ - ExceptionCode ec = 0; - m_node->insertData(m_offset, m_text, ec); - ASSERT(ec == 0); -} - -void InsertIntoTextNodeCommand::doUnapply() -{ - ExceptionCode ec = 0; - m_node->deleteData(m_offset, m_text.length(), ec); - ASSERT(ec == 0); -} - -} // namespace WebCore diff --git a/WebCore/editing/InsertIntoTextNodeCommand.h b/WebCore/editing/InsertIntoTextNodeCommand.h deleted file mode 100644 index aed5da8..0000000 --- a/WebCore/editing/InsertIntoTextNodeCommand.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (C) 2005, 2006, 2008 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 InsertIntoTextNodeCommand_h -#define InsertIntoTextNodeCommand_h - -#include "EditCommand.h" - -namespace WebCore { - -class Text; - -class InsertIntoTextNodeCommand : public SimpleEditCommand { -public: - static PassRefPtr<InsertIntoTextNodeCommand> create(PassRefPtr<Text> node, int offset, const String& text) - { - return adoptRef(new InsertIntoTextNodeCommand(node, offset, text)); - } - -private: - InsertIntoTextNodeCommand(PassRefPtr<Text> node, int offset, const String& text); - - virtual void doApply(); - virtual void doUnapply(); - - RefPtr<Text> m_node; - int m_offset; - String m_text; -}; - -} // namespace WebCore - -#endif // InsertIntoTextNodeCommand_h diff --git a/WebCore/editing/InsertLineBreakCommand.cpp b/WebCore/editing/InsertLineBreakCommand.cpp deleted file mode 100644 index d6b4f56..0000000 --- a/WebCore/editing/InsertLineBreakCommand.cpp +++ /dev/null @@ -1,187 +0,0 @@ -/* - * Copyright (C) 2005, 2006 Apple Computer, 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 "InsertLineBreakCommand.h" - -#include "CSSMutableStyleDeclaration.h" -#include "Document.h" -#include "Element.h" -#include "Frame.h" -#include "Text.h" -#include "VisiblePosition.h" -#include "Range.h" -#include "htmlediting.h" -#include "HTMLNames.h" -#include "visible_units.h" - -namespace WebCore { - -using namespace HTMLNames; - -InsertLineBreakCommand::InsertLineBreakCommand(Document* document) - : CompositeEditCommand(document) -{ -} - -bool InsertLineBreakCommand::preservesTypingStyle() const -{ - return true; -} - -void InsertLineBreakCommand::insertNodeAfterPosition(Node *node, const Position &pos) -{ - // 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. - Node *cb = pos.node()->enclosingBlockFlowElement(); - if (cb == pos.node()) - appendNode(node, cb); - else - insertNodeAfter(node, pos.node()); -} - -void InsertLineBreakCommand::insertNodeBeforePosition(Node *node, const Position &pos) -{ - // 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. - Node *cb = pos.node()->enclosingBlockFlowElement(); - if (cb == pos.node()) - appendNode(node, cb); - else - insertNodeBefore(node, pos.node()); -} - -// Whether we should insert a break element or a '\n'. -bool InsertLineBreakCommand::shouldUseBreakElement(const Position& insertionPos) -{ - // An editing position like [input, 0] actually refers to the position before - // the input element, and in that case we need to check the input element's - // parent's renderer. - Position p(rangeCompliantEquivalent(insertionPos)); - return p.node()->renderer() && !p.node()->renderer()->style()->preserveNewline(); -} - -void InsertLineBreakCommand::doApply() -{ - deleteSelection(); - Selection selection = endingSelection(); - if (selection.isNone()) - return; - - VisiblePosition caret(selection.visibleStart()); - Position pos(caret.deepEquivalent()); - - pos = positionAvoidingSpecialElementBoundary(pos); - - pos = positionOutsideTabSpan(pos); - - RefPtr<Node> nodeToInsert; - if (shouldUseBreakElement(pos)) - nodeToInsert = createBreakElement(document()); - else - nodeToInsert = document()->createTextNode("\n"); - - // FIXME: Need to merge text nodes when inserting just after or before text. - - if (isEndOfParagraph(caret) && !lineBreakExistsAtPosition(caret)) { - bool needExtraLineBreak = !pos.node()->hasTagName(hrTag) && !pos.node()->hasTagName(tableTag); - - insertNodeAt(nodeToInsert.get(), pos); - - if (needExtraLineBreak) - insertNodeBefore(nodeToInsert->cloneNode(false).get(), nodeToInsert.get()); - - VisiblePosition endingPosition(Position(nodeToInsert.get(), 0)); - setEndingSelection(Selection(endingPosition)); - } else if (pos.offset() <= caretMinOffset(pos.node())) { - insertNodeAt(nodeToInsert.get(), pos); - - // Insert an extra br or '\n' if the just inserted one collapsed. - if (!isStartOfParagraph(VisiblePosition(Position(nodeToInsert.get(), 0)))) - insertNodeBefore(nodeToInsert->cloneNode(false).get(), nodeToInsert.get()); - - setEndingSelection(Selection(positionAfterNode(nodeToInsert.get()), DOWNSTREAM)); - } else if (pos.offset() >= caretMaxOffset(pos.node())) { - insertNodeAt(nodeToInsert.get(), pos); - setEndingSelection(Selection(positionAfterNode(nodeToInsert.get()), DOWNSTREAM)); - } else { - // Split a text node - ASSERT(pos.node()->isTextNode()); - - // Do the split - ExceptionCode ec = 0; - Text *textNode = static_cast<Text *>(pos.node()); - RefPtr<Text> textBeforeNode = document()->createTextNode(textNode->substringData(0, selection.start().offset(), ec)); - deleteTextFromNode(textNode, 0, pos.offset()); - insertNodeBefore(textBeforeNode.get(), textNode); - insertNodeBefore(nodeToInsert.get(), textNode); - Position endingPosition = Position(textNode, 0); - - // Handle whitespace that occurs after the split - updateLayout(); - if (!endingPosition.isRenderedCharacter()) { - Position positionBeforeTextNode(positionBeforeNode(textNode)); - // Clear out all whitespace and insert one non-breaking space - deleteInsignificantTextDownstream(endingPosition); - ASSERT(!textNode->renderer() || textNode->renderer()->style()->collapseWhiteSpace()); - // Deleting insignificant whitespace will remove textNode if it contains nothing but insignificant whitespace. - if (textNode->inDocument()) - insertTextIntoNode(textNode, 0, nonBreakingSpaceString()); - else { - RefPtr<Text> nbspNode = document()->createTextNode(nonBreakingSpaceString()); - insertNodeAt(nbspNode.get(), positionBeforeTextNode); - endingPosition = Position(nbspNode.get(), 0); - } - } - - setEndingSelection(Selection(endingPosition, DOWNSTREAM)); - } - - // Handle the case where there is a typing style. - - CSSMutableStyleDeclaration* typingStyle = document()->frame()->typingStyle(); - - if (typingStyle && typingStyle->length() > 0) { - // Apply the typing style to the inserted line break, so that if the selection - // leaves and then comes back, new input will have the right style. - // FIXME: We shouldn't always apply the typing style to the line break here, - // see <rdar://problem/5794462>. - applyStyle(typingStyle, Position(nodeToInsert.get(), 0), - Position(nodeToInsert.get(), maxDeepOffset(nodeToInsert.get()))); - // Even though this applyStyle operates on a Range, it still sets an endingSelection(). - // It tries to set a Selection around the content it operated on. So, that Selection - // will either (a) select the line break we inserted, or it will (b) be a caret just - // before the line break (if the line break is at the end of a block it isn't selectable). - // So, this next call sets the endingSelection() to a caret just after the line break - // that we inserted, or just before it if it's at the end of a block. - setEndingSelection(endingSelection().visibleEnd()); - } - - rebalanceWhitespace(); -} - -} diff --git a/WebCore/editing/InsertLineBreakCommand.h b/WebCore/editing/InsertLineBreakCommand.h deleted file mode 100644 index 9e73add..0000000 --- a/WebCore/editing/InsertLineBreakCommand.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (C) 2005, 2006, 2008 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 InsertLineBreakCommand_h -#define InsertLineBreakCommand_h - -#include "CompositeEditCommand.h" - -namespace WebCore { - -class InsertLineBreakCommand : public CompositeEditCommand { -public: - static PassRefPtr<InsertLineBreakCommand> create(Document* document) - { - return adoptRef(new InsertLineBreakCommand(document)); - } - -private: - InsertLineBreakCommand(Document*); - - virtual void doApply(); - - virtual bool preservesTypingStyle() const; - - void insertNodeAfterPosition(Node*, const Position&); - void insertNodeBeforePosition(Node*, const Position&); - bool shouldUseBreakElement(const Position&); -}; - -} // namespace WebCore - -#endif // InsertLineBreakCommand_h diff --git a/WebCore/editing/InsertListCommand.cpp b/WebCore/editing/InsertListCommand.cpp deleted file mode 100644 index 7433516..0000000 --- a/WebCore/editing/InsertListCommand.cpp +++ /dev/null @@ -1,265 +0,0 @@ -/* - * Copyright (C) 2006 Apple Computer, 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 "Element.h" -#include "InsertListCommand.h" -#include "DocumentFragment.h" -#include "htmlediting.h" -#include "HTMLElement.h" -#include "HTMLNames.h" -#include "TextIterator.h" -#include "visible_units.h" - -namespace WebCore { - -using namespace HTMLNames; - -PassRefPtr<Node> InsertListCommand::insertList(Document* document, Type type) -{ - RefPtr<InsertListCommand> insertCommand = new InsertListCommand(document, type, ""); - insertCommand->apply(); - return insertCommand->m_listElement; -} - -Node* InsertListCommand::fixOrphanedListChild(Node* node) -{ - RefPtr<Element> listElement = createUnorderedListElement(document()); - insertNodeBefore(listElement.get(), node); - removeNode(node); - appendNode(node, listElement.get()); - m_listElement = listElement; - return listElement.get(); -} - -InsertListCommand::InsertListCommand(Document* document, Type type, const String& id) - : CompositeEditCommand(document), m_type(type), m_id(id), m_forceCreateList(false) -{ -} - -bool InsertListCommand::modifyRange() -{ - Selection selection = selectionForParagraphIteration(endingSelection()); - ASSERT(selection.isRange()); - VisiblePosition startOfSelection = selection.visibleStart(); - VisiblePosition endOfSelection = selection.visibleEnd(); - VisiblePosition startOfLastParagraph = startOfParagraph(endOfSelection); - - if (startOfParagraph(startOfSelection) == startOfLastParagraph) - return false; - - Node* startList = enclosingList(startOfSelection.deepEquivalent().node()); - Node* endList = enclosingList(endOfSelection.deepEquivalent().node()); - if (!startList || startList != endList) - m_forceCreateList = true; - - setEndingSelection(startOfSelection); - doApply(); - // Fetch the start of the selection after moving the first paragraph, - // because moving the paragraph will invalidate the original start. - // We'll use the new start to restore the original selection after - // we modified all selected paragraphs. - startOfSelection = endingSelection().visibleStart(); - VisiblePosition startOfCurrentParagraph = startOfNextParagraph(startOfSelection); - while (startOfCurrentParagraph != startOfLastParagraph) { - // doApply() may operate on and remove the last paragraph of the selection from the document - // if it's in the same list item as startOfCurrentParagraph. Return early to avoid an - // 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()) - return true; - setEndingSelection(startOfCurrentParagraph); - doApply(); - startOfCurrentParagraph = startOfNextParagraph(endingSelection().visibleStart()); - } - setEndingSelection(endOfSelection); - doApply(); - // Fetch the end of the selection, for the reason mentioned above. - endOfSelection = endingSelection().visibleEnd(); - setEndingSelection(Selection(startOfSelection, endOfSelection)); - m_forceCreateList = false; - return true; -} - -void InsertListCommand::doApply() -{ - if (endingSelection().isNone()) - return; - - if (!endingSelection().rootEditableElement()) - return; - - VisiblePosition visibleEnd = endingSelection().visibleEnd(); - VisiblePosition visibleStart = endingSelection().visibleStart(); - // When a selection ends at the start of a paragraph, we rarely paint - // the selection gap before that paragraph, because there often is no gap. - // In a case like this, it's not obvious to the user that the selection - // ends "inside" that paragraph, so it would be confusing if InsertUn{Ordered}List - // operated on that paragraph. - // FIXME: We paint the gap before some paragraphs that are indented with left - // margin/padding, but not others. We should make the gap painting more consistent and - // then use a left margin/padding rule here. - if (visibleEnd != visibleStart && isStartOfParagraph(visibleEnd)) - setEndingSelection(Selection(visibleStart, visibleEnd.previous(true))); - - if (endingSelection().isRange() && modifyRange()) - return; - - // 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(); - const QualifiedName listTag = (m_type == OrderedList) ? olTag : ulTag; - Node* listChildNode = enclosingListChild(selectionNode); - bool switchListType = false; - if (listChildNode) { - // Remove the list chlild. - Node* listNode = enclosingList(listChildNode); - if (!listNode) - listNode = fixOrphanedListChild(listChildNode); - if (!listNode->hasTagName(listTag)) - // listChildNode will be removed from the list and a list of type m_type will be created. - switchListType = true; - Node* nextListChild; - Node* previousListChild; - VisiblePosition start; - VisiblePosition end; - if (listChildNode->hasTagName(liTag)) { - start = VisiblePosition(Position(listChildNode, 0)); - end = VisiblePosition(Position(listChildNode, maxDeepOffset(listChildNode))); - nextListChild = listChildNode->nextSibling(); - previousListChild = listChildNode->previousSibling(); - } else { - // A paragraph is visually a list item minus a list marker. The paragraph will be moved. - start = startOfParagraph(endingSelection().visibleStart()); - end = endOfParagraph(endingSelection().visibleEnd()); - nextListChild = enclosingListChild(end.next().deepEquivalent().node()); - ASSERT(nextListChild != listChildNode); - if (enclosingList(nextListChild) != listNode) - nextListChild = 0; - previousListChild = enclosingListChild(start.previous().deepEquivalent().node()); - ASSERT(previousListChild != listChildNode); - if (enclosingList(previousListChild) != listNode) - previousListChild = 0; - } - // When removing a list, we must always create a placeholder to act as a point of insertion - // for the list content being removed. - RefPtr<Element> placeholder = createBreakElement(document()); - RefPtr<Node> nodeToInsert = placeholder; - // If the content of the list item will be moved into another list, put it in a list item - // so that we don't create an orphaned list child. - if (enclosingList(listNode)) { - nodeToInsert = createListItemElement(document()); - appendNode(placeholder.get(), nodeToInsert.get()); - } - - if (nextListChild && previousListChild) { - // We want to pull listChildNode out of listNode, and place it before nextListChild - // and after previousListChild, so we split listNode and insert it between the two lists. - // But to split listNode, we must first split ancestors of listChildNode between it and listNode, - // if any exist. - // FIXME: We appear to split at nextListChild as opposed to listChildNode so that when we remove - // listChildNode below in moveParagraphs, previousListChild will be removed along with it if it is - // unrendered. But we ought to remove nextListChild too, if it is unrendered. - splitElement(static_cast<Element*>(listNode), splitTreeToNode(nextListChild, listNode).get()); - insertNodeBefore(nodeToInsert.get(), listNode); - } else if (nextListChild || listChildNode->parentNode() != listNode) { - // Just because listChildNode has no previousListChild doesn't mean there isn't any content - // in listNode that comes before listChildNode, as listChildNode could have ancestors - // between it and listNode. So, we split up to listNode before inserting the placeholder - // where we're about to move listChildNode to. - if (listChildNode->parentNode() != listNode) - splitElement(static_cast<Element *>(listNode), splitTreeToNode(listChildNode, listNode).get()); - insertNodeBefore(nodeToInsert.get(), listNode); - } else - insertNodeAfter(nodeToInsert.get(), listNode); - - VisiblePosition insertionPoint = VisiblePosition(Position(placeholder.get(), 0)); - moveParagraphs(start, end, insertionPoint, true); - } - if (!listChildNode || switchListType || m_forceCreateList) { - // Create list. - VisiblePosition start = startOfParagraph(endingSelection().visibleStart()); - VisiblePosition end = endOfParagraph(endingSelection().visibleEnd()); - - // Check for adjoining lists. - VisiblePosition previousPosition = start.previous(true); - VisiblePosition nextPosition = end.next(true); - RefPtr<Element> listItemElement = createListItemElement(document()); - RefPtr<Element> placeholder = createBreakElement(document()); - appendNode(placeholder.get(), listItemElement.get()); - Node* previousList = outermostEnclosingList(previousPosition.deepEquivalent().node()); - Node* nextList = outermostEnclosingList(nextPosition.deepEquivalent().node()); - Node* startNode = start.deepEquivalent().node(); - Node* previousCell = enclosingTableCell(previousPosition.deepEquivalent()); - Node* nextCell = enclosingTableCell(nextPosition.deepEquivalent()); - Node* currentCell = enclosingTableCell(start.deepEquivalent()); - if (previousList && (!previousList->hasTagName(listTag) || startNode->isDescendantOf(previousList) || previousCell != currentCell)) - previousList = 0; - if (nextList && (!nextList->hasTagName(listTag) || startNode->isDescendantOf(nextList) || nextCell != currentCell)) - nextList = 0; - // Place list item into adjoining lists. - if (previousList) - appendNode(listItemElement.get(), previousList); - else if (nextList) - insertNodeAt(listItemElement.get(), Position(nextList, 0)); - else { - // Create the list. - RefPtr<Element> listElement = m_type == OrderedList ? createOrderedListElement(document()) : createUnorderedListElement(document()); - m_listElement = listElement; - if (!m_id.isEmpty()) - static_cast<HTMLElement*>(listElement.get())->setId(m_id); - appendNode(listItemElement.get(), listElement.get()); - - if (start == end && isBlock(start.deepEquivalent().node())) { - // 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. - RefPtr<Node> placeholder = insertBlockPlaceholder(start.deepEquivalent()); - start = VisiblePosition(Position(placeholder.get(), 0)); - end = start; - } - - // Insert the list at a position visually equivalent to start of the - // paragraph that is being moved into the list. - // Try to avoid inserting it somewhere where it will be surrounded by - // inline ancestors of start, since it is easier for editing to produce - // 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()); - if (listChild && listChild->hasTagName(liTag)) - insertionPos = positionBeforeNode(listChild); - - insertNodeAt(listElement.get(), insertionPos); - } - moveParagraph(start, end, VisiblePosition(Position(placeholder.get(), 0)), true); - if (nextList && previousList) - mergeIdenticalElements(static_cast<Element*>(previousList), static_cast<Element*>(nextList)); - } -} - -} diff --git a/WebCore/editing/InsertListCommand.h b/WebCore/editing/InsertListCommand.h deleted file mode 100644 index e9a8da9..0000000 --- a/WebCore/editing/InsertListCommand.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (C) 2006, 2008 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 InsertListCommand_h -#define InsertListCommand_h - -#include "CompositeEditCommand.h" - -namespace WebCore { - -class InsertListCommand : public CompositeEditCommand { -public: - enum Type { OrderedList, UnorderedList }; - - static PassRefPtr<InsertListCommand> create(Document* document, Type listType, const String& listID) - { - return adoptRef(new InsertListCommand(document, listType, listID)); - } - - static PassRefPtr<Node> insertList(Document*, Type); - - virtual bool preservesTypingStyle() const { return true; } - -private: - InsertListCommand(Document*, Type, const String&); - - virtual void doApply(); - virtual EditAction editingAction() const { return EditActionInsertList; } - - Node* fixOrphanedListChild(Node*); - bool modifyRange(); - RefPtr<Node> m_listElement; - Type m_type; - String m_id; - bool m_forceCreateList; -}; - -} // namespace WebCore - -#endif // InsertListCommand_h diff --git a/WebCore/editing/InsertNodeBeforeCommand.cpp b/WebCore/editing/InsertNodeBeforeCommand.cpp deleted file mode 100644 index 2315597..0000000 --- a/WebCore/editing/InsertNodeBeforeCommand.cpp +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (C) 2005, 2008 Apple Computer, 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 "InsertNodeBeforeCommand.h" - -#include "htmlediting.h" - -namespace WebCore { - -InsertNodeBeforeCommand::InsertNodeBeforeCommand(PassRefPtr<Node> insertChild, PassRefPtr<Node> refChild) - : SimpleEditCommand(refChild->document()), m_insertChild(insertChild), m_refChild(refChild) -{ - ASSERT(m_insertChild); - ASSERT(m_refChild); -} - -void InsertNodeBeforeCommand::doApply() -{ - ASSERT(m_insertChild); - ASSERT(m_refChild); - ASSERT(m_refChild->parentNode()); - // If the child to insert is already in a tree, inserting it will remove it from it's old location - // in an non-undoable way. We might eventually find it useful to do an undoable remove in this case. - ASSERT(!m_insertChild->parent()); - ASSERT(enclosingNodeOfType(Position(m_refChild->parentNode(), 0), &isContentEditable) || !m_refChild->parentNode()->attached()); - - ExceptionCode ec = 0; - m_refChild->parentNode()->insertBefore(m_insertChild.get(), m_refChild.get(), ec); - ASSERT(ec == 0); -} - -void InsertNodeBeforeCommand::doUnapply() -{ - ASSERT(m_insertChild); - ASSERT(m_refChild); - ASSERT(m_refChild->parentNode()); - - ExceptionCode ec = 0; - m_refChild->parentNode()->removeChild(m_insertChild.get(), ec); - ASSERT(ec == 0); -} - -} diff --git a/WebCore/editing/InsertNodeBeforeCommand.h b/WebCore/editing/InsertNodeBeforeCommand.h deleted file mode 100644 index 0904502..0000000 --- a/WebCore/editing/InsertNodeBeforeCommand.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (C) 2005, 2006, 2008 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 InsertNodeBeforeCommand_h -#define InsertNodeBeforeCommand_h - -#include "EditCommand.h" - -namespace WebCore { - -class InsertNodeBeforeCommand : public SimpleEditCommand { -public: - static PassRefPtr<InsertNodeBeforeCommand> create(PassRefPtr<Node> childToInsert, PassRefPtr<Node> childToInsertBefore) - { - return adoptRef(new InsertNodeBeforeCommand(childToInsert, childToInsertBefore)); - } - -private: - InsertNodeBeforeCommand(PassRefPtr<Node> childToInsert, PassRefPtr<Node> childToInsertBefore); - - virtual void doApply(); - virtual void doUnapply(); - - RefPtr<Node> m_insertChild; - RefPtr<Node> m_refChild; -}; - -} // namespace WebCore - -#endif // InsertNodeBeforeCommand_h diff --git a/WebCore/editing/InsertParagraphSeparatorCommand.cpp b/WebCore/editing/InsertParagraphSeparatorCommand.cpp deleted file mode 100644 index bcd2bfa..0000000 --- a/WebCore/editing/InsertParagraphSeparatorCommand.cpp +++ /dev/null @@ -1,332 +0,0 @@ -/* - * Copyright (C) 2005, 2006 Apple Computer, 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 "InsertParagraphSeparatorCommand.h" - -#include "Document.h" -#include "Logging.h" -#include "CSSComputedStyleDeclaration.h" -#include "CSSPropertyNames.h" -#include "Text.h" -#include "htmlediting.h" -#include "HTMLElement.h" -#include "HTMLNames.h" -#include "InsertLineBreakCommand.h" -#include "RenderObject.h" -#include "visible_units.h" - -namespace WebCore { - -using namespace HTMLNames; - -InsertParagraphSeparatorCommand::InsertParagraphSeparatorCommand(Document *document, bool mustUseDefaultParagraphElement) - : CompositeEditCommand(document) - , m_mustUseDefaultParagraphElement(mustUseDefaultParagraphElement) -{ -} - -bool InsertParagraphSeparatorCommand::preservesTypingStyle() const -{ - return true; -} - -void InsertParagraphSeparatorCommand::calculateStyleBeforeInsertion(const Position &pos) -{ - // It is only important to set a style to apply later if we're at the boundaries of - // a paragraph. Otherwise, content that is moved as part of the work of the command - // will lend their styles to the new paragraph without any extra work needed. - VisiblePosition visiblePos(pos, VP_DEFAULT_AFFINITY); - if (!isStartOfParagraph(visiblePos) && !isEndOfParagraph(visiblePos)) - return; - - m_style = styleAtPosition(pos); -} - -void InsertParagraphSeparatorCommand::applyStyleAfterInsertion(Node* originalEnclosingBlock) -{ - // Not only do we break out of header tags, but we also do not preserve the typing style, - // in order to match other browsers. - if (originalEnclosingBlock->hasTagName(h1Tag) || - originalEnclosingBlock->hasTagName(h2Tag) || - originalEnclosingBlock->hasTagName(h3Tag) || - originalEnclosingBlock->hasTagName(h4Tag) || - originalEnclosingBlock->hasTagName(h5Tag)) - return; - - if (!m_style) - return; - - computedStyle(endingSelection().start().node())->diff(m_style.get()); - if (m_style->length() > 0) - applyStyle(m_style.get()); -} - -bool InsertParagraphSeparatorCommand::shouldUseDefaultParagraphElement(Node* enclosingBlock) const -{ - if (m_mustUseDefaultParagraphElement) - return true; - - // Assumes that if there was a range selection, it was already deleted. - if (!isEndOfBlock(endingSelection().visibleStart())) - return false; - - return enclosingBlock->hasTagName(h1Tag) || - enclosingBlock->hasTagName(h2Tag) || - enclosingBlock->hasTagName(h3Tag) || - enclosingBlock->hasTagName(h4Tag) || - enclosingBlock->hasTagName(h5Tag); -} - -void InsertParagraphSeparatorCommand::doApply() -{ - bool splitText = false; - if (endingSelection().isNone()) - return; - - Position pos = endingSelection().start(); - - EAffinity affinity = endingSelection().affinity(); - - // Delete the current selection. - if (endingSelection().isRange()) { - calculateStyleBeforeInsertion(pos); - deleteSelection(false, true); - pos = endingSelection().start(); - affinity = endingSelection().affinity(); - } - - // FIXME: The rangeCompliantEquivalent conversion needs to be moved into enclosingBlock. - Node* startBlock = enclosingBlock(rangeCompliantEquivalent(pos).node()); - Position canonicalPos = VisiblePosition(pos).deepEquivalent(); - if (!startBlock || !startBlock->parentNode() || - isTableCell(startBlock) || - startBlock->hasTagName(formTag) || - canonicalPos.node()->renderer() && canonicalPos.node()->renderer()->isTable() || - canonicalPos.node()->hasTagName(hrTag)) { - applyCommandToComposite(InsertLineBreakCommand::create(document())); - return; - } - - // Use the leftmost candidate. - pos = pos.upstream(); - if (!pos.isCandidate()) - pos = pos.downstream(); - - // Adjust the insertion position after the delete - pos = positionAvoidingSpecialElementBoundary(pos); - VisiblePosition visiblePos(pos, affinity); - calculateStyleBeforeInsertion(pos); - - //--------------------------------------------------------------------- - // Handle special case of typing return on an empty list item - if (breakOutOfEmptyListItem()) - return; - - //--------------------------------------------------------------------- - // Prepare for more general cases. - // FIXME: We shouldn't peel off the node here because then we lose track of - // the fact that it's the node that belongs to an editing position and - // not a rangeCompliantEquivalent. - Node *startNode = pos.node(); - - bool isFirstInBlock = isStartOfBlock(visiblePos); - bool isLastInBlock = isEndOfBlock(visiblePos); - bool nestNewBlock = false; - - // Create block to be inserted. - RefPtr<Node> blockToInsert; - if (startBlock == startBlock->rootEditableElement()) { - blockToInsert = static_pointer_cast<Node>(createDefaultParagraphElement(document())); - nestNewBlock = true; - } else if (shouldUseDefaultParagraphElement(startBlock)) - blockToInsert = static_pointer_cast<Node>(createDefaultParagraphElement(document())); - else - blockToInsert = startBlock->cloneNode(false); - - //--------------------------------------------------------------------- - // Handle case when position is in the last visible position in its block, - // including when the block is empty. - if (isLastInBlock) { - if (nestNewBlock) { - if (isFirstInBlock && !lineBreakExistsAtPosition(visiblePos)) { - // The block is empty. Create an empty block to - // represent the paragraph that we're leaving. - RefPtr<Node> extraBlock = createDefaultParagraphElement(document()); - appendNode(extraBlock.get(), startBlock); - appendBlockPlaceholder(extraBlock.get()); - } - appendNode(blockToInsert.get(), startBlock); - } else - insertNodeAfter(blockToInsert.get(), startBlock); - - appendBlockPlaceholder(blockToInsert.get()); - setEndingSelection(Selection(Position(blockToInsert.get(), 0), DOWNSTREAM)); - applyStyleAfterInsertion(startBlock); - return; - } - - //--------------------------------------------------------------------- - // Handle case when position is in the first visible position in its block, and - // similar case where previous position is in another, presumeably nested, block. - if (isFirstInBlock || !inSameBlock(visiblePos, visiblePos.previous())) { - Node *refNode; - if (isFirstInBlock && !nestNewBlock) - refNode = startBlock; - else if (pos.node() == startBlock && nestNewBlock) { - refNode = startBlock->childNode(pos.offset()); - ASSERT(refNode); // must be true or we'd be in the end of block case - } else - refNode = pos.node(); - - // find ending selection position easily before inserting the paragraph - pos = pos.downstream(); - - insertNodeBefore(blockToInsert.get(), refNode); - appendBlockPlaceholder(blockToInsert.get()); - setEndingSelection(Selection(Position(blockToInsert.get(), 0), DOWNSTREAM)); - applyStyleAfterInsertion(startBlock); - setEndingSelection(Selection(pos, DOWNSTREAM)); - return; - } - - //--------------------------------------------------------------------- - // Handle the (more complicated) general case, - - // All of the content in the current block after visiblePos is - // about to be wrapped in a new paragraph element. Add a br before - // it if visiblePos is at the start of a paragraph so that the - // content will move down a line. - if (isStartOfParagraph(visiblePos)) { - RefPtr<Element> br = createBreakElement(document()); - insertNodeAt(br.get(), pos); - pos = positionAfterNode(br.get()); - } - - // Move downstream. Typing style code will take care of carrying along the - // style of the upstream position. - pos = pos.downstream(); - startNode = pos.node(); - - // Build up list of ancestors in between the start node and the start block. - Vector<Node*> ancestors; - if (startNode != startBlock) - for (Node* n = startNode->parentNode(); n && n != startBlock; n = n->parentNode()) - ancestors.append(n); - - // 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 = pos.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()) { - Text* textNode = static_cast<Text*>(leadingWhitespace.node()); - ASSERT(!textNode->renderer() || textNode->renderer()->style()->collapseWhiteSpace()); - replaceTextInNode(textNode, leadingWhitespace.offset(), 1, nonBreakingSpaceString()); - } - - // Split at pos if in the middle of a text node. - if (startNode->isTextNode()) { - Text *textNode = static_cast<Text *>(startNode); - bool atEnd = (unsigned)pos.offset() >= textNode->length(); - if (pos.offset() > 0 && !atEnd) { - splitTextNode(textNode, pos.offset()); - pos = Position(startNode, 0); - visiblePos = VisiblePosition(pos); - splitText = true; - } - } - - // Put the added block in the tree. - if (nestNewBlock) - appendNode(blockToInsert.get(), startBlock); - else - insertNodeAfter(blockToInsert.get(), startBlock); - - updateLayout(); - - // Make clones of ancestors in between the start node and the start block. - RefPtr<Node> parent = blockToInsert; - for (size_t i = ancestors.size(); i != 0; --i) { - RefPtr<Node> child = ancestors[i - 1]->cloneNode(false); // shallow clone - appendNode(child.get(), parent.get()); - parent = child.release(); - } - - // If the paragraph separator was inserted at the end of a paragraph, an empty line must be - // created. All of the nodes, starting at visiblePos, are about to be added to the new paragraph - // element. If the first node to be inserted won't be one that will hold an empty line open, add a br. - if (isEndOfParagraph(visiblePos) && !lineBreakExistsAtPosition(visiblePos)) - appendNode(createBreakElement(document()).get(), blockToInsert.get()); - - // Move the start node and the siblings of the start node. - if (startNode != startBlock) { - Node *n = startNode; - if (pos.offset() >= caretMaxOffset(startNode)) - n = startNode->nextSibling(); - - while (n && n != blockToInsert) { - Node *next = n->nextSibling(); - removeNode(n); - appendNode(n, parent.get()); - n = next; - } - } - - // Move everything after the start node. - if (!ancestors.isEmpty()) { - Node* leftParent = ancestors.first(); - while (leftParent && leftParent != startBlock) { - parent = parent->parentNode(); - Node* n = leftParent->nextSibling(); - while (n && n != blockToInsert) { - Node* next = n->nextSibling(); - removeNode(n); - appendNode(n, parent.get()); - n = next; - } - leftParent = leftParent->parentNode(); - } - } - - // Handle whitespace that occurs after the split - if (splitText) { - updateLayout(); - pos = Position(startNode, 0); - if (!pos.isRenderedCharacter()) { - // Clear out all whitespace and insert one non-breaking space - ASSERT(startNode); - ASSERT(startNode->isTextNode()); - ASSERT(!startNode->renderer() || startNode->renderer()->style()->collapseWhiteSpace()); - deleteInsignificantTextDownstream(pos); - insertTextIntoNode(static_cast<Text*>(startNode), 0, nonBreakingSpaceString()); - } - } - - setEndingSelection(Selection(Position(blockToInsert.get(), 0), DOWNSTREAM)); - applyStyleAfterInsertion(startBlock); -} - -} // namespace WebCore diff --git a/WebCore/editing/InsertParagraphSeparatorCommand.h b/WebCore/editing/InsertParagraphSeparatorCommand.h deleted file mode 100644 index 01c08bf..0000000 --- a/WebCore/editing/InsertParagraphSeparatorCommand.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (C) 2005, 2006, 2008 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 InsertParagraphSeparatorCommand_h -#define InsertParagraphSeparatorCommand_h - -#include "CompositeEditCommand.h" - -namespace WebCore { - -class InsertParagraphSeparatorCommand : public CompositeEditCommand { -public: - static PassRefPtr<InsertParagraphSeparatorCommand> create(Document* document, bool useDefaultParagraphElement = false) - { - return adoptRef(new InsertParagraphSeparatorCommand(document, useDefaultParagraphElement)); - } - -private: - InsertParagraphSeparatorCommand(Document*, bool useDefaultParagraphElement); - - virtual void doApply(); - - void calculateStyleBeforeInsertion(const Position&); - void applyStyleAfterInsertion(Node* originalEnclosingBlock); - - bool shouldUseDefaultParagraphElement(Node*) const; - - virtual bool preservesTypingStyle() const; - - RefPtr<CSSMutableStyleDeclaration> m_style; - - bool m_mustUseDefaultParagraphElement; -}; - -} - -#endif diff --git a/WebCore/editing/InsertTextCommand.cpp b/WebCore/editing/InsertTextCommand.cpp deleted file mode 100644 index a831859..0000000 --- a/WebCore/editing/InsertTextCommand.cpp +++ /dev/null @@ -1,242 +0,0 @@ -/* - * Copyright (C) 2005 Apple Computer, 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 "InsertTextCommand.h" - -#include "CharacterNames.h" -#include "CSSMutableStyleDeclaration.h" -#include "CSSComputedStyleDeclaration.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" - -namespace WebCore { - -InsertTextCommand::InsertTextCommand(Document *document) - : CompositeEditCommand(document), m_charactersAdded(0) -{ -} - -void InsertTextCommand::doApply() -{ -} - -Position InsertTextCommand::prepareForTextInsertion(const Position& p) -{ - Position pos = p; - // If an anchor was removed and the selection hasn't changed, we restore it. - RefPtr<Node> anchor = document()->frame()->editor()->removedAnchor(); - if (anchor) { - insertNodeAt(anchor.get(), pos); - document()->frame()->editor()->setRemovedAnchor(0); - pos = Position(anchor.get(), 0); - } - // 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()) { - RefPtr<Node> textNode = document()->createEditingTextNode(""); - insertNodeAt(textNode.get(), pos); - return Position(textNode.get(), 0); - } - - if (isTabSpanTextNode(pos.node())) { - RefPtr<Node> textNode = document()->createEditingTextNode(""); - insertNodeAtTabSpanPosition(textNode.get(), pos); - return Position(textNode.get(), 0); - } - - return pos; -} - -// This avoids the expense of a full fledged delete operation, and avoids a layout that typically results -// from text removal. -bool InsertTextCommand::performTrivialReplace(const String& text, bool selectInsertedText) -{ - if (!endingSelection().isRange()) - return false; - - if (text.contains('\t') || text.contains(' ') || text.contains('\n')) - return false; - - Position start = endingSelection().start(); - Position end = endingSelection().end(); - - if (start.node() != end.node() || !start.node()->isTextNode() || isTabSpanTextNode(start.node())) - return false; - - replaceTextInNode(static_cast<Text*>(start.node()), start.offset(), end.offset() - start.offset(), text); - - Position endPosition(start.node(), start.offset() + text.length()); - - // We could have inserted a part of composed character sequence, - // so we are basically treating ending selection as a range to avoid validation. - // <http://bugs.webkit.org/show_bug.cgi?id=15781> - Selection forcedEndingSelection; - forcedEndingSelection.setWithoutValidation(start, endPosition); - setEndingSelection(forcedEndingSelection); - - if (!selectInsertedText) - setEndingSelection(Selection(endingSelection().visibleEnd())); - - return true; -} - -void InsertTextCommand::input(const String& originalText, bool selectInsertedText) -{ - String text = originalText; - - ASSERT(text.find('\n') == -1); - - if (endingSelection().isNone()) - return; - - if (RenderObject* renderer = endingSelection().start().node()->renderer()) - if (renderer->style()->collapseWhiteSpace()) - // Turn all spaces into non breaking spaces, to make sure that they are treated - // literally, and aren't collapsed after insertion. They will be rebalanced - // (turned into a sequence of regular and non breaking spaces) below. - text.replace(' ', noBreakSpace); - - // Delete the current selection. - // FIXME: This delete operation blows away the typing style. - if (endingSelection().isRange()) { - if (performTrivialReplace(text, selectInsertedText)) - return; - deleteSelection(false, true, true, false); - } - - // Insert the character at the leftmost candidate. - Position startPosition = endingSelection().start().upstream(); - // 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(positionBeforeNode(startPosition.node())); - deleteInsignificantText(startPosition.upstream(), startPosition.downstream()); - if (!startPosition.node()->inDocument()) - startPosition = positionBeforeStartNode; - if (!startPosition.isCandidate()) - startPosition = startPosition.downstream(); - - // FIXME: This typing around anchor behavior doesn't exactly match TextEdit. In TextEdit, - // you won't be placed inside a link when typing after it if you've just placed the caret - // there with the mouse. - startPosition = positionAvoidingSpecialElementBoundary(startPosition, false); - - Position endPosition; - - if (text == "\t") { - endPosition = insertTab(startPosition); - startPosition = endPosition.previous(); - removePlaceholderAt(VisiblePosition(startPosition)); - m_charactersAdded += 1; - } else { - // Make sure the document is set up to receive text - startPosition = prepareForTextInsertion(startPosition); - removePlaceholderAt(VisiblePosition(startPosition)); - Text *textNode = static_cast<Text *>(startPosition.node()); - int offset = startPosition.offset(); - - insertTextIntoNode(textNode, offset, text); - endPosition = Position(textNode, offset + text.length()); - - // The insertion may require adjusting adjacent whitespace, if it is present. - rebalanceWhitespaceAt(endPosition); - // Rebalancing on both sides isn't necessary if we've inserted a space. - if (originalText != " ") - rebalanceWhitespaceAt(startPosition); - - m_charactersAdded += text.length(); - } - - // We could have inserted a part of composed character sequence, - // so we are basically treating ending selection as a range to avoid validation. - // <http://bugs.webkit.org/show_bug.cgi?id=15781> - Selection forcedEndingSelection; - forcedEndingSelection.setWithoutValidation(startPosition, endPosition); - setEndingSelection(forcedEndingSelection); - - // Handle the case where there is a typing style. - CSSMutableStyleDeclaration* typingStyle = document()->frame()->typingStyle(); - RefPtr<CSSComputedStyleDeclaration> endingStyle = endPosition.computedStyle(); - endingStyle->diff(typingStyle); - if (typingStyle && typingStyle->length() > 0) - applyStyle(typingStyle); - - if (!selectInsertedText) - setEndingSelection(Selection(endingSelection().end(), endingSelection().affinity())); -} - -Position InsertTextCommand::insertTab(const Position& pos) -{ - Position insertPos = VisiblePosition(pos, DOWNSTREAM).deepEquivalent(); - - Node *node = insertPos.node(); - unsigned int offset = insertPos.offset(); - - // keep tabs coalesced in tab span - if (isTabSpanTextNode(node)) { - insertTextIntoNode(static_cast<Text *>(node), offset, "\t"); - return Position(node, offset + 1); - } - - // create new tab span - RefPtr<Element> spanNode = createTabSpanElement(document()); - - // place it - if (!node->isTextNode()) { - insertNodeAt(spanNode.get(), insertPos); - } else { - Text *textNode = static_cast<Text *>(node); - if (offset >= textNode->length()) { - insertNodeAfter(spanNode.get(), textNode); - } else { - // split node to make room for the span - // NOTE: splitTextNode uses textNode for the - // second node in the split, so we need to - // insert the span before it. - if (offset > 0) - splitTextNode(textNode, offset); - insertNodeBefore(spanNode.get(), textNode); - } - } - - // return the position following the new tab - return Position(spanNode->lastChild(), caretMaxOffset(spanNode->lastChild())); -} - -bool InsertTextCommand::isInsertTextCommand() const -{ - return true; -} - -} diff --git a/WebCore/editing/InsertTextCommand.h b/WebCore/editing/InsertTextCommand.h deleted file mode 100644 index 650ca65..0000000 --- a/WebCore/editing/InsertTextCommand.h +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (C) 2005, 2006, 2008 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 InsertTextCommand_h -#define InsertTextCommand_h - -#include "CompositeEditCommand.h" - -namespace WebCore { - -class InsertTextCommand : public CompositeEditCommand { -public: - static PassRefPtr<InsertTextCommand> create(Document* document) - { - return adoptRef(new InsertTextCommand(document)); - } - - void input(const String& text, bool selectInsertedText = false); - -private: - InsertTextCommand(Document*); - - void deleteCharacter(); - unsigned charactersAdded() const { return m_charactersAdded; } - - virtual void doApply(); - virtual bool isInsertTextCommand() const; - - Position prepareForTextInsertion(const Position&); - Position insertTab(const Position&); - - bool performTrivialReplace(const String&, bool selectInsertedText); - - unsigned m_charactersAdded; -}; - -} // namespace WebCore - -#endif // InsertTextCommand_h diff --git a/WebCore/editing/JoinTextNodesCommand.cpp b/WebCore/editing/JoinTextNodesCommand.cpp deleted file mode 100644 index dddf43c..0000000 --- a/WebCore/editing/JoinTextNodesCommand.cpp +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (C) 2005, 2008 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 "JoinTextNodesCommand.h" - -#include "Text.h" - -namespace WebCore { - -JoinTextNodesCommand::JoinTextNodesCommand(PassRefPtr<Text> text1, PassRefPtr<Text> text2) - : SimpleEditCommand(text1->document()), m_text1(text1), m_text2(text2) -{ - ASSERT(m_text1); - ASSERT(m_text2); - ASSERT(m_text1->nextSibling() == m_text2); - ASSERT(m_text1->length() > 0); - ASSERT(m_text2->length() > 0); -} - -void JoinTextNodesCommand::doApply() -{ - ASSERT(m_text1); - ASSERT(m_text2); - ASSERT(m_text1->nextSibling() == m_text2); - - ExceptionCode ec = 0; - m_text2->insertData(0, m_text1->data(), ec); - ASSERT(ec == 0); - - m_text2->parentNode()->removeChild(m_text1.get(), ec); - ASSERT(ec == 0); - - m_offset = m_text1->length(); -} - -void JoinTextNodesCommand::doUnapply() -{ - ASSERT(m_text2); - ASSERT(m_offset > 0); - - ExceptionCode ec = 0; - - m_text2->deleteData(0, m_offset, ec); - ASSERT(ec == 0); - - m_text2->parentNode()->insertBefore(m_text1.get(), m_text2.get(), ec); - ASSERT(ec == 0); - - ASSERT(m_text2->previousSibling()->isTextNode()); - ASSERT(m_text2->previousSibling() == m_text1); -} - -} // namespace WebCore diff --git a/WebCore/editing/JoinTextNodesCommand.h b/WebCore/editing/JoinTextNodesCommand.h deleted file mode 100644 index 6fb5c56..0000000 --- a/WebCore/editing/JoinTextNodesCommand.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (C) 2005, 2006, 2008 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 JoinTextNodesCommand_h -#define JoinTextNodesCommand_h - -#include "EditCommand.h" - -namespace WebCore { - -class Text; - -class JoinTextNodesCommand : public SimpleEditCommand { -public: - static PassRefPtr<JoinTextNodesCommand> create(PassRefPtr<Text> text1, PassRefPtr<Text> text2) - { - return adoptRef(new JoinTextNodesCommand(text1, text2)); - } - -private: - JoinTextNodesCommand(PassRefPtr<Text>, PassRefPtr<Text>); - - virtual void doApply(); - virtual void doUnapply(); - - RefPtr<Text> m_text1; - RefPtr<Text> m_text2; - unsigned m_offset; -}; - -} // namespace WebCore - -#endif // JoinTextNodesCommand_h diff --git a/WebCore/editing/MergeIdenticalElementsCommand.cpp b/WebCore/editing/MergeIdenticalElementsCommand.cpp deleted file mode 100644 index 1819b92..0000000 --- a/WebCore/editing/MergeIdenticalElementsCommand.cpp +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (C) 2005, 2008 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 "MergeIdenticalElementsCommand.h" - -#include "Element.h" - -namespace WebCore { - -MergeIdenticalElementsCommand::MergeIdenticalElementsCommand(PassRefPtr<Element> first, PassRefPtr<Element> second) - : SimpleEditCommand(first->document()), m_element1(first), m_element2(second) -{ - ASSERT(m_element1); - ASSERT(m_element2); -} - -void MergeIdenticalElementsCommand::doApply() -{ - ASSERT(m_element1); - ASSERT(m_element2); - ASSERT(m_element1->nextSibling() == m_element2); - - ExceptionCode ec = 0; - - if (!m_atChild) - m_atChild = m_element2->firstChild(); - - while (m_element1->lastChild()) { - m_element2->insertBefore(m_element1->lastChild(), m_element2->firstChild(), ec); - ASSERT(ec == 0); - } - - m_element2->parentNode()->removeChild(m_element1.get(), ec); - ASSERT(ec == 0); -} - -void MergeIdenticalElementsCommand::doUnapply() -{ - ASSERT(m_element1); - ASSERT(m_element2); - - ExceptionCode ec = 0; - - m_element2->parent()->insertBefore(m_element1.get(), m_element2.get(), ec); - ASSERT(ec == 0); - - while (m_element2->firstChild() != m_atChild) { - ASSERT(m_element2->firstChild()); - m_element1->appendChild(m_element2->firstChild(), ec); - ASSERT(ec == 0); - } -} - -} // namespace WebCore diff --git a/WebCore/editing/MergeIdenticalElementsCommand.h b/WebCore/editing/MergeIdenticalElementsCommand.h deleted file mode 100644 index 1ce6302..0000000 --- a/WebCore/editing/MergeIdenticalElementsCommand.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (C) 2005, 2006, 2008 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 MergeIdenticalElementsCommand_h -#define MergeIdenticalElementsCommand_h - -#include "EditCommand.h" - -namespace WebCore { - -class MergeIdenticalElementsCommand : public SimpleEditCommand { -public: - static PassRefPtr<MergeIdenticalElementsCommand> create(PassRefPtr<Element> element1, PassRefPtr<Element> element2) - { - return adoptRef(new MergeIdenticalElementsCommand(element1, element2)); - } - -private: - MergeIdenticalElementsCommand(PassRefPtr<Element>, PassRefPtr<Element>); - - virtual void doApply(); - virtual void doUnapply(); - - RefPtr<Element> m_element1; - RefPtr<Element> m_element2; - RefPtr<Node> m_atChild; -}; - -} // namespace WebCore - -#endif // MergeIdenticalElementsCommand_h diff --git a/WebCore/editing/ModifySelectionListLevel.cpp b/WebCore/editing/ModifySelectionListLevel.cpp deleted file mode 100644 index 0b1123b..0000000 --- a/WebCore/editing/ModifySelectionListLevel.cpp +++ /dev/null @@ -1,293 +0,0 @@ -/* - * Copyright (C) 2006, 2008 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 "ModifySelectionListLevel.h" - -#include "Document.h" -#include "Element.h" -#include "Frame.h" -#include "RenderObject.h" -#include "SelectionController.h" -#include "htmlediting.h" - -namespace WebCore { - -ModifySelectionListLevelCommand::ModifySelectionListLevelCommand(Document* document) - : CompositeEditCommand(document) -{ -} - -bool ModifySelectionListLevelCommand::preservesTypingStyle() const -{ - return true; -} - -// This needs to be static so it can be called by canIncreaseSelectionListLevel and canDecreaseSelectionListLevel -static bool getStartEndListChildren(const Selection& selection, Node*& start, Node*& end) -{ - if (selection.isNone()) - return false; - - // start must be in a list child - Node* startListChild = enclosingListChild(selection.start().node()); - if (!startListChild) - return false; - - // end must be in a list child - Node* endListChild = selection.isRange() ? enclosingListChild(selection.end().node()) : startListChild; - if (!endListChild) - return false; - - // For a range selection we want the following behavior: - // - the start and end must be within the same overall list - // - the start must be at or above the level of the rest of the range - // - if the end is anywhere in a sublist lower than start, the whole sublist gets moved - // In terms of this function, this means: - // - endListChild must start out being be a sibling of startListChild, or be in a - // sublist of startListChild or a sibling - // - if endListChild is in a sublist of startListChild or a sibling, it must be adjusted - // to be the ancestor that is startListChild or its sibling - while (startListChild->parentNode() != endListChild->parentNode()) { - endListChild = endListChild->parentNode(); - if (!endListChild) - return false; - } - - // if the selection ends on a list item with a sublist, include the entire sublist - if (endListChild->renderer()->isListItem()) { - RenderObject* r = endListChild->renderer()->nextSibling(); - if (r && isListElement(r->element())) - endListChild = r->element(); - } - - start = startListChild; - end = endListChild; - return true; -} - -void ModifySelectionListLevelCommand::insertSiblingNodeRangeBefore(Node* startNode, Node* endNode, Node* refNode) -{ - Node* node = startNode; - while (1) { - Node* next = node->nextSibling(); - removeNode(node); - insertNodeBefore(node, refNode); - - if (node == endNode) - break; - - node = next; - } -} - -void ModifySelectionListLevelCommand::insertSiblingNodeRangeAfter(Node* startNode, Node* endNode, Node* refNode) -{ - Node* node = startNode; - while (1) { - Node* next = node->nextSibling(); - removeNode(node); - insertNodeAfter(node, refNode); - - if (node == endNode) - break; - - refNode = node; - node = next; - } -} - -void ModifySelectionListLevelCommand::appendSiblingNodeRange(Node* startNode, Node* endNode, Node* newParent) -{ - Node* node = startNode; - while (1) { - Node* next = node->nextSibling(); - removeNode(node); - appendNode(node, newParent); - - if (node == endNode) - break; - - node = next; - } -} - -IncreaseSelectionListLevelCommand::IncreaseSelectionListLevelCommand(Document* document, Type listType) - : ModifySelectionListLevelCommand(document) - , m_listType(listType) -{ -} - -// This needs to be static so it can be called by canIncreaseSelectionListLevel -static bool canIncreaseListLevel(const Selection& selection, Node*& start, Node*& end) -{ - if (!getStartEndListChildren(selection, start, end)) - return false; - - // start must not be the first child (because you need a prior one - // to increase relative to) - if (!start->renderer()->previousSibling()) - return false; - - return true; -} - -// For the moment, this is SPI and the only client (Mail.app) is satisfied. -// Here are two things to re-evaluate when making into API. -// 1. Currently, InheritedListType uses clones whereas OrderedList and -// UnorderedList create a new list node of the specified type. That is -// inconsistent wrt style. If that is not OK, here are some alternatives: -// - new nodes always inherit style (probably the best choice) -// - new nodes have always have no style -// - new nodes of the same type inherit style -// 2. Currently, the node we return may be either a pre-existing one or -// a new one. Is it confusing to return the pre-existing one without -// somehow indicating that it is not new? If so, here are some alternatives: -// - only return the list node if we created it -// - indicate whether the list node is new or pre-existing -// - (silly) client specifies whether to return pre-existing list nodes -void IncreaseSelectionListLevelCommand::doApply() -{ - Node* startListChild; - Node* endListChild; - if (!canIncreaseListLevel(endingSelection(), startListChild, endListChild)) - return; - - Node* previousItem = startListChild->renderer()->previousSibling()->element(); - if (isListElement(previousItem)) { - // move nodes up into preceding list - appendSiblingNodeRange(startListChild, endListChild, previousItem); - m_listElement = previousItem; - } else { - // create a sublist for the preceding element and move nodes there - RefPtr<Node> newParent; - switch (m_listType) { - case InheritedListType: - newParent = startListChild->parentNode()->cloneNode(false); - break; - case OrderedList: - newParent = createOrderedListElement(document()); - break; - case UnorderedList: - newParent = createUnorderedListElement(document()); - break; - } - insertNodeBefore(newParent.get(), startListChild); - appendSiblingNodeRange(startListChild, endListChild, newParent.get()); - m_listElement = newParent.get(); - } -} - -bool IncreaseSelectionListLevelCommand::canIncreaseSelectionListLevel(Document* document) -{ - Node* startListChild; - Node* endListChild; - return canIncreaseListLevel(document->frame()->selection()->selection(), startListChild, endListChild); -} - -PassRefPtr<Node> IncreaseSelectionListLevelCommand::increaseSelectionListLevelWithType(Document* document, Type listType) -{ - ASSERT(document); - ASSERT(document->frame()); - RefPtr<IncreaseSelectionListLevelCommand> modCommand = new IncreaseSelectionListLevelCommand(document, listType); - modCommand->apply(); - return modCommand->m_listElement; -} - -PassRefPtr<Node> IncreaseSelectionListLevelCommand::increaseSelectionListLevel(Document* document) -{ - return increaseSelectionListLevelWithType(document, InheritedListType); -} - -PassRefPtr<Node> IncreaseSelectionListLevelCommand::increaseSelectionListLevelOrdered(Document* document) -{ - return increaseSelectionListLevelWithType(document, OrderedList); -} - -PassRefPtr<Node> IncreaseSelectionListLevelCommand::increaseSelectionListLevelUnordered(Document* document) -{ - return increaseSelectionListLevelWithType(document, UnorderedList); -} - -DecreaseSelectionListLevelCommand::DecreaseSelectionListLevelCommand(Document* document) - : ModifySelectionListLevelCommand(document) -{ -} - -// This needs to be static so it can be called by canDecreaseSelectionListLevel -static bool canDecreaseListLevel(const Selection& selection, Node*& start, Node*& end) -{ - if (!getStartEndListChildren(selection, start, end)) - return false; - - // there must be a destination list to move the items to - if (!isListElement(start->parentNode()->parentNode())) - return false; - - return true; -} - -void DecreaseSelectionListLevelCommand::doApply() -{ - Node* startListChild; - Node* endListChild; - if (!canDecreaseListLevel(endingSelection(), startListChild, endListChild)) - return; - - Node* previousItem = startListChild->renderer()->previousSibling() ? startListChild->renderer()->previousSibling()->element() : 0; - Node* nextItem = endListChild->renderer()->nextSibling() ? endListChild->renderer()->nextSibling()->element() : 0; - Node* listNode = startListChild->parentNode(); - - if (!previousItem) { - // at start of sublist, move the child(ren) to before the sublist - insertSiblingNodeRangeBefore(startListChild, endListChild, listNode); - // if that was the whole sublist we moved, remove the sublist node - if (!nextItem) - removeNode(listNode); - } else if (!nextItem) { - // at end of list, move the child(ren) to after the sublist - insertSiblingNodeRangeAfter(startListChild, endListChild, listNode); - } else { - // in the middle of list, split the list and move the children to the divide - splitElement(static_cast<Element*>(listNode), startListChild); - insertSiblingNodeRangeBefore(startListChild, endListChild, listNode); - } -} - -bool DecreaseSelectionListLevelCommand::canDecreaseSelectionListLevel(Document* document) -{ - Node* startListChild; - Node* endListChild; - return canDecreaseListLevel(document->frame()->selection()->selection(), startListChild, endListChild); -} - -void DecreaseSelectionListLevelCommand::decreaseSelectionListLevel(Document* document) -{ - ASSERT(document); - ASSERT(document->frame()); - applyCommand(new DecreaseSelectionListLevelCommand(document)); -} - -} diff --git a/WebCore/editing/ModifySelectionListLevel.h b/WebCore/editing/ModifySelectionListLevel.h deleted file mode 100644 index 1e66d10..0000000 --- a/WebCore/editing/ModifySelectionListLevel.h +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (C) 2006 Apple Computer, 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 ModifySelectionListLevel_h -#define ModifySelectionListLevel_h - -#include "CompositeEditCommand.h" - -namespace WebCore { - -// ModifySelectionListLevelCommand provides functions useful for both increasing and decreasing the list level. -// It is the base class of IncreaseSelectionListLevelCommand and DecreaseSelectionListLevelCommand. -// It is not used on its own. -class ModifySelectionListLevelCommand : public CompositeEditCommand { -protected: - ModifySelectionListLevelCommand(Document*); - - void appendSiblingNodeRange(Node* startNode, Node* endNode, Node* newParent); - void insertSiblingNodeRangeBefore(Node* startNode, Node* endNode, Node* refNode); - void insertSiblingNodeRangeAfter(Node* startNode, Node* endNode, Node* refNode); - -private: - virtual bool preservesTypingStyle() const; -}; - -// IncreaseSelectionListLevelCommand moves the selected list items one level deeper. -class IncreaseSelectionListLevelCommand : public ModifySelectionListLevelCommand { -public: - static bool canIncreaseSelectionListLevel(Document*); - static PassRefPtr<Node> increaseSelectionListLevel(Document*); - static PassRefPtr<Node> increaseSelectionListLevelOrdered(Document*); - static PassRefPtr<Node> increaseSelectionListLevelUnordered(Document*); - -private: - enum Type { InheritedListType, OrderedList, UnorderedList }; - static PassRefPtr<Node> increaseSelectionListLevelWithType(Document*, Type listType); - - IncreaseSelectionListLevelCommand(Document*, Type); - virtual void doApply(); - - Type m_listType; - RefPtr<Node> m_listElement; -}; - -// DecreaseSelectionListLevelCommand moves the selected list items one level shallower. -class DecreaseSelectionListLevelCommand : public ModifySelectionListLevelCommand { -public: - static bool canDecreaseSelectionListLevel(Document*); - static void decreaseSelectionListLevel(Document*); - -private: - DecreaseSelectionListLevelCommand(Document*); - virtual void doApply(); -}; - -} // namespace WebCore - -#endif // ModifySelectionListLevel_h diff --git a/WebCore/editing/MoveSelectionCommand.cpp b/WebCore/editing/MoveSelectionCommand.cpp deleted file mode 100644 index 08587cb..0000000 --- a/WebCore/editing/MoveSelectionCommand.cpp +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright (C) 2005, 2006 Apple Computer, 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 "MoveSelectionCommand.h" - -#include "DocumentFragment.h" -#include "ReplaceSelectionCommand.h" - -namespace WebCore { - -MoveSelectionCommand::MoveSelectionCommand(PassRefPtr<DocumentFragment> fragment, const Position& position, bool smartMove) - : CompositeEditCommand(position.node()->document()), m_fragment(fragment), m_position(position), m_smartMove(smartMove) -{ - ASSERT(m_fragment); -} - -void MoveSelectionCommand::doApply() -{ - Selection selection = endingSelection(); - ASSERT(selection.isRange()); - - Position pos = m_position; - if (pos.isNull()) - return; - - // Update the position otherwise it may become invalid after the selection is deleted. - Node *positionNode = m_position.node(); - int positionOffset = m_position.offset(); - Position selectionEnd = selection.end(); - int selectionEndOffset = selectionEnd.offset(); - if (selectionEnd.node() == positionNode && selectionEndOffset < positionOffset) { - positionOffset -= selectionEndOffset; - Position selectionStart = selection.start(); - if (selectionStart.node() == positionNode) { - positionOffset += selectionStart.offset(); - } - pos = Position(positionNode, positionOffset); - } - - deleteSelection(m_smartMove); - - // If the node for the destination has been removed as a result of the deletion, - // 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()) - pos = endingSelection().start(); - - setEndingSelection(Selection(pos, endingSelection().affinity())); - applyCommandToComposite(ReplaceSelectionCommand::create(positionNode->document(), m_fragment, true, m_smartMove)); -} - -EditAction MoveSelectionCommand::editingAction() const -{ - return EditActionDrag; -} - -} // namespace WebCore diff --git a/WebCore/editing/MoveSelectionCommand.h b/WebCore/editing/MoveSelectionCommand.h deleted file mode 100644 index 253c02c..0000000 --- a/WebCore/editing/MoveSelectionCommand.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (C) 2005, 2006, 2008 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 MoveSelectionCommand_h -#define MoveSelectionCommand_h - -#include "CompositeEditCommand.h" - -namespace WebCore { - -class DocumentFragment; - -class MoveSelectionCommand : public CompositeEditCommand { -public: - static PassRefPtr<MoveSelectionCommand> create(PassRefPtr<DocumentFragment> fragment, const Position& position, bool smartMove = false) - { - return adoptRef(new MoveSelectionCommand(fragment, position, smartMove)); - } - -private: - MoveSelectionCommand(PassRefPtr<DocumentFragment>, const Position&, bool smartMove); - - virtual void doApply(); - virtual EditAction editingAction() const; - - RefPtr<DocumentFragment> m_fragment; - Position m_position; - bool m_smartMove; -}; - -} // namespace WebCore - -#endif // MoveSelectionCommand_h diff --git a/WebCore/editing/RemoveCSSPropertyCommand.cpp b/WebCore/editing/RemoveCSSPropertyCommand.cpp deleted file mode 100644 index d4afed0..0000000 --- a/WebCore/editing/RemoveCSSPropertyCommand.cpp +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (C) 2005, 2008 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 "RemoveCSSPropertyCommand.h" - -#include "CSSMutableStyleDeclaration.h" -#include <wtf/Assertions.h> - -namespace WebCore { - -RemoveCSSPropertyCommand::RemoveCSSPropertyCommand(Document* document, CSSStyleDeclaration* decl, int property) - : SimpleEditCommand(document) - , m_decl(decl->makeMutable()) - , m_property(property) - , m_important(false) -{ - ASSERT(m_decl); -} - -void RemoveCSSPropertyCommand::doApply() -{ - ASSERT(m_decl); - - m_oldValue = m_decl->getPropertyValue(m_property); - ASSERT(!m_oldValue.isNull()); - - m_important = m_decl->getPropertyPriority(m_property); - m_decl->removeProperty(m_property); -} - -void RemoveCSSPropertyCommand::doUnapply() -{ - ASSERT(m_decl); - ASSERT(!m_oldValue.isNull()); - - m_decl->setProperty(m_property, m_oldValue, m_important); -} - -} // namespace WebCore diff --git a/WebCore/editing/RemoveCSSPropertyCommand.h b/WebCore/editing/RemoveCSSPropertyCommand.h deleted file mode 100644 index 8a96b4b..0000000 --- a/WebCore/editing/RemoveCSSPropertyCommand.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (C) 2005, 2006, 2008 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 RemoveCSSPropertyCommand_h -#define RemoveCSSPropertyCommand_h - -#include "EditCommand.h" - -namespace WebCore { - -class CSSStyleDeclaration; - -class RemoveCSSPropertyCommand : public SimpleEditCommand { -public: - static PassRefPtr<RemoveCSSPropertyCommand> create(Document* document, CSSStyleDeclaration* style, int property) - { - return adoptRef(new RemoveCSSPropertyCommand(document, style, property)); - } - -private: - RemoveCSSPropertyCommand(Document*, CSSStyleDeclaration*, int property); - - virtual void doApply(); - virtual void doUnapply(); - - RefPtr<CSSMutableStyleDeclaration> m_decl; - int m_property; - String m_oldValue; - bool m_important; -}; - -} // namespace WebCore - -#endif // RemoveCSSPropertyCommand_h diff --git a/WebCore/editing/RemoveFormatCommand.cpp b/WebCore/editing/RemoveFormatCommand.cpp deleted file mode 100644 index 71f4015..0000000 --- a/WebCore/editing/RemoveFormatCommand.cpp +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright (C) 2007 Apple Computer, 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 "RemoveFormatCommand.h" - -#include "CSSComputedStyleDeclaration.h" -#include "Editor.h" -#include "Frame.h" -#include "HTMLNames.h" -#include "Selection.h" -#include "SelectionController.h" -#include "TextIterator.h" -#include "TypingCommand.h" - -namespace WebCore { - -using namespace HTMLNames; - -RemoveFormatCommand::RemoveFormatCommand(Document* document) - : CompositeEditCommand(document) -{ -} - -void RemoveFormatCommand::doApply() -{ - Frame* frame = document()->frame(); - - // Make a plain text string from the selection to remove formatting like tables and lists. - String string = plainText(frame->selection()->selection().toRange().get()); - - // Get the default style for this editable root, it's the style that we'll give the - // content that we're operating on. - Node* root = frame->selection()->rootEditableElement(); - RefPtr<CSSMutableStyleDeclaration> defaultStyle = computedStyle(root)->copyInheritableProperties(); - - // Delete the selected content. - // FIXME: We should be able to leave this to insertText, but its delete operation - // doesn't preserve the style we're about to set. - deleteSelection(); - - // Delete doesn't remove fully selected lists. - while (breakOutOfEmptyListItem()) - ; - - // If the selection was all formatting (like an empty list) the format-less text will - // be empty. Early return since we don't need to do any of the work that follows and - // to avoid the ASSERT that fires if input(...) is called with an empty String. - if (string.isEmpty()) - return; - - // Normally, deleting a fully selected anchor and then inserting text will re-create - // the removed anchor, but we don't want that behavior here. - frame->editor()->setRemovedAnchor(0); - // Insert the content with the default style. - // See <rdar://problem/5794382> RemoveFormat doesn't always reset text alignment - frame->setTypingStyle(defaultStyle.get()); - - inputText(string, true); -} - -} diff --git a/WebCore/editing/RemoveFormatCommand.h b/WebCore/editing/RemoveFormatCommand.h deleted file mode 100644 index daca2db..0000000 --- a/WebCore/editing/RemoveFormatCommand.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (C) 2007, 2008 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 RemoveFormatCommand_h -#define RemoveFormatCommand_h - -#include "CompositeEditCommand.h" - -namespace WebCore { - -class RemoveFormatCommand : public CompositeEditCommand { -public: - static PassRefPtr<RemoveFormatCommand> create(Document* document) - { - return adoptRef(new RemoveFormatCommand(document)); - } - -private: - RemoveFormatCommand(Document*); - - virtual void doApply(); - virtual EditAction editingAction() const { return EditActionUnspecified; } -}; - -} // namespace WebCore - -#endif // RemoveFormatCommand_h diff --git a/WebCore/editing/RemoveNodeAttributeCommand.cpp b/WebCore/editing/RemoveNodeAttributeCommand.cpp deleted file mode 100644 index bcf1fdf..0000000 --- a/WebCore/editing/RemoveNodeAttributeCommand.cpp +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (C) 2005, 2008 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 "RemoveNodeAttributeCommand.h" - -#include "Element.h" -#include <wtf/Assertions.h> - -namespace WebCore { - -RemoveNodeAttributeCommand::RemoveNodeAttributeCommand(PassRefPtr<Element> element, const QualifiedName& attribute) - : SimpleEditCommand(element->document()), m_element(element), m_attribute(attribute) -{ - ASSERT(m_element); -} - -void RemoveNodeAttributeCommand::doApply() -{ - ASSERT(m_element); - - m_oldValue = m_element->getAttribute(m_attribute); - ASSERT(!m_oldValue.isNull()); - - ExceptionCode ec = 0; - m_element->removeAttribute(m_attribute, ec); - ASSERT(ec == 0); -} - -void RemoveNodeAttributeCommand::doUnapply() -{ - ASSERT(m_element); - ASSERT(!m_oldValue.isNull()); - - ExceptionCode ec = 0; - m_element->setAttribute(m_attribute, m_oldValue.impl(), ec); - ASSERT(ec == 0); -} - -} // namespace WebCore diff --git a/WebCore/editing/RemoveNodeAttributeCommand.h b/WebCore/editing/RemoveNodeAttributeCommand.h deleted file mode 100644 index fd9127e..0000000 --- a/WebCore/editing/RemoveNodeAttributeCommand.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (C) 2005, 2006, 2008 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 RemoveNodeAttributeCommand_h -#define RemoveNodeAttributeCommand_h - -#include "EditCommand.h" -#include "QualifiedName.h" - -namespace WebCore { - -class RemoveNodeAttributeCommand : public SimpleEditCommand { -public: - static PassRefPtr<RemoveNodeAttributeCommand> create(PassRefPtr<Element> element, const QualifiedName& attribute) - { - return adoptRef(new RemoveNodeAttributeCommand(element, attribute)); - } - -private: - RemoveNodeAttributeCommand(PassRefPtr<Element>, const QualifiedName& attribute); - - virtual void doApply(); - virtual void doUnapply(); - - RefPtr<Element> m_element; - QualifiedName m_attribute; - AtomicString m_oldValue; -}; - -} // namespace WebCore - -#endif // RemoveNodeAttributeCommand_h diff --git a/WebCore/editing/RemoveNodeCommand.cpp b/WebCore/editing/RemoveNodeCommand.cpp deleted file mode 100644 index 2519730..0000000 --- a/WebCore/editing/RemoveNodeCommand.cpp +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (C) 2005, 2008 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 "RemoveNodeCommand.h" - -#include "Node.h" -#include <wtf/Assertions.h> - -namespace WebCore { - -RemoveNodeCommand::RemoveNodeCommand(PassRefPtr<Node> removeChild) - : SimpleEditCommand(removeChild->document()) - , m_removeChild(removeChild) - , m_parent(m_removeChild->parentNode()) - , m_refChild(m_removeChild->nextSibling()) -{ - ASSERT(m_parent); -} - -void RemoveNodeCommand::doApply() -{ - ASSERT(m_parent); - ASSERT(m_removeChild); - - ExceptionCode ec = 0; - m_parent->removeChild(m_removeChild.get(), ec); - ASSERT(ec == 0); -} - -void RemoveNodeCommand::doUnapply() -{ - ASSERT(m_parent); - ASSERT(m_removeChild); - - ExceptionCode ec = 0; - m_parent->insertBefore(m_removeChild.get(), m_refChild.get(), ec); - ASSERT(ec == 0); -} - -} diff --git a/WebCore/editing/RemoveNodeCommand.h b/WebCore/editing/RemoveNodeCommand.h deleted file mode 100644 index 802cb8b..0000000 --- a/WebCore/editing/RemoveNodeCommand.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (C) 2005, 2006, 2008 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 RemoveNodeCommand_h -#define RemoveNodeCommand_h - -#include "EditCommand.h" - -namespace WebCore { - -class RemoveNodeCommand : public SimpleEditCommand { -public: - static PassRefPtr<RemoveNodeCommand> create(PassRefPtr<Node> node) - { - return adoptRef(new RemoveNodeCommand(node)); - } - -private: - RemoveNodeCommand(PassRefPtr<Node>); - - virtual void doApply(); - virtual void doUnapply(); - - RefPtr<Node> m_removeChild; - RefPtr<Node> m_parent; - RefPtr<Node> m_refChild; -}; - -} // namespace WebCore - -#endif // RemoveNodeCommand_h diff --git a/WebCore/editing/RemoveNodePreservingChildrenCommand.cpp b/WebCore/editing/RemoveNodePreservingChildrenCommand.cpp deleted file mode 100644 index f583865..0000000 --- a/WebCore/editing/RemoveNodePreservingChildrenCommand.cpp +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (C) 2005, 2008 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 "RemoveNodePreservingChildrenCommand.h" - -#include "Node.h" -#include <wtf/Assertions.h> - -namespace WebCore { - -RemoveNodePreservingChildrenCommand::RemoveNodePreservingChildrenCommand(PassRefPtr<Node> node) - : CompositeEditCommand(node->document()), m_node(node) -{ - ASSERT(m_node); -} - -void RemoveNodePreservingChildrenCommand::doApply() -{ - while (Node* curr = m_node->firstChild()) { - removeNode(curr); - insertNodeBefore(curr, m_node.get()); - } - removeNode(m_node.get()); -} - -} diff --git a/WebCore/editing/RemoveNodePreservingChildrenCommand.h b/WebCore/editing/RemoveNodePreservingChildrenCommand.h deleted file mode 100644 index d2b635f..0000000 --- a/WebCore/editing/RemoveNodePreservingChildrenCommand.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (C) 2005, 2006, 2008 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 RemoveNodePreservingChildrenCommand_h -#define RemoveNodePreservingChildrenCommand_h - -#include "CompositeEditCommand.h" - -namespace WebCore { - -class RemoveNodePreservingChildrenCommand : public CompositeEditCommand { -public: - static PassRefPtr<RemoveNodePreservingChildrenCommand> create(PassRefPtr<Node> node) - { - return adoptRef(new RemoveNodePreservingChildrenCommand(node)); - } - -private: - RemoveNodePreservingChildrenCommand(PassRefPtr<Node>); - - virtual void doApply(); - - RefPtr<Node> m_node; -}; - -} // namespace WebCore - -#endif // RemoveNodePreservingChildrenCommand_h diff --git a/WebCore/editing/ReplaceSelectionCommand.cpp b/WebCore/editing/ReplaceSelectionCommand.cpp deleted file mode 100644 index 4304af3..0000000 --- a/WebCore/editing/ReplaceSelectionCommand.cpp +++ /dev/null @@ -1,1003 +0,0 @@ -/* - * Copyright (C) 2005, 2006, 2008 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 "ReplaceSelectionCommand.h" - -#include "ApplyStyleCommand.h" -#include "BeforeTextInsertedEvent.h" -#include "CSSComputedStyleDeclaration.h" -#include "CSSProperty.h" -#include "CSSPropertyNames.h" -#include "CSSValueKeywords.h" -#include "Document.h" -#include "DocumentFragment.h" -#include "EditingText.h" -#include "EventNames.h" -#include "Element.h" -#include "Frame.h" -#include "HTMLElement.h" -#include "HTMLInterchange.h" -#include "HTMLInputElement.h" -#include "HTMLNames.h" -#include "SelectionController.h" -#include "SmartReplace.h" -#include "TextIterator.h" -#include "htmlediting.h" -#include "markup.h" -#include "visible_units.h" - -namespace WebCore { - -using namespace HTMLNames; - -enum EFragmentType { EmptyFragment, SingleTextNodeFragment, TreeFragment }; - -// --- ReplacementFragment helper class - -class ReplacementFragment : Noncopyable { -public: - ReplacementFragment(Document*, DocumentFragment*, bool matchStyle, const Selection&); - - Node* firstChild() const; - Node* lastChild() const; - - bool isEmpty() const; - - bool hasInterchangeNewlineAtStart() const { return m_hasInterchangeNewlineAtStart; } - bool hasInterchangeNewlineAtEnd() const { return m_hasInterchangeNewlineAtEnd; } - - void removeNode(PassRefPtr<Node>); - void removeNodePreservingChildren(Node*); - -private: - PassRefPtr<Node> insertFragmentForTestRendering(Node* context); - void removeUnrenderedNodes(Node*); - void restoreTestRenderingNodesToFragment(Node*); - void removeInterchangeNodes(Node*); - - void insertNodeBefore(Node* node, Node* refNode); - - RefPtr<Document> m_document; - RefPtr<DocumentFragment> m_fragment; - bool m_matchStyle; - bool m_hasInterchangeNewlineAtStart; - bool m_hasInterchangeNewlineAtEnd; -}; - -static bool isInterchangeNewlineNode(const Node *node) -{ - static String interchangeNewlineClassString(AppleInterchangeNewline); - return node && node->hasTagName(brTag) && - static_cast<const Element *>(node)->getAttribute(classAttr) == interchangeNewlineClassString; -} - -static bool isInterchangeConvertedSpaceSpan(const Node *node) -{ - static String convertedSpaceSpanClassString(AppleConvertedSpace); - return node->isHTMLElement() && - static_cast<const HTMLElement *>(node)->getAttribute(classAttr) == convertedSpaceSpanClassString; -} - -ReplacementFragment::ReplacementFragment(Document* document, DocumentFragment* fragment, bool matchStyle, const Selection& selection) - : m_document(document), - m_fragment(fragment), - m_matchStyle(matchStyle), - m_hasInterchangeNewlineAtStart(false), - m_hasInterchangeNewlineAtEnd(false) -{ - if (!m_document) - return; - if (!m_fragment) - return; - if (!m_fragment->firstChild()) - return; - - Element* editableRoot = selection.rootEditableElement(); - ASSERT(editableRoot); - if (!editableRoot) - return; - - Node* shadowAncestorNode = editableRoot->shadowAncestorNode(); - - if (!editableRoot->inlineEventListenerForType(eventNames().webkitBeforeTextInsertedEvent) && - // FIXME: Remove these checks once textareas and textfields actually register an event handler. - !(shadowAncestorNode && shadowAncestorNode->renderer() && shadowAncestorNode->renderer()->isTextField()) && - !(shadowAncestorNode && shadowAncestorNode->renderer() && shadowAncestorNode->renderer()->isTextArea()) && - editableRoot->isContentRichlyEditable()) { - removeInterchangeNodes(m_fragment->firstChild()); - return; - } - - Node* styleNode = selection.base().node(); - RefPtr<Node> holder = insertFragmentForTestRendering(styleNode); - - RefPtr<Range> range = Selection::selectionFromContentsOfNode(holder.get()).toRange(); - String text = plainText(range.get()); - // Give the root a chance to change the text. - RefPtr<BeforeTextInsertedEvent> evt = BeforeTextInsertedEvent::create(text); - ExceptionCode ec = 0; - editableRoot->dispatchEvent(evt, ec); - ASSERT(ec == 0); - if (text != evt->text() || !editableRoot->isContentRichlyEditable()) { - restoreTestRenderingNodesToFragment(holder.get()); - removeNode(holder); - - m_fragment = createFragmentFromText(selection.toRange().get(), evt->text()); - if (!m_fragment->firstChild()) - return; - holder = insertFragmentForTestRendering(styleNode); - } - - removeInterchangeNodes(holder->firstChild()); - - removeUnrenderedNodes(holder.get()); - restoreTestRenderingNodesToFragment(holder.get()); - removeNode(holder); -} - -bool ReplacementFragment::isEmpty() const -{ - return (!m_fragment || !m_fragment->firstChild()) && !m_hasInterchangeNewlineAtStart && !m_hasInterchangeNewlineAtEnd; -} - -Node *ReplacementFragment::firstChild() const -{ - return m_fragment ? m_fragment->firstChild() : 0; -} - -Node *ReplacementFragment::lastChild() const -{ - return m_fragment ? m_fragment->lastChild() : 0; -} - -void ReplacementFragment::removeNodePreservingChildren(Node *node) -{ - if (!node) - return; - - while (RefPtr<Node> n = node->firstChild()) { - removeNode(n); - insertNodeBefore(n.get(), node); - } - removeNode(node); -} - -void ReplacementFragment::removeNode(PassRefPtr<Node> node) -{ - if (!node) - return; - - Node *parent = node->parentNode(); - if (!parent) - return; - - ExceptionCode ec = 0; - parent->removeChild(node.get(), ec); - ASSERT(ec == 0); -} - -void ReplacementFragment::insertNodeBefore(Node *node, Node *refNode) -{ - if (!node || !refNode) - return; - - Node *parent = refNode->parentNode(); - if (!parent) - return; - - ExceptionCode ec = 0; - parent->insertBefore(node, refNode, ec); - ASSERT(ec == 0); -} - -PassRefPtr<Node> ReplacementFragment::insertFragmentForTestRendering(Node* context) -{ - Node* body = m_document->body(); - if (!body) - return 0; - - RefPtr<StyledElement> holder = static_pointer_cast<StyledElement>(createDefaultParagraphElement(m_document.get())); - - ExceptionCode ec = 0; - - // Copy the whitespace and user-select style from the context onto this element. - // FIXME: We should examine other style properties to see if they would be appropriate to consider during the test rendering. - Node* n = context; - while (n && !n->isElementNode()) - n = n->parentNode(); - if (n) { - RefPtr<CSSComputedStyleDeclaration> conFontStyle = computedStyle(n); - CSSStyleDeclaration* style = holder->style(); - style->setProperty(CSSPropertyWhiteSpace, conFontStyle->getPropertyValue(CSSPropertyWhiteSpace), false, ec); - ASSERT(ec == 0); - style->setProperty(CSSPropertyWebkitUserSelect, conFontStyle->getPropertyValue(CSSPropertyWebkitUserSelect), false, ec); - ASSERT(ec == 0); - } - - holder->appendChild(m_fragment, ec); - ASSERT(ec == 0); - - body->appendChild(holder.get(), ec); - ASSERT(ec == 0); - - m_document->updateLayoutIgnorePendingStylesheets(); - - return holder.release(); -} - -void ReplacementFragment::restoreTestRenderingNodesToFragment(Node *holder) -{ - if (!holder) - return; - - ExceptionCode ec = 0; - while (RefPtr<Node> node = holder->firstChild()) { - holder->removeChild(node.get(), ec); - ASSERT(ec == 0); - m_fragment->appendChild(node.get(), ec); - ASSERT(ec == 0); - } -} - -void ReplacementFragment::removeUnrenderedNodes(Node* holder) -{ - Vector<Node*> unrendered; - - for (Node* node = holder->firstChild(); node; node = node->traverseNextNode(holder)) - if (!isNodeRendered(node) && !isTableStructureNode(node)) - unrendered.append(node); - - size_t n = unrendered.size(); - for (size_t i = 0; i < n; ++i) - removeNode(unrendered[i]); -} - -void ReplacementFragment::removeInterchangeNodes(Node* startNode) -{ - Node* node = startNode; - Node* newlineAtStartNode = 0; - Node* newlineAtEndNode = 0; - while (node) { - Node *next = node->traverseNextNode(); - if (isInterchangeNewlineNode(node)) { - if (next || node == startNode) { - m_hasInterchangeNewlineAtStart = true; - newlineAtStartNode = node; - } - else { - m_hasInterchangeNewlineAtEnd = true; - newlineAtEndNode = node; - } - } - else if (isInterchangeConvertedSpaceSpan(node)) { - RefPtr<Node> n = 0; - while ((n = node->firstChild())) { - removeNode(n); - insertNodeBefore(n.get(), node); - } - removeNode(node); - if (n) - next = n->traverseNextNode(); - } - node = next; - } - - if (newlineAtStartNode) - removeNode(newlineAtStartNode); - if (newlineAtEndNode) - removeNode(newlineAtEndNode); -} - -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) -{ -} - -bool ReplaceSelectionCommand::shouldMergeStart(bool selectionStartWasStartOfParagraph, bool fragmentHasInterchangeNewlineAtStart) -{ - VisiblePosition startOfInsertedContent(positionAtStartOfInsertedContent()); - VisiblePosition prev = startOfInsertedContent.previous(true); - if (prev.isNull()) - return false; - - return !selectionStartWasStartOfParagraph && - !fragmentHasInterchangeNewlineAtStart && - isStartOfParagraph(startOfInsertedContent) && - !startOfInsertedContent.deepEquivalent().node()->hasTagName(brTag) && - shouldMerge(startOfInsertedContent, prev); -} - -bool ReplaceSelectionCommand::shouldMergeEnd(bool selectionEndWasEndOfParagraph) -{ - VisiblePosition endOfInsertedContent(positionAtEndOfInsertedContent()); - VisiblePosition next = endOfInsertedContent.next(true); - if (next.isNull()) - return false; - - return !selectionEndWasEndOfParagraph && - isEndOfParagraph(endOfInsertedContent) && - !endOfInsertedContent.deepEquivalent().node()->hasTagName(brTag) && - shouldMerge(endOfInsertedContent, next); -} - -static bool isMailPasteAsQuotationNode(const Node* node) -{ - return node && node->hasTagName(blockquoteTag) && node->isElementNode() && static_cast<const Element*>(node)->getAttribute(classAttr) == ApplePasteAsQuotation; -} - -// Wrap CompositeEditCommand::removeNodePreservingChildren() so we can update the nodes we track -void ReplaceSelectionCommand::removeNodePreservingChildren(Node* node) -{ - if (m_firstNodeInserted == node) - m_firstNodeInserted = node->traverseNextNode(); - if (m_lastLeafInserted == node) - m_lastLeafInserted = node->lastChild() ? node->lastChild() : node->traverseNextSibling(); - CompositeEditCommand::removeNodePreservingChildren(node); -} - -// Wrap CompositeEditCommand::removeNodeAndPruneAncestors() so we can update the nodes we track -void ReplaceSelectionCommand::removeNodeAndPruneAncestors(Node* node) -{ - // prepare in case m_firstNodeInserted and/or m_lastLeafInserted get removed - // FIXME: shouldn't m_lastLeafInserted be adjusted using traversePreviousNode()? - Node* afterFirst = m_firstNodeInserted ? m_firstNodeInserted->traverseNextSibling() : 0; - Node* afterLast = m_lastLeafInserted ? m_lastLeafInserted->traverseNextSibling() : 0; - - CompositeEditCommand::removeNodeAndPruneAncestors(node); - - // adjust m_firstNodeInserted and m_lastLeafInserted since either or both may have been removed - if (m_lastLeafInserted && !m_lastLeafInserted->inDocument()) - m_lastLeafInserted = afterLast; - if (m_firstNodeInserted && !m_firstNodeInserted->inDocument()) - m_firstNodeInserted = m_lastLeafInserted && m_lastLeafInserted->inDocument() ? afterFirst : 0; -} - -bool ReplaceSelectionCommand::shouldMerge(const VisiblePosition& from, const VisiblePosition& to) -{ - if (from.isNull() || to.isNull()) - return false; - - Node* fromNode = from.deepEquivalent().node(); - Node* toNode = to.deepEquivalent().node(); - Node* fromNodeBlock = enclosingBlock(fromNode); - return !enclosingNodeOfType(from.deepEquivalent(), &isMailPasteAsQuotationNode) && - fromNodeBlock && (!fromNodeBlock->hasTagName(blockquoteTag) || isMailBlockquote(fromNodeBlock)) && - enclosingListChild(fromNode) == enclosingListChild(toNode) && - enclosingTableCell(from.deepEquivalent()) == enclosingTableCell(from.deepEquivalent()) && - // Don't merge to or from a position before or after a block because it would - // be a no-op and cause infinite recursion. - !isBlock(fromNode) && !isBlock(toNode); -} - -// Style rules that match just inserted elements could change their appearance, like -// a div inserted into a document with div { display:inline; }. -void ReplaceSelectionCommand::negateStyleRulesThatAffectAppearance() -{ - for (RefPtr<Node> node = m_firstNodeInserted.get(); node; node = node->traverseNextNode()) { - // FIXME: <rdar://problem/5371536> Style rules that match pasted content can change it's appearance - if (isStyleSpan(node.get())) { - HTMLElement* e = static_cast<HTMLElement*>(node.get()); - // There are other styles that style rules can give to style spans, - // but these are the two important ones because they'll prevent - // inserted content from appearing in the right paragraph. - // FIXME: Hyatt is concerned that selectively using display:inline will give inconsistent - // results. We already know one issue because td elements ignore their display property - // in quirks mode (which Mail.app is always in). We should look for an alternative. - if (isBlock(e)) - e->getInlineStyleDecl()->setProperty(CSSPropertyDisplay, CSSValueInline); - if (e->renderer() && e->renderer()->style()->floating() != FNONE) - e->getInlineStyleDecl()->setProperty(CSSPropertyFloat, CSSValueNone); - } - if (node == m_lastLeafInserted) - break; - } -} - -void ReplaceSelectionCommand::removeUnrenderedTextNodesAtEnds() -{ - document()->updateLayoutIgnorePendingStylesheets(); - if (!m_lastLeafInserted->renderer() && - m_lastLeafInserted->isTextNode() && - !enclosingNodeWithTag(Position(m_lastLeafInserted.get(), 0), selectTag) && - !enclosingNodeWithTag(Position(m_lastLeafInserted.get(), 0), scriptTag)) { - if (m_firstNodeInserted == m_lastLeafInserted) { - removeNode(m_lastLeafInserted.get()); - m_lastLeafInserted = 0; - m_firstNodeInserted = 0; - return; - } - RefPtr<Node> previous = m_lastLeafInserted->traversePreviousNode(); - removeNode(m_lastLeafInserted.get()); - m_lastLeafInserted = previous; - } - - // We don't have to make sure that m_firstNodeInserted isn't inside a select or script element, because - // it is a top level node in the fragment and the user can't insert into those elements. - if (!m_firstNodeInserted->renderer() && - m_firstNodeInserted->isTextNode()) { - if (m_firstNodeInserted == m_lastLeafInserted) { - removeNode(m_firstNodeInserted.get()); - m_firstNodeInserted = 0; - m_lastLeafInserted = 0; - return; - } - RefPtr<Node> next = m_firstNodeInserted->traverseNextSibling(); - removeNode(m_firstNodeInserted.get()); - m_firstNodeInserted = next; - } -} - -void ReplaceSelectionCommand::handlePasteAsQuotationNode() -{ - Node* node = m_firstNodeInserted.get(); - if (isMailPasteAsQuotationNode(node)) - removeNodeAttribute(static_cast<Element*>(node), classAttr); -} - -VisiblePosition ReplaceSelectionCommand::positionAtEndOfInsertedContent() -{ - Node* lastNode = m_lastLeafInserted.get(); - Node* enclosingSelect = enclosingNodeWithTag(Position(lastNode, 0), selectTag); - if (enclosingSelect) - lastNode = enclosingSelect; - return VisiblePosition(Position(lastNode, maxDeepOffset(lastNode))); -} - -VisiblePosition ReplaceSelectionCommand::positionAtStartOfInsertedContent() -{ - // Return the inserted content's first VisiblePosition. - return VisiblePosition(nextCandidate(positionBeforeNode(m_firstNodeInserted.get()))); -} - -// Remove style spans before insertion if they are unnecessary. It's faster because we'll -// avoid doing a layout. -static bool handleStyleSpansBeforeInsertion(ReplacementFragment& fragment, const Position& insertionPos) -{ - Node* topNode = fragment.firstChild(); - - // Handling this case is more complicated (see handleStyleSpans) and doesn't receive the optimization. - if (isMailPasteAsQuotationNode(topNode)) - return false; - - // Either there are no style spans in the fragment or a WebKit client has added content to the fragment - // before inserting it. Look for and handle style spans after insertion. - if (!isStyleSpan(topNode)) - return false; - - Node* sourceDocumentStyleSpan = topNode; - RefPtr<Node> copiedRangeStyleSpan = sourceDocumentStyleSpan->firstChild(); - - RefPtr<CSSMutableStyleDeclaration> styleAtInsertionPos = rangeCompliantEquivalent(insertionPos).computedStyle()->copyInheritableProperties(); - String styleText = styleAtInsertionPos->cssText(); - - if (styleText == static_cast<Element*>(sourceDocumentStyleSpan)->getAttribute(styleAttr)) { - fragment.removeNodePreservingChildren(sourceDocumentStyleSpan); - if (!isStyleSpan(copiedRangeStyleSpan.get())) - return true; - } - - if (isStyleSpan(copiedRangeStyleSpan.get()) && styleText == static_cast<Element*>(copiedRangeStyleSpan.get())->getAttribute(styleAttr)) { - fragment.removeNodePreservingChildren(copiedRangeStyleSpan.get()); - return true; - } - - return false; -} - -// At copy time, WebKit wraps copied content in a span that contains the source document's -// default styles. If the copied Range inherits any other styles from its ancestors, we put -// those styles on a second span. -// This function removes redundant styles from those spans, and removes the spans if all their -// styles are redundant. -// We should remove the Apple-style-span class when we're done, see <rdar://problem/5685600>. -// We should remove styles from spans that are overridden by all of their children, either here -// or at copy time. -void ReplaceSelectionCommand::handleStyleSpans() -{ - Node* sourceDocumentStyleSpan = 0; - Node* copiedRangeStyleSpan = 0; - // The style span that contains the source document's default style should be at - // the top of the fragment, but Mail sometimes adds a wrapper (for Paste As Quotation), - // so search for the top level style span instead of assuming it's at the top. - for (Node* node = m_firstNodeInserted.get(); node; node = node->traverseNextNode()) { - if (isStyleSpan(node)) { - sourceDocumentStyleSpan = node; - // If the copied Range's common ancestor had user applied inheritable styles - // on it, they'll be on a second style span, just below the one that holds the - // document defaults. - if (isStyleSpan(node->firstChild())) - copiedRangeStyleSpan = node->firstChild(); - break; - } - } - - // There might not be any style spans if we're pasting from another application or if - // we are here because of a document.execCommand("InsertHTML", ...) call. - if (!sourceDocumentStyleSpan) - return; - - RefPtr<CSSMutableStyleDeclaration> sourceDocumentStyle = static_cast<HTMLElement*>(sourceDocumentStyleSpan)->getInlineStyleDecl()->copy(); - Node* context = sourceDocumentStyleSpan->parentNode(); - - // If Mail wraps the fragment with a Paste as Quotation blockquote, styles from that element are - // allowed to override those from the source document, see <rdar://problem/4930986>. - if (isMailPasteAsQuotationNode(context)) { - RefPtr<CSSMutableStyleDeclaration> blockquoteStyle = computedStyle(context)->copyInheritableProperties(); - RefPtr<CSSMutableStyleDeclaration> parentStyle = computedStyle(context->parentNode())->copyInheritableProperties(); - parentStyle->diff(blockquoteStyle.get()); - - DeprecatedValueListConstIterator<CSSProperty> end; - for (DeprecatedValueListConstIterator<CSSProperty> it = blockquoteStyle->valuesIterator(); it != end; ++it) { - const CSSProperty& property = *it; - sourceDocumentStyle->removeProperty(property.id()); - } - - context = context->parentNode(); - } - - RefPtr<CSSMutableStyleDeclaration> contextStyle = computedStyle(context)->copyInheritableProperties(); - contextStyle->diff(sourceDocumentStyle.get()); - - // Remove block properties in the span's style. This prevents properties that probably have no effect - // currently from affecting blocks later if the style is cloned for a new block element during a future - // editing operation. - // FIXME: They *can* have an effect currently if blocks beneath the style span aren't individually marked - // with block styles by the editing engine used to style them. WebKit doesn't do this, but others might. - sourceDocumentStyle->removeBlockProperties(); - - // The styles on sourceDocumentStyleSpan are all redundant, and there is no copiedRangeStyleSpan - // to consider. We're finished. - if (sourceDocumentStyle->length() == 0 && !copiedRangeStyleSpan) { - removeNodePreservingChildren(sourceDocumentStyleSpan); - return; - } - - // There are non-redundant styles on sourceDocumentStyleSpan, but there is no - // copiedRangeStyleSpan. Clear the redundant styles from sourceDocumentStyleSpan - // and return. - if (sourceDocumentStyle->length() > 0 && !copiedRangeStyleSpan) { - setNodeAttribute(static_cast<Element*>(sourceDocumentStyleSpan), styleAttr, sourceDocumentStyle->cssText()); - return; - } - - RefPtr<CSSMutableStyleDeclaration> copiedRangeStyle = static_cast<HTMLElement*>(copiedRangeStyleSpan)->getInlineStyleDecl()->copy(); - - // We're going to put sourceDocumentStyleSpan's non-redundant styles onto copiedRangeStyleSpan, - // as long as they aren't overridden by ones on copiedRangeStyleSpan. - sourceDocumentStyle->merge(copiedRangeStyle.get(), true); - copiedRangeStyle = sourceDocumentStyle; - - removeNodePreservingChildren(sourceDocumentStyleSpan); - - // Remove redundant styles. - context = copiedRangeStyleSpan->parentNode(); - contextStyle = computedStyle(context)->copyInheritableProperties(); - contextStyle->diff(copiedRangeStyle.get()); - - // See the comments above about removing block properties. - copiedRangeStyle->removeBlockProperties(); - - // All the styles on copiedRangeStyleSpan are redundant, remove it. - if (copiedRangeStyle->length() == 0) { - removeNodePreservingChildren(copiedRangeStyleSpan); - return; - } - - // Clear the redundant styles from the span's style attribute. - // FIXME: If font-family:-webkit-monospace is non-redundant, then the font-size should stay, even if it - // appears redundant. - setNodeAttribute(static_cast<Element*>(copiedRangeStyleSpan), styleAttr, copiedRangeStyle->cssText()); -} - -void ReplaceSelectionCommand::doApply() -{ - Selection selection = endingSelection(); - ASSERT(selection.isCaretOrRange()); - ASSERT(selection.start().node()); - if (selection.isNone() || !selection.start().node()) - return; - - bool selectionIsPlainText = !selection.isContentRichlyEditable(); - - Element* currentRoot = selection.rootEditableElement(); - ReplacementFragment fragment(document(), m_documentFragment.get(), m_matchStyle, selection); - - if (m_matchStyle) - m_insertionStyle = styleAtPosition(selection.start()); - - VisiblePosition visibleStart = selection.visibleStart(); - VisiblePosition visibleEnd = selection.visibleEnd(); - - bool selectionEndWasEndOfParagraph = isEndOfParagraph(visibleEnd); - bool selectionStartWasStartOfParagraph = isStartOfParagraph(visibleStart); - - Node* startBlock = enclosingBlock(visibleStart.deepEquivalent().node()); - - if (selectionStartWasStartOfParagraph && selectionEndWasEndOfParagraph || - startBlock == currentRoot || - startBlock && startBlock->renderer() && startBlock->renderer()->isListItem() || - selectionIsPlainText) - m_preventNesting = false; - - Position insertionPos = selection.start(); - - if (selection.isRange()) { - // When the end of the selection being pasted into is at the end of a paragraph, and that selection - // spans multiple blocks, not merging may leave an empty line. - // When the start of the selection being pasted into is at the start of a block, not merging - // will leave hanging block(s). - bool mergeBlocksAfterDelete = isEndOfParagraph(visibleEnd) || isStartOfBlock(visibleStart); - // FIXME: We should only expand to include fully selected special elements if we are copying a - // selection and pasting it on top of itself. - deleteSelection(false, mergeBlocksAfterDelete, true, false); - visibleStart = endingSelection().visibleStart(); - if (fragment.hasInterchangeNewlineAtStart()) { - if (isEndOfParagraph(visibleStart) && !isStartOfParagraph(visibleStart)) { - if (!isEndOfDocument(visibleStart)) - setEndingSelection(visibleStart.next()); - } else - insertParagraphSeparator(); - } - insertionPos = endingSelection().start(); - } else { - ASSERT(selection.isCaret()); - if (fragment.hasInterchangeNewlineAtStart()) { - VisiblePosition next = visibleStart.next(true); - if (isEndOfParagraph(visibleStart) && !isStartOfParagraph(visibleStart) && next.isNotNull()) - setEndingSelection(next); - else - insertParagraphSeparator(); - } - // We split the current paragraph in two to avoid nesting the blocks from the fragment inside the current block. - // For example paste <div>foo</div><div>bar</div><div>baz</div> into <div>x^x</div>, where ^ is the caret. - // As long as the div styles are the same, visually you'd expect: <div>xbar</div><div>bar</div><div>bazx</div>, - // not <div>xbar<div>bar</div><div>bazx</div></div> - if (m_preventNesting && !isEndOfParagraph(visibleStart) && !isStartOfParagraph(visibleStart)) { - insertParagraphSeparator(); - setEndingSelection(endingSelection().visibleStart().previous()); - } - insertionPos = endingSelection().start(); - } - - // Inserting content could cause whitespace to collapse, e.g. inserting <div>foo</div> into hello^ world. - prepareWhitespaceAtPositionForSplit(insertionPos); - - // 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; - VisiblePosition originalVisPosBeforeEndBR; - if (endBR) - originalVisPosBeforeEndBR = VisiblePosition(endBR, 0, DOWNSTREAM).previous(); - - startBlock = enclosingBlock(insertionPos.node()); - - // Adjust insertionPos to prevent nesting. - if (m_preventNesting && startBlock) { - ASSERT(startBlock != currentRoot); - VisiblePosition visibleInsertionPos(insertionPos); - if (isEndOfBlock(visibleInsertionPos) && !(isStartOfBlock(visibleInsertionPos) && fragment.hasInterchangeNewlineAtEnd())) - insertionPos = positionAfterNode(startBlock); - else if (isStartOfBlock(visibleInsertionPos)) - insertionPos = positionBeforeNode(startBlock); - } - - // Paste into run of tabs splits the tab span. - insertionPos = positionOutsideTabSpan(insertionPos); - - // Paste at start or end of link goes outside of link. - insertionPos = positionAvoidingSpecialElementBoundary(insertionPos); - - // FIXME: Can this wait until after the operation has been performed? There doesn't seem to be - // any work performed after this that queries or uses the typing style. - if (Frame* frame = document()->frame()) - frame->clearTypingStyle(); - - bool handledStyleSpans = handleStyleSpansBeforeInsertion(fragment, insertionPos); - - // We're finished if there is nothing to add. - if (fragment.isEmpty() || !fragment.firstChild()) - return; - - // 1) Insert the content. - // 2) Remove redundant styles and style tags, this inner <b> for example: <b>foo <b>bar</b> baz</b>. - // 3) Merge the start of the added content with the content before the position being pasted into. - // 4) Do one of the following: a) expand the last br if the fragment ends with one and it collapsed, - // b) merge the last paragraph of the incoming fragment with the paragraph that contained the - // end of the selection that was pasted into, or c) handle an interchange newline at the end of the - // incoming fragment. - // 5) Add spaces for smart replace. - // 6) Select the replacement if requested, and match style if requested. - - VisiblePosition startOfInsertedContent, endOfInsertedContent; - - RefPtr<Node> refNode = fragment.firstChild(); - RefPtr<Node> node = refNode->nextSibling(); - - fragment.removeNode(refNode); - insertNodeAtAndUpdateNodesInserted(refNode.get(), insertionPos); - - while (node) { - Node* next = node->nextSibling(); - fragment.removeNode(node); - insertNodeAfterAndUpdateNodesInserted(node.get(), refNode.get()); - refNode = node; - node = next; - } - - removeUnrenderedTextNodesAtEnds(); - - negateStyleRulesThatAffectAppearance(); - - if (!handledStyleSpans) - handleStyleSpans(); - - // Mutation events (bug 20161) may have already removed the inserted content - if (!m_firstNodeInserted || !m_firstNodeInserted->inDocument()) - return; - - endOfInsertedContent = positionAtEndOfInsertedContent(); - startOfInsertedContent = positionAtStartOfInsertedContent(); - - // 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.offset() < startBlock->nodeIndex() && !isStartOfParagraph(startOfInsertedContent)) - insertNodeAt(createBreakElement(document()).get(), startOfInsertedContent.deepEquivalent()); - - Position lastPositionToSelect; - - bool interchangeNewlineAtEnd = fragment.hasInterchangeNewlineAtEnd(); - - if (shouldRemoveEndBR(endBR, originalVisPosBeforeEndBR)) - removeNodeAndPruneAncestors(endBR); - - if (shouldMergeStart(selectionStartWasStartOfParagraph, fragment.hasInterchangeNewlineAtStart())) { - // Bail to avoid infinite recursion. - if (m_movingParagraph) { - // setting display:inline does not work for td elements in quirks mode - ASSERT(m_firstNodeInserted->hasTagName(tdTag)); - return; - } - VisiblePosition destination = startOfInsertedContent.previous(); - VisiblePosition startOfParagraphToMove = startOfInsertedContent; - - // Merging the the first paragraph of inserted content with the content that came - // before the selection that was pasted into would also move content after - // the selection that was pasted into if: only one paragraph was being pasted, - // and it was not wrapped in a block, the selection that was pasted into ended - // at the end of a block and the next paragraph didn't start at the start of a block. - // Insert a line break just after the inserted content to separate it from what - // comes after and prevent that from happening. - VisiblePosition endOfInsertedContent = positionAtEndOfInsertedContent(); - if (startOfParagraph(endOfInsertedContent) == startOfParagraphToMove) - insertNodeAt(createBreakElement(document()).get(), endOfInsertedContent.deepEquivalent()); - - // 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(); - if (!m_lastLeafInserted->inDocument()) - m_lastLeafInserted = endingSelection().visibleEnd().deepEquivalent().upstream().node(); - } - - endOfInsertedContent = positionAtEndOfInsertedContent(); - startOfInsertedContent = positionAtStartOfInsertedContent(); - - if (interchangeNewlineAtEnd) { - VisiblePosition next = endOfInsertedContent.next(true); - - if (selectionEndWasEndOfParagraph || !isEndOfParagraph(endOfInsertedContent) || next.isNull()) { - if (!isStartOfParagraph(endOfInsertedContent)) { - setEndingSelection(endOfInsertedContent); - // Use a default paragraph element (a plain div) for the empty paragraph, using the last paragraph - // block's style seems to annoy users. - insertParagraphSeparator(true); - - // Select up to the paragraph separator that was added. - lastPositionToSelect = endingSelection().visibleStart().deepEquivalent(); - updateNodesInserted(lastPositionToSelect.node()); - } - } else { - // Select up to the beginning of the next paragraph. - lastPositionToSelect = next.deepEquivalent().downstream(); - } - - } else if (shouldMergeEnd(selectionEndWasEndOfParagraph)) { - // Bail to avoid infinite recursion. - if (m_movingParagraph) { - ASSERT_NOT_REACHED(); - return; - } - // Merging two paragraphs will destroy the moved one's block styles. Always move forward to preserve - // the block style of the paragraph already in the document, unless the paragraph to move would include the - // what was the start of the selection that was pasted into. - bool mergeForward = !inSameParagraph(startOfInsertedContent, endOfInsertedContent) || isStartOfParagraph(startOfInsertedContent); - - VisiblePosition destination = mergeForward ? endOfInsertedContent.next() : endOfInsertedContent; - VisiblePosition startOfParagraphToMove = mergeForward ? startOfParagraph(endOfInsertedContent) : endOfInsertedContent.next(); - - moveParagraph(startOfParagraphToMove, endOfParagraph(startOfParagraphToMove), destination); - // Merging forward will remove m_lastLeafInserted from the document. - // 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. - if (mergeForward) { - m_lastLeafInserted = destination.previous().deepEquivalent().node(); - if (!m_firstNodeInserted->inDocument()) - m_firstNodeInserted = endingSelection().visibleStart().deepEquivalent().node(); - } - } - - handlePasteAsQuotationNode(); - - endOfInsertedContent = positionAtEndOfInsertedContent(); - startOfInsertedContent = positionAtStartOfInsertedContent(); - - // Add spaces for smart replace. - if (m_smartReplace && currentRoot) { - // Disable smart replace for password fields. - Node* start = currentRoot->shadowAncestorNode(); - if (start->hasTagName(inputTag) && static_cast<HTMLInputElement*>(start)->inputType() == HTMLInputElement::PASSWORD) - m_smartReplace = false; - } - if (m_smartReplace) { - bool needsTrailingSpace = !isEndOfParagraph(endOfInsertedContent) && - !isCharacterSmartReplaceExempt(endOfInsertedContent.characterAfter(), false); - if (needsTrailingSpace) { - RenderObject* renderer = m_lastLeafInserted->renderer(); - bool collapseWhiteSpace = !renderer || renderer->style()->collapseWhiteSpace(); - Node* endNode = positionAtEndOfInsertedContent().deepEquivalent().upstream().node(); - if (endNode->isTextNode()) { - Text* text = static_cast<Text*>(endNode); - insertTextIntoNode(text, text->length(), collapseWhiteSpace ? nonBreakingSpaceString() : " "); - } else { - RefPtr<Node> node = document()->createEditingTextNode(collapseWhiteSpace ? nonBreakingSpaceString() : " "); - insertNodeAfterAndUpdateNodesInserted(node.get(), endNode); - } - } - - bool needsLeadingSpace = !isStartOfParagraph(startOfInsertedContent) && - !isCharacterSmartReplaceExempt(startOfInsertedContent.previous().characterAfter(), true); - if (needsLeadingSpace) { - RenderObject* renderer = m_lastLeafInserted->renderer(); - bool collapseWhiteSpace = !renderer || renderer->style()->collapseWhiteSpace(); - Node* startNode = positionAtStartOfInsertedContent().deepEquivalent().downstream().node(); - if (startNode->isTextNode()) { - Text* text = static_cast<Text*>(startNode); - insertTextIntoNode(text, 0, collapseWhiteSpace ? nonBreakingSpaceString() : " "); - } else { - RefPtr<Node> node = document()->createEditingTextNode(collapseWhiteSpace ? nonBreakingSpaceString() : " "); - // Don't updateNodesInserted. Doing so would set m_lastLeafInserted to be the node containing the - // leading space, but m_lastLeafInserted is supposed to mark the end of pasted content. - insertNodeBefore(node.get(), startNode); - // FIXME: Use positions to track the start/end of inserted content. - m_firstNodeInserted = node; - } - } - } - - completeHTMLReplacement(lastPositionToSelect); -} - -bool ReplaceSelectionCommand::shouldRemoveEndBR(Node* endBR, const VisiblePosition& originalVisPosBeforeEndBR) -{ - if (!endBR || !endBR->inDocument()) - return false; - - VisiblePosition visiblePos(Position(endBR, 0)); - - // Don't remove the br if nothing was inserted. - if (visiblePos.previous() == originalVisPosBeforeEndBR) - return false; - - // Remove the br if it is collapsed away and so is unnecessary. - if (!document()->inStrictMode() && isEndOfBlock(visiblePos) && !isStartOfParagraph(visiblePos)) - return true; - - // A br that was originally holding a line open should be displaced by inserted content or turned into a line break. - // A br that was originally acting as a line break should still be acting as a line break, not as a placeholder. - return isStartOfParagraph(visiblePos) && isEndOfParagraph(visiblePos); -} - -void ReplaceSelectionCommand::completeHTMLReplacement(const Position &lastPositionToSelect) -{ - Position start; - Position end; - - // FIXME: This should never not be the case. - if (m_firstNodeInserted && m_firstNodeInserted->inDocument() && m_lastLeafInserted && m_lastLeafInserted->inDocument()) { - - start = positionAtStartOfInsertedContent().deepEquivalent(); - end = positionAtEndOfInsertedContent().deepEquivalent(); - - // FIXME (11475): Remove this and require that the creator of the fragment to use nbsps. - rebalanceWhitespaceAt(start); - rebalanceWhitespaceAt(end); - - if (m_matchStyle) { - ASSERT(m_insertionStyle); - applyStyle(m_insertionStyle.get(), start, end); - } - - if (lastPositionToSelect.isNotNull()) - end = lastPositionToSelect; - } else if (lastPositionToSelect.isNotNull()) - start = end = lastPositionToSelect; - else - return; - - if (m_selectReplacement) - setEndingSelection(Selection(start, end, SEL_DEFAULT_AFFINITY)); - else - setEndingSelection(Selection(end, SEL_DEFAULT_AFFINITY)); -} - -EditAction ReplaceSelectionCommand::editingAction() const -{ - return m_editAction; -} - -void ReplaceSelectionCommand::insertNodeAfterAndUpdateNodesInserted(Node *insertChild, Node *refChild) -{ - insertNodeAfter(insertChild, refChild); - updateNodesInserted(insertChild); -} - -void ReplaceSelectionCommand::insertNodeAtAndUpdateNodesInserted(Node *insertChild, const Position& p) -{ - insertNodeAt(insertChild, p); - updateNodesInserted(insertChild); -} - -void ReplaceSelectionCommand::insertNodeBeforeAndUpdateNodesInserted(Node *insertChild, Node *refChild) -{ - insertNodeBefore(insertChild, refChild); - updateNodesInserted(insertChild); -} - -void ReplaceSelectionCommand::updateNodesInserted(Node *node) -{ - if (!node) - return; - - if (!m_firstNodeInserted) - m_firstNodeInserted = node; - - if (node == m_lastLeafInserted) - return; - - m_lastLeafInserted = node->lastDescendant(); -} - -} // namespace WebCore diff --git a/WebCore/editing/ReplaceSelectionCommand.h b/WebCore/editing/ReplaceSelectionCommand.h deleted file mode 100644 index dc669a3..0000000 --- a/WebCore/editing/ReplaceSelectionCommand.h +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright (C) 2005, 2006, 2008 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 ReplaceSelectionCommand_h -#define ReplaceSelectionCommand_h - -#include "CompositeEditCommand.h" - -namespace WebCore { - -class DocumentFragment; - -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) - { - return adoptRef(new ReplaceSelectionCommand(document, fragment, selectReplacement, smartReplace, matchStyle, preventNesting, movingParagraph, action)); - } - -private: - ReplaceSelectionCommand(Document*, PassRefPtr<DocumentFragment>, - bool selectReplacement, bool smartReplace, bool matchStyle, bool preventNesting, bool movingParagraph, EditAction); - - virtual void doApply(); - virtual EditAction editingAction() const; - - void completeHTMLReplacement(const Position& lastPositionToSelect); - - void insertNodeAfterAndUpdateNodesInserted(Node* insertChild, Node* refChild); - void insertNodeAtAndUpdateNodesInserted(Node*, const Position&); - void insertNodeBeforeAndUpdateNodesInserted(Node* insertChild, Node* refChild); - - void updateNodesInserted(Node*); - bool shouldRemoveEndBR(Node*, const VisiblePosition&); - - bool shouldMergeStart(bool, bool); - bool shouldMergeEnd(bool); - bool shouldMerge(const VisiblePosition&, const VisiblePosition&); - - void removeUnrenderedTextNodesAtEnds(); - - void negateStyleRulesThatAffectAppearance(); - void handleStyleSpans(); - void handlePasteAsQuotationNode(); - - virtual void removeNodePreservingChildren(Node*); - virtual void removeNodeAndPruneAncestors(Node*); - - VisiblePosition positionAtStartOfInsertedContent(); - VisiblePosition positionAtEndOfInsertedContent(); - - RefPtr<Node> m_firstNodeInserted; - RefPtr<Node> m_lastLeafInserted; - RefPtr<CSSMutableStyleDeclaration> m_insertionStyle; - bool m_selectReplacement; - bool m_smartReplace; - bool m_matchStyle; - RefPtr<DocumentFragment> m_documentFragment; - bool m_preventNesting; - bool m_movingParagraph; - EditAction m_editAction; -}; - -} // namespace WebCore - -#endif // ReplaceSelectionCommand_h diff --git a/WebCore/editing/Selection.cpp b/WebCore/editing/Selection.cpp deleted file mode 100644 index 9b17567..0000000 --- a/WebCore/editing/Selection.cpp +++ /dev/null @@ -1,597 +0,0 @@ -/* - * Copyright (C) 2004, 2005, 2006 Apple Computer, 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 "Selection.h" - -#include "CString.h" -#include "Document.h" -#include "Element.h" -#include "htmlediting.h" -#include "VisiblePosition.h" -#include "visible_units.h" -#include "Range.h" -#include <wtf/Assertions.h> -#include <stdio.h> - -namespace WebCore { - -Selection::Selection() - : m_affinity(DOWNSTREAM) - , m_granularity(CharacterGranularity) - , m_state(NONE) - , m_baseIsFirst(true) -{ -} - -Selection::Selection(const Position& pos, EAffinity affinity) - : m_base(pos) - , m_extent(pos) - , m_affinity(affinity) - , m_granularity(CharacterGranularity) -{ - validate(); -} - -Selection::Selection(const Position& base, const Position& extent, EAffinity affinity) - : m_base(base) - , m_extent(extent) - , m_affinity(affinity) - , m_granularity(CharacterGranularity) -{ - validate(); -} - -Selection::Selection(const VisiblePosition& pos) - : m_base(pos.deepEquivalent()) - , m_extent(pos.deepEquivalent()) - , m_affinity(pos.affinity()) - , m_granularity(CharacterGranularity) -{ - validate(); -} - -Selection::Selection(const VisiblePosition& base, const VisiblePosition& extent) - : m_base(base.deepEquivalent()) - , m_extent(extent.deepEquivalent()) - , m_affinity(base.affinity()) - , m_granularity(CharacterGranularity) -{ - validate(); -} - -Selection::Selection(const Range* range, EAffinity affinity) - : m_base(range->startPosition()) - , m_extent(range->endPosition()) - , m_affinity(affinity) - , m_granularity(CharacterGranularity) -{ - validate(); -} - -Selection Selection::selectionFromContentsOfNode(Node* node) -{ - return Selection(Position(node, 0), Position(node, maxDeepOffset(node)), DOWNSTREAM); -} - -void Selection::setBase(const Position& position) -{ - m_base = position; - validate(); -} - -void Selection::setBase(const VisiblePosition& visiblePosition) -{ - m_base = visiblePosition.deepEquivalent(); - validate(); -} - -void Selection::setExtent(const Position& position) -{ - m_extent = position; - validate(); -} - -void Selection::setExtent(const VisiblePosition& visiblePosition) -{ - m_extent = visiblePosition.deepEquivalent(); - validate(); -} - -PassRefPtr<Range> Selection::toRange() const -{ - if (isNone()) - return 0; - - // Make sure we have an updated layout since this function is called - // 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(); - - // Check again, because updating layout can clear the selection. - if (isNone()) - return 0; - - Position s, e; - if (isCaret()) { - // If the selection is a caret, move the range start upstream. This helps us match - // the conventions of text editors tested, which make style determinations based - // on the character before the caret, if any. - s = rangeCompliantEquivalent(m_start.upstream()); - e = s; - } else { - // If the selection is a range, select the minimum range that encompasses the selection. - // Again, this is to match the conventions of text editors tested, which make style - // determinations based on the first character of the selection. - // For instance, this operation helps to make sure that the "X" selected below is the - // only thing selected. The range should not be allowed to "leak" out to the end of the - // previous text node, or to the beginning of the next text node, each of which has a - // different style. - // - // On a treasure map, <b>X</b> marks the spot. - // ^ selected - // - ASSERT(isRange()); - s = m_start.downstream(); - e = m_end.upstream(); - if (Range::compareBoundaryPoints(s.node(), s.offset(), e.node(), e.offset()) > 0) { - // Make sure the start is before the end. - // The end can wind up before the start if collapsed whitespace is the only thing selected. - Position tmp = s; - s = e; - e = tmp; - } - s = rangeCompliantEquivalent(s); - e = rangeCompliantEquivalent(e); - } - - ExceptionCode ec = 0; - RefPtr<Range> result(Range::create(s.node()->document())); - result->setStart(s.node(), s.offset(), ec); - if (ec) { - LOG_ERROR("Exception setting Range start from Selection: %d", ec); - return 0; - } - result->setEnd(e.node(), e.offset(), ec); - if (ec) { - LOG_ERROR("Exception setting Range end from Selection: %d", ec); - return 0; - } - return result.release(); -} - -bool Selection::expandUsingGranularity(TextGranularity granularity) -{ - if (isNone()) - return false; - - m_granularity = granularity; - validate(); - return true; -} - -void Selection::validate() -{ - // Move the selection to rendered positions, if possible. - bool baseAndExtentEqual = m_base == m_extent; - if (m_base.isNotNull()) { - m_base = VisiblePosition(m_base, m_affinity).deepEquivalent(); - if (baseAndExtentEqual) - m_extent = m_base; - } - if (m_extent.isNotNull() && !baseAndExtentEqual) - m_extent = VisiblePosition(m_extent, m_affinity).deepEquivalent(); - - // Make sure we do not have a dangling base or extent. - if (m_base.isNull() && m_extent.isNull()) - m_baseIsFirst = true; - else if (m_base.isNull()) { - m_base = m_extent; - m_baseIsFirst = true; - } else if (m_extent.isNull()) { - m_extent = m_base; - m_baseIsFirst = true; - } else { - m_baseIsFirst = comparePositions(m_base, m_extent) <= 0; - } - - if (m_baseIsFirst) { - m_start = m_base; - m_end = m_extent; - } else { - m_start = m_extent; - m_end = m_base; - } - - // Expand the selection if requested. - switch (m_granularity) { - case CharacterGranularity: - // Don't do any expansion. - break; - case WordGranularity: { - // General case: Select the word the caret is positioned inside of, or at the start of (RightWordIfOnBoundary). - // Edge case: If the caret is after the last word in a soft-wrapped line or the last word in - // the document, select that last word (LeftWordIfOnBoundary). - // Edge case: If the caret is after the last word in a paragraph, select from the the end of the - // last word to the line break (also RightWordIfOnBoundary); - VisiblePosition start = VisiblePosition(m_start, m_affinity); - VisiblePosition originalEnd(m_end, m_affinity); - EWordSide side = RightWordIfOnBoundary; - if (isEndOfDocument(start) || (isEndOfLine(start) && !isStartOfLine(start) && !isEndOfParagraph(start))) - side = LeftWordIfOnBoundary; - m_start = startOfWord(start, side).deepEquivalent(); - side = RightWordIfOnBoundary; - if (isEndOfDocument(originalEnd) || (isEndOfLine(originalEnd) && !isStartOfLine(originalEnd) && !isEndOfParagraph(originalEnd))) - side = LeftWordIfOnBoundary; - - VisiblePosition wordEnd(endOfWord(originalEnd, side)); - VisiblePosition end(wordEnd); - - if (isEndOfParagraph(originalEnd)) { - // 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(); - - if (Node* table = isFirstPositionAfterTable(end)) { - // The paragraph break after the last paragraph in the last cell of a block table ends - // at the start of the paragraph after the table. - if (isBlock(table)) - end = end.next(true); - else - end = wordEnd; - } - - if (end.isNull()) - end = wordEnd; - - } - - m_end = end.deepEquivalent(); - break; - } - case SentenceGranularity: { - m_start = startOfSentence(VisiblePosition(m_start, m_affinity)).deepEquivalent(); - m_end = endOfSentence(VisiblePosition(m_end, m_affinity)).deepEquivalent(); - break; - } - case LineGranularity: { - m_start = startOfLine(VisiblePosition(m_start, m_affinity)).deepEquivalent(); - VisiblePosition end = endOfLine(VisiblePosition(m_end, m_affinity)); - // If the end of this line is at the end of a paragraph, include the space - // after the end of the line in the selection. - if (isEndOfParagraph(end)) { - VisiblePosition next = end.next(); - if (next.isNotNull()) - end = next; - } - m_end = end.deepEquivalent(); - break; - } - case LineBoundary: - m_start = startOfLine(VisiblePosition(m_start, m_affinity)).deepEquivalent(); - m_end = endOfLine(VisiblePosition(m_end, m_affinity)).deepEquivalent(); - break; - case ParagraphGranularity: { - VisiblePosition pos(m_start, m_affinity); - if (isStartOfLine(pos) && isEndOfDocument(pos)) - pos = pos.previous(); - m_start = startOfParagraph(pos).deepEquivalent(); - VisiblePosition visibleParagraphEnd = endOfParagraph(VisiblePosition(m_end, m_affinity)); - - // Include the "paragraph break" (the space from the end of this paragraph to the start - // of the next one) in the selection. - VisiblePosition end(visibleParagraphEnd.next()); - - if (Node* table = isFirstPositionAfterTable(end)) { - // The paragraph break after the last paragraph in the last cell of a block table ends - // at the start of the paragraph after the table, not at the position just after the table. - if (isBlock(table)) - end = end.next(true); - // There is no parargraph break after the last paragraph in the last cell of an inline table. - else - end = visibleParagraphEnd; - } - - if (end.isNull()) - end = visibleParagraphEnd; - - m_end = end.deepEquivalent(); - break; - } - case DocumentBoundary: - m_start = startOfDocument(VisiblePosition(m_start, m_affinity)).deepEquivalent(); - m_end = endOfDocument(VisiblePosition(m_end, m_affinity)).deepEquivalent(); - break; - case ParagraphBoundary: - m_start = startOfParagraph(VisiblePosition(m_start, m_affinity)).deepEquivalent(); - m_end = endOfParagraph(VisiblePosition(m_end, m_affinity)).deepEquivalent(); - break; - case SentenceBoundary: - m_start = startOfSentence(VisiblePosition(m_start, m_affinity)).deepEquivalent(); - m_end = endOfSentence(VisiblePosition(m_end, m_affinity)).deepEquivalent(); - break; - } - - // Make sure we do not have a dangling start or end. - if (m_start.isNull()) - m_start = m_end; - if (m_end.isNull()) - m_end = m_start; - - adjustForEditableContent(); - - // adjust the state - if (m_start.isNull()) { - ASSERT(m_end.isNull()); - m_state = NONE; - - // enforce downstream affinity if not caret, as affinity only - // makes sense for caret - m_affinity = DOWNSTREAM; - } else if (m_start == m_end || m_start.upstream() == m_end.upstream()) { - m_state = CARET; - } else { - m_state = RANGE; - - // enforce downstream affinity if not caret, as affinity only - // makes sense for caret - m_affinity = DOWNSTREAM; - - // "Constrain" the selection to be the smallest equivalent range of nodes. - // This is a somewhat arbitrary choice, but experience shows that it is - // useful to make to make the selection "canonical" (if only for - // purposes of comparing selections). This is an ideal point of the code - // to do this operation, since all selection changes that result in a RANGE - // come through here before anyone uses it. - // FIXME: Canonicalizing is good, but haven't we already done it (when we - // set these two positions to VisiblePosition deepEquivalent()s above)? - m_start = m_start.downstream(); - m_end = m_end.upstream(); - } -} - -// FIXME: This function breaks the invariant of this class. -// But because we use Selection to store values in editing commands for use when -// undoing the command, we need to be able to create a selection that while currently -// invalid, will be valid once the changes are undone. This is a design problem. -// To fix it we either need to change the invariants of Selection or create a new -// class for editing to use that can manipulate selections that are not currently valid. -void Selection::setWithoutValidation(const Position& base, const Position& extent) -{ - ASSERT(!base.isNull()); - ASSERT(!extent.isNull()); - ASSERT(base != extent); - ASSERT(m_affinity == DOWNSTREAM); - ASSERT(m_granularity == CharacterGranularity); - m_base = base; - m_extent = extent; - m_baseIsFirst = comparePositions(base, extent) <= 0; - if (m_baseIsFirst) { - m_start = base; - m_end = extent; - } else { - m_start = extent; - m_end = base; - } - m_state = RANGE; -} - -void Selection::adjustForEditableContent() -{ - if (m_base.isNull() || m_start.isNull() || m_end.isNull()) - return; - - Node* baseRoot = highestEditableRoot(m_base); - Node* startRoot = highestEditableRoot(m_start); - Node* endRoot = highestEditableRoot(m_end); - - Node* baseEditableAncestor = lowestEditableAncestor(m_base.node()); - - // The base, start and end are all in the same region. No adjustment necessary. - if (baseRoot == startRoot && baseRoot == endRoot) - return; - - // The selection is based in editable content. - if (baseRoot) { - // If the start is outside the base's editable root, cap it at the start of that root. - // If the start is in non-editable content that is inside the base's editable root, put it - // at the first editable position after start inside the base's editable root. - if (startRoot != baseRoot) { - VisiblePosition first = firstEditablePositionAfterPositionInRoot(m_start, baseRoot); - m_start = first.deepEquivalent(); - if (m_start.isNull()) { - ASSERT_NOT_REACHED(); - m_start = m_end; - } - } - // If the end is outside the base's editable root, cap it at the end of that root. - // If the end is in non-editable content that is inside the base's root, put it - // at the last editable position before the end inside the base's root. - if (endRoot != baseRoot) { - VisiblePosition last = lastEditablePositionBeforePositionInRoot(m_end, baseRoot); - m_end = last.deepEquivalent(); - if (m_end.isNull()) { - ASSERT_NOT_REACHED(); - m_end = m_start; - } - } - // The selection is based in non-editable content. - } else { - // FIXME: Non-editable pieces inside editable content should be atomic, in the same way that editable - // pieces in non-editable content are atomic. - - // 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()); - if (endRoot || endEditableAncestor != baseEditableAncestor) { - - Position p = previousVisuallyDistinctCandidate(m_end); - Node* shadowAncestor = endRoot ? endRoot->shadowAncestorNode() : 0; - if (p.isNull() && endRoot && (shadowAncestor != endRoot)) - p = Position(shadowAncestor, maxDeepOffset(shadowAncestor)); - while (p.isNotNull() && !(lowestEditableAncestor(p.node()) == baseEditableAncestor && !isEditablePosition(p))) { - Node* root = editableRootForPosition(p); - shadowAncestor = root ? root->shadowAncestorNode() : 0; - p = isAtomicNode(p.node()) ? positionBeforeNode(p.node()) : previousVisuallyDistinctCandidate(p); - if (p.isNull() && (shadowAncestor != root)) - p = Position(shadowAncestor, maxDeepOffset(shadowAncestor)); - } - VisiblePosition previous(p); - - if (previous.isNull()) { - ASSERT_NOT_REACHED(); - m_base = Position(); - m_extent = Position(); - validate(); - return; - } - m_end = previous.deepEquivalent(); - } - - // 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()); - if (startRoot || startEditableAncestor != baseEditableAncestor) { - Position p = nextVisuallyDistinctCandidate(m_start); - Node* shadowAncestor = startRoot ? startRoot->shadowAncestorNode() : 0; - if (p.isNull() && startRoot && (shadowAncestor != startRoot)) - p = Position(shadowAncestor, 0); - while (p.isNotNull() && !(lowestEditableAncestor(p.node()) == baseEditableAncestor && !isEditablePosition(p))) { - Node* root = editableRootForPosition(p); - shadowAncestor = root ? root->shadowAncestorNode() : 0; - p = isAtomicNode(p.node()) ? positionAfterNode(p.node()) : nextVisuallyDistinctCandidate(p); - if (p.isNull() && (shadowAncestor != root)) - p = Position(shadowAncestor, 0); - } - VisiblePosition next(p); - - if (next.isNull()) { - ASSERT_NOT_REACHED(); - m_base = Position(); - m_extent = Position(); - validate(); - return; - } - m_start = next.deepEquivalent(); - } - } - - // Correct the extent if necessary. - if (baseEditableAncestor != lowestEditableAncestor(m_extent.node())) - m_extent = m_baseIsFirst ? m_end : m_start; -} - -bool Selection::isContentEditable() const -{ - return isEditablePosition(start()); -} - -bool Selection::isContentRichlyEditable() const -{ - return isRichlyEditablePosition(start()); -} - -Element* Selection::rootEditableElement() const -{ - return editableRootForPosition(start()); -} - -Node* Selection::shadowTreeRootNode() const -{ - return start().node() ? start().node()->shadowTreeRootNode() : 0; -} - -void Selection::debugPosition() const -{ - if (!m_start.node()) - return; - - fprintf(stderr, "Selection =================\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.offset()); - } else { - Position pos = m_start; - fprintf(stderr, "start: %s %p:%d\n", pos.node()->nodeName().utf8().data(), pos.node(), pos.offset()); - fprintf(stderr, "-----------------------------------\n"); - pos = m_end; - fprintf(stderr, "end: %s %p:%d\n", pos.node()->nodeName().utf8().data(), pos.node(), pos.offset()); - fprintf(stderr, "-----------------------------------\n"); - } - - fprintf(stderr, "================================\n"); -} - -#ifndef NDEBUG - -void Selection::formatForDebugger(char* buffer, unsigned length) const -{ - String result; - String s; - - if (isNone()) { - result = "<none>"; - } else { - const int FormatBufferSize = 1024; - char s[FormatBufferSize]; - result += "from "; - start().formatForDebugger(s, FormatBufferSize); - result += s; - result += " to "; - end().formatForDebugger(s, FormatBufferSize); - result += s; - } - - strncpy(buffer, result.utf8().data(), length - 1); -} - -void Selection::showTreeForThis() const -{ - if (start().node()) { - start().node()->showTreeAndMark(start().node(), "S", end().node(), "E"); - fprintf(stderr, "start offset: %d, end offset: %d\n", start().offset(), end().offset()); - } -} - -#endif - -} // namespace WebCore - -#ifndef NDEBUG - -void showTree(const WebCore::Selection& sel) -{ - sel.showTreeForThis(); -} - -void showTree(const WebCore::Selection* sel) -{ - if (sel) - sel->showTreeForThis(); -} - -#endif diff --git a/WebCore/editing/Selection.h b/WebCore/editing/Selection.h deleted file mode 100644 index ae50073..0000000 --- a/WebCore/editing/Selection.h +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Copyright (C) 2004 Apple Computer, 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 Selection_h -#define Selection_h - -#include "TextGranularity.h" -#include "VisiblePosition.h" - -namespace WebCore { - -class Position; - -const EAffinity SEL_DEFAULT_AFFINITY = DOWNSTREAM; - -class Selection { -public: - enum EState { NONE, CARET, RANGE }; - enum EDirection { FORWARD, BACKWARD, RIGHT, LEFT }; - - Selection(); - - Selection(const Position&, EAffinity); - Selection(const Position&, const Position&, EAffinity); - - Selection(const Range*, EAffinity = SEL_DEFAULT_AFFINITY); - - Selection(const VisiblePosition&); - Selection(const VisiblePosition&, const VisiblePosition&); - - static Selection selectionFromContentsOfNode(Node*); - - EState state() const { return m_state; } - - void setAffinity(EAffinity affinity) { m_affinity = affinity; } - EAffinity affinity() const { return m_affinity; } - - void setBase(const Position&); - void setBase(const VisiblePosition&); - void setExtent(const Position&); - void setExtent(const VisiblePosition&); - - Position base() const { return m_base; } - Position extent() const { return m_extent; } - Position start() const { return m_start; } - Position end() const { return m_end; } - - VisiblePosition visibleStart() const { return VisiblePosition(m_start, isRange() ? DOWNSTREAM : affinity()); } - VisiblePosition visibleEnd() const { return VisiblePosition(m_end, isRange() ? UPSTREAM : affinity()); } - - bool isNone() const { return state() == NONE; } - bool isCaret() const { return state() == CARET; } - bool isRange() const { return state() == RANGE; } - bool isCaretOrRange() const { return state() != NONE; } - - bool isBaseFirst() const { return m_baseIsFirst; } - - bool expandUsingGranularity(TextGranularity granularity); - TextGranularity granularity() const { return m_granularity; } - - PassRefPtr<Range> toRange() const; - - Element* rootEditableElement() const; - bool isContentEditable() const; - bool isContentRichlyEditable() const; - Node* shadowTreeRootNode() const; - - void debugPosition() const; - -#ifndef NDEBUG - void formatForDebugger(char* buffer, unsigned length) const; - void showTreeForThis() const; -#endif - - void setWithoutValidation(const Position&, const Position&); - -private: - void validate(); - void adjustForEditableContent(); - - Position m_base; // base position for the selection - Position m_extent; // extent position for the selection - Position m_start; // start position for the selection - Position m_end; // end position for the selection - - EAffinity m_affinity; // the upstream/downstream affinity of the caret - TextGranularity m_granularity; // granularity of start/end selection - - // these are cached, can be recalculated by validate() - EState m_state; // the state of the selection - bool m_baseIsFirst; // true if base is before the extent -}; - -inline bool operator==(const Selection& a, const Selection& b) -{ - return a.start() == b.start() && a.end() == b.end() && a.affinity() == b.affinity() && a.granularity() == b.granularity() && a.isBaseFirst() == b.isBaseFirst(); -} - -inline bool operator!=(const Selection& a, const Selection& b) -{ - return !(a == b); -} - -} // namespace WebCore - -#ifndef NDEBUG -// Outside the WebCore namespace for ease of invocation from gdb. -void showTree(const WebCore::Selection&); -void showTree(const WebCore::Selection*); -#endif - -#endif // Selection_h diff --git a/WebCore/editing/SelectionController.cpp b/WebCore/editing/SelectionController.cpp deleted file mode 100644 index bf52be4..0000000 --- a/WebCore/editing/SelectionController.cpp +++ /dev/null @@ -1,1187 +0,0 @@ -/* - * Copyright (C) 2004, 2008 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 "SelectionController.h" - -#include "CString.h" -#include "DeleteSelectionCommand.h" -#include "Document.h" -#include "Editor.h" -#include "Element.h" -#include "EventHandler.h" -#include "EventNames.h" -#include "ExceptionCode.h" -#include "FocusController.h" -#include "Frame.h" -#include "FrameTree.h" -#include "FrameView.h" -#include "GraphicsContext.h" -#include "HTMLInputElement.h" -#include "HTMLNames.h" -#include "HitTestRequest.h" -#include "HitTestResult.h" -#include "Page.h" -#include "Range.h" -#include "RenderTheme.h" -#include "RenderView.h" -#include "TextIterator.h" -#include "TypingCommand.h" -#include "htmlediting.h" -#include "visible_units.h" -#include <stdio.h> - -#define EDIT_DEBUG 0 - -namespace WebCore { - -using namespace HTMLNames; - -const int NoXPosForVerticalArrowNavigation = INT_MIN; - -SelectionController::SelectionController(Frame* frame, bool isDragCaretController) - : m_needsLayout(true) - , m_lastChangeWasHorizontalExtension(false) - , m_frame(frame) - , m_isDragCaretController(isDragCaretController) - , m_isCaretBlinkingSuspended(false) - , m_xPosForVerticalArrowNavigation(NoXPosForVerticalArrowNavigation) - , m_focused(false) -{ -} - -void SelectionController::moveTo(const VisiblePosition &pos, bool userTriggered) -{ - setSelection(Selection(pos.deepEquivalent(), pos.deepEquivalent(), pos.affinity()), true, true, userTriggered); -} - -void SelectionController::moveTo(const VisiblePosition &base, const VisiblePosition &extent, bool userTriggered) -{ - setSelection(Selection(base.deepEquivalent(), extent.deepEquivalent(), base.affinity()), true, true, userTriggered); -} - -void SelectionController::moveTo(const Position &pos, EAffinity affinity, bool userTriggered) -{ - setSelection(Selection(pos, affinity), true, true, userTriggered); -} - -void SelectionController::moveTo(const Range *r, EAffinity affinity, bool userTriggered) -{ - setSelection(Selection(startPosition(r), endPosition(r), affinity), true, true, userTriggered); -} - -void SelectionController::moveTo(const Position &base, const Position &extent, EAffinity affinity, bool userTriggered) -{ - setSelection(Selection(base, extent, affinity), true, true, userTriggered); -} - -void SelectionController::setSelection(const Selection& s, bool closeTyping, bool clearTypingStyleAndRemovedAnchor, bool userTriggered) -{ - if (m_isDragCaretController) { - invalidateCaretRect(); - m_sel = s; - m_needsLayout = true; - invalidateCaretRect(); - return; - } - if (!m_frame) { - m_sel = s; - return; - } - - if (s.base().node() && s.base().node()->document() != m_frame->document()) { - s.base().node()->document()->frame()->selection()->setSelection(s, closeTyping, clearTypingStyleAndRemovedAnchor, userTriggered); - return; - } - - if (closeTyping) - TypingCommand::closeTyping(m_frame->editor()->lastEditCommand()); - - if (clearTypingStyleAndRemovedAnchor) { - m_frame->clearTypingStyle(); - m_frame->editor()->setRemovedAnchor(0); - } - - if (m_sel == s) - return; - - Selection oldSelection = m_sel; - - m_sel = s; - - m_needsLayout = true; - - if (!s.isNone()) - m_frame->setFocusedNodeIfNeeded(); - - m_frame->selectionLayoutChanged(); - // Always clear the x position used for vertical arrow navigation. - // It will be restored by the vertical arrow navigation code if necessary. - m_xPosForVerticalArrowNavigation = NoXPosForVerticalArrowNavigation; - selectFrameElementInParentIfFullySelected(); - m_frame->notifyRendererOfSelectionChange(userTriggered); - m_frame->respondToChangedSelection(oldSelection, closeTyping); - if (userTriggered) - m_frame->revealCaret(RenderLayer::gAlignToEdgeIfNeeded); - - notifyAccessibilityForSelectionChange(); -} - -static bool removingNodeRemovesPosition(Node* node, const Position& position) -{ - if (!position.node()) - return false; - - if (position.node() == node) - return true; - - if (!node->isElementNode()) - return false; - - Element* element = static_cast<Element*>(node); - return element->contains(position.node()) || element->contains(position.node()->shadowAncestorNode()); -} - -void SelectionController::nodeWillBeRemoved(Node *node) -{ - if (isNone()) - return; - - // There can't be a selection inside a fragment, so if a fragment's node is being removed, - // the selection in the document that created the fragment needs no adjustment. - if (node && highestAncestor(node)->nodeType() == Node::DOCUMENT_FRAGMENT_NODE) - return; - - bool baseRemoved = removingNodeRemovesPosition(node, m_sel.base()); - bool extentRemoved = removingNodeRemovesPosition(node, m_sel.extent()); - bool startRemoved = removingNodeRemovesPosition(node, m_sel.start()); - bool endRemoved = removingNodeRemovesPosition(node, m_sel.end()); - - bool clearRenderTreeSelection = false; - bool clearDOMTreeSelection = false; - - if (startRemoved || endRemoved) { - // FIXME: When endpoints are removed, we should just alter the selection, instead of blowing it away. - clearRenderTreeSelection = true; - clearDOMTreeSelection = true; - } else if (baseRemoved || extentRemoved) { - // The base and/or extent are about to be removed, but the start and end aren't. - // Change the base and extent to the start and end, but don't re-validate the - // selection, since doing so could move the start and end into the node - // that is about to be removed. - if (m_sel.isBaseFirst()) - m_sel.setWithoutValidation(m_sel.start(), m_sel.end()); - else - m_sel.setWithoutValidation(m_sel.end(), m_sel.start()); - // FIXME: This could be more efficient if we had an isNodeInRange function on Ranges. - } else if (Range::compareBoundaryPoints(m_sel.start(), Position(node, 0)) == -1 && - Range::compareBoundaryPoints(m_sel.end(), Position(node, 0)) == 1) { - // If we did nothing here, when this node's renderer was destroyed, the rect that it - // occupied would be invalidated, but, selection gaps that change as a result of - // the removal wouldn't be invalidated. - // FIXME: Don't do so much unnecessary invalidation. - clearRenderTreeSelection = true; - } - - if (clearRenderTreeSelection) { - RefPtr<Document> document = m_sel.start().node()->document(); - document->updateRendering(); - if (RenderView* view = static_cast<RenderView*>(document->renderer())) - view->clearSelection(); - } - - if (clearDOMTreeSelection) - setSelection(Selection(), false, false); -} - -void SelectionController::willBeModified(EAlteration alter, EDirection direction) -{ - switch (alter) { - case MOVE: - m_lastChangeWasHorizontalExtension = false; - break; - case EXTEND: - if (!m_lastChangeWasHorizontalExtension) { - m_lastChangeWasHorizontalExtension = true; - Position start = m_sel.start(); - Position end = m_sel.end(); - switch (direction) { - // FIXME: right for bidi? - case RIGHT: - case FORWARD: - m_sel.setBase(start); - m_sel.setExtent(end); - break; - case LEFT: - case BACKWARD: - m_sel.setBase(end); - m_sel.setExtent(start); - break; - } - } - break; - } -} - -VisiblePosition SelectionController::modifyExtendingRightForward(TextGranularity granularity) -{ - VisiblePosition pos(m_sel.extent(), m_sel.affinity()); - switch (granularity) { - case CharacterGranularity: - pos = pos.next(true); - break; - case WordGranularity: - pos = nextWordPosition(pos); - break; - case SentenceGranularity: - pos = nextSentencePosition(pos); - break; - case LineGranularity: - pos = nextLinePosition(pos, xPosForVerticalArrowNavigation(EXTENT)); - break; - case ParagraphGranularity: - pos = nextParagraphPosition(pos, xPosForVerticalArrowNavigation(EXTENT)); - break; - case SentenceBoundary: - pos = endOfSentence(VisiblePosition(m_sel.end(), m_sel.affinity())); - break; - case LineBoundary: - pos = endOfLine(VisiblePosition(m_sel.end(), m_sel.affinity())); - break; - case ParagraphBoundary: - pos = endOfParagraph(VisiblePosition(m_sel.end(), m_sel.affinity())); - break; - case DocumentBoundary: - pos = VisiblePosition(m_sel.end(), m_sel.affinity()); - if (isEditablePosition(pos.deepEquivalent())) - pos = endOfEditableContent(pos); - else - pos = endOfDocument(pos); - break; - } - - return pos; -} - -VisiblePosition SelectionController::modifyMovingRight(TextGranularity granularity) -{ - VisiblePosition pos; - switch (granularity) { - case CharacterGranularity: - if (isRange()) - pos = VisiblePosition(m_sel.end(), m_sel.affinity()); - else - pos = VisiblePosition(m_sel.extent(), m_sel.affinity()).right(true); - break; - case WordGranularity: - case SentenceGranularity: - case LineGranularity: - case ParagraphGranularity: - case SentenceBoundary: - case LineBoundary: - case ParagraphBoundary: - case DocumentBoundary: - // FIXME: Implement all of the above. - pos = modifyMovingForward(granularity); - break; - } - return pos; -} - -VisiblePosition SelectionController::modifyMovingForward(TextGranularity granularity) -{ - VisiblePosition pos; - // FIXME: Stay in editable content for the less common granularities. - switch (granularity) { - case CharacterGranularity: - if (isRange()) - pos = VisiblePosition(m_sel.end(), m_sel.affinity()); - else - pos = VisiblePosition(m_sel.extent(), m_sel.affinity()).next(true); - break; - case WordGranularity: - pos = nextWordPosition(VisiblePosition(m_sel.extent(), m_sel.affinity())); - break; - case SentenceGranularity: - pos = nextSentencePosition(VisiblePosition(m_sel.extent(), m_sel.affinity())); - break; - case LineGranularity: { - // down-arrowing from a range selection that ends at the start of a line needs - // to leave the selection at that line start (no need to call nextLinePosition!) - pos = VisiblePosition(m_sel.end(), m_sel.affinity()); - if (!isRange() || !isStartOfLine(pos)) - pos = nextLinePosition(pos, xPosForVerticalArrowNavigation(START)); - break; - } - case ParagraphGranularity: - pos = nextParagraphPosition(VisiblePosition(m_sel.end(), m_sel.affinity()), xPosForVerticalArrowNavigation(START)); - break; - case SentenceBoundary: - pos = endOfSentence(VisiblePosition(m_sel.end(), m_sel.affinity())); - break; - case LineBoundary: - pos = endOfLine(VisiblePosition(m_sel.end(), m_sel.affinity())); - break; - case ParagraphBoundary: - pos = endOfParagraph(VisiblePosition(m_sel.end(), m_sel.affinity())); - break; - case DocumentBoundary: - pos = VisiblePosition(m_sel.end(), m_sel.affinity()); - if (isEditablePosition(pos.deepEquivalent())) - pos = endOfEditableContent(pos); - else - pos = endOfDocument(pos); - break; - - } - return pos; -} - -VisiblePosition SelectionController::modifyExtendingLeftBackward(TextGranularity granularity) -{ - VisiblePosition pos(m_sel.extent(), m_sel.affinity()); - - // Extending a selection backward by word or character from just after a table selects - // the table. This "makes sense" from the user perspective, esp. when deleting. - // It was done here instead of in VisiblePosition because we want VPs to iterate - // over everything. - switch (granularity) { - case CharacterGranularity: - pos = pos.previous(true); - break; - case WordGranularity: - pos = previousWordPosition(pos); - break; - case SentenceGranularity: - pos = previousSentencePosition(pos); - break; - case LineGranularity: - pos = previousLinePosition(pos, xPosForVerticalArrowNavigation(EXTENT)); - break; - case ParagraphGranularity: - pos = previousParagraphPosition(pos, xPosForVerticalArrowNavigation(EXTENT)); - break; - case SentenceBoundary: - pos = startOfSentence(VisiblePosition(m_sel.start(), m_sel.affinity())); - break; - case LineBoundary: - pos = startOfLine(VisiblePosition(m_sel.start(), m_sel.affinity())); - break; - case ParagraphBoundary: - pos = startOfParagraph(VisiblePosition(m_sel.start(), m_sel.affinity())); - break; - case DocumentBoundary: - pos = VisiblePosition(m_sel.start(), m_sel.affinity()); - if (isEditablePosition(pos.deepEquivalent())) - pos = startOfEditableContent(pos); - else - pos = startOfDocument(pos); - break; - } - return pos; -} - -VisiblePosition SelectionController::modifyMovingLeft(TextGranularity granularity) -{ - VisiblePosition pos; - switch (granularity) { - case CharacterGranularity: - if (isRange()) - pos = VisiblePosition(m_sel.start(), m_sel.affinity()); - else - pos = VisiblePosition(m_sel.extent(), m_sel.affinity()).left(true); - break; - case WordGranularity: - case SentenceGranularity: - case LineGranularity: - case ParagraphGranularity: - case SentenceBoundary: - case LineBoundary: - case ParagraphBoundary: - case DocumentBoundary: - // FIXME: Implement all of the above. - pos = modifyMovingBackward(granularity); - break; - } - return pos; -} - -VisiblePosition SelectionController::modifyMovingBackward(TextGranularity granularity) -{ - VisiblePosition pos; - switch (granularity) { - case CharacterGranularity: - if (isRange()) - pos = VisiblePosition(m_sel.start(), m_sel.affinity()); - else - pos = VisiblePosition(m_sel.extent(), m_sel.affinity()).previous(true); - break; - case WordGranularity: - pos = previousWordPosition(VisiblePosition(m_sel.extent(), m_sel.affinity())); - break; - case SentenceGranularity: - pos = previousSentencePosition(VisiblePosition(m_sel.extent(), m_sel.affinity())); - break; - case LineGranularity: - pos = previousLinePosition(VisiblePosition(m_sel.start(), m_sel.affinity()), xPosForVerticalArrowNavigation(START)); - break; - case ParagraphGranularity: - pos = previousParagraphPosition(VisiblePosition(m_sel.start(), m_sel.affinity()), xPosForVerticalArrowNavigation(START)); - break; - case SentenceBoundary: - pos = startOfSentence(VisiblePosition(m_sel.start(), m_sel.affinity())); - break; - case LineBoundary: - pos = startOfLine(VisiblePosition(m_sel.start(), m_sel.affinity())); - break; - case ParagraphBoundary: - pos = startOfParagraph(VisiblePosition(m_sel.start(), m_sel.affinity())); - break; - case DocumentBoundary: - pos = VisiblePosition(m_sel.start(), m_sel.affinity()); - if (isEditablePosition(pos.deepEquivalent())) - pos = startOfEditableContent(pos); - else - pos = startOfDocument(pos); - break; - } - return pos; -} - -bool SelectionController::modify(EAlteration alter, EDirection dir, TextGranularity granularity, bool userTriggered) -{ - if (userTriggered) { - SelectionController trialSelectionController; - trialSelectionController.setLastChangeWasHorizontalExtension(m_lastChangeWasHorizontalExtension); - trialSelectionController.setSelection(m_sel); - trialSelectionController.modify(alter, dir, granularity, false); - - bool change = m_frame->shouldChangeSelection(trialSelectionController.selection()); - if (!change) - return false; - } - - if (m_frame) - m_frame->setSelectionGranularity(granularity); - - willBeModified(alter, dir); - - VisiblePosition pos; - switch (dir) { - case RIGHT: - if (alter == MOVE) - pos = modifyMovingRight(granularity); - else - pos = modifyExtendingRightForward(granularity); - break; - case FORWARD: - if (alter == EXTEND) - pos = modifyExtendingRightForward(granularity); - else - pos = modifyMovingForward(granularity); - break; - case LEFT: - if (alter == MOVE) - pos = modifyMovingLeft(granularity); - else - pos = modifyExtendingLeftBackward(granularity); - break; - case BACKWARD: - if (alter == EXTEND) - pos = modifyExtendingLeftBackward(granularity); - else - pos = modifyMovingBackward(granularity); - break; - } - - if (pos.isNull()) - return false; - - // Some of the above operations set an xPosForVerticalArrowNavigation. - // Setting a selection will clear it, so save it to possibly restore later. - // Note: the START position type is arbitrary because it is unused, it would be - // the requested position type if there were no xPosForVerticalArrowNavigation set. - int x = xPosForVerticalArrowNavigation(START); - - switch (alter) { - case MOVE: - moveTo(pos, userTriggered); - break; - case EXTEND: - setExtent(pos, userTriggered); - break; - } - - if (granularity == LineGranularity || granularity == ParagraphGranularity) - m_xPosForVerticalArrowNavigation = x; - - if (userTriggered) { - // User modified selection change also sets the granularity back to character. - // NOTE: The one exception is that we need to keep word granularity to - // preserve smart delete behavior when extending by word (e.g. double-click), - // then shift-option-right arrow, then delete needs to smart delete, per TextEdit. - if (!(alter == EXTEND && granularity == WordGranularity && m_frame->selectionGranularity() == WordGranularity)) - m_frame->setSelectionGranularity(CharacterGranularity); - } - - setNeedsLayout(); - - return true; -} - -// FIXME: Maybe baseline would be better? -static bool caretY(const VisiblePosition &c, int &y) -{ - IntRect rect = c.caretRect(); - if (rect.isEmpty()) - return false; - y = rect.y() + rect.height() / 2; - return true; -} - -bool SelectionController::modify(EAlteration alter, int verticalDistance, bool userTriggered) -{ - if (verticalDistance == 0) - return false; - - if (userTriggered) { - SelectionController trialSelectionController; - trialSelectionController.setSelection(m_sel); - trialSelectionController.modify(alter, verticalDistance, false); - - bool change = m_frame->shouldChangeSelection(trialSelectionController.selection()); - if (!change) - return false; - } - - bool up = verticalDistance < 0; - if (up) - verticalDistance = -verticalDistance; - - willBeModified(alter, up ? BACKWARD : FORWARD); - - VisiblePosition pos; - int xPos = 0; - switch (alter) { - case MOVE: - pos = VisiblePosition(up ? m_sel.start() : m_sel.end(), m_sel.affinity()); - xPos = xPosForVerticalArrowNavigation(up ? START : END); - m_sel.setAffinity(up ? UPSTREAM : DOWNSTREAM); - break; - case EXTEND: - pos = VisiblePosition(m_sel.extent(), m_sel.affinity()); - xPos = xPosForVerticalArrowNavigation(EXTENT); - m_sel.setAffinity(DOWNSTREAM); - break; - } - - int startY; - if (!caretY(pos, startY)) - return false; - if (up) - startY = -startY; - int lastY = startY; - - VisiblePosition result; - VisiblePosition next; - for (VisiblePosition p = pos; ; p = next) { - next = (up ? previousLinePosition : nextLinePosition)(p, xPos); - if (next.isNull() || next == p) - break; - int nextY; - if (!caretY(next, nextY)) - break; - if (up) - nextY = -nextY; - if (nextY - startY > verticalDistance) - break; - if (nextY >= lastY) { - lastY = nextY; - result = next; - } - } - - if (result.isNull()) - return false; - - switch (alter) { - case MOVE: - moveTo(result, userTriggered); - break; - case EXTEND: - setExtent(result, userTriggered); - break; - } - - if (userTriggered) - m_frame->setSelectionGranularity(CharacterGranularity); - - return true; -} - -bool SelectionController::expandUsingGranularity(TextGranularity granularity) -{ - if (isNone()) - return false; - - m_sel.expandUsingGranularity(granularity); - m_needsLayout = true; - return true; -} - -int SelectionController::xPosForVerticalArrowNavigation(EPositionType type) -{ - int x = 0; - - if (isNone()) - return x; - - Position pos; - switch (type) { - case START: - pos = m_sel.start(); - break; - case END: - pos = m_sel.end(); - break; - case BASE: - pos = m_sel.base(); - break; - case EXTENT: - pos = m_sel.extent(); - break; - } - - Frame *frame = pos.node()->document()->frame(); - if (!frame) - return x; - - if (m_xPosForVerticalArrowNavigation == NoXPosForVerticalArrowNavigation) { - VisiblePosition visiblePosition(pos, m_sel.affinity()); - // VisiblePosition creation can fail here if a node containing the selection becomes visibility:hidden - // after the selection is created and before this function is called. - x = visiblePosition.isNotNull() ? visiblePosition.caretRect().x() : 0; - m_xPosForVerticalArrowNavigation = x; - } - else - x = m_xPosForVerticalArrowNavigation; - - return x; -} - -void SelectionController::clear() -{ - setSelection(Selection()); -} - -void SelectionController::setBase(const VisiblePosition &pos, bool userTriggered) -{ - setSelection(Selection(pos.deepEquivalent(), m_sel.extent(), pos.affinity()), true, true, userTriggered); -} - -void SelectionController::setExtent(const VisiblePosition &pos, bool userTriggered) -{ - setSelection(Selection(m_sel.base(), pos.deepEquivalent(), pos.affinity()), true, true, userTriggered); -} - -void SelectionController::setBase(const Position &pos, EAffinity affinity, bool userTriggered) -{ - setSelection(Selection(pos, m_sel.extent(), affinity), true, true, userTriggered); -} - -void SelectionController::setExtent(const Position &pos, EAffinity affinity, bool userTriggered) -{ - setSelection(Selection(m_sel.base(), pos, affinity), true, true, userTriggered); -} - -void SelectionController::setNeedsLayout(bool flag) -{ - m_needsLayout = flag; -} - -void SelectionController::layout() -{ - if (isNone() || !m_sel.start().node()->inDocument() || !m_sel.end().node()->inDocument()) { - m_caretRect = IntRect(); - m_caretPositionOnLayout = IntPoint(); - return; - } - - m_sel.start().node()->document()->updateRendering(); - - m_caretRect = IntRect(); - m_caretPositionOnLayout = IntPoint(); - - if (isCaret()) { - VisiblePosition pos(m_sel.start(), m_sel.affinity()); - if (pos.isNotNull()) { - ASSERT(pos.deepEquivalent().node()->renderer()); - m_caretRect = pos.caretRect(); - - int x, y; - pos.deepEquivalent().node()->renderer()->absolutePositionForContent(x, y); - m_caretPositionOnLayout = IntPoint(x, y); - } - } - - m_needsLayout = false; -} - -IntRect SelectionController::caretRect() const -{ - if (m_needsLayout) - const_cast<SelectionController *>(this)->layout(); - - IntRect caret = m_caretRect; - - if (m_sel.start().node() && m_sel.start().node()->renderer()) { - int x, y; - m_sel.start().node()->renderer()->absolutePositionForContent(x, y); - caret.move(IntPoint(x, y) - m_caretPositionOnLayout); - } - - return caret; -} - -static IntRect repaintRectForCaret(IntRect caret) -{ - if (caret.isEmpty()) - return IntRect(); - // Ensure that the dirty rect intersects the block that paints the caret even in the case where - // the caret itself is just outside the block. See <https://bugs.webkit.org/show_bug.cgi?id=19086>. - caret.inflateX(1); - return caret; -} - -IntRect SelectionController::caretRepaintRect() const -{ - return repaintRectForCaret(caretRect()); -} - -bool SelectionController::recomputeCaretRect() -{ - if (!m_frame || !m_frame->document()) - return false; - - FrameView* v = m_frame->document()->view(); - if (!v) - return false; - - if (!m_needsLayout) - return false; - - IntRect oldRect = m_caretRect; - m_needsLayout = true; - IntRect newRect = caretRect(); - if (oldRect == newRect) - return false; - - if (RenderView* view = static_cast<RenderView*>(m_frame->document()->renderer())) { - view->repaintViewRectangle(repaintRectForCaret(oldRect), false); - view->repaintViewRectangle(repaintRectForCaret(newRect), false); - } - return true; -} - -void SelectionController::invalidateCaretRect() -{ - if (!isCaret()) - return; - - Document* d = m_sel.start().node()->document(); - - bool caretRectChanged = recomputeCaretRect(); - - // EDIT FIXME: This is an unfortunate hack. - // Basically, we can't trust this layout position since we - // can't guarantee that the check to see if we are in unrendered - // content will work at this point. We may have to wait for - // a layout and re-render of the document to happen. So, resetting this - // flag will cause another caret layout to happen the first time - // that we try to paint the caret after this call. That one will work since - // it happens after the document has accounted for any editing - // changes which may have been done. - // And, we need to leave this layout here so the caret moves right - // away after clicking. - m_needsLayout = true; - - if (!caretRectChanged) { - if (RenderView* view = static_cast<RenderView*>(d->renderer())) - view->repaintViewRectangle(caretRepaintRect(), false); - } -} - -void SelectionController::paintCaret(GraphicsContext *p, const IntRect &rect) -{ - if (! m_sel.isCaret()) - return; - - if (m_needsLayout) - layout(); - - IntRect caret = intersection(caretRect(), rect); - if (!caret.isEmpty()) { - Color caretColor = Color::black; - Element* element = rootEditableElement(); - if (element && element->renderer()) - caretColor = element->renderer()->style()->color(); - - p->fillRect(caret, caretColor); - } -} - -void SelectionController::debugRenderer(RenderObject *r, bool selected) const -{ - if (r->node()->isElementNode()) { - Element *element = static_cast<Element *>(r->node()); - fprintf(stderr, "%s%s\n", selected ? "==> " : " ", element->localName().string().utf8().data()); - } - else if (r->isText()) { - RenderText* textRenderer = static_cast<RenderText*>(r); - if (textRenderer->textLength() == 0 || !textRenderer->firstTextBox()) { - fprintf(stderr, "%s#text (empty)\n", selected ? "==> " : " "); - return; - } - - static const int max = 36; - String text = textRenderer->text(); - int textLength = text.length(); - if (selected) { - int offset = 0; - if (r->node() == m_sel.start().node()) - offset = m_sel.start().offset(); - else if (r->node() == m_sel.end().node()) - offset = m_sel.end().offset(); - - int pos; - InlineTextBox *box = textRenderer->findNextInlineTextBox(offset, pos); - text = text.substring(box->m_start, box->m_len); - - String show; - int mid = max / 2; - int caret = 0; - - // text is shorter than max - if (textLength < max) { - show = text; - caret = pos; - } - - // too few characters to left - else if (pos - mid < 0) { - show = text.left(max - 3) + "..."; - caret = pos; - } - - // enough characters on each side - else if (pos - mid >= 0 && pos + mid <= textLength) { - show = "..." + text.substring(pos - mid + 3, max - 6) + "..."; - caret = mid; - } - - // too few characters on right - else { - show = "..." + text.right(max - 3); - caret = pos - (textLength - show.length()); - } - - show.replace('\n', ' '); - show.replace('\r', ' '); - fprintf(stderr, "==> #text : \"%s\" at offset %d\n", show.utf8().data(), pos); - fprintf(stderr, " "); - for (int i = 0; i < caret; i++) - fprintf(stderr, " "); - fprintf(stderr, "^\n"); - } - else { - if ((int)text.length() > max) - text = text.left(max - 3) + "..."; - else - text = text.left(max); - fprintf(stderr, " #text : \"%s\"\n", text.utf8().data()); - } - } -} - -bool SelectionController::contains(const IntPoint& point) -{ - Document* document = m_frame->document(); - - // Treat a collapsed selection like no selection. - if (!isRange()) - return false; - if (!document->renderer()) - return false; - - HitTestRequest request(true, true); - HitTestResult result(point); - document->renderer()->layer()->hitTest(request, result); - Node* innerNode = result.innerNode(); - if (!innerNode || !innerNode->renderer()) - return false; - - VisiblePosition visiblePos(innerNode->renderer()->positionForPoint(result.localPoint())); - if (visiblePos.isNull()) - return false; - - if (m_sel.visibleStart().isNull() || m_sel.visibleEnd().isNull()) - return false; - - Position start(m_sel.visibleStart().deepEquivalent()); - Position end(m_sel.visibleEnd().deepEquivalent()); - Position p(visiblePos.deepEquivalent()); - - return comparePositions(start, p) <= 0 && comparePositions(p, end) <= 0; -} - -// Workaround for the fact that it's hard to delete a frame. -// Call this after doing user-triggered selections to make it easy to delete the frame you entirely selected. -// Can't do this implicitly as part of every setSelection call because in some contexts it might not be good -// for the focus to move to another frame. So instead we call it from places where we are selecting with the -// mouse or the keyboard after setting the selection. -void SelectionController::selectFrameElementInParentIfFullySelected() -{ - // Find the parent frame; if there is none, then we have nothing to do. - Frame* parent = m_frame->tree()->parent(); - if (!parent) - return; - Page* page = m_frame->page(); - if (!page) - return; - - // Check if the selection contains the entire frame contents; if not, then there is nothing to do. - if (!isRange()) - return; - if (!isStartOfDocument(selection().visibleStart())) - return; - if (!isEndOfDocument(selection().visibleEnd())) - return; - - // Get to the <iframe> or <frame> (or even <object>) element in the parent frame. - Document* doc = m_frame->document(); - if (!doc) - return; - Element* ownerElement = doc->ownerElement(); - if (!ownerElement) - return; - Node* ownerElementParent = ownerElement->parentNode(); - if (!ownerElementParent) - return; - - // This method's purpose is it to make it easier to select iframes (in order to delete them). Don't do anything if the iframe isn't deletable. - if (!ownerElementParent->isContentEditable()) - return; - - // 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)); - - // Focus on the parent frame, and then select from before this element to after. - Selection newSelection(beforeOwnerElement, afterOwnerElement); - if (parent->shouldChangeSelection(newSelection)) { - page->focusController()->setFocusedFrame(parent); - parent->selection()->setSelection(newSelection); - } -} - -void SelectionController::selectAll() -{ - Document* document = m_frame->document(); - if (!document) - return; - - if (document->focusedNode() && document->focusedNode()->canSelectAll()) { - document->focusedNode()->selectAll(); - return; - } - - Node* root = 0; - if (isContentEditable()) - root = highestEditableRoot(m_sel.start()); - else { - root = shadowTreeRootNode(); - if (!root) - root = document->documentElement(); - } - if (!root) - return; - Selection newSelection(Selection::selectionFromContentsOfNode(root)); - if (m_frame->shouldChangeSelection(newSelection)) - setSelection(newSelection); - selectFrameElementInParentIfFullySelected(); - m_frame->notifyRendererOfSelectionChange(true); -} - -bool SelectionController::setSelectedRange(Range* range, EAffinity affinity, bool closeTyping) -{ - if (!range) - return false; - - ExceptionCode ec = 0; - Node* startContainer = range->startContainer(ec); - if (ec) - return false; - - Node* endContainer = range->endContainer(ec); - if (ec) - return false; - - ASSERT(startContainer); - ASSERT(endContainer); - ASSERT(startContainer->document() == endContainer->document()); - - m_frame->document()->updateLayoutIgnorePendingStylesheets(); - - // Non-collapsed ranges are not allowed to start at the end of a line that is wrapped, - // they start at the beginning of the next line instead - bool collapsed = range->collapsed(ec); - if (ec) - return false; - - int startOffset = range->startOffset(ec); - if (ec) - return false; - - int endOffset = range->endOffset(ec); - if (ec) - return false; - - // FIXME: Can we provide extentAffinity? - VisiblePosition visibleStart(startContainer, startOffset, collapsed ? affinity : DOWNSTREAM); - VisiblePosition visibleEnd(endContainer, endOffset, SEL_DEFAULT_AFFINITY); - setSelection(Selection(visibleStart, visibleEnd), closeTyping); - return true; -} - -bool SelectionController::isInPasswordField() const -{ - Node* startNode = start().node(); - if (!startNode) - return false; - - startNode = startNode->shadowAncestorNode(); - if (!startNode) - return false; - - if (!startNode->hasTagName(inputTag)) - return false; - - return static_cast<HTMLInputElement*>(startNode)->inputType() == HTMLInputElement::PASSWORD; -} - -bool SelectionController::isInsideNode() const -{ - Node* startNode = start().node(); - if (!startNode) - return false; - return !isTableElement(startNode) && !editingIgnoresContent(startNode); -} - -void SelectionController::focusedOrActiveStateChanged() -{ - bool activeAndFocused = isFocusedAndActive(); - - // Because RenderObject::selectionBackgroundColor() and - // RenderObject::selectionForegroundColor() check if the frame is active, - // we have to update places those colors were painted. - if (RenderView* view = static_cast<RenderView*>(m_frame->document()->renderer())) - view->repaintViewRectangle(enclosingIntRect(m_frame->selectionRect())); - - // Caret appears in the active frame. - if (activeAndFocused) - m_frame->setSelectionFromNone(); - m_frame->setCaretVisible(activeAndFocused); - - // Update for caps lock state - m_frame->eventHandler()->capsLockStateMayHaveChanged(); - - // Because CSSStyleSelector::checkOneSelector() and - // RenderTheme::isFocused() check if the frame is active, we have to - // update style and theme state that depended on those. - if (Node* node = m_frame->document()->focusedNode()) { - node->setChanged(); - if (RenderObject* renderer = node->renderer()) - if (renderer && renderer->style()->hasAppearance()) - theme()->stateChanged(renderer, FocusState); - } - - // Secure keyboard entry is set by the active frame. - if (m_frame->document()->useSecureKeyboardEntryWhenActive()) - m_frame->setUseSecureKeyboardEntry(activeAndFocused); -} - -void SelectionController::pageActivationChanged() -{ - focusedOrActiveStateChanged(); -} - -void SelectionController::setFocused(bool flag) -{ - if (m_focused == flag) - return; - m_focused = flag; - - focusedOrActiveStateChanged(); - - if (Document* doc = m_frame->document()) - doc->dispatchWindowEvent(flag ? eventNames().focusEvent : eventNames().blurEvent, false, false); -} - -bool SelectionController::isFocusedAndActive() const -{ - return m_focused && m_frame->page() && m_frame->page()->focusController()->isActive(); -} - -#ifndef NDEBUG - -void SelectionController::formatForDebugger(char* buffer, unsigned length) const -{ - m_sel.formatForDebugger(buffer, length); -} - -void SelectionController::showTreeForThis() const -{ - m_sel.showTreeForThis(); -} - -#endif - -} - -#ifndef NDEBUG - -void showTree(const WebCore::SelectionController& sel) -{ - sel.showTreeForThis(); -} - -void showTree(const WebCore::SelectionController* sel) -{ - if (sel) - sel->showTreeForThis(); -} - -#endif diff --git a/WebCore/editing/SelectionController.h b/WebCore/editing/SelectionController.h deleted file mode 100644 index ba89670..0000000 --- a/WebCore/editing/SelectionController.h +++ /dev/null @@ -1,186 +0,0 @@ -/* - * Copyright (C) 2004 Apple Computer, 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 SelectionController_h -#define SelectionController_h - -#include "IntRect.h" -#include "Selection.h" -#include "Range.h" -#include <wtf/Noncopyable.h> - -namespace WebCore { - -class Frame; -class GraphicsContext; -class RenderObject; -class VisiblePosition; - -class SelectionController : Noncopyable { -public: - enum EAlteration { MOVE, EXTEND }; - enum EDirection { FORWARD, BACKWARD, RIGHT, LEFT }; - - SelectionController(Frame* = 0, bool isDragCaretController = false); - - Element* rootEditableElement() const { return m_sel.rootEditableElement(); } - bool isContentEditable() const { return m_sel.isContentEditable(); } - bool isContentRichlyEditable() const { return m_sel.isContentRichlyEditable(); } - Node* shadowTreeRootNode() const { return m_sel.shadowTreeRootNode(); } - - void moveTo(const Range*, EAffinity, bool userTriggered = false); - void moveTo(const VisiblePosition&, bool userTriggered = false); - void moveTo(const VisiblePosition&, const VisiblePosition&, bool userTriggered = false); - void moveTo(const Position&, EAffinity, bool userTriggered = false); - void moveTo(const Position&, const Position&, EAffinity, bool userTriggered = false); - - const Selection& selection() const { return m_sel; } - void setSelection(const Selection&, bool closeTyping = true, bool clearTypingStyleAndRemovedAnchor = true, bool userTriggered = false); - bool setSelectedRange(Range*, EAffinity, bool closeTyping); - void selectAll(); - void clear(); - - // Call this after doing user-triggered selections to make it easy to delete the frame you entirely selected. - void selectFrameElementInParentIfFullySelected(); - - bool contains(const IntPoint&); - - Selection::EState state() const { return m_sel.state(); } - - EAffinity affinity() const { return m_sel.affinity(); } - - bool modify(EAlteration, EDirection, TextGranularity, bool userTriggered = false); - bool modify(EAlteration, int verticalDistance, bool userTriggered = false); - bool expandUsingGranularity(TextGranularity); - - void setBase(const VisiblePosition&, bool userTriggered = false); - void setBase(const Position&, EAffinity, bool userTriggered = false); - void setExtent(const VisiblePosition&, bool userTriggered = false); - void setExtent(const Position&, EAffinity, bool userTriggered = false); - - Position base() const { return m_sel.base(); } - Position extent() const { return m_sel.extent(); } - Position start() const { return m_sel.start(); } - Position end() const { return m_sel.end(); } - - IntRect caretRect() const; - void setNeedsLayout(bool flag = true); - - void setLastChangeWasHorizontalExtension(bool b) { m_lastChangeWasHorizontalExtension = b; } - void willBeModified(EAlteration, EDirection); - - bool isNone() const { return m_sel.isNone(); } - bool isCaret() const { return m_sel.isCaret(); } - bool isRange() const { return m_sel.isRange(); } - bool isCaretOrRange() const { return m_sel.isCaretOrRange(); } - bool isInPasswordField() const; - bool isInsideNode() const; - - PassRefPtr<Range> toRange() const { return m_sel.toRange(); } - - void debugRenderer(RenderObject*, bool selected) const; - - void nodeWillBeRemoved(Node*); - - bool recomputeCaretRect(); // returns true if caret rect moved - void invalidateCaretRect(); - void paintCaret(GraphicsContext*, const IntRect&); - - // Used to suspend caret blinking while the mouse is down. - void setCaretBlinkingSuspended(bool suspended) { m_isCaretBlinkingSuspended = suspended; } - bool isCaretBlinkingSuspended() const { return m_isCaretBlinkingSuspended; } - - // Focus - void setFocused(bool); - bool isFocusedAndActive() const; - void pageActivationChanged(); - -#ifndef NDEBUG - void formatForDebugger(char* buffer, unsigned length) const; - void showTreeForThis() const; -#endif - -private: - enum EPositionType { START, END, BASE, EXTENT }; - - VisiblePosition modifyExtendingRightForward(TextGranularity); - VisiblePosition modifyMovingRight(TextGranularity); - VisiblePosition modifyMovingForward(TextGranularity); - VisiblePosition modifyExtendingLeftBackward(TextGranularity); - VisiblePosition modifyMovingLeft(TextGranularity); - VisiblePosition modifyMovingBackward(TextGranularity); - - void layout(); - IntRect caretRepaintRect() const; - - int xPosForVerticalArrowNavigation(EPositionType); - -#if PLATFORM(MAC) - void notifyAccessibilityForSelectionChange(); -#else - void notifyAccessibilityForSelectionChange() {}; -#endif - - void focusedOrActiveStateChanged(); - - Selection m_sel; - - IntRect m_caretRect; // caret coordinates, size, and position - - // m_caretPositionOnLayout stores the scroll offset on the previous call to SelectionController::layout(). - // When asked for caretRect(), we correct m_caretRect for offset due to scrolling since the last layout(). - // This is faster than doing another layout(). - IntPoint m_caretPositionOnLayout; - - bool m_needsLayout : 1; // true if the caret and expectedVisible rectangles need to be calculated - bool m_lastChangeWasHorizontalExtension : 1; - Frame* m_frame; - bool m_isDragCaretController; - - bool m_isCaretBlinkingSuspended; - - int m_xPosForVerticalArrowNavigation; - bool m_focused; -}; - -inline bool operator==(const SelectionController& a, const SelectionController& b) -{ - return a.start() == b.start() && a.end() == b.end() && a.affinity() == b.affinity(); -} - -inline bool operator!=(const SelectionController& a, const SelectionController& b) -{ - return !(a == b); -} - -} // namespace WebCore - -#ifndef NDEBUG -// Outside the WebCore namespace for ease of invocation from gdb. -void showTree(const WebCore::SelectionController&); -void showTree(const WebCore::SelectionController*); -#endif - -#endif // SelectionController_h diff --git a/WebCore/editing/SetNodeAttributeCommand.cpp b/WebCore/editing/SetNodeAttributeCommand.cpp deleted file mode 100644 index 995dd1e..0000000 --- a/WebCore/editing/SetNodeAttributeCommand.cpp +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (C) 2005, 2008 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 "SetNodeAttributeCommand.h" - -#include "Element.h" -#include <wtf/Assertions.h> - -namespace WebCore { - -SetNodeAttributeCommand::SetNodeAttributeCommand(PassRefPtr<Element> element, - const QualifiedName& attribute, const String &value) - : SimpleEditCommand(element->document()), m_element(element), m_attribute(attribute), m_value(value) -{ - ASSERT(m_element); - ASSERT(!m_value.isNull()); -} - -void SetNodeAttributeCommand::doApply() -{ - ASSERT(m_element); - ASSERT(!m_value.isNull()); - - ExceptionCode ec = 0; - m_oldValue = m_element->getAttribute(m_attribute); - m_element->setAttribute(m_attribute, m_value.impl(), ec); - ASSERT(ec == 0); -} - -void SetNodeAttributeCommand::doUnapply() -{ - ASSERT(m_element); - - ExceptionCode ec = 0; - if (m_oldValue.isNull()) - m_element->removeAttribute(m_attribute, ec); - else - m_element->setAttribute(m_attribute, m_oldValue.impl(), ec); - ASSERT(ec == 0); -} - -} // namespace WebCore - diff --git a/WebCore/editing/SetNodeAttributeCommand.h b/WebCore/editing/SetNodeAttributeCommand.h deleted file mode 100644 index aec79a3..0000000 --- a/WebCore/editing/SetNodeAttributeCommand.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (C) 2005, 2006, 2008 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 SetNodeAttributeCommand_h -#define SetNodeAttributeCommand_h - -#include "EditCommand.h" -#include "QualifiedName.h" - -namespace WebCore { - -class SetNodeAttributeCommand : public SimpleEditCommand { -public: - static PassRefPtr<SetNodeAttributeCommand> create(PassRefPtr<Element> element, const QualifiedName& attribute, const AtomicString& value) - { - return adoptRef(new SetNodeAttributeCommand(element, attribute, value)); - } - -private: - SetNodeAttributeCommand(PassRefPtr<Element>, const QualifiedName& attribute, const String& value); - - virtual void doApply(); - virtual void doUnapply(); - - RefPtr<Element> m_element; - QualifiedName m_attribute; - AtomicString m_value; - AtomicString m_oldValue; -}; - -} // namespace WebCore - -#endif // SetNodeAttributeCommand_h diff --git a/WebCore/editing/SmartReplace.cpp b/WebCore/editing/SmartReplace.cpp deleted file mode 100644 index c5f5240..0000000 --- a/WebCore/editing/SmartReplace.cpp +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (C) 2007 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. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of - * its contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS 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 "SmartReplace.h" - -#if !PLATFORM(CF) && !USE(ICU_UNICODE) - -namespace WebCore { - -bool isCharacterSmartReplaceExempt(UChar32 c, bool isPreviousCharacter) -{ - return false; -} - -} - -#endif // !PLATFORM(CF) diff --git a/WebCore/editing/SmartReplace.h b/WebCore/editing/SmartReplace.h deleted file mode 100644 index 5a37137..0000000 --- a/WebCore/editing/SmartReplace.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (C) 2007 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. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of - * its contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS 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 <wtf/unicode/Unicode.h> - -namespace WebCore { - -bool isCharacterSmartReplaceExempt(UChar32 c, bool isPreviousCharacter); - -} // namespace WebCore diff --git a/WebCore/editing/SmartReplaceCF.cpp b/WebCore/editing/SmartReplaceCF.cpp deleted file mode 100644 index f2fd985..0000000 --- a/WebCore/editing/SmartReplaceCF.cpp +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (C) 2007 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. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of - * its contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS 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 "SmartReplace.h" - -#include <CoreFoundation/CFCharacterSet.h> - -namespace WebCore { - -static CFMutableCharacterSetRef getSmartSet(bool isPreviousCharacter) -{ - static CFMutableCharacterSetRef preSmartSet = NULL; - static CFMutableCharacterSetRef postSmartSet = NULL; - CFMutableCharacterSetRef smartSet = isPreviousCharacter ? preSmartSet : postSmartSet; - if (!smartSet) { - smartSet = CFCharacterSetCreateMutable(kCFAllocatorDefault); - CFCharacterSetAddCharactersInString(smartSet, isPreviousCharacter ? CFSTR("([\"\'#$/-`{") : CFSTR(")].,;:?\'!\"%*-/}")); - CFCharacterSetUnion(smartSet, CFCharacterSetGetPredefined(kCFCharacterSetWhitespaceAndNewline)); - // Adding CJK ranges - CFCharacterSetAddCharactersInRange(smartSet, CFRangeMake(0x1100, 256)); // Hangul Jamo (0x1100 - 0x11FF) - CFCharacterSetAddCharactersInRange(smartSet, CFRangeMake(0x2E80, 352)); // CJK & Kangxi Radicals (0x2E80 - 0x2FDF) - CFCharacterSetAddCharactersInRange(smartSet, CFRangeMake(0x2FF0, 464)); // Ideograph Descriptions, CJK Symbols, Hiragana, Katakana, Bopomofo, Hangul Compatibility Jamo, Kanbun, & Bopomofo Ext (0x2FF0 - 0x31BF) - CFCharacterSetAddCharactersInRange(smartSet, CFRangeMake(0x3200, 29392)); // Enclosed CJK, CJK Ideographs (Uni Han & Ext A), & Yi (0x3200 - 0xA4CF) - CFCharacterSetAddCharactersInRange(smartSet, CFRangeMake(0xAC00, 11183)); // Hangul Syllables (0xAC00 - 0xD7AF) - CFCharacterSetAddCharactersInRange(smartSet, CFRangeMake(0xF900, 352)); // CJK Compatibility Ideographs (0xF900 - 0xFA5F) - CFCharacterSetAddCharactersInRange(smartSet, CFRangeMake(0xFE30, 32)); // CJK Compatibility From (0xFE30 - 0xFE4F) - CFCharacterSetAddCharactersInRange(smartSet, CFRangeMake(0xFF00, 240)); // Half/Full Width Form (0xFF00 - 0xFFEF) - CFCharacterSetAddCharactersInRange(smartSet, CFRangeMake(0x20000, 0xA6D7)); // CJK Ideograph Exntension B - CFCharacterSetAddCharactersInRange(smartSet, CFRangeMake(0x2F800, 0x021E)); // CJK Compatibility Ideographs (0x2F800 - 0x2FA1D) - - if (isPreviousCharacter) - preSmartSet = smartSet; - else { - CFCharacterSetUnion(smartSet, CFCharacterSetGetPredefined(kCFCharacterSetPunctuation)); - postSmartSet = smartSet; - } - } - return smartSet; -} - -bool isCharacterSmartReplaceExempt(UChar32 c, bool isPreviousCharacter) -{ - return CFCharacterSetIsLongCharacterMember(getSmartSet(isPreviousCharacter), c); -} - -} diff --git a/WebCore/editing/SmartReplaceICU.cpp b/WebCore/editing/SmartReplaceICU.cpp deleted file mode 100644 index 18be647..0000000 --- a/WebCore/editing/SmartReplaceICU.cpp +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright (C) 2007 Apple Inc. All rights reserved. - * Copyright (C) 2008 Tony Chang <idealisms@gmail.com> - * - * 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. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of - * its contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS 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 "SmartReplace.h" - -#if !PLATFORM(CF) && USE(ICU_UNICODE) -#include "PlatformString.h" -#include <unicode/uset.h> -#include <wtf/Assertions.h> - -namespace WebCore { - -static void addAllCodePoints(USet* smartSet, const String& string) { - const UChar* characters = string.characters(); - for (size_t i = 0; i < string.length(); i++) - uset_add(smartSet, characters[i]); -} - -// This is mostly a port of the code in WebCore/editing/SmartReplaceCF.cpp -// except we use icu in place of CoreFoundations character classes. -static USet* getSmartSet(bool isPreviousCharacter) -{ - static USet* preSmartSet = NULL; - static USet* postSmartSet = NULL; - USet* smartSet = isPreviousCharacter ? preSmartSet : postSmartSet; - if (!smartSet) { - // Whitespace and newline (kCFCharacterSetWhitespaceAndNewline) - UErrorCode ec = U_ZERO_ERROR; - String whitespaceAndNewline = "[[:WSpace:] [\\u000A\\u000B\\u000C\\u000D\\u0085]]"; - smartSet = uset_openPattern(whitespaceAndNewline.characters(), whitespaceAndNewline.length(), &ec); - ASSERT(U_SUCCESS(ec)); - - // CJK ranges - uset_addRange(smartSet, 0x1100, 0x1100 + 256); // Hangul Jamo (0x1100 - 0x11FF) - uset_addRange(smartSet, 0x2E80, 0x2E80 + 352); // CJK & Kangxi Radicals (0x2E80 - 0x2FDF) - uset_addRange(smartSet, 0x2FF0, 0x2FF0 + 464); // Ideograph Descriptions, CJK Symbols, Hiragana, Katakana, Bopomofo, Hangul Compatibility Jamo, Kanbun, & Bopomofo Ext (0x2FF0 - 0x31BF) - uset_addRange(smartSet, 0x3200, 0x3200 + 29392); // Enclosed CJK, CJK Ideographs (Uni Han & Ext A), & Yi (0x3200 - 0xA4CF) - uset_addRange(smartSet, 0xAC00, 0xAC00 + 11183); // Hangul Syllables (0xAC00 - 0xD7AF) - uset_addRange(smartSet, 0xF900, 0xF900 + 352); // CJK Compatibility Ideographs (0xF900 - 0xFA5F) - uset_addRange(smartSet, 0xFE30, 0xFE30 + 32); // CJK Compatibility From (0xFE30 - 0xFE4F) - uset_addRange(smartSet, 0xFF00, 0xFF00 + 240); // Half/Full Width Form (0xFF00 - 0xFFEF) - uset_addRange(smartSet, 0x20000, 0x20000 + 0xA6D7); // CJK Ideograph Exntension B - uset_addRange(smartSet, 0x2F800, 0x2F800 + 0x021E); // CJK Compatibility Ideographs (0x2F800 - 0x2FA1D) - - if (isPreviousCharacter) { - addAllCodePoints(smartSet, "([\"\'#$/-`{"); - preSmartSet = smartSet; - } else { - addAllCodePoints(smartSet, ")].,;:?\'!\"%*-/}"); - - // Punctuation (kCFCharacterSetPunctuation) - UErrorCode ec = U_ZERO_ERROR; - String punctuationClass = "[:P:]"; - USet* icuPunct = uset_openPattern(punctuationClass.characters(), punctuationClass.length(), &ec); - ASSERT(U_SUCCESS(ec)); - uset_addAll(smartSet, icuPunct); - uset_close(icuPunct); - - postSmartSet = smartSet; - } - } - return smartSet; -} - -bool isCharacterSmartReplaceExempt(UChar32 c, bool isPreviousCharacter) -{ - return uset_contains(getSmartSet(isPreviousCharacter), c); -} - -} - -#endif // !PLATFORM(CF) && USE(ICU_UNICODE) diff --git a/WebCore/editing/SplitElementCommand.cpp b/WebCore/editing/SplitElementCommand.cpp deleted file mode 100644 index b056fee..0000000 --- a/WebCore/editing/SplitElementCommand.cpp +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright (C) 2005, 2008 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 "SplitElementCommand.h" - -#include "Element.h" -#include <wtf/Assertions.h> - -namespace WebCore { - -SplitElementCommand::SplitElementCommand(PassRefPtr<Element> element, PassRefPtr<Node> atChild) - : SimpleEditCommand(element->document()), m_element2(element), m_atChild(atChild) -{ - ASSERT(m_element2); - ASSERT(m_atChild); -} - -void SplitElementCommand::doApply() -{ - ASSERT(m_element2); - ASSERT(m_atChild); - ASSERT(m_atChild->parentNode() == m_element2); - - ExceptionCode ec = 0; - - if (!m_element1) { - // create only if needed. - // if reapplying, this object will already exist. - m_element1 = static_pointer_cast<Element>(m_element2->cloneNode(false)); - ASSERT(m_element1); - } - - m_element2->parent()->insertBefore(m_element1.get(), m_element2.get(), ec); - ASSERT(ec == 0); - - // Bail if we were asked to split at a bogus child, to avoid hanging below. - if (!m_atChild || m_atChild->parentNode() != m_element2) - return; - - while (m_element2->firstChild() != m_atChild) { - ASSERT(m_element2->firstChild()); - m_element1->appendChild(m_element2->firstChild(), ec); - ASSERT(ec == 0); - } -} - -void SplitElementCommand::doUnapply() -{ - ASSERT(m_element1); - ASSERT(m_element2); - ASSERT(m_atChild); - - ASSERT(m_element1->nextSibling() == m_element2); - ASSERT(m_element2->firstChild() && m_element2->firstChild() == m_atChild); - - ExceptionCode ec = 0; - - while (m_element1->lastChild()) { - m_element2->insertBefore(m_element1->lastChild(), m_element2->firstChild(), ec); - ASSERT(ec == 0); - } - - m_element2->parentNode()->removeChild(m_element1.get(), ec); - ASSERT(ec == 0); -} - -} // namespace WebCore diff --git a/WebCore/editing/SplitElementCommand.h b/WebCore/editing/SplitElementCommand.h deleted file mode 100644 index 2732762..0000000 --- a/WebCore/editing/SplitElementCommand.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (C) 2005, 2006, 2008 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 SplitElementCommand_h -#define SplitElementCommand_h - -#include "EditCommand.h" - -namespace WebCore { - -class SplitElementCommand : public SimpleEditCommand { -public: - static PassRefPtr<SplitElementCommand> create(PassRefPtr<Element> element, PassRefPtr<Node> splitPointChild) - { - return adoptRef(new SplitElementCommand(element, splitPointChild)); - } - -private: - SplitElementCommand(PassRefPtr<Element>, PassRefPtr<Node> splitPointChild); - - virtual void doApply(); - virtual void doUnapply(); - - RefPtr<Element> m_element1; - RefPtr<Element> m_element2; - RefPtr<Node> m_atChild; -}; - -} // namespace WebCore - -#endif // SplitElementCommand_h diff --git a/WebCore/editing/SplitTextNodeCommand.cpp b/WebCore/editing/SplitTextNodeCommand.cpp deleted file mode 100644 index be6cd1d..0000000 --- a/WebCore/editing/SplitTextNodeCommand.cpp +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright (C) 2005, 2008 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 "SplitTextNodeCommand.h" - -#include "Document.h" -#include "Text.h" -#include <wtf/Assertions.h> - -namespace WebCore { - -SplitTextNodeCommand::SplitTextNodeCommand(PassRefPtr<Text> text, int offset) - : SimpleEditCommand(text->document()), m_text2(text), m_offset(offset) -{ - ASSERT(m_text2); - ASSERT(m_text2->length() > 0); -} - -void SplitTextNodeCommand::doApply() -{ - ASSERT(m_text2); - ASSERT(m_offset > 0); - - ExceptionCode ec = 0; - - // NOTE: Various callers rely on the fact that the original node becomes - // the second node (i.e. the new node is inserted before the existing one). - // That is not a fundamental dependency (i.e. it could be re-coded), but - // rather is based on how this code happens to work. - if (!m_text1) { - // create only if needed. - // if reapplying, this object will already exist. - m_text1 = document()->createTextNode(m_text2->substringData(0, m_offset, ec)); - ASSERT(ec == 0); - ASSERT(m_text1); - } - - document()->copyMarkers(m_text2.get(), 0, m_offset, m_text1.get(), 0); - m_text2->deleteData(0, m_offset, ec); - ASSERT(ec == 0); - - m_text2->parentNode()->insertBefore(m_text1.get(), m_text2.get(), ec); - ASSERT(ec == 0); - - ASSERT(m_text2->previousSibling()->isTextNode()); - ASSERT(m_text2->previousSibling() == m_text1); -} - -void SplitTextNodeCommand::doUnapply() -{ - ASSERT(m_text1); - ASSERT(m_text2); - ASSERT(m_text1->nextSibling() == m_text2); - - ExceptionCode ec = 0; - m_text2->insertData(0, m_text1->data(), ec); - ASSERT(ec == 0); - - document()->copyMarkers(m_text1.get(), 0, m_offset, m_text2.get(), 0); - - m_text2->parentNode()->removeChild(m_text1.get(), ec); - ASSERT(ec == 0); - - m_offset = m_text1->length(); -} - -} // namespace WebCore diff --git a/WebCore/editing/SplitTextNodeCommand.h b/WebCore/editing/SplitTextNodeCommand.h deleted file mode 100644 index 48d444c..0000000 --- a/WebCore/editing/SplitTextNodeCommand.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (C) 2005, 2006, 2008 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 SplitTextNodeCommand_h -#define SplitTextNodeCommand_h - -#include "EditCommand.h" - -namespace WebCore { - -class Text; - -class SplitTextNodeCommand : public SimpleEditCommand { -public: - static PassRefPtr<SplitTextNodeCommand> create(PassRefPtr<Text> node, int offset) - { - return adoptRef(new SplitTextNodeCommand(node, offset)); - } - -private: - SplitTextNodeCommand(PassRefPtr<Text>, int offset); - - virtual void doApply(); - virtual void doUnapply(); - - RefPtr<Text> m_text1; - RefPtr<Text> m_text2; - unsigned m_offset; -}; - -} // namespace WebCore - -#endif // SplitTextNodeCommand_h diff --git a/WebCore/editing/SplitTextNodeContainingElementCommand.cpp b/WebCore/editing/SplitTextNodeContainingElementCommand.cpp deleted file mode 100644 index 7038a17..0000000 --- a/WebCore/editing/SplitTextNodeContainingElementCommand.cpp +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (C) 2005 Apple Computer, 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 "SplitTextNodeContainingElementCommand.h" - -#include "Element.h" -#include "Text.h" -#include "RenderObject.h" -#include <wtf/Assertions.h> - -namespace WebCore { - -SplitTextNodeContainingElementCommand::SplitTextNodeContainingElementCommand(PassRefPtr<Text> text, int offset) - : CompositeEditCommand(text->document()), m_text(text), m_offset(offset) -{ - ASSERT(m_text); - ASSERT(m_text->length() > 0); -} - -void SplitTextNodeContainingElementCommand::doApply() -{ - ASSERT(m_text); - ASSERT(m_offset > 0); - - splitTextNode(m_text.get(), m_offset); - - Node *parentNode = m_text->parentNode(); - if (!parentNode->renderer() || !parentNode->renderer()->isInline()) { - wrapContentsInDummySpan(static_cast<Element *>(parentNode)); - parentNode = parentNode->firstChild(); - } - - splitElement(static_cast<Element *>(parentNode), m_text.get()); -} - -} diff --git a/WebCore/editing/SplitTextNodeContainingElementCommand.h b/WebCore/editing/SplitTextNodeContainingElementCommand.h deleted file mode 100644 index 4e6af4f..0000000 --- a/WebCore/editing/SplitTextNodeContainingElementCommand.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (C) 2005, 2006, 2008 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 SplitTextNodeContainingElementCommand_h -#define SplitTextNodeContainingElementCommand_h - -#include "CompositeEditCommand.h" - -namespace WebCore { - -class SplitTextNodeContainingElementCommand : public CompositeEditCommand { -public: - static PassRefPtr<SplitTextNodeContainingElementCommand> create(PassRefPtr<Text> node, int offset) - { - return adoptRef(new SplitTextNodeContainingElementCommand(node, offset)); - } - -private: - SplitTextNodeContainingElementCommand(PassRefPtr<Text>, int offset); - - virtual void doApply(); - - RefPtr<Text> m_text; - int m_offset; -}; - -} // namespace WebCore - -#endif // SplitTextNodeContainingElementCommand_h diff --git a/WebCore/editing/TextAffinity.h b/WebCore/editing/TextAffinity.h deleted file mode 100644 index 5562cc4..0000000 --- a/WebCore/editing/TextAffinity.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (C) 2004 Apple Computer, 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 TextAffinity_h -#define TextAffinity_h - -#include <wtf/Platform.h> - -#ifdef __OBJC__ -#include <AppKit/NSTextView.h> -#endif - -namespace WebCore { - -// These match the AppKit values for these concepts. -// From NSTextView.h: -// NSSelectionAffinityUpstream = 0 -// NSSelectionAffinityDownstream = 1 -typedef enum { UPSTREAM = 0, DOWNSTREAM = 1 } EAffinity; - -#ifdef __OBJC__ -inline NSSelectionAffinity kit(EAffinity affinity) -{ - return static_cast<NSSelectionAffinity>(affinity); -} - -inline EAffinity core(NSSelectionAffinity affinity) -{ - return static_cast<EAffinity>(affinity); -} -#endif - -} // namespace WebCore - -#endif // TextAffinity_h diff --git a/WebCore/editing/TextGranularity.h b/WebCore/editing/TextGranularity.h deleted file mode 100644 index 09cc4ed..0000000 --- a/WebCore/editing/TextGranularity.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (C) 2004 Apple Computer, 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 TextGranularity_h -#define TextGranularity_h - -namespace WebCore { - -// FIXME: This really should be broken up into more than one concept. -// Frame doesn't need the 3 boundaries in this enum. -enum TextGranularity { - CharacterGranularity, - WordGranularity, - SentenceGranularity, - LineGranularity, - ParagraphGranularity, - SentenceBoundary, - LineBoundary, - ParagraphBoundary, - DocumentBoundary -}; - -} - -#endif diff --git a/WebCore/editing/TextIterator.cpp b/WebCore/editing/TextIterator.cpp deleted file mode 100644 index 093e851..0000000 --- a/WebCore/editing/TextIterator.cpp +++ /dev/null @@ -1,1445 +0,0 @@ -/* - * Copyright (C) 2004, 2005, 2006, 2007 Apple Inc. All rights reserved. - * Copyright (C) 2005 Alexey Proskuryakov. - * - * 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 "TextIterator.h" - -#include "CharacterNames.h" -#include "Document.h" -#include "Element.h" -#include "HTMLNames.h" -#include "htmlediting.h" -#include "InlineTextBox.h" -#include "Position.h" -#include "Range.h" -#include "RenderTableCell.h" -#include "RenderTableRow.h" -#include "RenderTextControl.h" -#include "visible_units.h" - -using namespace std; -using namespace WTF::Unicode; - -namespace WebCore { - -using namespace HTMLNames; - -// Buffer that knows how to compare with a search target. -// Keeps enough of the previous text to be able to search in the future, but no more. -class CircularSearchBuffer : Noncopyable { -public: - CircularSearchBuffer(const String& target, bool isCaseSensitive); - - void clear() { m_cursor = 0; m_isBufferFull = false; } - void append(UChar); - - bool isMatch() const; - unsigned length() const; - -private: - void append(UChar, bool isCharacterStart); - - String m_target; - bool m_isCaseSensitive; - - Vector<UChar> m_characterBuffer; - Vector<bool> m_isCharacterStartBuffer; - bool m_isBufferFull; - unsigned m_cursor; -}; - -// -------- - -TextIterator::TextIterator() - : m_startContainer(0) - , m_startOffset(0) - , m_endContainer(0) - , m_endOffset(0) - , m_positionNode(0) - , m_lastCharacter(0) - , m_emitCharactersBetweenAllVisiblePositions(false) - , m_enterTextControls(false) -{ -} - -TextIterator::TextIterator(const Range* r, bool emitCharactersBetweenAllVisiblePositions, bool enterTextControls) - : m_inShadowContent(false) - , m_startContainer(0) - , m_startOffset(0) - , m_endContainer(0) - , m_endOffset(0) - , m_positionNode(0) - , m_emitCharactersBetweenAllVisiblePositions(emitCharactersBetweenAllVisiblePositions) - , m_enterTextControls(enterTextControls) -{ - if (!r) - return; - - ExceptionCode ec = 0; - - // get and validate the range endpoints - Node *startContainer = r->startContainer(ec); - int startOffset = r->startOffset(ec); - Node *endContainer = r->endContainer(ec); - int endOffset = r->endOffset(ec); - if (ec) - return; - - // Callers should be handing us well-formed ranges. If we discover that this isn't - // the case, we could consider changing this assertion to an early return. - ASSERT(r->boundaryPointsValid()); - - // remember range - this does not change - m_startContainer = startContainer; - m_startOffset = startOffset; - m_endContainer = endContainer; - m_endOffset = endOffset; - - for (Node* n = startContainer; n; n = n->parentNode()) { - if (n->isShadowNode()) { - m_inShadowContent = true; - break; - } - } - - // set up the current node for processing - m_node = r->firstNode(); - if (m_node == 0) - return; - m_offset = m_node == m_startContainer ? m_startOffset : 0; - m_handledNode = false; - m_handledChildren = false; - - // calculate first out of bounds node - m_pastEndNode = r->pastLastNode(); - - // initialize node processing state - m_needAnotherNewline = false; - m_textBox = 0; - - // initialize record of previous node processing - m_haveEmitted = false; - m_lastTextNode = 0; - m_lastTextNodeEndedWithCollapsedSpace = false; - m_lastCharacter = 0; - -#ifndef NDEBUG - // need this just because of the assert in advance() - m_positionNode = m_node; -#endif - - // identify the first run - advance(); -} - -void TextIterator::advance() -{ - // reset the run information - m_positionNode = 0; - m_textLength = 0; - - // handle remembered node that needed a newline after the text node's newline - if (m_needAnotherNewline) { - // Emit the extra newline, and position it *inside* m_node, after m_node's - // contents, in case it's a block, in the same way that we position the first - // newline. The range for the emitted newline should start where the line - // break begins. - // FIXME: It would be cleaner if we emitted two newlines during the last - // iteration, instead of using m_needAnotherNewline. - Node* baseNode = m_node->lastChild() ? m_node->lastChild() : m_node; - emitCharacter('\n', baseNode->parentNode(), baseNode, 1, 1); - m_needAnotherNewline = false; - return; - } - - // handle remembered text box - if (m_textBox) { - handleTextBox(); - if (m_positionNode) - return; - } - - while (m_node && m_node != m_pastEndNode) { - // if the range ends at offset 0 of an element, represent the - // position, but not the content, of that element e.g. if the - // node is a blockflow element, emit a newline that - // precedes the element - if (m_node == m_endContainer && m_endOffset == 0) { - representNodeOffsetZero(); - m_node = 0; - return; - } - - RenderObject *renderer = m_node->renderer(); - if (!renderer) { - m_handledNode = true; - m_handledChildren = true; - } else { - // handle current node according to its type - if (!m_handledNode) { - if (renderer->isText() && m_node->nodeType() == Node::TEXT_NODE) // FIXME: What about CDATA_SECTION_NODE? - m_handledNode = handleTextNode(); - else if (renderer && (renderer->isImage() || renderer->isWidget() || (renderer->element() && renderer->element()->isControl()))) - m_handledNode = handleReplacedElement(); - else - m_handledNode = handleNonTextNode(); - if (m_positionNode) - return; - } - } - - // find a new current node to handle in depth-first manner, - // calling exitNode() as we come back thru a parent node - Node *next = m_handledChildren ? 0 : m_node->firstChild(); - m_offset = 0; - if (!next) { - next = m_node->nextSibling(); - if (!next) { - bool pastEnd = m_node->traverseNextNode() == m_pastEndNode; - Node* parentNode = m_node->parentNode(); - if (!parentNode && m_inShadowContent) { - m_inShadowContent = false; - parentNode = m_node->shadowParentNode(); - } - while (!next && parentNode) { - if (pastEnd && parentNode == m_endContainer || m_endContainer->isDescendantOf(parentNode)) - return; - bool haveRenderer = m_node->renderer(); - m_node = parentNode; - parentNode = m_node->parentNode(); - if (!parentNode && m_inShadowContent) { - m_inShadowContent = false; - parentNode = m_node->shadowParentNode(); - } - if (haveRenderer) - exitNode(); - if (m_positionNode) { - m_handledNode = true; - m_handledChildren = true; - return; - } - next = m_node->nextSibling(); - } - } - } - - // set the new current node - m_node = next; - m_handledNode = false; - m_handledChildren = false; - - // how would this ever be? - if (m_positionNode) - return; - } -} - -static inline bool compareBoxStart(const InlineTextBox *first, const InlineTextBox *second) -{ - return first->start() < second->start(); -} - -bool TextIterator::handleTextNode() -{ - RenderText* renderer = static_cast<RenderText*>(m_node->renderer()); - if (renderer->style()->visibility() != VISIBLE) - return false; - - m_lastTextNode = m_node; - String str = renderer->text(); - - // handle pre-formatted text - if (!renderer->style()->collapseWhiteSpace()) { - int runStart = m_offset; - if (m_lastTextNodeEndedWithCollapsedSpace) { - emitCharacter(' ', m_node, 0, runStart, runStart); - return false; - } - int strLength = str.length(); - int end = (m_node == m_endContainer) ? m_endOffset : INT_MAX; - int runEnd = min(strLength, end); - - if (runStart >= runEnd) - return true; - - emitText(m_node, runStart, runEnd); - return true; - } - - if (!renderer->firstTextBox() && str.length() > 0) { - m_lastTextNodeEndedWithCollapsedSpace = true; // entire block is collapsed space - return true; - } - - // Used when text boxes are out of order (Hebrew/Arabic w/ embeded LTR text) - if (renderer->containsReversedText()) { - m_sortedTextBoxes.clear(); - for (InlineTextBox * textBox = renderer->firstTextBox(); textBox; textBox = textBox->nextTextBox()) { - m_sortedTextBoxes.append(textBox); - } - std::sort(m_sortedTextBoxes.begin(), m_sortedTextBoxes.end(), compareBoxStart); - m_sortedTextBoxesPosition = 0; - } - - m_textBox = renderer->containsReversedText() ? m_sortedTextBoxes[0] : renderer->firstTextBox(); - handleTextBox(); - return true; -} - -void TextIterator::handleTextBox() -{ - RenderText *renderer = static_cast<RenderText *>(m_node->renderer()); - String str = renderer->text(); - int start = m_offset; - int end = (m_node == m_endContainer) ? m_endOffset : INT_MAX; - while (m_textBox) { - int textBoxStart = m_textBox->m_start; - int runStart = max(textBoxStart, start); - - // Check for collapsed space at the start of this run. - InlineTextBox *firstTextBox = renderer->containsReversedText() ? m_sortedTextBoxes[0] : renderer->firstTextBox(); - bool needSpace = m_lastTextNodeEndedWithCollapsedSpace - || (m_textBox == firstTextBox && textBoxStart == runStart && runStart > 0); - if (needSpace && !isCollapsibleWhitespace(m_lastCharacter) && m_lastCharacter) { - emitCharacter(' ', m_node, 0, runStart, runStart); - return; - } - int textBoxEnd = textBoxStart + m_textBox->m_len; - int runEnd = min(textBoxEnd, end); - - // Determine what the next text box will be, but don't advance yet - InlineTextBox *nextTextBox = 0; - if (renderer->containsReversedText()) { - if (m_sortedTextBoxesPosition + 1 < m_sortedTextBoxes.size()) - nextTextBox = m_sortedTextBoxes[m_sortedTextBoxesPosition + 1]; - } else - nextTextBox = m_textBox->nextTextBox(); - - if (runStart < runEnd) { - // Handle either a single newline character (which becomes a space), - // or a run of characters that does not include a newline. - // This effectively translates newlines to spaces without copying the text. - if (str[runStart] == '\n') { - emitCharacter(' ', m_node, 0, runStart, runStart + 1); - m_offset = runStart + 1; - } else { - int subrunEnd = str.find('\n', runStart); - if (subrunEnd == -1 || subrunEnd > runEnd) - subrunEnd = runEnd; - - m_offset = subrunEnd; - emitText(m_node, runStart, subrunEnd); - } - - // If we are doing a subrun that doesn't go to the end of the text box, - // come back again to finish handling this text box; don't advance to the next one. - if (m_positionEndOffset < textBoxEnd) - return; - - // Advance and return - int nextRunStart = nextTextBox ? nextTextBox->m_start : str.length(); - if (nextRunStart > runEnd) - m_lastTextNodeEndedWithCollapsedSpace = true; // collapsed space between runs or at the end - m_textBox = nextTextBox; - if (renderer->containsReversedText()) - ++m_sortedTextBoxesPosition; - return; - } - // Advance and continue - m_textBox = nextTextBox; - if (renderer->containsReversedText()) - ++m_sortedTextBoxesPosition; - } -} - -bool TextIterator::handleReplacedElement() -{ - RenderObject* renderer = m_node->renderer(); - if (renderer->style()->visibility() != VISIBLE) - return false; - - if (m_lastTextNodeEndedWithCollapsedSpace) { - emitCharacter(' ', m_lastTextNode->parentNode(), m_lastTextNode, 1, 1); - return false; - } - - if (m_enterTextControls && (renderer->isTextArea() || renderer->isTextField())) { - m_node = static_cast<RenderTextControl*>(renderer)->innerTextElement(); - m_offset = 0; - m_inShadowContent = true; - return false; - } - - m_haveEmitted = true; - - if (m_emitCharactersBetweenAllVisiblePositions) { - // We want replaced elements to behave like punctuation for boundary - // finding, and to simply take up space for the selection preservation - // code in moveParagraphs, so we use a comma. - emitCharacter(',', m_node->parentNode(), m_node, 0, 1); - return true; - } - - m_positionNode = m_node->parentNode(); - m_positionOffsetBaseNode = m_node; - m_positionStartOffset = 0; - m_positionEndOffset = 1; - - m_textCharacters = 0; - m_textLength = 0; - - m_lastCharacter = 0; - - return true; -} - -static bool shouldEmitTabBeforeNode(Node* node) -{ - RenderObject* r = node->renderer(); - - // Table cells are delimited by tabs. - if (!r || !isTableCell(node)) - return false; - - // Want a tab before every cell other than the first one - RenderTableCell* rc = static_cast<RenderTableCell*>(r); - RenderTable* t = rc->table(); - return t && (t->cellBefore(rc) || t->cellAbove(rc)); -} - -static bool shouldEmitNewlineForNode(Node* node) -{ - // br elements are represented by a single newline. - RenderObject* r = node->renderer(); - if (!r) - return node->hasTagName(brTag); - - return r->isBR(); -} - -static bool shouldEmitNewlinesBeforeAndAfterNode(Node* node) -{ - // Block flow (versus inline flow) is represented by having - // a newline both before and after the element. - RenderObject* r = node->renderer(); - if (!r) { - return (node->hasTagName(blockquoteTag) - || node->hasTagName(ddTag) - || node->hasTagName(divTag) - || node->hasTagName(dlTag) - || node->hasTagName(dtTag) - || node->hasTagName(h1Tag) - || node->hasTagName(h2Tag) - || node->hasTagName(h3Tag) - || node->hasTagName(h4Tag) - || node->hasTagName(h5Tag) - || node->hasTagName(h6Tag) - || node->hasTagName(hrTag) - || node->hasTagName(liTag) - || node->hasTagName(listingTag) - || node->hasTagName(olTag) - || node->hasTagName(pTag) - || node->hasTagName(preTag) - || node->hasTagName(trTag) - || node->hasTagName(ulTag)); - } - - // Need to make an exception for table cells, because they are blocks, but we - // want them tab-delimited rather than having newlines before and after. - if (isTableCell(node)) - return false; - - // Need to make an exception for table row elements, because they are neither - // "inline" or "RenderBlock", but we want newlines for them. - if (r->isTableRow()) { - RenderTable* t = static_cast<RenderTableRow*>(r)->table(); - if (t && !t->isInline()) - return true; - } - - return !r->isInline() && r->isRenderBlock() && !r->isFloatingOrPositioned() && !r->isBody(); -} - -static bool shouldEmitNewlineAfterNode(Node* node) -{ - // FIXME: It should be better but slower to create a VisiblePosition here. - if (!shouldEmitNewlinesBeforeAndAfterNode(node)) - return false; - // Check if this is the very last renderer in the document. - // If so, then we should not emit a newline. - while ((node = node->traverseNextSibling())) - if (node->renderer()) - return true; - return false; -} - -static bool shouldEmitNewlineBeforeNode(Node* node) -{ - return shouldEmitNewlinesBeforeAndAfterNode(node); -} - -static bool shouldEmitExtraNewlineForNode(Node* node) -{ - // When there is a significant collapsed bottom margin, emit an extra - // newline for a more realistic result. We end up getting the right - // result even without margin collapsing. For example: <div><p>text</p></div> - // will work right even if both the <div> and the <p> have bottom margins. - RenderObject* r = node->renderer(); - if (!r) - return false; - - // NOTE: We only do this for a select set of nodes, and fwiw WinIE appears - // not to do this at all - if (node->hasTagName(h1Tag) - || node->hasTagName(h2Tag) - || node->hasTagName(h3Tag) - || node->hasTagName(h4Tag) - || node->hasTagName(h5Tag) - || node->hasTagName(h6Tag) - || node->hasTagName(pTag)) { - RenderStyle* style = r->style(); - if (style) { - int bottomMargin = r->collapsedMarginBottom(); - int fontSize = style->fontDescription().computedPixelSize(); - if (bottomMargin * 2 >= fontSize) - return true; - } - } - - return false; -} - -bool TextIterator::shouldRepresentNodeOffsetZero() -{ - if (m_emitCharactersBetweenAllVisiblePositions && m_node->renderer() && m_node->renderer()->isTable()) - return true; - - // Leave element positioned flush with start of a paragraph - // (e.g. do not insert tab before a table cell at the start of a paragraph) - if (m_lastCharacter == '\n') - return false; - - // Otherwise, show the position if we have emitted any characters - if (m_haveEmitted) - return true; - - // We've not emitted anything yet. Generally, there is no need for any positioning then. - // The only exception is when the element is visually not in the same line as - // the start of the range (e.g. the range starts at the end of the previous paragraph). - // NOTE: Creating VisiblePositions and comparing them is relatively expensive, so we - // make quicker checks to possibly avoid that. Another check that we could make is - // is whether the inline vs block flow changed since the previous visible element. - // I think we're already in a special enough case that that won't be needed, tho. - - // If we are at the start, obviously no newline is needed. - if (m_node == m_startContainer) - return false; - - // If we are outside the start container's subtree, assume we need a newline. - // FIXME: m_startContainer could be an inline block - if (!m_node->isDescendantOf(m_startContainer)) - return true; - - // If we started as m_startContainer offset 0 and the current node is a descendant of - // the start container, we already had enough context to correctly decide whether to - // emit a newline after a preceding block. We chose not to emit (m_haveEmitted is false), - // so don't second guess that now. - // NOTE: Is this really correct when m_node is not a leftmost descendant? Probably - // immaterial since we likely would have already emitted something by now. - if (m_startOffset == 0) - return false; - - // 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); - return currPos.isNotNull() && !inSameLine(startPos, currPos); -} - -bool TextIterator::shouldEmitSpaceBeforeAndAfterNode(Node* node) -{ - return node->renderer() && node->renderer()->isTable() && (node->renderer()->isInline() || m_emitCharactersBetweenAllVisiblePositions); -} - -void TextIterator::representNodeOffsetZero() -{ - // Emit a character to show the positioning of m_node. - - // When we haven't been emitting any characters, shouldRepresentNodeOffsetZero() can - // create VisiblePositions, which is expensive. So, we perform the inexpensive checks - // on m_node to see if it necessitates emitting a character first and will early return - // before encountering shouldRepresentNodeOffsetZero()s worse case behavior. - if (shouldEmitTabBeforeNode(m_node)) { - if (shouldRepresentNodeOffsetZero()) - emitCharacter('\t', m_node->parentNode(), m_node, 0, 0); - } else if (shouldEmitNewlineBeforeNode(m_node)) { - if (shouldRepresentNodeOffsetZero()) - emitCharacter('\n', m_node->parentNode(), m_node, 0, 0); - } else if (shouldEmitSpaceBeforeAndAfterNode(m_node)) { - if (shouldRepresentNodeOffsetZero()) - emitCharacter(' ', m_node->parentNode(), m_node, 0, 0); - } -} - -bool TextIterator::handleNonTextNode() -{ - if (shouldEmitNewlineForNode(m_node)) - emitCharacter('\n', m_node->parentNode(), m_node, 0, 1); - else if (m_emitCharactersBetweenAllVisiblePositions && m_node->renderer() && m_node->renderer()->isHR()) - emitCharacter(' ', m_node->parentNode(), m_node, 0, 1); - else - representNodeOffsetZero(); - - return true; -} - -void TextIterator::exitNode() -{ - // prevent emitting a newline when exiting a collapsed block at beginning of the range - // FIXME: !m_haveEmitted does not necessarily mean there was a collapsed block... it could - // have been an hr (e.g.). Also, a collapsed block could have height (e.g. a table) and - // therefore look like a blank line. - if (!m_haveEmitted) - return; - - // Emit with a position *inside* m_node, after m_node's contents, in - // case it is a block, because the run should start where the - // emitted character is positioned visually. - Node* baseNode = m_node->lastChild() ? m_node->lastChild() : m_node; - // FIXME: This shouldn't require the m_lastTextNode to be true, but we can't change that without making - // the logic in _web_attributedStringFromRange match. We'll get that for free when we switch to use - // TextIterator in _web_attributedStringFromRange. - // See <rdar://problem/5428427> for an example of how this mismatch will cause problems. - if (m_lastTextNode && shouldEmitNewlineAfterNode(m_node)) { - // use extra newline to represent margin bottom, as needed - bool addNewline = shouldEmitExtraNewlineForNode(m_node); - - // FIXME: We need to emit a '\n' as we leave an empty block(s) that - // contain a VisiblePosition when doing selection preservation. - if (m_lastCharacter != '\n') { - // insert a newline with a position following this block's contents. - emitCharacter('\n', baseNode->parentNode(), baseNode, 1, 1); - // remember whether to later add a newline for the current node - ASSERT(!m_needAnotherNewline); - m_needAnotherNewline = addNewline; - } else if (addNewline) - // insert a newline with a position following this block's contents. - emitCharacter('\n', baseNode->parentNode(), baseNode, 1, 1); - } - - // If nothing was emitted, see if we need to emit a space. - if (!m_positionNode && shouldEmitSpaceBeforeAndAfterNode(m_node)) - emitCharacter(' ', baseNode->parentNode(), baseNode, 1, 1); -} - -void TextIterator::emitCharacter(UChar c, Node *textNode, Node *offsetBaseNode, int textStartOffset, int textEndOffset) -{ - m_haveEmitted = true; - - // remember information with which to construct the TextIterator::range() - // NOTE: textNode is often not a text node, so the range will specify child nodes of positionNode - m_positionNode = textNode; - m_positionOffsetBaseNode = offsetBaseNode; - m_positionStartOffset = textStartOffset; - m_positionEndOffset = textEndOffset; - - // remember information with which to construct the TextIterator::characters() and length() - m_singleCharacterBuffer = c; - m_textCharacters = &m_singleCharacterBuffer; - m_textLength = 1; - - // remember some iteration state - m_lastTextNodeEndedWithCollapsedSpace = false; - m_lastCharacter = c; -} - -void TextIterator::emitText(Node *textNode, int textStartOffset, int textEndOffset) -{ - RenderText* renderer = static_cast<RenderText*>(m_node->renderer()); - String str = renderer->text(); - - m_positionNode = textNode; - m_positionOffsetBaseNode = 0; - m_positionStartOffset = textStartOffset; - m_positionEndOffset = textEndOffset; - m_textCharacters = str.characters() + textStartOffset; - m_textLength = textEndOffset - textStartOffset; - m_lastCharacter = str[textEndOffset - 1]; - - m_lastTextNodeEndedWithCollapsedSpace = false; - m_haveEmitted = true; -} - -PassRefPtr<Range> TextIterator::range() const -{ - // use the current run information, if we have it - if (m_positionNode) { - if (m_positionOffsetBaseNode) { - int index = m_positionOffsetBaseNode->nodeIndex(); - m_positionStartOffset += index; - m_positionEndOffset += index; - m_positionOffsetBaseNode = 0; - } - return Range::create(m_positionNode->document(), m_positionNode, m_positionStartOffset, m_positionNode, m_positionEndOffset); - } - - // otherwise, return the end of the overall range we were given - if (m_endContainer) - return Range::create(m_endContainer->document(), m_endContainer, m_endOffset, m_endContainer, m_endOffset); - - return 0; -} - -Node* TextIterator::node() const -{ - RefPtr<Range> textRange = range(); - if (!textRange) - return 0; - - Node* node = textRange->startContainer(); - if (!node) - return 0; - if (node->offsetInCharacters()) - return node; - - return node->childNode(textRange->startOffset()); -} - -// -------- - -SimplifiedBackwardsTextIterator::SimplifiedBackwardsTextIterator() : m_positionNode(0) -{ -} - -SimplifiedBackwardsTextIterator::SimplifiedBackwardsTextIterator(const Range *r) -{ - m_positionNode = 0; - - if (!r) - return; - - int exception = 0; - Node *startNode = r->startContainer(exception); - if (exception) - return; - Node *endNode = r->endContainer(exception); - if (exception) - return; - int startOffset = r->startOffset(exception); - if (exception) - return; - int endOffset = r->endOffset(exception); - if (exception) - return; - - if (!startNode->offsetInCharacters()) { - if (startOffset >= 0 && startOffset < static_cast<int>(startNode->childNodeCount())) { - startNode = startNode->childNode(startOffset); - startOffset = 0; - } - } - if (!endNode->offsetInCharacters()) { - if (endOffset > 0 && endOffset <= static_cast<int>(endNode->childNodeCount())) { - endNode = endNode->childNode(endOffset - 1); - endOffset = endNode->offsetInCharacters() ? endNode->maxCharacterOffset() : endNode->childNodeCount(); - } - } - - m_node = endNode; - m_offset = endOffset; - m_handledNode = false; - m_handledChildren = endOffset == 0; - - m_startNode = startNode; - m_startOffset = startOffset; - m_endNode = endNode; - m_endOffset = endOffset; - -#ifndef NDEBUG - // Need this just because of the assert. - m_positionNode = endNode; -#endif - - m_lastTextNode = 0; - m_lastCharacter = '\n'; - - if (startOffset == 0 || !startNode->firstChild()) { - m_pastStartNode = startNode->previousSibling(); - while (!m_pastStartNode && startNode->parentNode()) { - startNode = startNode->parentNode(); - m_pastStartNode = startNode->previousSibling(); - } - } else - m_pastStartNode = startNode->childNode(startOffset - 1); - - advance(); -} - -void SimplifiedBackwardsTextIterator::advance() -{ - ASSERT(m_positionNode); - - m_positionNode = 0; - m_textLength = 0; - - while (m_node && m_node != m_pastStartNode) { - // 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(); - if (renderer && renderer->isText() && m_node->nodeType() == Node::TEXT_NODE) { - // FIXME: What about CDATA_SECTION_NODE? - if (renderer->style()->visibility() == VISIBLE && m_offset > 0) - m_handledNode = handleTextNode(); - } else if (renderer && (renderer->isImage() || renderer->isWidget())) { - if (renderer->style()->visibility() == VISIBLE && m_offset > 0) - m_handledNode = handleReplacedElement(); - } else - m_handledNode = handleNonTextNode(); - if (m_positionNode) - return; - } - - Node* next = m_handledChildren ? 0 : m_node->lastChild(); - if (!next) { - // Exit empty containers as we pass over them or containers - // where [container, 0] is where we started iterating. - if (!m_handledNode && - canHaveChildrenForEditing(m_node) && - m_node->parentNode() && - (!m_node->lastChild() || m_node == m_endNode && m_endOffset == 0)) { - exitNode(); - if (m_positionNode) { - m_handledNode = true; - m_handledChildren = true; - return; - } - } - // Exit all other containers. - next = m_node->previousSibling(); - while (!next) { - if (!m_node->parentNode()) - break; - m_node = m_node->parentNode(); - exitNode(); - if (m_positionNode) { - m_handledNode = true; - m_handledChildren = true; - return; - } - next = m_node->previousSibling(); - } - } - - m_node = next; - m_offset = m_node ? caretMaxOffset(m_node) : 0; - m_handledNode = false; - m_handledChildren = false; - - if (m_positionNode) - return; - } -} - -bool SimplifiedBackwardsTextIterator::handleTextNode() -{ - m_lastTextNode = m_node; - - RenderText *renderer = static_cast<RenderText *>(m_node->renderer()); - String str = renderer->text(); - - if (!renderer->firstTextBox() && str.length() > 0) - return true; - - m_positionEndOffset = m_offset; - - m_offset = (m_node == m_startNode) ? m_startOffset : 0; - m_positionNode = m_node; - m_positionStartOffset = m_offset; - m_textLength = m_positionEndOffset - m_positionStartOffset; - m_textCharacters = str.characters() + m_positionStartOffset; - - m_lastCharacter = str[m_positionEndOffset - 1]; - - return true; -} - -bool SimplifiedBackwardsTextIterator::handleReplacedElement() -{ - unsigned index = m_node->nodeIndex(); - // We want replaced elements to behave like punctuation for boundary - // finding, and to simply take up space for the selection preservation - // code in moveParagraphs, so we use a comma. Unconditionally emit - // here because this iterator is only used for boundary finding. - emitCharacter(',', m_node->parentNode(), index, index + 1); - return true; -} - -bool SimplifiedBackwardsTextIterator::handleNonTextNode() -{ - // We can use a linefeed in place of a tab because this simple iterator is only used to - // find boundaries, not actual content. A linefeed breaks words, sentences, and paragraphs. - if (shouldEmitNewlineForNode(m_node) || - shouldEmitNewlineAfterNode(m_node) || - shouldEmitTabBeforeNode(m_node)) { - unsigned index = m_node->nodeIndex(); - // The start of this emitted range is wrong, ensuring correctness would require - // VisiblePositions and so would be slow. previousBoundary expects this. - emitCharacter('\n', m_node->parentNode(), index + 1, index + 1); - } - - return true; -} - -void SimplifiedBackwardsTextIterator::exitNode() -{ - if (shouldEmitNewlineForNode(m_node) || - shouldEmitNewlineBeforeNode(m_node) || - shouldEmitTabBeforeNode(m_node)) - // The start of this emitted range is wrong, ensuring correctness would require - // VisiblePositions and so would be slow. previousBoundary expects this. - emitCharacter('\n', m_node, 0, 0); -} - -void SimplifiedBackwardsTextIterator::emitCharacter(UChar c, Node *node, int startOffset, int endOffset) -{ - m_singleCharacterBuffer = c; - m_positionNode = node; - m_positionStartOffset = startOffset; - m_positionEndOffset = endOffset; - m_textCharacters = &m_singleCharacterBuffer; - m_textLength = 1; - m_lastCharacter = c; -} - -PassRefPtr<Range> SimplifiedBackwardsTextIterator::range() const -{ - if (m_positionNode) - return Range::create(m_positionNode->document(), m_positionNode, m_positionStartOffset, m_positionNode, m_positionEndOffset); - - return Range::create(m_startNode->document(), m_startNode, m_startOffset, m_startNode, m_startOffset); -} - -// -------- - -CharacterIterator::CharacterIterator() - : m_offset(0) - , m_runOffset(0) - , m_atBreak(true) -{ -} - -CharacterIterator::CharacterIterator(const Range *r, bool emitCharactersBetweenAllVisiblePositions, bool enterTextControls) - : m_offset(0) - , m_runOffset(0) - , m_atBreak(true) - , m_textIterator(r, emitCharactersBetweenAllVisiblePositions, enterTextControls) -{ - while (!atEnd() && m_textIterator.length() == 0) - m_textIterator.advance(); -} - -PassRefPtr<Range> CharacterIterator::range() const -{ - RefPtr<Range> r = m_textIterator.range(); - if (!m_textIterator.atEnd()) { - if (m_textIterator.length() <= 1) { - ASSERT(m_runOffset == 0); - } else { - int exception = 0; - Node *n = r->startContainer(exception); - ASSERT(n == r->endContainer(exception)); - int offset = r->startOffset(exception) + m_runOffset; - r->setStart(n, offset, exception); - r->setEnd(n, offset + 1, exception); - } - } - return r.release(); -} - -void CharacterIterator::advance(int count) -{ - if (count <= 0) { - ASSERT(count == 0); - return; - } - - m_atBreak = false; - - // easy if there is enough left in the current m_textIterator run - int remaining = m_textIterator.length() - m_runOffset; - if (count < remaining) { - m_runOffset += count; - m_offset += count; - return; - } - - // exhaust the current m_textIterator run - count -= remaining; - m_offset += remaining; - - // move to a subsequent m_textIterator run - for (m_textIterator.advance(); !atEnd(); m_textIterator.advance()) { - int runLength = m_textIterator.length(); - if (runLength == 0) - m_atBreak = true; - else { - // see whether this is m_textIterator to use - if (count < runLength) { - m_runOffset = count; - m_offset += count; - return; - } - - // exhaust this m_textIterator run - count -= runLength; - m_offset += runLength; - } - } - - // ran to the end of the m_textIterator... no more runs left - m_atBreak = true; - m_runOffset = 0; -} - -String CharacterIterator::string(int numChars) -{ - Vector<UChar> result; - result.reserveCapacity(numChars); - while (numChars > 0 && !atEnd()) { - int runSize = min(numChars, length()); - result.append(characters(), runSize); - numChars -= runSize; - advance(runSize); - } - return String::adopt(result); -} - -// -------- - -WordAwareIterator::WordAwareIterator() -: m_previousText(0), m_didLookAhead(false) -{ -} - -WordAwareIterator::WordAwareIterator(const Range *r) -: m_previousText(0), m_didLookAhead(false), m_textIterator(r) -{ - m_didLookAhead = true; // so we consider the first chunk from the text iterator - advance(); // get in position over the first chunk of text -} - -// We're always in one of these modes: -// - The current chunk in the text iterator is our current chunk -// (typically its a piece of whitespace, or text that ended with whitespace) -// - The previous chunk in the text iterator is our current chunk -// (we looked ahead to the next chunk and found a word boundary) -// - We built up our own chunk of text from many chunks from the text iterator - -// FIXME: Perf could be bad for huge spans next to each other that don't fall on word boundaries - -void WordAwareIterator::advance() -{ - m_previousText = 0; - m_buffer.clear(); // toss any old buffer we built up - - // If last time we did a look-ahead, start with that looked-ahead chunk now - if (!m_didLookAhead) { - ASSERT(!m_textIterator.atEnd()); - m_textIterator.advance(); - } - m_didLookAhead = false; - - // Go to next non-empty chunk - while (!m_textIterator.atEnd() && m_textIterator.length() == 0) - m_textIterator.advance(); - m_range = m_textIterator.range(); - - if (m_textIterator.atEnd()) - return; - - while (1) { - // If this chunk ends in whitespace we can just use it as our chunk. - if (isSpaceOrNewline(m_textIterator.characters()[m_textIterator.length() - 1])) - return; - - // If this is the first chunk that failed, save it in previousText before look ahead - if (m_buffer.isEmpty()) { - m_previousText = m_textIterator.characters(); - m_previousLength = m_textIterator.length(); - } - - // Look ahead to next chunk. If it is whitespace or a break, we can use the previous stuff - m_textIterator.advance(); - if (m_textIterator.atEnd() || m_textIterator.length() == 0 || isSpaceOrNewline(m_textIterator.characters()[0])) { - m_didLookAhead = true; - return; - } - - if (m_buffer.isEmpty()) { - // Start gobbling chunks until we get to a suitable stopping point - m_buffer.append(m_previousText, m_previousLength); - m_previousText = 0; - } - m_buffer.append(m_textIterator.characters(), m_textIterator.length()); - int exception = 0; - m_range->setEnd(m_textIterator.range()->endContainer(exception), m_textIterator.range()->endOffset(exception), exception); - } -} - -int WordAwareIterator::length() const -{ - if (!m_buffer.isEmpty()) - return m_buffer.size(); - if (m_previousText) - return m_previousLength; - return m_textIterator.length(); -} - -const UChar* WordAwareIterator::characters() const -{ - if (!m_buffer.isEmpty()) - return m_buffer.data(); - if (m_previousText) - return m_previousText; - return m_textIterator.characters(); -} - -// -------- - -CircularSearchBuffer::CircularSearchBuffer(const String& s, bool isCaseSensitive) - : m_target(isCaseSensitive ? s : s.foldCase()) - , m_isCaseSensitive(isCaseSensitive) - , m_characterBuffer(m_target.length()) - , m_isCharacterStartBuffer(m_target.length()) - , m_isBufferFull(false) - , m_cursor(0) -{ - ASSERT(!m_target.isEmpty()); - m_target.replace(noBreakSpace, ' '); -} - -inline void CircularSearchBuffer::append(UChar c, bool isStart) -{ - m_characterBuffer[m_cursor] = c == noBreakSpace ? ' ' : c; - m_isCharacterStartBuffer[m_cursor] = isStart; - if (++m_cursor == m_target.length()) { - m_cursor = 0; - m_isBufferFull = true; - } -} - -inline void CircularSearchBuffer::append(UChar c) -{ - if (m_isCaseSensitive) { - append(c, true); - return; - } - const int maxFoldedCharacters = 16; // sensible maximum is 3, this should be more than enough - UChar foldedCharacters[maxFoldedCharacters]; - bool error; - int numFoldedCharacters = foldCase(foldedCharacters, maxFoldedCharacters, &c, 1, &error); - ASSERT(!error); - ASSERT(numFoldedCharacters); - ASSERT(numFoldedCharacters <= maxFoldedCharacters); - if (!error && numFoldedCharacters) { - numFoldedCharacters = min(numFoldedCharacters, maxFoldedCharacters); - append(foldedCharacters[0], true); - for (int i = 1; i < numFoldedCharacters; ++i) - append(foldedCharacters[i], false); - } -} - -inline bool CircularSearchBuffer::isMatch() const -{ - if (!m_isBufferFull) - return false; - if (!m_isCharacterStartBuffer[m_cursor]) - return false; - - unsigned tailSpace = m_target.length() - m_cursor; - return memcmp(&m_characterBuffer[m_cursor], m_target.characters(), tailSpace * sizeof(UChar)) == 0 - && memcmp(&m_characterBuffer[0], m_target.characters() + tailSpace, m_cursor * sizeof(UChar)) == 0; -} - -// Returns the number of characters that were appended to the buffer (what we are searching in). -// That's not necessarily the same length as the passed-in target string, because case folding -// can make two strings match even though they're not the same length. -unsigned CircularSearchBuffer::length() const -{ - ASSERT(isMatch()); - - unsigned bufferSize = m_target.length(); - unsigned length = 0; - for (unsigned i = 0; i < bufferSize; ++i) - length += m_isCharacterStartBuffer[i]; - return length; -} - -// -------- - -int TextIterator::rangeLength(const Range *r, bool forSelectionPreservation) -{ - int length = 0; - for (TextIterator it(r, forSelectionPreservation); !it.atEnd(); it.advance()) - length += it.length(); - - return length; -} - -PassRefPtr<Range> TextIterator::subrange(Range* entireRange, int characterOffset, int characterCount) -{ - CharacterIterator chars(entireRange); - - chars.advance(characterOffset); - RefPtr<Range> start = chars.range(); - - chars.advance(characterCount); - RefPtr<Range> end = chars.range(); - - ExceptionCode ec = 0; - RefPtr<Range> result(Range::create(entireRange->ownerDocument(), - start->startContainer(ec), - start->startOffset(ec), - end->startContainer(ec), - end->startOffset(ec))); - ASSERT(!ec); - - return result.release(); -} - -PassRefPtr<Range> TextIterator::rangeFromLocationAndLength(Element *scope, int rangeLocation, int rangeLength, bool forSelectionPreservation) -{ - RefPtr<Range> resultRange = scope->document()->createRange(); - - int docTextPosition = 0; - int rangeEnd = rangeLocation + rangeLength; - bool startRangeFound = false; - - RefPtr<Range> textRunRange; - - TextIterator it(rangeOfContents(scope).get(), forSelectionPreservation); - - // FIXME: the atEnd() check shouldn't be necessary, workaround for <http://bugs.webkit.org/show_bug.cgi?id=6289>. - if (rangeLocation == 0 && rangeLength == 0 && it.atEnd()) { - int exception = 0; - textRunRange = it.range(); - - resultRange->setStart(textRunRange->startContainer(exception), 0, exception); - ASSERT(exception == 0); - resultRange->setEnd(textRunRange->startContainer(exception), 0, exception); - ASSERT(exception == 0); - - return resultRange.release(); - } - - for (; !it.atEnd(); it.advance()) { - int len = it.length(); - textRunRange = it.range(); - - bool foundStart = rangeLocation >= docTextPosition && rangeLocation <= docTextPosition + len; - bool foundEnd = rangeEnd >= docTextPosition && rangeEnd <= docTextPosition + len; - - // Fix textRunRange->endPosition(), but only if foundStart || foundEnd, because it is only - // in those cases that textRunRange is used. - if (foundStart || foundEnd) { - // FIXME: This is a workaround for the fact that the end of a run is often at the wrong - // position for emitted '\n's. - if (len == 1 && it.characters()[0] == UChar('\n')) { - Position runStart = textRunRange->startPosition(); - Position runEnd = VisiblePosition(runStart).next().deepEquivalent(); - if (runEnd.isNotNull()) { - ExceptionCode ec = 0; - textRunRange->setEnd(runEnd.node(), runEnd.offset(), ec); - } - } - } - - if (foundStart) { - startRangeFound = true; - int exception = 0; - if (textRunRange->startContainer(exception)->isTextNode()) { - int offset = rangeLocation - docTextPosition; - resultRange->setStart(textRunRange->startContainer(exception), offset + textRunRange->startOffset(exception), exception); - } else { - if (rangeLocation == docTextPosition) - resultRange->setStart(textRunRange->startContainer(exception), textRunRange->startOffset(exception), exception); - else - resultRange->setStart(textRunRange->endContainer(exception), textRunRange->endOffset(exception), exception); - } - } - - if (foundEnd) { - int exception = 0; - if (textRunRange->startContainer(exception)->isTextNode()) { - int offset = rangeEnd - docTextPosition; - resultRange->setEnd(textRunRange->startContainer(exception), offset + textRunRange->startOffset(exception), exception); - } else { - if (rangeEnd == docTextPosition) - resultRange->setEnd(textRunRange->startContainer(exception), textRunRange->startOffset(exception), exception); - else - resultRange->setEnd(textRunRange->endContainer(exception), textRunRange->endOffset(exception), exception); - } - docTextPosition += len; - break; - } - docTextPosition += len; - } - - if (!startRangeFound) - return 0; - - if (rangeLength != 0 && rangeEnd > docTextPosition) { // rangeEnd is out of bounds - int exception = 0; - resultRange->setEnd(textRunRange->endContainer(exception), textRunRange->endOffset(exception), exception); - } - - return resultRange.release(); -} - -// -------- - -UChar* plainTextToMallocAllocatedBuffer(const Range* r, unsigned& bufferLength) -{ - UChar* result = 0; - - // Do this in pieces to avoid massive reallocations if there is a large amount of text. - // Use system malloc for buffers since they can consume lots of memory and current TCMalloc is unable return it back to OS. - static const unsigned cMaxSegmentSize = 1 << 16; - bufferLength = 0; - typedef pair<UChar*, unsigned> TextSegment; - Vector<TextSegment>* textSegments = 0; - Vector<UChar> textBuffer; - textBuffer.reserveCapacity(cMaxSegmentSize); - for (TextIterator it(r); !it.atEnd(); it.advance()) { - if (textBuffer.size() && textBuffer.size() + it.length() > cMaxSegmentSize) { - UChar* newSegmentBuffer = static_cast<UChar*>(malloc(textBuffer.size() * sizeof(UChar))); - if (!newSegmentBuffer) - goto exit; - memcpy(newSegmentBuffer, textBuffer.data(), textBuffer.size() * sizeof(UChar)); - if (!textSegments) - textSegments = new Vector<TextSegment>; - textSegments->append(make_pair(newSegmentBuffer, (unsigned)textBuffer.size())); - textBuffer.clear(); - } - textBuffer.append(it.characters(), it.length()); - bufferLength += it.length(); - } - - if (!bufferLength) - return 0; - - // Since we know the size now, we can make a single buffer out of the pieces with one big alloc - result = static_cast<UChar*>(malloc(bufferLength * sizeof(UChar))); - if (!result) - goto exit; - - { - UChar* resultPos = result; - if (textSegments) { - unsigned size = textSegments->size(); - for (unsigned i = 0; i < size; ++i) { - const TextSegment& segment = textSegments->at(i); - memcpy(resultPos, segment.first, segment.second * sizeof(UChar)); - resultPos += segment.second; - } - } - memcpy(resultPos, textBuffer.data(), textBuffer.size() * sizeof(UChar)); - } - -exit: - if (textSegments) { - unsigned size = textSegments->size(); - for (unsigned i = 0; i < size; ++i) - free(textSegments->at(i).first); - delete textSegments; - } - return result; -} - -String plainText(const Range* r) -{ - unsigned length; - UChar* buf = plainTextToMallocAllocatedBuffer(r, length); - if (!buf) - return ""; - String result(buf, length); - free(buf); - return result; -} - -PassRefPtr<Range> findPlainText(const Range* range, const String& target, bool forward, bool caseSensitive) -{ - // FIXME: Can we do Boyer-Moore or equivalent instead for speed? - - ExceptionCode ec = 0; - RefPtr<Range> result = range->cloneRange(ec); - result->collapse(!forward, ec); - - // FIXME: This code does not allow \n at the moment because of issues with <br>. - // Once we fix those, we can remove this check. - if (target.isEmpty() || target.find('\n') != -1) - return result.release(); - - unsigned matchStart = 0; - unsigned matchLength = 0; - { - CircularSearchBuffer searchBuffer(target, caseSensitive); - CharacterIterator it(range, false, true); - for (;;) { - if (searchBuffer.isMatch()) { - // Note that we found a match, and where we found it. - unsigned matchEnd = it.characterOffset(); - matchLength = searchBuffer.length(); - ASSERT(matchLength); - ASSERT(matchEnd >= matchLength); - matchStart = matchEnd - matchLength; - // If searching forward, stop on the first match. - // If searching backward, don't stop, so we end up with the last match. - if (forward) - break; - } - if (it.atBreak()) { - if (it.atEnd()) - break; - searchBuffer.clear(); - } - searchBuffer.append(it.characters()[0]); - it.advance(1); - } - } - - if (matchLength) { - CharacterIterator it(range, false, true); - it.advance(matchStart); - result->setStart(it.range()->startContainer(ec), it.range()->startOffset(ec), ec); - it.advance(matchLength - 1); - result->setEnd(it.range()->endContainer(ec), it.range()->endOffset(ec), ec); - } - - return result.release(); -} - -} diff --git a/WebCore/editing/TextIterator.h b/WebCore/editing/TextIterator.h deleted file mode 100644 index 11a8354..0000000 --- a/WebCore/editing/TextIterator.h +++ /dev/null @@ -1,253 +0,0 @@ -/* - * Copyright (C) 2004, 2006 Apple Computer, 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 TextIterator_h -#define TextIterator_h - -#include "InlineTextBox.h" -#include "Range.h" -#include <wtf/Vector.h> - -namespace WebCore { - -// FIXME: Can't really answer this question correctly without knowing the white-space mode. -// FIXME: Move this somewhere else in the editing directory. It doesn't belong here. -inline bool isCollapsibleWhitespace(UChar c) -{ - switch (c) { - case ' ': - case '\n': - return true; - default: - return false; - } -} - -String plainText(const Range*); -UChar* plainTextToMallocAllocatedBuffer(const Range*, unsigned& bufferLength); -PassRefPtr<Range> findPlainText(const Range*, const String&, bool forward, bool caseSensitive); - -// Iterates through the DOM range, returning all the text, and 0-length boundaries -// at points where replaced elements break up the text flow. The text comes back in -// chunks so as to optimize for performance of the iteration. - -class TextIterator { -public: - TextIterator(); - explicit TextIterator(const Range*, bool emitCharactersBetweenAllVisiblePositions = false, bool enterTextControls = false); - - bool atEnd() const { return !m_positionNode; } - void advance(); - - int length() const { return m_textLength; } - const UChar* characters() const { return m_textCharacters; } - - PassRefPtr<Range> range() const; - Node* node() const; - - static int rangeLength(const Range*, bool spacesForReplacedElements = false); - static PassRefPtr<Range> rangeFromLocationAndLength(Element* scope, int rangeLocation, int rangeLength, bool spacesForReplacedElements = false); - static PassRefPtr<Range> subrange(Range* entireRange, int characterOffset, int characterCount); - -private: - void exitNode(); - bool shouldRepresentNodeOffsetZero(); - bool shouldEmitSpaceBeforeAndAfterNode(Node*); - void representNodeOffsetZero(); - bool handleTextNode(); - bool handleReplacedElement(); - bool handleNonTextNode(); - void handleTextBox(); - void emitCharacter(UChar, Node *textNode, Node *offsetBaseNode, int textStartOffset, int textEndOffset); - void emitText(Node *textNode, int textStartOffset, int textEndOffset); - - // Current position, not necessarily of the text being returned, but position - // as we walk through the DOM tree. - Node *m_node; - int m_offset; - bool m_handledNode; - bool m_handledChildren; - bool m_inShadowContent; - - // The range. - Node *m_startContainer; - int m_startOffset; - Node *m_endContainer; - int m_endOffset; - Node *m_pastEndNode; - - // The current text and its position, in the form to be returned from the iterator. - Node *m_positionNode; - mutable Node *m_positionOffsetBaseNode; - mutable int m_positionStartOffset; - mutable int m_positionEndOffset; - const UChar* m_textCharacters; - int m_textLength; - - // Used when there is still some pending text from the current node; when these - // are false and 0, we go back to normal iterating. - bool m_needAnotherNewline; - InlineTextBox *m_textBox; - - // Used to do the whitespace collapsing logic. - Node *m_lastTextNode; - bool m_lastTextNodeEndedWithCollapsedSpace; - UChar m_lastCharacter; - - // Used for whitespace characters that aren't in the DOM, so we can point at them. - UChar m_singleCharacterBuffer; - - // Used when text boxes are out of order (Hebrew/Arabic w/ embeded LTR text) - Vector<InlineTextBox*> m_sortedTextBoxes; - size_t m_sortedTextBoxesPosition; - - // Used when deciding whether to emit a "positioning" (e.g. newline) before any other content - bool m_haveEmitted; - - // Used by selection preservation code. There should be one character emitted between every VisiblePosition - // in the Range used to create the TextIterator. - // FIXME <rdar://problem/6028818>: This functionality should eventually be phased out when we rewrite - // moveParagraphs to not clone/destroy moved content. - bool m_emitCharactersBetweenAllVisiblePositions; - bool m_enterTextControls; -}; - -// Iterates through the DOM range, returning all the text, and 0-length boundaries -// at points where replaced elements break up the text flow. The text comes back in -// chunks so as to optimize for performance of the iteration. -class SimplifiedBackwardsTextIterator { -public: - SimplifiedBackwardsTextIterator(); - explicit SimplifiedBackwardsTextIterator(const Range *); - - bool atEnd() const { return !m_positionNode; } - void advance(); - - int length() const { return m_textLength; } - const UChar* characters() const { return m_textCharacters; } - - PassRefPtr<Range> range() const; - -private: - void exitNode(); - bool handleTextNode(); - bool handleReplacedElement(); - bool handleNonTextNode(); - void emitCharacter(UChar, Node *Node, int startOffset, int endOffset); - - // Current position, not necessarily of the text being returned, but position - // as we walk through the DOM tree. - Node* m_node; - int m_offset; - bool m_handledNode; - bool m_handledChildren; - - // End of the range. - Node* m_startNode; - int m_startOffset; - // Start of the range. - Node* m_endNode; - int m_endOffset; - - // The current text and its position, in the form to be returned from the iterator. - Node* m_positionNode; - int m_positionStartOffset; - int m_positionEndOffset; - const UChar* m_textCharacters; - int m_textLength; - - // Used to do the whitespace logic. - Node* m_lastTextNode; - UChar m_lastCharacter; - - // 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; -}; - -// Builds on the text iterator, adding a character position so we can walk one -// character at a time, or faster, as needed. Useful for searching. -class CharacterIterator { -public: - CharacterIterator(); - explicit CharacterIterator(const Range* r, bool emitCharactersBetweenAllVisiblePositions = false, bool enterTextControls = false); - - void advance(int numCharacters); - - bool atBreak() const { return m_atBreak; } - bool atEnd() const { return m_textIterator.atEnd(); } - - int length() const { return m_textIterator.length() - m_runOffset; } - const UChar* characters() const { return m_textIterator.characters() + m_runOffset; } - String string(int numChars); - - int characterOffset() const { return m_offset; } - PassRefPtr<Range> range() const; - -private: - int m_offset; - int m_runOffset; - bool m_atBreak; - - TextIterator m_textIterator; -}; - -// Very similar to the TextIterator, except that the chunks of text returned are "well behaved", -// meaning they never end split up a word. This is useful for spellcheck or (perhaps one day) searching. -class WordAwareIterator { -public: - WordAwareIterator(); - explicit WordAwareIterator(const Range *r); - - bool atEnd() const { return !m_didLookAhead && m_textIterator.atEnd(); } - void advance(); - - int length() const; - const UChar* characters() const; - - // Range of the text we're currently returning - PassRefPtr<Range> range() const { return m_range; } - -private: - // text from the previous chunk from the textIterator - const UChar* m_previousText; - int m_previousLength; - - // many chunks from textIterator concatenated - Vector<UChar> m_buffer; - - // Did we have to look ahead in the textIterator to confirm the current chunk? - bool m_didLookAhead; - - RefPtr<Range> m_range; - - TextIterator m_textIterator; -}; - -} - -#endif diff --git a/WebCore/editing/TypingCommand.cpp b/WebCore/editing/TypingCommand.cpp deleted file mode 100644 index ef144a1..0000000 --- a/WebCore/editing/TypingCommand.cpp +++ /dev/null @@ -1,535 +0,0 @@ -/* - * Copyright (C) 2005, 2006, 2007, 2008 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 "TypingCommand.h" - -#include "BeforeTextInsertedEvent.h" -#include "BreakBlockquoteCommand.h" -#include "DeleteSelectionCommand.h" -#include "Document.h" -#include "Editor.h" -#include "Element.h" -#include "Frame.h" -#include "InsertLineBreakCommand.h" -#include "InsertParagraphSeparatorCommand.h" -#include "InsertTextCommand.h" -#include "SelectionController.h" -#include "VisiblePosition.h" -#include "htmlediting.h" -#include "visible_units.h" - -namespace WebCore { - -TypingCommand::TypingCommand(Document *document, ETypingCommand commandType, const String &textToInsert, bool selectInsertedText, TextGranularity granularity) - : CompositeEditCommand(document), - m_commandType(commandType), - m_textToInsert(textToInsert), - m_openForMoreTyping(true), - m_applyEditing(false), - m_selectInsertedText(selectInsertedText), - m_smartDelete(false), - m_granularity(granularity), - m_openedByBackwardDelete(false) -{ -} - -void TypingCommand::deleteSelection(Document* document, bool smartDelete) -{ - ASSERT(document); - - Frame* frame = document->frame(); - ASSERT(frame); - - if (!frame->selection()->isRange()) - return; - - EditCommand* lastEditCommand = frame->editor()->lastEditCommand(); - if (isOpenForMoreTypingCommand(lastEditCommand)) { - static_cast<TypingCommand*>(lastEditCommand)->deleteSelection(smartDelete); - return; - } - - RefPtr<TypingCommand> typingCommand = TypingCommand::create(document, DeleteSelection, "", false); - typingCommand->setSmartDelete(smartDelete); - typingCommand->apply(); -} - -void TypingCommand::deleteKeyPressed(Document *document, bool smartDelete, TextGranularity granularity) -{ - ASSERT(document); - - Frame *frame = document->frame(); - ASSERT(frame); - - EditCommand* lastEditCommand = frame->editor()->lastEditCommand(); - if (isOpenForMoreTypingCommand(lastEditCommand)) { - static_cast<TypingCommand*>(lastEditCommand)->deleteKeyPressed(granularity); - return; - } - - RefPtr<TypingCommand> typingCommand = TypingCommand::create(document, DeleteKey, "", false, granularity); - typingCommand->setSmartDelete(smartDelete); - typingCommand->apply(); -} - -void TypingCommand::forwardDeleteKeyPressed(Document *document, bool smartDelete, TextGranularity granularity) -{ - // FIXME: Forward delete in TextEdit appears to open and close a new typing command. - ASSERT(document); - - Frame *frame = document->frame(); - ASSERT(frame); - - EditCommand* lastEditCommand = frame->editor()->lastEditCommand(); - if (isOpenForMoreTypingCommand(lastEditCommand)) { - static_cast<TypingCommand*>(lastEditCommand)->forwardDeleteKeyPressed(granularity); - return; - } - - RefPtr<TypingCommand> typingCommand = TypingCommand::create(document, ForwardDeleteKey, "", false, granularity); - typingCommand->setSmartDelete(smartDelete); - typingCommand->apply(); -} - -void TypingCommand::insertText(Document* document, const String& text, bool selectInsertedText, bool insertedTextIsComposition) -{ - ASSERT(document); - - Frame* frame = document->frame(); - ASSERT(frame); - - insertText(document, text, frame->selection()->selection(), selectInsertedText, insertedTextIsComposition); -} - -void TypingCommand::insertText(Document* document, const String& text, const Selection& selectionForInsertion, bool selectInsertedText, bool insertedTextIsComposition) -{ - ASSERT(document); - - RefPtr<Frame> frame = document->frame(); - ASSERT(frame); - - Selection currentSelection = frame->selection()->selection(); - bool changeSelection = currentSelection != selectionForInsertion; - - String newText = text; - Node* startNode = selectionForInsertion.start().node(); - - if (startNode && startNode->rootEditableElement() && !insertedTextIsComposition) { - // Send BeforeTextInsertedEvent. The event handler will update text if necessary. - ExceptionCode ec = 0; - RefPtr<BeforeTextInsertedEvent> evt = BeforeTextInsertedEvent::create(text); - startNode->rootEditableElement()->dispatchEvent(evt, ec); - newText = evt->text(); - } - - if (newText.isEmpty()) - return; - - // Set the starting and ending selection appropriately if we are using a selection - // that is different from the current selection. In the future, we should change EditCommand - // to deal with custom selections in a general way that can be used by all of the commands. - RefPtr<EditCommand> lastEditCommand = frame->editor()->lastEditCommand(); - if (isOpenForMoreTypingCommand(lastEditCommand.get())) { - TypingCommand* lastTypingCommand = static_cast<TypingCommand*>(lastEditCommand.get()); - if (changeSelection) { - lastTypingCommand->setStartingSelection(selectionForInsertion); - lastTypingCommand->setEndingSelection(selectionForInsertion); - } - lastTypingCommand->insertText(newText, selectInsertedText); - if (changeSelection) { - lastTypingCommand->setEndingSelection(currentSelection); - frame->selection()->setSelection(currentSelection); - } - return; - } - - RefPtr<TypingCommand> cmd = TypingCommand::create(document, InsertText, newText, selectInsertedText); - if (changeSelection) { - cmd->setStartingSelection(selectionForInsertion); - cmd->setEndingSelection(selectionForInsertion); - } - applyCommand(cmd); - if (changeSelection) { - cmd->setEndingSelection(currentSelection); - frame->selection()->setSelection(currentSelection); - } -} - -void TypingCommand::insertLineBreak(Document *document) -{ - ASSERT(document); - - Frame *frame = document->frame(); - ASSERT(frame); - - EditCommand* lastEditCommand = frame->editor()->lastEditCommand(); - if (isOpenForMoreTypingCommand(lastEditCommand)) { - static_cast<TypingCommand*>(lastEditCommand)->insertLineBreak(); - return; - } - - applyCommand(TypingCommand::create(document, InsertLineBreak)); -} - -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(); - return; - } - - applyCommand(TypingCommand::create(document, InsertParagraphSeparatorInQuotedContent)); -} - -void TypingCommand::insertParagraphSeparator(Document *document) -{ - ASSERT(document); - - Frame *frame = document->frame(); - ASSERT(frame); - - EditCommand* lastEditCommand = frame->editor()->lastEditCommand(); - if (isOpenForMoreTypingCommand(lastEditCommand)) { - static_cast<TypingCommand*>(lastEditCommand)->insertParagraphSeparator(); - return; - } - - applyCommand(TypingCommand::create(document, InsertParagraphSeparator)); -} - -bool TypingCommand::isOpenForMoreTypingCommand(const EditCommand* cmd) -{ - return cmd && cmd->isTypingCommand() && static_cast<const TypingCommand*>(cmd)->isOpenForMoreTyping(); -} - -void TypingCommand::closeTyping(EditCommand* cmd) -{ - if (isOpenForMoreTypingCommand(cmd)) - static_cast<TypingCommand*>(cmd)->closeTyping(); -} - -void TypingCommand::doApply() -{ - if (endingSelection().isNone()) - return; - - if (m_commandType == DeleteKey) - if (m_commands.isEmpty()) - m_openedByBackwardDelete = true; - - switch (m_commandType) { - case DeleteSelection: - deleteSelection(m_smartDelete); - return; - case DeleteKey: - deleteKeyPressed(m_granularity); - return; - case ForwardDeleteKey: - forwardDeleteKeyPressed(m_granularity); - return; - case InsertLineBreak: - insertLineBreak(); - return; - case InsertParagraphSeparator: - insertParagraphSeparator(); - return; - case InsertParagraphSeparatorInQuotedContent: - insertParagraphSeparatorInQuotedContent(); - return; - case InsertText: - insertText(m_textToInsert, m_selectInsertedText); - return; - } - - ASSERT_NOT_REACHED(); -} - -EditAction TypingCommand::editingAction() const -{ - return EditActionTyping; -} - -void TypingCommand::markMisspellingsAfterTyping() -{ - if (!document()->frame()->editor()->isContinuousSpellCheckingEnabled()) - return; - // Take a look at the selection that results after typing and determine whether we need to spellcheck. - // Since the word containing the current selection is never marked, this does a check to - // see if typing made a new word that is not in the current selection. Basically, you - // get this by being at the end of a word and typing a space. - VisiblePosition start(endingSelection().start(), endingSelection().affinity()); - VisiblePosition previous = start.previous(); - if (previous.isNotNull()) { - VisiblePosition p1 = startOfWord(previous, LeftWordIfOnBoundary); - VisiblePosition p2 = startOfWord(start, LeftWordIfOnBoundary); - if (p1 != p2) - document()->frame()->editor()->markMisspellingsAfterTypingToPosition(p1); - } -} - -void TypingCommand::typingAddedToOpenCommand() -{ - markMisspellingsAfterTyping(); - // Do not apply editing to the frame on the first time through. - // The frame will get told in the same way as all other commands. - // But since this command stays open and is used for additional typing, - // we need to tell the frame here as other commands are added. - if (m_applyEditing) - document()->frame()->editor()->appliedEditing(this); - m_applyEditing = true; -} - -void TypingCommand::insertText(const String &text, bool selectInsertedText) -{ - // FIXME: Need to implement selectInsertedText for cases where more than one insert is involved. - // This requires support from insertTextRunWithoutNewlines and insertParagraphSeparator for extending - // an existing selection; at the moment they can either put the caret after what's inserted or - // select what's inserted, but there's no way to "extend selection" to include both an old selection - // that ends just before where we want to insert text and the newly inserted text. - int offset = 0; - int newline; - while ((newline = text.find('\n', offset)) != -1) { - if (newline != offset) - insertTextRunWithoutNewlines(text.substring(offset, newline - offset), false); - insertParagraphSeparator(); - offset = newline + 1; - } - if (offset == 0) - insertTextRunWithoutNewlines(text, selectInsertedText); - else { - int length = text.length(); - if (length != offset) { - insertTextRunWithoutNewlines(text.substring(offset, length - offset), selectInsertedText); - } - } -} - -void TypingCommand::insertTextRunWithoutNewlines(const String &text, bool selectInsertedText) -{ - RefPtr<InsertTextCommand> command; - if (!document()->frame()->typingStyle() && !m_commands.isEmpty()) { - EditCommand* lastCommand = m_commands.last().get(); - if (lastCommand->isInsertTextCommand()) - command = static_cast<InsertTextCommand*>(lastCommand); - } - if (!command) { - command = InsertTextCommand::create(document()); - applyCommandToComposite(command); - } - command->input(text, selectInsertedText); - typingAddedToOpenCommand(); -} - -void TypingCommand::insertLineBreak() -{ - applyCommandToComposite(InsertLineBreakCommand::create(document())); - typingAddedToOpenCommand(); -} - -void TypingCommand::insertParagraphSeparator() -{ - applyCommandToComposite(InsertParagraphSeparatorCommand::create(document())); - typingAddedToOpenCommand(); -} - -void TypingCommand::insertParagraphSeparatorInQuotedContent() -{ - applyCommandToComposite(BreakBlockquoteCommand::create(document())); - typingAddedToOpenCommand(); -} - -void TypingCommand::deleteKeyPressed(TextGranularity granularity) -{ - Selection selectionToDelete; - Selection selectionAfterUndo; - - switch (endingSelection().state()) { - case Selection::RANGE: - selectionToDelete = endingSelection(); - selectionAfterUndo = selectionToDelete; - break; - case Selection::CARET: { - m_smartDelete = false; - - SelectionController selection; - selection.setSelection(endingSelection()); - selection.modify(SelectionController::EXTEND, SelectionController::BACKWARD, granularity); - - // When the caret is at the start of the editable area in an empty list item, break out of the list item. - if (endingSelection().visibleStart().previous(true).isNull()) { - if (breakOutOfEmptyListItem()) { - typingAddedToOpenCommand(); - return; - } - } - - VisiblePosition visibleStart(endingSelection().visibleStart()); - // If the caret is at the start of a paragraph after a table, move content into the last table cell. - if (isStartOfParagraph(visibleStart) && isFirstPositionAfterTable(visibleStart.previous(true))) { - // Unless the caret is just before a table. We don't want to move a table into the last table cell. - if (isLastPositionBeforeTable(visibleStart)) - return; - // Extend the selection backward into the last cell, then deletion will handle the move. - selection.modify(SelectionController::EXTEND, SelectionController::BACKWARD, granularity); - // If the caret is just after a table, select the table and don't delete anything. - } else if (Node* table = isFirstPositionAfterTable(visibleStart)) { - setEndingSelection(Selection(Position(table, 0), endingSelection().start(), DOWNSTREAM)); - typingAddedToOpenCommand(); - return; - } - - selectionToDelete = selection.selection(); - if (!startingSelection().isRange() || selectionToDelete.base() != startingSelection().start()) - selectionAfterUndo = selectionToDelete; - else - // It's a little tricky to compute what the starting selection would have been in the original document. - // We can't let the Selection 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. - selectionAfterUndo.setWithoutValidation(startingSelection().end(), selectionToDelete.extent()); - break; - } - case Selection::NONE: - ASSERT_NOT_REACHED(); - break; - } - - if (selectionToDelete.isCaretOrRange() && document()->frame()->shouldDeleteSelection(selectionToDelete)) { - // Make undo select everything that has been deleted, unless an undo will undo more than just this deletion. - // FIXME: This behaves like TextEdit except for the case where you open with text insertion and then delete - // more text than you insert. In that case all of the text that was around originally should be selected. - if (m_openedByBackwardDelete) - setStartingSelection(selectionAfterUndo); - CompositeEditCommand::deleteSelection(selectionToDelete, m_smartDelete); - setSmartDelete(false); - typingAddedToOpenCommand(); - } -} - -void TypingCommand::forwardDeleteKeyPressed(TextGranularity granularity) -{ - Selection selectionToDelete; - Selection selectionAfterUndo; - - switch (endingSelection().state()) { - case Selection::RANGE: - selectionToDelete = endingSelection(); - selectionAfterUndo = selectionToDelete; - break; - case Selection::CARET: { - m_smartDelete = false; - - // Handle delete at beginning-of-block case. - // Do nothing in the case that the caret is at the start of a - // root editable element or at the start of a document. - SelectionController selection; - selection.setSelection(endingSelection()); - selection.modify(SelectionController::EXTEND, SelectionController::FORWARD, granularity); - Position downstreamEnd = endingSelection().end().downstream(); - VisiblePosition visibleEnd = endingSelection().visibleEnd(); - 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.offset() == 0) { - setEndingSelection(Selection(endingSelection().end(), Position(downstreamEnd.node(), maxDeepOffset(downstreamEnd.node())), DOWNSTREAM)); - typingAddedToOpenCommand(); - return; - } - - // deleting to end of paragraph when at end of paragraph needs to merge the next paragraph (if any) - if (granularity == ParagraphBoundary && selection.selection().isCaret() && isEndOfParagraph(selection.selection().visibleEnd())) - selection.modify(SelectionController::EXTEND, SelectionController::FORWARD, CharacterGranularity); - - selectionToDelete = selection.selection(); - if (!startingSelection().isRange() || selectionToDelete.base() != startingSelection().start()) - selectionAfterUndo = selectionToDelete; - else { - // It's a little tricky to compute what the starting selection would have been in the original document. - // We can't let the Selection 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()) - extent = selectionToDelete.extent(); - else { - int extraCharacters; - if (selectionToDelete.start().node() == selectionToDelete.end().node()) - extraCharacters = selectionToDelete.end().offset() - selectionToDelete.start().offset(); - else - extraCharacters = selectionToDelete.end().offset(); - extent = Position(extent.node(), extent.offset() + extraCharacters); - } - selectionAfterUndo.setWithoutValidation(startingSelection().start(), extent); - } - break; - } - case Selection::NONE: - ASSERT_NOT_REACHED(); - break; - } - - if (selectionToDelete.isCaretOrRange() && document()->frame()->shouldDeleteSelection(selectionToDelete)) { - // make undo select what was deleted - setStartingSelection(selectionAfterUndo); - CompositeEditCommand::deleteSelection(selectionToDelete, m_smartDelete); - setSmartDelete(false); - typingAddedToOpenCommand(); - } -} - -void TypingCommand::deleteSelection(bool smartDelete) -{ - CompositeEditCommand::deleteSelection(smartDelete); - typingAddedToOpenCommand(); -} - -bool TypingCommand::preservesTypingStyle() const -{ - switch (m_commandType) { - case DeleteSelection: - case DeleteKey: - case ForwardDeleteKey: - case InsertParagraphSeparator: - case InsertLineBreak: - return true; - case InsertParagraphSeparatorInQuotedContent: - case InsertText: - return false; - } - ASSERT_NOT_REACHED(); - return false; -} - -bool TypingCommand::isTypingCommand() const -{ - return true; -} - -} // namespace WebCore diff --git a/WebCore/editing/TypingCommand.h b/WebCore/editing/TypingCommand.h deleted file mode 100644 index 17ea526..0000000 --- a/WebCore/editing/TypingCommand.h +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright (C) 2005, 2006, 2007, 2008 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 TypingCommand_h -#define TypingCommand_h - -#include "CompositeEditCommand.h" - -namespace WebCore { - -class TypingCommand : public CompositeEditCommand { -public: - enum ETypingCommand { - DeleteSelection, - DeleteKey, - ForwardDeleteKey, - InsertText, - InsertLineBreak, - InsertParagraphSeparator, - InsertParagraphSeparatorInQuotedContent - }; - - static void deleteSelection(Document*, bool smartDelete = false); - static void deleteKeyPressed(Document*, bool smartDelete = false, TextGranularity = CharacterGranularity); - static void forwardDeleteKeyPressed(Document*, bool smartDelete = false, TextGranularity = CharacterGranularity); - static void insertText(Document*, const String&, bool selectInsertedText = false, bool insertedTextIsComposition = false); - static void insertText(Document*, const String&, const Selection&, bool selectInsertedText = false, bool insertedTextIsComposition = false); - static void insertLineBreak(Document*); - static void insertParagraphSeparator(Document*); - static void insertParagraphSeparatorInQuotedContent(Document*); - static bool isOpenForMoreTypingCommand(const EditCommand*); - static void closeTyping(EditCommand*); - - bool isOpenForMoreTyping() const { return m_openForMoreTyping; } - void closeTyping() { m_openForMoreTyping = false; } - - void insertText(const String &text, bool selectInsertedText); - void insertTextRunWithoutNewlines(const String &text, bool selectInsertedText); - void insertLineBreak(); - void insertParagraphSeparatorInQuotedContent(); - void insertParagraphSeparator(); - void deleteKeyPressed(TextGranularity); - void forwardDeleteKeyPressed(TextGranularity); - void deleteSelection(bool); - -private: - static PassRefPtr<TypingCommand> create(Document* document, ETypingCommand command, const String& text = "", bool selectInsertedText = false, TextGranularity granularity = CharacterGranularity) - { - return adoptRef(new TypingCommand(document, command, text, selectInsertedText, granularity)); - } - - TypingCommand(Document*, ETypingCommand, const String& text, bool selectInsertedText, TextGranularity); - - bool smartDelete() const { return m_smartDelete; } - void setSmartDelete(bool smartDelete) { m_smartDelete = smartDelete; } - - virtual void doApply(); - virtual EditAction editingAction() const; - virtual bool isTypingCommand() const; - virtual bool preservesTypingStyle() const; - - void markMisspellingsAfterTyping(); - void typingAddedToOpenCommand(); - - ETypingCommand m_commandType; - String m_textToInsert; - bool m_openForMoreTyping; - bool m_applyEditing; - bool m_selectInsertedText; - bool m_smartDelete; - TextGranularity m_granularity; - - // Undoing a series of backward deletes will restore a selection around all of the - // characters that were deleted, but only if the typing command being undone - // was opened with a backward delete. - bool m_openedByBackwardDelete; -}; - -} // namespace WebCore - -#endif // TypingCommand_h diff --git a/WebCore/editing/UnlinkCommand.cpp b/WebCore/editing/UnlinkCommand.cpp deleted file mode 100644 index 0ba9a06..0000000 --- a/WebCore/editing/UnlinkCommand.cpp +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (C) 2006 Apple Computer, 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 "UnlinkCommand.h" - -#include "HTMLAnchorElement.h" - -namespace WebCore { - -UnlinkCommand::UnlinkCommand(Document* document) - : CompositeEditCommand(document) -{ -} - -void UnlinkCommand::doApply() -{ - // FIXME: If a caret is inside a link, remove it. - if (!endingSelection().isRange()) - return; - - pushPartiallySelectedAnchorElementsDown(); - - HTMLAnchorElement* anchorElement = new HTMLAnchorElement(document()); - removeStyledElement(anchorElement); -} - -} diff --git a/WebCore/editing/UnlinkCommand.h b/WebCore/editing/UnlinkCommand.h deleted file mode 100644 index f3d560f..0000000 --- a/WebCore/editing/UnlinkCommand.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (C) 2006, 2008 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 UnlinkCommand_h -#define UnlinkCommand_h - -#include "CompositeEditCommand.h" - -namespace WebCore { - -class UnlinkCommand : public CompositeEditCommand { -public: - static PassRefPtr<UnlinkCommand> create(Document* document) - { - return adoptRef(new UnlinkCommand(document)); - } - -private: - UnlinkCommand(Document*); - - virtual void doApply(); - virtual EditAction editingAction() const { return EditActionUnlink; } -}; - -} // namespace WebCore - -#endif // UnlinkCommand_h diff --git a/WebCore/editing/VisiblePosition.cpp b/WebCore/editing/VisiblePosition.cpp deleted file mode 100644 index 626c5b4..0000000 --- a/WebCore/editing/VisiblePosition.cpp +++ /dev/null @@ -1,654 +0,0 @@ -/* - * Copyright (C) 2004 Apple Computer, 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 "VisiblePosition.h" - -#include "CString.h" -#include "Document.h" -#include "Element.h" -#include "HTMLNames.h" -#include "InlineTextBox.h" -#include "Logging.h" -#include "Range.h" -#include "Text.h" -#include "htmlediting.h" -#include "visible_units.h" -#include <stdio.h> - -namespace WebCore { - -using namespace HTMLNames; - -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; - - m_deepPosition = canonicalPosition(position); - - // When not at a line wrap, make sure to end up with DOWNSTREAM affinity. - if (m_affinity == UPSTREAM && (isNull() || inSameLine(VisiblePosition(position, DOWNSTREAM), *this))) - m_affinity = DOWNSTREAM; -} - -VisiblePosition VisiblePosition::next(bool stayInEditableContent) const -{ - VisiblePosition next(nextVisuallyDistinctCandidate(m_deepPosition), m_affinity); - - if (!stayInEditableContent) - return next; - - return honorEditableBoundaryAtOrAfter(next); -} - -VisiblePosition VisiblePosition::previous(bool stayInEditableContent) const -{ - // find first previous DOM position that is visible - Position pos = previousVisuallyDistinctCandidate(m_deepPosition); - - // return null visible position if there is no previous visible position - if (pos.atStart()) - return VisiblePosition(); - - VisiblePosition prev = VisiblePosition(pos, DOWNSTREAM); - ASSERT(prev != *this); - -#ifndef NDEBUG - // we should always be able to make the affinity DOWNSTREAM, because going previous from an - // UPSTREAM position can never yield another UPSTREAM position (unless line wrap length is 0!). - if (prev.isNotNull() && m_affinity == UPSTREAM) { - VisiblePosition temp = prev; - temp.setAffinity(UPSTREAM); - ASSERT(inSameLine(temp, prev)); - } -#endif - - if (!stayInEditableContent) - return prev; - - return honorEditableBoundaryAtOrBefore(prev); -} - -Position VisiblePosition::leftVisuallyDistinctCandidate() const -{ - Position p = m_deepPosition; - if (!p.node()) - return Position(); - - Position downstreamStart = p.downstream(); - TextDirection primaryDirection = LTR; - for (RenderObject* r = p.node()->renderer(); r; r = r->parent()) { - if (r->isBlockFlow()) { - primaryDirection = r->style()->direction(); - break; - } - } - - while (true) { - InlineBox* box; - int offset; - p.getInlineBoxAndOffset(m_affinity, primaryDirection, box, offset); - if (!box) - return primaryDirection == LTR ? previousVisuallyDistinctCandidate(m_deepPosition) : nextVisuallyDistinctCandidate(m_deepPosition); - - RenderObject* renderer = box->object(); - - while (true) { - if ((renderer->isReplaced() || renderer->isBR()) && offset == box->caretRightmostOffset()) - return box->direction() == LTR ? previousVisuallyDistinctCandidate(m_deepPosition) : nextVisuallyDistinctCandidate(m_deepPosition); - - offset = box->direction() == LTR ? renderer->previousOffset(offset) : renderer->nextOffset(offset); - - int caretMinOffset = box->caretMinOffset(); - int caretMaxOffset = box->caretMaxOffset(); - - if (offset > caretMinOffset && offset < caretMaxOffset) - break; - - if (box->direction() == LTR ? offset < caretMinOffset : offset > caretMaxOffset) { - // Overshot to the left. - InlineBox* prevBox = box->prevLeafChild(); - if (!prevBox) - return primaryDirection == LTR ? previousVisuallyDistinctCandidate(m_deepPosition) : nextVisuallyDistinctCandidate(m_deepPosition); - - // Reposition at the other logical position corresponding to our edge's visual position and go for another round. - box = prevBox; - renderer = box->object(); - offset = prevBox->caretRightmostOffset(); - continue; - } - - ASSERT(offset == box->caretLeftmostOffset()); - - unsigned char level = box->bidiLevel(); - InlineBox* prevBox = box->prevLeafChild(); - - if (box->direction() == primaryDirection) { - if (!prevBox || prevBox->bidiLevel() >= level) - break; - - level = prevBox->bidiLevel(); - - InlineBox* nextBox = box; - do { - nextBox = nextBox->nextLeafChild(); - } while (nextBox && nextBox->bidiLevel() > level); - - if (nextBox && nextBox->bidiLevel() == level) - break; - - while (InlineBox* prevBox = box->prevLeafChild()) { - if (prevBox->bidiLevel() < level) - break; - box = prevBox; - } - renderer = box->object(); - offset = box->caretRightmostOffset(); - if (box->direction() == primaryDirection) - break; - continue; - } - - if (prevBox) { - box = prevBox; - renderer = box->object(); - offset = box->caretRightmostOffset(); - if (box->bidiLevel() > level) { - do { - prevBox = box->prevLeafChild(); - } while (prevBox && prevBox->bidiLevel() > level); - - if (!prevBox || prevBox->bidiLevel() < level) - continue; - } - } else { - // Trailing edge of a secondary run. Set to the leading edge of the entire run. - while (true) { - while (InlineBox* nextBox = box->nextLeafChild()) { - if (nextBox->bidiLevel() < level) - break; - box = nextBox; - } - if (box->bidiLevel() == level) - break; - level = box->bidiLevel(); - while (InlineBox* prevBox = box->prevLeafChild()) { - if (prevBox->bidiLevel() < level) - break; - box = prevBox; - } - if (box->bidiLevel() == level) - break; - level = box->bidiLevel(); - } - renderer = box->object(); - offset = primaryDirection == LTR ? box->caretMinOffset() : box->caretMaxOffset(); - } - break; - } - - p = Position(renderer->element(), offset); - - if (p.isCandidate() && p.downstream() != downstreamStart || p.atStart() || p.atEnd()) - return p; - } -} - -VisiblePosition VisiblePosition::left(bool stayInEditableContent) const -{ - Position pos = leftVisuallyDistinctCandidate(); - if (pos.atStart() || pos.atEnd()) - return VisiblePosition(); - - VisiblePosition left = VisiblePosition(pos, DOWNSTREAM); - ASSERT(left != *this); - - if (!stayInEditableContent) - return left; - - // FIXME: This may need to do something different from "before". - return honorEditableBoundaryAtOrBefore(left); -} - -Position VisiblePosition::rightVisuallyDistinctCandidate() const -{ - Position p = m_deepPosition; - if (!p.node()) - return Position(); - - Position downstreamStart = p.downstream(); - TextDirection primaryDirection = LTR; - for (RenderObject* r = p.node()->renderer(); r; r = r->parent()) { - if (r->isBlockFlow()) { - primaryDirection = r->style()->direction(); - break; - } - } - - while (true) { - InlineBox* box; - int offset; - p.getInlineBoxAndOffset(m_affinity, primaryDirection, box, offset); - if (!box) - return primaryDirection == LTR ? nextVisuallyDistinctCandidate(m_deepPosition) : previousVisuallyDistinctCandidate(m_deepPosition); - - RenderObject* renderer = box->object(); - - while (true) { - if ((renderer->isReplaced() || renderer->isBR()) && offset == box->caretLeftmostOffset()) - return box->direction() == LTR ? nextVisuallyDistinctCandidate(m_deepPosition) : previousVisuallyDistinctCandidate(m_deepPosition); - - offset = box->direction() == LTR ? renderer->nextOffset(offset) : renderer->previousOffset(offset); - - int caretMinOffset = box->caretMinOffset(); - int caretMaxOffset = box->caretMaxOffset(); - - if (offset > caretMinOffset && offset < caretMaxOffset) - break; - - if (box->direction() == LTR ? offset > caretMaxOffset : offset < caretMinOffset) { - // Overshot to the right. - InlineBox* nextBox = box->nextLeafChild(); - if (!nextBox) - return primaryDirection == LTR ? nextVisuallyDistinctCandidate(m_deepPosition) : previousVisuallyDistinctCandidate(m_deepPosition); - - // Reposition at the other logical position corresponding to our edge's visual position and go for another round. - box = nextBox; - renderer = box->object(); - offset = nextBox->caretLeftmostOffset(); - continue; - } - - ASSERT(offset == box->caretRightmostOffset()); - - unsigned char level = box->bidiLevel(); - InlineBox* nextBox = box->nextLeafChild(); - - if (box->direction() == primaryDirection) { - if (!nextBox || nextBox->bidiLevel() >= level) - break; - - level = nextBox->bidiLevel(); - - InlineBox* prevBox = box; - do { - prevBox = prevBox->prevLeafChild(); - } while (prevBox && prevBox->bidiLevel() > level); - - if (prevBox && prevBox->bidiLevel() == level) // For example, abc FED 123 ^ CBA - break; - - // For example, abc 123 ^ CBA - while (InlineBox* nextBox = box->nextLeafChild()) { - if (nextBox->bidiLevel() < level) - break; - box = nextBox; - } - renderer = box->object(); - offset = box->caretLeftmostOffset(); - if (box->direction() == primaryDirection) - break; - continue; - } - - if (nextBox) { - box = nextBox; - renderer = box->object(); - offset = box->caretLeftmostOffset(); - if (box->bidiLevel() > level) { - do { - nextBox = box->nextLeafChild(); - } while (nextBox && nextBox->bidiLevel() > level); - - if (!nextBox || nextBox->bidiLevel() < level) - continue; - } - } else { - // Trailing edge of a secondary run. Set to the leading edge of the entire run. - while (true) { - while (InlineBox* prevBox = box->prevLeafChild()) { - if (prevBox->bidiLevel() < level) - break; - box = prevBox; - } - if (box->bidiLevel() == level) - break; - level = box->bidiLevel(); - while (InlineBox* nextBox = box->nextLeafChild()) { - if (nextBox->bidiLevel() < level) - break; - box = nextBox; - } - if (box->bidiLevel() == level) - break; - level = box->bidiLevel(); - } - renderer = box->object(); - offset = primaryDirection == LTR ? box->caretMaxOffset() : box->caretMinOffset(); - } - break; - } - - p = Position(renderer->element(), offset); - - if (p.isCandidate() && p.downstream() != downstreamStart || p.atStart() || p.atEnd()) - return p; - } -} - -VisiblePosition VisiblePosition::right(bool stayInEditableContent) const -{ - Position pos = rightVisuallyDistinctCandidate(); - if (pos.atStart() || pos.atEnd()) - return VisiblePosition(); - - VisiblePosition right = VisiblePosition(pos, DOWNSTREAM); - ASSERT(right != *this); - - if (!stayInEditableContent) - return right; - - // FIXME: This may need to do something different from "after". - return honorEditableBoundaryAtOrAfter(right); -} - -VisiblePosition VisiblePosition::honorEditableBoundaryAtOrBefore(const VisiblePosition &pos) const -{ - if (pos.isNull()) - return pos; - - 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)) - return VisiblePosition(); - - // Return pos itself if the two are from the very same editable region, or both are non-editable - // FIXME: In the non-editable case, just because the new position is non-editable doesn't mean movement - // to it is allowed. Selection::adjustForEditableContent has this problem too. - if (highestEditableRoot(pos.deepEquivalent()) == highestRoot) - return pos; - - // Return empty position if this position is non-editable, but pos is editable - // FIXME: Move to the previous non-editable region. - if (!highestRoot) - return VisiblePosition(); - - // Return the last position before pos that is in the same editable region as this position - return lastEditablePositionBeforePositionInRoot(pos.deepEquivalent(), highestRoot); -} - -VisiblePosition VisiblePosition::honorEditableBoundaryAtOrAfter(const VisiblePosition &pos) const -{ - if (pos.isNull()) - return pos; - - 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)) - return VisiblePosition(); - - // Return pos itself if the two are from the very same editable region, or both are non-editable - // FIXME: In the non-editable case, just because the new position is non-editable doesn't mean movement - // to it is allowed. Selection::adjustForEditableContent has this problem too. - if (highestEditableRoot(pos.deepEquivalent()) == highestRoot) - return pos; - - // Return empty position if this position is non-editable, but pos is editable - // FIXME: Move to the next non-editable region. - if (!highestRoot) - return VisiblePosition(); - - // Return the next position after pos that is in the same editable region as this position - return firstEditablePositionAfterPositionInRoot(pos.deepEquivalent(), highestRoot); -} - -Position canonicalizeCandidate(const Position& candidate) -{ - if (candidate.isNull()) - return Position(); - ASSERT(candidate.isCandidate()); - Position upstream = candidate.upstream(); - if (upstream.isCandidate()) - return upstream; - return candidate; -} - -Position VisiblePosition::canonicalPosition(const Position& position) -{ - // FIXME (9535): Canonicalizing to the leftmost candidate means that if we're at a line wrap, we will - // ask renderers to paint downstream carets for other renderers. - // 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) - return Position(); - - node->document()->updateLayoutIgnorePendingStylesheets(); - - Position candidate = position.upstream(); - if (candidate.isCandidate()) - return candidate; - candidate = position.downstream(); - if (candidate.isCandidate()) - return candidate; - - // When neither upstream or downstream gets us to a candidate (upstream/downstream won't leave - // 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(); - - // 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()) - 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()) - return next.isNotNull() ? next : prev; - - bool prevIsInSameEditableElement = prevNode && editableRootForPosition(prev) == editingRoot; - bool nextIsInSameEditableElement = nextNode && editableRootForPosition(next) == editingRoot; - if (prevIsInSameEditableElement && !nextIsInSameEditableElement) - return prev; - - if (nextIsInSameEditableElement && !prevIsInSameEditableElement) - return next; - - if (!nextIsInSameEditableElement && !prevIsInSameEditableElement) - return Position(); - - // The new position should be in the same block flow element. Favor that. - Node *originalBlock = node->enclosingBlockFlowElement(); - bool nextIsOutsideOriginalBlock = !nextNode->isDescendantOf(originalBlock) && nextNode != originalBlock; - bool prevIsOutsideOriginalBlock = !prevNode->isDescendantOf(originalBlock) && prevNode != originalBlock; - if (nextIsOutsideOriginalBlock && !prevIsOutsideOriginalBlock) - return prev; - - return next; -} - -UChar 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()) - return 0; - Text* textNode = static_cast<Text*>(pos.node()); - int offset = pos.offset(); - if ((unsigned)offset >= textNode->length()) - return 0; - return textNode->data()[offset]; -} - -IntRect VisiblePosition::caretRect() const -{ - Node* node = m_deepPosition.node(); - if (!node) - return IntRect(); - - RenderObject* renderer = node->renderer(); - if (!renderer) - return IntRect(); - - InlineBox* inlineBox; - int caretOffset; - getInlineBoxAndOffset(inlineBox, caretOffset); - - if (inlineBox) - renderer = inlineBox->object(); - - return renderer->caretRect(inlineBox, caretOffset); -} - -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.offset()); -} - -#ifndef NDEBUG - -void VisiblePosition::formatForDebugger(char* buffer, unsigned length) const -{ - m_deepPosition.formatForDebugger(buffer, length); -} - -void VisiblePosition::showTreeForThis() const -{ - m_deepPosition.showTreeForThis(); -} - -#endif - -PassRefPtr<Range> makeRange(const VisiblePosition &start, const VisiblePosition &end) -{ - Position s = rangeCompliantEquivalent(start); - Position e = rangeCompliantEquivalent(end); - return Range::create(s.node()->document(), s.node(), s.offset(), e.node(), e.offset()); -} - -VisiblePosition startVisiblePosition(const Range *r, EAffinity affinity) -{ - int exception = 0; - return VisiblePosition(r->startContainer(exception), r->startOffset(exception), affinity); -} - -VisiblePosition endVisiblePosition(const Range *r, EAffinity affinity) -{ - int exception = 0; - return VisiblePosition(r->endContainer(exception), r->endOffset(exception), affinity); -} - -bool setStart(Range *r, const VisiblePosition &visiblePosition) -{ - if (!r) - return false; - Position p = rangeCompliantEquivalent(visiblePosition); - int code = 0; - r->setStart(p.node(), p.offset(), code); - return code == 0; -} - -bool setEnd(Range *r, const VisiblePosition &visiblePosition) -{ - if (!r) - return false; - Position p = rangeCompliantEquivalent(visiblePosition); - int code = 0; - r->setEnd(p.node(), p.offset(), code); - return code == 0; -} - -Node *enclosingBlockFlowElement(const VisiblePosition &visiblePosition) -{ - if (visiblePosition.isNull()) - return NULL; - - return visiblePosition.deepEquivalent().node()->enclosingBlockFlowElement(); -} - -bool isFirstVisiblePositionInNode(const VisiblePosition &visiblePosition, const Node *node) -{ - if (visiblePosition.isNull()) - return false; - - if (!visiblePosition.deepEquivalent().node()->isDescendantOf(node)) - return false; - - VisiblePosition previous = visiblePosition.previous(); - return previous.isNull() || !previous.deepEquivalent().node()->isDescendantOf(node); -} - -bool isLastVisiblePositionInNode(const VisiblePosition &visiblePosition, const Node *node) -{ - if (visiblePosition.isNull()) - return false; - - if (!visiblePosition.deepEquivalent().node()->isDescendantOf(node)) - return false; - - VisiblePosition next = visiblePosition.next(); - return next.isNull() || !next.deepEquivalent().node()->isDescendantOf(node); -} - -} // namespace WebCore - -#ifndef NDEBUG - -void showTree(const WebCore::VisiblePosition* vpos) -{ - if (vpos) - vpos->showTreeForThis(); -} - -void showTree(const WebCore::VisiblePosition& vpos) -{ - vpos.showTreeForThis(); -} - -#endif diff --git a/WebCore/editing/VisiblePosition.h b/WebCore/editing/VisiblePosition.h deleted file mode 100644 index 79f3a57..0000000 --- a/WebCore/editing/VisiblePosition.h +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Copyright (C) 2004, 2008 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 VisiblePosition_h -#define VisiblePosition_h - -#include "Node.h" -#include "Position.h" -#include "TextDirection.h" - -namespace WebCore { - -// VisiblePosition default affinity is downstream because -// the callers do not really care (they just want the -// deep position without regard to line position), and this -// is cheaper than UPSTREAM -#define VP_DEFAULT_AFFINITY DOWNSTREAM - -// Callers who do not know where on the line the position is, -// but would like UPSTREAM if at a line break or DOWNSTREAM -// otherwise, need a clear way to specify that. The -// constructors auto-correct UPSTREAM to DOWNSTREAM if the -// position is not at a line break. -#define VP_UPSTREAM_IF_POSSIBLE UPSTREAM - -class InlineBox; - -class VisiblePosition { -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(); } - - bool isNull() const { return m_deepPosition.isNull(); } - bool isNotNull() const { return m_deepPosition.isNotNull(); } - - Position deepEquivalent() const { return m_deepPosition; } - EAffinity affinity() const { ASSERT(m_affinity == UPSTREAM || m_affinity == DOWNSTREAM); return m_affinity; } - void setAffinity(EAffinity affinity) { m_affinity = affinity; } - - // next() and previous() will increment/decrement by a character cluster. - VisiblePosition next(bool stayInEditableContent = false) const; - VisiblePosition previous(bool stayInEditableContent = false) const; - VisiblePosition honorEditableBoundaryAtOrBefore(const VisiblePosition&) const; - VisiblePosition honorEditableBoundaryAtOrAfter(const VisiblePosition&) const; - - VisiblePosition left(bool stayInEditableContent = false) const; - VisiblePosition right(bool stayInEditableContent = false) const; - - UChar characterAfter() const; - UChar characterBefore() const { return previous().characterAfter(); } - - void debugPosition(const char* msg = "") const; - - Element* rootEditableElement() const { return m_deepPosition.isNotNull() ? m_deepPosition.node()->rootEditableElement() : 0; } - - void getInlineBoxAndOffset(InlineBox*& inlineBox, int& caretOffset) const - { - m_deepPosition.getInlineBoxAndOffset(m_affinity, inlineBox, caretOffset); - } - - void getInlineBoxAndOffset(TextDirection primaryDirection, InlineBox*& inlineBox, int& caretOffset) const - { - m_deepPosition.getInlineBoxAndOffset(m_affinity, primaryDirection, inlineBox, caretOffset); - } - - IntRect caretRect() const; - -#ifndef NDEBUG - void formatForDebugger(char* buffer, unsigned length) const; - void showTreeForThis() const; -#endif - -private: - void init(const Position&, EAffinity); - Position canonicalPosition(const Position&); - - Position leftVisuallyDistinctCandidate() const; - Position rightVisuallyDistinctCandidate() const; - - Position m_deepPosition; - EAffinity m_affinity; -}; - -// FIXME: This shouldn't ignore affinity. -inline bool operator==(const VisiblePosition& a, const VisiblePosition& b) -{ - return a.deepEquivalent() == b.deepEquivalent(); -} - -inline bool operator!=(const VisiblePosition& a, const VisiblePosition& b) -{ - return !(a == b); -} - -PassRefPtr<Range> makeRange(const VisiblePosition&, const VisiblePosition&); -bool setStart(Range*, const VisiblePosition&); -bool setEnd(Range*, const VisiblePosition&); -VisiblePosition startVisiblePosition(const Range*, EAffinity); -VisiblePosition endVisiblePosition(const Range*, EAffinity); - -Node *enclosingBlockFlowElement(const VisiblePosition&); - -bool isFirstVisiblePositionInNode(const VisiblePosition&, const Node*); -bool isLastVisiblePositionInNode(const VisiblePosition&, const Node*); - -} // namespace WebCore - -#ifndef NDEBUG -// Outside the WebCore namespace for ease of invocation from gdb. -void showTree(const WebCore::VisiblePosition*); -void showTree(const WebCore::VisiblePosition&); -#endif - -#endif // VisiblePosition_h diff --git a/WebCore/editing/WrapContentsInDummySpanCommand.cpp b/WebCore/editing/WrapContentsInDummySpanCommand.cpp deleted file mode 100644 index 86bf440..0000000 --- a/WebCore/editing/WrapContentsInDummySpanCommand.cpp +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (C) 2005 Apple Computer, 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 "WrapContentsInDummySpanCommand.h" - -#include "ApplyStyleCommand.h" -#include "HTMLElement.h" - -namespace WebCore { - -WrapContentsInDummySpanCommand::WrapContentsInDummySpanCommand(PassRefPtr<Element> element) - : SimpleEditCommand(element->document()), m_element(element) -{ - ASSERT(m_element); -} - -void WrapContentsInDummySpanCommand::doApply() -{ - ASSERT(m_element); - - ExceptionCode ec = 0; - - if (!m_dummySpan) - m_dummySpan = static_pointer_cast<HTMLElement>(createStyleSpanElement(document())); - - while (m_element->firstChild()) { - m_dummySpan->appendChild(m_element->firstChild(), ec); - ASSERT(ec == 0); - } - - m_element->appendChild(m_dummySpan.get(), ec); - ASSERT(ec == 0); -} - -void WrapContentsInDummySpanCommand::doUnapply() -{ - ASSERT(m_element); - ASSERT(m_dummySpan); - - ASSERT(m_element->firstChild() == m_dummySpan); - ASSERT(!m_element->firstChild()->nextSibling()); - - ExceptionCode ec = 0; - - while (m_dummySpan->firstChild()) { - m_element->appendChild(m_dummySpan->firstChild(), ec); - ASSERT(ec == 0); - } - - m_element->removeChild(m_dummySpan.get(), ec); - ASSERT(ec == 0); -} - -} // namespace WebCore diff --git a/WebCore/editing/WrapContentsInDummySpanCommand.h b/WebCore/editing/WrapContentsInDummySpanCommand.h deleted file mode 100644 index 63a2ae7..0000000 --- a/WebCore/editing/WrapContentsInDummySpanCommand.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (C) 2005, 2006, 2008 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 WrapContentsInDummySpanCommand_h -#define WrapContentsInDummySpanCommand_h - -#include "EditCommand.h" - -namespace WebCore { - -class WrapContentsInDummySpanCommand : public SimpleEditCommand { -public: - static PassRefPtr<WrapContentsInDummySpanCommand> create(PassRefPtr<Element> element) - { - return adoptRef(new WrapContentsInDummySpanCommand(element)); - } - -private: - WrapContentsInDummySpanCommand(PassRefPtr<Element>); - - virtual void doApply(); - virtual void doUnapply(); - - RefPtr<Element> m_element; - RefPtr<Element> m_dummySpan; -}; - -} // namespace WebCore - -#endif // WrapContentsInDummySpanCommand_h diff --git a/WebCore/editing/android/EditorAndroid.cpp b/WebCore/editing/android/EditorAndroid.cpp deleted file mode 100644 index 8e00326..0000000 --- a/WebCore/editing/android/EditorAndroid.cpp +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2009, The Android Open Source Project - * Copyright (C) 2006, 2007 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 "Editor.h" - -#include "ClipboardAndroid.h" - -namespace WebCore { - -PassRefPtr<Clipboard> Editor::newGeneralClipboard(ClipboardAccessPolicy policy) -{ - return new ClipboardAndroid(policy, false); -} - -} // namespace WebCore diff --git a/WebCore/editing/htmlediting.cpp b/WebCore/editing/htmlediting.cpp deleted file mode 100644 index 68ea754..0000000 --- a/WebCore/editing/htmlediting.cpp +++ /dev/null @@ -1,991 +0,0 @@ -/* - * Copyright (C) 2004, 2005, 2006, 2007 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 "htmlediting.h" - -#include "CharacterNames.h" -#include "Document.h" -#include "EditingText.h" -#include "HTMLElement.h" -#include "HTMLInterchange.h" -#include "HTMLNames.h" -#include "PositionIterator.h" -#include "RenderObject.h" -#include "RegularExpression.h" -#include "Range.h" -#include "Selection.h" -#include "Text.h" -#include "TextIterator.h" -#include "VisiblePosition.h" -#include "visible_units.h" - -using namespace std; - -namespace WebCore { - -using namespace HTMLNames; - -// Atomic means that the node has no children, or has children which are ignored for the -// purposes of editing. -bool isAtomicNode(const Node *node) -{ - return node && (!node->hasChildNodes() || editingIgnoresContent(node)); -} - -// Returns true for nodes that either have no content, or have content that is ignored (skipped -// over) while editing. There are no VisiblePositions inside these nodes. -bool editingIgnoresContent(const Node* node) -{ - return !canHaveChildrenForEditing(node) && !node->isTextNode(); -} - -bool canHaveChildrenForEditing(const Node* node) -{ - return !node->hasTagName(hrTag) && - !node->hasTagName(brTag) && - !node->hasTagName(imgTag) && - !node->hasTagName(buttonTag) && - !node->hasTagName(inputTag) && - !node->hasTagName(textareaTag) && - !node->hasTagName(objectTag) && - !node->hasTagName(iframeTag) && - !node->hasTagName(embedTag) && - !node->hasTagName(appletTag) && - !node->hasTagName(selectTag) && - !node->isTextNode(); -} - -// Compare two positions, taking into account the possibility that one or both -// could be inside a shadow tree. Only works for non-null values. -int comparePositions(const Position& a, const Position& b) -{ - Node* nodeA = a.node(); - ASSERT(nodeA); - Node* nodeB = b.node(); - ASSERT(nodeB); - int offsetA = a.offset(); - int offsetB = b.offset(); - - Node* shadowAncestorA = nodeA->shadowAncestorNode(); - if (shadowAncestorA == nodeA) - shadowAncestorA = 0; - Node* shadowAncestorB = nodeB->shadowAncestorNode(); - if (shadowAncestorB == nodeB) - shadowAncestorB = 0; - - int bias = 0; - if (shadowAncestorA != shadowAncestorB) { - if (shadowAncestorA) { - nodeA = shadowAncestorA; - offsetA = 0; - bias = 1; - } - if (shadowAncestorB) { - nodeB = shadowAncestorB; - offsetB = 0; - bias = -1; - } - } - - int result = Range::compareBoundaryPoints(nodeA, offsetA, nodeB, offsetB); - return result ? result : bias; -} - -Node* highestEditableRoot(const Position& position) -{ - Node* node = position.node(); - if (!node) - return 0; - - Node* highestRoot = editableRootForPosition(position); - if (!highestRoot) - return 0; - - node = highestRoot; - while (node) { - if (node->isContentEditable()) - highestRoot = node; - if (node->hasTagName(bodyTag)) - break; - node = node->parentNode(); - } - - return highestRoot; -} - -Node* lowestEditableAncestor(Node* node) -{ - if (!node) - return 0; - - Node *lowestRoot = 0; - while (node) { - if (node->isContentEditable()) - return node->rootEditableElement(); - if (node->hasTagName(bodyTag)) - break; - node = node->parentNode(); - } - - return lowestRoot; -} - -bool isEditablePosition(const Position& p) -{ - Node* node = p.node(); - if (!node) - return false; - - if (node->renderer() && node->renderer()->isTable()) - node = node->parentNode(); - - return node->isContentEditable(); -} - -bool isRichlyEditablePosition(const Position& p) -{ - Node* node = p.node(); - if (!node) - return false; - - if (node->renderer() && node->renderer()->isTable()) - node = node->parentNode(); - - return node->isContentRichlyEditable(); -} - -Element* editableRootForPosition(const Position& p) -{ - Node* node = p.node(); - if (!node) - return 0; - - if (node->renderer() && node->renderer()->isTable()) - node = node->parentNode(); - - return node->rootEditableElement(); -} - -bool isContentEditable(const Node* node) -{ - return node->isContentEditable(); -} - -Position nextCandidate(const Position& position) -{ - PositionIterator p = position; - while (!p.atEnd()) { - p.increment(); - if (p.isCandidate()) - return p; - } - return Position(); -} - -Position nextVisuallyDistinctCandidate(const Position& position) -{ - Position p = position; - Position downstreamStart = p.downstream(); - while (!p.atEnd()) { - p = p.next(UsingComposedCharacters); - if (p.isCandidate() && p.downstream() != downstreamStart) - return p; - } - return Position(); -} - -Position previousCandidate(const Position& position) -{ - PositionIterator p = position; - while (!p.atStart()) { - p.decrement(); - if (p.isCandidate()) - return p; - } - return Position(); -} - -Position previousVisuallyDistinctCandidate(const Position& position) -{ - Position p = position; - Position downstreamStart = p.downstream(); - while (!p.atStart()) { - p = p.previous(UsingComposedCharacters); - if (p.isCandidate() && p.downstream() != downstreamStart) - return p; - } - return Position(); -} - -VisiblePosition firstEditablePositionAfterPositionInRoot(const Position& position, Node* highestRoot) -{ - // position falls before highestRoot. - if (comparePositions(position, Position(highestRoot, 0)) == -1 && highestRoot->isContentEditable()) - return VisiblePosition(Position(highestRoot, 0)); - - Position p = position; - - if (Node* shadowAncestor = p.node()->shadowAncestorNode()) - if (shadowAncestor != p.node()) - p = Position(shadowAncestor, maxDeepOffset(shadowAncestor)); - - while (p.node() && !isEditablePosition(p) && p.node()->isDescendantOf(highestRoot)) - p = isAtomicNode(p.node()) ? positionAfterNode(p.node()) : nextVisuallyDistinctCandidate(p); - - if (p.node() && !p.node()->isDescendantOf(highestRoot)) - return VisiblePosition(); - - return VisiblePosition(p); -} - -VisiblePosition lastEditablePositionBeforePositionInRoot(const Position& position, Node* highestRoot) -{ - // When position falls after highestRoot, the result is easy to compute. - if (comparePositions(position, Position(highestRoot, maxDeepOffset(highestRoot))) == 1) - return VisiblePosition(Position(highestRoot, maxDeepOffset(highestRoot))); - - Position p = position; - - if (Node* shadowAncestor = p.node()->shadowAncestorNode()) - if (shadowAncestor != p.node()) - p = Position(shadowAncestor, 0); - - while (p.node() && !isEditablePosition(p) && p.node()->isDescendantOf(highestRoot)) - p = isAtomicNode(p.node()) ? positionBeforeNode(p.node()) : previousVisuallyDistinctCandidate(p); - - if (p.node() && !p.node()->isDescendantOf(highestRoot)) - return VisiblePosition(); - - return VisiblePosition(p); -} - -// Whether or not content before and after this node will collapse onto the same line as it. -bool isBlock(const Node* node) -{ - return node && node->renderer() && !node->renderer()->isInline(); -} - -// FIXME: Deploy this in all of the places where enclosingBlockFlow/enclosingBlockFlowOrTableElement are used. -// FIXME: Pass a position to this function. The enclosing block of [table, x] for example, should be the -// block that contains the table and not the table, and this function should be the only one responsible for -// knowing about these kinds of special cases. -Node* enclosingBlock(Node* node) -{ - return enclosingNodeOfType(Position(node, 0), &isBlock); -} - -Position rangeCompliantEquivalent(const Position& pos) -{ - if (pos.isNull()) - return Position(); - - Node *node = pos.node(); - - if (pos.offset() <= 0) { - if (node->parentNode() && (editingIgnoresContent(node) || isTableElement(node))) - return positionBeforeNode(node); - return Position(node, 0); - } - - if (node->offsetInCharacters()) - return Position(node, min(node->maxCharacterOffset(), pos.offset())); - - int maxCompliantOffset = node->childNodeCount(); - if (pos.offset() > maxCompliantOffset) { - if (node->parentNode()) - return positionAfterNode(node); - - // there is no other option at this point than to - // use the highest allowed position in the node - return Position(node, maxCompliantOffset); - } - - // Editing should never generate positions like this. - if ((pos.offset() < maxCompliantOffset) && editingIgnoresContent(node)) { - ASSERT_NOT_REACHED(); - return node->parentNode() ? positionBeforeNode(node) : Position(node, 0); - } - - if (pos.offset() == maxCompliantOffset && (editingIgnoresContent(node) || isTableElement(node))) - return positionAfterNode(node); - - return Position(pos); -} - -Position rangeCompliantEquivalent(const VisiblePosition& vpos) -{ - return rangeCompliantEquivalent(vpos.deepEquivalent()); -} - -// This method is used to create positions in the DOM. It returns the maximum valid offset -// in a node. It returns 1 for some elements even though they do not have children, which -// creates technically invalid DOM Positions. Be sure to call rangeCompliantEquivalent -// on a Position before using it to create a DOM Range, or an exception will be thrown. -int maxDeepOffset(const Node *node) -{ - ASSERT(node); - if (!node) - return 0; - if (node->offsetInCharacters()) - return node->maxCharacterOffset(); - - if (node->hasChildNodes()) - return node->childNodeCount(); - - // NOTE: This should preempt the childNodeCount for, e.g., select nodes - if (editingIgnoresContent(node)) - return 1; - - return 0; -} - -String stringWithRebalancedWhitespace(const String& string, bool startIsStartOfParagraph, bool endIsEndOfParagraph) -{ - static String twoSpaces(" "); - static String nbsp("\xa0"); - static String pattern(" \xa0"); - - String rebalancedString = string; - - rebalancedString.replace(noBreakSpace, ' '); - rebalancedString.replace('\n', ' '); - rebalancedString.replace('\t', ' '); - - rebalancedString.replace(twoSpaces, pattern); - - if (startIsStartOfParagraph && rebalancedString[0] == ' ') - rebalancedString.replace(0, 1, nbsp); - int end = rebalancedString.length() - 1; - if (endIsEndOfParagraph && rebalancedString[end] == ' ') - rebalancedString.replace(end, 1, nbsp); - - return rebalancedString; -} - -bool isTableStructureNode(const Node *node) -{ - RenderObject *r = node->renderer(); - return (r && (r->isTableCell() || r->isTableRow() || r->isTableSection() || r->isTableCol())); -} - -const String& nonBreakingSpaceString() -{ - static String nonBreakingSpaceString = String(&noBreakSpace, 1); - return nonBreakingSpaceString; -} - -// FIXME: need to dump this -bool isSpecialElement(const Node *n) -{ - if (!n) - return false; - - if (!n->isHTMLElement()) - return false; - - if (n->isLink()) - return true; - - RenderObject *renderer = n->renderer(); - if (!renderer) - return false; - - if (renderer->style()->display() == TABLE || renderer->style()->display() == INLINE_TABLE) - return true; - - if (renderer->style()->isFloating()) - return true; - - if (renderer->style()->position() != StaticPosition) - return true; - - return false; -} - -// Checks if a string is a valid tag for the FormatBlockCommand function of execCommand. Expects lower case strings. -bool validBlockTag(const String& blockTag) -{ - if (blockTag == "address" || - blockTag == "blockquote" || - blockTag == "dd" || - blockTag == "div" || - blockTag == "dl" || - blockTag == "dt" || - blockTag == "h1" || - blockTag == "h2" || - blockTag == "h3" || - blockTag == "h4" || - blockTag == "h5" || - blockTag == "h6" || - blockTag == "p" || - blockTag == "pre") - return true; - return false; -} - -static Node* firstInSpecialElement(const Position& pos) -{ - Node* rootEditableElement = pos.node()->rootEditableElement(); - for (Node* n = pos.node(); n && n->rootEditableElement() == rootEditableElement; n = n->parentNode()) - if (isSpecialElement(n)) { - VisiblePosition vPos = VisiblePosition(pos, DOWNSTREAM); - VisiblePosition firstInElement = VisiblePosition(n, 0, DOWNSTREAM); - if (isTableElement(n) && vPos == firstInElement.next()) - return n; - if (vPos == firstInElement) - return n; - } - return 0; -} - -static Node* lastInSpecialElement(const Position& pos) -{ - Node* rootEditableElement = pos.node()->rootEditableElement(); - for (Node* n = pos.node(); n && n->rootEditableElement() == rootEditableElement; n = n->parentNode()) - if (isSpecialElement(n)) { - VisiblePosition vPos = VisiblePosition(pos, DOWNSTREAM); - VisiblePosition lastInElement = VisiblePosition(n, n->childNodeCount(), DOWNSTREAM); - if (isTableElement(n) && vPos == lastInElement.previous()) - return n; - if (vPos == lastInElement) - return n; - } - return 0; -} - -bool isFirstVisiblePositionInSpecialElement(const Position& pos) -{ - return firstInSpecialElement(pos); -} - -Position positionBeforeContainingSpecialElement(const Position& pos, Node** containingSpecialElement) -{ - Node* n = firstInSpecialElement(pos); - if (!n) - return pos; - Position result = positionBeforeNode(n); - if (result.isNull() || result.node()->rootEditableElement() != pos.node()->rootEditableElement()) - return pos; - if (containingSpecialElement) - *containingSpecialElement = n; - return result; -} - -bool isLastVisiblePositionInSpecialElement(const Position& pos) -{ - return lastInSpecialElement(pos); -} - -Position positionAfterContainingSpecialElement(const Position& pos, Node **containingSpecialElement) -{ - Node* n = lastInSpecialElement(pos); - if (!n) - return pos; - Position result = positionAfterNode(n); - if (result.isNull() || result.node()->rootEditableElement() != pos.node()->rootEditableElement()) - return pos; - if (containingSpecialElement) - *containingSpecialElement = n; - return result; -} - -Position positionOutsideContainingSpecialElement(const Position &pos, Node **containingSpecialElement) -{ - if (isFirstVisiblePositionInSpecialElement(pos)) - return positionBeforeContainingSpecialElement(pos, containingSpecialElement); - if (isLastVisiblePositionInSpecialElement(pos)) - return positionAfterContainingSpecialElement(pos, containingSpecialElement); - return pos; -} - -Node* isFirstPositionAfterTable(const VisiblePosition& visiblePosition) -{ - Position upstream(visiblePosition.deepEquivalent().upstream()); - if (upstream.node() && upstream.node()->renderer() && upstream.node()->renderer()->isTable() && upstream.offset() == maxDeepOffset(upstream.node())) - return upstream.node(); - - return 0; -} - -Node* isLastPositionBeforeTable(const VisiblePosition& visiblePosition) -{ - Position downstream(visiblePosition.deepEquivalent().downstream()); - if (downstream.node() && downstream.node()->renderer() && downstream.node()->renderer()->isTable() && downstream.offset() == 0) - return downstream.node(); - - return 0; -} - -Position positionBeforeNode(const Node *node) -{ - return Position(node->parentNode(), node->nodeIndex()); -} - -Position positionAfterNode(const Node *node) -{ - return Position(node->parentNode(), node->nodeIndex() + 1); -} - -bool isListElement(Node *n) -{ - return (n && (n->hasTagName(ulTag) || n->hasTagName(olTag) || n->hasTagName(dlTag))); -} - -Node* enclosingNodeWithTag(const Position& p, const QualifiedName& tagName) -{ - if (p.isNull()) - return 0; - - Node* root = highestEditableRoot(p); - for (Node* n = p.node(); n; n = n->parentNode()) { - if (root && !isContentEditable(n)) - continue; - if (n->hasTagName(tagName)) - return n; - if (n == root) - return 0; - } - - return 0; -} - -Node* enclosingNodeOfType(const Position& p, bool (*nodeIsOfType)(const Node*), bool onlyReturnEditableNodes) -{ - if (p.isNull()) - return 0; - - Node* root = highestEditableRoot(p); - for (Node* n = p.node(); 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 && !isContentEditable(n) && onlyReturnEditableNodes) - continue; - if ((*nodeIsOfType)(n)) - return n; - if (n == root) - return 0; - } - - return 0; -} - -Node* enclosingTableCell(const Position& p) -{ - return enclosingNodeOfType(p, &isTableCell); -} - -Node* enclosingAnchorElement(const Position& p) -{ - if (p.isNull()) - return 0; - - Node* node = p.node(); - while (node && !(node->isElementNode() && node->isLink())) - node = node->parentNode(); - return node; -} - -Node* enclosingList(Node* node) -{ - if (!node) - return 0; - - Node* root = highestEditableRoot(Position(node, 0)); - - for (Node* n = node->parentNode(); n; n = n->parentNode()) { - if (n->hasTagName(ulTag) || n->hasTagName(olTag)) - return n; - if (n == root) - return 0; - } - - return 0; -} - -Node* enclosingListChild(Node *node) -{ - if (!node) - return 0; - // Check for a list item element, or for a node whose parent is a list element. Such a node - // will appear visually as a list item (but without a list marker) - Node* root = highestEditableRoot(Position(node, 0)); - - // FIXME: This function is inappropriately named if it starts with node instead of node->parentNode() - for (Node* n = node; n && n->parentNode(); n = n->parentNode()) { - if (n->hasTagName(liTag) || isListElement(n->parentNode())) - return n; - if (n == root || isTableCell(n)) - return 0; - } - - return 0; -} - -static Node* embeddedSublist(Node* listItem) -{ - // Check the DOM so that we'll find collapsed sublists without renderers. - for (Node* n = listItem->firstChild(); n; n = n->nextSibling()) { - if (isListElement(n)) - return n; - } - - return 0; -} - -static Node* appendedSublist(Node* listItem) -{ - // Check the DOM so that we'll find collapsed sublists without renderers. - for (Node* n = listItem->nextSibling(); n; n = n->nextSibling()) { - if (isListElement(n)) - return n; - if (n->renderer() && n->renderer()->isListItem()) - return 0; - } - - return 0; -} - -Node* enclosingEmptyListItem(const VisiblePosition& visiblePos) -{ - // Check that position is on a line by itself inside a list item - Node* listChildNode = enclosingListChild(visiblePos.deepEquivalent().node()); - if (!listChildNode || !isStartOfParagraph(visiblePos) || !isEndOfParagraph(visiblePos)) - return 0; - - VisiblePosition firstInListChild(Position(listChildNode, 0)); - VisiblePosition lastInListChild(Position(listChildNode, maxDeepOffset(listChildNode))); - - if (firstInListChild != visiblePos || lastInListChild != visiblePos) - return 0; - - if (embeddedSublist(listChildNode) || appendedSublist(listChildNode)) - return 0; - - return listChildNode; -} - -Node* outermostEnclosingListChild(Node* node) -{ - Node* listNode = 0; - Node* nextNode = node; - while ((nextNode = enclosingListChild(nextNode))) - listNode = nextNode; - return listNode; -} - -Node* outermostEnclosingList(Node* node) -{ - Node* listNode = 0; - Node* nextNode = node; - while ((nextNode = enclosingList(nextNode))) - listNode = nextNode; - return listNode; -} - -Node* highestAncestor(Node* node) -{ - ASSERT(node); - Node* parent = node; - while ((node = node->parentNode())) - parent = node; - return parent; -} - -// FIXME: do not require renderer, so that this can be used within fragments, or rename to isRenderedTable() -bool isTableElement(Node* n) -{ - if (!n || !n->isElementNode()) - return false; - - RenderObject* renderer = n->renderer(); - return (renderer && (renderer->style()->display() == TABLE || renderer->style()->display() == INLINE_TABLE)); -} - -bool isTableCell(const Node* node) -{ - RenderObject* r = node->renderer(); - if (!r) - return node->hasTagName(tdTag) || node->hasTagName(thTag); - - return r->isTableCell(); -} - -PassRefPtr<Element> createDefaultParagraphElement(Document *document) -{ - ExceptionCode ec = 0; - RefPtr<Element> element = document->createElementNS(xhtmlNamespaceURI, "div", ec); - ASSERT(ec == 0); - return element.release(); -} - -PassRefPtr<Element> createBreakElement(Document *document) -{ - ExceptionCode ec = 0; - RefPtr<Element> breakNode = document->createElementNS(xhtmlNamespaceURI, "br", ec); - ASSERT(ec == 0); - return breakNode.release(); -} - -PassRefPtr<Element> createOrderedListElement(Document *document) -{ - ExceptionCode ec = 0; - RefPtr<Element> element = document->createElementNS(xhtmlNamespaceURI, "ol", ec); - ASSERT(ec == 0); - return element.release(); -} - -PassRefPtr<Element> createUnorderedListElement(Document *document) -{ - ExceptionCode ec = 0; - RefPtr<Element> element = document->createElementNS(xhtmlNamespaceURI, "ul", ec); - ASSERT(ec == 0); - return element.release(); -} - -PassRefPtr<Element> createListItemElement(Document *document) -{ - ExceptionCode ec = 0; - RefPtr<Element> breakNode = document->createElementNS(xhtmlNamespaceURI, "li", ec); - ASSERT(ec == 0); - return breakNode.release(); -} - -PassRefPtr<Element> createElement(Document* document, const String& tagName) -{ - ExceptionCode ec = 0; - RefPtr<Element> breakNode = document->createElementNS(xhtmlNamespaceURI, tagName, ec); - ASSERT(ec == 0); - return breakNode.release(); -} - -bool isTabSpanNode(const Node *node) -{ - return node && node->hasTagName(spanTag) && node->isElementNode() && static_cast<const Element *>(node)->getAttribute(classAttr) == AppleTabSpanClass; -} - -bool isTabSpanTextNode(const Node *node) -{ - return node && node->isTextNode() && node->parentNode() && isTabSpanNode(node->parentNode()); -} - -Node *tabSpanNode(const Node *node) -{ - return isTabSpanTextNode(node) ? node->parentNode() : 0; -} - -Position positionBeforeTabSpan(const Position& pos) -{ - Node *node = pos.node(); - if (isTabSpanTextNode(node)) - node = tabSpanNode(node); - else if (!isTabSpanNode(node)) - return pos; - - return positionBeforeNode(node); -} - -PassRefPtr<Element> createTabSpanElement(Document* document, PassRefPtr<Node> tabTextNode) -{ - // make the span to hold the tab - ExceptionCode ec = 0; - RefPtr<Element> spanElement = document->createElementNS(xhtmlNamespaceURI, "span", ec); - ASSERT(ec == 0); - spanElement->setAttribute(classAttr, AppleTabSpanClass); - spanElement->setAttribute(styleAttr, "white-space:pre"); - - // add tab text to that span - if (!tabTextNode) - tabTextNode = document->createEditingTextNode("\t"); - spanElement->appendChild(tabTextNode, ec); - ASSERT(ec == 0); - - return spanElement.release(); -} - -PassRefPtr<Element> createTabSpanElement(Document* document, const String& tabText) -{ - return createTabSpanElement(document, document->createTextNode(tabText)); -} - -PassRefPtr<Element> createTabSpanElement(Document* document) -{ - return createTabSpanElement(document, PassRefPtr<Node>()); -} - -bool isNodeRendered(const Node *node) -{ - if (!node) - return false; - - RenderObject *renderer = node->renderer(); - if (!renderer) - return false; - - return renderer->style()->visibility() == VISIBLE; -} - -Node *nearestMailBlockquote(const Node *node) -{ - for (Node *n = const_cast<Node *>(node); n; n = n->parentNode()) { - if (isMailBlockquote(n)) - return n; - } - return 0; -} - -bool isMailBlockquote(const Node *node) -{ - if (!node || !node->isElementNode() && !node->hasTagName(blockquoteTag)) - return false; - - return static_cast<const Element *>(node)->getAttribute("type") == "cite"; -} - -int caretMinOffset(const Node* n) -{ - RenderObject* r = n->renderer(); - ASSERT(!n->isCharacterDataNode() || !r || r->isText()); // FIXME: This was a runtime check that seemingly couldn't fail; changed it to an assertion for now. - return r ? r->caretMinOffset() : 0; -} - -int caretMaxOffset(const Node* n) -{ - RenderObject* r = n->renderer(); - ASSERT(!n->isCharacterDataNode() || !r || r->isText()); // FIXME: This was a runtime check that seemingly couldn't fail; changed it to an assertion for now. - if (r) - return r->caretMaxOffset(); - - if (n->isCharacterDataNode()) { - const CharacterData* c = static_cast<const CharacterData*>(n); - return static_cast<int>(c->length()); - } - return 1; -} - -bool lineBreakExistsAtPosition(const VisiblePosition& visiblePosition) -{ - if (visiblePosition.isNull()) - return false; - - Position downstream(visiblePosition.deepEquivalent().downstream()); - return downstream.node()->hasTagName(brTag) || - downstream.node()->isTextNode() && downstream.node()->renderer()->style()->preserveNewline() && visiblePosition.characterAfter() == '\n'; -} - -// Modifies selections that have an end point at the edge of a table -// that contains the other endpoint so that they don't confuse -// code that iterates over selected paragraphs. -Selection selectionForParagraphIteration(const Selection& original) -{ - Selection newSelection(original); - VisiblePosition startOfSelection(newSelection.visibleStart()); - VisiblePosition endOfSelection(newSelection.visibleEnd()); - - // If the end of the selection to modify is just after a table, and - // if the start of the selection is inside that table, then the last paragraph - // 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)) - newSelection = Selection(startOfSelection, endOfSelection.previous(true)); - - // If the start of the selection to modify is just before a table, - // and if the end of the selection is inside that table, then the first paragraph - // 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)) - newSelection = Selection(startOfSelection.next(true), endOfSelection); - - return newSelection; -} - - -int indexForVisiblePosition(VisiblePosition& visiblePosition) -{ - if (visiblePosition.isNull()) - return 0; - Position p(visiblePosition.deepEquivalent()); - RefPtr<Range> range = Range::create(p.node()->document(), Position(p.node()->document(), 0), rangeCompliantEquivalent(p)); - return TextIterator::rangeLength(range.get(), true); -} - -PassRefPtr<Range> avoidIntersectionWithNode(const Range* range, Node* node) -{ - if (!range) - return 0; - - Document* document = range->ownerDocument(); - - Node* startContainer = range->startContainer(); - int startOffset = range->startOffset(); - Node* endContainer = range->endContainer(); - int endOffset = range->endOffset(); - - if (!startContainer) - return 0; - - ASSERT(endContainer); - - if (startContainer == node || startContainer->isDescendantOf(node)) { - ASSERT(node->parentNode()); - startContainer = node->parentNode(); - startOffset = node->nodeIndex(); - } - if (endContainer == node || endContainer->isDescendantOf(node)) { - ASSERT(node->parentNode()); - endContainer = node->parentNode(); - endOffset = node->nodeIndex(); - } - - return Range::create(document, startContainer, startOffset, endContainer, endOffset); -} - -Selection avoidIntersectionWithNode(const Selection& selection, Node* node) -{ - if (selection.isNone()) - return Selection(selection); - - Selection updatedSelection(selection); - Node* base = selection.base().node(); - Node* extent = selection.extent().node(); - ASSERT(base); - ASSERT(extent); - - if (base == node || base->isDescendantOf(node)) { - ASSERT(node->parentNode()); - updatedSelection.setBase(Position(node->parentNode(), node->nodeIndex())); - } - - if (extent == node || extent->isDescendantOf(node)) { - ASSERT(node->parentNode()); - updatedSelection.setExtent(Position(node->parentNode(), node->nodeIndex())); - } - - return updatedSelection; -} - -} // namespace WebCore diff --git a/WebCore/editing/htmlediting.h b/WebCore/editing/htmlediting.h deleted file mode 100644 index 84d3434..0000000 --- a/WebCore/editing/htmlediting.h +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Copyright (C) 2004, 2006 Apple Computer, 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 htmlediting_h -#define htmlediting_h - -#include <wtf/Forward.h> -#include "HTMLNames.h" - -namespace WebCore { - -class Document; -class Element; -class Node; -class Position; -class Range; -class Selection; -class String; -class VisiblePosition; - -Position rangeCompliantEquivalent(const Position&); -Position rangeCompliantEquivalent(const VisiblePosition&); -int maxDeepOffset(const Node*); -bool isAtomicNode(const Node*); -bool editingIgnoresContent(const Node*); -bool canHaveChildrenForEditing(const Node*); -Node* highestEditableRoot(const Position&); -VisiblePosition firstEditablePositionAfterPositionInRoot(const Position&, Node*); -VisiblePosition lastEditablePositionBeforePositionInRoot(const Position&, Node*); -int comparePositions(const Position&, const Position&); -Node* lowestEditableAncestor(Node*); -bool isContentEditable(const Node*); -Position nextCandidate(const Position&); -Position nextVisuallyDistinctCandidate(const Position&); -Position previousCandidate(const Position&); -Position previousVisuallyDistinctCandidate(const Position&); -bool isEditablePosition(const Position&); -bool isRichlyEditablePosition(const Position&); -Element* editableRootForPosition(const Position&); -bool isBlock(const Node*); -Node* enclosingBlock(Node*); - -String stringWithRebalancedWhitespace(const String&, bool, bool); -const String& nonBreakingSpaceString(); - -//------------------------------------------------------------------------------------------ - -Position positionBeforeNode(const Node*); -Position positionAfterNode(const Node*); - -PassRefPtr<Range> avoidIntersectionWithNode(const Range*, Node*); -Selection avoidIntersectionWithNode(const Selection&, Node*); - -bool isSpecialElement(const Node*); -bool validBlockTag(const String&); - -PassRefPtr<Element> createDefaultParagraphElement(Document*); -PassRefPtr<Element> createBreakElement(Document*); -PassRefPtr<Element> createOrderedListElement(Document*); -PassRefPtr<Element> createUnorderedListElement(Document*); -PassRefPtr<Element> createListItemElement(Document*); -PassRefPtr<Element> createElement(Document*, const String&); - -bool isTabSpanNode(const Node*); -bool isTabSpanTextNode(const Node*); -Node* tabSpanNode(const Node*); -Position positionBeforeTabSpan(const Position&); -PassRefPtr<Element> createTabSpanElement(Document*); -PassRefPtr<Element> createTabSpanElement(Document*, PassRefPtr<Node> tabTextNode); -PassRefPtr<Element> createTabSpanElement(Document*, const String& tabText); - -bool isNodeRendered(const Node*); -bool isMailBlockquote(const Node*); -Node* nearestMailBlockquote(const Node*); -int caretMinOffset(const Node*); -int caretMaxOffset(const Node*); - -//------------------------------------------------------------------------------------------ - -bool isTableStructureNode(const Node*); -PassRefPtr<Element> createBlockPlaceholderElement(Document*); - -bool isFirstVisiblePositionInSpecialElement(const Position&); -Position positionBeforeContainingSpecialElement(const Position&, Node** containingSpecialElement=0); -bool isLastVisiblePositionInSpecialElement(const Position&); -Position positionAfterContainingSpecialElement(const Position&, Node** containingSpecialElement=0); -Position positionOutsideContainingSpecialElement(const Position&, Node** containingSpecialElement=0); -Node* isLastPositionBeforeTable(const VisiblePosition&); -Node* isFirstPositionAfterTable(const VisiblePosition&); - -Node* enclosingNodeWithTag(const Position&, const QualifiedName&); -Node* enclosingNodeOfType(const Position&, bool (*nodeIsOfType)(const Node*), bool onlyReturnEditableNodes = true); -Node* enclosingTableCell(const Position&); -Node* enclosingEmptyListItem(const VisiblePosition&); -Node* enclosingAnchorElement(const Position&); -bool isListElement(Node*); -Node* enclosingList(Node*); -Node* outermostEnclosingList(Node*); -Node* enclosingListChild(Node*); -Node* highestAncestor(Node*); -bool isTableElement(Node*); -bool isTableCell(const Node*); - -bool lineBreakExistsAtPosition(const VisiblePosition&); - -Selection selectionForParagraphIteration(const Selection&); - -int indexForVisiblePosition(VisiblePosition&); - -} - -#endif diff --git a/WebCore/editing/mac/EditorMac.mm b/WebCore/editing/mac/EditorMac.mm deleted file mode 100644 index b98fc00..0000000 --- a/WebCore/editing/mac/EditorMac.mm +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright (C) 2006, 2007, 2008 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. - */ - -#import "config.h" -#import "Editor.h" - -#import "ClipboardMac.h" -#import "DocLoader.h" -#import "Frame.h" -#import "FrameView.h" - -namespace WebCore { - -extern "C" { - -// Kill ring calls. Would be better to use NSKillRing.h, but that's not available as API or SPI. - -void _NSInitializeKillRing(); -void _NSAppendToKillRing(NSString *); -void _NSPrependToKillRing(NSString *); -NSString *_NSYankFromKillRing(); -void _NSNewKillRingSequence(); -void _NSSetKillRingToYankedState(); - -} - -PassRefPtr<Clipboard> Editor::newGeneralClipboard(ClipboardAccessPolicy policy) -{ - return ClipboardMac::create(false, [NSPasteboard generalPasteboard], policy, 0); -} - -static void initializeKillRingIfNeeded() -{ - static bool initializedKillRing = false; - if (!initializedKillRing) { - initializedKillRing = true; - _NSInitializeKillRing(); - } -} - -void Editor::appendToKillRing(const String& string) -{ - initializeKillRingIfNeeded(); - _NSAppendToKillRing(string); -} - -void Editor::prependToKillRing(const String& string) -{ - initializeKillRingIfNeeded(); - _NSPrependToKillRing(string); -} - -String Editor::yankFromKillRing() -{ - initializeKillRingIfNeeded(); - return _NSYankFromKillRing(); -} - -void Editor::startNewKillRingSequence() -{ - initializeKillRingIfNeeded(); - _NSNewKillRingSequence(); -} - -void Editor::setKillRingToYankedState() -{ - initializeKillRingIfNeeded(); - _NSSetKillRingToYankedState(); -} - -void Editor::showFontPanel() -{ - [[NSFontManager sharedFontManager] orderFrontFontPanel:nil]; -} - -void Editor::showStylesPanel() -{ - [[NSFontManager sharedFontManager] orderFrontStylesPanel:nil]; -} - -void Editor::showColorPanel() -{ - [[NSApplication sharedApplication] orderFrontColorPanel:nil]; -} - -// FIXME: We want to use the platform-independent code instead. But when we last -// tried to do so it seemed that we first need to move more of the logic from -// -[WebHTMLView.cpp _documentFragmentFromPasteboard] into PasteboardMac. - -void Editor::paste() -{ - ASSERT(m_frame->document()); - FrameView* view = m_frame->view(); - if (!view) - return; - DocLoader* loader = m_frame->document()->docLoader(); - loader->setAllowStaleResources(true); - [view->documentView() tryToPerform:@selector(paste:) with:nil]; - loader->setAllowStaleResources(false); -} - -} // namespace WebCore diff --git a/WebCore/editing/mac/SelectionControllerMac.mm b/WebCore/editing/mac/SelectionControllerMac.mm deleted file mode 100644 index e07024a..0000000 --- a/WebCore/editing/mac/SelectionControllerMac.mm +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (C) 2007 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 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. - */ - -#import "config.h" -#import "SelectionController.h" - -#import "AXObjectCache.h" -#import "Document.h" -#import "Frame.h" -#import "FrameView.h" -#import "RenderView.h" -#import "Selection.h" - -#import <ApplicationServices/ApplicationServices.h> - -namespace WebCore { - -void SelectionController::notifyAccessibilityForSelectionChange() -{ - if (AXObjectCache::accessibilityEnabled() && m_sel.start().isNotNull() && m_sel.end().isNotNull()) - m_frame->document()->axObjectCache()->postNotification(m_sel.start().node()->renderer(), "AXSelectedTextChanged"); - - // if zoom feature is enabled, insertion point changes should update the zoom - if (UAZoomEnabled() && m_sel.isCaret() && m_sel.start().node()) { - RenderView *renderView = static_cast<RenderView*>(m_sel.start().node()->renderer()); - if (renderView) { - IntRect selectionRect = caretRect(); - IntRect viewRect = renderView->viewRect(); - FrameView* frameView = renderView->view()->frameView(); - if (frameView) { - selectionRect = frameView->contentsToScreen(selectionRect); - viewRect = frameView->contentsToScreen(viewRect); - CGRect cgCaretRect = CGRectMake(selectionRect.x(), selectionRect.y(), selectionRect.width(), selectionRect.height()); - CGRect cgViewRect = CGRectMake(viewRect.x(), viewRect.y(), viewRect.width(), viewRect.height()); - (void)UAZoomChangeFocus(&cgViewRect, &cgCaretRect, kUAZoomFocusTypeInsertionPoint); - } - } - } -} - - -} // namespace WebCore diff --git a/WebCore/editing/markup.cpp b/WebCore/editing/markup.cpp deleted file mode 100644 index b067002..0000000 --- a/WebCore/editing/markup.cpp +++ /dev/null @@ -1,1219 +0,0 @@ -/* - * Copyright (C) 2004, 2005, 2006, 2007, 2008 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 "markup.h" - -#include "CDATASection.h" -#include "CharacterNames.h" -#include "Comment.h" -#include "CSSComputedStyleDeclaration.h" -#include "CSSPrimitiveValue.h" -#include "CSSProperty.h" -#include "CSSPropertyNames.h" -#include "CSSRule.h" -#include "CSSRuleList.h" -#include "CSSStyleRule.h" -#include "CSSStyleSelector.h" -#include "CSSValue.h" -#include "CSSValueKeywords.h" -#include "DeleteButtonController.h" -#include "Document.h" -#include "DocumentFragment.h" -#include "DocumentType.h" -#include "Editor.h" -#include "Frame.h" -#include "HTMLElement.h" -#include "HTMLNames.h" -#include "InlineTextBox.h" -#include "Logging.h" -#include "ProcessingInstruction.h" -#include "QualifiedName.h" -#include "Range.h" -#include "Selection.h" -#include "TextIterator.h" -#include "htmlediting.h" -#include "visible_units.h" - -using namespace std; - -namespace WebCore { - -using namespace HTMLNames; - -static inline bool shouldSelfClose(const Node *node); - -class AttributeChange { -public: - AttributeChange() - : m_name(nullAtom, nullAtom, nullAtom) - { - } - - AttributeChange(PassRefPtr<Element> element, const QualifiedName& name, const String& value) - : m_element(element), m_name(name), m_value(value) - { - } - - void apply() - { - m_element->setAttribute(m_name, m_value); - } - -private: - RefPtr<Element> m_element; - QualifiedName m_name; - String m_value; -}; - -static void appendAttributeValue(Vector<UChar>& result, const String& attr, bool escapeNBSP) -{ - const UChar* uchars = attr.characters(); - unsigned len = attr.length(); - unsigned lastCopiedFrom = 0; - - static const String ampEntity("&"); - static const String gtEntity(">"); - static const String ltEntity("<"); - static const String quotEntity("""); - static const String nbspEntity(" "); - - for (unsigned i = 0; i < len; ++i) { - UChar c = uchars[i]; - switch (c) { - case '&': - result.append(uchars + lastCopiedFrom, i - lastCopiedFrom); - append(result, ampEntity); - lastCopiedFrom = i + 1; - break; - case '<': - result.append(uchars + lastCopiedFrom, i - lastCopiedFrom); - append(result, ltEntity); - lastCopiedFrom = i + 1; - break; - case '>': - result.append(uchars + lastCopiedFrom, i - lastCopiedFrom); - append(result, gtEntity); - lastCopiedFrom = i + 1; - break; - case '"': - result.append(uchars + lastCopiedFrom, i - lastCopiedFrom); - append(result, quotEntity); - lastCopiedFrom = i + 1; - break; - case noBreakSpace: - if (escapeNBSP) { - result.append(uchars + lastCopiedFrom, i - lastCopiedFrom); - append(result, nbspEntity); - lastCopiedFrom = i + 1; - } - break; - } - } - - result.append(uchars + lastCopiedFrom, len - lastCopiedFrom); -} - -static void appendEscapedContent(Vector<UChar>& result, pair<const UChar*, size_t> range, bool escapeNBSP) -{ - const UChar* uchars = range.first; - unsigned len = range.second; - unsigned lastCopiedFrom = 0; - - static const String ampEntity("&"); - static const String gtEntity(">"); - static const String ltEntity("<"); - static const String nbspEntity(" "); - - for (unsigned i = 0; i < len; ++i) { - UChar c = uchars[i]; - switch (c) { - case '&': - result.append(uchars + lastCopiedFrom, i - lastCopiedFrom); - append(result, ampEntity); - lastCopiedFrom = i + 1; - break; - case '<': - result.append(uchars + lastCopiedFrom, i - lastCopiedFrom); - append(result, ltEntity); - lastCopiedFrom = i + 1; - break; - case '>': - result.append(uchars + lastCopiedFrom, i - lastCopiedFrom); - append(result, gtEntity); - lastCopiedFrom = i + 1; - break; - case noBreakSpace: - if (escapeNBSP) { - result.append(uchars + lastCopiedFrom, i - lastCopiedFrom); - append(result, nbspEntity); - lastCopiedFrom = i + 1; - } - break; - } - } - - result.append(uchars + lastCopiedFrom, len - lastCopiedFrom); -} - -static String escapeContentText(const String& in, bool escapeNBSP) -{ - Vector<UChar> buffer; - appendEscapedContent(buffer, make_pair(in.characters(), in.length()), escapeNBSP); - return String::adopt(buffer); -} - -static void appendQuotedURLAttributeValue(Vector<UChar>& result, const String& urlString) -{ - UChar quoteChar = '\"'; - String strippedURLString = urlString.stripWhiteSpace(); - if (protocolIs(strippedURLString, "javascript")) { - // minimal escaping for javascript urls - if (strippedURLString.contains('"')) { - if (strippedURLString.contains('\'')) - strippedURLString.replace('\"', """); - else - quoteChar = '\''; - } - result.append(quoteChar); - append(result, strippedURLString); - result.append(quoteChar); - return; - } - - // FIXME: This does not fully match other browsers. Firefox percent-escapes non-ASCII characters for innerHTML. - result.append(quoteChar); - appendAttributeValue(result, urlString, false); - result.append(quoteChar); -} - -static String stringValueForRange(const Node* node, const Range* range) -{ - if (!range) - return node->nodeValue(); - - String str = node->nodeValue(); - ExceptionCode ec; - if (node == range->endContainer(ec)) - str.truncate(range->endOffset(ec)); - if (node == range->startContainer(ec)) - str.remove(0, range->startOffset(ec)); - return str; -} - -static inline pair<const UChar*, size_t> ucharRange(const Node *node, const Range *range) -{ - String str = node->nodeValue(); - const UChar* characters = str.characters(); - size_t length = str.length(); - - if (range) { - ExceptionCode ec; - if (node == range->endContainer(ec)) - length = range->endOffset(ec); - if (node == range->startContainer(ec)) { - size_t start = range->startOffset(ec); - characters += start; - length -= start; - } - } - - return make_pair(characters, length); -} - -static inline void appendUCharRange(Vector<UChar>& result, const pair<const UChar*, size_t> range) -{ - result.append(range.first, range.second); -} - -static String renderedText(const Node* node, const Range* range) -{ - if (!node->isTextNode()) - return String(); - - ExceptionCode ec; - const Text* textNode = static_cast<const Text*>(node); - unsigned startOffset = 0; - unsigned endOffset = textNode->length(); - - if (range && node == range->startContainer(ec)) - startOffset = range->startOffset(ec); - if (range && node == range->endContainer(ec)) - endOffset = range->endOffset(ec); - - Position start(const_cast<Node*>(node), startOffset); - Position end(const_cast<Node*>(node), endOffset); - return plainText(Range::create(node->document(), start, end).get()); -} - -static PassRefPtr<CSSMutableStyleDeclaration> styleFromMatchedRulesForElement(Element* element, bool authorOnly = true) -{ - RefPtr<CSSMutableStyleDeclaration> style = CSSMutableStyleDeclaration::create(); - RefPtr<CSSRuleList> matchedRules = element->document()->styleSelector()->styleRulesForElement(element, authorOnly); - if (matchedRules) { - for (unsigned i = 0; i < matchedRules->length(); i++) { - if (matchedRules->item(i)->type() == CSSRule::STYLE_RULE) { - RefPtr<CSSMutableStyleDeclaration> s = static_cast<CSSStyleRule*>(matchedRules->item(i))->style(); - style->merge(s.get(), true); - } - } - } - - return style.release(); -} - -static void removeEnclosingMailBlockquoteStyle(CSSMutableStyleDeclaration* style, Node* node) -{ - Node* blockquote = nearestMailBlockquote(node); - if (!blockquote || !blockquote->parentNode()) - return; - - RefPtr<CSSMutableStyleDeclaration> parentStyle = Position(blockquote->parentNode(), 0).computedStyle()->copyInheritableProperties(); - RefPtr<CSSMutableStyleDeclaration> blockquoteStyle = Position(blockquote, 0).computedStyle()->copyInheritableProperties(); - parentStyle->diff(blockquoteStyle.get()); - blockquoteStyle->diff(style); -} - -static void removeDefaultStyles(CSSMutableStyleDeclaration* style, Document* document) -{ - if (!document || !document->documentElement()) - return; - - RefPtr<CSSMutableStyleDeclaration> documentStyle = computedStyle(document->documentElement())->copyInheritableProperties(); - documentStyle->diff(style); -} - -static bool shouldAddNamespaceElem(const Element* elem) -{ - // Don't add namespace attribute if it is already defined for this elem. - const AtomicString& prefix = elem->prefix(); - AtomicString attr = !prefix.isEmpty() ? "xmlns:" + prefix : "xmlns"; - return !elem->hasAttribute(attr); -} - -static bool shouldAddNamespaceAttr(const Attribute* attr, HashMap<AtomicStringImpl*, AtomicStringImpl*>& namespaces) -{ - // Don't add namespace attributes twice - static const AtomicString xmlnsURI = "http://www.w3.org/2000/xmlns/"; - static const QualifiedName xmlnsAttr(nullAtom, "xmlns", xmlnsURI); - if (attr->name() == xmlnsAttr) { - namespaces.set(emptyAtom.impl(), attr->value().impl()); - return false; - } - - QualifiedName xmlnsPrefixAttr("xmlns", attr->localName(), xmlnsURI); - if (attr->name() == xmlnsPrefixAttr) { - namespaces.set(attr->localName().impl(), attr->value().impl()); - return false; - } - - return true; -} - -static void appendNamespace(Vector<UChar>& result, const AtomicString& prefix, const AtomicString& ns, HashMap<AtomicStringImpl*, AtomicStringImpl*>& namespaces) -{ - if (ns.isEmpty()) - return; - - // Use emptyAtoms's impl() for both null and empty strings since the HashMap can't handle 0 as a key - AtomicStringImpl* pre = prefix.isEmpty() ? emptyAtom.impl() : prefix.impl(); - AtomicStringImpl* foundNS = namespaces.get(pre); - if (foundNS != ns.impl()) { - namespaces.set(pre, ns.impl()); - static const String xmlns("xmlns"); - result.append(' '); - append(result, xmlns); - if (!prefix.isEmpty()) { - result.append(':'); - append(result, prefix); - } - - result.append('='); - result.append('"'); - appendAttributeValue(result, ns, false); - result.append('"'); - } -} - -static void appendDocumentType(Vector<UChar>& result, const DocumentType* n) -{ - if (n->name().isEmpty()) - return; - - append(result, "<!DOCTYPE "); - append(result, n->name()); - if (!n->publicId().isEmpty()) { - append(result, " PUBLIC \""); - append(result, n->publicId()); - append(result, "\""); - if (!n->systemId().isEmpty()) { - append(result, " \""); - append(result, n->systemId()); - append(result, "\""); - } - } else if (!n->systemId().isEmpty()) { - append(result, " SYSTEM \""); - append(result, n->systemId()); - append(result, "\""); - } - if (!n->internalSubset().isEmpty()) { - append(result, " ["); - append(result, n->internalSubset()); - append(result, "]"); - } - append(result, ">"); -} - -static void appendStartMarkup(Vector<UChar>& result, const Node *node, const Range *range, EAnnotateForInterchange annotate, bool convertBlocksToInlines = false, HashMap<AtomicStringImpl*, AtomicStringImpl*>* namespaces = 0) -{ - bool documentIsHTML = node->document()->isHTMLDocument(); - switch (node->nodeType()) { - case Node::TEXT_NODE: { - if (Node* parent = node->parentNode()) { - if (parent->hasTagName(scriptTag) - || parent->hasTagName(styleTag) - || parent->hasTagName(textareaTag) - || parent->hasTagName(xmpTag)) { - appendUCharRange(result, ucharRange(node, range)); - break; - } - } - if (!annotate) { - appendEscapedContent(result, ucharRange(node, range), documentIsHTML); - break; - } - - bool useRenderedText = !enclosingNodeWithTag(Position(const_cast<Node*>(node), 0), selectTag); - String markup = escapeContentText(useRenderedText ? renderedText(node, range) : stringValueForRange(node, range), false); - if (annotate) - markup = convertHTMLTextToInterchangeFormat(markup, static_cast<const Text*>(node)); - append(result, markup); - break; - } - case Node::COMMENT_NODE: - // FIXME: Comment content is not escaped, but XMLSerializer (and possibly other callers) should raise an exception if it includes "-->". - append(result, "<!--"); - append(result, static_cast<const Comment*>(node)->nodeValue()); - append(result, "-->"); - break; - case Node::DOCUMENT_NODE: - case Node::DOCUMENT_FRAGMENT_NODE: - break; - case Node::DOCUMENT_TYPE_NODE: - appendDocumentType(result, static_cast<const DocumentType*>(node)); - break; - case Node::PROCESSING_INSTRUCTION_NODE: { - // FIXME: PI data is not escaped, but XMLSerializer (and possibly other callers) this should raise an exception if it includes "?>". - const ProcessingInstruction* n = static_cast<const ProcessingInstruction*>(node); - append(result, "<?"); - append(result, n->target()); - append(result, " "); - append(result, n->data()); - append(result, "?>"); - break; - } - case Node::ELEMENT_NODE: { - result.append('<'); - const Element* el = static_cast<const Element*>(node); - bool convert = convertBlocksToInlines & isBlock(const_cast<Node*>(node)); - append(result, el->nodeNamePreservingCase()); - NamedAttrMap *attrs = el->attributes(); - unsigned length = attrs->length(); - if (!documentIsHTML && namespaces && shouldAddNamespaceElem(el)) - appendNamespace(result, el->prefix(), el->namespaceURI(), *namespaces); - - for (unsigned int i = 0; i < length; i++) { - Attribute *attr = attrs->attributeItem(i); - // We'll handle the style attribute separately, below. - if (attr->name() == styleAttr && el->isHTMLElement() && (annotate || convert)) - continue; - result.append(' '); - - if (documentIsHTML) - append(result, attr->name().localName()); - else - append(result, attr->name().toString()); - - result.append('='); - - if (el->isURLAttribute(attr)) - appendQuotedURLAttributeValue(result, attr->value()); - else { - result.append('\"'); - appendAttributeValue(result, attr->value(), documentIsHTML); - result.append('\"'); - } - - if (!documentIsHTML && namespaces && shouldAddNamespaceAttr(attr, *namespaces)) - appendNamespace(result, attr->prefix(), attr->namespaceURI(), *namespaces); - } - - if (el->isHTMLElement() && (annotate || convert)) { - Element* element = const_cast<Element*>(el); - RefPtr<CSSMutableStyleDeclaration> style = static_cast<HTMLElement*>(element)->getInlineStyleDecl()->copy(); - if (annotate) { - RefPtr<CSSMutableStyleDeclaration> styleFromMatchedRules = styleFromMatchedRulesForElement(const_cast<Element*>(el)); - // Styles from the inline style declaration, held in the variable "style", take precedence - // over those from matched rules. - styleFromMatchedRules->merge(style.get()); - style = styleFromMatchedRules; - - RefPtr<CSSComputedStyleDeclaration> computedStyleForElement = computedStyle(element); - RefPtr<CSSMutableStyleDeclaration> fromComputedStyle = CSSMutableStyleDeclaration::create(); - - DeprecatedValueListConstIterator<CSSProperty> end; - for (DeprecatedValueListConstIterator<CSSProperty> it = style->valuesIterator(); it != end; ++it) { - const CSSProperty& property = *it; - CSSValue* value = property.value(); - // The property value, if it's a percentage, may not reflect the actual computed value. - // For example: style="height: 1%; overflow: visible;" in quirksmode - // FIXME: There are others like this, see <rdar://problem/5195123> Slashdot copy/paste fidelity problem - if (value->cssValueType() == CSSValue::CSS_PRIMITIVE_VALUE) - if (static_cast<CSSPrimitiveValue*>(value)->primitiveType() == CSSPrimitiveValue::CSS_PERCENTAGE) - if (RefPtr<CSSValue> computedPropertyValue = computedStyleForElement->getPropertyCSSValue(property.id())) - fromComputedStyle->addParsedProperty(CSSProperty(property.id(), computedPropertyValue)); - } - - style->merge(fromComputedStyle.get()); - } - if (convert) - style->setProperty(CSSPropertyDisplay, CSSValueInline, true); - if (style->length() > 0) { - static const String stylePrefix(" style=\""); - append(result, stylePrefix); - appendAttributeValue(result, style->cssText(), documentIsHTML); - result.append('\"'); - } - } - - if (shouldSelfClose(el)) { - if (el->isHTMLElement()) - result.append(' '); // XHTML 1.0 <-> HTML compatibility. - result.append('/'); - } - result.append('>'); - break; - } - case Node::CDATA_SECTION_NODE: { - // FIXME: CDATA content is not escaped, but XMLSerializer (and possibly other callers) should raise an exception if it includes "]]>". - const CDATASection* n = static_cast<const CDATASection*>(node); - append(result, "<![CDATA["); - append(result, n->data()); - append(result, "]]>"); - break; - } - case Node::ATTRIBUTE_NODE: - case Node::ENTITY_NODE: - case Node::ENTITY_REFERENCE_NODE: - case Node::NOTATION_NODE: - case Node::XPATH_NAMESPACE_NODE: - ASSERT_NOT_REACHED(); - break; - } -} - -static String getStartMarkup(const Node *node, const Range *range, EAnnotateForInterchange annotate, bool convertBlocksToInlines = false, HashMap<AtomicStringImpl*, AtomicStringImpl*>* namespaces = 0) -{ - Vector<UChar> result; - appendStartMarkup(result, node, range, annotate, convertBlocksToInlines, namespaces); - return String::adopt(result); -} - -static inline bool doesHTMLForbidEndTag(const Node *node) -{ - if (node->isHTMLElement()) { - const HTMLElement* htmlElt = static_cast<const HTMLElement*>(node); - return (htmlElt->endTagRequirement() == TagStatusForbidden); - } - return false; -} - -// Rules of self-closure -// 1. No elements in HTML documents use the self-closing syntax. -// 2. Elements w/ children never self-close because they use a separate end tag. -// 3. HTML elements which do not have a "forbidden" end tag will close with a separate end tag. -// 4. Other elements self-close. -static inline bool shouldSelfClose(const Node *node) -{ - if (node->document()->isHTMLDocument()) - return false; - if (node->hasChildNodes()) - return false; - if (node->isHTMLElement() && !doesHTMLForbidEndTag(node)) - return false; - return true; -} - -static void appendEndMarkup(Vector<UChar>& result, const Node* node) -{ - if (!node->isElementNode() || shouldSelfClose(node) || (!node->hasChildNodes() && doesHTMLForbidEndTag(node))) - return; - - result.append('<'); - result.append('/'); - append(result, static_cast<const Element*>(node)->nodeNamePreservingCase()); - result.append('>'); -} - -static String getEndMarkup(const Node *node) -{ - Vector<UChar> result; - appendEndMarkup(result, node); - return String::adopt(result); -} - -static void appendMarkup(Vector<UChar>& result, Node* startNode, bool onlyIncludeChildren, Vector<Node*>* nodes, const HashMap<AtomicStringImpl*, AtomicStringImpl*>* namespaces = 0) -{ - HashMap<AtomicStringImpl*, AtomicStringImpl*> namespaceHash; - if (namespaces) - namespaceHash = *namespaces; - - if (!onlyIncludeChildren) { - if (nodes) - nodes->append(startNode); - - appendStartMarkup(result,startNode, 0, DoNotAnnotateForInterchange, false, &namespaceHash); - } - // print children - if (!(startNode->document()->isHTMLDocument() && doesHTMLForbidEndTag(startNode))) - for (Node* current = startNode->firstChild(); current; current = current->nextSibling()) - appendMarkup(result, current, false, nodes, &namespaceHash); - - // Print my ending tag - if (!onlyIncludeChildren) - appendEndMarkup(result, startNode); -} - -static void completeURLs(Node* node, const String& baseURL) -{ - Vector<AttributeChange> changes; - - KURL parsedBaseURL(baseURL); - - Node* end = node->traverseNextSibling(); - for (Node* n = node; n != end; n = n->traverseNextNode()) { - if (n->isElementNode()) { - Element* e = static_cast<Element*>(n); - NamedAttrMap* attrs = e->attributes(); - unsigned length = attrs->length(); - for (unsigned i = 0; i < length; i++) { - Attribute* attr = attrs->attributeItem(i); - if (e->isURLAttribute(attr)) - changes.append(AttributeChange(e, attr->name(), KURL(parsedBaseURL, attr->value()).string())); - } - } - } - - size_t numChanges = changes.size(); - for (size_t i = 0; i < numChanges; ++i) - changes[i].apply(); -} - -static bool needInterchangeNewlineAfter(const VisiblePosition& v) -{ - VisiblePosition next = v.next(); - Node* upstreamNode = next.deepEquivalent().upstream().node(); - Node* downstreamNode = v.deepEquivalent().downstream().node(); - // 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); -} - -static PassRefPtr<CSSMutableStyleDeclaration> styleFromMatchedRulesAndInlineDecl(const Node* node) -{ - if (!node->isHTMLElement()) - return 0; - - // FIXME: Having to const_cast here is ugly, but it is quite a bit of work to untangle - // the non-const-ness of styleFromMatchedRulesForElement. - HTMLElement* element = const_cast<HTMLElement*>(static_cast<const HTMLElement*>(node)); - RefPtr<CSSMutableStyleDeclaration> style = styleFromMatchedRulesForElement(element); - RefPtr<CSSMutableStyleDeclaration> inlineStyleDecl = element->getInlineStyleDecl(); - style->merge(inlineStyleDecl.get()); - return style.release(); -} - -static bool propertyMissingOrEqualToNone(CSSMutableStyleDeclaration* style, int propertyID) -{ - if (!style) - return false; - RefPtr<CSSValue> value = style->getPropertyCSSValue(propertyID); - if (!value) - return true; - if (!value->isPrimitiveValue()) - return false; - return static_cast<CSSPrimitiveValue*>(value.get())->getIdent() == CSSValueNone; -} - -static bool elementHasTextDecorationProperty(const Node* node) -{ - RefPtr<CSSMutableStyleDeclaration> style = styleFromMatchedRulesAndInlineDecl(node); - if (!style) - return false; - return !propertyMissingOrEqualToNone(style.get(), CSSPropertyTextDecoration); -} - -String joinMarkups(const Vector<String> preMarkups, const Vector<String>& postMarkups) -{ - size_t length = 0; - - size_t preCount = preMarkups.size(); - for (size_t i = 0; i < preCount; ++i) - length += preMarkups[i].length(); - - size_t postCount = postMarkups.size(); - for (size_t i = 0; i < postCount; ++i) - length += postMarkups[i].length(); - - Vector<UChar> result; - result.reserveCapacity(length); - - for (size_t i = preCount; i > 0; --i) - append(result, preMarkups[i - 1]); - - for (size_t i = 0; i < postCount; ++i) - append(result, postMarkups[i]); - - return String::adopt(result); -} - -// FIXME: Shouldn't we omit style info when annotate == DoNotAnnotateForInterchange? -// FIXME: At least, annotation and style info should probably not be included in range.markupString() -String createMarkup(const Range* range, Vector<Node*>* nodes, EAnnotateForInterchange annotate, bool convertBlocksToInlines) -{ - static const String interchangeNewlineString = String("<br class=\"") + AppleInterchangeNewline + "\">"; - - if (!range) - return ""; - - Document* document = range->ownerDocument(); - if (!document) - return ""; - - bool documentIsHTML = document->isHTMLDocument(); - - // Disable the delete button so it's elements are not serialized into the markup, - // but make sure neither endpoint is inside the delete user interface. - Frame* frame = document->frame(); - DeleteButtonController* deleteButton = frame ? frame->editor()->deleteButtonController() : 0; - RefPtr<Range> updatedRange = avoidIntersectionWithNode(range, deleteButton ? deleteButton->containerElement() : 0); - if (!updatedRange) - return ""; - - if (deleteButton) - deleteButton->disable(); - - ExceptionCode ec = 0; - bool collapsed = updatedRange->collapsed(ec); - ASSERT(ec == 0); - if (collapsed) - return ""; - Node* commonAncestor = updatedRange->commonAncestorContainer(ec); - ASSERT(ec == 0); - if (!commonAncestor) - return ""; - - document->updateLayoutIgnorePendingStylesheets(); - - Vector<String> markups; - Vector<String> preMarkups; - Node* pastEnd = updatedRange->pastLastNode(); - Node* lastClosed = 0; - Vector<Node*> ancestorsToClose; - - Node* startNode = updatedRange->firstNode(); - VisiblePosition visibleStart(updatedRange->startPosition(), VP_DEFAULT_AFFINITY); - VisiblePosition visibleEnd(updatedRange->endPosition(), VP_DEFAULT_AFFINITY); - if (annotate && needInterchangeNewlineAfter(visibleStart)) { - if (visibleStart == visibleEnd.previous()) { - if (deleteButton) - deleteButton->enable(); - return interchangeNewlineString; - } - - markups.append(interchangeNewlineString); - startNode = visibleStart.next().deepEquivalent().node(); - } - - Node* next; - for (Node* n = startNode; n != pastEnd; n = next) { - - // According to <rdar://problem/5730668>, it is possible for n to blow past pastEnd and become null here. This - // shouldn't be possible. This null check will prevent crashes (but create too much markup) and the ASSERT will - // hopefully lead us to understanding the problem. - ASSERT(n); - if (!n) - break; - - next = n->traverseNextNode(); - bool skipDescendants = false; - bool addMarkupForNode = true; - - if (!n->renderer() && !enclosingNodeWithTag(Position(n, 0), selectTag)) { - skipDescendants = true; - addMarkupForNode = false; - next = n->traverseNextSibling(); - // Don't skip over pastEnd. - if (pastEnd && pastEnd->isDescendantOf(n)) - next = pastEnd; - } - - if (isBlock(n) && canHaveChildrenForEditing(n) && next == pastEnd) - // Don't write out empty block containers that aren't fully selected. - continue; - - // Add the node to the markup. - if (addMarkupForNode) { - markups.append(getStartMarkup(n, updatedRange.get(), annotate)); - if (nodes) - nodes->append(n); - } - - if (n->firstChild() == 0 || skipDescendants) { - // Node has no children, or we are skipping it's descendants, add its close tag now. - if (addMarkupForNode) { - markups.append(getEndMarkup(n)); - lastClosed = n; - } - - // Check if the node is the last leaf of a tree. - if (!n->nextSibling() || next == pastEnd) { - if (!ancestorsToClose.isEmpty()) { - // Close up the ancestors. - do { - Node *ancestor = ancestorsToClose.last(); - if (next != pastEnd && next->isDescendantOf(ancestor)) - break; - // Not at the end of the range, close ancestors up to sibling of next node. - markups.append(getEndMarkup(ancestor)); - lastClosed = ancestor; - ancestorsToClose.removeLast(); - } while (!ancestorsToClose.isEmpty()); - } - - // Surround the currently accumulated markup with markup for ancestors we never opened as we leave the subtree(s) rooted at those ancestors. - Node* nextParent = next ? next->parentNode() : 0; - if (next != pastEnd && n != nextParent) { - Node* lastAncestorClosedOrSelf = n->isDescendantOf(lastClosed) ? lastClosed : n; - for (Node *parent = lastAncestorClosedOrSelf->parent(); parent != 0 && parent != nextParent; parent = parent->parentNode()) { - // All ancestors that aren't in the ancestorsToClose list should either be a) unrendered: - if (!parent->renderer()) - continue; - // or b) ancestors that we never encountered during a pre-order traversal starting at startNode: - ASSERT(startNode->isDescendantOf(parent)); - preMarkups.append(getStartMarkup(parent, updatedRange.get(), annotate)); - markups.append(getEndMarkup(parent)); - if (nodes) - nodes->append(parent); - lastClosed = parent; - } - } - } - } else if (addMarkupForNode && !skipDescendants) - // We added markup for this node, and we're descending into it. Set it to close eventually. - ancestorsToClose.append(n); - } - - // Include ancestors that aren't completely inside the range but are required to retain - // the structure and appearance of the copied markup. - Node* specialCommonAncestor = 0; - Node* commonAncestorBlock = commonAncestor ? enclosingBlock(commonAncestor) : 0; - if (annotate && commonAncestorBlock) { - if (commonAncestorBlock->hasTagName(tbodyTag) || commonAncestorBlock->hasTagName(trTag)) { - Node* table = commonAncestorBlock->parentNode(); - while (table && !table->hasTagName(tableTag)) - table = table->parentNode(); - if (table) - specialCommonAncestor = table; - } else if (commonAncestorBlock->hasTagName(listingTag) - || commonAncestorBlock->hasTagName(olTag) - || commonAncestorBlock->hasTagName(preTag) - || commonAncestorBlock->hasTagName(tableTag) - || commonAncestorBlock->hasTagName(ulTag) - || commonAncestorBlock->hasTagName(xmpTag)) - specialCommonAncestor = commonAncestorBlock; - } - - bool selectedOneOrMoreParagraphs = startOfParagraph(visibleStart) != startOfParagraph(visibleEnd) || - isStartOfParagraph(visibleStart) && isEndOfParagraph(visibleEnd); - - // Retain the Mail quote level by including all ancestor mail block quotes. - if (lastClosed && annotate && selectedOneOrMoreParagraphs) { - for (Node *ancestor = lastClosed->parentNode(); ancestor; ancestor = ancestor->parentNode()) - if (isMailBlockquote(ancestor)) - specialCommonAncestor = ancestor; - } - - Node* checkAncestor = specialCommonAncestor ? specialCommonAncestor : commonAncestor; - if (checkAncestor->renderer()) { - RefPtr<CSSMutableStyleDeclaration> checkAncestorStyle = computedStyle(checkAncestor)->copyInheritableProperties(); - if (!propertyMissingOrEqualToNone(checkAncestorStyle.get(), CSSPropertyWebkitTextDecorationsInEffect)) - specialCommonAncestor = enclosingNodeOfType(Position(checkAncestor, 0), &elementHasTextDecorationProperty); - } - - // If a single tab is selected, commonAncestor will be a text node inside a tab span. - // If two or more tabs are selected, commonAncestor will be the tab span. - // In either case, if there is a specialCommonAncestor already, it will necessarily be above - // any tab span that needs to be included. - if (!specialCommonAncestor && isTabSpanTextNode(commonAncestor)) - specialCommonAncestor = commonAncestor->parentNode(); - if (!specialCommonAncestor && isTabSpanNode(commonAncestor)) - specialCommonAncestor = commonAncestor; - - if (Node *enclosingAnchor = enclosingNodeWithTag(Position(specialCommonAncestor ? specialCommonAncestor : commonAncestor, 0), aTag)) - specialCommonAncestor = enclosingAnchor; - - Node* body = enclosingNodeWithTag(Position(commonAncestor, 0), bodyTag); - // FIXME: Only include markup for a fully selected root (and ancestors of lastClosed up to that root) if - // there are styles/attributes on those nodes that need to be included to preserve the appearance of the copied markup. - // FIXME: Do this for all fully selected blocks, not just the body. - Node* fullySelectedRoot = body && *Selection::selectionFromContentsOfNode(body).toRange() == *updatedRange ? body : 0; - if (annotate && fullySelectedRoot) - specialCommonAncestor = fullySelectedRoot; - - if (specialCommonAncestor && lastClosed) { - // Also include all of the ancestors of lastClosed up to this special ancestor. - for (Node* ancestor = lastClosed->parentNode(); ancestor; ancestor = ancestor->parentNode()) { - if (ancestor == fullySelectedRoot && !convertBlocksToInlines) { - RefPtr<CSSMutableStyleDeclaration> style = styleFromMatchedRulesAndInlineDecl(fullySelectedRoot); - - // Bring the background attribute over, but not as an attribute because a background attribute on a div - // appears to have no effect. - if (!style->getPropertyCSSValue(CSSPropertyBackgroundImage) && static_cast<Element*>(fullySelectedRoot)->hasAttribute(backgroundAttr)) - style->setProperty(CSSPropertyBackgroundImage, "url('" + static_cast<Element*>(fullySelectedRoot)->getAttribute(backgroundAttr) + "')"); - - if (style->length()) { - Vector<UChar> openTag; - static const String divStyle("<div style=\""); - append(openTag, divStyle); - appendAttributeValue(openTag, style->cssText(), documentIsHTML); - openTag.append('\"'); - openTag.append('>'); - preMarkups.append(String::adopt(openTag)); - - static const String divCloseTag("</div>"); - markups.append(divCloseTag); - } - } else { - preMarkups.append(getStartMarkup(ancestor, updatedRange.get(), annotate, convertBlocksToInlines)); - markups.append(getEndMarkup(ancestor)); - } - if (nodes) - nodes->append(ancestor); - - lastClosed = ancestor; - - if (ancestor == specialCommonAncestor) - break; - } - } - - static const String styleSpanOpen = String("<span class=\"" AppleStyleSpanClass "\" style=\""); - static const String styleSpanClose("</span>"); - - // Add a wrapper span with the styles that all of the nodes in the markup inherit. - Node* parentOfLastClosed = lastClosed ? lastClosed->parentNode() : 0; - if (parentOfLastClosed && parentOfLastClosed->renderer()) { - RefPtr<CSSMutableStyleDeclaration> style = computedStyle(parentOfLastClosed)->copyInheritableProperties(); - - // Styles that Mail blockquotes contribute should only be placed on the Mail blockquote, to help - // us differentiate those styles from ones that the user has applied. This helps us - // get the color of content pasted into blockquotes right. - removeEnclosingMailBlockquoteStyle(style.get(), parentOfLastClosed); - - // Document default styles will be added on another wrapper span. - removeDefaultStyles(style.get(), document); - - // Since we are converting blocks to inlines, remove any inherited block properties that are in the style. - // This cuts out meaningless properties and prevents properties from magically affecting blocks later - // if the style is cloned for a new block element during a future editing operation. - if (convertBlocksToInlines) - style->removeBlockProperties(); - - if (style->length() > 0) { - Vector<UChar> openTag; - append(openTag, styleSpanOpen); - appendAttributeValue(openTag, style->cssText(), documentIsHTML); - openTag.append('\"'); - openTag.append('>'); - preMarkups.append(String::adopt(openTag)); - - markups.append(styleSpanClose); - } - } - - if (lastClosed && lastClosed != document->documentElement()) { - // Add a style span with the document's default styles. We add these in a separate - // span so that at paste time we can differentiate between document defaults and user - // applied styles. - RefPtr<CSSMutableStyleDeclaration> defaultStyle = computedStyle(document->documentElement())->copyInheritableProperties(); - - if (defaultStyle->length() > 0) { - Vector<UChar> openTag; - append(openTag, styleSpanOpen); - appendAttributeValue(openTag, defaultStyle->cssText(), documentIsHTML); - openTag.append('\"'); - openTag.append('>'); - preMarkups.append(String::adopt(openTag)); - markups.append(styleSpanClose); - } - } - - // FIXME: The interchange newline should be placed in the block that it's in, not after all of the content, unconditionally. - if (annotate && needInterchangeNewlineAfter(visibleEnd.previous())) - markups.append(interchangeNewlineString); - - if (deleteButton) - deleteButton->enable(); - - return joinMarkups(preMarkups, markups); -} - -PassRefPtr<DocumentFragment> createFragmentFromMarkup(Document* document, const String& markup, const String& baseURL) -{ - ASSERT(document->documentElement()->isHTMLElement()); - // FIXME: What if the document element is not an HTML element? - HTMLElement *element = static_cast<HTMLElement*>(document->documentElement()); - - RefPtr<DocumentFragment> fragment = element->createContextualFragment(markup); - - if (fragment && !baseURL.isEmpty() && baseURL != blankURL() && baseURL != document->baseURL()) - completeURLs(fragment.get(), baseURL); - - return fragment.release(); -} - -String createMarkup(const Node* node, EChildrenOnly includeChildren, Vector<Node*>* nodes) -{ - Vector<UChar> result; - - if (!node) - return ""; - - Document* document = node->document(); - Frame* frame = document->frame(); - DeleteButtonController* deleteButton = frame ? frame->editor()->deleteButtonController() : 0; - - // disable the delete button so it's elements are not serialized into the markup - if (deleteButton) { - if (node->isDescendantOf(deleteButton->containerElement())) - return ""; - deleteButton->disable(); - } - - appendMarkup(result, const_cast<Node*>(node), includeChildren, nodes); - - if (deleteButton) - deleteButton->enable(); - - return String::adopt(result); -} - -static void fillContainerFromString(ContainerNode* paragraph, const String& string) -{ - Document* document = paragraph->document(); - - ExceptionCode ec = 0; - if (string.isEmpty()) { - paragraph->appendChild(createBlockPlaceholderElement(document), ec); - ASSERT(ec == 0); - return; - } - - ASSERT(string.find('\n') == -1); - - Vector<String> tabList; - string.split('\t', true, tabList); - String tabText = ""; - bool first = true; - size_t numEntries = tabList.size(); - for (size_t i = 0; i < numEntries; ++i) { - const String& s = tabList[i]; - - // append the non-tab textual part - if (!s.isEmpty()) { - if (!tabText.isEmpty()) { - paragraph->appendChild(createTabSpanElement(document, tabText), ec); - ASSERT(ec == 0); - tabText = ""; - } - RefPtr<Node> textNode = document->createTextNode(stringWithRebalancedWhitespace(s, first, i + 1 == numEntries)); - paragraph->appendChild(textNode.release(), ec); - ASSERT(ec == 0); - } - - // there is a tab after every entry, except the last entry - // (if the last character is a tab, the list gets an extra empty entry) - if (i + 1 != numEntries) - tabText.append('\t'); - else if (!tabText.isEmpty()) { - paragraph->appendChild(createTabSpanElement(document, tabText), ec); - ASSERT(ec == 0); - } - - first = false; - } -} - -PassRefPtr<DocumentFragment> createFragmentFromText(Range* context, const String& text) -{ - if (!context) - return 0; - - Node* styleNode = context->firstNode(); - if (!styleNode) { - styleNode = context->startPosition().node(); - if (!styleNode) - return 0; - } - - Document* document = styleNode->document(); - RefPtr<DocumentFragment> fragment = document->createDocumentFragment(); - - if (text.isEmpty()) - return fragment.release(); - - String string = text; - string.replace("\r\n", "\n"); - string.replace('\r', '\n'); - - ExceptionCode ec = 0; - RenderObject* renderer = styleNode->renderer(); - if (renderer && renderer->style()->preserveNewline()) { - fragment->appendChild(document->createTextNode(string), ec); - ASSERT(ec == 0); - if (string.endsWith("\n")) { - RefPtr<Element> element; - element = document->createElementNS(xhtmlNamespaceURI, "br", ec); - ASSERT(ec == 0); - element->setAttribute(classAttr, AppleInterchangeNewline); - fragment->appendChild(element.release(), ec); - ASSERT(ec == 0); - } - return fragment.release(); - } - - // A string with no newlines gets added inline, rather than being put into a paragraph. - if (string.find('\n') == -1) { - fillContainerFromString(fragment.get(), string); - return fragment.release(); - } - - // Break string into paragraphs. Extra line breaks turn into empty paragraphs. - Node* block = enclosingBlock(context->firstNode()); - bool useClonesOfEnclosingBlock = block && !block->hasTagName(bodyTag) && !block->hasTagName(htmlTag) && block != editableRootForPosition(context->startPosition()); - - Vector<String> list; - string.split('\n', true, list); // true gets us empty strings in the list - size_t numLines = list.size(); - for (size_t i = 0; i < numLines; ++i) { - const String& s = list[i]; - - RefPtr<Element> element; - if (s.isEmpty() && i + 1 == numLines) { - // For last line, use the "magic BR" rather than a P. - element = document->createElementNS(xhtmlNamespaceURI, "br", ec); - ASSERT(ec == 0); - element->setAttribute(classAttr, AppleInterchangeNewline); - } else { - element = useClonesOfEnclosingBlock ? static_cast<Element*>(block->cloneNode(false).get()) : createDefaultParagraphElement(document); - fillContainerFromString(element.get(), s); - } - fragment->appendChild(element.release(), ec); - ASSERT(ec == 0); - } - return fragment.release(); -} - -PassRefPtr<DocumentFragment> createFragmentFromNodes(Document *document, const Vector<Node*>& nodes) -{ - if (!document) - return 0; - - // disable the delete button so it's elements are not serialized into the markup - if (document->frame()) - document->frame()->editor()->deleteButtonController()->disable(); - - RefPtr<DocumentFragment> fragment = document->createDocumentFragment(); - - ExceptionCode ec = 0; - size_t size = nodes.size(); - for (size_t i = 0; i < size; ++i) { - RefPtr<Element> element = createDefaultParagraphElement(document); - element->appendChild(nodes[i], ec); - ASSERT(ec == 0); - fragment->appendChild(element.release(), ec); - ASSERT(ec == 0); - } - - if (document->frame()) - document->frame()->editor()->deleteButtonController()->enable(); - - return fragment.release(); -} - -String createFullMarkup(const Node* node) -{ - if (!node) - return String(); - - Document* document = node->document(); - if (!document) - return String(); - - Frame* frame = document->frame(); - if (!frame) - return String(); - - // FIXME: This is never "for interchange". Is that right? - String markupString = createMarkup(node, IncludeNode, 0); - Node::NodeType nodeType = node->nodeType(); - if (nodeType != Node::DOCUMENT_NODE && nodeType != Node::DOCUMENT_TYPE_NODE) - markupString = frame->documentTypeString() + markupString; - - return markupString; -} - -String createFullMarkup(const Range* range) -{ - if (!range) - return String(); - - Node* node = range->startContainer(); - if (!node) - return String(); - - Document* document = node->document(); - if (!document) - return String(); - - Frame* frame = document->frame(); - if (!frame) - return String(); - - // FIXME: This is always "for interchange". Is that right? See the previous method. - return frame->documentTypeString() + createMarkup(range, 0, AnnotateForInterchange); -} - -} diff --git a/WebCore/editing/markup.h b/WebCore/editing/markup.h deleted file mode 100644 index 6b7333c..0000000 --- a/WebCore/editing/markup.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (C) 2004 Apple Computer, 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 markup_h -#define markup_h - -#include "HTMLInterchange.h" -#include <wtf/Forward.h> -#include <wtf/Vector.h> - -namespace WebCore { - - class Document; - class DocumentFragment; - class Node; - class Range; - class String; - - enum EChildrenOnly { IncludeNode, ChildrenOnly }; - - PassRefPtr<DocumentFragment> createFragmentFromText(Range* context, const String& text); - PassRefPtr<DocumentFragment> createFragmentFromMarkup(Document*, const String& markup, const String& baseURL); - PassRefPtr<DocumentFragment> createFragmentFromNodes(Document*, const Vector<Node*>&); - - String createMarkup(const Range*, - Vector<Node*>* = 0, EAnnotateForInterchange = DoNotAnnotateForInterchange, bool convertBlocksToInlines = false); - String createMarkup(const Node*, EChildrenOnly = IncludeNode, Vector<Node*>* = 0); - - String createFullMarkup(const Node*); - String createFullMarkup(const Range*); - -} - -#endif // markup_h diff --git a/WebCore/editing/qt/EditorQt.cpp b/WebCore/editing/qt/EditorQt.cpp deleted file mode 100644 index 89ee78e..0000000 --- a/WebCore/editing/qt/EditorQt.cpp +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (C) 2006 Zack Rusin <zack@kde.org> - * Copyright (C) 2006 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 "Editor.h" - -#include "ClipboardAccessPolicy.h" -#include "ClipboardQt.h" -#include "Document.h" -#include "Element.h" -#include "Selection.h" -#include "SelectionController.h" -#include "TextIterator.h" -#include "htmlediting.h" -#include "visible_units.h" - -namespace WebCore { - -PassRefPtr<Clipboard> Editor::newGeneralClipboard(ClipboardAccessPolicy policy) -{ - return ClipboardQt::create(policy); -} - -} // namespace WebCore diff --git a/WebCore/editing/visible_units.cpp b/WebCore/editing/visible_units.cpp deleted file mode 100644 index 33118f1..0000000 --- a/WebCore/editing/visible_units.cpp +++ /dev/null @@ -1,960 +0,0 @@ -/* - * Copyright (C) 2004 Apple Computer, 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 "visible_units.h" - -#include "Document.h" -#include "Element.h" -#include "HTMLNames.h" -#include "RenderBlock.h" -#include "RenderLayer.h" -#include "TextBoundaries.h" -#include "TextBreakIterator.h" -#include "TextIterator.h" -#include "htmlediting.h" - -namespace WebCore { - -using namespace HTMLNames; - -static VisiblePosition previousBoundary(const VisiblePosition &c, unsigned (*searchFunction)(const UChar *, unsigned)) -{ - Position pos = c.deepEquivalent(); - Node *n = pos.node(); - if (!n) - return VisiblePosition(); - Document *d = n->document(); - Node *de = d->documentElement(); - if (!de) - return VisiblePosition(); - Node *boundary = n->enclosingBlockFlowElement(); - if (!boundary) - return VisiblePosition(); - bool isContentEditable = boundary->isContentEditable(); - while (boundary && boundary != de && boundary->parentNode() && isContentEditable == boundary->parentNode()->isContentEditable()) - boundary = boundary->parentNode(); - - Position start = rangeCompliantEquivalent(Position(boundary, 0)); - Position end = rangeCompliantEquivalent(pos); - RefPtr<Range> searchRange = Range::create(d); - - int exception = 0; - searchRange->setStart(start.node(), start.offset(), exception); - searchRange->setEnd(end.node(), end.offset(), exception); - - ASSERT(!exception); - if (exception) - return VisiblePosition(); - - SimplifiedBackwardsTextIterator it(searchRange.get()); - Vector<UChar, 1024> string; - unsigned next = 0; - bool inTextSecurityMode = start.node() && start.node()->renderer() && start.node()->renderer()->style()->textSecurity() != TSNONE; - while (!it.atEnd()) { - // iterate to get chunks until the searchFunction returns a non-zero value. - if (!inTextSecurityMode) - string.prepend(it.characters(), it.length()); - else { - // Treat bullets used in the text security mode as regular characters when looking for boundaries - String iteratorString(it.characters(), it.length()); - iteratorString = iteratorString.impl()->secure('x'); - string.prepend(iteratorString.characters(), iteratorString.length()); - } - - next = searchFunction(string.data(), string.size()); - if (next != 0) - break; - it.advance(); - } - - if (it.atEnd() && next == 0) { - pos = it.range()->startPosition(); - } else if (next != 0) { - Node *node = it.range()->startContainer(exception); - if (node->isTextNode() || (node->renderer() && node->renderer()->isBR())) - // The next variable contains a usable index into a text node - pos = Position(node, next); - else { - // Use the end of the found range, the start is not guaranteed to - // be correct. - Position end = it.range()->endPosition(); - VisiblePosition boundary(end); - unsigned i = it.length() - next; - while (i--) - boundary = boundary.previous(); - return boundary; - } - } - - return VisiblePosition(pos, DOWNSTREAM); -} - -static VisiblePosition nextBoundary(const VisiblePosition &c, unsigned (*searchFunction)(const UChar *, unsigned)) -{ - Position pos = c.deepEquivalent(); - Node *n = pos.node(); - if (!n) - return VisiblePosition(); - Document *d = n->document(); - Node *de = d->documentElement(); - if (!de) - return VisiblePosition(); - Node *boundary = n->enclosingBlockFlowElement(); - if (!boundary) - return VisiblePosition(); - bool isContentEditable = boundary->isContentEditable(); - while (boundary && boundary != de && boundary->parentNode() && isContentEditable == boundary->parentNode()->isContentEditable()) - boundary = boundary->parentNode(); - - RefPtr<Range> searchRange(d->createRange()); - Position start(rangeCompliantEquivalent(pos)); - ExceptionCode ec = 0; - searchRange->selectNodeContents(boundary, ec); - searchRange->setStart(start.node(), start.offset(), ec); - TextIterator it(searchRange.get(), true); - Vector<UChar, 1024> string; - unsigned next = 0; - bool inTextSecurityMode = start.node() && start.node()->renderer() && start.node()->renderer()->style()->textSecurity() != TSNONE; - while (!it.atEnd()) { - // Keep asking the iterator for chunks until the search function - // returns an end value not equal to the length of the string passed to it. - if (!inTextSecurityMode) - string.append(it.characters(), it.length()); - else { - // Treat bullets used in the text security mode as regular characters when looking for boundaries - String iteratorString(it.characters(), it.length()); - iteratorString = iteratorString.impl()->secure('x'); - string.append(iteratorString.characters(), iteratorString.length()); - } - - next = searchFunction(string.data(), string.size()); - if (next != string.size()) - break; - it.advance(); - } - - if (it.atEnd() && next == string.size()) { - pos = it.range()->startPosition(); - } else if (next != 0) { - // Use the character iterator to translate the next value into a DOM position. - CharacterIterator charIt(searchRange.get(), true); - charIt.advance(next - 1); - pos = charIt.range()->endPosition(); - - // FIXME: workaround for collapsed range (where only start position is correct) emitted for some emitted newlines (see rdar://5192593) - VisiblePosition visPos = VisiblePosition(pos); - if (visPos == VisiblePosition(charIt.range()->startPosition())) - pos = visPos.next(true).deepEquivalent(); - } - - // generate VisiblePosition, use UPSTREAM affinity if possible - return VisiblePosition(pos, VP_UPSTREAM_IF_POSSIBLE); -} - -// --------- - -static unsigned startWordBoundary(const UChar* characters, unsigned length) -{ - int start, end; - findWordBoundary(characters, length, length, &start, &end); - return start; -} - -VisiblePosition startOfWord(const VisiblePosition &c, EWordSide side) -{ - // FIXME: This returns a null VP for c at the start of the document - // and side == LeftWordIfOnBoundary - VisiblePosition p = c; - if (side == RightWordIfOnBoundary) { - // at paragraph end, the startofWord is the current position - if (isEndOfParagraph(c)) - return c; - - p = c.next(); - if (p.isNull()) - return c; - } - return previousBoundary(p, startWordBoundary); -} - -static unsigned endWordBoundary(const UChar* characters, unsigned length) -{ - int start, end; - findWordBoundary(characters, length, 0, &start, &end); - return end; -} - -VisiblePosition endOfWord(const VisiblePosition &c, EWordSide side) -{ - VisiblePosition p = c; - if (side == LeftWordIfOnBoundary) { - if (isStartOfParagraph(c)) - return c; - - p = c.previous(); - if (p.isNull()) - return c; - } else if (isEndOfParagraph(c)) - return c; - - return nextBoundary(p, endWordBoundary); -} - -static unsigned previousWordPositionBoundary(const UChar* characters, unsigned length) -{ - return findNextWordFromIndex(characters, length, length, false); -} - -VisiblePosition previousWordPosition(const VisiblePosition &c) -{ - VisiblePosition prev = previousBoundary(c, previousWordPositionBoundary); - return c.honorEditableBoundaryAtOrAfter(prev); -} - -static unsigned nextWordPositionBoundary(const UChar* characters, unsigned length) -{ - return findNextWordFromIndex(characters, length, 0, true); -} - -VisiblePosition nextWordPosition(const VisiblePosition &c) -{ - VisiblePosition next = nextBoundary(c, nextWordPositionBoundary); - return c.honorEditableBoundaryAtOrBefore(next); -} - -// --------- - -static RootInlineBox *rootBoxForLine(const VisiblePosition &c) -{ - Position p = c.deepEquivalent(); - Node *node = p.node(); - if (!node) - return 0; - - RenderObject *renderer = node->renderer(); - if (!renderer) - return 0; - - InlineBox* box; - int offset; - c.getInlineBoxAndOffset(box, offset); - - return box ? box->root() : 0; -} - -static VisiblePosition positionAvoidingFirstPositionInTable(const VisiblePosition& c) -{ - // return table offset 0 instead of the first VisiblePosition inside the table - VisiblePosition previous = c.previous(); - if (isLastPositionBeforeTable(previous)) - return previous; - - return c; -} - -static VisiblePosition startPositionForLine(const VisiblePosition& c) -{ - if (c.isNull()) - return VisiblePosition(); - - RootInlineBox *rootBox = rootBoxForLine(c); - if (!rootBox) { - // 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.offset() == 0) - return positionAvoidingFirstPositionInTable(c); - - return VisiblePosition(); - } - - // Generated content (e.g. list markers and CSS :before and :after - // pseudoelements) have no corresponding DOM element, and so cannot be - // represented by a VisiblePosition. Use whatever follows instead. - InlineBox *startBox = rootBox->firstLeafChild(); - Node *startNode; - while (1) { - if (!startBox) - return VisiblePosition(); - - RenderObject *startRenderer = startBox->object(); - if (!startRenderer) - return VisiblePosition(); - - startNode = startRenderer->element(); - if (startNode) - break; - - startBox = startBox->nextLeafChild(); - } - - int startOffset = 0; - if (startBox->isInlineTextBox()) { - InlineTextBox *startTextBox = static_cast<InlineTextBox *>(startBox); - startOffset = startTextBox->m_start; - } - - VisiblePosition visPos = VisiblePosition(startNode, startOffset, DOWNSTREAM); - return positionAvoidingFirstPositionInTable(visPos); -} - -VisiblePosition startOfLine(const VisiblePosition& c) -{ - VisiblePosition visPos = startPositionForLine(c); - - if (visPos.isNotNull()) { - // Make sure the start of line is not greater than the given input position. Else use the previous position to - // obtain start of line. This condition happens when the input position is before the space character at the end - // of a soft-wrapped non-editable line. In this scenario, startPositionForLine would incorrectly hand back a position - // greater than the input position. This fix is to account for the discrepancy between lines with webkit-line-break:after-white-space - // style versus lines without that style, which would break before a space by default. - Position p = visPos.deepEquivalent(); - if (p.offset() > c.deepEquivalent().offset() && p.node()->isSameNode(c.deepEquivalent().node())) { - visPos = c.previous(); - if (visPos.isNull()) - return VisiblePosition(); - visPos = startPositionForLine(visPos); - } - } - - return c.honorEditableBoundaryAtOrAfter(visPos); -} - -static VisiblePosition endPositionForLine(const VisiblePosition& c) -{ - if (c.isNull()) - return VisiblePosition(); - - RootInlineBox *rootBox = rootBoxForLine(c); - if (!rootBox) { - // 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.offset() == 0) - return c; - return VisiblePosition(); - } - - // Generated content (e.g. list markers and CSS :before and :after - // pseudoelements) have no corresponding DOM element, and so cannot be - // represented by a VisiblePosition. Use whatever precedes instead. - Node *endNode; - InlineBox *endBox = rootBox->lastLeafChild(); - while (1) { - if (!endBox) - return VisiblePosition(); - - RenderObject *endRenderer = endBox->object(); - if (!endRenderer) - return VisiblePosition(); - - endNode = endRenderer->element(); - if (endNode) - break; - - endBox = endBox->prevLeafChild(); - } - - int endOffset = 1; - if (endNode->hasTagName(brTag)) { - endOffset = 0; - } else if (endBox->isInlineTextBox()) { - InlineTextBox *endTextBox = static_cast<InlineTextBox *>(endBox); - endOffset = endTextBox->m_start; - if (!endTextBox->isLineBreak()) - endOffset += endTextBox->m_len; - } - - return VisiblePosition(endNode, endOffset, VP_UPSTREAM_IF_POSSIBLE); -} - -VisiblePosition endOfLine(const VisiblePosition& c) -{ - VisiblePosition visPos = endPositionForLine(c); - - // Make sure the end of line is at the same line as the given input position. Else use the previous position to - // obtain end of line. This condition happens when the input position is before the space character at the end - // of a soft-wrapped non-editable line. In this scenario, endPositionForLine would incorrectly hand back a position - // in the next line instead. This fix is to account for the discrepancy between lines with webkit-line-break:after-white-space style - // versus lines without that style, which would break before a space by default. - if (!inSameLine(c, visPos)) { - visPos = c.previous(); - if (visPos.isNull()) - return VisiblePosition(); - visPos = endPositionForLine(visPos); - } - - return c.honorEditableBoundaryAtOrBefore(visPos); -} - -bool inSameLine(const VisiblePosition &a, const VisiblePosition &b) -{ - return a.isNotNull() && startOfLine(a) == startOfLine(b); -} - -bool isStartOfLine(const VisiblePosition &p) -{ - return p.isNotNull() && p == startOfLine(p); -} - -bool isEndOfLine(const VisiblePosition &p) -{ - return p.isNotNull() && p == endOfLine(p); -} - -// The first leaf before node that has the same editability as node. -static Node* previousLeafWithSameEditability(Node* node) -{ - bool editable = node->isContentEditable(); - Node* n = node->previousLeafNode(); - while (n) { - if (editable == n->isContentEditable()) - return n; - n = n->previousLeafNode(); - } - return 0; -} - -VisiblePosition previousLinePosition(const VisiblePosition &visiblePosition, int x) -{ - Position p = visiblePosition.deepEquivalent(); - Node *node = p.node(); - Node* highestRoot = highestEditableRoot(p); - if (!node) - return VisiblePosition(); - - node->document()->updateLayoutIgnorePendingStylesheets(); - - RenderObject *renderer = node->renderer(); - if (!renderer) - return VisiblePosition(); - - RenderBlock *containingBlock = 0; - RootInlineBox *root = 0; - InlineBox* box; - int ignoredCaretOffset; - visiblePosition.getInlineBoxAndOffset(box, ignoredCaretOffset); - if (box) { - root = box->root()->prevRootBox(); - if (root) - containingBlock = renderer->containingBlock(); - } - - if (!root) { - // This containing editable block does not have a previous line. - // Need to move back to previous containing editable block in this root editable - // block and find the last root line box in that block. - Node* startBlock = enclosingBlock(node); - Node* n = previousLeafWithSameEditability(node); - while (n && startBlock == enclosingBlock(n)) - n = previousLeafWithSameEditability(n); - while (n) { - if (highestEditableRoot(Position(n, 0)) != highestRoot) - break; - Position pos(n, caretMinOffset(n)); - if (pos.isCandidate()) { - ASSERT(n->renderer()); - Position maxPos(n, caretMaxOffset(n)); - maxPos.getInlineBoxAndOffset(DOWNSTREAM, box, ignoredCaretOffset); - if (box) { - // previous root line box found - root = box->root(); - containingBlock = n->renderer()->containingBlock(); - break; - } - - return VisiblePosition(pos, DOWNSTREAM); - } - n = previousLeafWithSameEditability(n); - } - } - - if (root) { - // FIXME: Can be wrong for multi-column layout. - int absx, absy; - containingBlock->absolutePositionForContent(absx, absy); - if (containingBlock->hasOverflowClip()) - containingBlock->layer()->subtractScrollOffset(absx, absy); - RenderObject *renderer = root->closestLeafChildForXPos(x - absx, isEditablePosition(p))->object(); - Node* node = renderer->element(); - if (editingIgnoresContent(node)) - return Position(node->parent(), node->nodeIndex()); - return renderer->positionForCoordinates(x - absx, root->topOverflow()); - } - - // Could not find a previous line. This means we must already be on the first line. - // Move to the start of the content in this block, which effectively moves us - // to the start of the line we're on. - Node* rootElement = node->isContentEditable() ? node->rootEditableElement() : node->document()->documentElement(); - return VisiblePosition(rootElement, 0, DOWNSTREAM); -} - -static Node* nextLeafWithSameEditability(Node* node, int offset) -{ - bool editable = node->isContentEditable(); - ASSERT(offset >= 0); - Node* child = node->childNode(offset); - Node* n = child ? child->nextLeafNode() : node->nextLeafNode(); - while (n) { - if (editable == n->isContentEditable()) - return n; - n = n->nextLeafNode(); - } - return 0; -} - -static Node* nextLeafWithSameEditability(Node* node) -{ - if (!node) - return 0; - - bool editable = node->isContentEditable(); - Node* n = node->nextLeafNode(); - while (n) { - if (editable == n->isContentEditable()) - return n; - n = n->nextLeafNode(); - } - return 0; -} - -VisiblePosition nextLinePosition(const VisiblePosition &visiblePosition, int x) -{ - Position p = visiblePosition.deepEquivalent(); - Node *node = p.node(); - Node* highestRoot = highestEditableRoot(p); - if (!node) - return VisiblePosition(); - - node->document()->updateLayoutIgnorePendingStylesheets(); - - RenderObject *renderer = node->renderer(); - if (!renderer) - return VisiblePosition(); - - RenderBlock *containingBlock = 0; - RootInlineBox *root = 0; - InlineBox* box; - int ignoredCaretOffset; - visiblePosition.getInlineBoxAndOffset(box, ignoredCaretOffset); - if (box) { - root = box->root()->nextRootBox(); - if (root) - containingBlock = renderer->containingBlock(); - } - - if (!root) { - // This containing editable block does not have a next line. - // Need to move forward to next containing editable block in this root editable - // block and find the first root line box in that block. - Node* startBlock = enclosingBlock(node); - Node* n = nextLeafWithSameEditability(node, p.offset()); - while (n && startBlock == enclosingBlock(n)) - n = nextLeafWithSameEditability(n); - while (n) { - if (highestEditableRoot(Position(n, 0)) != highestRoot) - break; - Position pos(n, caretMinOffset(n)); - if (pos.isCandidate()) { - ASSERT(n->renderer()); - pos.getInlineBoxAndOffset(DOWNSTREAM, box, ignoredCaretOffset); - if (box) { - // next root line box found - root = box->root(); - containingBlock = n->renderer()->containingBlock(); - break; - } - - return VisiblePosition(pos, DOWNSTREAM); - } - n = nextLeafWithSameEditability(n); - } - } - - if (root) { - // FIXME: Can be wrong for multi-column layout. - int absx, absy; - containingBlock->absolutePositionForContent(absx, absy); - if (containingBlock->hasOverflowClip()) - containingBlock->layer()->subtractScrollOffset(absx, absy); - RenderObject *renderer = root->closestLeafChildForXPos(x - absx, isEditablePosition(p))->object(); - Node* node = renderer->element(); - if (editingIgnoresContent(node)) - return Position(node->parent(), node->nodeIndex()); - return renderer->positionForCoordinates(x - absx, root->topOverflow()); - } - - // Could not find a next line. This means we must already be on the last line. - // 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); -} - -// --------- - -static unsigned startSentenceBoundary(const UChar* characters, unsigned length) -{ - TextBreakIterator* iterator = sentenceBreakIterator(characters, length); - // FIXME: The following function can return -1; we don't handle that. - return textBreakPreceding(iterator, length); -} - -VisiblePosition startOfSentence(const VisiblePosition &c) -{ - return previousBoundary(c, startSentenceBoundary); -} - -static unsigned endSentenceBoundary(const UChar* characters, unsigned length) -{ - TextBreakIterator* iterator = sentenceBreakIterator(characters, length); - return textBreakNext(iterator); -} - -// FIXME: This includes the space after the punctuation that marks the end of the sentence. -VisiblePosition endOfSentence(const VisiblePosition &c) -{ - return nextBoundary(c, endSentenceBoundary); -} - -static unsigned previousSentencePositionBoundary(const UChar* characters, unsigned length) -{ - // FIXME: This is identical to startSentenceBoundary. I'm pretty sure that's not right. - TextBreakIterator* iterator = sentenceBreakIterator(characters, length); - // FIXME: The following function can return -1; we don't handle that. - return textBreakPreceding(iterator, length); -} - -VisiblePosition previousSentencePosition(const VisiblePosition &c) -{ - VisiblePosition prev = previousBoundary(c, previousSentencePositionBoundary); - return c.honorEditableBoundaryAtOrAfter(prev); -} - -static unsigned nextSentencePositionBoundary(const UChar* characters, unsigned length) -{ - // FIXME: This is identical to endSentenceBoundary. This isn't right, it needs to - // move to the equivlant position in the following sentence. - TextBreakIterator* iterator = sentenceBreakIterator(characters, length); - return textBreakFollowing(iterator, 0); -} - -VisiblePosition nextSentencePosition(const VisiblePosition &c) -{ - VisiblePosition next = nextBoundary(c, nextSentencePositionBoundary); - return c.honorEditableBoundaryAtOrBefore(next); -} - -// FIXME: Broken for positions before/after images that aren't inline (5027702) -VisiblePosition startOfParagraph(const VisiblePosition &c) -{ - Position p = c.deepEquivalent(); - Node *startNode = p.node(); - - if (!startNode) - return VisiblePosition(); - - if (startNode->renderer() - && ((startNode->renderer()->isTable() && !startNode->renderer()->isInline()) - || startNode->renderer()->isHR()) - && p.offset() == maxDeepOffset(startNode)) - return VisiblePosition(Position(startNode, 0)); - - Node* startBlock = enclosingBlock(startNode); - - Node *node = startNode; - int offset = p.offset(); - - Node *n = startNode; - while (n) { - if (n->isContentEditable() != startNode->isContentEditable()) - break; - RenderObject *r = n->renderer(); - if (!r) { - n = n->traversePreviousNodePostOrder(startBlock); - continue; - } - RenderStyle *style = r->style(); - if (style->visibility() != VISIBLE) { - n = n->traversePreviousNodePostOrder(startBlock); - continue; - } - - if (r->isBR() || isBlock(n)) - break; - - if (r->isText()) { - if (style->preserveNewline()) { - const UChar* chars = static_cast<RenderText*>(r)->characters(); - int i = static_cast<RenderText*>(r)->textLength(); - int o = offset; - if (n == startNode && o < i) - i = max(0, o); - while (--i >= 0) - if (chars[i] == '\n') - return VisiblePosition(n, i + 1, DOWNSTREAM); - } - node = n; - offset = 0; - n = n->traversePreviousNodePostOrder(startBlock); - } else if (editingIgnoresContent(n) || isTableElement(n)) { - node = n; - offset = 0; - n = n->previousSibling() ? n->previousSibling() : n->traversePreviousNodePostOrder(startBlock); - } else - n = n->traversePreviousNodePostOrder(startBlock); - } - - return VisiblePosition(node, offset, DOWNSTREAM); -} - -// FIXME: Broken for positions before/after images that aren't inline (5027702) -VisiblePosition endOfParagraph(const VisiblePosition &c) -{ - if (c.isNull()) - return VisiblePosition(); - - Position p = c.deepEquivalent(); - Node* startNode = p.node(); - - if (startNode->renderer() - && ((startNode->renderer()->isTable() && !startNode->renderer()->isInline()) - || startNode->renderer()->isHR()) - && p.offset() == 0) - return VisiblePosition(Position(startNode, maxDeepOffset(startNode))); - - Node* startBlock = enclosingBlock(startNode); - Node *stayInsideBlock = startBlock; - - Node *node = startNode; - int offset = p.offset(); - - Node *n = startNode; - while (n) { - if (n->isContentEditable() != startNode->isContentEditable()) - break; - RenderObject *r = n->renderer(); - if (!r) { - n = n->traverseNextNode(stayInsideBlock); - continue; - } - RenderStyle *style = r->style(); - if (style->visibility() != VISIBLE) { - n = n->traverseNextNode(stayInsideBlock); - continue; - } - - if (r->isBR() || isBlock(n)) - break; - - // FIXME: We avoid returning a position where the renderer can't accept the caret. - // We should probably do this in other cases such as startOfParagraph. - if (r->isText() && r->caretMaxRenderedOffset() > 0) { - int length = static_cast<RenderText*>(r)->textLength(); - if (style->preserveNewline()) { - const UChar* chars = static_cast<RenderText*>(r)->characters(); - int o = n == startNode ? offset : 0; - for (int i = o; i < length; ++i) - if (chars[i] == '\n') - return VisiblePosition(n, i, DOWNSTREAM); - } - node = n; - offset = r->caretMaxOffset(); - n = n->traverseNextNode(stayInsideBlock); - } else if (editingIgnoresContent(n) || isTableElement(n)) { - node = n; - offset = maxDeepOffset(n); - n = n->traverseNextSibling(stayInsideBlock); - } else - n = n->traverseNextNode(stayInsideBlock); - } - - return VisiblePosition(node, offset, DOWNSTREAM); -} - -VisiblePosition startOfNextParagraph(const VisiblePosition& visiblePosition) -{ - VisiblePosition paragraphEnd(endOfParagraph(visiblePosition)); - VisiblePosition afterParagraphEnd(paragraphEnd.next(true)); - // The position after the last position in the last cell of a table - // is not the start of the next paragraph. - if (isFirstPositionAfterTable(afterParagraphEnd)) - return afterParagraphEnd.next(true); - return afterParagraphEnd; -} - -bool inSameParagraph(const VisiblePosition &a, const VisiblePosition &b) -{ - return a.isNotNull() && startOfParagraph(a) == startOfParagraph(b); -} - -bool isStartOfParagraph(const VisiblePosition &pos) -{ - return pos.isNotNull() && pos == startOfParagraph(pos); -} - -bool isEndOfParagraph(const VisiblePosition &pos) -{ - return pos.isNotNull() && pos == endOfParagraph(pos); -} - -VisiblePosition previousParagraphPosition(const VisiblePosition &p, int x) -{ - VisiblePosition pos = p; - do { - VisiblePosition n = previousLinePosition(pos, x); - if (n.isNull() || n == pos) - return p; - pos = n; - } while (inSameParagraph(p, pos)); - return pos; -} - -VisiblePosition nextParagraphPosition(const VisiblePosition &p, int x) -{ - VisiblePosition pos = p; - do { - VisiblePosition n = nextLinePosition(pos, x); - if (n.isNull() || n == pos) - return p; - pos = n; - } while (inSameParagraph(p, pos)); - return pos; -} - -// --------- - -VisiblePosition startOfBlock(const VisiblePosition &c) -{ - Position p = c.deepEquivalent(); - Node *startNode = p.node(); - if (!startNode) - return VisiblePosition(); - return VisiblePosition(Position(startNode->enclosingBlockFlowElement(), 0), DOWNSTREAM); -} - -VisiblePosition endOfBlock(const VisiblePosition &c) -{ - Position p = c.deepEquivalent(); - - Node *startNode = p.node(); - if (!startNode) - return VisiblePosition(); - - Node *startBlock = startNode->enclosingBlockFlowElement(); - - return VisiblePosition(startBlock, startBlock->childNodeCount(), VP_DEFAULT_AFFINITY); -} - -bool inSameBlock(const VisiblePosition &a, const VisiblePosition &b) -{ - return !a.isNull() && enclosingBlockFlowElement(a) == enclosingBlockFlowElement(b); -} - -bool isStartOfBlock(const VisiblePosition &pos) -{ - return pos.isNotNull() && pos == startOfBlock(pos); -} - -bool isEndOfBlock(const VisiblePosition &pos) -{ - return pos.isNotNull() && pos == endOfBlock(pos); -} - -// --------- - -VisiblePosition startOfDocument(const Node* node) -{ - if (!node) - return VisiblePosition(); - - return VisiblePosition(node->document()->documentElement(), 0, DOWNSTREAM); -} - -VisiblePosition startOfDocument(const VisiblePosition &c) -{ - return startOfDocument(c.deepEquivalent().node()); -} - -VisiblePosition endOfDocument(const Node* node) -{ - if (!node || !node->document()) - return VisiblePosition(); - - Element* doc = node->document()->documentElement(); - return VisiblePosition(doc, doc->childNodeCount(), DOWNSTREAM); -} - -VisiblePosition endOfDocument(const VisiblePosition &c) -{ - return endOfDocument(c.deepEquivalent().node()); -} - -bool inSameDocument(const VisiblePosition &a, const VisiblePosition &b) -{ - Position ap = a.deepEquivalent(); - Node *an = ap.node(); - if (!an) - return false; - Position bp = b.deepEquivalent(); - Node *bn = bp.node(); - if (an == bn) - return true; - - return an->document() == bn->document(); -} - -bool isStartOfDocument(const VisiblePosition &p) -{ - return p.isNotNull() && p.previous().isNull(); -} - -bool isEndOfDocument(const VisiblePosition &p) -{ - return p.isNotNull() && p.next().isNull(); -} - -// --------- - -VisiblePosition startOfEditableContent(const VisiblePosition& visiblePosition) -{ - Node* highestRoot = highestEditableRoot(visiblePosition.deepEquivalent()); - if (!highestRoot) - return VisiblePosition(); - - return VisiblePosition(highestRoot, 0, DOWNSTREAM); -} - -VisiblePosition endOfEditableContent(const VisiblePosition& visiblePosition) -{ - Node* highestRoot = highestEditableRoot(visiblePosition.deepEquivalent()); - if (!highestRoot) - return VisiblePosition(); - - return VisiblePosition(highestRoot, maxDeepOffset(highestRoot), DOWNSTREAM); -} - -} diff --git a/WebCore/editing/visible_units.h b/WebCore/editing/visible_units.h deleted file mode 100644 index 2663888..0000000 --- a/WebCore/editing/visible_units.h +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright (C) 2004 Apple Computer, 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 visible_units_h -#define visible_units_h - -#include "Document.h" -#include "TextAffinity.h" - -namespace WebCore { - -class VisiblePosition; - -enum EWordSide { RightWordIfOnBoundary = false, LeftWordIfOnBoundary = true }; - -// words -VisiblePosition startOfWord(const VisiblePosition &, EWordSide = RightWordIfOnBoundary); -VisiblePosition endOfWord(const VisiblePosition &, EWordSide = RightWordIfOnBoundary); -VisiblePosition previousWordPosition(const VisiblePosition &); -VisiblePosition nextWordPosition(const VisiblePosition &); - -// sentences -VisiblePosition startOfSentence(const VisiblePosition &); -VisiblePosition endOfSentence(const VisiblePosition &); -VisiblePosition previousSentencePosition(const VisiblePosition &); -VisiblePosition nextSentencePosition(const VisiblePosition &); - -// lines -VisiblePosition startOfLine(const VisiblePosition &); -VisiblePosition endOfLine(const VisiblePosition &); -VisiblePosition previousLinePosition(const VisiblePosition &, int x); -VisiblePosition nextLinePosition(const VisiblePosition &, int x); -bool inSameLine(const VisiblePosition &, const VisiblePosition &); -bool isStartOfLine(const VisiblePosition &); -bool isEndOfLine(const VisiblePosition &); - -// paragraphs (perhaps a misnomer, can be divided by line break elements) -VisiblePosition startOfParagraph(const VisiblePosition&); -VisiblePosition endOfParagraph(const VisiblePosition&); -VisiblePosition startOfNextParagraph(const VisiblePosition&); -VisiblePosition previousParagraphPosition(const VisiblePosition &, int x); -VisiblePosition nextParagraphPosition(const VisiblePosition &, int x); -bool inSameParagraph(const VisiblePosition &, const VisiblePosition &); -bool isStartOfParagraph(const VisiblePosition &); -bool isEndOfParagraph(const VisiblePosition &); - -// blocks (true paragraphs; line break elements don't break blocks) -VisiblePosition startOfBlock(const VisiblePosition &); -VisiblePosition endOfBlock(const VisiblePosition &); -bool inSameBlock(const VisiblePosition &, const VisiblePosition &); -bool isStartOfBlock(const VisiblePosition &); -bool isEndOfBlock(const VisiblePosition &); - -// document -VisiblePosition startOfDocument(const Node*); -VisiblePosition endOfDocument(const Node*); -VisiblePosition startOfDocument(const VisiblePosition &); -VisiblePosition endOfDocument(const VisiblePosition &); -bool inSameDocument(const VisiblePosition &, const VisiblePosition &); -bool isStartOfDocument(const VisiblePosition &); -bool isEndOfDocument(const VisiblePosition &); - -// editable content -VisiblePosition startOfEditableContent(const VisiblePosition&); -VisiblePosition endOfEditableContent(const VisiblePosition&); - -} // namespace WebCore - -#endif // VisiblePosition_h diff --git a/WebCore/editing/wx/EditorWx.cpp b/WebCore/editing/wx/EditorWx.cpp deleted file mode 100644 index 0a63215..0000000 --- a/WebCore/editing/wx/EditorWx.cpp +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (C) 2007 Kevin Ollivier <kevino@theolliviers.com> - * - * 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 "Editor.h" - -#include "ClipboardWx.h" - -namespace WebCore { - -PassRefPtr<Clipboard> Editor::newGeneralClipboard(ClipboardAccessPolicy policy) -{ - return ClipboardWx::create(policy, true); -} - -} |