LayoutTests:
authorjusting <justing@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 19 Apr 2007 20:27:47 +0000 (20:27 +0000)
committerjusting <justing@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 19 Apr 2007 20:27:47 +0000 (20:27 +0000)
        Reviewed by darin

        <rdar://problem/5142012>
        GoogleDocs: Crash at WebCore::Range::startPosition() when creating a list from a link

        Tests the change to pushAnchorElementDown, where we remove
        the anchor element that has had copies of it pushed down:
        * editing/execCommand/5142012-1-expected.checksum: Added.
        * editing/execCommand/5142012-1-expected.png: Added.
        * editing/execCommand/5142012-1-expected.txt: Added.
        * editing/execCommand/5142012-1.html: Added.

        Demonstrates the crash (fixed by adding nil-checks
        to moveParagraphs):
        * editing/execCommand/5142012-2-expected.checksum: Added.
        * editing/execCommand/5142012-2-expected.png: Added.
        * editing/execCommand/5142012-2-expected.txt: Added.
        * editing/execCommand/5142012-2.html: Added.

        Tests the changes to positionAvoidingSpecialElementBoundary,
        where we push down anchors before we avoid them so that
        we don't also avoid structural elements like lists and paragraphs:
        * editing/execCommand/5142012-3-expected.checksum: Added.
        * editing/execCommand/5142012-3-expected.png: Added.
        * editing/execCommand/5142012-3-expected.txt: Added.
        * editing/execCommand/5142012-3.html: Added.

WebCore:

        Reviewed by darin

        <rdar://problem/5142012>
        GoogleDocs: Crash at WebCore::Range::startPosition() when creating a list from a link

        List creation uses moveParagraphs to push content into list items.
        Its fragment creation (using createMarkup) incorrectly uses regular
        spaces instead of nbsps for 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.  We use the result from rangeFromLocationAndLength
        (null) and crash.

        Also when moveParagraphs tries to push content into a list item surrounded
        by an anchor, it fails because positionAvoidingSpecialElementBoundary avoids
        the anchor, which also avoids the list item.  This was fixed by pushing
        down anchors before avoiding them.

        * editing/CompositeEditCommand.cpp:
        (WebCore::CompositeEditCommand::pushAnchorElementDown): Remove the
        old anchor after we push down clones of it, this is what callers
        expect.
        (WebCore::CompositeEditCommand::pushPartiallySelectedAnchorElementsDown):
        Call the new enclosingAnchorElement, that takes in a position instead
        of a node.
        (WebCore::CompositeEditCommand::moveParagraphs): If spaces collapsed
        as a result of the move, rangeFromLocationAndLength can return null,
        bail and don't try to preserve the selection in that case.
        (WebCore::CompositeEditCommand::positionAvoidingSpecialElementBoundary):
        Moved from htmlediting.cpp.
        Make sure anchors are pushed down before avoiding them so that we don't
        also avoid structural elements like lists and blocks.
        * editing/CompositeEditCommand.h:
        * editing/TypingCommand.cpp: Moved isFirst/LastPositionBefore/AfterTable
        to htmlediting.cpp.
        * editing/htmlediting.cpp:
        Moved positionAvoidingSpecialElementBoundary so that it could call
        pushAnchorElementDown.
        (WebCore::isFirstPositionAfterTable): Moved here.
        (WebCore::isLastPositionBeforeTable): Moved here.
        (WebCore::enclosingAnchorElement): Moved here.
        (WebCore::enclosingListChild): Removed an extraneous space.
        * editing/htmlediting.h:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@20958 268f45cc-cd09-0410-ab3c-d52691b4dbfc

