Title: [280071] branches/safari-612.1.24.0-branch/Source
Revision
280071
Author
repst...@apple.com
Date
2021-07-19 19:18:47 -0700 (Mon, 19 Jul 2021)

Log Message

Cherry-pick r280019. rdar://problem/80788628

    [iOS] [AX] Keyboard text candidates don't update when changing selection using Switch Control
    https://bugs.webkit.org/show_bug.cgi?id=228051
    rdar://79944295

    Reviewed by Tim Horton.

    Source/WebCore:

    See WebKit ChangeLog for more details.

    * accessibility/AccessibilityRenderObject.cpp:
    (WebCore::AccessibilityRenderObject::setSelectedTextRange):
    (WebCore::AccessibilityRenderObject::setSelectedVisiblePositionRange const):

    Call the new EditorClient methods added below. Additionally, drive-by fix `setSelectedVisiblePositionRange` so
    that it also performs a user-triggered selection change in the case where the range is not collapsed; this
    matches the behavior of `setSelectedTextRange` as well.

    * page/EditorClient.h:
    (WebCore::EditorClient::willChangeSelectionForAccessibility):
    (WebCore::EditorClient::didChangeSelectionForAccessibility):

    Add new EditorClient hooks, `(will|did)ChangeSelectionForAccessibility`, that accessibility code can call before
    and after it triggers a selection change; we use these two codepaths above when using the "Previous/Next
    Character", "Previous/Next Word", and "Previous/Next Line" Switch Control menu actions.

    Source/WebKit:

    Add support for updating text candidates by calling into UIKeyboardImpl when changing the selection via
    accessibility UI (e.g. when using Switch Control). To achieve this, we add a new EditorState flag that indicates
    whether or not the EditorState change was triggered by accessibility; in WKContentView, we then consult this
    flag and call `-beginSelectionChange/-endSelectionChange` in the case where the updated editor state change was
    triggered by accessibility and we're also not already in the scope of another selection change or gesture-based
    selection change.

    Note that the latter part of the check is critical in order to avoid deadlocks in UIKeyboardTaskQueue; see
    r246013, r246425 and r246665, which introduced and later reverted a similar mechanism for updating
    UIKeyboardImpl, but after every selection change.

    * Shared/EditorState.cpp:
    (WebKit::EditorState::encode const):
    (WebKit::EditorState::decode):
    (WebKit::operator<<):
    * Shared/EditorState.h:

    Add the new boolean flag, `triggeredByAccessibilitySelectionChange`.

    * UIProcess/WebPageProxy.cpp:
    * UIProcess/ios/WKContentViewInteraction.h:
    * UIProcess/ios/WKContentViewInteraction.mm:
    (-[WKContentView beginSelectionChange]):
    (-[WKContentView endSelectionChange]):

    Add a new `unsigned` flag, `_selectionChangeNestingLevel`, that lets us know if we're expecting an eventual call
    to `-endSelectionChange`. See `-_selectionChanged` below for more details. Note that we don't need to reset
    `_selectionChangeNestingLevel` to 0 when cleaning up the interaction after web process termination, because
    the completion handlers that contain all the balanced calls to `-endSelectionChange` should be invoked after
    such an event anyways.

    (-[WKContentView _selectionChanged]):

    If the `triggeredByAccessibilitySelectionChange` bit is set on the incoming (post layout data) EditorState and:
    (1) UIKit isn't in the middle of handling a gesture that modifies the selection, and...
    (2) We aren't expecting a call to `-endSelectionChange`.

    ...then we force UIKeyboardImpl to reload its UI (text suggestions, B/I/U state, etc.) using the incoming
    EditorState. This allows accessibility-driven selection changes using Switch Control to behave like gesture-
    based selection changes.

    * WebProcess/WebCoreSupport/WebEditorClient.cpp:
    (WebKit::WebEditorClient::willChangeSelectionForAccessibility):
    (WebKit::WebEditorClient::didChangeSelectionForAccessibility):

    Implement a couple of client hooks in WebKit2 so that we set a boolean flag on WebPage,
    `m_isChangingSelectionForAccessibility`, to `true` over the scope of an AX-driven selection change. See related
    AX changes in WebCore for more details.

    * WebProcess/WebCoreSupport/WebEditorClient.h:
    * WebProcess/WebPage/WebPage.cpp:
    (WebKit::WebPage::hasPendingEditorStateUpdate const):

    Add a helper method so that we can conveniently check whether we have a pending editor state update to flush,
    since the previous boolean flag is now a tri-state enum (`m_pendingEditorStateUpdateStatus`).

    (WebKit::WebPage::editorState const):
    (WebKit::WebPage::willCommitLayerTree):
    (WebKit::WebPage::sendEditorStateUpdate):
    (WebKit::WebPage::scheduleFullEditorStateUpdate):

    If we're in the scope of an accessibility selection change, transition `m_pendingEditorStateUpdateStatus` to
    `ScheduledDuringAccessibilitySelectionChange`; otherwise, just use `Scheduled`.

    (WebKit::WebPage::flushPendingEditorStateUpdate):

    Refactor `m_hasPendingEditorStateUpdate` so that it now captures three states:
    (1) We haven't scheduled an editor state update yet.
    (2) We've scheduled an editor state update that was not triggered by accessibility.
    (3) We've scheduled an editor state update that was triggered by accessibility.

    The distinction between (2) and (3) allows us to populate EditorState's new
    `triggeredByAccessibilitySelectionChange` flag.

    * WebProcess/WebPage/WebPage.h:
    (WebKit::WebPage::willChangeSelectionForAccessibility):
    (WebKit::WebPage::didChangeSelectionForAccessibility):

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

