[iOS] ASSERTION FAILURE: !isMissingPostLayoutData in WebKit::EditorState::postLayoutD...
authordbates@webkit.org <dbates@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 25 Mar 2020 16:50:27 +0000 (16:50 +0000)
committerdbates@webkit.org <dbates@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 25 Mar 2020 16:50:27 +0000 (16:50 +0000)
https://bugs.webkit.org/show_bug.cgi?id=199960
<rdar://problem/53323966>

Reviewed by Simon Fraser.

Source/WebKit:

Refactor the computation of editor state so that we can request that a layout be performed
each time we compute the editor state as part of asking the UI process to interpret a key
event. The full (read: after layout) editor state is needed for UIKit to perform a deletion
because UIKit wants to know how many characters are before the selection. Otherwise, we hit
an assert due to the fact the last editor state sent (when the Web process asked the UI process
to interpret the key) is missing layout data.

The refactoring also moves the Cocoa-common code out of the platform-independent WebPage.cpp
file into WebPageCocoa.mm.

One side effect of the refactoring is that we no longer allow the platformEditorState() function
to override the isMissingPostLayoutData bit. Currently it can even though the calling code, the
platform independent code (PIE) in WebPage, may have attached layout data. Now the PIE code sets
this bit if it attached layout data and the platformEditorState() function only attaches more
layout data if that bit is set. platformEditorState() never unsets that bit (i.e. sets isMissingPostLayoutData
to true).

The patch also removes m_isEditorStateMissingPostLayoutData in WebPage.h. This instance variable
has been unused since <https://trac.webkit.org/changeset/221064/webkit>. Also we haven't been using
IncludePostLayoutDataHint::No since the last reference to it was removed in <https://trac.webkit.org/changeset/244494/webkit>.

I also renamed platformEditorState() to getPlatformEditorState() since it has an out argument.

Test: editing/deleting/ios/backspace-last-character.html

* Shared/EditorState.h:
* UIProcess/API/glib/WebKitEditorState.cpp:
(webkitEditorStateCreate): Initialize _WebKitEditorStatePrivate::typingAttributes to WEBKIT_EDITOR_TYPING_ATTRIBUTE_NONE.
* WebProcess/WebPage/Cocoa/WebPageCocoa.mm:
(WebKit::WebPage::getPlatformEditorStateCommon const): Added. Moved Cocoa-common code from WebPage.cpp to here.
* WebProcess/WebPage/WebPage.cpp:
(WebKit::WebPage::editorState const): Move Cocoa-common code to WebPageCocoa.mm. Change enum to
track whether a layout should be performed. Keep the current behavior of only including post layout
data if the frame view does not need a layout. This behavior is encoded in the enumerator ShouldPerformLayout::Default.
which is the default argument value for the argument shouldPerformLayout.
* WebProcess/WebPage/WebPage.h:
(WebKit::WebPage::platformNeedsLayoutForEditorState const): Added. Non-Cocoa port implementation
that returns false.
* WebProcess/WebPage/glib/WebPageGLib.cpp:
(WebKit::WebPage::getPlatformEditorState const): Early return if isMissingPostLayoutData is true.
(WebKit::WebPage::platformEditorState const): Deleted.
* WebProcess/WebPage/ios/WebPageIOS.mm:
(WebKit::WebPage::platformNeedsLayoutForEditorState const): Added. Keep the current behavior of
performing a layout if we have a composition or a hardware keyboard is attached.
(WebKit::WebPage::getPlatformEditorState const): Call platformEditorStateCommon(). Bail out early
if isMissingPostLayoutData is true.
(WebKit::WebPage::handleEditingKeyboardEvent): The important part of this patch. Request a layout
when computing the editor state that we will send to the UI process.
(WebKit::WebPage::platformEditorState const): Deleted.
* WebProcess/WebPage/mac/WebPageMac.mm:
(WebKit::WebPage::getPlatformEditorState const): Call platformEditorStateCommon(). Bail out early
if isMissingPostLayoutData is true.
(WebKit::WebPage::platformEditorState const): Deleted.
* WebProcess/WebPage/playstation/WebPagePlayStation.cpp:
(WebKit::WebPage::getPlatformEditorState const): Update as needed.
(WebKit::WebPage::platformEditorState const): Deleted.
* WebProcess/WebPage/win/WebPageWin.cpp:
(WebKit::WebPage::getPlatformEditorState const): Update as needed.
(WebKit::WebPage::platformEditorState const): Deleted.

