Add an editing command for creating and inserting child lists
authorwenson_hsieh@apple.com <wenson_hsieh@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 8 Nov 2018 03:14:37 +0000 (03:14 +0000)
committerwenson_hsieh@apple.com <wenson_hsieh@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 8 Nov 2018 03:14:37 +0000 (03:14 +0000)
https://bugs.webkit.org/show_bug.cgi?id=191335
<rdar://problem/45814050>

Reviewed by Ryosuke Niwa.

Source/WebCore:

Currently, insertOrderedList and insertUnorderedList only toggle or change list state (i.e. if the selection is
in an ordered or unordered list, reinserting the same list type removes the current list, and inserting a
different list type changes the enclosing list).

However, for certain internal clients (e.g. Mail), if the start of the selection is enclosed by a list item, we
instead create a new list item and insert it after the enclosing list item, and then create a new list within
that list item. Currently, this logic is implemented in Mail for legacy-WebKit-based Mail compose. This patch
brings this logic into WebKit in the form of a new editing command.

Tests: editing/execCommand/insert-nested-lists-in-table.html
       editing/execCommand/insert-nested-lists-with-pre.html
       editing/execCommand/insert-nested-lists.html

* Sources.txt:
* WebCore.xcodeproj/project.pbxproj:
* editing/Editor.cpp:
(WebCore::Editor::insertOrderedList):
(WebCore::Editor::insertUnorderedList):
* editing/EditorCommand.cpp:
(WebCore::executeInsertOrderedList):
(WebCore::executeInsertUnorderedList):
(WebCore::executeInsertNestedUnorderedList):
(WebCore::executeInsertNestedOrderedList):
(WebCore::createCommandMap):
* editing/IndentOutdentCommand.cpp:
(WebCore::IndentOutdentCommand::outdentParagraph):
* editing/InsertListCommand.cpp:
(WebCore::InsertListCommand::doApply):
(WebCore::InsertListCommand::editingAction const):
* editing/InsertListCommand.h:

Change a couple of `enum`s into `enum class`es.

* editing/InsertNestedListCommand.cpp: Added.
(WebCore::InsertNestedListCommand::insertUnorderedList):
(WebCore::InsertNestedListCommand::insertOrderedList):
(WebCore::InsertNestedListCommand::doApply):
* editing/InsertNestedListCommand.h: Added.

Add a new edit command to insert a new list (as a child of any containing list). If the start of the selection
is in a list item, we create a new list item, move the selection into the list item, and increment its list
level; otherwise, simply fall back to inserting a list.

* editing/ModifySelectionListLevel.cpp:
(WebCore::IncreaseSelectionListLevelCommand::doApply):
(WebCore::IncreaseSelectionListLevelCommand::increaseSelectionListLevel):
(WebCore::IncreaseSelectionListLevelCommand::increaseSelectionListLevelOrdered):
(WebCore::IncreaseSelectionListLevelCommand::increaseSelectionListLevelUnordered):
* editing/ModifySelectionListLevel.h:

Expose this constructor, allowing other edit commands to change selection list level as a composite edit
command. Also, change an `enum` into an `enum class`.

(WebCore::IncreaseSelectionListLevelCommand::create):

LayoutTests:

Add a new layout tests that exercise the "InsertNested(Un)orderedList" editing commands in several scenarios
including undo, redo, executing the edit command with a ranged selection, outdenting to decrease list level,
inserting lists in and around tables and table cells, and inserting lists in and around pre elements.

* editing/execCommand/insert-nested-lists-expected.txt: Added.
* editing/execCommand/insert-nested-lists-in-table-expected.txt: Added.
* editing/execCommand/insert-nested-lists-in-table.html: Added.
* editing/execCommand/insert-nested-lists-with-pre-expected.txt: Added.
* editing/execCommand/insert-nested-lists-with-pre.html: Added.
* editing/execCommand/insert-nested-lists.html: Added.

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