Modified Paths

Diff

Modified: branches/safari-612.1.24.0-branch/Source/WebCore/ChangeLog (280070 => 280071)


--- branches/safari-612.1.24.0-branch/Source/WebCore/ChangeLog	2021-07-20 02:18:41 UTC (rev 280070)
+++ branches/safari-612.1.24.0-branch/Source/WebCore/ChangeLog	2021-07-20 02:18:47 UTC (rev 280071)
@@ -1,3 +1,142 @@
+2021-07-19  Russell Epstein  <repst...@apple.com>
+
+        Cherry-pick r280019. rdar://problem/80788628
+
+    [iOS] [AX] Keyboard text candidates don't update when changing selection using Switch Control
+    https://bugs.webkit.org/show_bug.cgi?id=228051
+    rdar://79944295
+    
+    Reviewed by Tim Horton.
+    
+    Source/WebCore:
+    
+    See WebKit ChangeLog for more details.
+    
+    * accessibility/AccessibilityRenderObject.cpp:
+    (WebCore::AccessibilityRenderObject::setSelectedTextRange):
+    (WebCore::AccessibilityRenderObject::setSelectedVisiblePositionRange const):
+    
+    Call the new EditorClient methods added below. Additionally, drive-by fix `setSelectedVisiblePositionRange` so
+    that it also performs a user-triggered selection change in the case where the range is not collapsed; this
+    matches the behavior of `setSelectedTextRange` as well.
+    
+    * page/EditorClient.h:
+    (WebCore::EditorClient::willChangeSelectionForAccessibility):
+    (WebCore::EditorClient::didChangeSelectionForAccessibility):
+    
+    Add new EditorClient hooks, `(will|did)ChangeSelectionForAccessibility`, that accessibility code can call before
+    and after it triggers a selection change; we use these two codepaths above when using the "Previous/Next
+    Character", "Previous/Next Word", and "Previous/Next Line" Switch Control menu actions.
+    
+    Source/WebKit:
+    
+    Add support for updating text candidates by calling into UIKeyboardImpl when changing the selection via
+    accessibility UI (e.g. when using Switch Control). To achieve this, we add a new EditorState flag that indicates
+    whether or not the EditorState change was triggered by accessibility; in WKContentView, we then consult this
+    flag and call `-beginSelectionChange/-endSelectionChange` in the case where the updated editor state change was
+    triggered by accessibility and we're also not already in the scope of another selection change or gesture-based
+    selection change.
+    
+    Note that the latter part of the check is critical in order to avoid deadlocks in UIKeyboardTaskQueue; see
+    r246013, r246425 and r246665, which introduced and later reverted a similar mechanism for updating
+    UIKeyboardImpl, but after every selection change.
+    
+    * Shared/EditorState.cpp:
+    (WebKit::EditorState::encode const):
+    (WebKit::EditorState::decode):
+    (WebKit::operator<<):
+    * Shared/EditorState.h:
+    
+    Add the new boolean flag, `triggeredByAccessibilitySelectionChange`.
+    
+    * UIProcess/WebPageProxy.cpp:
+    * UIProcess/ios/WKContentViewInteraction.h:
+    * UIProcess/ios/WKContentViewInteraction.mm:
+    (-[WKContentView beginSelectionChange]):
+    (-[WKContentView endSelectionChange]):
+    
+    Add a new `unsigned` flag, `_selectionChangeNestingLevel`, that lets us know if we're expecting an eventual call
+    to `-endSelectionChange`. See `-_selectionChanged` below for more details. Note that we don't need to reset
+    `_selectionChangeNestingLevel` to 0 when cleaning up the interaction after web process termination, because
+    the completion handlers that contain all the balanced calls to `-endSelectionChange` should be invoked after
+    such an event anyways.
+    
+    (-[WKContentView _selectionChanged]):
+    
+    If the `triggeredByAccessibilitySelectionChange` bit is set on the incoming (post layout data) EditorState and:
+    (1) UIKit isn't in the middle of handling a gesture that modifies the selection, and...
+    (2) We aren't expecting a call to `-endSelectionChange`.
+    
+    ...then we force UIKeyboardImpl to reload its UI (text suggestions, B/I/U state, etc.) using the incoming
+    EditorState. This allows accessibility-driven selection changes using Switch Control to behave like gesture-
+    based selection changes.
+    
+    * WebProcess/WebCoreSupport/WebEditorClient.cpp:
+    (WebKit::WebEditorClient::willChangeSelectionForAccessibility):
+    (WebKit::WebEditorClient::didChangeSelectionForAccessibility):
+    
+    Implement a couple of client hooks in WebKit2 so that we set a boolean flag on WebPage,
+    `m_isChangingSelectionForAccessibility`, to `true` over the scope of an AX-driven selection change. See related
+    AX changes in WebCore for more details.
+    
+    * WebProcess/WebCoreSupport/WebEditorClient.h:
+    * WebProcess/WebPage/WebPage.cpp:
+    (WebKit::WebPage::hasPendingEditorStateUpdate const):
+    
+    Add a helper method so that we can conveniently check whether we have a pending editor state update to flush,
+    since the previous boolean flag is now a tri-state enum (`m_pendingEditorStateUpdateStatus`).
+    
+    (WebKit::WebPage::editorState const):
+    (WebKit::WebPage::willCommitLayerTree):
+    (WebKit::WebPage::sendEditorStateUpdate):
+    (WebKit::WebPage::scheduleFullEditorStateUpdate):
+    
+    If we're in the scope of an accessibility selection change, transition `m_pendingEditorStateUpdateStatus` to
+    `ScheduledDuringAccessibilitySelectionChange`; otherwise, just use `Scheduled`.
+    
+    (WebKit::WebPage::flushPendingEditorStateUpdate):
+    
+    Refactor `m_hasPendingEditorStateUpdate` so that it now captures three states:
+    (1) We haven't scheduled an editor state update yet.
+    (2) We've scheduled an editor state update that was not triggered by accessibility.
+    (3) We've scheduled an editor state update that was triggered by accessibility.
+    
+    The distinction between (2) and (3) allows us to populate EditorState's new
+    `triggeredByAccessibilitySelectionChange` flag.
+    
+    * WebProcess/WebPage/WebPage.h:
+    (WebKit::WebPage::willChangeSelectionForAccessibility):
+    (WebKit::WebPage::didChangeSelectionForAccessibility):
+    
+    
+    git-svn-id: https://svn.webkit.org/repository/webkit/trunk@280019 268f45cc-cd09-0410-ab3c-d52691b4dbfc
+
+    2021-07-18  Wenson Hsieh  <wenson_hs...@apple.com>
+
+            [iOS] [AX] Keyboard text candidates don't update when changing selection using Switch Control
+            https://bugs.webkit.org/show_bug.cgi?id=228051
+            rdar://79944295
+
+            Reviewed by Tim Horton.
+
+            See WebKit ChangeLog for more details.
+
+            * accessibility/AccessibilityRenderObject.cpp:
+            (WebCore::AccessibilityRenderObject::setSelectedTextRange):
+            (WebCore::AccessibilityRenderObject::setSelectedVisiblePositionRange const):
+
+            Call the new EditorClient methods added below. Additionally, drive-by fix `setSelectedVisiblePositionRange` so
+            that it also performs a user-triggered selection change in the case where the range is not collapsed; this
+            matches the behavior of `setSelectedTextRange` as well.
+
+            * page/EditorClient.h:
+            (WebCore::EditorClient::willChangeSelectionForAccessibility):
+            (WebCore::EditorClient::didChangeSelectionForAccessibility):
+
+            Add new EditorClient hooks, `(will|did)ChangeSelectionForAccessibility`, that accessibility code can call before
+            and after it triggers a selection change; we use these two codepaths above when using the "Previous/Next
+            Character", "Previous/Next Word", and "Previous/Next Line" Switch Control menu actions.
+
 2021-07-14  Alex Christensen  <achristen...@webkit.org>
 
         Link against Catalyst ANGLE-shared.dylib when linking Catalyst WebCore.framework

