Title: [240476] trunk
Revision
240476
Author
[email protected]
Date
2019-01-25 09:23:06 -0800 (Fri, 25 Jan 2019)

Log Message

Need a way for _javascript_ (or bundle) code to participate in undo
https://bugs.webkit.org/show_bug.cgi?id=190009
<rdar://problem/44807048>

Reviewed by Ryosuke Niwa.

Source/WebCore:

Finish hooking up `UndoManager::addItems()` to CustomUndoStep.

Tests: editing/undo-manager/undo-manager-add-item-exceptions.html
       editing/undo-manager/undo-manager-add-item.html
       editing/undo-manager/undo-manager-delete-stale-undo-items.html
       editing/undo-manager/undo-manager-item-labels.html
       editing/undo-manager/undo-manager-undo-redo-after-garbage-collection.html

* editing/CompositeEditCommand.h:
* editing/CustomUndoStep.cpp:
(WebCore::CustomUndoStep::didRemoveFromUndoManager):

Add a method to invalidate CustomUndoStep. This clears out the pointer to the undo item, and also invalidates
the UndoItem, removing it from its UndoManager.

* editing/CustomUndoStep.h:
* editing/Editor.cpp:
(WebCore::Editor::registerCustomUndoStep):

Add a helper method to register a CustomUndoStep as a platform undoable step.

* editing/Editor.h:
* editing/UndoStep.h:
* page/UndoItem.h:
(WebCore::UndoItem::undoManager const):
* page/UndoManager.cpp:
(WebCore::UndoManager::addItem):

Create a CustomUndoStep with the given UndoItem, and register it with the platform undo manager.

* page/UndoManager.h:
* page/UndoManager.idl:

Mark addItem() as capable of throwing exceptions.

Source/WebKit:

Invalidate undo steps when removing them from WebPage. Invalidation is a no-op for editing actions that come
from the UA, but for custom undo steps backed by an UndoItem, we clear out the custom undo step's pointer to its
UndoItem and additionally disconnect the UndoItem from its UndoManager.

* WebProcess/WebPage/WebPage.cpp:
(WebKit::WebPage::addWebUndoStep):
(WebKit::WebPage::removeWebEditCommand):
* WebProcess/WebPage/WebUndoStep.h:
(WebKit::WebUndoStep::invalidate):

Tools:

Add UIScriptController helpers to grab the platform undo and redo action labels. Currently only implemented for
Cocoa platforms in WebKit2. See other ChangeLogs for more detail.

* DumpRenderTree/ios/UIScriptControllerIOS.mm:
(WTR::UIScriptController::lastUndoLabel const):
(WTR::UIScriptController::firstRedoLabel const):
(WTR::UIScriptController::platformUndoManager const):
* DumpRenderTree/mac/UIScriptControllerMac.mm:
(WTR::UIScriptController::lastUndoLabel const):
(WTR::UIScriptController::firstRedoLabel const):
(WTR::UIScriptController::platformUndoManager const):
* TestRunnerShared/UIScriptContext/Bindings/UIScriptController.idl:
* TestRunnerShared/UIScriptContext/UIScriptController.cpp:
(WTR::UIScriptController::lastUndoLabel const):
(WTR::UIScriptController::firstRedoLabel const):
* TestRunnerShared/UIScriptContext/UIScriptController.h:
* WebKitTestRunner/UIScriptControllerCocoa.mm:
(WTR::UIScriptController::lastUndoLabel const):
(WTR::UIScriptController::firstRedoLabel const):
* WebKitTestRunner/ios/UIScriptControllerIOS.mm:
(WTR::UIScriptController::platformUndoManager const):
* WebKitTestRunner/mac/UIScriptControllerMac.mm:
(WTR::UIScriptController::platformUndoManager const):

LayoutTests:

Add a few new layout tests covering `UndoManager.addItem()`.

* editing/undo-manager/undo-manager-add-item-exceptions-expected.txt: Added.
* editing/undo-manager/undo-manager-add-item-exceptions.html: Added.

Add a test to verify that we throw exceptions when calling addItem() in a couple of circumstances.

* editing/undo-manager/undo-manager-add-item-expected.txt: Added.
* editing/undo-manager/undo-manager-add-item.html: Added.

Add a test that exercises the new API in both the top-level context and a child frame.

* editing/undo-manager/undo-manager-delete-stale-undo-items-expected.txt: Added.
* editing/undo-manager/undo-manager-delete-stale-undo-items.html: Added.

Add a test to verify that after adding undo items, undoing, and then performing other edit actions, garbage
collection will destroy JS wrappers for the previously added UndoItems, since these undo items' handlers can no
longer be invoked.

* editing/undo-manager/undo-manager-item-labels-expected.txt: Added.
* editing/undo-manager/undo-manager-item-labels.html: Added.

Add a test verifying that the undo and redo action labels are updated correctly when undoing and redoing.

* editing/undo-manager/undo-manager-undo-redo-after-garbage-collection-expected.txt: Added.
* editing/undo-manager/undo-manager-undo-redo-after-garbage-collection.html: Added.

Add a test to verify that triggering garbage collection after adding an undo item without keeping references to
the item (or its undo/redo handlers) doesn't break the API.

* resources/ui-helper.js:
(window.UIHelper.undoAndRedoLabels):

Add a helper method to grab the platform's current undo and redo action names.

(window.UIHelper):

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (240475 => 240476)


