- Revision
- 261812
- Author
- [email protected]
- Date
- 2020-05-18 08:23:09 -0700 (Mon, 18 May 2020)
Log Message
[iOS] Unexpected capitalization of next word after repositioning caret
https://bugs.webkit.org/show_bug.cgi?id=211969
<rdar://problem/62605526>
Reviewed by Alex Christensen.
Source/WebKit:
The changes in r242551 refactored synchronous autocorrection context request logic such that it uses
`waitForAndDispatchImmediately` instead of `sendSync`, in order to make it interruptible by unbounded sync IPC
sent from the web process. If the UI process receives sync IPC, it will immediately cancel the autocorrection
context request (returning an empty context), before proceeding to handle the incoming sync IPC.
In a more recent version of iOS, other changes around spellchecking have caused the synchronous message
`WebPageProxy::checkTextOfParagraph` to be sent from the web process in such a way that it now frequently
coincides with the synchronous autocorrection context request being sent from the UI process. The result is that
we now frequently end up cancelling autocorrection requests early by responding with empty contexts. This
manifests in the keyboard sometimes losing information about its autocapitalization context and believing that
it is in an empty text field, which reverts to default autocorrection suggestions and autocapitalizes the
software keyboard.
To fix this, instead of using the `InterruptWaitingIfSyncMessageArrives` option when waiting for the IPC
response, add and use a new flag that allows us to process an incoming sync IPC message if we're waiting for
the sync message response. We use this new IPC flag when waiting synchronously for HandleAutocorrectionContext.
Test: editing/selection/ios/changing-selection-does-not-trigger-autocapitalization.html
* Platform/IPC/Connection.cpp:
(IPC::Connection::processIncomingMessage):
If the new IPC flag is set and the incoming message is synchronous, allow it to immediately dispatch the sync
message by enqueueing it and then waking up `m_waitForMessageCondition` so that it can process the message.
* Platform/IPC/Connection.h:
* Platform/spi/ios/UIKitSPI.h:
* UIProcess/ios/WKContentViewInteraction.mm:
(-[WKContentView requestAutocorrectionContextWithCompletionHandler:]):
Use `DispatchIncomingSyncMessagesWhileWaiting` instead of `InterruptWaitingIfSyncMessageArrives`.
Tools:
Add a new UIScriptController hook to query whether or not the software keyboard is in shifted state.
* TestRunnerShared/UIScriptContext/Bindings/UIScriptController.idl:
* TestRunnerShared/UIScriptContext/UIScriptController.h:
(WTR::UIScriptController::keyboardIsAutomaticallyShifted const):
* TestWebKitAPI/Tests/WebKitCocoa/WKAttachmentTests.mm:
Adjust a couple of API tests that copy and paste back-to-back, so that they wait for the copy to finish before
attempting to paste.
* WebKitTestRunner/ios/UIScriptControllerIOS.h:
* WebKitTestRunner/ios/UIScriptControllerIOS.mm:
(WTR::UIScriptControllerIOS::keyboardIsAutomaticallyShifted const):
LayoutTests:
Add a new layout test to verify that the keyboard does not automatically shift (i.e. autocapitalize) when
changing selection quickly inside a text field.
* editing/selection/ios/changing-selection-does-not-trigger-autocapitalization-expected.txt: Added.
* editing/selection/ios/changing-selection-does-not-trigger-autocapitalization.html: Added.
* resources/ui-helper.js:
(window.UIHelper.keyboardIsAutomaticallyShifted):
Modified Paths
Added Paths
Diff
Modified: trunk/LayoutTests/ChangeLog (261811 => 261812)
--- trunk/LayoutTests/ChangeLog 2020-05-18 15:00:17 UTC (rev 261811)
+++ trunk/LayoutTests/ChangeLog 2020-05-18 15:23:09 UTC (rev 261812)
@@ -1,3 +1,19 @@
+2020-05-18 Wenson Hsieh <[email protected]>
+
+ [iOS] Unexpected capitalization of next word after repositioning caret
+ https://bugs.webkit.org/show_bug.cgi?id=211969
+ <rdar://problem/62605526>
+
+ Reviewed by Alex Christensen.
+
+ Add a new layout test to verify that the keyboard does not automatically shift (i.e. autocapitalize) when
+ changing selection quickly inside a text field.
+
+ * editing/selection/ios/changing-selection-does-not-trigger-autocapitalization-expected.txt: Added.
+ * editing/selection/ios/changing-selection-does-not-trigger-autocapitalization.html: Added.
+ * resources/ui-helper.js:
+ (window.UIHelper.keyboardIsAutomaticallyShifted):
+
2020-05-18 Diego Pino Garcia <[email protected]>
[GTK] Gardening, update test expectations after r261779
Added: trunk/LayoutTests/editing/selection/ios/changing-selection-does-not-trigger-autocapitalization-expected.txt (0 => 261812)
--- trunk/LayoutTests/editing/selection/ios/changing-selection-does-not-trigger-autocapitalization-expected.txt (rev 0)
+++ trunk/LayoutTests/editing/selection/ios/changing-selection-does-not-trigger-autocapitalization-expected.txt 2020-05-18 15:23:09 UTC (rev 261812)
@@ -0,0 +1,11 @@
+
+Verifies that tapping to change selection does not leave the software keyboard in a state where it is automatically shifted. To manually run the test, focus the input field, tap on 'world', and then tap on 'hello' such that the final selection is after the word 'hello'. The keyboard should not be in shifted state.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS keyboardIsAutomaticallyShifted is false
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
Added: trunk/LayoutTests/editing/selection/ios/changing-selection-does-not-trigger-autocapitalization.html (0 => 261812)
--- trunk/LayoutTests/editing/selection/ios/changing-selection-does-not-trigger-autocapitalization.html (rev 0)
+++ trunk/LayoutTests/editing/selection/ios/changing-selection-does-not-trigger-autocapitalization.html 2020-05-18 15:23:09 UTC (rev 261812)
@@ -0,0 +1,62 @@
+<!DOCTYPE html> <!-- webkit-test-runner [ useFlexibleViewport=true ] -->
+<html>
+<head>
+<script src=""
+<script src=""
+<meta name=viewport content="width=device-width, initial-scale=1, user-scalable=no">
+<style>
+body, html {
+ width: 100%;
+ height: 100%;
+ margin: 0;
+}
+
+input {
+ font-size: 40px;
+ width: 300px;
+ height: 100px;
+}
+</style>
+</head>
+<body>
+ <input value="hello world">
+ <p id="description"></p>
+ <p id="console"></p>
+ <script>
+ const input = document.querySelector("input");
+ jsTestIsAsync = true;
+
+ async function tapAndWaitForSelectionChange(x, y)
+ {
+ await new Promise(resolve => {
+ let doneCount = 0;
+ const checkDone = () => {
+ if (++doneCount != 2)
+ return;
+
+ document.removeEventListener("selectionchange", checkDone);
+ resolve();
+ };
+ document.addEventListener("selectionchange", checkDone);
+ UIHelper.activateAt(x, y).then(checkDone);
+ });
+ }
+
+ addEventListener("load", async () => {
+ description("Verifies that tapping to change selection does not leave the software keyboard in a state where it is automatically shifted. To manually run the test, focus the input field, tap on 'world', and then tap on 'hello' such that the final selection is after the word 'hello'. The keyboard should not be in shifted state.");
+ if (window.testRunner)
+ await UIHelper.setHardwareKeyboardAttached(false);
+
+ await UIHelper.activateElementAndWaitForInputSession(input);
+ await tapAndWaitForSelectionChange(150, 50);
+ await tapAndWaitForSelectionChange(75, 50);
+
+ await UIHelper.ensurePresentationUpdate();
+ keyboardIsAutomaticallyShifted = await UIHelper.keyboardIsAutomaticallyShifted();
+
+ shouldBeFalse("keyboardIsAutomaticallyShifted");
+ finishJSTest();
+ });
+</script>
+</body>
+</html>
Modified: trunk/LayoutTests/resources/ui-helper.js (261811 => 261812)
--- trunk/LayoutTests/resources/ui-helper.js 2020-05-18 15:00:17 UTC (rev 261811)
+++ trunk/LayoutTests/resources/ui-helper.js 2020-05-18 15:23:09 UTC (rev 261812)
@@ -308,6 +308,13 @@
});
}
+ static keyboardIsAutomaticallyShifted()
+ {
+ return new Promise(resolve => {
+ testRunner.runUIScript(`uiController.keyboardIsAutomaticallyShifted`, result => resolve(result === "true"));
+ });
+ }
+
static ensurePresentationUpdate()
{
if (!this.isWebKit2()) {
Modified: trunk/Source/WebKit/ChangeLog (261811 => 261812)
--- trunk/Source/WebKit/ChangeLog 2020-05-18 15:00:17 UTC (rev 261811)
+++ trunk/Source/WebKit/ChangeLog 2020-05-18 15:23:09 UTC (rev 261812)
@@ -1,3 +1,43 @@
+2020-05-18 Wenson Hsieh <[email protected]>
+
+ [iOS] Unexpected capitalization of next word after repositioning caret
+ https://bugs.webkit.org/show_bug.cgi?id=211969
+ <rdar://problem/62605526>
+
+ Reviewed by Alex Christensen.
+
+ The changes in r242551 refactored synchronous autocorrection context request logic such that it uses
+ `waitForAndDispatchImmediately` instead of `sendSync`, in order to make it interruptible by unbounded sync IPC
+ sent from the web process. If the UI process receives sync IPC, it will immediately cancel the autocorrection
+ context request (returning an empty context), before proceeding to handle the incoming sync IPC.
+
+ In a more recent version of iOS, other changes around spellchecking have caused the synchronous message
+ `WebPageProxy::checkTextOfParagraph` to be sent from the web process in such a way that it now frequently
+ coincides with the synchronous autocorrection context request being sent from the UI process. The result is that
+ we now frequently end up cancelling autocorrection requests early by responding with empty contexts. This
+ manifests in the keyboard sometimes losing information about its autocapitalization context and believing that
+ it is in an empty text field, which reverts to default autocorrection suggestions and autocapitalizes the
+ software keyboard.
+
+ To fix this, instead of using the `InterruptWaitingIfSyncMessageArrives` option when waiting for the IPC
+ response, add and use a new flag that allows us to process an incoming sync IPC message if we're waiting for
+ the sync message response. We use this new IPC flag when waiting synchronously for HandleAutocorrectionContext.
+
+ Test: editing/selection/ios/changing-selection-does-not-trigger-autocapitalization.html
+
+ * Platform/IPC/Connection.cpp:
+ (IPC::Connection::processIncomingMessage):
+
+ If the new IPC flag is set and the incoming message is synchronous, allow it to immediately dispatch the sync
+ message by enqueueing it and then waking up `m_waitForMessageCondition` so that it can process the message.
+
+ * Platform/IPC/Connection.h:
+ * Platform/spi/ios/UIKitSPI.h:
+ * UIProcess/ios/WKContentViewInteraction.mm:
+ (-[WKContentView requestAutocorrectionContextWithCompletionHandler:]):
+
+ Use `DispatchIncomingSyncMessagesWhileWaiting` instead of `InterruptWaitingIfSyncMessageArrives`.
+
2020-05-18 Lauro Moura <[email protected]>
webkitpy: Update test data after r261776
Modified: trunk/Source/WebKit/Platform/IPC/Connection.cpp (261811 => 261812)
--- trunk/Source/WebKit/Platform/IPC/Connection.cpp 2020-05-18 15:00:17 UTC (rev 261811)
+++ trunk/Source/WebKit/Platform/IPC/Connection.cpp 2020-05-18 15:23:09 UTC (rev 261812)
@@ -754,6 +754,11 @@
return;
}
+ if (m_waitingForMessage->waitForOptions.contains(WaitForOption::DispatchIncomingSyncMessagesWhileWaiting) && message->isSyncMessage() && SyncMessageState::singleton().processIncomingMessage(*this, message)) {
+ m_waitForMessageCondition.notifyOne();
+ return;
+ }
+
if (m_waitingForMessage->waitForOptions.contains(WaitForOption::InterruptWaitingIfSyncMessageArrives) && message->isSyncMessage()) {
m_waitingForMessage->messageWaitingInterrupted = true;
m_waitForMessageCondition.notifyOne();
Modified: trunk/Source/WebKit/Platform/IPC/Connection.h (261811 => 261812)
--- trunk/Source/WebKit/Platform/IPC/Connection.h 2020-05-18 15:00:17 UTC (rev 261811)
+++ trunk/Source/WebKit/Platform/IPC/Connection.h 2020-05-18 15:23:09 UTC (rev 261812)
@@ -74,6 +74,7 @@
enum class WaitForOption {
// Use this to make waitForMessage be interrupted immediately by any incoming sync messages.
InterruptWaitingIfSyncMessageArrives = 1 << 0,
+ DispatchIncomingSyncMessagesWhileWaiting = 1 << 1,
};
#define MESSAGE_CHECK_BASE(assertion, connection) MESSAGE_CHECK_COMPLETION_BASE(assertion, connection, (void)0)
Modified: trunk/Source/WebKit/Platform/spi/ios/UIKitSPI.h (261811 => 261812)
--- trunk/Source/WebKit/Platform/spi/ios/UIKitSPI.h 2020-05-18 15:00:17 UTC (rev 261811)
+++ trunk/Source/WebKit/Platform/spi/ios/UIKitSPI.h 2020-05-18 15:23:09 UTC (rev 261812)
@@ -321,6 +321,7 @@
- (void)addInputString:(NSString *)string withFlags:(NSUInteger)flags;
- (void)addInputString:(NSString *)string withFlags:(NSUInteger)flags withInputManagerHint:(NSString *)hint;
- (BOOL)autocorrectSpellingEnabled;
+- (BOOL)isAutoShifted;
- (void)deleteFromInput;
- (void)deleteFromInputWithFlags:(NSUInteger)flags;
- (void)replaceText:(id)replacement;
Modified: trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm (261811 => 261812)
--- trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm 2020-05-18 15:00:17 UTC (rev 261811)
+++ trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm 2020-05-18 15:23:09 UTC (rev 261812)
@@ -4238,7 +4238,7 @@
_page->requestAutocorrectionContext();
if (useSyncRequest) {
- _page->process().connection()->waitForAndDispatchImmediately<Messages::WebPageProxy::HandleAutocorrectionContext>(_page->webPageID(), 1_s, IPC::WaitForOption::InterruptWaitingIfSyncMessageArrives);
+ _page->process().connection()->waitForAndDispatchImmediately<Messages::WebPageProxy::HandleAutocorrectionContext>(_page->webPageID(), 1_s, IPC::WaitForOption::DispatchIncomingSyncMessagesWhileWaiting);
[self _cancelPendingAutocorrectionContextHandler];
}
}
Modified: trunk/Tools/ChangeLog (261811 => 261812)
--- trunk/Tools/ChangeLog 2020-05-18 15:00:17 UTC (rev 261811)
+++ trunk/Tools/ChangeLog 2020-05-18 15:23:09 UTC (rev 261812)
@@ -1,3 +1,25 @@
+2020-05-18 Wenson Hsieh <[email protected]>
+
+ [iOS] Unexpected capitalization of next word after repositioning caret
+ https://bugs.webkit.org/show_bug.cgi?id=211969
+ <rdar://problem/62605526>
+
+ Reviewed by Alex Christensen.
+
+ Add a new UIScriptController hook to query whether or not the software keyboard is in shifted state.
+
+ * TestRunnerShared/UIScriptContext/Bindings/UIScriptController.idl:
+ * TestRunnerShared/UIScriptContext/UIScriptController.h:
+ (WTR::UIScriptController::keyboardIsAutomaticallyShifted const):
+ * TestWebKitAPI/Tests/WebKitCocoa/WKAttachmentTests.mm:
+
+ Adjust a couple of API tests that copy and paste back-to-back, so that they wait for the copy to finish before
+ attempting to paste.
+
+ * WebKitTestRunner/ios/UIScriptControllerIOS.h:
+ * WebKitTestRunner/ios/UIScriptControllerIOS.mm:
+ (WTR::UIScriptControllerIOS::keyboardIsAutomaticallyShifted const):
+
2020-05-18 Carlos Garcia Campos <[email protected]>
[GTK] Add WebKitContextMenuItemType for paste as plaintext
Modified: trunk/Tools/TestRunnerShared/UIScriptContext/Bindings/UIScriptController.idl (261811 => 261812)
--- trunk/Tools/TestRunnerShared/UIScriptContext/Bindings/UIScriptController.idl 2020-05-18 15:00:17 UTC (rev 261811)
+++ trunk/Tools/TestRunnerShared/UIScriptContext/Bindings/UIScriptController.idl 2020-05-18 15:23:09 UTC (rev 261812)
@@ -80,6 +80,8 @@
void toggleCapsLock(object callback);
void setContinuousSpellCheckingEnabled(boolean enabled);
+ readonly attribute boolean keyboardIsAutomaticallyShifted;
+
void rawKeyDown(DOMString key);
void rawKeyUp(DOMString key);
Modified: trunk/Tools/TestRunnerShared/UIScriptContext/UIScriptController.h (261811 => 261812)
--- trunk/Tools/TestRunnerShared/UIScriptContext/UIScriptController.h 2020-05-18 15:00:17 UTC (rev 261811)
+++ trunk/Tools/TestRunnerShared/UIScriptContext/UIScriptController.h 2020-05-18 15:23:09 UTC (rev 261812)
@@ -163,6 +163,11 @@
virtual void keyDown(JSStringRef character, JSValueRef modifierArray) { notImplemented(); }
virtual void toggleCapsLock(JSValueRef callback) { notImplemented(); }
virtual void setContinuousSpellCheckingEnabled(bool) { notImplemented(); }
+ virtual bool keyboardIsAutomaticallyShifted() const
+ {
+ notImplemented();
+ return false;
+ }
virtual void rawKeyDown(JSStringRef) { notImplemented(); }
virtual void rawKeyUp(JSStringRef) { notImplemented(); }
Modified: trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/WKAttachmentTests.mm (261811 => 261812)
--- trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/WKAttachmentTests.mm 2020-05-18 15:00:17 UTC (rev 261811)
+++ trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/WKAttachmentTests.mm 2020-05-18 15:23:09 UTC (rev 261812)
@@ -1154,7 +1154,7 @@
observer.expectAttachmentUpdates(@[ ], @[ originalAttachment.get() ]);
}
[webView selectAll:nil];
- [webView _executeEditCommand:@"Copy" argument:nil completion:nil];
+ [webView _synchronouslyExecuteEditCommand:@"Copy" argument:nil];
[webView evaluateJavaScript:@"getSelection().collapseToEnd()" completionHandler:nil];
{
ObserveAttachmentUpdatesForScope observer(webView.get());
@@ -1196,7 +1196,7 @@
observer.expectAttachmentUpdates(@[ ], @[ originalAttachment.get() ]);
}
[webView selectAll:nil];
- [webView _executeEditCommand:@"Copy" argument:nil completion:nil];
+ [webView _synchronouslyExecuteEditCommand:@"Copy" argument:nil];
[webView evaluateJavaScript:@"getSelection().collapseToEnd()" completionHandler:nil];
{
ObserveAttachmentUpdatesForScope observer(webView.get());
Modified: trunk/Tools/WebKitTestRunner/ios/UIScriptControllerIOS.h (261811 => 261812)
--- trunk/Tools/WebKitTestRunner/ios/UIScriptControllerIOS.h 2020-05-18 15:00:17 UTC (rev 261811)
+++ trunk/Tools/WebKitTestRunner/ios/UIScriptControllerIOS.h 2020-05-18 15:23:09 UTC (rev 261812)
@@ -136,6 +136,7 @@
long numberOfStrokesInEditableImage() override;
void setKeyboardInputModeIdentifier(JSStringRef) override;
void toggleCapsLock(JSValueRef) override;
+ bool keyboardIsAutomaticallyShifted() const override;
JSObjectRef attachmentInfo(JSStringRef) override;
UIView *platformContentView() const override;
JSObjectRef calendarType() const override;
Modified: trunk/Tools/WebKitTestRunner/ios/UIScriptControllerIOS.mm (261811 => 261812)
--- trunk/Tools/WebKitTestRunner/ios/UIScriptControllerIOS.mm 2020-05-18 15:00:17 UTC (rev 261811)
+++ trunk/Tools/WebKitTestRunner/ios/UIScriptControllerIOS.mm 2020-05-18 15:23:09 UTC (rev 261812)
@@ -1205,6 +1205,11 @@
doAsyncTask(callback);
}
+bool UIScriptControllerIOS::keyboardIsAutomaticallyShifted() const
+{
+ return UIKeyboardImpl.activeInstance.isAutoShifted;
+}
+
JSObjectRef UIScriptControllerIOS::attachmentInfo(JSStringRef jsAttachmentIdentifier)
{
auto attachmentIdentifier = toWTFString(toWK(jsAttachmentIdentifier));