Modified: branches/safari-612.1.24.0-branch/Source/WebCore/accessibility/AccessibilityRenderObject.cpp (280070 => 280071)


--- branches/safari-612.1.24.0-branch/Source/WebCore/accessibility/AccessibilityRenderObject.cpp	2021-07-20 02:18:41 UTC (rev 280070)
+++ branches/safari-612.1.24.0-branch/Source/WebCore/accessibility/AccessibilityRenderObject.cpp	2021-07-20 02:18:47 UTC (rev 280071)
@@ -41,6 +41,7 @@
 #include "DocumentSVG.h"
 #include "Editing.h"
 #include "Editor.h"
+#include "EditorClient.h"
 #include "ElementIterator.h"
 #include "EventHandler.h"
 #include "FloatRect.h"
@@ -1716,6 +1717,9 @@
 {
     setTextSelectionIntent(axObjectCache(), range.length ? AXTextStateChangeTypeSelectionExtend : AXTextStateChangeTypeSelectionMove);
 
+    if (auto client = m_renderer->document().editor().client())
+        client->willChangeSelectionForAccessibility();
+
     if (isNativeTextControl()) {
         HTMLTextFormControlElement& textControl = downcast<RenderTextControl>(*m_renderer).textFormControlElement();
         textControl.setSelectionRange(range.start, range.start + range.length);
@@ -1733,6 +1737,9 @@
     }
     
     clearTextSelectionIntent(axObjectCache());
+
+    if (auto client = m_renderer->document().editor().client())
+        client->didChangeSelectionForAccessibility();
 }
 
 URL AccessibilityRenderObject::url() const
@@ -2275,6 +2282,9 @@
         && isVisiblePositionRangeInDifferentDocument(range))
         return;
 
+    if (auto client = m_renderer->document().editor().client())
+        client->willChangeSelectionForAccessibility();
+
     // make selection and tell the document to use it. if it's zero length, then move to that position
     if (range.start == range.end) {
         setTextSelectionIntent(axObjectCache(), AXTextStateChangeTypeSelectionMove);
@@ -2290,10 +2300,13 @@
         setTextSelectionIntent(axObjectCache(), AXTextStateChangeTypeSelectionExtend);
 
         VisibleSelection newSelection = VisibleSelection(range.start, range.end);
-        m_renderer->frame().selection().setSelection(newSelection, FrameSelection::defaultSetSelectionOptions());
+        m_renderer->frame().selection().setSelection(newSelection, FrameSelection::defaultSetSelectionOptions(UserTriggered));
     }
 
     clearTextSelectionIntent(axObjectCache());
