Skip to content

Commit

Permalink
[Writing Suggestions] Prediction sometimes appears at the end of the …
Browse files Browse the repository at this point in the history
…document in Mail compose

https://bugs.webkit.org/show_bug.cgi?id=274223
rdar://128109513

Reviewed by Richard Robinson.

Writing suggestions are still sometimes inserted in the wrong place in the render tree. This happens
under these conditions:

1.  The text node before the caret selection is inside of an anonymous renderer.
2.  There are one or more renderers that come after the text node's renderer in tree order, that are
    descendants of the element that contains the text node.

This is one concrete example, wherein an anonymous block-level renderer is added to contain a single
text node directly underneath the `body`:

```
<body renderer>
    <anonymous renderer>
        <text renderer> // this is the node before suggestions
        * // writing suggestions are supposed to be inserted here
    <any other renderer>
    …
    * // writing suggestions are inserted here instead
```

To fix this, we simply adjust the logic in `updateWritingSuggestionsRenderer` to insert the
generated writing suggestions renderer inside of the text node's parent, such that it's always
the next sibling of the text node.

* LayoutTests/editing/input/mac/writing-suggestions-in-anonymous-renderer-expected.html: Added.
* LayoutTests/editing/input/mac/writing-suggestions-in-anonymous-renderer.html: Added.

Add another layout test to exercise this change.

* Source/WebCore/rendering/updating/RenderTreeUpdaterGeneratedContent.cpp:
(WebCore::RenderTreeUpdater::GeneratedContent::updateWritingSuggestionsRenderer):

Canonical link: https://commits.webkit.org/278833@main
  • Loading branch information
whsieh committed May 15, 2024
1 parent d957a61 commit 316776c
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 13 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<!DOCTYPE html>
<html>
<head>
<script src="../../../resources/ui-helper.js"></script>
<style>
body {
font-size: 16px;
width: 300px;
height: 300px;
caret-color: transparent;
}
</style>
<script>
addEventListener("load", async () => {
getSelection().setPosition(document.body.childNodes[0], 4);
if (!window.testRunner)
return;
testRunner.waitUntilDone();
await UIHelper.setInlinePrediction("Hello", 4);
await UIHelper.ensurePresentationUpdate();
testRunner.notifyDone();
});
</script>
</head>
<body contenteditable>Hell<div></div></body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<!DOCTYPE html>
<html>
<head>
<script src="../../../resources/ui-helper.js"></script>
<style>
body {
font-size: 16px;
width: 300px;
height: 300px;
caret-color: transparent;
}
</style>
<script>
addEventListener("load", async () => {
getSelection().setPosition(document.body.childNodes[0], 1);
if (!window.testRunner)
return;
testRunner.waitUntilDone();
await UIHelper.setInlinePrediction("Hello", 4);
await UIHelper.ensurePresentationUpdate();
testRunner.notifyDone();
});
</script>
</head>
<body contenteditable><span>Hell</span><div></div></body>
</html>
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,12 @@ void RenderTreeUpdater::GeneratedContent::updateWritingSuggestionsRenderer(Rende
return;
}

CheckedPtr parentForWritingSuggestions = nodeBeforeWritingSuggestionsTextRenderer->parent();
if (!parentForWritingSuggestions) {
destroyWritingSuggestionsIfNeeded();
return;
}

auto textWithoutSuggestion = nodeBeforeWritingSuggestionsTextRenderer->text();

auto offset = writingSuggestionData->offset();
Expand Down Expand Up @@ -345,23 +351,13 @@ void RenderTreeUpdater::GeneratedContent::updateWritingSuggestionsRenderer(Rende
auto newWritingSuggestionsRenderer = WebCore::createRenderer<RenderInline>(RenderObject::Type::Inline, renderer.document(), WTFMove(newStyle));
newWritingSuggestionsRenderer->initializeStyle();

auto rendererAfterWritingSuggestions = [&]() -> CheckedPtr<RenderObject> {
CheckedPtr rendererBefore = nodeBeforeWritingSuggestions->renderer();
if (!rendererBefore || rendererBefore->parent() != &renderer)
return nullptr;

CheckedPtr rendererAfter = rendererBefore->nextSibling();
if (!rendererAfter || rendererAfter->parent() != &renderer)
return nullptr;

return rendererAfter;
}();
CheckedPtr rendererAfterWritingSuggestions = nodeBeforeWritingSuggestionsTextRenderer->nextSibling();

auto writingSuggestionsText = WebCore::createRenderer<RenderText>(RenderObject::Type::Text, renderer.document(), writingSuggestionData->content());
m_updater.m_builder.attach(*newWritingSuggestionsRenderer, WTFMove(writingSuggestionsText));

editor.setWritingSuggestionRenderer(*newWritingSuggestionsRenderer.get());
m_updater.m_builder.attach(renderer, WTFMove(newWritingSuggestionsRenderer), rendererAfterWritingSuggestions.get());
m_updater.m_builder.attach(*parentForWritingSuggestions, WTFMove(newWritingSuggestionsRenderer), rendererAfterWritingSuggestions.get());

auto* prefixNode = nodeBeforeWritingSuggestionsTextRenderer->textNode();
if (!prefixNode) {
Expand All @@ -372,7 +368,7 @@ void RenderTreeUpdater::GeneratedContent::updateWritingSuggestionsRenderer(Rende

if (!suffix.isEmpty()) {
auto suffixRenderer = WebCore::createRenderer<RenderText>(RenderObject::Type::Text, *prefixNode, suffix);
m_updater.m_builder.attach(renderer, WTFMove(suffixRenderer), rendererAfterWritingSuggestions.get());
m_updater.m_builder.attach(*parentForWritingSuggestions, WTFMove(suffixRenderer), rendererAfterWritingSuggestions.get());
}
}
}
Expand Down

0 comments on commit 316776c

Please sign in to comment.