WebCore:
authorjustin.garcia@apple.com <justin.garcia@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 21 Nov 2008 01:37:00 +0000 (01:37 +0000)
committerjustin.garcia@apple.com <justin.garcia@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 21 Nov 2008 01:37:00 +0000 (01:37 +0000)
2008-11-20  Justin Garcia  <justin.garcia@apple.com>

        Reviewed by Darin Adler.

        <rdar://problem/2610675> Blank line that is quoted can't be deleted

        If the caret is in an empty quoted paragraph, and either there is nothing before that
        paragraph, or what is before is unquoted, and the user presses delete, unquote that
        paragraph.

        * editing/CompositeEditCommand.cpp:
        (WebCore::CompositeEditCommand::breakOutOfEmptyMailBlockquotedParagraph): Added.
        Removes the line break that holds open an empty paragraph and then attempts to
        prune the blockquote(s) that held that line break in case they have been emptied
        out.  Places a line break to create in empty unquoted paragraph in place of the
        quoted one that was removed.
        * editing/CompositeEditCommand.h:
        * editing/TypingCommand.cpp:
        (WebCore::TypingCommand::deleteKeyPressed): Call breakOutOfEmptyBlockquotedParagraph.
        * editing/htmlediting.cpp:
        (WebCore::highestEnclosingNodeOfType): Added.
        * editing/htmlediting.h:

LayoutTests:

2008-11-20  Justin Garcia  <justin.garcia@apple.com>

        Reviewed by Darin Adler.

        <rdar://problem/2610675> Blank line that is quoted can't be deleted

        * editing/deleting/2610675-1-expected.txt: Added.
        * editing/deleting/2610675-1.html: Added.
        * editing/deleting/2610675-2-expected.txt: Added.
        * editing/deleting/2610675-2.html: Added.
        * editing/deleting/2610675-3-expected.txt: Added.
        * editing/deleting/2610675-3.html: Added.

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

13 files changed:
LayoutTests/ChangeLog
LayoutTests/editing/deleting/2610675-1-expected.txt [new file with mode: 0644]
LayoutTests/editing/deleting/2610675-1.html [new file with mode: 0644]
LayoutTests/editing/deleting/2610675-2-expected.txt [new file with mode: 0644]
LayoutTests/editing/deleting/2610675-2.html [new file with mode: 0644]
LayoutTests/editing/deleting/2610675-3-expected.txt [new file with mode: 0644]
LayoutTests/editing/deleting/2610675-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 9fe3183..e6541a4 100644 (file)
@@ -1,3 +1,16 @@
+2008-11-20  Justin Garcia  <justin.garcia@apple.com>
+
+        Reviewed by Darin Adler.
+        
+        <rdar://problem/2610675> Blank line that is quoted can't be deleted
+
+        * editing/deleting/2610675-1-expected.txt: Added.
+        * editing/deleting/2610675-1.html: Added.
+        * editing/deleting/2610675-2-expected.txt: Added.
+        * editing/deleting/2610675-2.html: Added.
+        * editing/deleting/2610675-3-expected.txt: Added.
+        * editing/deleting/2610675-3.html: Added.
+
 2008-11-20  Dean Jackson  <dino@apple.com>
 
         Reviewed by Antti Koivisto