+
+    if (auto client = m_renderer->document().editor().client())
+        client->didChangeSelectionForAccessibility();
 }
 
 VisiblePosition AccessibilityRenderObject::visiblePositionForPoint(const IntPoint& point) const

Modified: branches/safari-612.1.24.0-branch/Source/WebCore/page/EditorClient.h (280070 => 280071)


--- branches/safari-612.1.24.0-branch/Source/WebCore/page/EditorClient.h	2021-07-20 02:18:41 UTC (rev 280070)
+++ branches/safari-612.1.24.0-branch/Source/WebCore/page/EditorClient.h	2021-07-20 02:18:47 UTC (rev 280071)
@@ -192,6 +192,9 @@
     virtual bool canShowFontPanel() const = 0;
 
     virtual bool shouldAllowSingleClickToChangeSelection(Node&, const VisibleSelection&) const { return true; }
+
+    virtual void willChangeSelectionForAccessibility() { }
+    virtual void didChangeSelectionForAccessibility() { }
 };
 
 }

Modified: branches/safari-612.1.24.0-branch/Source/WebKit/ChangeLog (280070 => 280071)


--- branches/safari-612.1.24.0-branch/Source/WebKit/ChangeLog	2021-07-20 02:18:41 UTC (rev 280070)
+++ branches/safari-612.1.24.0-branch/Source/WebKit/ChangeLog	2021-07-20 02:18:47 UTC (rev 280071)
@@ -1,5 +1,204 @@
 2021-07-19  Russell Epstein  <repst...@apple.com>
 
+        Cherry-pick r280019. rdar://problem/80788628
+
+    [iOS] [AX] Keyboard text candidates don't update when changing selection using Switch Control
+    https://bugs.webkit.org/show_bug.cgi?id=228051
+    rdar://79944295
+    
+    Reviewed by Tim Horton.
+    
+    Source/WebCore:
+    
+    See WebKit ChangeLog for more details.
+    
+    * accessibility/AccessibilityRenderObject.cpp:
+    (WebCore::AccessibilityRenderObject::setSelectedTextRange):
+    (WebCore::AccessibilityRenderObject::setSelectedVisiblePositionRange const):
+    
+    Call the new EditorClient methods added below. Additionally, drive-by fix `setSelectedVisiblePositionRange` so
+    that it also performs a user-triggered selection change in the case where the range is not collapsed; this
+    matches the behavior of `setSelectedTextRange` as well.
+    
+    * page/EditorClient.h:
+    (WebCore::EditorClient::willChangeSelectionForAccessibility):
+    (WebCore::EditorClient::didChangeSelectionForAccessibility):
+    
+    Add new EditorClient hooks, `(will|did)ChangeSelectionForAccessibility`, that accessibility code can call before
+    and after it triggers a selection change; we use these two codepaths above when using the "Previous/Next
+    Character", "Previous/Next Word", and "Previous/Next Line" Switch Control menu actions.
+    
+    Source/WebKit:
+    
+    Add support for updating text candidates by calling into UIKeyboardImpl when changing the selection via
+    accessibility UI (e.g. when using Switch Control). To achieve this, we add a new EditorState flag that indicates
+    whether or not the EditorState change was triggered by accessibility; in WKContentView, we then consult this
+    flag and call `-beginSelectionChange/-endSelectionChange` in the case where the updated editor state change was
+    triggered by accessibility and we're also not already in the scope of another selection change or gesture-based
+    selection change.
+    
+    Note that the latter part of the check is critical in order to avoid deadlocks in UIKeyboardTaskQueue; see
+    r246013, r246425 and r246665, which introduced and later reverted a similar mechanism for updating
+    UIKeyboardImpl, but after every selection change.
+    
+    * Shared/EditorState.cpp:
+    (WebKit::EditorState::encode const):
+    (WebKit::EditorState::decode):
+    (WebKit::operator<<):
+    * Shared/EditorState.h:
+    
+    Add the new boolean flag, `triggeredByAccessibilitySelectionChange`.
+    
+    * UIProcess/WebPageProxy.cpp:
+    * UIProcess/ios/WKContentViewInteraction.h:
+    * UIProcess/ios/WKContentViewInteraction.mm:
+    (-[WKContentView beginSelectionChange]):
+    (-[WKContentView endSelectionChange]):
+    
+    Add a new `unsigned` flag, `_selectionChangeNestingLevel`, that lets us know if we're expecting an eventual call
+    to `-endSelectionChange`. See `-_selectionChanged` below for more details. Note that we don't need to reset
+    `_selectionChangeNestingLevel` to 0 when cleaning up the interaction after web process termination, because
+    the completion handlers that contain all the balanced calls to `-endSelectionChange` should be invoked after
+    such an event anyways.
+    
+    (-[WKContentView _selectionChanged]):
+    
+    If the `triggeredByAccessibilitySelectionChange` bit is set on the incoming (post layout data) EditorState and:
+    (1) UIKit isn't in the middle of handling a gesture that modifies the selection, and...
+    (2) We aren't expecting a call to `-endSelectionChange`.
+    
+    ...then we force UIKeyboardImpl to reload its UI (text suggestions, B/I/U state, etc.) using the incoming
+    EditorState. This allows accessibility-driven selection changes using Switch Control to behave like gesture-
+    based selection changes.
+    
+    * WebProcess/WebCoreSupport/WebEditorClient.cpp:
+    (WebKit::WebEditorClient::willChangeSelectionForAccessibility):
+    (WebKit::WebEditorClient::didChangeSelectionForAccessibility):
+    
+    Implement a couple of client hooks in WebKit2 so that we set a boolean flag on WebPage,
+    `m_isChangingSelectionForAccessibility`, to `true` over the scope of an AX-driven selection change. See related
+    AX changes in WebCore for more details.
+    
+    * WebProcess/WebCoreSupport/WebEditorClient.h:
+    * WebProcess/WebPage/WebPage.cpp:
+    (WebKit::WebPage::hasPendingEditorStateUpdate const):
+    
+    Add a helper method so that we can conveniently check whether we have a pending editor state update to flush,
+    since the previous boolean flag is now a tri-state enum (`m_pendingEditorStateUpdateStatus`).
+    
+    (WebKit::WebPage::editorState const):
+    (WebKit::WebPage::willCommitLayerTree):
+    (WebKit::WebPage::sendEditorStateUpdate):
+    (WebKit::WebPage::scheduleFullEditorStateUpdate):
+    
+    If we're in the scope of an accessibility selection change, transition `m_pendingEditorStateUpdateStatus` to
+    `ScheduledDuringAccessibilitySelectionChange`; otherwise, just use `Scheduled`.
+    
+    (WebKit::WebPage::flushPendingEditorStateUpdate):
+    
+    Refactor `m_hasPendingEditorStateUpdate` so that it now captures three states:
+    (1) We haven't scheduled an editor state update yet.
+    (2) We've scheduled an editor state update that was not triggered by accessibility.
+    (3) We've scheduled an editor state update that was triggered by accessibility.
+    
+    The distinction between (2) and (3) allows us to populate EditorState's new
+    `triggeredByAccessibilitySelectionChange` flag.
+    
+    * WebProcess/WebPage/WebPage.h:
+    (WebKit::WebPage::willChangeSelectionForAccessibility):
+    (WebKit::WebPage::didChangeSelectionForAccessibility):
+    
+    
+    git-svn-id: https://svn.webkit.org/repository/webkit/trunk@280019 268f45cc-cd09-0410-ab3c-d52691b4dbfc
+
+    2021-07-18  Wenson Hsieh  <wenson_hs...@apple.com>
+
+            [iOS] [AX] Keyboard text candidates don't update when changing selection using Switch Control
+            https://bugs.webkit.org/show_bug.cgi?id=228051
+            rdar://79944295
+
+            Reviewed by Tim Horton.
+
+            Add support for updating text candidates by calling into UIKeyboardImpl when changing the selection via
+            accessibility UI (e.g. when using Switch Control). To achieve this, we add a new EditorState flag that indicates
+            whether or not the EditorState change was triggered by accessibility; in WKContentView, we then consult this
+            flag and call `-beginSelectionChange/-endSelectionChange` in the case where the updated editor state change was
+            triggered by accessibility and we're also not already in the scope of another selection change or gesture-based
+            selection change.
+
+            Note that the latter part of the check is critical in order to avoid deadlocks in UIKeyboardTaskQueue; see
+            r246013, r246425 and r246665, which introduced and later reverted a similar mechanism for updating
+            UIKeyboardImpl, but after every selection change.
+
+            * Shared/EditorState.cpp:
+            (WebKit::EditorState::encode const):
+            (WebKit::EditorState::decode):
+            (WebKit::operator<<):
+            * Shared/EditorState.h:
+
+            Add the new boolean flag, `triggeredByAccessibilitySelectionChange`.
+
+            * UIProcess/WebPageProxy.cpp:
+            * UIProcess/ios/WKContentViewInteraction.h:
+            * UIProcess/ios/WKContentViewInteraction.mm:
+            (-[WKContentView beginSelectionChange]):
+            (-[WKContentView endSelectionChange]):
+
+            Add a new `unsigned` flag, `_selectionChangeNestingLevel`, that lets us know if we're expecting an eventual call
+            to `-endSelectionChange`. See `-_selectionChanged` below for more details. Note that we don't need to reset
+            `_selectionChangeNestingLevel` to 0 when cleaning up the interaction after web process termination, because
+            the completion handlers that contain all the balanced calls to `-endSelectionChange` should be invoked after
+            such an event anyways.
+
+            (-[WKContentView _selectionChanged]):
+
+            If the `triggeredByAccessibilitySelectionChange` bit is set on the incoming (post layout data) EditorState and:
+            (1) UIKit isn't in the middle of handling a gesture that modifies the selection, and...
+            (2) We aren't expecting a call to `-endSelectionChange`.
+
+            ...then we force UIKeyboardImpl to reload its UI (text suggestions, B/I/U state, etc.) using the incoming
+            EditorState. This allows accessibility-driven selection changes using Switch Control to behave like gesture-
+            based selection changes.
+
+            * WebProcess/WebCoreSupport/WebEditorClient.cpp:
+            (WebKit::WebEditorClient::willChangeSelectionForAccessibility):
+            (WebKit::WebEditorClient::didChangeSelectionForAccessibility):
+
+            Implement a couple of client hooks in WebKit2 so that we set a boolean flag on WebPage,
+            `m_isChangingSelectionForAccessibility`, to `true` over the scope of an AX-driven selection change. See related
+            AX changes in WebCore for more details.
+
+            * WebProcess/WebCoreSupport/WebEditorClient.h:
+            * WebProcess/WebPage/WebPage.cpp:
+            (WebKit::WebPage::hasPendingEditorStateUpdate const):
+
+            Add a helper method so that we can conveniently check whether we have a pending editor state update to flush,
+            since the previous boolean flag is now a tri-state enum (`m_pendingEditorStateUpdateStatus`).
+
+            (WebKit::WebPage::editorState const):
+            (WebKit::WebPage::willCommitLayerTree):
+            (WebKit::WebPage::sendEditorStateUpdate):
+            (WebKit::WebPage::scheduleFullEditorStateUpdate):
+
+            If we're in the scope of an accessibility selection change, transition `m_pendingEditorStateUpdateStatus` to
+            `ScheduledDuringAccessibilitySelectionChange`; otherwise, just use `Scheduled`.
+
+            (WebKit::WebPage::flushPendingEditorStateUpdate):
+
+            Refactor `m_hasPendingEditorStateUpdate` so that it now captures three states:
+            (1) We haven't scheduled an editor state update yet.
+            (2) We've scheduled an editor state update that was not triggered by accessibility.
+            (3) We've scheduled an editor state update that was triggered by accessibility.
+
+            The distinction between (2) and (3) allows us to populate EditorState's new
+            `triggeredByAccessibilitySelectionChange` flag.
+
+            * WebProcess/WebPage/WebPage.h:
+            (WebKit::WebPage::willChangeSelectionForAccessibility):
+            (WebKit::WebPage::didChangeSelectionForAccessibility):
+
+2021-07-19  Russell Epstein  <repst...@apple.com>
+
         Cherry-pick r279975. rdar://problem/80788597
 
     [iOS] WKMouseGestureRecognizer should not have failure requirements on WKDeferringGestureRecognizer

Modified: branches/safari-612.1.24.0-branch/Source/WebKit/Shared/EditorState.cpp (280070 => 280071)


--- branches/safari-612.1.24.0-branch/Source/WebKit/Shared/EditorState.cpp	2021-07-20 02:18:41 UTC (rev 280070)
+++ branches/safari-612.1.24.0-branch/Source/WebKit/Shared/EditorState.cpp	2021-07-20 02:18:47 UTC (rev 280071)
@@ -45,6 +45,7 @@
     encoder << isInPasswordField;
     encoder << isInPlugin;
     encoder << hasComposition;
+    encoder << triggeredByAccessibilitySelectionChange;
     encoder << isMissingPostLayoutData;
     if (!isMissingPostLayoutData)
         m_postLayoutData.encode(encoder);
@@ -85,6 +86,9 @@
     if (!decoder.decode(result.hasComposition))
         return false;
 
+    if (!decoder.decode(result.triggeredByAccessibilitySelectionChange))
+        return false;
+
     if (!decoder.decode(result.isMissingPostLayoutData))
         return false;
 
@@ -281,6 +285,8 @@
         ts.dumpProperty("isInPlugin", editorState.isInPlugin);
     if (editorState.hasComposition)
         ts.dumpProperty("hasComposition", editorState.hasComposition);
+    if (editorState.triggeredByAccessibilitySelectionChange)
+        ts.dumpProperty("triggeredByAccessibilitySelectionChange", editorState.triggeredByAccessibilitySelectionChange);
     if (editorState.isMissingPostLayoutData)
         ts.dumpProperty("isMissingPostLayoutData", editorState.isMissingPostLayoutData);
 

Modified: branches/safari-612.1.24.0-branch/Source/WebKit/Shared/EditorState.h (280070 => 280071)


--- branches/safari-612.1.24.0-branch/Source/WebKit/Shared/EditorState.h	2021-07-20 02:18:41 UTC (rev 280070)
+++ branches/safari-612.1.24.0-branch/Source/WebKit/Shared/EditorState.h	2021-07-20 02:18:47 UTC (rev 280071)
@@ -81,6 +81,7 @@
     bool isInPasswordField { false };
     bool isInPlugin { false };
     bool hasComposition { false };