19 files changed:
LayoutTests/ChangeLog
LayoutTests/editing/execCommand/5142012-1-expected.checksum [new file with mode: 0644]
LayoutTests/editing/execCommand/5142012-1-expected.png [new file with mode: 0644]
LayoutTests/editing/execCommand/5142012-1-expected.txt [new file with mode: 0644]
LayoutTests/editing/execCommand/5142012-1.html [new file with mode: 0644]
LayoutTests/editing/execCommand/5142012-2-expected.checksum [new file with mode: 0644]
LayoutTests/editing/execCommand/5142012-2-expected.png [new file with mode: 0644]
LayoutTests/editing/execCommand/5142012-2-expected.txt [new file with mode: 0644]
LayoutTests/editing/execCommand/5142012-2.html [new file with mode: 0644]
LayoutTests/editing/execCommand/5142012-3-expected.checksum [new file with mode: 0644]
LayoutTests/editing/execCommand/5142012-3-expected.png [new file with mode: 0644]
LayoutTests/editing/execCommand/5142012-3-expected.txt [new file with mode: 0644]
LayoutTests/editing/execCommand/5142012-3.html [new file with mode: 0644]
WebCore/ChangeLog
WebCore/editing/CompositeEditCommand.cpp
WebCore/editing/CompositeEditCommand.h
WebCore/editing/TypingCommand.cpp
WebCore/editing/htmlediting.cpp
WebCore/editing/htmlediting.h

index bbebfaf..5799a72 100644 (file)
@@ -1,3 +1,32 @@
+2007-04-19  Justin Garcia  <justin.garcia@apple.com>
+
+        Reviewed by darin
+        
+        <rdar://problem/5142012> 
+        GoogleDocs: Crash at WebCore::Range::startPosition() when creating a list from a link
+
+        Tests the change to pushAnchorElementDown, where we remove
+        the anchor element that has had copies of it pushed down:
+        * editing/execCommand/5142012-1-expected.checksum: Added.
+        * editing/execCommand/5142012-1-expected.png: Added.
+        * editing/execCommand/5142012-1-expected.txt: Added.
+        * editing/execCommand/5142012-1.html: Added.
+        
+        Demonstrates the crash (fixed by adding nil-checks 
+        to moveParagraphs):
+        * editing/execCommand/5142012-2-expected.checksum: Added.
+        * editing/execCommand/5142012-2-expected.png: Added.
+        * editing/execCommand/5142012-2-expected.txt: Added.
+        * editing/execCommand/5142012-2.html: Added.
+        
+        Tests the changes to positionAvoidingSpecialElementBoundary,
+        where we push down anchors before we avoid them so that
+        we don't also avoid structural elements like lists and paragraphs:
+        * editing/execCommand/5142012-3-expected.checksum: Added.
+        * editing/execCommand/5142012-3-expected.png: Added.
+        * editing/execCommand/5142012-3-expected.txt: Added.
+        * editing/execCommand/5142012-3.html: Added.
+
 2007-04-19  Beth Dakin  <bdakin@apple.com>
 
         Reviewed by Hyatt.