LayoutTests:

Add a test to ensure we do not assert when using the software keyboard to type a character
into a <textarea> and then delete it.

* TestExpectations: Skip tests in editing/deleting/ios on all platforms.
* editing/deleting/ios/backspace-last-character-expected.txt: Added.
* editing/deleting/ios/backspace-last-character.html: Added.
* platform/ios/TestExpectations: Unskip tests in editing/deleting/ios on iOS.

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

16 files changed:
LayoutTests/ChangeLog
LayoutTests/TestExpectations
LayoutTests/editing/deleting/ios/backspace-last-character-expected.txt [new file with mode: 0644]
LayoutTests/editing/deleting/ios/backspace-last-character.html [new file with mode: 0644]
LayoutTests/platform/ios/TestExpectations
Source/WebKit/ChangeLog
Source/WebKit/Shared/EditorState.h
Source/WebKit/UIProcess/API/glib/WebKitEditorState.cpp
Source/WebKit/WebProcess/WebPage/Cocoa/WebPageCocoa.mm
Source/WebKit/WebProcess/WebPage/WebPage.cpp
Source/WebKit/WebProcess/WebPage/WebPage.h
Source/WebKit/WebProcess/WebPage/glib/WebPageGLib.cpp
Source/WebKit/WebProcess/WebPage/ios/WebPageIOS.mm
Source/WebKit/WebProcess/WebPage/mac/WebPageMac.mm
Source/WebKit/WebProcess/WebPage/playstation/WebPagePlayStation.cpp
Source/WebKit/WebProcess/WebPage/win/WebPageWin.cpp

index 44cf553..47eea88 100644 (file)
@@ -1,3 +1,19 @@
+2020-03-24  Daniel Bates  <dabates@apple.com>
+
+        [iOS] ASSERTION FAILURE: !isMissingPostLayoutData in WebKit::EditorState::postLayoutData()
+        https://bugs.webkit.org/show_bug.cgi?id=199960
+        <rdar://problem/53323966>
+
+        Reviewed by Simon Fraser.
+
+        Add a test to ensure we do not assert when using the software keyboard to type a character
+        into a <textarea> and then delete it.
+
+        * TestExpectations: Skip tests in editing/deleting/ios on all platforms.
+        * editing/deleting/ios/backspace-last-character-expected.txt: Added.
+        * editing/deleting/ios/backspace-last-character.html: Added.
+        * platform/ios/TestExpectations: Unskip tests in editing/deleting/ios on iOS.
+
 2020-03-25  Simon Fraser  <simon.fraser@apple.com>
 
         REGRESSION (r251385): box-shadow interferes with backdrop-filter
index eee458e..15ef344 100644 (file)
@@ -21,6 +21,7 @@ editing/input/ios [ Skip ]
 editing/find [ Skip ]
 editing/pasteboard/gtk [ Skip ]
 editing/selection/ios [ Skip ]
+editing/deleting/ios [ Skip ]
 editing/undo-manager [ Skip ]
 tiled-drawing [ Skip ]
 fast/css/ios [ Skip ]
