Title: [254314] trunk
Revision
254314
Author
wenson_hs...@apple.com
Date
2020-01-09 16:40:14 -0800 (Thu, 09 Jan 2020)

Log Message

Text manipulation controller should not observe changes in new replacement elements
https://bugs.webkit.org/show_bug.cgi?id=206015
<rdar://problem/58353667>

Reviewed by Tim Horton.

Source/WebCore:

TextManipulationController may insert elements in the process of completing text replacement operations. When
renderers are created for these elements (after the next layout pass), the controller is notified via
TextManipulationController::didCreateRendererForElement, which causes it to begin observing the newly inserted
elements. For certain clients, this may lead to an unending cycle of text manipulation updates as the new
text manipulation items' tokens will be replaced with new tokens, and we never reach a stable state.

To mitigate this, we avoid adding newly visible elements to m_mutatedElements in the case where the newly
visible elements were recently inserted by text replacement. See below for more details.

Test: TextManipulation.CompleteTextManipulationDoesNotCreateMoreTextManipulationItems

* editing/TextManipulationController.cpp:
(WebCore::TextManipulationController::didCreateRendererForElement):

Avoid considering an element that has a new renderer, if it is an element we had just inserted using text
manipulation APIs.

(WebCore::TextManipulationController::replace):

As we iterate over and apply each text replacement, remember the elements we've inserted using a WeakHashSet;
this set is cleared in a queued async task, after which layout should be up to date.

* editing/TextManipulationController.h:

Tools:

* TestWebKitAPI/Tests/WebKitCocoa/TextManipulation.mm:

Add a new API test to verify that we don't fire text manipulation item callbacks due to inserting elements when
completing text manipulation.

(-[TextManipulationDelegate initWithItemCallback]): Deleted.

Remove a stray initializer that was unused (and also doesn't take an ItemCallback, as its name might suggest).

Modified Paths

Diff

Modified: trunk/Source/WebCore/ChangeLog (254313 => 254314)


--- trunk/Source/WebCore/ChangeLog	2020-01-10 00:19:26 UTC (rev 254313)
+++ trunk/Source/WebCore/ChangeLog	2020-01-10 00:40:14 UTC (rev 254314)
@@ -1,3 +1,35 @@
+2020-01-09  Wenson Hsieh  <wenson_hs...@apple.com>
+
+        Text manipulation controller should not observe changes in new replacement elements
+        https://bugs.webkit.org/show_bug.cgi?id=206015
+        <rdar://problem/58353667>
+
+        Reviewed by Tim Horton.
+
+        TextManipulationController may insert elements in the process of completing text replacement operations. When
+        renderers are created for these elements (after the next layout pass), the controller is notified via
+        TextManipulationController::didCreateRendererForElement, which causes it to begin observing the newly inserted
+        elements. For certain clients, this may lead to an unending cycle of text manipulation updates as the new
+        text manipulation items' tokens will be replaced with new tokens, and we never reach a stable state.
+
+        To mitigate this, we avoid adding newly visible elements to m_mutatedElements in the case where the newly
+        visible elements were recently inserted by text replacement. See below for more details.
+
+        Test: TextManipulation.CompleteTextManipulationDoesNotCreateMoreTextManipulationItems
+
+        * editing/TextManipulationController.cpp:
+        (WebCore::TextManipulationController::didCreateRendererForElement):
+
+        Avoid considering an element that has a new renderer, if it is an element we had just inserted using text
+        manipulation APIs.
+
+        (WebCore::TextManipulationController::replace):
+
+        As we iterate over and apply each text replacement, remember the elements we've inserted using a WeakHashSet;
+        this set is cleared in a queued async task, after which layout should be up to date.
+
+        * editing/TextManipulationController.h:
+
 2020-01-09  Brent Fulgham  <bfulg...@apple.com>
 
         REGRESSION (r253662): Large Data URLs are not being handled properly

Modified: trunk/Source/WebCore/editing/TextManipulationController.cpp (254313 => 254314)


--- trunk/Source/WebCore/editing/TextManipulationController.cpp	2020-01-10 00:19:26 UTC (rev 254313)
+++ trunk/Source/WebCore/editing/TextManipulationController.cpp	2020-01-10 00:40:14 UTC (rev 254314)
@@ -178,6 +178,9 @@
 
 void TextManipulationController::didCreateRendererForElement(Element& element)
 {
+    if (m_recentlyInsertedElements.contains(element))
+        return;
+
     if (m_mutatedElements.computesEmpty())
         scheduleObservartionUpdate();
 
@@ -398,7 +401,13 @@
             insertionPoint.containerNode()->insertBefore(insertion.child, insertionPoint.computeNodeBeforePosition());
         else
             insertion.parentIfDifferentFromCommonAncestor->appendChild(insertion.child);
+        if (is<Element>(insertion.child.get()))
+            m_recentlyInsertedElements.add(downcast<Element>(insertion.child.get()));
     }
+    m_document->eventLoop().queueTask(TaskSource::InternalAsyncTask, [weakThis = makeWeakPtr(*this)] {
+        if (auto strongThis = weakThis.get())
+            strongThis->m_recentlyInsertedElements.clear();
+    });
 
     return ManipulationResult::Success;
 }

