LayoutTests/ChangeLog

 12011-06-07 Annie Sullivan <sullivan@chromium.org>
 2
 3 Reviewed by NOBODY (OOPS!).
 4
 5 REGRESSION: Hitting enter in the middle of this span causes the cursor to go to the end of the span
 6 https://bugs.webkit.org/show_bug.cgi?id=61594
 7
 8 Added new layout tests for this problem, one in the middle of a text node, and one at the end.
 9
 10 * editing/inserting/return-key-before-br-in-span-expected.txt: Added.
 11 * editing/inserting/return-key-before-br-in-span.html: Added.
 12 * editing/inserting/return-key-middle-of-span-expected.txt: Added.
 13 * editing/inserting/return-key-middle-of-span.html: Added.
 14
1152011-06-07 Young Han Lee <joybro@company100.net>
216
317 Reviewed by Kenneth Rohde Christiansen.

LayoutTests/editing/inserting/return-key-before-br-in-span-expected.txt

 1This sets the selection to the end of the first line, and hits the enter key.
 2Expected behavior: a div is created around the second line, and the cursor is placed at the start of the second line. See bug 61594.
 3| <span>
 4| id="wrapper"
 5| "First line"
 6| <div>
 7| <span>
 8| <#selection-caret>
 9| <br>
 10| "Second line"

LayoutTests/editing/inserting/return-key-before-br-in-span.html

 1<!DOCTYPE html>
 2<html>
 3<body>
 4<div contenteditable id="root"><span id="wrapper">First line<br>Second line</span></div>
 5<script src="../../resources/dump-as-markup.js"></script>
 6<script>
 7Markup.description("This sets the selection to the end of the first line, and hits the enter key.\n"
 8 + "Expected behavior: a div is created around the second line, and the cursor is placed at the "
 9 + "start of the second line. See bug 61594.");
 10var sel = window.getSelection();
 11sel.setPosition(document.getElementById("wrapper"), 1);
 12eventSender.keyDown('\n');
 13Markup.dump(root);
 14
 15</script>
 16</html>

LayoutTests/editing/inserting/return-key-middle-of-span-expected.txt

 1This sets the selection to the middle of the first line, and hits the enter key.
 2Expected behavior: the text node is split at the cursor. The span is cloned, and everything that was split out is placed inside the clone in a new div which is a child of the root. See bug 61594.
 3| <span>
 4| id="wrapper"
 5| "First"
 6| <div>
 7| <span>
 8| "<#selection-caret> line"
 9| <br>
 10| "Second line"

LayoutTests/editing/inserting/return-key-middle-of-span.html

 1<!DOCTYPE html>
 2<html>
 3<body>
 4<div contenteditable id="root"><span id="wrapper">First line<br>Second line</span></div>
 5<script src="../../resources/dump-as-markup.js"></script>
 6<script>
 7Markup.description("This sets the selection to the middle of the first line, and hits the enter key.\n"
 8 + "Expected behavior: the text node is split at the cursor. The span is cloned, and everything "
 9 + "that was split out is placed inside the clone in a new div which is a child of the root. See bug 61594.");
 10var sel = window.getSelection();
 11sel.setPosition(document.getElementById("wrapper").firstChild, 5);
 12eventSender.keyDown('\n');
 13Markup.dump(root);
 14</script>
 15</html>

Source/WebCore/ChangeLog

 12011-06-07 Annie Sullivan <sullivan@chromium.org>
 2
 3 Reviewed by NOBODY (OOPS!).
 4
 5 REGRESSION: Hitting enter in the middle of this span causes the cursor to go to the end of the span
 6 https://bugs.webkit.org/show_bug.cgi?id=61594
 7
 8 Changed splitTreeToNode to a method splitTree() which takes a Position, and updated callers.
 9
 10 Tests: editing/inserting/return-key-before-br-in-span.html
 11 editing/inserting/return-key-middle-of-span.html
 12
 13 * editing/CompositeEditCommand.cpp:
 14 (WebCore::CompositeEditCommand::splitTree):
 15 * editing/CompositeEditCommand.h:
 16 * editing/FormatBlockCommand.cpp:
 17 (WebCore::FormatBlockCommand::formatRange):
 18 * editing/IndentOutdentCommand.cpp:
 19 (WebCore::IndentOutdentCommand::indentIntoBlockquote):
 20 (WebCore::IndentOutdentCommand::outdentParagraph):
 21 * editing/InsertListCommand.cpp:
 22 (WebCore::InsertListCommand::unlistifyParagraph):
 23 * editing/InsertParagraphSeparatorCommand.cpp:
 24 (WebCore::InsertParagraphSeparatorCommand::doApply):
 25 * editing/ReplaceSelectionCommand.cpp:
 26 (WebCore::ReplaceSelectionCommand::doApply):
 27 (WebCore::ReplaceSelectionCommand::insertAsListItems):
 28 * editing/htmlediting.cpp:
 29
1302011-06-07 Young Han Lee <joybro@company100.net>
231
332 Reviewed by Kenneth Rohde Christiansen.

