Reviewed by Darin Adler.
<rdar://problem/
5575101> GoogleDocs: Hang in SplitElementCommand::doApply when outdenting a list item in a particular list
* editing/CompositeEditCommand.cpp:
(WebCore::CompositeEditCommand::splitTreeToNode): Moved here.
* editing/CompositeEditCommand.h:
* editing/IndentOutdentCommand.cpp: Moved splitTreeToNode.
* editing/IndentOutdentCommand.h: Ditto, and removed unimplemented splitTreeTo.
* editing/InsertListCommand.cpp:
(WebCore::InsertListCommand::doApply): Split ancestors of listChildNode between
it and listNode, if they exists, so that moving listChildNode doesn't put it out
of order. Added a test case to cover each change.
* editing/SplitElementCommand.cpp:
(WebCore::SplitElementCommand::doApply): Added an ASSERT to catch code that
tries to split a container at a bogus child, and an early return to avoid a
hang in that case.
LayoutTests:
Reviewed by Darin Adler.
<rdar://problem/
5575101> Hang in SplitElementCommand::doApply when outdenting a list item in a particular list
* editing/execCommand/
5575101-1-expected.txt: Added.
* editing/execCommand/
5575101-1.html: Added.
* editing/execCommand/
5575101-2-expected.txt: Added.
* editing/execCommand/
5575101-2.html: Added.
* editing/execCommand/
5575101-3-expected.txt: Added.
* editing/execCommand/
5575101-3.html: Added.
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@28716
268f45cc-cd09-0410-ab3c-
d52691b4dbfc
+2007-12-14 Justin Garcia <justin.garcia@apple.com>
+
+ Reviewed by Darin Adler.
+
+ <rdar://problem/5575101> Hang in SplitElementCommand::doApply when outdenting a list item in a particular list
+
+ * editing/execCommand/5575101-1-expected.txt: Added.
+ * editing/execCommand/5575101-1.html: Added.
+ * editing/execCommand/5575101-2-expected.txt: Added.
+ * editing/execCommand/5575101-2.html: Added.
+ * editing/execCommand/5575101-3-expected.txt: Added.
+ * editing/execCommand/5575101-3.html: Added.
+
2007-12-14 Anders Carlsson <andersca@apple.com>
Reviewed by Darin and Geoff.
--- /dev/null
+This tests for a hang when outdenting a list item within a sublist twice.
+
+one
+two
+three
+four
--- /dev/null
+<p>This tests for a hang when outdenting a list item within a sublist twice.</p>
+<div contenteditable="true">
+<ol>
+ <li>one</li>
+ <li id="insert">two<ol><li id="outdent">three</li><li>four</li></ol></li>
+</ol>
+</div>
+
+<script>
+if (window.layoutTestController)
+ window.layoutTestController.dumpAsText();
+outdent = document.getElementById("outdent");
+window.getSelection().setPosition(outdent, 0);
+document.execCommand("Outdent");
+document.execCommand("Outdent");
+</script>
--- /dev/null
+This tests for a bug where outdenting a list item twice would incorrectly move it above the list it was outdented from. Below you should see 'two' in order with 'one' and 'three' and it should not be in a list.
+
+one
+two
+three
--- /dev/null
+<p>This tests for a bug where outdenting a list item twice would incorrectly move it above the list it was outdented from. Below you should see 'two' in order with 'one' and 'three' and it should not be in a list.</p>
+<div contenteditable="true">
+<ol>
+ <li>one</li>
+ <li id="insert"><ol><li id="outdent">two</li><li>three</li></ol></li>
+</ol>
+</div>
+
+<script>
+if (window.layoutTestController)
+ window.layoutTestController.dumpAsText();
+outdent = document.getElementById("outdent");
+window.getSelection().setPosition(outdent, 0);
+document.execCommand("Outdent");
+document.execCommand("Outdent");
+</script>
--- /dev/null
+This tests for a bug where outdenting a list item twice would incorrectly move it above the list it was outdented from. Below you should see 'two' in order with 'one' and 'three' and it should not be in a list.
+
+one
+two
+three
--- /dev/null
+<p>This tests for a bug where outdenting a list item twice would incorrectly move it above the list it was outdented from. Below you should see 'two' in order with 'one' and 'three' and it should not be in a list.</p>
+<div contenteditable="true">
+<ol>
+ <li>one</li>
+ <li id="insert"><ol><li id="outdent">two</li></ol></li>
+ <li>three</li>
+</ol>
+</div>
+
+<script>
+if (window.layoutTestController)
+ window.layoutTestController.dumpAsText();
+outdent = document.getElementById("outdent");
+window.getSelection().setPosition(outdent, 0);
+document.execCommand("Outdent");
+document.execCommand("Outdent");
+</script>
+2007-12-14 Justin Garcia <justin.garcia@apple.com>
+
+ Reviewed by Darin Adler.
+
+ <rdar://problem/5575101> GoogleDocs: Hang in SplitElementCommand::doApply when outdenting a list item in a particular list
+
+ * editing/CompositeEditCommand.cpp:
+ (WebCore::CompositeEditCommand::splitTreeToNode): Moved here.
+ * editing/CompositeEditCommand.h:
+ * editing/IndentOutdentCommand.cpp: Moved splitTreeToNode.
+ * editing/IndentOutdentCommand.h: Ditto, and removed unimplemented splitTreeTo.
+ * editing/InsertListCommand.cpp:
+ (WebCore::InsertListCommand::doApply): Split ancestors of listChildNode between
+ it and listNode, if they exists, so that moving listChildNode doesn't put it out
+ of order. Added a test case to cover each change.
+ * editing/SplitElementCommand.cpp:
+ (WebCore::SplitElementCommand::doApply): Added an ASSERT to catch code that
+ tries to split a container at a bogus child, and an early return to avoid a
+ hang in that case.
+
2007-12-14 Anders Carlsson <andersca@apple.com>
Reviewed by Darin and Geoff.
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.
+Node* CompositeEditCommand::splitTreeToNode(Node* start, Node* end, bool splitAncestor)
+{
+ 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(new SplitElementCommand(static_cast<Element*>(node->parent()), node));
+ }
+ if (splitAncestor)
+ return splitTreeToNode(end, end->parent());
+ return node;
+}
+
PassRefPtr<Element> createBlockPlaceholderElement(Document* document)
{
ExceptionCode ec = 0;
bool breakOutOfEmptyListItem();
Position positionAvoidingSpecialElementBoundary(const Position&, bool alwaysAvoidAnchors = true);
+
+ Node* splitTreeToNode(Node*, Node*, bool splitAncestor = false);
Vector<RefPtr<EditCommand> > m_commands;
return placeholder.get();
}
-// 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.
-Node* IndentOutdentCommand::splitTreeToNode(Node* start, Node* end, bool splitAncestor)
-{
- 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(new SplitElementCommand(static_cast<Element*>(node->parent()), node));
- }
- if (splitAncestor)
- return splitTreeToNode(end, end->parent());
- return node;
-}
-
static int indexForVisiblePosition(VisiblePosition& visiblePosition)
{
if (visiblePosition.isNull())
virtual void doApply();
virtual EditAction editingAction() const { return m_typeOfAction == Indent ? EditActionIndent : EditActionOutdent; }
private:
- void splitTreeTo(Node* start, Node* stop);
bool modifyRange();
EIndentType m_typeOfAction;
int m_marginInPixels;
void indentRegion();
void outdentRegion();
void outdentParagraph();
- Node* splitTreeToNode(Node*, Node*, bool splitAncestor = false);
Node* prepareBlockquoteLevelForInsertion(VisiblePosition&, Node**);
};
nodeToInsert = createListItemElement(document());
appendNode(placeholder.get(), nodeToInsert.get());
}
+
if (nextListChild && previousListChild) {
- splitElement(static_cast<Element *>(listNode), nextListChild);
+ // 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));
insertNodeBefore(nodeToInsert.get(), listNode);
- } else if (nextListChild)
+ } 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));
insertNodeBefore(nodeToInsert.get(), listNode);
- else
+ } else
insertNodeAfter(nodeToInsert.get(), listNode);
+
VisiblePosition insertionPoint = VisiblePosition(Position(placeholder.get(), 0));
moveParagraphs(start, end, insertionPoint, true);
}
{
ASSERT(m_element2);
ASSERT(m_atChild);
+ ASSERT(m_atChild->parentNode() == m_element2);
ExceptionCode ec = 0;
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);