diff --git a/LayoutTests/editing/deleting/2610675-1-expected.txt b/LayoutTests/editing/deleting/2610675-1-expected.txt
new file mode 100644 (file)
index 0000000..be7bd52
--- /dev/null
@@ -0,0 +1,7 @@
+This tests to see that if the caret is in an empty quoted paragraph, that paragraph is in content that preserve newlines, and there's no quoted content before that paragraph, then pressing delete removes that paragraph's quoting. Below you should see the attribution line, an empty paragraph, an unquoted paragraph, and then a quoted paragraph with "quoted" in it.
+
+<div><br></div>
+<div>On Tuesday, Justin wrote:</div>
+<div><br></div>
+not&nbsp;quoted<br><blockquote type="cite" id="blockquote"><div style="white-space: pre;">quoted</div></blockquote>
+
diff --git a/LayoutTests/editing/deleting/2610675-1.html b/LayoutTests/editing/deleting/2610675-1.html
new file mode 100644 (file)
index 0000000..e1eef0f
--- /dev/null
@@ -0,0 +1,34 @@
+<html>
+<head>
+<style>
+blockquote[type="cite"] {
+    color: blue;
+    margin: 0px;
+    padding-left: 4px;
+    border-left: 2px solid blue;
+}
+</style>
+</head>
+<body>
+<div id="description">This tests to see that if the caret is in an empty quoted paragraph, that paragraph is in content that preserve newlines, and there's no quoted content before that paragraph, then pressing delete removes that paragraph's quoting.  Below you should see the attribution line, an empty paragraph, an unquoted paragraph, and then a quoted paragraph with "quoted" in it.</div>
+<div id="edit" contentEditable="true">
+<div><br></div>
+<div>On Tuesday, Justin wrote:</div>
+<div><br></div>
+<blockquote type="cite" id="blockquote"><div style="white-space: pre;">
+quoted</div></blockquote>
+</div>
+
+<script>
+if (window.layoutTestController)
+    window.layoutTestController.dumpAsText();
+blockquote = document.getElementById("blockquote");
+window.getSelection().setPosition(blockquote, 0);
+document.execCommand("Delete");
+document.execCommand("InsertText", false, "not quoted");
+if (window.layoutTestController)
+    document.body.innerText = document.getElementById("description").innerText + "\n" + document.getElementById("edit").innerHTML;
+</script>
+
+</body>
+</html>
diff --git a/LayoutTests/editing/deleting/2610675-2-expected.txt b/LayoutTests/editing/deleting/2610675-2-expected.txt
new file mode 100644 (file)
index 0000000..d8b4c37
--- /dev/null
@@ -0,0 +1,7 @@
+This tests to see that if the caret is in an empty quoted paragraph, and there's no quoted content before that paragraph, then pressing delete removes that paragraph's quoting. Below you should see the attribution line, an empty paragraph, an unquoted paragraph, and then two quoted paragraphs with "quoted" in them.
+
+<div><br></div>
+<div>On Tuesday, Justin wrote:</div>
+<div><br></div>
+not&nbsp;quoted<br><blockquote type="cite" id="blockquote">quoted<br>quoted</blockquote>
+
diff --git a/LayoutTests/editing/deleting/2610675-2.html b/LayoutTests/editing/deleting/2610675-2.html
new file mode 100644 (file)
index 0000000..4ae0dc9
--- /dev/null
@@ -0,0 +1,33 @@
+<html>
+<head>
+<style>
+blockquote[type="cite"] {
+    color: blue;
+    margin: 0px;
+    padding-left: 4px;
+    border-left: 2px solid blue;
+}
+</style>
+</head>
+<body>
+<div id="description">This tests to see that if the caret is in an empty quoted paragraph, and there's no quoted content before that paragraph, then pressing delete removes that paragraph's quoting.  Below you should see the attribution line, an empty paragraph, an unquoted paragraph, and then two quoted paragraphs with "quoted" in them.</div>
+<div id="edit" contentEditable="true">
+<div><br></div>
+<div>On Tuesday, Justin wrote:</div>
+<div><br></div>
+<blockquote type="cite" id="blockquote"><br>quoted<br>quoted</blockquote>
+</div>
+
+<script>
+if (window.layoutTestController)
+    window.layoutTestController.dumpAsText();
+blockquote = document.getElementById("blockquote");
+window.getSelection().setPosition(blockquote, 0);
+document.execCommand("Delete");
+document.execCommand("InsertText", false, "not quoted");
+if (window.layoutTestController)
+    document.body.innerText = document.getElementById("description").innerText + "\n" + document.getElementById("edit").innerHTML;
+</script>
+
+</body>
+</html>
diff --git a/LayoutTests/editing/deleting/2610675-3-expected.txt b/LayoutTests/editing/deleting/2610675-3-expected.txt
new file mode 100644 (file)
index 0000000..eca5f3f
--- /dev/null
@@ -0,0 +1,2 @@
+This tests to see that if the caret is in an empty quoted paragraph, and there's no quoted content before that paragraph, then pressing delete removes that paragraph's quoting. Below you should see just an unquoted paragraph.
+not&nbsp;quoted
diff --git a/LayoutTests/editing/deleting/2610675-3.html b/LayoutTests/editing/deleting/2610675-3.html
new file mode 100644 (file)
index 0000000..8274e21
--- /dev/null
@@ -0,0 +1,28 @@
+<html>
+<head>
+<style>
+blockquote[type="cite"] {
+    color: blue;
+    margin: 0px;
+    padding-left: 4px;
+    border-left: 2px solid blue;
+}
+</style>
+</head>
+<body>
+<div id="description">This tests to see that if the caret is in an empty quoted paragraph, and there's no quoted content before that paragraph, then pressing delete removes that paragraph's quoting.  Below you should see just an unquoted paragraph.</div>
+<div id="edit" contentEditable="true"><blockquote type="cite" id="blockquote"><br></blockquote></div>
+
+<script>
+if (window.layoutTestController)
+    window.layoutTestController.dumpAsText();
+blockquote = document.getElementById("blockquote");
+window.getSelection().setPosition(blockquote, 0);
+document.execCommand("Delete");
+document.execCommand("InsertText", false, "not quoted");
+if (window.layoutTestController)
+    document.body.innerText = document.getElementById("description").innerText + "\n" + document.getElementById("edit").innerHTML;
+</script>
+
+</body>
+</html>
index 0fd471b..8cdca77 100644 (file)
@@ -1,3 +1,26 @@
+2008-11-20  Justin Garcia  <justin.garcia@apple.com>
+
+        Reviewed by Darin Adler.
+
+        <rdar://problem/2610675> Blank line that is quoted can't be deleted
+        
+        If the caret is in an empty quoted paragraph, and either there is nothing before that
+        paragraph, or what is before is unquoted, and the user presses delete, unquote that
+        paragraph.
+
+        * editing/CompositeEditCommand.cpp:
+        (WebCore::CompositeEditCommand::breakOutOfEmptyMailBlockquotedParagraph): Added.
+        Removes the line break that holds open an empty paragraph and then attempts to
+        prune the blockquote(s) that held that line break in case they have been emptied
+        out.  Places a line break to create in empty unquoted paragraph in place of the
+        quoted one that was removed.
+        * editing/CompositeEditCommand.h:
+        * editing/TypingCommand.cpp:
+        (WebCore::TypingCommand::deleteKeyPressed): Call breakOutOfEmptyBlockquotedParagraph.
+        * editing/htmlediting.cpp:
+        (WebCore::highestEnclosingNodeOfType): Added.
+        * editing/htmlediting.h:
+
 2008-11-20  Dean Jackson  <dino@apple.com>
 
         Reviewed by Antti Koivisto