diff --git a/LayoutTests/editing/deleting/ios/backspace-last-character-expected.txt b/LayoutTests/editing/deleting/ios/backspace-last-character-expected.txt
new file mode 100644 (file)
index 0000000..50564d9
--- /dev/null
@@ -0,0 +1,11 @@
+This tests using the software keyboard and pressing 'w' then backspace deletes the 'w' and does not cause an assertion failure in a debug build.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS document.getElementById("input").value is "w"
+PASS document.getElementById("input").value is ""
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/editing/deleting/ios/backspace-last-character.html b/LayoutTests/editing/deleting/ios/backspace-last-character.html
new file mode 100644 (file)
index 0000000..acf51f3
--- /dev/null
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src="../../../resources/js-test.js"></script>
+<script src="../../../resources/ui-helper.js"></script>
+</head>
+<body>
+<div id="test-container">
+    <textarea id="input"></textarea>
+    <textarea id="output" readonly></textarea>
+</div>
+<script>
+window.jsTestIsAsync = true;
+
+let input = document.getElementById("input");
+
+function appendANewline()
+{
+    let outputElement = document.getElementById("output");
+    outputElement.value = outputElement.value + "\n";
+}
+
+async function runTest()
+{
+    if (window.testRunner)
+        await UIHelper.setHardwareKeyboardAttached(false);
+
+    await UIHelper.callFunctionAndWaitForEvent(() => window.testRunner && UIHelper.activateElement(input), input, "focus");
+
+    await UIHelper.callFunctionAndWaitForEvent(() => window.testRunner && UIHelper.keyDown("w"), input, "input");
+    shouldBeEqualToString('document.getElementById("input").value', "w");
+
+    await UIHelper.callFunctionAndWaitForEvent(() => window.testRunner && UIHelper.keyDown("\b"), input, "input");
+    shouldBeEqualToString('document.getElementById("input").value', "");
+
+    document.body.removeChild(document.getElementById("test-container"));
+    finishJSTest();
+}
+
+description("This tests using the software keyboard and pressing 'w' then <kbd>backspace</kbd> deletes the 'w' and does not cause an assertion failure in a debug build.")
+input.addEventListener("keydown", appendANewline);
+
+runTest();
+</script>
+</body>
+</html>
index b44b2bf..e901d3e 100644 (file)
@@ -8,6 +8,7 @@
 
 accessibility/ios-simulator [ Pass ]
 displaylists [ Pass ]
+editing/deleting/ios [ Pass ]
 http/tests/quicklook [ Pass ]
 media/ios [ Pass ]
 quicklook [ Pass ]
index 322cf03..2ff53f5 100644 (file)
@@ -1,3 +1,71 @@
+2020-03-24  Daniel Bates  <dabates@apple.com>
+
+        [iOS] ASSERTION FAILURE: !isMissingPostLayoutData in WebKit::EditorState::postLayoutData()
+        https://bugs.webkit.org/show_bug.cgi?id=199960
+        <rdar://problem/53323966>
+
+        Reviewed by Simon Fraser.
+
+        Refactor the computation of editor state so that we can request that a layout be performed
+        each time we compute the editor state as part of asking the UI process to interpret a key
+        event. The full (read: after layout) editor state is needed for UIKit to perform a deletion
+        because UIKit wants to know how many characters are before the selection. Otherwise, we hit
+        an assert due to the fact the last editor state sent (when the Web process asked the UI process
+        to interpret the key) is missing layout data.
+
+        The refactoring also moves the Cocoa-common code out of the platform-independent WebPage.cpp
+        file into WebPageCocoa.mm.
+
+        One side effect of the refactoring is that we no longer allow the platformEditorState() function
+        to override the isMissingPostLayoutData bit. Currently it can even though the calling code, the
+        platform independent code (PIE) in WebPage, may have attached layout data. Now the PIE code sets
+        this bit if it attached layout data and the platformEditorState() function only attaches more
+        layout data if that bit is set. platformEditorState() never unsets that bit (i.e. sets isMissingPostLayoutData
+        to true).
+
+        The patch also removes m_isEditorStateMissingPostLayoutData in WebPage.h. This instance variable
+        has been unused since <https://trac.webkit.org/changeset/221064/webkit>. Also we haven't been using
+        IncludePostLayoutDataHint::No since the last reference to it was removed in <https://trac.webkit.org/changeset/244494/webkit>.
+
+        I also renamed platformEditorState() to getPlatformEditorState() since it has an out argument.
+
+        Test: editing/deleting/ios/backspace-last-character.html
+
+        * Shared/EditorState.h:
+        * UIProcess/API/glib/WebKitEditorState.cpp:
+        (webkitEditorStateCreate): Initialize _WebKitEditorStatePrivate::typingAttributes to WEBKIT_EDITOR_TYPING_ATTRIBUTE_NONE.
+        * WebProcess/WebPage/Cocoa/WebPageCocoa.mm:
+        (WebKit::WebPage::getPlatformEditorStateCommon const): Added. Moved Cocoa-common code from WebPage.cpp to here.
+        * WebProcess/WebPage/WebPage.cpp:
+        (WebKit::WebPage::editorState const): Move Cocoa-common code to WebPageCocoa.mm. Change enum to
+        track whether a layout should be performed. Keep the current behavior of only including post layout
+        data if the frame view does not need a layout. This behavior is encoded in the enumerator ShouldPerformLayout::Default.
+        which is the default argument value for the argument shouldPerformLayout.
+        * WebProcess/WebPage/WebPage.h:
+        (WebKit::WebPage::platformNeedsLayoutForEditorState const): Added. Non-Cocoa port implementation
+        that returns false.
+        * WebProcess/WebPage/glib/WebPageGLib.cpp:
+        (WebKit::WebPage::getPlatformEditorState const): Early return if isMissingPostLayoutData is true.
+        (WebKit::WebPage::platformEditorState const): Deleted.
+        * WebProcess/WebPage/ios/WebPageIOS.mm:
+        (WebKit::WebPage::platformNeedsLayoutForEditorState const): Added. Keep the current behavior of
+        performing a layout if we have a composition or a hardware keyboard is attached.
+        (WebKit::WebPage::getPlatformEditorState const): Call platformEditorStateCommon(). Bail out early
+        if isMissingPostLayoutData is true.
+        (WebKit::WebPage::handleEditingKeyboardEvent): The important part of this patch. Request a layout
+        when computing the editor state that we will send to the UI process.
+        (WebKit::WebPage::platformEditorState const): Deleted.
+        * WebProcess/WebPage/mac/WebPageMac.mm:
+        (WebKit::WebPage::getPlatformEditorState const): Call platformEditorStateCommon(). Bail out early
+        if isMissingPostLayoutData is true.
+        (WebKit::WebPage::platformEditorState const): Deleted.
+        * WebProcess/WebPage/playstation/WebPagePlayStation.cpp:
+        (WebKit::WebPage::getPlatformEditorState const): Update as needed.
+        (WebKit::WebPage::platformEditorState const): Deleted.
+        * WebProcess/WebPage/win/WebPageWin.cpp:
+        (WebKit::WebPage::getPlatformEditorState const): Update as needed.
+        (WebKit::WebPage::platformEditorState const): Deleted.
+
 2020-03-25  Kate Cheney  <katherine_cheney@apple.com>
 
         App-bound domain checks should provide more debugging details at script evaluation sites
