Commit e387c2e0 authored by tony@chromium.org's avatar tony@chromium.org

2010-02-05 Tony Chang <tony@chromium.org>

        Reviewed by Eric Seidel.

        https://bugs.webkit.org/show_bug.cgi?id=24872
        Add a test to make sure copying from a list and pasting into a list
        keeps the list at the same indention level rather than nesting.

        * editing/pasteboard/paste-list-002-expected.txt: Added.
        * editing/pasteboard/paste-list-002.html: Added.
2010-02-05  Tony Chang  <tony@chromium.org>

        Reviewed by Eric Seidel.

        https://bugs.webkit.org/show_bug.cgi?id=24872
        When pasting a list into another list should not indent another level.
        If the cursor is at the beginning of the line, it should insert the
        list items before the current list item.  If the cursor is at the end
        of the line, it should insert the list items after the current list item.

        This matches Firefox and IE and makes the common activity of reordering a list
        work as expected.

        This also adds a small helper method (isListItem) to htmlediting.h.

        Test: editing/pasteboard/paste-list-002.html

        * editing/ReplaceSelectionCommand.cpp:
        (WebCore::ReplaceSelectionCommand::doApply):
        (WebCore::ReplaceSelectionCommand::insertAsListItems):
        * editing/ReplaceSelectionCommand.h:
        * editing/htmlediting.cpp:
        (WebCore::isListItem):
        (WebCore::appendedSublist):
        * editing/htmlediting.h:

git-svn-id: svn://svn.chromium.org/blink/trunk@54413 bbb929c8-8fbe-4397-9dbb-9b2b20218538
parent 8387aafd
2010-02-05 Tony Chang <tony@chromium.org>
Reviewed by Eric Seidel.
https://bugs.webkit.org/show_bug.cgi?id=24872
Add a test to make sure copying from a list and pasting into a list
keeps the list at the same indention level rather than nesting.
* editing/pasteboard/paste-list-002-expected.txt: Added.
* editing/pasteboard/paste-list-002.html: Added.
2010-02-04 Fumitoshi Ukai <ukai@chromium.org> 2010-02-04 Fumitoshi Ukai <ukai@chromium.org>
Reviewed by Alexey Proskuryakov. Reviewed by Alexey Proskuryakov.
......
Copy/pasting list items in a list. This test should be run with DRT for pasteboard access.
PASS: <li>alpha</li><ul><li>beta</li><li>gamma</li></ul><li>beta</li><li>gamma</li><li id="delta">delta</li><li>beta</li><li>gamma</li><li></li>
alpha
beta
gamma
beta
gamma
delta
beta
gamma
<html>
<script src=../editing.js></script>
<script>
function editingTest() {
// Select "beta" and "gamma".
moveSelectionForwardByLineCommand();
for (i = 0; i < 2; i++)
extendSelectionForwardByLineCommand();
copyCommand();
// Paste with the cursor right before "delta".
moveSelectionForwardByLineCommand();
pasteCommand();
// Verify that the cursor is in the right place (still before delta).
var selection = window.getSelection();
if (selection.baseNode.parentNode != document.getElementById("delta") ||
selection.baseOffset != 0 || !selection.isCollapsed)
throw "Wrong selection position on before paste.";
// Paste with the cursor at the end of "delta".
moveSelectionForwardByWordCommand();
pasteCommand();
// Verify that the cursor is in the right place (new list item after delta).
var selection = window.getSelection();
if (!selection.isCollapsed || selection.baseNode.value != "")
throw "Wrong selection position on after paste.";
}
</script>
<body>
<div contentEditable="true">
<p>Copy/pasting list items in a list. This test should be run with DRT for pasteboard access.</p>
<p id="results">FAIL</p>
<ul id="test">
<li>alpha</li>
<li>beta</li>
<li>gamma</li>
<li id="delta">delta</li>
</ul>
</div>
<script>
if (window.layoutTestController)
layoutTestController.dumpAsText();
var elem = document.getElementById("test");
var selection = window.getSelection();
selection.setPosition(elem, 0);
editingTest();
// Rerun the test but have the source list be indented.
document.getElementById("test").innerHTML = "<li>alpha</li><ul><li>beta</li><li>gamma</li></ul><li id='delta'>delta</li>";
selection.setPosition(elem, 0);
editingTest();
document.getElementById("results").innerText = "PASS: " + document.getElementById("test").innerHTML;
</script>
</body>
</html>
2010-02-05 Tony Chang <tony@chromium.org>
Reviewed by Eric Seidel.
https://bugs.webkit.org/show_bug.cgi?id=24872
When pasting a list into another list should not indent another level.
If the cursor is at the beginning of the line, it should insert the
list items before the current list item. If the cursor is at the end
of the line, it should insert the list items after the current list item.
This matches Firefox and IE and makes the common activity of reordering a list
work as expected.
This also adds a small helper method (isListItem) to htmlediting.h.
Test: editing/pasteboard/paste-list-002.html
* editing/ReplaceSelectionCommand.cpp:
(WebCore::ReplaceSelectionCommand::doApply):
(WebCore::ReplaceSelectionCommand::insertAsListItems):
* editing/ReplaceSelectionCommand.h:
* editing/htmlediting.cpp:
(WebCore::isListItem):
(WebCore::appendedSublist):
* editing/htmlediting.h:
2010-02-04 Mark Rowe <mrowe@apple.com> 2010-02-04 Mark Rowe <mrowe@apple.com>
Reviewed by Timothy Hatcher. Reviewed by Timothy Hatcher.
......
...@@ -752,9 +752,7 @@ void ReplaceSelectionCommand::doApply() ...@@ -752,9 +752,7 @@ void ReplaceSelectionCommand::doApply()
bool startIsInsideMailBlockquote = nearestMailBlockquote(insertionPos.node()); bool startIsInsideMailBlockquote = nearestMailBlockquote(insertionPos.node());
if ((selectionStartWasStartOfParagraph && selectionEndWasEndOfParagraph && !startIsInsideMailBlockquote) || if ((selectionStartWasStartOfParagraph && selectionEndWasEndOfParagraph && !startIsInsideMailBlockquote) ||
startBlock == currentRoot || startBlock == currentRoot || isListItem(startBlock) || selectionIsPlainText)
(startBlock && startBlock->renderer() && startBlock->renderer()->isListItem()) ||
selectionIsPlainText)
m_preventNesting = false; m_preventNesting = false;
if (selection.isRange()) { if (selection.isRange()) {
...@@ -871,7 +869,12 @@ void ReplaceSelectionCommand::doApply() ...@@ -871,7 +869,12 @@ void ReplaceSelectionCommand::doApply()
RefPtr<Node> node = refNode->nextSibling(); RefPtr<Node> node = refNode->nextSibling();
fragment.removeNode(refNode); fragment.removeNode(refNode);
insertNodeAtAndUpdateNodesInserted(refNode, insertionPos);
Node* blockStart = enclosingBlock(insertionPos.node());
if (isListElement(refNode.get()) && blockStart->renderer()->isListItem())
refNode = insertAsListItems(refNode, blockStart, insertionPos);
else
insertNodeAtAndUpdateNodesInserted(refNode, insertionPos);
// Mutation events (bug 22634) may have already removed the inserted content // Mutation events (bug 22634) may have already removed the inserted content
if (!refNode->inDocument()) if (!refNode->inDocument())
...@@ -960,9 +963,15 @@ void ReplaceSelectionCommand::doApply() ...@@ -960,9 +963,15 @@ void ReplaceSelectionCommand::doApply()
if (selectionEndWasEndOfParagraph || !isEndOfParagraph(endOfInsertedContent) || next.isNull()) { if (selectionEndWasEndOfParagraph || !isEndOfParagraph(endOfInsertedContent) || next.isNull()) {
if (!isStartOfParagraph(endOfInsertedContent)) { if (!isStartOfParagraph(endOfInsertedContent)) {
setEndingSelection(endOfInsertedContent); setEndingSelection(endOfInsertedContent);
// Use a default paragraph element (a plain div) for the empty paragraph, using the last paragraph Node* enclosingNode = enclosingBlock(endOfInsertedContent.deepEquivalent().node());
// block's style seems to annoy users. if (isListItem(enclosingNode)) {
insertParagraphSeparator(true); RefPtr<Node> newListItem = createListItemElement(document());
insertNodeAfter(newListItem, enclosingNode);
setEndingSelection(VisiblePosition(Position(newListItem, 0)));
} else
// 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. // Select up to the paragraph separator that was added.
lastPositionToSelect = endingSelection().visibleStart().deepEquivalent(); lastPositionToSelect = endingSelection().visibleStart().deepEquivalent();
...@@ -1111,6 +1120,39 @@ void ReplaceSelectionCommand::insertNodeBeforeAndUpdateNodesInserted(PassRefPtr< ...@@ -1111,6 +1120,39 @@ void ReplaceSelectionCommand::insertNodeBeforeAndUpdateNodesInserted(PassRefPtr<
updateNodesInserted(nodeToUpdate); updateNodesInserted(nodeToUpdate);
} }
// If the user is inserting a list into an existing list, instead of nesting the list,
// we put the list items into the existing list.
Node* ReplaceSelectionCommand::insertAsListItems(PassRefPtr<Node> listElement, Node* insertionNode, const Position& p)
{
while (listElement->hasChildNodes() && isListElement(listElement->firstChild()) && listElement->childNodeCount() == 1)
listElement = listElement->firstChild();
bool isStart = isStartOfParagraph(p);
bool isEnd = isEndOfParagraph(p);
Node* lastNode = insertionNode;
while (RefPtr<Node> listItem = listElement->firstChild()) {
ExceptionCode ec = 0;
listElement->removeChild(listItem.get(), ec);
ASSERT(!ec);
if (isStart)
insertNodeBefore(listItem, lastNode);
else if (isEnd) {
insertNodeAfter(listItem, lastNode);
lastNode = listItem.get();
} else {
// FIXME: If we're in the middle of a list item, we should split it into two separate
// list items and insert these nodes between them. For now, just append the nodes.
insertNodeAfter(listItem, lastNode);
lastNode = listItem.get();
}
}
if (isStart)
lastNode = lastNode->previousSibling();
updateNodesInserted(lastNode);
return lastNode;
}
void ReplaceSelectionCommand::updateNodesInserted(Node *node) void ReplaceSelectionCommand::updateNodesInserted(Node *node)
{ {
if (!node) if (!node)
......
...@@ -54,6 +54,7 @@ private: ...@@ -54,6 +54,7 @@ private:
void insertNodeAfterAndUpdateNodesInserted(PassRefPtr<Node> insertChild, Node* refChild); void insertNodeAfterAndUpdateNodesInserted(PassRefPtr<Node> insertChild, Node* refChild);
void insertNodeAtAndUpdateNodesInserted(PassRefPtr<Node>, const Position&); void insertNodeAtAndUpdateNodesInserted(PassRefPtr<Node>, const Position&);
void insertNodeBeforeAndUpdateNodesInserted(PassRefPtr<Node> insertChild, Node* refChild); void insertNodeBeforeAndUpdateNodesInserted(PassRefPtr<Node> insertChild, Node* refChild);
Node* insertAsListItems(PassRefPtr<Node>, Node* insertionNode, const Position&);
void updateNodesInserted(Node*); void updateNodesInserted(Node*);
bool shouldRemoveEndBR(Node*, const VisiblePosition&); bool shouldRemoveEndBR(Node*, const VisiblePosition&);
......
...@@ -658,6 +658,11 @@ bool isListElement(Node *n) ...@@ -658,6 +658,11 @@ bool isListElement(Node *n)
return (n && (n->hasTagName(ulTag) || n->hasTagName(olTag) || n->hasTagName(dlTag))); return (n && (n->hasTagName(ulTag) || n->hasTagName(olTag) || n->hasTagName(dlTag)));
} }
bool isListItem(Node *n)
{
return n && n->renderer() && n->renderer()->isListItem();
}
Node* enclosingNodeWithTag(const Position& p, const QualifiedName& tagName) Node* enclosingNodeWithTag(const Position& p, const QualifiedName& tagName)
{ {
if (p.isNull()) if (p.isNull())
...@@ -779,7 +784,7 @@ static Node* appendedSublist(Node* listItem) ...@@ -779,7 +784,7 @@ static Node* appendedSublist(Node* listItem)
for (Node* n = listItem->nextSibling(); n; n = n->nextSibling()) { for (Node* n = listItem->nextSibling(); n; n = n->nextSibling()) {
if (isListElement(n)) if (isListElement(n))
return static_cast<HTMLElement*>(n); return static_cast<HTMLElement*>(n);
if (n->renderer() && n->renderer()->isListItem()) if (isListItem(listItem))
return 0; return 0;
} }
......
...@@ -90,6 +90,7 @@ bool isTableCell(const Node*); ...@@ -90,6 +90,7 @@ bool isTableCell(const Node*);
bool isEmptyTableCell(const Node*); bool isEmptyTableCell(const Node*);
bool isTableStructureNode(const Node*); bool isTableStructureNode(const Node*);
bool isListElement(Node*); bool isListElement(Node*);
bool isListItem(Node*);
bool isNodeRendered(const Node*); bool isNodeRendered(const Node*);
bool isNodeVisiblyContainedWithin(Node*, const Range*); bool isNodeVisiblyContainedWithin(Node*, const Range*);
bool isRenderedAsNonInlineTableImageOrHR(const Node*); bool isRenderedAsNonInlineTableImageOrHR(const Node*);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment