<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[279310] trunk/Source/WebKit</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/279310">279310</a></dd>
<dt>Author</dt> <dd>wenson_hsieh@apple.com</dd>
<dt>Date</dt> <dd>2021-06-26 19:06:45 -0700 (Sat, 26 Jun 2021)</dd>
</dl>

<h3>Log Message</h3>
<pre>[iOS] Safari sometimes hangs under sync IPC in `-[WKWebView _setSuppressSoftwareKeyboard:]`
https://bugs.webkit.org/show_bug.cgi?id=227424
rdar://79745385

Reviewed by Tim Horton.

When activating streamlined AutoFill, Safari calls UIKit SPI (`-_setSuppressSoftwareKeyboard:`) on WKWebView to
ensure that the normal software keyboard doesn't briefly appear instead of the AutoFill input view; after
requesting AutoFill credentials, Safari then stops suppressing the software keyboard by setting the SPI property
back to NO. In WebKit, we override `-[WKWebView _setSuppressSoftwareKeyboard:]`, such that WKContentView's
keyboard suppression state follows the web view's state (this is necessary, since WKContentView is the actual
`-firstResponder` when editing focused text inputs). However, when changing software keyboard suppression from
YES to NO, UIKit reloads input views and (in the process) calls into
`-requestAutocorrectionContextWithCompletionHandler:`, which then makes a sync IPC call into the web process.

To avoid this sync IPC call, we refactor the implementation of `-[WKWebView _setSuppressSoftwareKeyboard:]`,
such that we don't immediately attempt to unsuppress the software keyboard by calling into WKContentView.
Instead, we asynchronously request an autocorrection context from the web process, and then call
`-[WKContentView _setSuppressSoftwareKeyboard:NO]` after the autocorrection context request completes (using
the last known autocorrection context data in the UI process rather than making a sync IPC call).

* UIProcess/API/ios/WKWebViewIOS.mm:
(-[WKWebView _setSuppressSoftwareKeyboard:]):

Call into `-updateSoftwareKeyboardSuppressionStateFromWebView` below.

* UIProcess/ios/WKContentViewInteraction.h:
* UIProcess/ios/WKContentViewInteraction.mm:
(-[WKContentView cleanUpInteraction]):

Invoke `-unsuppressSoftwareKeyboardUsingLastAutocorrectionContextIfNeeded` to ensure that we stop software
keyboard suppression if the web process terminates while we're waiting for autocorrection context data to
arrive.

(-[WKContentView requestAutocorrectionContextWithCompletionHandler:]):
(-[WKContentView _handleAutocorrectionContext:]):
(-[WKContentView updateSoftwareKeyboardSuppressionStateFromWebView]):

Add a new helper that keeps WKContentView's software keyboard suppression state in sync with the WKWebView's
software keyboard suppression state. In the case where we're supressing the software keyboard, we can simply
call into `-[WKContentView _setSuppressSoftwareKeyboard:]` right away, since UIKit won't try to request an
autocorrection context.

However, in the case where we're unsuppressing the software keyboard, set a new flag, don't immediately forward
the call to WKContentView. Instead, set the `_unsuppressSoftwareKeyboardAfterNextAutocorrectionContextUpdate`
flag to YES and call into WebPageProxy to request an updated autocorrection context. Upon receiving the response
in `-[WKContentView _handleAutocorrectionContext:]`, we then unset the flag and unsuppress the software keyboard
(crucially, using `_lastAutocorrectionContext` instead of making a synchronous call back to the web content
process).