+    bool triggeredByAccessibilitySelectionChange { false };
     bool isMissingPostLayoutData { true };
 
     struct PostLayoutData {

Modified: branches/safari-612.1.24.0-branch/Source/WebKit/UIProcess/ios/WKContentViewInteraction.h (280070 => 280071)


--- branches/safari-612.1.24.0-branch/Source/WebKit/UIProcess/ios/WKContentViewInteraction.h	2021-07-20 02:18:41 UTC (rev 280070)
+++ branches/safari-612.1.24.0-branch/Source/WebKit/UIProcess/ios/WKContentViewInteraction.h	2021-07-20 02:18:47 UTC (rev 280071)
@@ -463,6 +463,7 @@
     RetainPtr<NSDictionary> _additionalContextForStrongPasswordAssistance;
 
     std::optional<UChar32> _lastInsertedCharacterToOverrideCharacterBeforeSelection;
+    unsigned _selectionChangeNestingLevel;
 
 #if ENABLE(DRAG_SUPPORT)
     WebKit::DragDropInteractionState _dragDropInteractionState;

Modified: branches/safari-612.1.24.0-branch/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm (280070 => 280071)


--- branches/safari-612.1.24.0-branch/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm	2021-07-20 02:18:41 UTC (rev 280070)
+++ branches/safari-612.1.24.0-branch/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm	2021-07-20 02:18:47 UTC (rev 280071)
@@ -4938,6 +4938,8 @@
 
 - (void)beginSelectionChange
 {
+    _selectionChangeNestingLevel++;
+
     [self.inputDelegate selectionWillChange:self];
 }
 
@@ -4944,6 +4946,11 @@
 - (void)endSelectionChange
 {
     [self.inputDelegate selectionDidChange:self];
+
+    if (_selectionChangeNestingLevel)
+        _selectionChangeNestingLevel--;
+    else
+        ASSERT_NOT_REACHED();
 }
 
 - (void)willFinishIgnoringCalloutBarFadeAfterPerformingAction
@@ -7203,6 +7210,12 @@
         _lastInsertedCharacterToOverrideCharacterBeforeSelection = std::nullopt;
         if (!_usingGestureForSelection && _focusedElementInformation.autocapitalizeType == WebCore::AutocapitalizeType::Words)
             [UIKeyboardImpl.sharedInstance clearShiftState];
+
+        if (!_usingGestureForSelection && !_selectionChangeNestingLevel && _page->editorState().triggeredByAccessibilitySelectionChange) {
+            // Force UIKit to reload all EditorState-based UI; in particular, this includes text candidates.
+            [self beginSelectionChange];
+            [self endSelectionChange];
+        }
     }
 }
 

Modified: branches/safari-612.1.24.0-branch/Source/WebKit/WebProcess/WebCoreSupport/WebEditorClient.cpp (280070 => 280071)


--- branches/safari-612.1.24.0-branch/Source/WebKit/WebProcess/WebCoreSupport/WebEditorClient.cpp	2021-07-20 02:18:41 UTC (rev 280070)
+++ branches/safari-612.1.24.0-branch/Source/WebKit/WebProcess/WebCoreSupport/WebEditorClient.cpp	2021-07-20 02:18:47 UTC (rev 280071)
@@ -587,6 +587,16 @@
     m_page->send(Messages::WebPageProxy::RequestCheckingOfString(requestID, request.data(), insertionPointFromCurrentSelection(currentSelection)));
 }
 
+void WebEditorClient::willChangeSelectionForAccessibility()
+{
+    m_page->willChangeSelectionForAccessibility();
+}
+
+void WebEditorClient::didChangeSelectionForAccessibility()
+{
+    m_page->didChangeSelectionForAccessibility();
+}
+
 void WebEditorClient::willSetInputMethodState()
 {
 }

Modified: branches/safari-612.1.24.0-branch/Source/WebKit/WebProcess/WebCoreSupport/WebEditorClient.h (280070 => 280071)


--- branches/safari-612.1.24.0-branch/Source/WebKit/WebProcess/WebCoreSupport/WebEditorClient.h	2021-07-20 02:18:41 UTC (rev 280070)
+++ branches/safari-612.1.24.0-branch/Source/WebKit/WebProcess/WebCoreSupport/WebEditorClient.h	2021-07-20 02:18:47 UTC (rev 280071)
@@ -191,6 +191,9 @@
     bool shouldSuppressPasswordEcho() const final;
 #endif
 
+    void willChangeSelectionForAccessibility() final;
+    void didChangeSelectionForAccessibility() final;
+
     bool performTwoStepDrop(WebCore::DocumentFragment&, const WebCore::SimpleRange&, bool isMove) final;
     bool supportsGlobalSelection() final;
 

Modified: branches/safari-612.1.24.0-branch/Source/WebKit/WebProcess/WebPage/WebPage.cpp (280070 => 280071)


--- branches/safari-612.1.24.0-branch/Source/WebKit/WebProcess/WebPage/WebPage.cpp	2021-07-20 02:18:41 UTC (rev 280070)
+++ branches/safari-612.1.24.0-branch/Source/WebKit/WebProcess/WebPage/WebPage.cpp	2021-07-20 02:18:47 UTC (rev 280071)
@@ -1194,6 +1194,11 @@
 }
 #endif
 