--- trunk/LayoutTests/ChangeLog	2019-01-25 16:50:18 UTC (rev 240475)
+++ trunk/LayoutTests/ChangeLog	2019-01-25 17:23:06 UTC (rev 240476)
@@ -1,3 +1,48 @@
+2019-01-25  Wenson Hsieh  <[email protected]>
+
+        Need a way for _javascript_ (or bundle) code to participate in undo
+        https://bugs.webkit.org/show_bug.cgi?id=190009
+        <rdar://problem/44807048>
+
+        Reviewed by Ryosuke Niwa.
+
+        Add a few new layout tests covering `UndoManager.addItem()`.
+
+        * editing/undo-manager/undo-manager-add-item-exceptions-expected.txt: Added.
+        * editing/undo-manager/undo-manager-add-item-exceptions.html: Added.
+
+        Add a test to verify that we throw exceptions when calling addItem() in a couple of circumstances.
+
+        * editing/undo-manager/undo-manager-add-item-expected.txt: Added.
+        * editing/undo-manager/undo-manager-add-item.html: Added.
+
+        Add a test that exercises the new API in both the top-level context and a child frame.
+
+        * editing/undo-manager/undo-manager-delete-stale-undo-items-expected.txt: Added.
+        * editing/undo-manager/undo-manager-delete-stale-undo-items.html: Added.
+
+        Add a test to verify that after adding undo items, undoing, and then performing other edit actions, garbage
+        collection will destroy JS wrappers for the previously added UndoItems, since these undo items' handlers can no
+        longer be invoked.
+
+        * editing/undo-manager/undo-manager-item-labels-expected.txt: Added.
+        * editing/undo-manager/undo-manager-item-labels.html: Added.
+
+        Add a test verifying that the undo and redo action labels are updated correctly when undoing and redoing.
+
+        * editing/undo-manager/undo-manager-undo-redo-after-garbage-collection-expected.txt: Added.
+        * editing/undo-manager/undo-manager-undo-redo-after-garbage-collection.html: Added.
+
+        Add a test to verify that triggering garbage collection after adding an undo item without keeping references to
+        the item (or its undo/redo handlers) doesn't break the API.
+
+        * resources/ui-helper.js:
+        (window.UIHelper.undoAndRedoLabels):
+
+        Add a helper method to grab the platform's current undo and redo action names.
+
+        (window.UIHelper):
+
 2019-01-25  Devin Rousso  <[email protected]>
 
         Web Inspector: improve invalid Audit/Recording JSON error messages

Added: trunk/LayoutTests/editing/undo-manager/undo-manager-add-item-exceptions-expected.txt (0 => 240476)


--- trunk/LayoutTests/editing/undo-manager/undo-manager-add-item-exceptions-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/editing/undo-manager/undo-manager-add-item-exceptions-expected.txt	2019-01-25 17:23:06 UTC (rev 240476)
@@ -0,0 +1,14 @@
+Verifies that UndoManager.addItem() throws _javascript_ exceptions. This test requires WebKitTestRunner.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS firstFrameDocument.undoManager.addItem(item) threw exception SecurityError: A browsing context is required to add an UndoItem.
+PASS document.undoManager.addItem(item) did not throw exception.
+PASS secondFrameDocument.undoManager.addItem(item) threw exception InvalidModificationError: This item has already been added to an UndoManager.
+Performed undo.
+Performed redo.
+PASS successfullyParsed is true
+
+TEST COMPLETE
+

Added: trunk/LayoutTests/editing/undo-manager/undo-manager-add-item-exceptions.html (0 => 240476)


--- trunk/LayoutTests/editing/undo-manager/undo-manager-add-item-exceptions.html	                        (rev 0)
+++ trunk/LayoutTests/editing/undo-manager/undo-manager-add-item-exceptions.html	2019-01-25 17:23:06 UTC (rev 240476)
@@ -0,0 +1,31 @@
+<!DOCTYPE html> <!-- webkit-test-runner [ enableUndoManagerAPI=true ] -->
+<html>
+    <meta charset="utf8">
+    <head>
+        <script src=""
+        <script>
+        addEventListener("load", () => {
+            description("Verifies that UndoManager.addItem() throws _javascript_ exceptions. This test requires WebKitTestRunner.");
+
+            item = new UndoItem({ label: '', undo: () => debug("Performed undo."), redo: () => debug("Performed redo.") });
+
+            const firstFrame = document.getElementById("one");
+            firstFrameDocument = firstFrame.contentDocument;
+            firstFrame.remove();
+
+            secondFrameDocument = document.getElementById("two").contentDocument;
+
+            shouldThrow("firstFrameDocument.undoManager.addItem(item)");
+            shouldNotThrow("document.undoManager.addItem(item)");
+            shouldThrow("secondFrameDocument.undoManager.addItem(item)");
+
+            document.execCommand("Undo");
+            document.execCommand("Redo");
+        });
+        </script>
+    </head>
+    <body>
+        <iframe id="one" srcdoc="<body>One</body>"></iframe>
+        <iframe id="two" srcdoc="<body>Two</body>"></iframe>
+    </body>
+</html>

Added: trunk/LayoutTests/editing/undo-manager/undo-manager-add-item-expected.txt (0 => 240476)


--- trunk/LayoutTests/editing/undo-manager/undo-manager-add-item-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/editing/undo-manager/undo-manager-add-item-expected.txt	2019-01-25 17:23:06 UTC (rev 240476)
@@ -0,0 +1,32 @@
+This test verifies that UndoManager.addItem() can be used to add undo items to the platform undo stack.
+
+
+After adding an undo item (mainframe):
+PASS undoName is "mainframe"
+PASS redoName is ""
+After performing undo (mainframe):
+** UNDO **
+PASS undoName is ""
+PASS redoName is "mainframe"
+After performing redo (mainframe):
+** REDO **
+PASS undoName is "mainframe"
+PASS redoName is ""
+After adding an undo item (subframe):
+PASS undoName is "subframe"
+PASS redoName is ""
+After performing undo (subframe):
+PASS undoName is "mainframe"
+PASS redoName is "subframe"
+After performing redo (subframe):
+PASS undoName is "subframe"
+PASS redoName is ""
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
+
+--------
+Frame: '<!--frame1-->'
+--------
+UNDO, REDO

Added: trunk/LayoutTests/editing/undo-manager/undo-manager-add-item.html (0 => 240476)