index 7e08f2c..3a8f623 100644 (file)
@@ -897,6 +897,62 @@ bool CompositeEditCommand::breakOutOfEmptyListItem()
     return true;
 }
 
+// If the caret is in an empty quoted paragraph, and either there is nothing before that
+// paragraph, or what is before is unquoted, and the user presses delete, unquote that paragraph.
+bool CompositeEditCommand::breakOutOfEmptyMailBlockquotedParagraph()
+{
+    if (!endingSelection().isCaret())
+        return false;
+        
+    VisiblePosition caret(endingSelection().visibleStart());
+    Node* highestBlockquote = highestEnclosingNodeOfType(caret.deepEquivalent(), &isMailBlockquote);
+    if (!highestBlockquote)
+        return false;
+        
+    if (!isStartOfParagraph(caret) || !isEndOfParagraph(caret))
+        return false;
+    
+    VisiblePosition previous(caret.previous(true));
+    // Only move forward if there's nothing before the caret, or if there's unquoted content before it.
+    if (enclosingNodeOfType(previous.deepEquivalent(), &isMailBlockquote))
+        return false;
+    
+    RefPtr<Node> br = createBreakElement(document());
+    // We want to replace this quoted paragraph with an unquoted one, so insert a br
+    // to hold the caret before the highest blockquote.
+    insertNodeBefore(br.get(), highestBlockquote);
+    VisiblePosition atBR(Position(br.get(), 0));
+    // If the br we inserted collapsed, for example foo<br><blockquote>...</blockquote>, insert 
+    // a second one.
+    if (!isStartOfParagraph(atBR))
+        insertNodeBefore(createBreakElement(document()).get(), br.get());
+    setEndingSelection(Selection(atBR));
+    
+    // If this is an empty paragraph there must be a line break here.
+    if (!lineBreakExistsAtPosition(caret))
+        return false;
+    
+    Position caretPos(caret.deepEquivalent());
+    // A line break is either a br or a preserved newline.
+    ASSERT(caretPos.node()->hasTagName(brTag) || caretPos.node()->isTextNode() && caretPos.node()->renderer()->style()->preserveNewline());
+    
+    if (caretPos.node()->hasTagName(brTag)) {
+        Position beforeBR(positionBeforeNode(caretPos.node()));
+        removeNode(caretPos.node());
+        prune(beforeBR.node());
+    } else {
+        ASSERT(caretPos.offset() == 0);
+        Text* textNode = static_cast<Text*>(caretPos.node());
+        Node* parentNode = textNode->parentNode();
+        // The preserved newline must be the first thing in the node, since otherwise the previous
+        // paragraph would be quoted, and we verified that it wasn't above.
+        deleteTextFromNode(textNode, 0, 1);
+        prune(parentNode);
+    }
+    
+    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
index 07f2323..0ee0b63 100644 (file)
@@ -101,6 +101,7 @@ protected:
     void moveParagraphs(const VisiblePosition&, const VisiblePosition&, const VisiblePosition&, bool preserveSelection = false, bool preserveStyle = true);
     
     bool breakOutOfEmptyListItem();
+    bool breakOutOfEmptyMailBlockquotedParagraph();
     
     Position positionAvoidingSpecialElementBoundary(const Position&, bool alwaysAvoidAnchors = true);
     
index 535aece..03b5263 100644 (file)
@@ -379,6 +379,11 @@ void TypingCommand::deleteKeyPressed(TextGranularity granularity, bool killRing)
             selectionAfterUndo = selectionToDelete;
             break;
         case Selection::CARET: {
+            if (breakOutOfEmptyMailBlockquotedParagraph()) {
+                typingAddedToOpenCommand();
+                return;
+            }
+        
             m_smartDelete = false;
 
             SelectionController selection;
index b5eb104..b994ce6 100644 (file)
@@ -593,6 +593,20 @@ Node* enclosingNodeOfType(const Position& p, bool (*nodeIsOfType)(const Node*),
     return 0;
 }
 
+Node* highestEnclosingNodeOfType(const Position& p, bool (*nodeIsOfType)(const Node*))
+{
+    Node* highest = 0;
+    Node* root = highestEditableRoot(p);
+    for (Node* n = p.node(); n; n = n->parentNode()) {
+        if ((*nodeIsOfType)(n))
+            highest = n;
+        if (n == root)
+            break;
+    }
+    
+    return highest;
+}
+
 Node* enclosingTableCell(const Position& p)
 {
     return enclosingNodeOfType(p, &isTableCell);
index 4a6a392..96c4dcb 100644 (file)
@@ -113,6 +113,7 @@ Node* isFirstPositionAfterTable(const VisiblePosition&);
 
 Node* enclosingNodeWithTag(const Position&, const QualifiedName&);
 Node* enclosingNodeOfType(const Position&, bool (*nodeIsOfType)(const Node*), bool onlyReturnEditableNodes = true);
+Node* highestEnclosingNodeOfType(const Position&, bool (*nodeIsOfType)(const Node*));
 Node* enclosingTableCell(const Position&);
 Node* enclosingEmptyListItem(const VisiblePosition&);
 Node* enclosingAnchorElement(const Position&);