(-[WKContentView unsuppressSoftwareKeyboardUsingLastAutocorrectionContextIfNeeded]):</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkSourceWebKitChangeLog">trunk/Source/WebKit/ChangeLog</a></li>
<li><a href="#trunkSourceWebKitUIProcessAPIiosWKWebViewIOSmm">trunk/Source/WebKit/UIProcess/API/ios/WKWebViewIOS.mm</a></li>
<li><a href="#trunkSourceWebKitUIProcessiosWKContentViewInteractionh">trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.h</a></li>
<li><a href="#trunkSourceWebKitUIProcessiosWKContentViewInteractionmm">trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkSourceWebKitChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit/ChangeLog (279309 => 279310)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit/ChangeLog    2021-06-27 01:07:51 UTC (rev 279309)
+++ trunk/Source/WebKit/ChangeLog       2021-06-27 02:06:45 UTC (rev 279310)
</span><span class="lines">@@ -1,3 +1,57 @@
</span><ins>+2021-06-26  Wenson Hsieh  <wenson_hsieh@apple.com>
+
+        [iOS] Safari sometimes hangs under sync IPC in `-[WKWebView _setSuppressSoftwareKeyboard:]`
+        https://bugs.webkit.org/show_bug.cgi?id=227424
+        rdar://79745385
+
+        Reviewed by Tim Horton.
+
+        When activating streamlined AutoFill, Safari calls UIKit SPI (`-_setSuppressSoftwareKeyboard:`) on WKWebView to
+        ensure that the normal software keyboard doesn't briefly appear instead of the AutoFill input view; after
+        requesting AutoFill credentials, Safari then stops suppressing the software keyboard by setting the SPI property
+        back to NO. In WebKit, we override `-[WKWebView _setSuppressSoftwareKeyboard:]`, such that WKContentView's
+        keyboard suppression state follows the web view's state (this is necessary, since WKContentView is the actual
+        `-firstResponder` when editing focused text inputs). However, when changing software keyboard suppression from
+        YES to NO, UIKit reloads input views and (in the process) calls into
+        `-requestAutocorrectionContextWithCompletionHandler:`, which then makes a sync IPC call into the web process.
+
+        To avoid this sync IPC call, we refactor the implementation of `-[WKWebView _setSuppressSoftwareKeyboard:]`,
+        such that we don't immediately attempt to unsuppress the software keyboard by calling into WKContentView.
+        Instead, we asynchronously request an autocorrection context from the web process, and then call
+        `-[WKContentView _setSuppressSoftwareKeyboard:NO]` after the autocorrection context request completes (using
+        the last known autocorrection context data in the UI process rather than making a sync IPC call).
+
+        * UIProcess/API/ios/WKWebViewIOS.mm:
+        (-[WKWebView _setSuppressSoftwareKeyboard:]):
+
+        Call into `-updateSoftwareKeyboardSuppressionStateFromWebView` below.
+
+        * UIProcess/ios/WKContentViewInteraction.h:
+        * UIProcess/ios/WKContentViewInteraction.mm:
+        (-[WKContentView cleanUpInteraction]):
+
+        Invoke `-unsuppressSoftwareKeyboardUsingLastAutocorrectionContextIfNeeded` to ensure that we stop software
+        keyboard suppression if the web process terminates while we're waiting for autocorrection context data to
+        arrive.
+
+        (-[WKContentView requestAutocorrectionContextWithCompletionHandler:]):
+        (-[WKContentView _handleAutocorrectionContext:]):
+        (-[WKContentView updateSoftwareKeyboardSuppressionStateFromWebView]):
+
+        Add a new helper that keeps WKContentView's software keyboard suppression state in sync with the WKWebView's
+        software keyboard suppression state. In the case where we're supressing the software keyboard, we can simply
+        call into `-[WKContentView _setSuppressSoftwareKeyboard:]` right away, since UIKit won't try to request an
+        autocorrection context.
+
+        However, in the case where we're unsuppressing the software keyboard, set a new flag, don't immediately forward
+        the call to WKContentView. Instead, set the `_unsuppressSoftwareKeyboardAfterNextAutocorrectionContextUpdate`
+        flag to YES and call into WebPageProxy to request an updated autocorrection context. Upon receiving the response
+        in `-[WKContentView _handleAutocorrectionContext:]`, we then unset the flag and unsuppress the software keyboard
+        (crucially, using `_lastAutocorrectionContext` instead of making a synchronous call back to the web content
+        process).
+
+        (-[WKContentView unsuppressSoftwareKeyboardUsingLastAutocorrectionContextIfNeeded]):
+
</ins><span class="cx"> 2021-06-25  Brent Fulgham  <bfulgham@apple.com>
</span><span class="cx"> 
</span><span class="cx">         [macOS] Add logging and clean up AppSSO flows 
</span></span></pre></div>
<a id="trunkSourceWebKitUIProcessAPIiosWKWebViewIOSmm"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit/UIProcess/API/ios/WKWebViewIOS.mm (279309 => 279310)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit/UIProcess/API/ios/WKWebViewIOS.mm    2021-06-27 01:07:51 UTC (rev 279309)
+++ trunk/Source/WebKit/UIProcess/API/ios/WKWebViewIOS.mm       2021-06-27 02:06:45 UTC (rev 279310)
</span><span class="lines">@@ -3083,8 +3083,8 @@
</span><span class="cx"> 
</span><span class="cx"> - (void)_setSuppressSoftwareKeyboard:(BOOL)suppressSoftwareKeyboard
</span><span class="cx"> {
</span><del>-    [super _setSuppressSoftwareKeyboard:suppressSoftwareKeyboard];
-    [_contentView _setSuppressSoftwareKeyboard:suppressSoftwareKeyboard];
</del><ins>+    super._suppressSoftwareKeyboard = suppressSoftwareKeyboard;
+    [_contentView updateSoftwareKeyboardSuppressionStateFromWebView];
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> - (void)_snapshotRect:(CGRect)rectInViewCoordinates intoImageOfWidth:(CGFloat)imageWidth completionHandler:(void(^)(CGImageRef))completionHandler
</span></span></pre></div>
<a id="trunkSourceWebKitUIProcessiosWKContentViewInteractionh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.h (279309 => 279310)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.h     2021-06-27 01:07:51 UTC (rev 279309)
+++ trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.h        2021-06-27 02:06:45 UTC (rev 279310)
</span><span class="lines">@@ -441,6 +441,8 @@
</span><span class="cx">     BOOL _isFocusingElementWithKeyboard;
</span><span class="cx">     BOOL _isBlurringFocusedElement;
</span><span class="cx">     BOOL _isRelinquishingFirstResponderToFocusedElement;
</span><ins>+    BOOL _unsuppressSoftwareKeyboardAfterNextAutocorrectionContextUpdate;
+    BOOL _isUnsuppressingSoftwareKeyboardUsingLastAutocorrectionContext;
</ins><span class="cx"> 
</span><span class="cx">     BOOL _focusRequiresStrongPasswordAssistance;
</span><span class="cx">     BOOL _waitingForEditDragSnapshot;
</span><span class="lines">@@ -709,6 +711,8 @@
</span><span class="cx"> - (void)_didChangeLinkPreviewAvailability;
</span><span class="cx"> - (void)setContinuousSpellCheckingEnabled:(BOOL)enabled;
</span><span class="cx"> 
</span><ins>+- (void)updateSoftwareKeyboardSuppressionStateFromWebView;
+
</ins><span class="cx"> #if USE(UICONTEXTMENU)
</span><span class="cx"> - (UIView *)textEffectsWindow;
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceWebKitUIProcessiosWKContentViewInteractionmm"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm (279309 => 279310)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm    2021-06-27 01:07:51 UTC (rev 279309)
+++ trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm       2021-06-27 02:06:45 UTC (rev 279310)
</span><span class="lines">@@ -1180,6 +1180,7 @@
</span><span class="cx">     [self _removeContainerForContextMenuHintPreviews];
</span><span class="cx">     [self _removeContainerForDragPreviews];
</span><span class="cx">     [self _removeContainerForDropPreviews];
</span><ins>+    [self unsuppressSoftwareKeyboardUsingLastAutocorrectionContextIfNeeded];
</ins><span class="cx"> 
</span><span class="cx">     _hasSetUpInteractions = NO;
</span><span class="cx">     _suppressSelectionAssistantReasons = { };
</span><span class="lines">@@ -4623,7 +4624,20 @@
</span><span class="cx">         return;
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    if (_page->isRunningModalJavaScriptDialog() || _domPasteRequestHandler) {
</del><ins>+    bool respondWithLastKnownAutocorrectionContext = ([&] {
+        if (_page->isRunningModalJavaScriptDialog())
+            return true;
+
+        if (_domPasteRequestHandler)
+            return true;
+
+        if (_isUnsuppressingSoftwareKeyboardUsingLastAutocorrectionContext)
+            return true;
+
+        return false;
+    })();
+
+    if (respondWithLastKnownAutocorrectionContext) {
</ins><span class="cx">         completionHandler([WKAutocorrectionContext autocorrectionContextWithWebContext:_lastAutocorrectionContext]);
</span><span class="cx">         return;
</span><span class="cx">     }
</span><span class="lines">@@ -4649,8 +4663,34 @@
</span><span class="cx"> {
</span><span class="cx">     _lastAutocorrectionContext = context;
</span><span class="cx">     [self _invokePendingAutocorrectionContextHandler:[WKAutocorrectionContext autocorrectionContextWithWebContext:context]];
</span><ins>+    [self unsuppressSoftwareKeyboardUsingLastAutocorrectionContextIfNeeded];
</ins><span class="cx"> }
</span><span class="cx"> 
</span><ins>+- (void)updateSoftwareKeyboardSuppressionStateFromWebView
+{
+    BOOL webViewIsSuppressingSoftwareKeyboard = [_webView _suppressSoftwareKeyboard];
+    if (webViewIsSuppressingSoftwareKeyboard) {
+        _unsuppressSoftwareKeyboardAfterNextAutocorrectionContextUpdate = NO;
+        self._suppressSoftwareKeyboard = webViewIsSuppressingSoftwareKeyboard;
+        return;
+    }
+
+    if (self._suppressSoftwareKeyboard == webViewIsSuppressingSoftwareKeyboard)
+        return;
+
+    if (!std::exchange(_unsuppressSoftwareKeyboardAfterNextAutocorrectionContextUpdate, YES))
+        _page->requestAutocorrectionContext();
+}
+
+- (void)unsuppressSoftwareKeyboardUsingLastAutocorrectionContextIfNeeded
+{
+    if (!std::exchange(_unsuppressSoftwareKeyboardAfterNextAutocorrectionContextUpdate, NO))
+        return;
+
+    SetForScope<BOOL> unsuppressSoftwareKeyboardScope { _isUnsuppressingSoftwareKeyboardUsingLastAutocorrectionContext, YES };
+    self._suppressSoftwareKeyboard = NO;
+}
+
</ins><span class="cx"> - (void)runModalJavaScriptDialog:(CompletionHandler<void()>&&)callback
</span><span class="cx"> {
</span><span class="cx">     if (_isFocusingElementWithKeyboard)
</span></span></pre>
</div>
</div>

</body>
</html>