--- trunk/LayoutTests/editing/undo-manager/undo-manager-add-item.html	                        (rev 0)
+++ trunk/LayoutTests/editing/undo-manager/undo-manager-add-item.html	2019-01-25 17:23:06 UTC (rev 240476)
@@ -0,0 +1,93 @@
+<!DOCTYPE html> <!-- webkit-test-runner [ enableUndoManagerAPI=true ] -->
+<html>
+    <head>
+        <script src=""
+        <script src=""
+        <script>
+        function addUndoItem()
+        {
+            document.undoManager.addItem(new UndoItem({
+                label: "mainframe",
+                undo: () => debug("** UNDO **"),
+                redo: () => debug("** REDO **")
+            }));
+        }
+        </script>
+    </head>
+    <body>
+        <p>This test verifies that <code>UndoManager.addItem()</code> can be used to add undo items to the platform undo stack.</p>
+        <iframe id="frame" srcdoc="
+            <body>
+                <pre id='output'></pre>
+            </body>
+            <script>
+            function appendOutput(string)
+            {
+                if (output.textContent.length)
+                    output.textContent += ', ';
+                output.textContent += string;
+            }
+
+            function addUndoItem()
+            {
+                document.undoManager.addItem(new UndoItem({
+                    label: 'subframe',
+                    undo: () => appendOutput('UNDO'),
+                    redo: () => appendOutput('REDO')
+                }));
+            }
+            </script>
+        "></iframe>
+        <pre id='console'></pre>
+    </body>
+    <script>
+        jsTestIsAsync = true;
+        undoName = null;
+        redoName = null;
+
+        if (window.testRunner)
+            testRunner.dumpChildFramesAsText();
+
+        addEventListener("load", async () => {
+            debug("After adding an undo item (mainframe):");
+            addUndoItem();
+            [undoName, redoName] = await UIHelper.undoAndRedoLabels();
+            shouldBeEqualToString("undoName", "mainframe");
+            shouldBeEqualToString("redoName", "");
+
+            debug("After performing undo (mainframe):");
+            document.execCommand("undo");
+            [undoName, redoName] = await UIHelper.undoAndRedoLabels();
+            shouldBeEqualToString("undoName", "");
+            shouldBeEqualToString("redoName", "mainframe");
+
+            debug("After performing redo (mainframe):");
+            document.execCommand("redo");
+            [undoName, redoName] = await UIHelper.undoAndRedoLabels();
+            shouldBeEqualToString("undoName", "mainframe");
+            shouldBeEqualToString("redoName", "");
+
+            const frameWindow = frame.contentWindow;
+
+            debug("After adding an undo item (subframe):");
+            frameWindow.addUndoItem();
+            [undoName, redoName] = await UIHelper.undoAndRedoLabels();
+            shouldBeEqualToString("undoName", "subframe");
+            shouldBeEqualToString("redoName", "");
+
+            debug("After performing undo (subframe):");
+            document.execCommand("undo");
+            [undoName, redoName] = await UIHelper.undoAndRedoLabels();
+            shouldBeEqualToString("undoName", "mainframe");
+            shouldBeEqualToString("redoName", "subframe");
+
+            debug("After performing redo (subframe):");
+            document.execCommand("redo");
+            [undoName, redoName] = await UIHelper.undoAndRedoLabels();
+            shouldBeEqualToString("undoName", "subframe");
+            shouldBeEqualToString("redoName", "");
+
+            finishJSTest();
+        });
+    </script>
+</html>

Added: trunk/LayoutTests/editing/undo-manager/undo-manager-delete-stale-undo-items-expected.txt (0 => 240476)


--- trunk/LayoutTests/editing/undo-manager/undo-manager-delete-stale-undo-items-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/editing/undo-manager/undo-manager-delete-stale-undo-items-expected.txt	2019-01-25 17:23:06 UTC (rev 240476)
@@ -0,0 +1,10 @@
+E
+Verifies that JSUndoItems are deleted when they are no longer needed by the platform clipboard. This test requires WebKitTestRunner.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+PASS objectCountBeforeClearingUndoStack - objectCountAfterClearingUndoStack is >= 580
+PASS successfullyParsed is true
+
+TEST COMPLETE
+

Added: trunk/LayoutTests/editing/undo-manager/undo-manager-delete-stale-undo-items.html (0 => 240476)


--- trunk/LayoutTests/editing/undo-manager/undo-manager-delete-stale-undo-items.html	                        (rev 0)
+++ trunk/LayoutTests/editing/undo-manager/undo-manager-delete-stale-undo-items.html	2019-01-25 17:23:06 UTC (rev 240476)
@@ -0,0 +1,66 @@
+<!DOCTYPE html> <!-- webkit-test-runner [ enableUndoManagerAPI=true ] -->
+<html>
+    <meta charset="utf8">
+    <head>
+        <script src=""
+        <script src=""
+        <script>
+        jsTestIsAsync = true;
+
+        function objectCountAfterSimulatingMemoryPressureAndGarbageCollection() {
+            internals.beginSimulatedMemoryPressure();
+            internals.endSimulatedMemoryPressure();
+            GCController.collect();
+            return GCController.getJSObjectCount();
+        }
+
+        async function runTest() {
+            description("Verifies that JSUndoItems are deleted when they are no longer needed by the platform clipboard. This test requires WebKitTestRunner.");
+
+            if (!window.GCController)
+                return;
+
+            editor = document.getElementById("editor");
+            editor.focus();
+
+            const undoItemCount = 200;
+
+            for (let i = 0; i < undoItemCount; ++i) {
+                document.undoManager.addItem(new UndoItem({
+                    label: "",
+                    undo: () => { },
+                    redo: () => { }
+                }));
+            }
+
+            for (let i = 0; i < undoItemCount; ++i)
+                document.execCommand("Undo");
+
+            objectCountBeforeClearingUndoStack = objectCountAfterSimulatingMemoryPressureAndGarbageCollection();
+
+            selectAllCommand();
+            typeCharacterCommand("E");
+
+            // Wait until almost all of the wrappers are collected. For each UndoItem, we expect a total of 3 wrappers:
+            // one each for the undo and redo handlers, and one for the UndoItem itself. However, we also give ourselves
+            // some wiggle room for additional wrappers created when calling some testing helper functions. This is
+            // still a useful test, since it will time out in the case where either our undo items or their undo/redo
+            // handlers are not properly relinquished once they're no longer needed.
+            const minimumNumberOfWrappersToCollectBeforePassing = 3 * undoItemCount - 20;
+            objectCountAfterClearingUndoStack = objectCountBeforeClearingUndoStack;
+            while (objectCountBeforeClearingUndoStack - objectCountAfterClearingUndoStack < minimumNumberOfWrappersToCollectBeforePassing) {
+                await new Promise(resolve => setTimeout(resolve, 100));
+                objectCountAfterClearingUndoStack = objectCountAfterSimulatingMemoryPressureAndGarbageCollection();
+            }
+
+            shouldBeGreaterThanOrEqual("objectCountBeforeClearingUndoStack - objectCountAfterClearingUndoStack", `${minimumNumberOfWrappersToCollectBeforePassing}`);
+            finishJSTest();
+        }
+        </script>
+    </head>
+    <body _onload_="runTest()">
+        <div contenteditable id="editor"></div>
+        <pre id="description"></pre>
+        <pre id="console"></pre>
+    </body>
+</html>

Added: trunk/LayoutTests/editing/undo-manager/undo-manager-item-labels-expected.txt (0 => 240476)


--- trunk/LayoutTests/editing/undo-manager/undo-manager-item-labels-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/editing/undo-manager/undo-manager-item-labels-expected.txt	2019-01-25 17:23:06 UTC (rev 240476)
@@ -0,0 +1,36 @@
+
+Verifies that setting the label attribute of UndoItem affects the undo and redo action names in the platform undo manager.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+After typing:
+PASS undoName is "Typing"
+PASS redoName is ""
+After adding the first UndoItem:
+PASS undoName is "First 🥇"
+PASS redoName is ""
+After adding the second UndoItem:
+PASS undoName is "Second 🥈"
+PASS redoName is ""
+After undoing:
+PASS undoName is "First 🥇"
+PASS redoName is "Second 🥈"
+After undoing again:
+PASS undoName is "Typing"
+PASS redoName is "First 🥇"
+After redoing:
+PASS undoName is "First 🥇"
+PASS redoName is "Second 🥈"
+After redoing again:
+PASS undoName is "Second 🥈"
+PASS redoName is ""
+After undoing and then pasting:
+PASS undoName is "Paste"
+PASS redoName is ""
+After undoing the paste command:
+PASS undoName is "First 🥇"
+PASS redoName is "Paste"
+PASS successfullyParsed is true
+
+TEST COMPLETE
+

Added: trunk/LayoutTests/editing/undo-manager/undo-manager-item-labels.html (0 => 240476)


--- trunk/LayoutTests/editing/undo-manager/undo-manager-item-labels.html	                        (rev 0)
+++ trunk/LayoutTests/editing/undo-manager/undo-manager-item-labels.html	2019-01-25 17:23:06 UTC (rev 240476)
@@ -0,0 +1,95 @@
+<!DOCTYPE html> <!-- webkit-test-runner [ enableUndoManagerAPI=true ] -->
+<html>
+    <meta charset="utf8">
+    <head>
+        <script src=""
+        <script src=""
+        <script src=""
+        <script>
+        jsTestIsAsync = true;
+        undoName = null;
+        redoName = null;
+
+        async function runTest() {
+            description("Verifies that setting the label attribute of UndoItem affects the undo and redo action names "
+                + "in the platform undo manager.");
+
+            field.focus();
+
+            debug("After typing:");
+            document.execCommand("InsertText", true, "hello");
+            [undoName, redoName] = await UIHelper.undoAndRedoLabels();
+            shouldBeEqualToString("undoName", "Typing");
+            shouldBeEqualToString("redoName", "");
+
+            function createUndoItemWithLabel(labelString) {
+                return new UndoItem({
+                    label: labelString,
+                    undo: () => { },
+                    redo: () => { }
+                });
+            }
+
+            debug("After adding the first UndoItem:");
+            const firstItem = createUndoItemWithLabel("First 🥇");
+            document.undoManager.addItem(firstItem);
+            [undoName, redoName] = await UIHelper.undoAndRedoLabels();
+            shouldBeEqualToString("undoName", firstItem.label);
+            shouldBeEqualToString("redoName", "");
+
+            debug("After adding the second UndoItem:");
+            const secondItem = createUndoItemWithLabel("Second 🥈");
+            document.undoManager.addItem(secondItem);
+            [undoName, redoName] = await UIHelper.undoAndRedoLabels();
+            shouldBeEqualToString("undoName", secondItem.label);
+            shouldBeEqualToString("redoName", "");
+
+            debug("After undoing:");
+            document.execCommand("undo");
+            [undoName, redoName] = await UIHelper.undoAndRedoLabels();
+            shouldBeEqualToString("undoName", firstItem.label);
+            shouldBeEqualToString("redoName", secondItem.label);
+
+            debug("After undoing again:");
+            document.execCommand("undo");
+            [undoName, redoName] = await UIHelper.undoAndRedoLabels();
+            shouldBeEqualToString("undoName", "Typing");
+            shouldBeEqualToString("redoName", firstItem.label);
+
+            debug("After redoing:");
+            document.execCommand("redo");
+            [undoName, redoName] = await UIHelper.undoAndRedoLabels();
+            shouldBeEqualToString("undoName", firstItem.label);
+            shouldBeEqualToString("redoName", secondItem.label);
+
+            debug("After redoing again:");
+            document.execCommand("redo");
+            [undoName, redoName] = await UIHelper.undoAndRedoLabels();
+            shouldBeEqualToString("undoName", secondItem.label);
+            shouldBeEqualToString("redoName", "");
+
+            debug("After undoing and then pasting:");
+            document.execCommand("undo");
+            selectAllCommand();
+            copyCommand();
+            pasteCommand();
+            [undoName, redoName] = await UIHelper.undoAndRedoLabels();
+            shouldBeEqualToString("undoName", "Paste");
+            shouldBeEqualToString("redoName", "");
+
+            debug("After undoing the paste command:");
+            document.execCommand("undo");
+            [undoName, redoName] = await UIHelper.undoAndRedoLabels();
+            shouldBeEqualToString("undoName", firstItem.label);
+            shouldBeEqualToString("redoName", "Paste");
+
+            finishJSTest();
+        }
+        </script>
+    </head>
+    <body _onload_="runTest()">
+        <input id="field"></input>
+        <pre id="description"></pre>
+        <pre id="console"></pre>
+    </body>
+</html>

Added: trunk/LayoutTests/editing/undo-manager/undo-manager-undo-redo-after-garbage-collection-expected.txt (0 => 240476)


--- trunk/LayoutTests/editing/undo-manager/undo-manager-undo-redo-after-garbage-collection-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/editing/undo-manager/undo-manager-undo-redo-after-garbage-collection-expected.txt	2019-01-25 17:23:06 UTC (rev 240476)
@@ -0,0 +1,11 @@
+After redo
+Verifies that undo and redo callback handlers survive garbage collection. This test requires WebKitTestRunner.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+PASS text.textContent is "After undo"
+PASS text.textContent is "After redo"
+PASS successfullyParsed is true
+
+TEST COMPLETE
+

Added: trunk/LayoutTests/editing/undo-manager/undo-manager-undo-redo-after-garbage-collection.html (0 => 240476)