Source/WebCore/editing/CompositeEditCommand.cpp

4444#include "InsertParagraphSeparatorCommand.h"
4545#include "InsertTextCommand.h"
4646#include "MergeIdenticalElementsCommand.h"
 47#include "NodeList.h"
4748#include "Range.h"
4849#include "RemoveCSSPropertyCommand.h"
4950#include "RemoveNodeCommand.h"

@@Position CompositeEditCommand::positionAvoidingSpecialElementBoundary(const Posi
12281229 return result;
12291230}
12301231
1231 // Splits the tree parent by parent until we reach the specified ancestor. We use VisiblePositions
1232 // to determine if the split is necessary. Returns the last split node.
1233 PassRefPtr<Node> CompositeEditCommand::splitTreeToNode(Node* start, Node* end, bool shouldSplitAncestor)
 1232PassRefPtr<Node> CompositeEditCommand::splitTree(const Position& start, Node* end, AncestorSplitPolicy splitPolicy)
12341233{
1235  ASSERT(start);
12361234 ASSERT(end);
1237  ASSERT(start != end);
 1235 ASSERT(start.containerNode() != end);
12381236
1239  RefPtr<Node> node;
1240  if (shouldSplitAncestor && end->parentNode())
 1237 if (splitPolicy == Split && end->parentNode())
12411238 end = end->parentNode();
1242 
12431239 RefPtr<Node> endNode = end;
1244  for (node = start; node && node->parentNode() != endNode; node = node->parentNode()) {
 1240
 1241 Node* startNode = start.containerNode();
 1242 // For text nodes, handle the case where the offset is in the middle of the node or at the end,
 1243 // moving the start node to the beginning of the position, splitting the node if necessary.
 1244 if (startNode->isTextNode()) {
 1245 // FIXME: startNode can be null if traverseNextNode returns null. What should it be set to in that case?
 1246 if (start.offsetInContainerNode() == caretMaxOffset(start.containerNode()))
 1247 startNode = startNode->traverseNextNode(end);
 1248 else if (start.offsetInContainerNode() > caretMinOffset(start.containerNode()))
 1249 splitTextNode(static_cast<Text*>(start.containerNode()), start.offsetInContainerNode());
 1250 }
 1251
 1252 RefPtr<Node> node;
 1253 for (node = startNode; node && node->parentNode() != endNode; node = node->parentNode()) {
12451254 if (!node->parentNode()->isElementNode())
12461255 break;
12471256 // Do not split a node when doing so introduces an empty node.

Source/WebCore/editing/CompositeEditCommand.h

@@class HTMLElement;
3737class StyledElement;
3838class Text;
3939
 40enum AncestorSplitPolicy { DoNotSplit, Split };
 41
4042class CompositeEditCommand : public EditCommand {
4143public:
4244 virtual ~CompositeEditCommand();

@@protected:
115117 bool breakOutOfEmptyMailBlockquotedParagraph();
116118
117119 Position positionAvoidingSpecialElementBoundary(const Position&);
118 
119  PassRefPtr<Node> splitTreeToNode(Node*, Node*, bool splitAncestor = false);
 120
 121 PassRefPtr<Node> splitTree(const Position&, Node*, AncestorSplitPolicy splitPolicy = DoNotSplit);
120122
121123 Vector<RefPtr<EditCommand> > m_commands;
122124

Source/WebCore/editing/FormatBlockCommand.cpp

@@void FormatBlockCommand::formatSelection(const VisiblePosition& startOfSelection
6161void FormatBlockCommand::formatRange(const Position& start, const Position& end, const Position& endOfSelection, RefPtr<Element>& blockNode)
6262{
6363 Node* nodeToSplitTo = enclosingBlockToSplitTreeTo(start.deprecatedNode());
64  RefPtr<Node> outerBlock = (start.deprecatedNode() == nodeToSplitTo) ? start.deprecatedNode() : splitTreeToNode(start.deprecatedNode(), nodeToSplitTo);
 64 RefPtr<Node> outerBlock = (start.deprecatedNode() == nodeToSplitTo) ? start.deprecatedNode() : splitTree(firstPositionInNode(start.deprecatedNode()), nodeToSplitTo);
6565 RefPtr<Node> nodeAfterInsertionPosition = outerBlock;
6666
6767 RefPtr<Range> range = Range::create(document(), start, endOfSelection);

Source/WebCore/editing/IndentOutdentCommand.cpp

@@void IndentOutdentCommand::indentIntoBlockquote(const Position& start, const Pos
103103 if (!nodeToSplitTo)
104104 return;
105105
106  RefPtr<Node> outerBlock = (start.deprecatedNode() == nodeToSplitTo) ? start.deprecatedNode() : splitTreeToNode(start.deprecatedNode(), nodeToSplitTo);
 106 RefPtr<Node> outerBlock = (start.deprecatedNode() == nodeToSplitTo) ? start.deprecatedNode() : splitTree(firstPositionInNode(start.deprecatedNode()), nodeToSplitTo);
107107
108108 if (!targetBlockquote) {
109109 // Create a new blockquote and insert it as a child of the root editable element. We accomplish

@@void IndentOutdentCommand::outdentParagraph()
170170 Node* enclosingBlockFlow = enclosingBlock(visibleStartOfParagraph.deepEquivalent().deprecatedNode());
171171 RefPtr<Node> splitBlockquoteNode = enclosingNode;
172172 if (enclosingBlockFlow != enclosingNode)
173  splitBlockquoteNode = splitTreeToNode(enclosingBlockFlow, enclosingNode, true);
 173 splitBlockquoteNode = splitTree(firstPositionInNode(enclosingBlockFlow), enclosingNode, Split);
174174 else {
175175 // We split the blockquote at where we start outdenting.
176176 splitElement(static_cast<Element*>(enclosingNode), visibleStartOfParagraph.deepEquivalent().deprecatedNode());

Source/WebCore/editing/InsertListCommand.cpp

@@void InsertListCommand::unlistifyParagraph(const VisiblePosition& originalStart,
289289 // FIXME: We appear to split at nextListChild as opposed to listChildNode so that when we remove
290290 // listChildNode below in moveParagraphs, previousListChild will be removed along with it if it is
291291 // unrendered. But we ought to remove nextListChild too, if it is unrendered.
292  splitElement(listNode, splitTreeToNode(nextListChild, listNode));
 292 splitElement(listNode, splitTree(firstPositionInNode(nextListChild), listNode));
293293 insertNodeBefore(nodeToInsert, listNode);
294294 } else if (nextListChild || listChildNode->parentNode() != listNode) {
295295 // Just because listChildNode has no previousListChild doesn't mean there isn't any content

@@void InsertListCommand::unlistifyParagraph(const VisiblePosition& originalStart,
297297 // between it and listNode. So, we split up to listNode before inserting the placeholder
298298 // where we're about to move listChildNode to.
299299 if (listChildNode->parentNode() != listNode)
300  splitElement(listNode, splitTreeToNode(listChildNode, listNode).get());
 300 splitElement(listNode, splitTree(firstPositionInNode(listChildNode), listNode).get());
301301 insertNodeBefore(nodeToInsert, listNode);
302302 } else
303303 insertNodeAfter(nodeToInsert, listNode);

Source/WebCore/editing/InsertParagraphSeparatorCommand.cpp

@@void InsertParagraphSeparatorCommand::doApply()
347347 if (insertionPosition.containerNode() == startBlock)
348348 n = insertionPosition.computeNodeAfterPosition();
349349 else {
350  splitTreeToNode(insertionPosition.containerNode(), startBlock);
 350 splitTree(insertionPosition, startBlock);
351351
352352 for (n = startBlock->firstChild(); n; n = n->nextSibling()) {
353353 if (comparePositions(VisiblePosition(insertionPosition), positionBeforeNode(n)) <= 0)

Source/WebCore/editing/ReplaceSelectionCommand.cpp

@@void ReplaceSelectionCommand::doApply()
946946 // FIXME: isInlineNodeWithStyle does not check editability.
947947 if (RefPtr<Node> nodeToSplitTo = highestEnclosingNodeOfType(insertionPos, isInlineNodeWithStyle)) {
948948 if (insertionPos.containerNode() != nodeToSplitTo) {
949  nodeToSplitTo = splitTreeToNode(insertionPos.anchorNode(), nodeToSplitTo.get(), true).get();
 949 nodeToSplitTo = splitTree(insertionPos, nodeToSplitTo.get(), Split).get();
950950 insertionPos = positionInParentBeforeNode(nodeToSplitTo.get());
951951 }
952952 }

@@Node* ReplaceSelectionCommand::insertAsListItems(PassRefPtr<Node> listElement, N
12481248 // If we're in the middle of a list item, we should split it into two separate
12491249 // list items and insert these nodes between them.
12501250 if (isMiddle) {
1251  int textNodeOffset = insertPos.offsetInContainerNode();
1252  if (insertPos.deprecatedNode()->isTextNode() && textNodeOffset > 0)
1253  splitTextNode(static_cast<Text*>(insertPos.deprecatedNode()), textNodeOffset);
1254  splitTreeToNode(insertPos.deprecatedNode(), lastNode, true);
 1251 splitTree(insertPos, lastNode, Split);
12551252 }
12561253
12571254 while (RefPtr<Node> listItem = listElement->firstChild()) {

Source/WebCore/editing/htmlediting.cpp

@@Element* editableRootForPosition(const Position& p)
187187
188188// Finds the enclosing element until which the tree can be split.
189189// When a user hits ENTER, he/she won't expect this element to be split into two.
190 // You may pass it as the second argument of splitTreeToNode.
 190// You may pass it as the second argument of splitTree.
191191Element* unsplittableElementForPosition(const Position& p)
192192{
193193 // Since enclosingNodeOfType won't search beyond the highest root editable node,