+bool WebPage::hasPendingEditorStateUpdate() const
+{
+    return m_pendingEditorStateUpdateStatus != PendingEditorStateUpdateStatus::NotScheduled;
+}
+
 EditorState WebPage::editorState(ShouldPerformLayout shouldPerformLayout) const
 {
     // Ref the frame because this function may perform layout, which may cause frame destruction.
@@ -1221,6 +1226,7 @@
     result.isInPasswordField = selection.isInPasswordField();
     result.hasComposition = editor.hasComposition();
     result.shouldIgnoreSelectionChanges = editor.ignoreSelectionChanges() || (editor.client() && !editor.client()->shouldRevealCurrentSelectionAfterInsertion());
+    result.triggeredByAccessibilitySelectionChange = m_pendingEditorStateUpdateStatus == PendingEditorStateUpdateStatus::ScheduledDuringAccessibilitySelectionChange || m_isChangingSelectionForAccessibility;
 
     Ref<Document> document = *frame->document();
     result.originIdentifierForPasteboard = document->originIdentifierForPasteboard();
@@ -4119,9 +4125,9 @@
     m_pendingPageExtendedBackgroundColorChange = false;
     m_pendingSampledPageTopColorChange = false;
 
-    if (m_hasPendingEditorStateUpdate) {
+    if (hasPendingEditorStateUpdate()) {
         layerTransaction.setEditorState(editorState());
-        m_hasPendingEditorStateUpdate = false;
+        m_pendingEditorStateUpdateStatus = PendingEditorStateUpdateStatus::NotScheduled;
     }
 }
 
@@ -6443,7 +6449,7 @@
     if (frame.editor().ignoreSelectionChanges())
         return;
 
-    m_hasPendingEditorStateUpdate = false;
+    m_pendingEditorStateUpdateStatus = PendingEditorStateUpdateStatus::NotScheduled;
 
     // If we immediately dispatch an EditorState update to the UI process, layout may not be up to date yet.
     // If that is the case, just send what we have (i.e. don't include post-layout data) and wait until the
@@ -6457,10 +6463,17 @@
 
 void WebPage::scheduleFullEditorStateUpdate()
 {
-    if (m_hasPendingEditorStateUpdate)
+    if (hasPendingEditorStateUpdate()) {
+        if (m_isChangingSelectionForAccessibility)
+            m_pendingEditorStateUpdateStatus = PendingEditorStateUpdateStatus::ScheduledDuringAccessibilitySelectionChange;
         return;
+    }
 
-    m_hasPendingEditorStateUpdate = true;
+    if (m_isChangingSelectionForAccessibility)
+        m_pendingEditorStateUpdateStatus = PendingEditorStateUpdateStatus::ScheduledDuringAccessibilitySelectionChange;
+    else
+        m_pendingEditorStateUpdateStatus = PendingEditorStateUpdateStatus::Scheduled;
+
     // FIXME: Scheduling a compositing layer flush here can be more expensive than necessary.
     // Instead, we should just compute and send post-layout editor state during the next frame.
     m_drawingArea->triggerRenderingUpdate();
@@ -6520,7 +6533,7 @@
 
 void WebPage::flushPendingEditorStateUpdate()
 {
-    if (!m_hasPendingEditorStateUpdate)
+    if (!hasPendingEditorStateUpdate())
         return;
 
     Frame& frame = m_page->focusController().focusedOrMainFrame();

Modified: branches/safari-612.1.24.0-branch/Source/WebKit/WebProcess/WebPage/WebPage.h (280070 => 280071)


--- branches/safari-612.1.24.0-branch/Source/WebKit/WebProcess/WebPage/WebPage.h	2021-07-20 02:18:41 UTC (rev 280070)
+++ branches/safari-612.1.24.0-branch/Source/WebKit/WebProcess/WebPage/WebPage.h	2021-07-20 02:18:47 UTC (rev 280071)
@@ -551,7 +551,7 @@
     WebCore::WebGLLoadPolicy resolveWebGLPolicyForURL(WebFrame*, const URL&);
 #endif
     
-    enum class ShouldPerformLayout { Default, Yes };
+    enum class ShouldPerformLayout : bool { Default, Yes };
     EditorState editorState(ShouldPerformLayout = ShouldPerformLayout::Default) const;
     void updateEditorStateAfterLayoutIfEditabilityChanged();
 
@@ -823,6 +823,9 @@
     void requestDocumentEditingContext(WebKit::DocumentEditingContextRequest, CompletionHandler<void(WebKit::DocumentEditingContext)>&&);
 #endif
 
+    void willChangeSelectionForAccessibility() { m_isChangingSelectionForAccessibility = true; }
+    void didChangeSelectionForAccessibility() { m_isChangingSelectionForAccessibility = false; }
+
 #if PLATFORM(IOS_FAMILY) && ENABLE(IOS_TOUCH_EVENTS)
     void dispatchAsynchronousTouchEvents(Vector<std::pair<WebTouchEvent, CompletionHandler<void(bool)>>, 1>&&);
     void cancelAsynchronousTouchEvents(Vector<std::pair<WebTouchEvent, CompletionHandler<void(bool)>>, 1>&&);
@@ -1913,6 +1916,8 @@
 
     void platformIsPlayingMediaDidChange();
 
+    bool hasPendingEditorStateUpdate() const;
+
     WebCore::PageIdentifier m_identifier;
 
     std::unique_ptr<WebCore::Page> m_page;
@@ -2175,8 +2180,14 @@
     bool m_pendingThemeColorChange { false };
     bool m_pendingPageExtendedBackgroundColorChange { false };
     bool m_pendingSampledPageTopColorChange { false };
-    bool m_hasPendingEditorStateUpdate { false };
 
+    enum class PendingEditorStateUpdateStatus : uint8_t {
+        NotScheduled,
+        Scheduled,
+        ScheduledDuringAccessibilitySelectionChange,
+    };
+    PendingEditorStateUpdateStatus m_pendingEditorStateUpdateStatus { PendingEditorStateUpdateStatus::NotScheduled };
+
 #if ENABLE(IOS_TOUCH_EVENTS)
     CompletionHandler<void(bool)> m_pendingSynchronousTouchEventReply;
 #endif
@@ -2265,6 +2276,7 @@
     bool m_mainFrameProgressCompleted { false };
     bool m_shouldDispatchFakeMouseMoveEvents { true };
     bool m_isSelectingTextWhileInsertingAsynchronously { false };
+    bool m_isChangingSelectionForAccessibility { false };
 
     enum class EditorStateIsContentEditable { No, Yes, Unset };
     mutable EditorStateIsContentEditable m_lastEditorStateWasContentEditable { EditorStateIsContentEditable::Unset };
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to