--- trunk/LayoutTests/editing/undo-manager/undo-manager-undo-redo-after-garbage-collection.html	                        (rev 0)
+++ trunk/LayoutTests/editing/undo-manager/undo-manager-undo-redo-after-garbage-collection.html	2019-01-25 17:23:06 UTC (rev 240476)
@@ -0,0 +1,35 @@
+<!DOCTYPE html> <!-- webkit-test-runner [ enableUndoManagerAPI=true ] -->
+<html>
+    <meta charset="utf8">
+    <head>
+        <script src=""
+        <script src=""
+        <script>
+        function runTest() {
+            description("Verifies that undo and redo callback handlers survive garbage collection. This test requires WebKitTestRunner.");
+
+            text = document.getElementById("text");
+
+            document.undoManager.addItem(new UndoItem({
+                label: "Test action",
+                undo: () => text.textContent = "After undo",
+                redo: () => text.textContent = "After redo"
+            }));
+
+            if (window.GCController)
+                GCController.collect();
+
+            document.execCommand("Undo");
+            shouldBeEqualToString("text.textContent", "After undo");
+
+            document.execCommand("Redo");
+            shouldBeEqualToString("text.textContent", "After redo");
+        }
+        </script>
+    </head>
+    <body _onload_="runTest()">
+        <div id="text">Initial state</div>
+        <pre id="description"></pre>
+        <pre id="console"></pre>
+    </body>
+</html>

Modified: trunk/LayoutTests/resources/ui-helper.js (240475 => 240476)


--- trunk/LayoutTests/resources/ui-helper.js	2019-01-25 16:50:18 UTC (rev 240475)
+++ trunk/LayoutTests/resources/ui-helper.js	2019-01-25 17:23:06 UTC (rev 240476)
@@ -566,4 +566,13 @@
             resolve({ x: offsetX, y: offsetY });
         }));
     }
+
+    static undoAndRedoLabels()
+    {
+        if (!this.isWebKit2())
+            return Promise.resolve();
+
+        const script = "JSON.stringify([uiController.lastUndoLabel, uiController.firstRedoLabel])";
+        return new Promise(resolve => testRunner.runUIScript(script, result => resolve(JSON.parse(result))));
+    }
 }

Modified: trunk/Source/WebCore/ChangeLog (240475 => 240476)


--- trunk/Source/WebCore/ChangeLog	2019-01-25 16:50:18 UTC (rev 240475)
+++ trunk/Source/WebCore/ChangeLog	2019-01-25 17:23:06 UTC (rev 240476)
@@ -1,3 +1,46 @@
+2019-01-25  Wenson Hsieh  <[email protected]>
+
+        Need a way for _javascript_ (or bundle) code to participate in undo
+        https://bugs.webkit.org/show_bug.cgi?id=190009
+        <rdar://problem/44807048>
+
+        Reviewed by Ryosuke Niwa.
+
+        Finish hooking up `UndoManager::addItems()` to CustomUndoStep.
+
+        Tests: editing/undo-manager/undo-manager-add-item-exceptions.html
+               editing/undo-manager/undo-manager-add-item.html
+               editing/undo-manager/undo-manager-delete-stale-undo-items.html
+               editing/undo-manager/undo-manager-item-labels.html
+               editing/undo-manager/undo-manager-undo-redo-after-garbage-collection.html
+
+        * editing/CompositeEditCommand.h:
+        * editing/CustomUndoStep.cpp:
+        (WebCore::CustomUndoStep::didRemoveFromUndoManager):
+
+        Add a method to invalidate CustomUndoStep. This clears out the pointer to the undo item, and also invalidates
+        the UndoItem, removing it from its UndoManager.
+
+        * editing/CustomUndoStep.h:
+        * editing/Editor.cpp:
+        (WebCore::Editor::registerCustomUndoStep):
+
+        Add a helper method to register a CustomUndoStep as a platform undoable step.
+
+        * editing/Editor.h:
+        * editing/UndoStep.h:
+        * page/UndoItem.h:
+        (WebCore::UndoItem::undoManager const):
+        * page/UndoManager.cpp:
+        (WebCore::UndoManager::addItem):
+
+        Create a CustomUndoStep with the given UndoItem, and register it with the platform undo manager.
+
+        * page/UndoManager.h:
+        * page/UndoManager.idl:
+
+        Mark addItem() as capable of throwing exceptions.
+
 2019-01-25  Zalan Bujtas  <[email protected]>
 
         [LFC][BFC][MarginCollapsing] Add "clear" to static position computation.

Modified: trunk/Source/WebCore/editing/CompositeEditCommand.h (240475 => 240476)


--- trunk/Source/WebCore/editing/CompositeEditCommand.h	2019-01-25 16:50:18 UTC (rev 240475)
+++ trunk/Source/WebCore/editing/CompositeEditCommand.h	2019-01-25 17:23:06 UTC (rev 240476)
@@ -89,6 +89,7 @@
     EditCommandComposition(Document&, const VisibleSelection& startingSelection, const VisibleSelection& endingSelection, EditAction);
 
     String label() const final;
+    void didRemoveFromUndoManager() final { }
 
     RefPtr<Document> m_document;
     VisibleSelection m_startingSelection;

Modified: trunk/Source/WebCore/editing/CustomUndoStep.cpp (240475 => 240476)


--- trunk/Source/WebCore/editing/CustomUndoStep.cpp	2019-01-25 16:50:18 UTC (rev 240475)
+++ trunk/Source/WebCore/editing/CustomUndoStep.cpp	2019-01-25 17:23:06 UTC (rev 240476)
@@ -74,4 +74,10 @@
     return m_undoItem->label();
 }
 
+void CustomUndoStep::didRemoveFromUndoManager()
+{
+    if (auto undoItem = std::exchange(m_undoItem, nullptr))
+        undoItem->invalidate();
+}
+
 } // namespace WebCore

Modified: trunk/Source/WebCore/editing/CustomUndoStep.h (240475 => 240476)


--- trunk/Source/WebCore/editing/CustomUndoStep.h	2019-01-25 16:50:18 UTC (rev 240475)
+++ trunk/Source/WebCore/editing/CustomUndoStep.h	2019-01-25 17:23:06 UTC (rev 240476)
@@ -49,6 +49,7 @@
     EditAction editingAction() const final { return EditAction::Unspecified; }
     String label() const final;
 
+    void didRemoveFromUndoManager() final;
     bool isValid() const;
 
     WeakPtr<UndoItem> m_undoItem;

Modified: trunk/Source/WebCore/editing/Editor.cpp (240475 => 240476)