index 6faa7ce..b396077 100644 (file)
@@ -74,7 +74,7 @@ struct EditorState {
     bool isInPasswordField { false };
     bool isInPlugin { false };
     bool hasComposition { false };
-    bool isMissingPostLayoutData { false };
+    bool isMissingPostLayoutData { true };
 
 #if PLATFORM(IOS_FAMILY)
     WebCore::IntRect firstMarkedRect;
index fd91bf4..0cc084e 100644 (file)
@@ -109,6 +109,7 @@ WebKitEditorState* webkitEditorStateCreate(WebPageProxy& page)
 {
     WebKitEditorState* editorState = WEBKIT_EDITOR_STATE(g_object_new(WEBKIT_TYPE_EDITOR_STATE, nullptr));
     editorState->priv->page = &page;
+    editorState->priv->typingAttributes = WEBKIT_EDITOR_TYPING_ATTRIBUTE_NONE;
     webkitEditorStateChanged(editorState, page.editorState());
     return editorState;
 }
index 5661a04..58bb956 100644 (file)
 #import "WebRemoteObjectRegistry.h"
 #import <pal/spi/cocoa/LaunchServicesSPI.h>
 #import <WebCore/DictionaryLookup.h>
+#import <WebCore/Editing.h>
 #import <WebCore/Editor.h>
 #import <WebCore/EventHandler.h>
 #import <WebCore/EventNames.h>
 #import <WebCore/FocusController.h>
 #import <WebCore/FrameView.h>
 #import <WebCore/HTMLConverter.h>
+#import <WebCore/HTMLOListElement.h>
+#import <WebCore/HTMLUListElement.h>
 #import <WebCore/HitTestResult.h>
 #import <WebCore/NodeRenderStyle.h>
 #import <WebCore/PaymentCoordinator.h>
@@ -311,6 +314,7 @@ RetainPtr<CFDataRef> WebPage::pdfSnapshotAtSize(IntRect rect, IntSize bitmapSize
     return data;
 }
 
+
 void WebPage::getProcessDisplayName(CompletionHandler<void(String&&)>&& completionHandler)
 {
 #if PLATFORM(MAC)
@@ -320,6 +324,71 @@ void WebPage::getProcessDisplayName(CompletionHandler<void(String&&)>&& completi
 #endif
 }
 
+void WebPage::getPlatformEditorStateCommon(const Frame& frame, EditorState& result) const
+{
+    if (result.isMissingPostLayoutData)
+        return;
+
+    const auto& selection = frame.selection().selection();
+
+    if (!result.isContentEditable || selection.isNone())
+        return;
+
+    auto& postLayoutData = result.postLayoutData();
+    if (auto editingStyle = EditingStyle::styleAtSelectionStart(selection)) {
+        if (editingStyle->hasStyle(CSSPropertyFontWeight, "bold"_s))
+            postLayoutData.typingAttributes |= AttributeBold;
+
+        if (editingStyle->hasStyle(CSSPropertyFontStyle, "italic"_s) || editingStyle->hasStyle(CSSPropertyFontStyle, "oblique"_s))
+            postLayoutData.typingAttributes |= AttributeItalics;
+
+        if (editingStyle->hasStyle(CSSPropertyWebkitTextDecorationsInEffect, "underline"_s))
+            postLayoutData.typingAttributes |= AttributeUnderline;
+
+        if (auto* styleProperties = editingStyle->style()) {
+            bool isLeftToRight = styleProperties->propertyAsValueID(CSSPropertyDirection) == CSSValueLtr;
+            switch (styleProperties->propertyAsValueID(CSSPropertyTextAlign)) {
+            case CSSValueRight:
+            case CSSValueWebkitRight:
+                postLayoutData.textAlignment = RightAlignment;
+                break;
+            case CSSValueLeft:
+            case CSSValueWebkitLeft:
+                postLayoutData.textAlignment = LeftAlignment;
+                break;
+            case CSSValueCenter:
+            case CSSValueWebkitCenter:
+                postLayoutData.textAlignment = CenterAlignment;
+                break;
+            case CSSValueJustify:
+                postLayoutData.textAlignment = JustifiedAlignment;
+                break;
+            case CSSValueStart:
+                postLayoutData.textAlignment = isLeftToRight ? LeftAlignment : RightAlignment;
+                break;
+            case CSSValueEnd:
+                postLayoutData.textAlignment = isLeftToRight ? RightAlignment : LeftAlignment;
+                break;
+            default:
+                break;
+            }
+            if (auto textColor = styleProperties->propertyAsColor(CSSPropertyColor))
+                postLayoutData.textColor = *textColor;
+        }
+    }
+
+    if (auto* enclosingListElement = enclosingList(selection.start().containerNode())) {
+        if (is<HTMLUListElement>(*enclosingListElement))
+            postLayoutData.enclosingListType = UnorderedList;
+        else if (is<HTMLOListElement>(*enclosingListElement))
+            postLayoutData.enclosingListType = OrderedList;
+        else
+            ASSERT_NOT_REACHED();
+    }
+
+    postLayoutData.baseWritingDirection = frame.editor().baseWritingDirectionForSelectionStart();
+}
+
 } // namespace WebKit
 
 #endif // PLATFORM(COCOA)
index 032867b..b63709e 100644 (file)
 #include <WebCore/HTMLInputElement.h>
 #include <WebCore/HTMLMenuElement.h>
 #include <WebCore/HTMLMenuItemElement.h>
-#include <WebCore/HTMLOListElement.h>
 #include <WebCore/HTMLPlugInElement.h>
 #include <WebCore/HTMLPlugInImageElement.h>
 #include <WebCore/HTMLSelectElement.h>
 #include <WebCore/HTMLTextFormControlElement.h>
-#include <WebCore/HTMLUListElement.h>
 #include <WebCore/HistoryController.h>
 #include <WebCore/HistoryItem.h>
 #include <WebCore/HitTestResult.h>
@@ -1036,9 +1034,10 @@ WebCore::WebGLLoadPolicy WebPage::resolveWebGLPolicyForURL(WebFrame*, const URL&
 }
 #endif
 
-EditorState WebPage::editorState(IncludePostLayoutDataHint shouldIncludePostLayoutData) const
+EditorState WebPage::editorState(ShouldPerformLayout shouldPerformLayout) const
 {
-    Frame& frame = m_page->focusController().focusedOrMainFrame();
+    // Ref the frame because this function may perform layout, which may cause frame destruction.
+    Ref<Frame> frame = m_page->focusController().focusedOrMainFrame();
 
     EditorState result;
 
@@ -1051,8 +1050,8 @@ EditorState WebPage::editorState(IncludePostLayoutDataHint shouldIncludePostLayo
         }
     }
 
-    const VisibleSelection& selection = frame.selection().selection();
-    const Editor& editor = frame.editor();
+    const VisibleSelection& selection = frame->selection().selection();
+    const Editor& editor = frame->editor();
 
     result.selectionIsNone = selection.isNone();
     result.selectionIsRange = selection.isRange();
@@ -1062,11 +1061,15 @@ EditorState WebPage::editorState(IncludePostLayoutDataHint shouldIncludePostLayo
     result.hasComposition = editor.hasComposition();
     result.shouldIgnoreSelectionChanges = editor.ignoreSelectionChanges() || (editor.client() && !editor.client()->shouldRevealCurrentSelectionAfterInsertion());
 
-    if (auto* document = frame.document())
-        result.originIdentifierForPasteboard = document->originIdentifierForPasteboard();
+    Ref<Document> document = *frame->document();
+    result.originIdentifierForPasteboard = document->originIdentifierForPasteboard();
+
+    if (shouldPerformLayout == ShouldPerformLayout::Yes || platformNeedsLayoutForEditorState(frame))
+        document->updateLayout(); // May cause document destruction
+
+    if (auto* frameView = document->view(); frameView && !frameView->needsLayout()) {
+        result.isMissingPostLayoutData = false;
 
-    bool canIncludePostLayoutData = frame.view() && !frame.view()->needsLayout();
-    if (shouldIncludePostLayoutData == IncludePostLayoutDataHint::Yes && canIncludePostLayoutData) {
         auto& postLayoutData = result.postLayoutData();
         postLayoutData.canCut = editor.canCut();
         postLayoutData.canCopy = editor.canCopy();
@@ -1074,66 +1077,9 @@ EditorState WebPage::editorState(IncludePostLayoutDataHint shouldIncludePostLayo
 
         if (m_needsFontAttributes)
             postLayoutData.fontAttributes = editor.fontAttributesAtSelectionStart();
-
-#if PLATFORM(COCOA)
-        if (result.isContentEditable && !selection.isNone()) {
-            if (auto editingStyle = EditingStyle::styleAtSelectionStart(selection)) {
-                if (editingStyle->hasStyle(CSSPropertyFontWeight, "bold"))
-                    postLayoutData.typingAttributes |= AttributeBold;
-
-                if (editingStyle->hasStyle(CSSPropertyFontStyle, "italic") || editingStyle->hasStyle(CSSPropertyFontStyle, "oblique"))
-                    postLayoutData.typingAttributes |= AttributeItalics;
-
-                if (editingStyle->hasStyle(CSSPropertyWebkitTextDecorationsInEffect, "underline"))
-                    postLayoutData.typingAttributes |= AttributeUnderline;
-
-                if (auto* styleProperties = editingStyle->style()) {
-                    bool isLeftToRight = styleProperties->propertyAsValueID(CSSPropertyDirection) == CSSValueLtr;
-                    switch (styleProperties->propertyAsValueID(CSSPropertyTextAlign)) {
-                    case CSSValueRight:
-                    case CSSValueWebkitRight:
-                        postLayoutData.textAlignment = RightAlignment;
-                        break;
-                    case CSSValueLeft:
-                    case CSSValueWebkitLeft:
-                        postLayoutData.textAlignment = LeftAlignment;
-                        break;
-                    case CSSValueCenter:
-                    case CSSValueWebkitCenter:
-                        postLayoutData.textAlignment = CenterAlignment;
-                        break;
-                    case CSSValueJustify:
-                        postLayoutData.textAlignment = JustifiedAlignment;
-                        break;
-                    case CSSValueStart:
-                        postLayoutData.textAlignment = isLeftToRight ? LeftAlignment : RightAlignment;
-                        break;
-                    case CSSValueEnd:
-                        postLayoutData.textAlignment = isLeftToRight ? RightAlignment : LeftAlignment;
-                        break;
-                    default:
-                        break;
-                    }
-                    if (auto textColor = styleProperties->propertyAsColor(CSSPropertyColor))
-                        postLayoutData.textColor = *textColor;
-                }
-            }
-
-            if (auto* enclosingListElement = enclosingList(selection.start().containerNode())) {
-                if (is<HTMLUListElement>(*enclosingListElement))
-                    postLayoutData.enclosingListType = UnorderedList;
-                else if (is<HTMLOListElement>(*enclosingListElement))
-                    postLayoutData.enclosingListType = OrderedList;
-                else
-                    ASSERT_NOT_REACHED();
-            }
-
-            postLayoutData.baseWritingDirection = editor.baseWritingDirectionForSelectionStart();
-        }
-#endif
     }
 
-    platformEditorState(frame, result, shouldIncludePostLayoutData);
+    getPlatformEditorState(frame, result);
 
     m_lastEditorStateWasContentEditable = result.isContentEditable ? EditorStateIsContentEditable::Yes : EditorStateIsContentEditable::No;
 
index 09b6084..1d16c84 100644 (file)
@@ -478,8 +478,8 @@ public:
     WebCore::WebGLLoadPolicy resolveWebGLPolicyForURL(WebFrame*, const URL&);
 #endif
     
-    enum class IncludePostLayoutDataHint { No, Yes };
-    EditorState editorState(IncludePostLayoutDataHint = IncludePostLayoutDataHint::Yes) const;
+    enum class ShouldPerformLayout { Default, Yes };
+    EditorState editorState(ShouldPerformLayout = ShouldPerformLayout::Default) const;
     void updateEditorStateAfterLayoutIfEditabilityChanged();
 
     // options are RenderTreeExternalRepresentationBehavior values.
@@ -1316,9 +1316,11 @@ private:
     void platformInitialize();
     void platformReinitialize();
     void platformDetach();
-    void platformEditorState(WebCore::Frame&, EditorState& result, IncludePostLayoutDataHint) const;
+    void getPlatformEditorState(WebCore::Frame&, EditorState&) const;
+    bool platformNeedsLayoutForEditorState(const WebCore::Frame&) const;
     void platformWillPerformEditingCommand();
     void sendEditorStateUpdate();
+    void getPlatformEditorStateCommon(const WebCore::Frame&, EditorState&) const;
 
 #if HAVE(TOUCH_BAR)
     void sendTouchBarMenuDataAddedUpdate(WebCore::HTMLMenuElement&);
@@ -2009,7 +2011,6 @@ private:
 
     bool m_mainFrameProgressCompleted { false };
     bool m_shouldDispatchFakeMouseMoveEvents { true };
-    bool m_isEditorStateMissingPostLayoutData { false };
     bool m_isSelectingTextWhileInsertingAsynchronously { false };
 
     enum class EditorStateIsContentEditable { No, Yes, Unset };
@@ -2081,6 +2082,7 @@ private:
 
 #if !PLATFORM(IOS_FAMILY)
 inline void WebPage::platformWillPerformEditingCommand() { }
+inline bool WebPage::platformNeedsLayoutForEditorState(const WebCore::Frame&) const { return false; }
 #endif
 
 } // namespace WebKit
index d545b98..4e86f05 100644 (file)
@@ -68,12 +68,10 @@ void WebPage::sendMessageToWebExtension(UserMessage&& message)
     sendMessageToWebExtensionWithReply(WTFMove(message), [](UserMessage&&) { });
 }
 
-void WebPage::platformEditorState(Frame& frame, EditorState& result, IncludePostLayoutDataHint shouldIncludePostLayoutData) const
+void WebPage::getPlatformEditorState(Frame& frame, EditorState& result) const
 {
-    if (shouldIncludePostLayoutData == IncludePostLayoutDataHint::No || !frame.view() || frame.view()->needsLayout()) {
-        result.isMissingPostLayoutData = true;
+    if (result.isMissingPostLayoutData || !frame.view() || frame.view()->needsLayout())
         return;
-    }
 
     auto& postLayoutData = result.postLayoutData();
     postLayoutData.caretRectAtStart = frame.selection().absoluteCaretBounds();
index 76c0057..3797bc5 100644 (file)
@@ -223,8 +223,21 @@ bool WebPage::isTransparentOrFullyClipped(const Element& element) const
     return renderer->hasNonEmptyVisibleRectRespectingParentFrames();
 }
 
-void WebPage::platformEditorState(Frame& frame, EditorState& result, IncludePostLayoutDataHint shouldIncludePostLayoutData) const
+bool WebPage::platformNeedsLayoutForEditorState(const Frame& frame) const
 {
+    // If we have a composition or are using a hardware keyboard then we need to send the full
+    // editor so that the UIProcess can update UI, including the position of the caret.
+    bool needsLayout = frame.editor().hasComposition();
+#if !PLATFORM(MACCATALYST)
+    needsLayout |= m_keyboardIsAttached;
+#endif
+    return needsLayout;
+}
+
+void WebPage::getPlatformEditorState(Frame& frame, EditorState& result) const
+{
+    getPlatformEditorStateCommon(frame, result);
+
     FrameView* view = frame.view();
     if (!view) {
         result.isMissingPostLayoutData = true;
@@ -255,14 +268,11 @@ void WebPage::platformEditorState(Frame& frame, EditorState& result, IncludePost
 #if !PLATFORM(MACCATALYST)
     requiresPostLayoutData |= m_keyboardIsAttached;
 #endif
-    if ((shouldIncludePostLayoutData == IncludePostLayoutDataHint::No || needsLayout) && !requiresPostLayoutData) {
-        result.isMissingPostLayoutData = true;
+    if (needsLayout || result.isMissingPostLayoutData)
         return;
-    }
 
     auto& postLayoutData = result.postLayoutData();
-    
-    const VisibleSelection& selection = frame.selection().selection();
+    const auto& selection = frame.selection().selection();
     postLayoutData.isStableStateUpdate = m_isInStableState;
     bool startNodeIsInsideFixedPosition = false;
     bool endNodeIsInsideFixedPosition = false;
@@ -479,7 +489,7 @@ bool WebPage::handleEditingKeyboardEvent(KeyboardEvent& event)
 
     // FIXME: Interpret the event immediately upon receiving it in UI process, without sending to WebProcess first.
     bool eventWasHandled = false;
-    bool sendResult = WebProcess::singleton().parentProcessConnection()->sendSync(Messages::WebPageProxy::InterpretKeyEvent(editorState(), platformEvent->type() == PlatformKeyboardEvent::Char),
+    bool sendResult = WebProcess::singleton().parentProcessConnection()->sendSync(Messages::WebPageProxy::InterpretKeyEvent(editorState(ShouldPerformLayout::Yes), platformEvent->type() == PlatformKeyboardEvent::Char),
         Messages::WebPageProxy::InterpretKeyEvent::Reply(eventWasHandled), m_identifier);
     return sendResult && eventWasHandled;
 }
index 2c5896d..40f0520 100644 (file)
@@ -133,12 +133,12 @@ void WebPage::platformDetach()
     [m_mockAccessibilityElement setWebPage:nullptr];
 }
 
-void WebPage::platformEditorState(Frame& frame, EditorState& result, IncludePostLayoutDataHint shouldIncludePostLayoutData) const
+void WebPage::getPlatformEditorState(Frame& frame, EditorState& result) const
 {
-    if (shouldIncludePostLayoutData == IncludePostLayoutDataHint::No || !frame.view() || frame.view()->needsLayout() || !result.isContentEditable) {
-        result.isMissingPostLayoutData = true;
+    getPlatformEditorStateCommon(frame, result);
+
+    if (result.isMissingPostLayoutData)
         return;
-    }
 
     auto& selection = frame.selection().selection();
     RefPtr<Range> selectedRange = selection.toNormalizedRange();
index 451ce25..cbda48c 100644 (file)
@@ -71,7 +71,7 @@ bool WebPage::handleEditingKeyboardEvent(WebCore::KeyboardEvent& event)
     return false;
 }
 
-void WebPage::platformEditorState(Frame& frame, EditorState& result, IncludePostLayoutDataHint shouldIncludePostLayoutData) const
+void WebPage::getPlatformEditorState(Frame& frame, EditorState& result) const
 {
     notImplemented();
 }
index 624f4d1..9bfc869 100644 (file)
@@ -63,7 +63,7 @@ void WebPage::platformDetach()
 {
 }
 
-void WebPage::platformEditorState(Frame& frame, EditorState& result, IncludePostLayoutDataHint shouldIncludePostLayoutData) const
+void WebPage::getPlatformEditorState(Frame&, EditorState&) const
 {
 }