19 files changed:
LayoutTests/ChangeLog
LayoutTests/editing/execCommand/insert-nested-lists-expected.txt [new file with mode: 0644]
LayoutTests/editing/execCommand/insert-nested-lists-in-table-expected.txt [new file with mode: 0644]
LayoutTests/editing/execCommand/insert-nested-lists-in-table.html [new file with mode: 0644]
LayoutTests/editing/execCommand/insert-nested-lists-with-pre-expected.txt [new file with mode: 0644]
LayoutTests/editing/execCommand/insert-nested-lists-with-pre.html [new file with mode: 0644]
LayoutTests/editing/execCommand/insert-nested-lists.html [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/Sources.txt
Source/WebCore/WebCore.xcodeproj/project.pbxproj
Source/WebCore/editing/Editor.cpp
Source/WebCore/editing/EditorCommand.cpp
Source/WebCore/editing/IndentOutdentCommand.cpp
Source/WebCore/editing/InsertListCommand.cpp
Source/WebCore/editing/InsertListCommand.h
Source/WebCore/editing/InsertNestedListCommand.cpp [new file with mode: 0644]
Source/WebCore/editing/InsertNestedListCommand.h [new file with mode: 0644]
Source/WebCore/editing/ModifySelectionListLevel.cpp
Source/WebCore/editing/ModifySelectionListLevel.h

index 1e5ac76..a4f9ca5 100644 (file)
@@ -1,3 +1,22 @@
+2018-11-07  Wenson Hsieh  <wenson_hsieh@apple.com>
+
+        Add an editing command for creating and inserting child lists
+        https://bugs.webkit.org/show_bug.cgi?id=191335
+        <rdar://problem/45814050>
+
+        Reviewed by Ryosuke Niwa.
+
+        Add a new layout tests that exercise the "InsertNested(Un)orderedList" editing commands in several scenarios
+        including undo, redo, executing the edit command with a ranged selection, outdenting to decrease list level,
+        inserting lists in and around tables and table cells, and inserting lists in and around pre elements.
+
+        * editing/execCommand/insert-nested-lists-expected.txt: Added.
+        * editing/execCommand/insert-nested-lists-in-table-expected.txt: Added.
+        * editing/execCommand/insert-nested-lists-in-table.html: Added.
+        * editing/execCommand/insert-nested-lists-with-pre-expected.txt: Added.
+        * editing/execCommand/insert-nested-lists-with-pre.html: Added.
+        * editing/execCommand/insert-nested-lists.html: Added.
+
 2018-11-07  Chris Dumez  <cdumez@apple.com>
 
         ASSERT(renderer()) under HTMLTextAreaElement::updateValue()
diff --git a/LayoutTests/editing/execCommand/insert-nested-lists-expected.txt b/LayoutTests/editing/execCommand/insert-nested-lists-expected.txt
new file mode 100644 (file)
index 0000000..a469c7b
--- /dev/null
@@ -0,0 +1,94 @@
+Verifies that 'InsertNestedUnorderedList' and 'InsertNestedOrderedList' work as expected. This test requires WebKitTestRunner or DumpRenderTree.
+
+After unordered list insertion:
+| <ul>
+|   <li>
+|     "foo<#selection-caret>"
+|     <br>
+
+After typing and ordered sublist insertion:
+| <ul>
+|   <li>
+|     "foo"
+|     <br>
+|   <ol>
+|     <li>
+|       "bar<#selection-caret>"
+
+After the second unordered list insertion:
+| <ul>
+|   <li>
+|     "foo"
+|     <br>
+|   <ol>
+|     <li>
+|       "bar"
+|     <ul>
+|       <li>
+|         <#selection-caret>
+
+After undoing the last list insertion:
+| <ul>
+|   <li>
+|     "foo"
+|     <br>
+|   <ol>
+|     <li>
+|       "bar<#selection-caret>"
+
+After redoing the last list insertion:
+| <ul>
+|   <li>
+|     "foo"
+|     <br>
+|   <ol>
+|     <li>
+|       "bar"
+|     <ul>
+|       <li>
+|         <#selection-caret>
+
+After selecting a range and inserting another unordered list:
+| <ul>
+|   <li>
+|     "foo"
+|     <br>
+|   <ol>
+|     <li>
+|       "bar"
+|     <ul>
+|       <li>
+|         <#selection-caret>
+|       <ul>
+|         <li>
+
+After outdenting:
+| <ul>
+|   <li>
+|     "foo"
+|     <br>
+|   <ol>
+|     <li>
+|       "bar"
+|     <li>
+|       <#selection-caret>
+|       <br>
+|     <ul>
+|       <ul>
+|         <li>
+
+After outdenting again:
+| <ul>
+|   <li>
+|     "foo"
+|     <br>
+|   <ol>
+|     <li>
+|       "bar"
+|   <li>
+|     "baz<#selection-caret>"
+|     <br>
+|   <ol>
+|     <ul>
+|       <ul>
+|         <li>
diff --git a/LayoutTests/editing/execCommand/insert-nested-lists-in-table-expected.txt b/LayoutTests/editing/execCommand/insert-nested-lists-in-table-expected.txt
new file mode 100644 (file)
index 0000000..9921ab3
--- /dev/null
@@ -0,0 +1,142 @@
+Verifies that 'InsertNestedUnorderedList' and 'InsertNestedOrderedList' work as expected in table cells. This test requires WebKitTestRunner or DumpRenderTree.
+
+After inserting a list in a table cell:
+| "
+    "
+| <table>
+|   "
+        "
+|   <tbody>
+|     "
+            "
+|     <tr>
+|       <th>
+|         "Left"
+|       <th>
+|         "Right"
+|     "
+            "
+|     <tr>
+|       <td>
+|         id="foo"
+|         "Foo"
+|       <td>
+|         "Bar"
+|     "
+            "
+|     <tr>
+|       <td>
+|         "Baz"
+|       <td>
+|         id="garply"
+|         <ol>
+|           <li>
+|             "<#selection-caret>Garply"
+|             <br>
+|       " "
+|     "
+        "
+|   "
+    "
+| "
+"
+
+After wrapping the table in a list:
+| "
+    "
+| <ul>
+|   <li>
+|     <#selection-anchor>
+|     <table>
+|       <tbody>
+|         <tr>
+|           <th>
+|             "Left"
+|           <th>
+|             "Right"
+|         <tr>
+|           <td>
+|             id="foo"
+|             "Foo"
+|           <td>
+|             "Bar"
+|         <tr>
+|           <td>
+|             "Baz"
+|           <td>
+|             id="garply"
+|             <ol>
+|               <li>
+|                 "Garply"
+|                 <br>
+|     <#selection-focus>
+| "
+"
+
+After inserting a nested list below the outer list:
+| "
+    "
+| <ul>
+|   <li>
+|     <table>
+|       <tbody>
+|         <tr>
+|           <th>
+|             "Left"
+|           <th>
+|             "Right"
+|         <tr>
+|           <td>
+|             id="foo"
+|             "Foo"
+|           <td>
+|             "Bar"
+|         <tr>
+|           <td>
+|             "Baz"
+|           <td>
+|             id="garply"
+|             <ol>
+|               <li>
+|                 "Garply"
+|                 <br>
+|   <ul>
+|     <li>
+|       <#selection-caret>
+| "
+"
+
+After inserting another list under a table cell:
+| "
+    "
+| <ul>
+|   <li>
+|     <table>
+|       <tbody>
+|         <tr>
+|           <th>
+|             "Left"
+|           <th>
+|             "Right"
+|         <tr>
+|           <td>
+|             id="foo"
+|             "Foo"
+|           <td>
+|             "Bar"
+|         <tr>
+|           <td>
+|             "Baz"
+|           <td>
+|             id="garply"
+|             <ol>
+|               <li>
+|                 "Garply"
+|                 <br>
+|               <ol>
+|                 <li>
+|                   <#selection-caret>
+|   <ul>
+|     <li>
+| "
+"
diff --git a/LayoutTests/editing/execCommand/insert-nested-lists-in-table.html b/LayoutTests/editing/execCommand/insert-nested-lists-in-table.html
new file mode 100644 (file)
index 0000000..1a6acd6
--- /dev/null
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src="../editing.js"></script>
+<script src="../../resources/dump-as-markup.js"></script>
+</head>
+<body>
+<div contenteditable id="editor">
+    <table>
+        <tbody>
+            <tr><th>Left</th><th>Right</th></tr>
+            <tr><td id="foo">Foo</td><td>Bar</td></tr>
+            <tr><td>Baz</td><td id="garply">Garply</td> </tr>
+        </tbody>
+    </table>
+</div>
+</body>
+<script>
+Markup.description("Verifies that 'InsertNestedUnorderedList' and 'InsertNestedOrderedList' work as expected in table cells. This test requires WebKitTestRunner or DumpRenderTree.");
+
+editor.focus();
+getSelection().setPosition(garply.childNodes[0], 0);
+document.execCommand("InsertNestedOrderedList");
+Markup.dump("editor", "After inserting a list in a table cell");
+
+selectAllCommand();
+document.execCommand("InsertNestedUnorderedList");
+Markup.dump("editor", "After wrapping the table in a list");
+
+getSelection().setPosition(foo.childNodes[0], 0);
+document.execCommand("InsertNestedUnorderedList");
+Markup.dump("editor", "After inserting a nested list below the outer list");
+
+getSelection().setPosition(garply.childNodes[0], 3);
+document.execCommand("InsertNestedOrderedList");
+Markup.dump("editor", "After inserting another list under a table cell");
+</script>
+</html>
diff --git a/LayoutTests/editing/execCommand/insert-nested-lists-with-pre-expected.txt b/LayoutTests/editing/execCommand/insert-nested-lists-with-pre-expected.txt
new file mode 100644 (file)
index 0000000..19317a4
--- /dev/null
@@ -0,0 +1,36 @@
+Verifies that 'InsertNestedUnorderedList' and 'InsertNestedOrderedList' work as expected with pre elements. This test requires WebKitTestRunner or DumpRenderTree.
+
+After inserting a list inside the pre:
+| "
+    "
+| <pre>
+|   id="pre"
+|   "Hello"
+|   <ul>
+|     <li>
+|       id="foo"
+|       "foo"
+|     <ol>
+|       <li>
+|         <#selection-caret>
+|   "world"
+| "
+"
+
+After inserting a list around the pre:
+| "
+    "
+| <pre>
+|   id="pre"
+|   <ul>
+|     <li>
+|       "<#selection-caret>Hello"
+|       <br>
+|     <li>
+|       id="foo"
+|       "foo"
+|     <ol>
+|       <li>
+|   "world"
+| "
+"
diff --git a/LayoutTests/editing/execCommand/insert-nested-lists-with-pre.html b/LayoutTests/editing/execCommand/insert-nested-lists-with-pre.html
new file mode 100644 (file)
index 0000000..093420a
--- /dev/null
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src="../editing.js"></script>
+<script src="../../resources/dump-as-markup.js"></script>
+</head>
+<body>
+<div contenteditable id="editor">
+    <pre id="pre">Hello<ul><li id="foo">foo</li></ul>world</pre>
+</div>
+</body>
+<script>
+Markup.description("Verifies that 'InsertNestedUnorderedList' and 'InsertNestedOrderedList' work as expected with pre elements. This test requires WebKitTestRunner or DumpRenderTree.");
+
+editor.focus();
+getSelection().setPosition(foo.childNodes[0], 0);
+document.execCommand("InsertNestedOrderedList");
+Markup.dump("editor", "After inserting a list inside the pre");
+
+getSelection().setPosition(pre.childNodes[0], 0);
+document.execCommand("InsertNestedUnorderedList");
+Markup.dump("editor", "After inserting a list around the pre");
+</script>
+</html>
diff --git a/LayoutTests/editing/execCommand/insert-nested-lists.html b/LayoutTests/editing/execCommand/insert-nested-lists.html
new file mode 100644 (file)
index 0000000..bd4f34b
--- /dev/null
@@ -0,0 +1,50 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src="../editing.js"></script>
+<script src="../../resources/dump-as-markup.js"></script>
+</head>
+<body>
+<div contenteditable id="editor"></div>
+</body>
+<script>
+(async () => {
+    Markup.waitUntilDone();
+    Markup.description("Verifies that 'InsertNestedUnorderedList' and 'InsertNestedOrderedList' work as expected. This test requires WebKitTestRunner or DumpRenderTree.");
+    editor.focus();
+
+    [..."foo"].map(typeCharacterCommand);
+    document.execCommand("InsertNestedUnorderedList");
+    Markup.dump("editor", "After unordered list insertion");
+
+    document.execCommand("InsertNestedOrderedList");
+    [..."bar"].map(typeCharacterCommand);
+    Markup.dump("editor", "After typing and ordered sublist insertion");
+
+    // By default, AppKit on macOS coalesces undo operations that occur within the same runloop. Wait until the next
+    // runloop before inserting another unordered list to ensure that it gets its own entry in the undo stack.
+    await new Promise(resolve => requestAnimationFrame(resolve));
+
+    document.execCommand("InsertNestedUnorderedList");
+    Markup.dump("editor", "After the second unordered list insertion");
+
+    undoCommand();
+    Markup.dump("editor", "After undoing the last list insertion");
+
+    redoCommand();
+    Markup.dump("editor", "After redoing the last list insertion");
+
+    execExtendSelectionBackwardByLineCommand();
+    document.execCommand("InsertNestedUnorderedList");
+    Markup.dump("editor", "After selecting a range and inserting another unordered list");
+
+    document.execCommand("Outdent");
+    Markup.dump("editor", "After outdenting");
+
+    [..."baz"].map(typeCharacterCommand);
+    document.execCommand("Outdent");
+    Markup.dump("editor", "After outdenting again");
+    Markup.notifyDone();
+})();
+</script>
+</html>
index 31f2e4d..9182b39 100644 (file)
@@ -1,3 +1,66 @@
+2018-11-07  Wenson Hsieh  <wenson_hsieh@apple.com>
+
+        Add an editing command for creating and inserting child lists
+        https://bugs.webkit.org/show_bug.cgi?id=191335
+        <rdar://problem/45814050>
+
+        Reviewed by Ryosuke Niwa.
+
+        Currently, insertOrderedList and insertUnorderedList only toggle or change list state (i.e. if the selection is
+        in an ordered or unordered list, reinserting the same list type removes the current list, and inserting a
+        different list type changes the enclosing list).
+
+        However, for certain internal clients (e.g. Mail), if the start of the selection is enclosed by a list item, we
+        instead create a new list item and insert it after the enclosing list item, and then create a new list within
+        that list item. Currently, this logic is implemented in Mail for legacy-WebKit-based Mail compose. This patch
+        brings this logic into WebKit in the form of a new editing command.
+
+        Tests: editing/execCommand/insert-nested-lists-in-table.html
+               editing/execCommand/insert-nested-lists-with-pre.html
+               editing/execCommand/insert-nested-lists.html
+
+        * Sources.txt:
+        * WebCore.xcodeproj/project.pbxproj:
+        * editing/Editor.cpp:
+        (WebCore::Editor::insertOrderedList):
+        (WebCore::Editor::insertUnorderedList):
+        * editing/EditorCommand.cpp:
+        (WebCore::executeInsertOrderedList):
+        (WebCore::executeInsertUnorderedList):
+        (WebCore::executeInsertNestedUnorderedList):
+        (WebCore::executeInsertNestedOrderedList):
+        (WebCore::createCommandMap):
+        * editing/IndentOutdentCommand.cpp:
+        (WebCore::IndentOutdentCommand::outdentParagraph):
+        * editing/InsertListCommand.cpp:
+        (WebCore::InsertListCommand::doApply):
+        (WebCore::InsertListCommand::editingAction const):
+        * editing/InsertListCommand.h:
+
+        Change a couple of `enum`s into `enum class`es.
+
+        * editing/InsertNestedListCommand.cpp: Added.
+        (WebCore::InsertNestedListCommand::insertUnorderedList):
+        (WebCore::InsertNestedListCommand::insertOrderedList):
+        (WebCore::InsertNestedListCommand::doApply):
+        * editing/InsertNestedListCommand.h: Added.
+
+        Add a new edit command to insert a new list (as a child of any containing list). If the start of the selection
+        is in a list item, we create a new list item, move the selection into the list item, and increment its list
+        level; otherwise, simply fall back to inserting a list.
+
+        * editing/ModifySelectionListLevel.cpp:
+        (WebCore::IncreaseSelectionListLevelCommand::doApply):
+        (WebCore::IncreaseSelectionListLevelCommand::increaseSelectionListLevel):
+        (WebCore::IncreaseSelectionListLevelCommand::increaseSelectionListLevelOrdered):
+        (WebCore::IncreaseSelectionListLevelCommand::increaseSelectionListLevelUnordered):
+        * editing/ModifySelectionListLevel.h:
+
+        Expose this constructor, allowing other edit commands to change selection list level as a composite edit
+        command. Also, change an `enum` into an `enum class`.
+
+        (WebCore::IncreaseSelectionListLevelCommand::create):
+
 2018-11-07  Chris Dumez  <cdumez@apple.com>
 
         ASSERT(renderer()) under HTMLTextAreaElement::updateValue()
index 42bf854..d4d6fbe 100644 (file)
@@ -903,6 +903,7 @@ editing/FontAttributeChanges.cpp
 editing/FormatBlockCommand.cpp
 editing/FrameSelection.cpp
 editing/HTMLInterchange.cpp
+editing/InsertNestedListCommand.cpp
 editing/IndentOutdentCommand.cpp
 editing/InsertIntoTextNodeCommand.cpp
 editing/InsertLineBreakCommand.cpp
index 0f26e6a..73dc451 100644 (file)
                F47A09D120A93A9700240FAE /* DisabledAdaptations.h in Headers */ = {isa = PBXBuildFile; fileRef = F47A09CF20A939F600240FAE /* DisabledAdaptations.h */; settings = {ATTRIBUTES = (Private, ); }; };
                F47A5E3E195B8C8A00483100 /* StyleScrollSnapPoints.h in Headers */ = {isa = PBXBuildFile; fileRef = F47A5E3B195B8C8A00483100 /* StyleScrollSnapPoints.h */; settings = {ATTRIBUTES = (Private, ); }; };
                F47A633D1FF6FD500081B3CC /* PromisedAttachmentInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = F47A633C1FF6FD500081B3CC /* PromisedAttachmentInfo.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               F47AD23B21934FF40051DD0B /* InsertNestedListCommand.h in Headers */ = {isa = PBXBuildFile; fileRef = F47AD2382193499A0051DD0B /* InsertNestedListCommand.h */; };
                F48223101E3869B80066FC79 /* WebItemProviderPasteboard.mm in Sources */ = {isa = PBXBuildFile; fileRef = F482230E1E3869B80066FC79 /* WebItemProviderPasteboard.mm */; };
                F48223111E3869B80066FC79 /* WebItemProviderPasteboard.h in Headers */ = {isa = PBXBuildFile; fileRef = F482230F1E3869B80066FC79 /* WebItemProviderPasteboard.h */; settings = {ATTRIBUTES = (Private, ); }; };
                F48223131E386E240066FC79 /* AbstractPasteboard.h in Headers */ = {isa = PBXBuildFile; fileRef = F48223121E386E240066FC79 /* AbstractPasteboard.h */; settings = {ATTRIBUTES = (Private, ); }; };
                F47A5E3A195B8C8A00483100 /* StyleScrollSnapPoints.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = StyleScrollSnapPoints.cpp; sourceTree = "<group>"; };
                F47A5E3B195B8C8A00483100 /* StyleScrollSnapPoints.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = StyleScrollSnapPoints.h; sourceTree = "<group>"; };
                F47A633C1FF6FD500081B3CC /* PromisedAttachmentInfo.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PromisedAttachmentInfo.h; sourceTree = "<group>"; };
+               F47AD2382193499A0051DD0B /* InsertNestedListCommand.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = InsertNestedListCommand.h; sourceTree = "<group>"; };
+               F47AD2392193499A0051DD0B /* InsertNestedListCommand.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = InsertNestedListCommand.cpp; sourceTree = "<group>"; };
                F482230E1E3869B80066FC79 /* WebItemProviderPasteboard.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = WebItemProviderPasteboard.mm; sourceTree = "<group>"; };
                F482230F1E3869B80066FC79 /* WebItemProviderPasteboard.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WebItemProviderPasteboard.h; sourceTree = "<group>"; };
                F48223121E386E240066FC79 /* AbstractPasteboard.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AbstractPasteboard.h; sourceTree = "<group>"; };
                                93309D9D099E64910056E581 /* InsertLineBreakCommand.h */,
                                D07DEAB70A36554A00CA30F8 /* InsertListCommand.cpp */,
                                D07DEAB80A36554A00CA30F8 /* InsertListCommand.h */,
+                               F47AD2392193499A0051DD0B /* InsertNestedListCommand.cpp */,
+                               F47AD2382193499A0051DD0B /* InsertNestedListCommand.h */,
                                93309D9E099E64910056E581 /* InsertNodeBeforeCommand.cpp */,
                                93309D9F099E64910056E581 /* InsertNodeBeforeCommand.h */,
                                93309DA0099E64910056E581 /* InsertParagraphSeparatorCommand.cpp */,
                                51E399021D6E4750009C8831 /* GameControllerGamepadProvider.h in Headers */,
                                516C62211950D48700337E75 /* GamepadEvent.h in Headers */,
                                51A9D9EA195B931F001B2B5C /* GamepadManager.h in Headers */,
-                               412DE4B8219285C00075F3A7 /* RTCRtpCapabilities.h in Headers */,
                                515BE1921D54F5FB00DD7C68 /* GamepadProvider.h in Headers */,
                                515BE1931D54F5FB00DD7C68 /* GamepadProviderClient.h in Headers */,
                                8EC6C963201A251600FBFA53 /* GapLength.h in Headers */,
                                93309DEA099E64920056E581 /* InsertIntoTextNodeCommand.h in Headers */,
                                93309DEC099E64920056E581 /* InsertLineBreakCommand.h in Headers */,
                                D07DEABA0A36554A00CA30F8 /* InsertListCommand.h in Headers */,
+                               F47AD23B21934FF40051DD0B /* InsertNestedListCommand.h in Headers */,
                                93309DEE099E64920056E581 /* InsertNodeBeforeCommand.h in Headers */,
                                93309DF0099E64920056E581 /* InsertParagraphSeparatorCommand.h in Headers */,
                                93309DF2099E64920056E581 /* InsertTextCommand.h in Headers */,
                                078E094217D16E1C00420AA1 /* RTCPeerConnectionHandlerClient.h in Headers */,
                                078E092417D14D1C00420AA1 /* RTCPeerConnectionIceEvent.h in Headers */,
                                5E2C437C1BCF9A840001E2BC /* RTCPeerConnectionInternalsBuiltins.h in Headers */,
+                               412DE4B8219285C00075F3A7 /* RTCRtpCapabilities.h in Headers */,
                                5E2C43631BCEE32B0001E2BC /* RTCRtpReceiver.h in Headers */,
                                5E2C43611BCEE3230001E2BC /* RTCRtpSender.h in Headers */,
                                5E5E2B141CFC3E75000C0D85 /* RTCRtpTransceiver.h in Headers */,
index 0c63811..44362ed 100644 (file)
@@ -804,7 +804,7 @@ RefPtr<Node> Editor::insertOrderedList()
     if (!canEditRichly())
         return nullptr;
         
-    RefPtr<Node> newList = InsertListCommand::insertList(document(), InsertListCommand::OrderedList);
+    auto newList = InsertListCommand::insertList(document(), InsertListCommand::Type::OrderedList);
     revealSelectionAfterEditingOperation();
     return newList;
 }
@@ -814,7 +814,7 @@ RefPtr<Node> Editor::insertUnorderedList()
     if (!canEditRichly())
         return nullptr;
         
-    RefPtr<Node> newList = InsertListCommand::insertList(document(), InsertListCommand::UnorderedList);
+    auto newList = InsertListCommand::insertList(document(), InsertListCommand::Type::UnorderedList);
     revealSelectionAfterEditingOperation();
     return newList;
 }
index ff1ad30..86f9ccc 100644 (file)
@@ -47,6 +47,7 @@
 #include "HTMLNames.h"
 #include "IndentOutdentCommand.h"
 #include "InsertListCommand.h"
+#include "InsertNestedListCommand.h"
 #include "Page.h"
 #include "Pasteboard.h"
 #include "RenderBox.h"
@@ -509,7 +510,7 @@ static bool executeInsertNewlineInQuotedContent(Frame& frame, Event*, EditorComm
 static bool executeInsertOrderedList(Frame& frame, Event*, EditorCommandSource, const String&)
 {
     ASSERT(frame.document());
-    InsertListCommand::create(*frame.document(), InsertListCommand::OrderedList)->apply();
+    InsertListCommand::create(*frame.document(), InsertListCommand::Type::OrderedList)->apply();
     return true;
 }
 
@@ -533,7 +534,21 @@ static bool executeInsertText(Frame& frame, Event*, EditorCommandSource, const S
 static bool executeInsertUnorderedList(Frame& frame, Event*, EditorCommandSource, const String&)
 {
     ASSERT(frame.document());
-    InsertListCommand::create(*frame.document(), InsertListCommand::UnorderedList)->apply();
+    InsertListCommand::create(*frame.document(), InsertListCommand::Type::UnorderedList)->apply();
+    return true;
+}
+
+static bool executeInsertNestedUnorderedList(Frame& frame, Event*, EditorCommandSource, const String&)
+{
+    ASSERT(frame.document());
+    InsertNestedListCommand::insertUnorderedList(*frame.document());
+    return true;
+}
+
+static bool executeInsertNestedOrderedList(Frame& frame, Event*, EditorCommandSource, const String&)
+{
+    ASSERT(frame.document());
+    InsertNestedListCommand::insertOrderedList(*frame.document());
     return true;
 }
 
@@ -1582,10 +1597,12 @@ static const CommandMap& createCommandMap()
         { "InsertNewline", { executeInsertNewline, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, isTextInsertion, doNotAllowExecutionWhenDisabled } },    
         { "InsertNewlineInQuotedContent", { executeInsertNewlineInQuotedContent, supported, enabledInRichlyEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
         { "InsertOrderedList", { executeInsertOrderedList, supported, enabledInRichlyEditableText, stateOrderedList, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
+        { "InsertNestedOrderedList", { executeInsertNestedOrderedList, supported, enabledInRichlyEditableText, stateOrderedList, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
         { "InsertParagraph", { executeInsertParagraph, supported, enabledInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
         { "InsertTab", { executeInsertTab, supportedFromMenuOrKeyBinding, enabledInEditableText, stateNone, valueNull, isTextInsertion, doNotAllowExecutionWhenDisabled } },
         { "InsertText", { executeInsertText, supported, enabledInEditableText, stateNone, valueNull, isTextInsertion, doNotAllowExecutionWhenDisabled } },
         { "InsertUnorderedList", { executeInsertUnorderedList, supported, enabledInRichlyEditableText, stateUnorderedList, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
+        { "InsertNestedUnorderedList", { executeInsertNestedUnorderedList, supported, enabledInRichlyEditableText, stateUnorderedList, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
         { "Italic", { executeToggleItalic, supported, enabledInRichlyEditableText, stateItalic, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
         { "JustifyCenter", { executeJustifyCenter, supported, enabledInRichlyEditableText, stateJustifyCenter, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
         { "JustifyFull", { executeJustifyFull, supported, enabledInRichlyEditableText, stateJustifyFull, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
index 394208a..b1b50ae 100644 (file)
@@ -133,11 +133,11 @@ void IndentOutdentCommand::outdentParagraph()
 
     // Use InsertListCommand to remove the selection from the list
     if (enclosingNode->hasTagName(olTag)) {
-        applyCommandToComposite(InsertListCommand::create(document(), InsertListCommand::OrderedList));
+        applyCommandToComposite(InsertListCommand::create(document(), InsertListCommand::Type::OrderedList));
         return;        
     }
     if (enclosingNode->hasTagName(ulTag)) {
-        applyCommandToComposite(InsertListCommand::create(document(), InsertListCommand::UnorderedList));
+        applyCommandToComposite(InsertListCommand::create(document(), InsertListCommand::Type::UnorderedList));
         return;
     }
     
index c571be1..0a87d3d 100644 (file)
@@ -128,7 +128,7 @@ void InsertListCommand::doApply()
             return;
     }
 
-    auto& listTag = (m_type == OrderedList) ? olTag : ulTag;
+    auto& listTag = (m_type == Type::OrderedList) ? olTag : ulTag;
     if (endingSelection().isRange()) {
         VisibleSelection selection = selectionForParagraphIteration(endingSelection());
         ASSERT(selection.isRange());
@@ -192,7 +192,7 @@ void InsertListCommand::doApply()
 
 EditAction InsertListCommand::editingAction() const
 {
-    return m_type == OrderedList ? EditAction::InsertOrderedList : EditAction::InsertUnorderedList;
+    return m_type == Type::OrderedList ? EditAction::InsertOrderedList : EditAction::InsertUnorderedList;
 }
 
 void InsertListCommand::doApplyForSingleParagraph(bool forceCreateList, const HTMLQualifiedName& listTag, Range* currentSelection)
index ede082e..eabe004 100644 (file)
@@ -34,7 +34,7 @@ class HTMLQualifiedName;
 
 class InsertListCommand final : public CompositeEditCommand {
 public:
-    enum Type { OrderedList, UnorderedList };
+    enum class Type : uint8_t { OrderedList, UnorderedList };
 
     static Ref<InsertListCommand> create(Document& document, Type listType)
     {
diff --git a/Source/WebCore/editing/InsertNestedListCommand.cpp b/Source/WebCore/editing/InsertNestedListCommand.cpp
new file mode 100644 (file)
index 0000000..774f75d
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2018 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "InsertNestedListCommand.h"
+
+#include "Editing.h"
+#include "HTMLLIElement.h"
+#include "HTMLNames.h"
+#include "InsertListCommand.h"
+#include "ModifySelectionListLevel.h"
+
+namespace WebCore {
+
+void InsertNestedListCommand::insertUnorderedList(Document& document)
+{
+    InsertNestedListCommand::create(document, Type::UnorderedList)->apply();
+}
+
+void InsertNestedListCommand::insertOrderedList(Document& document)
+{
+    InsertNestedListCommand::create(document, Type::OrderedList)->apply();
+}
+
+void InsertNestedListCommand::doApply()
+{
+    if (endingSelection().isNoneOrOrphaned() || !endingSelection().isContentRichlyEditable())
+        return;
+
+    if (auto enclosingItem = makeRefPtr(enclosingElementWithTag(endingSelection().visibleStart().deepEquivalent(), HTMLNames::liTag))) {
+        auto newListItem = HTMLLIElement::create(document());
+        insertNodeAfter(newListItem.copyRef(), *enclosingItem);
+        setEndingSelection({ Position { newListItem.ptr(), Position::PositionIsBeforeChildren }, DOWNSTREAM });
+
+        auto commandType = m_type == Type::OrderedList ? IncreaseSelectionListLevelCommand::Type::OrderedList : IncreaseSelectionListLevelCommand::Type::UnorderedList;
+        applyCommandToComposite(IncreaseSelectionListLevelCommand::create(document(), commandType));
+        return;
+    }
+
+    auto commandType = m_type == Type::OrderedList ? InsertListCommand::Type::OrderedList : InsertListCommand::Type::UnorderedList;
+    applyCommandToComposite(InsertListCommand::create(document(), commandType));
+}
+
+} // namespace WebCore
diff --git a/Source/WebCore/editing/InsertNestedListCommand.h b/Source/WebCore/editing/InsertNestedListCommand.h
new file mode 100644 (file)
index 0000000..23f5c05
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2018 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include "CompositeEditCommand.h"
+#include "EditAction.h"
+
+namespace WebCore {
+
+class InsertNestedListCommand final : public CompositeEditCommand {
+public:
+    static void insertUnorderedList(Document&);
+    static void insertOrderedList(Document&);
+
+private:
+    enum class Type : uint8_t { OrderedList, UnorderedList };
+
+    static Ref<InsertNestedListCommand> create(Document& document, Type type)
+    {
+        return adoptRef(*new InsertNestedListCommand(document, type));
+    }
+
+    InsertNestedListCommand(Document& document, Type type)
+        : CompositeEditCommand(document)
+        , m_type(type)
+    {
+    }
+
+    EditAction editingAction() const final
+    {
+        if (m_type == Type::OrderedList)
+            return EditAction::InsertOrderedList;
+
+        return EditAction::InsertUnorderedList;
+    }
+
+    bool preservesTypingStyle() const final { return true; }
+    void doApply() final;
+
+    Type m_type;
+};
+
+} // namespace WebCore
index 9e5d015..682548e 100644 (file)
@@ -185,17 +185,17 @@ void IncreaseSelectionListLevelCommand::doApply()
         // create a sublist for the preceding element and move nodes there
         RefPtr<Element> newParent;
         switch (m_listType) {
-            case InheritedListType:
-                newParent = startListChild->parentElement();
-                if (newParent)
-                    newParent = newParent->cloneElementWithoutChildren(document());
-                break;
-            case OrderedList:
-                newParent = HTMLOListElement::create(document());
-                break;
-            case UnorderedList:
-                newParent = HTMLUListElement::create(document());
-                break;
+        case Type::InheritedListType:
+            newParent = startListChild->parentElement();
+            if (newParent)
+                newParent = newParent->cloneElementWithoutChildren(document());
+            break;
+        case Type::OrderedList:
+            newParent = HTMLOListElement::create(document());
+            break;
+        case Type::UnorderedList:
+            newParent = HTMLUListElement::create(document());
+            break;
         }
         insertNodeBefore(*newParent, *startListChild);
         appendSiblingNodeRange(startListChild, endListChild, newParent.get());
@@ -221,17 +221,17 @@ RefPtr<Node> IncreaseSelectionListLevelCommand::increaseSelectionListLevel(Docum
 
 RefPtr<Node> IncreaseSelectionListLevelCommand::increaseSelectionListLevel(Document* document)
 {
-    return increaseSelectionListLevel(document, InheritedListType);
+    return increaseSelectionListLevel(document, Type::InheritedListType);
 }
 
 RefPtr<Node> IncreaseSelectionListLevelCommand::increaseSelectionListLevelOrdered(Document* document)
 {
-    return increaseSelectionListLevel(document, OrderedList);
+    return increaseSelectionListLevel(document, Type::OrderedList);
 }
 
 RefPtr<Node> IncreaseSelectionListLevelCommand::increaseSelectionListLevelUnordered(Document* document)
 {
-    return increaseSelectionListLevel(document, UnorderedList);
+    return increaseSelectionListLevel(document, Type::UnorderedList);
 }
 
 DecreaseSelectionListLevelCommand::DecreaseSelectionListLevelCommand(Document& document)
index a2cfd39..57fea3a 100644 (file)
@@ -47,19 +47,19 @@ private:
 // IncreaseSelectionListLevelCommand moves the selected list items one level deeper.
 class IncreaseSelectionListLevelCommand : public ModifySelectionListLevelCommand {
 public:
+    enum class Type : uint8_t { InheritedListType, OrderedList, UnorderedList };
+    static Ref<IncreaseSelectionListLevelCommand> create(Document& document, Type type)
+    {
+        return adoptRef(*new IncreaseSelectionListLevelCommand(document, type));
+    }
+
     static bool canIncreaseSelectionListLevel(Document*);
     static RefPtr<Node> increaseSelectionListLevel(Document*);
     static RefPtr<Node> increaseSelectionListLevelOrdered(Document*);
     static RefPtr<Node> increaseSelectionListLevelUnordered(Document*);
 
 private:
-    enum Type { InheritedListType, OrderedList, UnorderedList };
     static RefPtr<Node> increaseSelectionListLevel(Document*, Type);
-
-    static Ref<IncreaseSelectionListLevelCommand> create(Document& document, Type type)
-    {
-        return adoptRef(*new IncreaseSelectionListLevelCommand(document, type));
-    }
     
     IncreaseSelectionListLevelCommand(Document&, Type);