--- trunk/Source/WebCore/editing/Editor.cpp	2019-01-25 16:50:18 UTC (rev 240475)
+++ trunk/Source/WebCore/editing/Editor.cpp	2019-01-25 17:23:06 UTC (rev 240476)
@@ -39,6 +39,7 @@
 #include "ClipboardEvent.h"
 #include "CompositionEvent.h"
 #include "CreateLinkCommand.h"
+#include "CustomUndoStep.h"
 #include "DataTransfer.h"
 #include "DeleteSelectionCommand.h"
 #include "DictationAlternative.h"
@@ -1740,6 +1741,13 @@
         client()->redo();
 }
 
+void Editor::registerCustomUndoStep(Ref<CustomUndoStep>&& undoStep)
+{
+    ASSERT(RuntimeEnabledFeatures::sharedFeatures().undoManagerAPIEnabled());
+    if (auto* client = this->client())
+        client->registerUndoStep(WTFMove(undoStep));
+}
+
 void Editor::didBeginEditing()
 {
     if (client())

Modified: trunk/Source/WebCore/editing/Editor.h (240475 => 240476)


--- trunk/Source/WebCore/editing/Editor.h	2019-01-25 16:50:18 UTC (rev 240475)
+++ trunk/Source/WebCore/editing/Editor.h	2019-01-25 17:23:06 UTC (rev 240476)
@@ -58,6 +58,7 @@
 class ArchiveResource;
 class DataTransfer;
 class CompositeEditCommand;
+class CustomUndoStep;
 class DeleteButtonController;
 class EditCommand;
 class EditCommandComposition;
@@ -324,6 +325,8 @@
     bool canRedo() const;
     void redo();
 
+    void registerCustomUndoStep(Ref<CustomUndoStep>&&);
+
     void didBeginEditing();
     void didEndEditing();
     void willWriteSelectionToPasteboard(Range*);

Modified: trunk/Source/WebCore/editing/UndoStep.h (240475 => 240476)


--- trunk/Source/WebCore/editing/UndoStep.h	2019-01-25 16:50:18 UTC (rev 240475)
+++ trunk/Source/WebCore/editing/UndoStep.h	2019-01-25 17:23:06 UTC (rev 240476)
@@ -44,6 +44,7 @@
     virtual void reapply() = 0;
     virtual EditAction editingAction() const = 0;
     virtual String label() const = 0;
+    virtual void didRemoveFromUndoManager() = 0;
 };
 
 } // namespace WebCore

Modified: trunk/Source/WebCore/page/UndoItem.h (240475 => 240476)


--- trunk/Source/WebCore/page/UndoItem.h	2019-01-25 16:50:18 UTC (rev 240475)
+++ trunk/Source/WebCore/page/UndoItem.h	2019-01-25 17:23:06 UTC (rev 240476)
@@ -55,6 +55,7 @@
 
     Document* document() const;
 
+    UndoManager* undoManager() const { return m_undoManager.get(); }
     void setUndoManager(UndoManager*);
 
     const String& label() const { return m_label; }

Modified: trunk/Source/WebCore/page/UndoManager.cpp (240475 => 240476)


--- trunk/Source/WebCore/page/UndoManager.cpp	2019-01-25 16:50:18 UTC (rev 240475)
+++ trunk/Source/WebCore/page/UndoManager.cpp	2019-01-25 17:23:06 UTC (rev 240476)
@@ -26,6 +26,8 @@
 #include "config.h"
 #include "UndoManager.h"
 
+#include "CustomUndoStep.h"
+#include "Frame.h"
 #include "UndoItem.h"
 #include <wtf/IsoMallocInlines.h>
 
@@ -40,12 +42,19 @@
 
 UndoManager::~UndoManager() = default;
 
-void UndoManager::addItem(Ref<UndoItem>&& item)
+ExceptionOr<void> UndoManager::addItem(Ref<UndoItem>&& item)
 {
-    UNUSED_PARAM(m_document);
+    if (item->undoManager())
+        return Exception { InvalidModificationError, "This item has already been added to an UndoManager"_s };
 
+    auto frame = makeRefPtr(m_document.frame());
+    if (!frame)
+        return Exception { SecurityError, "A browsing context is required to add an UndoItem"_s };
+
     item->setUndoManager(this);
+    frame->editor().registerCustomUndoStep(CustomUndoStep::create(item));
     m_items.add(WTFMove(item));
+    return { };
 }
 
 void UndoManager::removeItem(UndoItem& item)

Modified: trunk/Source/WebCore/page/UndoManager.h (240475 => 240476)


--- trunk/Source/WebCore/page/UndoManager.h	2019-01-25 16:50:18 UTC (rev 240475)
+++ trunk/Source/WebCore/page/UndoManager.h	2019-01-25 17:23:06 UTC (rev 240476)
@@ -25,6 +25,7 @@
 
 #pragma once
 
+#include "ExceptionOr.h"
 #include <wtf/IsoMalloc.h>
 #include <wtf/Ref.h>
 #include <wtf/RefCounted.h>
@@ -48,7 +49,7 @@
 
     void removeItem(UndoItem&);
     void removeAllItems();
-    void addItem(Ref<UndoItem>&&);
+    ExceptionOr<void> addItem(Ref<UndoItem>&&);
     Document& document() { return m_document; }
 
 private:

Modified: trunk/Source/WebCore/page/UndoManager.idl (240475 => 240476)


--- trunk/Source/WebCore/page/UndoManager.idl	2019-01-25 16:50:18 UTC (rev 240475)
+++ trunk/Source/WebCore/page/UndoManager.idl	2019-01-25 17:23:06 UTC (rev 240476)
@@ -28,5 +28,5 @@
     ImplementationLacksVTable,
     GenerateIsReachable=ImplDocument,
 ] interface UndoManager {
-    void addItem(UndoItem item);
+    [MayThrowException] void addItem(UndoItem item);
 };

Modified: trunk/Source/WebKit/ChangeLog (240475 => 240476)


