summaryrefslogtreecommitdiffstats
path: root/WebCore/editing/htmlediting.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'WebCore/editing/htmlediting.cpp')
-rw-r--r--WebCore/editing/htmlediting.cpp126
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)