Modified: trunk/Source/WebCore/editing/TextManipulationController.h (254313 => 254314)


--- trunk/Source/WebCore/editing/TextManipulationController.h	2020-01-10 00:19:26 UTC (rev 254313)
+++ trunk/Source/WebCore/editing/TextManipulationController.h	2020-01-10 00:40:14 UTC (rev 254314)
@@ -122,6 +122,7 @@
 
     WeakPtr<Document> m_document;
     WeakHashSet<Element> m_mutatedElements;
+    WeakHashSet<Element> m_recentlyInsertedElements;
     ManipulationItemCallback m_callback;
     Vector<ExclusionRule> m_exclusionRules;
     HashMap<ItemIdentifier, ManipulationItem> m_items;

Modified: trunk/Tools/ChangeLog (254313 => 254314)


--- trunk/Tools/ChangeLog	2020-01-10 00:19:26 UTC (rev 254313)
+++ trunk/Tools/ChangeLog	2020-01-10 00:40:14 UTC (rev 254314)
@@ -1,3 +1,20 @@
+2020-01-09  Wenson Hsieh  <wenson_hs...@apple.com>
+
+        Text manipulation controller should not observe changes in new replacement elements
+        https://bugs.webkit.org/show_bug.cgi?id=206015
+        <rdar://problem/58353667>
+
+        Reviewed by Tim Horton.
+
+        * TestWebKitAPI/Tests/WebKitCocoa/TextManipulation.mm:
+
+        Add a new API test to verify that we don't fire text manipulation item callbacks due to inserting elements when
+        completing text manipulation.
+
+        (-[TextManipulationDelegate initWithItemCallback]): Deleted.
+
+        Remove a stray initializer that was unused (and also doesn't take an ItemCallback, as its name might suggest).
+
 2020-01-09  Ryan Haddad  <ryanhad...@apple.com>
 
         Move macOS Test262, JSC, and perf queues to Catalina

Modified: trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/TextManipulation.mm (254313 => 254314)


--- trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/TextManipulation.mm	2020-01-10 00:19:26 UTC (rev 254313)
+++ trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/TextManipulation.mm	2020-01-10 00:40:14 UTC (rev 254314)
@@ -62,14 +62,6 @@
     return self;
 }
 
-- (instancetype)initWithItemCallback
-{
-    if (!(self = [super init]))
-        return nil;
-    _items = adoptNS([[NSMutableArray alloc] init]);
-    return self;
-}
-
 - (void)_webView:(WKWebView *)webView didFindTextManipulationItem:(_WKTextManipulationItem *)item
 {
     [_items addObject:item];
@@ -905,6 +897,50 @@
     EXPECT_WK_STREQ("<p>Hello, <em>WebKitten</em></p>", [webView stringByEvaluatingJavaScript:@"document.body.innerHTML"]);
 }
 
+TEST(TextManipulation, CompleteTextManipulationDoesNotCreateMoreTextManipulationItems)
+{
+    auto delegate = adoptNS([[TextManipulationDelegate alloc] init]);
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 400, 400)]);
+    [webView _setTextManipulationDelegate:delegate.get()];
+
+    [webView synchronouslyLoadHTMLString:@"<!DOCTYPE html><html><body><p>Foo <strong>bar</strong> baz</p></body></html>"];
+
+    auto configuration = adoptNS([[_WKTextManipulationConfiguration alloc] init]);
+
+    done = false;
+    [webView _startTextManipulationsWithConfiguration:configuration.get() completion:^{
+        done = true;
+    }];
+    TestWebKitAPI::Util::run(&done);
+
+    auto *items = [delegate items];
+    EXPECT_EQ(items.count, 1UL);
+
+    auto* firstItem = items.firstObject;
+    EXPECT_EQ(firstItem.tokens.count, 3UL);
+
+    __block BOOL foundNewItemAfterCompletingTextManipulation = false;
+    [delegate setItemCallback:^(_WKTextManipulationItem *) {
+        foundNewItemAfterCompletingTextManipulation = true;
+    }];
+
+    done = false;
+    [webView _completeTextManipulation:(_WKTextManipulationItem *)createItem(firstItem.identifier, {
+        { firstItem.tokens[0].identifier, @"bar " },
+        { firstItem.tokens[1].identifier, @"garply" },
+        { firstItem.tokens[2].identifier, @" foo" },
+    }).get() completion:^(BOOL success) {
+        EXPECT_TRUE(success);
+        done = true;
+    }];
+
+    TestWebKitAPI::Util::run(&done);
+    [webView waitForNextPresentationUpdate];
+
+    EXPECT_FALSE(foundNewItemAfterCompletingTextManipulation);
+    EXPECT_WK_STREQ("<p>bar <strong>garply</strong> foo</p>", [webView stringByEvaluatingJavaScript:@"document.body.innerHTML"]);
+}
+
 TEST(TextManipulation, TextManipulationTokenDebugDescription)
 {
     auto token = adoptNS([[_WKTextManipulationToken alloc] init]);
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to