--- trunk/Source/WebKit/ChangeLog	2019-01-25 16:50:18 UTC (rev 240475)
+++ trunk/Source/WebKit/ChangeLog	2019-01-25 17:23:06 UTC (rev 240476)
@@ -1,3 +1,21 @@
+2019-01-25  Wenson Hsieh  <[email protected]>
+
+        Need a way for _javascript_ (or bundle) code to participate in undo
+        https://bugs.webkit.org/show_bug.cgi?id=190009
+        <rdar://problem/44807048>
+
+        Reviewed by Ryosuke Niwa.
+
+        Invalidate undo steps when removing them from WebPage. Invalidation is a no-op for editing actions that come
+        from the UA, but for custom undo steps backed by an UndoItem, we clear out the custom undo step's pointer to its
+        UndoItem and additionally disconnect the UndoItem from its UndoManager.
+
+        * WebProcess/WebPage/WebPage.cpp:
+        (WebKit::WebPage::addWebUndoStep):
+        (WebKit::WebPage::removeWebEditCommand):
+        * WebProcess/WebPage/WebUndoStep.h:
+        (WebKit::WebUndoStep::invalidate):
+
 2019-01-25  Patrick Griffis  <[email protected]>
 
         [GTK][WPE] Add API to add paths to sandbox

Modified: trunk/Source/WebKit/WebProcess/WebPage/WebPage.cpp (240475 => 240476)


--- trunk/Source/WebKit/WebProcess/WebPage/WebPage.cpp	2019-01-25 16:50:18 UTC (rev 240475)
+++ trunk/Source/WebKit/WebProcess/WebPage/WebPage.cpp	2019-01-25 17:23:06 UTC (rev 240476)
@@ -3791,12 +3791,14 @@
 
 void WebPage::addWebUndoStep(WebUndoStepID stepID, Ref<WebUndoStep>&& entry)
 {
-    m_undoStepMap.set(stepID, WTFMove(entry));
+    auto addResult = m_undoStepMap.set(stepID, WTFMove(entry));
+    ASSERT_UNUSED(addResult, addResult.isNewEntry);
 }
 
 void WebPage::removeWebEditCommand(WebUndoStepID stepID)
 {
-    m_undoStepMap.remove(stepID);
+    if (auto undoStep = m_undoStepMap.take(stepID))
+        undoStep->didRemoveFromUndoManager();
 }
 
 bool WebPage::isAlwaysOnLoggingAllowed() const

Modified: trunk/Source/WebKit/WebProcess/WebPage/WebUndoStep.h (240475 => 240476)


--- trunk/Source/WebKit/WebProcess/WebPage/WebUndoStep.h	2019-01-25 16:50:18 UTC (rev 240475)
+++ trunk/Source/WebKit/WebProcess/WebPage/WebUndoStep.h	2019-01-25 17:23:06 UTC (rev 240476)
@@ -39,6 +39,8 @@
     WebCore::UndoStep& step() const { return m_step.get(); }
     WebUndoStepID stepID() const { return m_stepID; }
 
+    void didRemoveFromUndoManager() { m_step->didRemoveFromUndoManager(); }
+
 private:
     WebUndoStep(Ref<WebCore::UndoStep>&& step, WebUndoStepID stepID)
         : m_step(WTFMove(step))

Modified: trunk/Tools/ChangeLog (240475 => 240476)


--- trunk/Tools/ChangeLog	2019-01-25 16:50:18 UTC (rev 240475)
+++ trunk/Tools/ChangeLog	2019-01-25 17:23:06 UTC (rev 240476)
@@ -1,3 +1,35 @@
+2019-01-25  Wenson Hsieh  <[email protected]>
+
+        Need a way for _javascript_ (or bundle) code to participate in undo
+        https://bugs.webkit.org/show_bug.cgi?id=190009
+        <rdar://problem/44807048>
+
+        Reviewed by Ryosuke Niwa.
+
+        Add UIScriptController helpers to grab the platform undo and redo action labels. Currently only implemented for
+        Cocoa platforms in WebKit2. See other ChangeLogs for more detail.
+
+        * DumpRenderTree/ios/UIScriptControllerIOS.mm:
+        (WTR::UIScriptController::lastUndoLabel const):
+        (WTR::UIScriptController::firstRedoLabel const):
+        (WTR::UIScriptController::platformUndoManager const):
+        * DumpRenderTree/mac/UIScriptControllerMac.mm:
+        (WTR::UIScriptController::lastUndoLabel const):
+        (WTR::UIScriptController::firstRedoLabel const):
+        (WTR::UIScriptController::platformUndoManager const):
+        * TestRunnerShared/UIScriptContext/Bindings/UIScriptController.idl:
+        * TestRunnerShared/UIScriptContext/UIScriptController.cpp:
+        (WTR::UIScriptController::lastUndoLabel const):
+        (WTR::UIScriptController::firstRedoLabel const):
+        * TestRunnerShared/UIScriptContext/UIScriptController.h:
+        * WebKitTestRunner/UIScriptControllerCocoa.mm:
+        (WTR::UIScriptController::lastUndoLabel const):
+        (WTR::UIScriptController::firstRedoLabel const):
+        * WebKitTestRunner/ios/UIScriptControllerIOS.mm:
+        (WTR::UIScriptController::platformUndoManager const):
+        * WebKitTestRunner/mac/UIScriptControllerMac.mm:
+        (WTR::UIScriptController::platformUndoManager const):
+
 2019-01-25  Zalan Bujtas  <[email protected]>
 
         [LFC][BFC][MarginCollapsing] Add "clear" to static position computation.

Modified: trunk/Tools/DumpRenderTree/ios/UIScriptControllerIOS.mm (240475 => 240476)


--- trunk/Tools/DumpRenderTree/ios/UIScriptControllerIOS.mm	2019-01-25 16:50:18 UTC (rev 240475)
+++ trunk/Tools/DumpRenderTree/ios/UIScriptControllerIOS.mm	2019-01-25 17:23:06 UTC (rev 240476)
@@ -450,6 +450,21 @@
 {
 }
 
+JSRetainPtr<JSStringRef> UIScriptController::lastUndoLabel() const
+{
+    return nullptr;
 }
 
+JSRetainPtr<JSStringRef> UIScriptController::firstRedoLabel() const
+{
+    return nullptr;
+}
+
+NSUndoManager *UIScriptController::platformUndoManager() const
+{
+    return nil;
+}
+
+}
+
 #endif // PLATFORM(IOS_FAMILY)

Modified: trunk/Tools/DumpRenderTree/mac/UIScriptControllerMac.mm (240475 => 240476)


--- trunk/Tools/DumpRenderTree/mac/UIScriptControllerMac.mm	2019-01-25 16:50:18 UTC (rev 240475)
+++ trunk/Tools/DumpRenderTree/mac/UIScriptControllerMac.mm	2019-01-25 17:23:06 UTC (rev 240476)
@@ -215,6 +215,21 @@
     doAsyncTask(callback);
 }
 
