diff options
Diffstat (limited to 'WebCore/editing/htmlediting.cpp')
-rw-r--r-- | WebCore/editing/htmlediting.cpp | 126 |
1 files changed, 117 insertions, 9 deletions
diff --git a/WebCore/editing/htmlediting.cpp b/WebCore/editing/htmlediting.cpp index 7b51295..4124c48 100644 --- a/WebCore/editing/htmlediting.cpp +++ b/WebCore/editing/htmlediting.cpp @@ -84,6 +84,7 @@ bool canHaveChildrenForEditing(const Node* node) !node->hasTagName(embedTag) && !node->hasTagName(appletTag) && !node->hasTagName(selectTag) && + !node->hasTagName(datagridTag) && #if ENABLE(WML) !node->hasTagName(WMLNames::doTag) && #endif @@ -206,9 +207,18 @@ Element* editableRootForPosition(const Position& p) return node->rootEditableElement(); } -bool isContentEditable(const Node* node) +// Finds the enclosing element until which the tree can be split. +// When a user hits ENTER, he/she won't expect this element to be split into two. +// You may pass it as the second argument of splitTreeToNode. +Element* unsplittableElementForPosition(const Position& p) { - return node->isContentEditable(); + // Since enclosingNodeOfType won't search beyond the highest root editable node, + // this code works even if the closest table cell was outside of the root editable node. + Element* enclosingCell = static_cast<Element*>(enclosingNodeOfType(p, &isTableCell, true)); + if (enclosingCell) + return enclosingCell; + + return editableRootForPosition(p); } Position nextCandidate(const Position& position) @@ -564,16 +574,79 @@ Node* isLastPositionBeforeTable(const VisiblePosition& visiblePosition) Position positionBeforeNode(const Node* node) { - // FIXME: This should ASSERT(node->parentNode()) + ASSERT(node); + // FIXME: Should ASSERT(node->parentNode()) but doing so results in editing/deleting/delete-ligature-001.html crashing return Position(node->parentNode(), node->nodeIndex()); } Position positionAfterNode(const Node* node) { - // FIXME: This should ASSERT(node->parentNode()) + ASSERT(node); + // FIXME: Should ASSERT(node->parentNode()) but doing so results in editing/deleting/delete-ligature-001.html crashing return Position(node->parentNode(), node->nodeIndex() + 1); } +// Returns the visible position at the beginning of a node +VisiblePosition visiblePositionBeforeNode(Node* node) +{ + ASSERT(node); + if (node->childNodeCount()) + return VisiblePosition(node, 0, DOWNSTREAM); + ASSERT(node->parentNode()); + return positionBeforeNode(node); +} + +// Returns the visible position at the ending of a node +VisiblePosition visiblePositionAfterNode(Node* node) +{ + ASSERT(node); + if (node->childNodeCount()) + return VisiblePosition(node, node->childNodeCount(), DOWNSTREAM); + ASSERT(node->parentNode()); + return positionAfterNode(node); +} + +// Create a range object with two visible positions, start and end. +// create(PassRefPtr<Document>, const Position&, const Position&); will use deprecatedEditingOffset +// Use this function instead of create a regular range object (avoiding editing offset). +PassRefPtr<Range> createRange(PassRefPtr<Document> document, const VisiblePosition& start, const VisiblePosition& end, ExceptionCode& ec) +{ + ec = 0; + RefPtr<Range> selectedRange = Range::create(document); + selectedRange->setStart(start.deepEquivalent().containerNode(), start.deepEquivalent().computeOffsetInContainerNode(), ec); + if (!ec) + selectedRange->setEnd(end.deepEquivalent().containerNode(), end.deepEquivalent().computeOffsetInContainerNode(), ec); + return selectedRange.release(); +} + +// Extend rangeToExtend to include nodes that wraps range and visibly starts and ends inside or at the boudnaries of maximumRange +// e.g. if the original range spaned "hello" in <div>hello</div>, then this function extends the range to contain div's around it. +// Call this function before copying / moving paragraphs to contain all wrapping nodes. +// This function stops extending the range immediately below rootNode; i.e. the extended range can contain a child node of rootNode +// but it can never contain rootNode itself. +PassRefPtr<Range> extendRangeToWrappingNodes(PassRefPtr<Range> range, const Range* maximumRange, const Node* rootNode) +{ + ASSERT(range); + ASSERT(maximumRange); + + ExceptionCode ec = 0; + Node* ancestor = range->commonAncestorContainer(ec);// find the cloeset common ancestor + Node* highestNode = 0; + // traverse through ancestors as long as they are contained within the range, content-editable, and below rootNode (could be =0). + while (ancestor && ancestor->isContentEditable() && isNodeVisiblyContainedWithin(ancestor, maximumRange) && ancestor != rootNode) { + highestNode = ancestor; + ancestor = ancestor->parentNode(); + } + + if (!highestNode) + return range; + + // Create new range with the highest editable node contained within the range + RefPtr<Range> extendedRange = Range::create(range->ownerDocument()); + extendedRange->selectNode(highestNode, ec); + return extendedRange.release(); +} + bool isListElement(Node *n) { return (n && (n->hasTagName(ulTag) || n->hasTagName(olTag) || n->hasTagName(dlTag))); @@ -586,7 +659,7 @@ Node* enclosingNodeWithTag(const Position& p, const QualifiedName& tagName) Node* root = highestEditableRoot(p); for (Node* n = p.node(); n; n = n->parentNode()) { - if (root && !isContentEditable(n)) + if (root && !n->isContentEditable()) continue; if (n->hasTagName(tagName)) return n; @@ -606,7 +679,7 @@ Node* enclosingNodeOfType(const Position& p, bool (*nodeIsOfType)(const Node*), 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) + if (root && !n->isContentEditable() && onlyReturnEditableNodes) continue; if ((*nodeIsOfType)(n)) return n; @@ -664,7 +737,7 @@ HTMLElement* enclosingList(Node* node) return 0; } -Node* enclosingListChild(Node *node) +HTMLElement* enclosingListChild(Node *node) { if (!node) return 0; @@ -675,7 +748,7 @@ Node* enclosingListChild(Node *node) // 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; + return static_cast<HTMLElement*>(n); if (n == root || isTableCell(n)) return 0; } @@ -737,6 +810,18 @@ HTMLElement* outermostEnclosingList(Node* node) return list; } +bool canMergeLists(Element* firstList, Element* secondList) +{ + if (!firstList || !secondList) + return false; + + return firstList->hasTagName(secondList->tagQName())// make sure the list types match (ol vs. ul) + && firstList->isContentEditable() && secondList->isContentEditable()// both lists are editable + && firstList->rootEditableElement() == secondList->rootEditableElement()// don't cross editing boundaries + && isVisiblyAdjacent(positionAfterNode(firstList), positionBeforeNode(secondList)); + // Make sure there is no visible content between this li and the previous list +} + Node* highestAncestor(Node* node) { ASSERT(node); @@ -961,7 +1046,7 @@ VisibleSelection selectionForParagraphIteration(const VisibleSelection& original } -int indexForVisiblePosition(VisiblePosition& visiblePosition) +int indexForVisiblePosition(const VisiblePosition& visiblePosition) { if (visiblePosition.isNull()) return 0; @@ -970,6 +1055,29 @@ int indexForVisiblePosition(VisiblePosition& visiblePosition) return TextIterator::rangeLength(range.get(), true); } +// Determines whether two positions are visibly next to each other (first then second) +// while ignoring whitespaces and unrendered nodes +bool isVisiblyAdjacent(const Position& first, const Position& second) +{ + return VisiblePosition(first) == VisiblePosition(second.upstream()); +} + +// Determines whether a node is inside a range or visibly starts and ends at the boundaries of the range. +// Call this function to determine whether a node is visibly fit inside selectedRange +bool isNodeVisiblyContainedWithin(Node* node, const Range* selectedRange) +{ + ASSERT(node); + ASSERT(selectedRange); + // If the node is inside the range, then it surely is contained within + ExceptionCode ec = 0; + if (selectedRange->compareNode(node, ec) == Range::NODE_INSIDE) + return true; + + // If the node starts and ends at where selectedRange starts and ends, the node is contained within + return visiblePositionBeforeNode(node) == selectedRange->startPosition() + && visiblePositionAfterNode(node) == selectedRange->endPosition(); +} + PassRefPtr<Range> avoidIntersectionWithNode(const Range* range, Node* node) { if (!range) |