diff --git a/LayoutTests/editing/execCommand/5142012-1-expected.checksum b/LayoutTests/editing/execCommand/5142012-1-expected.checksum
new file mode 100644 (file)
index 0000000..685d8f8
--- /dev/null
@@ -0,0 +1 @@
+b3d66ada1fd2edafe539c915eace7f76
\ No newline at end of file
diff --git a/LayoutTests/editing/execCommand/5142012-1-expected.png b/LayoutTests/editing/execCommand/5142012-1-expected.png
new file mode 100644 (file)
index 0000000..4652a94
Binary files /dev/null and b/LayoutTests/editing/execCommand/5142012-1-expected.png differ
diff --git a/LayoutTests/editing/execCommand/5142012-1-expected.txt b/LayoutTests/editing/execCommand/5142012-1-expected.txt
new file mode 100644 (file)
index 0000000..110ae86
--- /dev/null
@@ -0,0 +1,27 @@
+layer at (0,0) size 800x600
+  RenderView at (0,0) size 800x600
+layer at (0,0) size 800x600
+  RenderBlock {HTML} at (0,0) size 800x600
+    RenderBody {BODY} at (8,8) size 784x584
+      RenderBlock {P} at (0,0) size 784x36
+        RenderText {#text} at (0,0) size 766x36
+          text run at (0,0) width 449: "This tests for a bug when removing links from a selection with Unlink. "
+          text run at (449,0) width 317: "There shouldn't be any links *inside the selection*"
+          text run at (0,18) width 43: "below."
+      RenderBlock {DIV} at (0,52) size 784x36
+        RenderBlock {DIV} at (0,0) size 784x18
+          RenderInline {A} at (0,0) size 23x18 [color=#0000EE]
+            RenderText {#text} at (0,0) size 23x18
+              text run at (0,0) width 23: "Hel"
+          RenderInline {SPAN} at (0,0) size 12x18
+            RenderText {#text} at (23,0) size 12x18
+              text run at (23,0) width 12: "lo"
+        RenderBlock (anonymous) at (0,18) size 784x0
+        RenderBlock {DIV} at (0,18) size 784x18
+          RenderInline {SPAN} at (0,0) size 15x18
+            RenderText {#text} at (0,0) size 15x18
+              text run at (0,0) width 15: "W"
+          RenderText {#text} at (15,0) size 30x18
+            text run at (15,0) width 30: "orld!"
+selection start: position 0 of child 0 {#text} of child 1 {SPAN} of child 0 {DIV} of child 2 {DIV} of child 0 {BODY} of child 0 {HTML} of document
+selection end:   position 1 of child 0 {#text} of child 0 {SPAN} of child 1 {DIV} of child 2 {DIV} of child 0 {BODY} of child 0 {HTML} of document
diff --git a/LayoutTests/editing/execCommand/5142012-1.html b/LayoutTests/editing/execCommand/5142012-1.html
new file mode 100644 (file)
index 0000000..198a53f
--- /dev/null
@@ -0,0 +1,11 @@
+<p>This tests for a bug when removing links from a selection with Unlink.  There shouldn't be any links *inside the selection* below.</p>
+<div contenteditable="true"><a href="http:://www.google.com/"><div>Hel<span id="start">lo</span></div></a><div><span id="end">W</span>orld!</div></div>
+
+<script>
+var start = document.getElementById("start");
+var end = document.getElementById("end");
+var sel = window.getSelection();
+sel.setBaseAndExtent(start, 0, end, end.childNodes.length);
+
+document.execCommand("Unlink");
+</script>
diff --git a/LayoutTests/editing/execCommand/5142012-2-expected.checksum b/LayoutTests/editing/execCommand/5142012-2-expected.checksum
new file mode 100644 (file)
index 0000000..642fd61
--- /dev/null
@@ -0,0 +1 @@
+49e5eb38281f11bae8413322de536537
\ No newline at end of file
diff --git a/LayoutTests/editing/execCommand/5142012-2-expected.png b/LayoutTests/editing/execCommand/5142012-2-expected.png
new file mode 100644 (file)
index 0000000..26d054e
Binary files /dev/null and b/LayoutTests/editing/execCommand/5142012-2-expected.png differ
diff --git a/LayoutTests/editing/execCommand/5142012-2-expected.txt b/LayoutTests/editing/execCommand/5142012-2-expected.txt
new file mode 100644 (file)
index 0000000..9ff9b0f
--- /dev/null
@@ -0,0 +1,16 @@
+layer at (0,0) size 800x600
+  RenderView at (0,0) size 800x600
+layer at (0,0) size 800x600
+  RenderBlock {HTML} at (0,0) size 800x600
+    RenderBody {BODY} at (8,8) size 784x576
+      RenderBlock {UL} at (0,0) size 784x18
+        RenderListItem {LI} at (40,0) size 744x18
+          RenderListMarker at (-17,0) size 7x18: bullet
+          RenderInline {A} at (0,0) size 736x18 [color=#0000EE]
+            RenderText {#text} at (0,0) size 736x18
+              text run at (0,0) width 336: "This tests for a crash when creating a list from a link. "
+              text run at (336,0) width 400: "This paragraph should be a link and should be inside a list item."
+          RenderText {#text} at (0,0) size 0x0
+      RenderBlock (anonymous) at (0,34) size 784x0
+selection start: position 0 of child 0 {#text} of child 0 {A} of child 0 {LI} of child 0 {UL} of child 0 {BODY} of child 0 {HTML} of document
+selection end:   position 123 of child 0 {#text} of child 0 {A} of child 0 {LI} of child 0 {UL} of child 0 {BODY} of child 0 {HTML} of document
diff --git a/LayoutTests/editing/execCommand/5142012-2.html b/LayoutTests/editing/execCommand/5142012-2.html
new file mode 100644 (file)
index 0000000..096e98a
--- /dev/null
@@ -0,0 +1,6 @@
+<body contenteditable="true"><a href="#">This tests for a crash when creating a list from a link.  This paragraph should be a link and should be inside a list item.</a> <br></div>
+<script>
+document.body.focus();
+document.execCommand("SelectAll");
+document.execCommand("InsertUnorderedList");
+</script>
diff --git a/LayoutTests/editing/execCommand/5142012-3-expected.checksum b/LayoutTests/editing/execCommand/5142012-3-expected.checksum
new file mode 100644 (file)
index 0000000..6cfadf1
--- /dev/null
@@ -0,0 +1 @@
+65effc05a0d416b772ebbaf78bba2b76
\ No newline at end of file
diff --git a/LayoutTests/editing/execCommand/5142012-3-expected.png b/LayoutTests/editing/execCommand/5142012-3-expected.png
new file mode 100644 (file)
index 0000000..34fa672
Binary files /dev/null and b/LayoutTests/editing/execCommand/5142012-3-expected.png differ
diff --git a/LayoutTests/editing/execCommand/5142012-3-expected.txt b/LayoutTests/editing/execCommand/5142012-3-expected.txt
new file mode 100644 (file)
index 0000000..7224b45
--- /dev/null
@@ -0,0 +1,22 @@
+layer at (0,0) size 800x600
+  RenderView at (0,0) size 800x600
+layer at (0,0) size 800x600
+  RenderBlock {HTML} at (0,0) size 800x600
+    RenderBody {BODY} at (8,8) size 784x584
+      RenderBlock {P} at (0,0) size 784x36
+        RenderText {#text} at (0,0) size 759x36
+          text run at (0,0) width 467: "This tests for a bug when inserting after a link at the end of the document. "
+          text run at (467,0) width 292: "Like TextEdit, we insert content outside of the"
+          text run at (0,18) width 643: "link, but inserting outside of the link shouldn't cause the content to be inserted in the wrong paragraph."
+      RenderBlock {DIV} at (0,52) size 784x36
+        RenderBlock (anonymous) at (0,0) size 784x18
+          RenderText {#text} at (0,0) size 259x18
+            text run at (0,0) width 259: "This paragraph should not contains links."
+        RenderBlock {DIV} at (0,18) size 784x18
+          RenderInline {A} at (0,0) size 194x18 [color=#0000EE]
+            RenderText {#text} at (0,0) size 194x18
+              text run at (0,0) width 194: "This sentence should be a link."
+          RenderText {#text} at (194,0) size 164x18
+            text run at (194,0) width 164: " This sentence should not."
+        RenderBlock (anonymous) at (0,36) size 784x0
+caret: position 26 of child 1 {#text} of child 1 {DIV} of child 2 {DIV} of child 0 {BODY} of child 0 {HTML} of document
diff --git a/LayoutTests/editing/execCommand/5142012-3.html b/LayoutTests/editing/execCommand/5142012-3.html
new file mode 100644 (file)
index 0000000..cac5e74
--- /dev/null
@@ -0,0 +1,11 @@
+<p>This tests for a bug when inserting after a link at the end of the document.  Like TextEdit, we insert content outside of the link, but inserting outside of the link shouldn't cause the content to be inserted in the wrong paragraph.</p>
+<div contenteditable="true">This paragraph should not contains links.<a href="#"><div id="div">This sentence should be a link.</div></a></div>
+
+<script>
+var sel = window.getSelection();
+var div = document.getElementById("div");
+sel.setPosition(div, div.childNodes.length);
+
+document.execCommand("InsertText", false, " ");
+document.execCommand("InsertText", false, "This sentence should not.");
+</script>
index 1b82fe6..7f4f010 100644 (file)
@@ -1,3 +1,49 @@
+2007-04-19  Justin Garcia  <justin.garcia@apple.com>
+
+        Reviewed by darin
+
+        <rdar://problem/5142012> 
+        GoogleDocs: Crash at WebCore::Range::startPosition() when creating a list from a link
+        
+        List creation uses moveParagraphs to push content into list items.
+        Its fragment creation (using createMarkup) incorrectly uses regular
+        spaces instead of nbsps for 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.  We use the result from rangeFromLocationAndLength
+        (null) and crash.
+        
+        Also when moveParagraphs tries to push content into a list item surrounded
+        by an anchor, it fails because positionAvoidingSpecialElementBoundary avoids 
+        the anchor, which also avoids the list item.  This was fixed by pushing
+        down anchors before avoiding them.
+        
+        * editing/CompositeEditCommand.cpp:
+        (WebCore::CompositeEditCommand::pushAnchorElementDown): Remove the
+        old anchor after we push down clones of it, this is what callers
+        expect.
+        (WebCore::CompositeEditCommand::pushPartiallySelectedAnchorElementsDown):
+        Call the new enclosingAnchorElement, that takes in a position instead
+        of a node.
+        (WebCore::CompositeEditCommand::moveParagraphs): If spaces collapsed
+        as a result of the move, rangeFromLocationAndLength can return null,
+        bail and don't try to preserve the selection in that case.
+        (WebCore::CompositeEditCommand::positionAvoidingSpecialElementBoundary):
+        Moved from htmlediting.cpp.
+        Make sure anchors are pushed down before avoiding them so that we don't
+        also avoid structural elements like lists and blocks.
+        * editing/CompositeEditCommand.h:
+        * editing/TypingCommand.cpp: Moved isFirst/LastPositionBefore/AfterTable
+        to htmlediting.cpp.
+        * editing/htmlediting.cpp:
+        Moved positionAvoidingSpecialElementBoundary so that it could call
+        pushAnchorElementDown.
+        (WebCore::isFirstPositionAfterTable): Moved here.
+        (WebCore::isLastPositionBeforeTable): Moved here.
+        (WebCore::enclosingAnchorElement): Moved here.
+        (WebCore::enclosingListChild): Removed an extraneous space.
+        * editing/htmlediting.h:
+
 2007-04-19  Beth Dakin  <bdakin@apple.com>
 
         Reviewed by Hyatt.
index d5d4b8c..045586f 100644 (file)
@@ -636,13 +636,6 @@ Node* CompositeEditCommand::moveParagraphContentsToNewBlockIfNecessary(const Pos
     return newBlock.get();
 }
 
-Node* enclosingAnchorElement(Node* node)
-{
-    while (node && !(node->isElementNode() && node->isLink()))
-        node = node->parentNode();
-    return node;
-}
-
 void CompositeEditCommand::pushAnchorElementDown(Node* anchorNode)
 {
     if (!anchorNode)
@@ -652,6 +645,9 @@ void CompositeEditCommand::pushAnchorElementDown(Node* anchorNode)
     
     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
@@ -664,12 +660,12 @@ void CompositeEditCommand::pushPartiallySelectedAnchorElementsDown()
     VisiblePosition visibleStart(originalSelection.start());
     VisiblePosition visibleEnd(originalSelection.end());
     
-    Node* startAnchor = enclosingAnchorElement(originalSelection.start().node());
+    Node* startAnchor = enclosingAnchorElement(originalSelection.start());
     VisiblePosition startOfStartAnchor(Position(startAnchor, 0));
     if (startAnchor && startOfStartAnchor != visibleStart)
         pushAnchorElementDown(startAnchor);
 
-    Node* endAnchor = enclosingAnchorElement(originalSelection.end().node());
+    Node* endAnchor = enclosingAnchorElement(originalSelection.end());
     VisiblePosition endOfEndAnchor(Position(endAnchor, 0));
     if (endAnchor && endOfEndAnchor != visibleEnd)
         pushAnchorElementDown(endAnchor);
@@ -790,9 +786,15 @@ void CompositeEditCommand::moveParagraphs(const VisiblePosition& startOfParagrap
     applyCommandToComposite(new ReplaceSelectionCommand(document(), fragment.get(), true, false, !preserveStyle, false));
     
     if (preserveSelection && startIndex != -1) {
-        setEndingSelection(Selection(TextIterator::rangeFromLocationAndLength(document()->documentElement(), destinationIndex + startIndex, 0, true)->startPosition(), 
-                                     TextIterator::rangeFromLocationAndLength(document()->documentElement(), destinationIndex + endIndex, 0, true)->startPosition(), 
-                                     DOWNSTREAM));
+        // 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));
     }
 }
 
@@ -829,6 +831,64 @@ bool CompositeEditCommand::breakOutOfEmptyListItem()
     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& p, bool alwaysAvoidAnchors)
+{
+    if (p.isNull())
+        return p;
+        
+    VisiblePosition visiblePos(p);
+    Node* enclosingAnchor = enclosingAnchorElement(p);
+    Position result = p;
+    // 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 (Node* anchor = enclosingAnchorElement(p))
+                if (p.node() != anchor && p.node()->parentNode() != anchor) {
+                    pushAnchorElementDown(anchor);
+                    enclosingAnchor = enclosingAnchorElement(p);
+                }
+            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 (Node* anchor = enclosingAnchorElement(p))
+                if (p.node() != anchor && p.node()->parentNode() != anchor) {
+                    pushAnchorElementDown(anchor);
+                    enclosingAnchor = enclosingAnchorElement(p);
+                }
+            result = positionBeforeNode(enclosingAnchor);
+        }
+    // FIXME: If this avoids positions before/after tables, then it should
+    // probably avoid positions before/after replaced elements, before brs,
+    // etc.  Since it doesn't and there are no known cases where we insert
+    // into such elements, avoiding tables is probably done elsewhere
+    // and is not needed here.
+    } else if (isTableElement(p.node())) {
+        if (isFirstPositionAfterTable(visiblePos))
+            result = positionAfterNode(p.node());
+        if (isLastPositionBeforeTable(visiblePos))
+            result = positionBeforeNode(p.node());
+    }
+        
+    if (result.isNull() || !editableRootForPosition(result))
+        result = p;
+    
+    return result;
+}
+
 PassRefPtr<Element> createBlockPlaceholderElement(Document* document)
 {
     ExceptionCode ec = 0;
index 5e9647f..b8a3491 100644 (file)
@@ -99,6 +99,8 @@ protected:
     void moveParagraphs(const VisiblePosition&, const VisiblePosition&, const VisiblePosition&, bool preserveSelection = false, bool preserveStyle = true);
     
     bool breakOutOfEmptyListItem();
+    
+    Position positionAvoidingSpecialElementBoundary(const Position&, bool alwaysAvoidAnchors = true);
 
     Vector<RefPtr<EditCommand> > m_commands;
 
index fd12e19..cc83543 100644 (file)
@@ -333,24 +333,6 @@ void TypingCommand::insertParagraphSeparatorInQuotedContent()
     typingAddedToOpenCommand();
 }
 
-static 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;
-}
-
-static Node* isLastPositionBeforeTable(const VisiblePosition& visiblePosition)
-{
-    Position downstream(visiblePosition.deepEquivalent().upstream());
-    if (downstream.node() && downstream.node()->renderer() && downstream.node()->renderer()->isTable() && downstream.offset() == maxDeepOffset(downstream.node()))
-        return downstream.node();
-    
-    return 0;
-}
-
 void TypingCommand::deleteKeyPressed(TextGranularity granularity)
 {
     Selection selectionToDelete;
index 9a1819d..b796604 100644 (file)
@@ -528,6 +528,24 @@ Position positionOutsideContainingSpecialElement(const Position &pos, Node **con
     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());
@@ -591,6 +609,17 @@ Node* enclosingTableCell(const Position& p)
     return 0;
 }
 
+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)
@@ -608,7 +637,7 @@ Node* enclosingList(Node* node)
     return 0;
 }
 
-Node* enclosingListChild (Node *node)
+Node* enclosingListChild(Node *node)
 {
     if (!node)
         return 0;
@@ -701,44 +730,6 @@ bool isTableElement(Node* n)
     return (renderer && (renderer->style()->display() == TABLE || renderer->style()->display() == INLINE_TABLE));
 }
 
-// This function is necessary because a VisiblePosition is allowed
-// to be at the start or end of elements where we do not want to
-// add content directly.  For example, clicking at the end of a hyperlink,
-// then typing, needs to add the text after the link.  Also, table
-// offset 0 and table offset childNodeCount are valid VisiblePostions,
-// but we can not add more content right there... it needs to go before
-// or after the table.
-Position positionAvoidingSpecialElementBoundary(const Position &pos, bool avoidAnchor)
-{
-    if (pos.isNull())
-        return pos;
-        
-    VisiblePosition vPos = VisiblePosition(pos, DOWNSTREAM);
-    Node* enclosingAnchor = enclosingNodeWithTag(pos.node(), aTag);
-    Position result;
-    if (enclosingAnchor && !isBlock(enclosingAnchor)) {
-        // If the caret is after an anchor, do insertion inside the anchor unless it's the last 
-        // VisiblePosition in the document, to match TextEdit.
-        if (VisiblePosition(Position(enclosingAnchor, maxDeepOffset(enclosingAnchor))) == vPos && (isEndOfDocument(vPos) || avoidAnchor))
-            result = positionAfterNode(enclosingAnchor);
-        // If the caret is before an anchor, do insertion outside the anchor unless it's the first
-        // VisiblePosition in a paragraph, to match TextEdit.
-        if (VisiblePosition(Position(enclosingAnchor, 0)) == vPos && (!isStartOfParagraph(vPos) || avoidAnchor))
-            result = positionBeforeNode(enclosingAnchor);
-    } else if (isTableElement(pos.node())) {
-        if (VisiblePosition(Position(pos.node(), maxDeepOffset(pos.node()))) == vPos)
-            result = positionAfterNode(pos.node());
-        if (VisiblePosition(Position(pos.node(), 0)) == vPos)
-            result = positionBeforeNode(pos.node());
-    } else
-        return pos;
-        
-    if (result.isNull() || !editableRootForPosition(result))
-        result = pos;
-    
-    return result;
-}
-
 PassRefPtr<Element> createDefaultParagraphElement(Document *document)
 {
     ExceptionCode ec = 0;
index df90717..10e3e05 100644 (file)
@@ -100,18 +100,20 @@ Position positionBeforeContainingSpecialElement(const Position&, Node** containi
 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(Node*, const QualifiedName&);
 Node* enclosingNodeOfType(Node*, bool (*nodeIsOfType)(Node*));
 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*);
-Position positionAvoidingSpecialElementBoundary(const Position&, bool avoidAnchor = true);
 
 }