+JSRetainPtr<JSStringRef> UIScriptController::lastUndoLabel() const
+{
+    return nullptr;
 }
 
+JSRetainPtr<JSStringRef> UIScriptController::firstRedoLabel() const
+{
+    return nullptr;
+}
+
+NSUndoManager *UIScriptController::platformUndoManager() const
+{
+    return nil;
+}
+
+}
+
 #endif // PLATFORM(MAC)

Modified: trunk/Tools/TestRunnerShared/UIScriptContext/Bindings/UIScriptController.idl (240475 => 240476)


--- trunk/Tools/TestRunnerShared/UIScriptContext/Bindings/UIScriptController.idl	2019-01-25 16:50:18 UTC (rev 240475)
+++ trunk/Tools/TestRunnerShared/UIScriptContext/Bindings/UIScriptController.idl	2019-01-25 17:23:06 UTC (rev 240476)
@@ -290,8 +290,11 @@
     void makeWindowContentViewFirstResponder();
     readonly attribute boolean isWindowContentViewFirstResponder;
 
+    object attachmentInfo(DOMString attachmentIdentifier);
+
+    // Editing
     void drawSquareInEditableImage();
     readonly attribute long numberOfStrokesInEditableImage;
-
-    object attachmentInfo(DOMString attachmentIdentifier);
+    readonly attribute DOMString lastUndoLabel;
+    readonly attribute DOMString firstRedoLabel;
 };

Modified: trunk/Tools/TestRunnerShared/UIScriptContext/UIScriptController.cpp (240475 => 240476)


--- trunk/Tools/TestRunnerShared/UIScriptContext/UIScriptController.cpp	2019-01-25 16:50:18 UTC (rev 240475)
+++ trunk/Tools/TestRunnerShared/UIScriptContext/UIScriptController.cpp	2019-01-25 17:23:06 UTC (rev 240476)
@@ -569,6 +569,16 @@
 {
 }
 
+JSRetainPtr<JSStringRef> UIScriptController::lastUndoLabel() const
+{
+    return nullptr;
+}
+
+JSRetainPtr<JSStringRef> UIScriptController::firstRedoLabel() const
+{
+    return nullptr;
+}
+
 #endif // !PLATFORM(COCOA)
 
 #if !PLATFORM(MAC)

Modified: trunk/Tools/TestRunnerShared/UIScriptContext/UIScriptController.h (240475 => 240476)


--- trunk/Tools/TestRunnerShared/UIScriptContext/UIScriptController.h	2019-01-25 16:50:18 UTC (rev 240475)
+++ trunk/Tools/TestRunnerShared/UIScriptContext/UIScriptController.h	2019-01-25 17:23:06 UTC (rev 240476)
@@ -31,6 +31,8 @@
 #include <wtf/Optional.h>
 #include <wtf/Ref.h>
 
+OBJC_CLASS NSUndoManager;
+
 namespace WebCore {
 class FloatRect;
 }
@@ -206,6 +208,9 @@
     void drawSquareInEditableImage();
     long numberOfStrokesInEditableImage();
 
+    JSRetainPtr<JSStringRef> lastUndoLabel() const;
+    JSRetainPtr<JSStringRef> firstRedoLabel() const;
+
     JSObjectRef attachmentInfo(JSStringRef attachmentIdentifier);
 
 private:
@@ -225,6 +230,10 @@
     void platformClearAllCallbacks();
     void platformPlayBackEventStream(JSStringRef, JSValueRef);
 
+#if PLATFORM(COCOA)
+    NSUndoManager *platformUndoManager() const;
+#endif
+
     JSClassRef wrapperClass() final;
 
     JSObjectRef objectFromRect(const WebCore::FloatRect&) const;

Modified: trunk/Tools/WebKitTestRunner/UIScriptControllerCocoa.mm (240475 => 240476)


--- trunk/Tools/WebKitTestRunner/UIScriptControllerCocoa.mm	2019-01-25 16:50:18 UTC (rev 240475)
+++ trunk/Tools/WebKitTestRunner/UIScriptControllerCocoa.mm	2019-01-25 17:23:06 UTC (rev 240476)
@@ -192,5 +192,15 @@
     return nullptr;
 #endif
 }
-    
+
+JSRetainPtr<JSStringRef> UIScriptController::lastUndoLabel() const
+{
+    return JSStringCreateWithCFString((__bridge CFStringRef)platformUndoManager().undoActionName);
+}
+
+JSRetainPtr<JSStringRef> UIScriptController::firstRedoLabel() const
+{
+    return JSStringCreateWithCFString((__bridge CFStringRef)platformUndoManager().redoActionName);
+}
+
 } // namespace WTR

Modified: trunk/Tools/WebKitTestRunner/ios/UIScriptControllerIOS.mm (240475 => 240476)


--- trunk/Tools/WebKitTestRunner/ios/UIScriptControllerIOS.mm	2019-01-25 16:50:18 UTC (rev 240475)
+++ trunk/Tools/WebKitTestRunner/ios/UIScriptControllerIOS.mm	2019-01-25 17:23:06 UTC (rev 240476)
@@ -971,6 +971,11 @@
     return JSValueToObject(m_context->jsContext(), [JSValue valueWithObject:attachmentInfoDictionary inContext:[JSContext contextWithJSGlobalContextRef:m_context->jsContext()]].JSValueRef, nullptr);
 }
 
+NSUndoManager *UIScriptController::platformUndoManager() const
+{
+    return [(UIView *)[TestController::singleton().mainWebView()->platformView() valueForKeyPath:@"_currentContentView"] undoManager];
 }
 
+}
+
 #endif // PLATFORM(IOS_FAMILY)

Modified: trunk/Tools/WebKitTestRunner/mac/UIScriptControllerMac.mm (240475 => 240476)


--- trunk/Tools/WebKitTestRunner/mac/UIScriptControllerMac.mm	2019-01-25 16:50:18 UTC (rev 240475)
+++ trunk/Tools/WebKitTestRunner/mac/UIScriptControllerMac.mm	2019-01-25 17:23:06 UTC (rev 240476)
@@ -209,4 +209,9 @@
     doAsyncTask(callback);
 }
 
+NSUndoManager *UIScriptController::platformUndoManager() const
+{
+    return TestController::singleton().mainWebView()->platformView().undoManager;
+}
+
 } // namespace WTR
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to