Modified: trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj (261896 => 261897)
--- trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj 2020-05-20 00:01:49 UTC (rev 261896)
+++ trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj 2020-05-20 00:05:46 UTC (rev 261897)
@@ -1014,6 +1014,7 @@
CEBCA1391E3A807A00C73293 /* page-with-csp-iframe.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = CEBCA1341E3A803400C73293 /* page-with-csp-iframe.html */; };
CEBCA13A1E3A807A00C73293 /* page-without-csp.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = CEBCA1371E3A803400C73293 /* page-without-csp.html */; };
CEBCA13B1E3A807A00C73293 /* page-without-csp-iframe.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = CEBCA1361E3A803400C73293 /* page-without-csp-iframe.html */; };
+ CED73D37246F29FA00DAE327 /* InsertTextAlternatives.mm in Sources */ = {isa = PBXBuildFile; fileRef = CED73D35246F204C00DAE327 /* InsertTextAlternatives.mm */; };
CEDA12412437C9FB00C28A9E /* editable-region-composited-and-non-composited-overlap.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = CEDA12402437C9EA00C28A9E /* editable-region-composited-and-non-composited-overlap.html */; };
D34E08761E4E42E1005FF14A /* WKWebViewGetContents.mm in Sources */ = {isa = PBXBuildFile; fileRef = D3BE5E341E4CE85E00FD563A /* WKWebViewGetContents.mm */; };
DF4B273921A47728009BD1CA /* WKNSDictionaryEmptyDictionaryCrash.mm in Sources */ = {isa = PBXBuildFile; fileRef = DF4B273821A47727009BD1CA /* WKNSDictionaryEmptyDictionaryCrash.mm */; };
@@ -2651,6 +2652,7 @@
CEBCA1351E3A803400C73293 /* page-with-csp.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = "page-with-csp.html"; sourceTree = "<group>"; };
CEBCA1361E3A803400C73293 /* page-without-csp-iframe.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = "page-without-csp-iframe.html"; sourceTree = "<group>"; };
CEBCA1371E3A803400C73293 /* page-without-csp.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = "page-without-csp.html"; sourceTree = "<group>"; };
+ CED73D35246F204C00DAE327 /* InsertTextAlternatives.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = InsertTextAlternatives.mm; sourceTree = "<group>"; };
CEDA12402437C9EA00C28A9E /* editable-region-composited-and-non-composited-overlap.html */ = {isa = PBXFileReference; lastKnownFileType = text.html; name = "editable-region-composited-and-non-composited-overlap.html"; path = "ios/editable-region-composited-and-non-composited-overlap.html"; sourceTree = SOURCE_ROOT; };
D3BE5E341E4CE85E00FD563A /* WKWebViewGetContents.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = WKWebViewGetContents.mm; sourceTree = "<group>"; };
DC69AA621CF77C6500C6272F /* ScopedLambda.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ScopedLambda.cpp; sourceTree = "<group>"; };
@@ -3114,6 +3116,7 @@
CA97B3922193663B0045DF6F /* IndexedDBUserDelete.mm */,
0E404A8A2166DDF8008271BA /* InjectedBundleNodeHandleIsSelectElement.mm */,
79C5D430209D768300F1E7CA /* InjectedBundleNodeHandleIsTextField.mm */,
+ CED73D35246F204C00DAE327 /* InsertTextAlternatives.mm */,
2DB0232E1E4E871800707123 /* InteractionDeadlockAfterCrash.mm */,
2D116E1223E0CB3900208900 /* iOSMouseSupport.mm */,
5C69BDD41F82A7EB000F4F4B /* _javascript_DuringNavigation.mm */,
@@ -4985,6 +4988,7 @@
7CCE7EFC1A411AE600447C4C /* InjectedBundleFrameHitTest.cpp in Sources */,
7CCE7EFD1A411AE600447C4C /* InjectedBundleInitializationUserDataCallbackWins.cpp in Sources */,
7C83E0B81D0A64BD00FEBCF3 /* InjectedBundleMakeAllShadowRootsOpen.cpp in Sources */,
+ CED73D37246F29FA00DAE327 /* InsertTextAlternatives.mm in Sources */,
7CCE7EC31A411A7E00447C4C /* InspectorBar.mm in Sources */,
F44A531121B8990300DBB99C /* InstanceMethodSwizzler.mm in Sources */,
7CCE7EDA1A411A8700447C4C /* InstanceMethodSwizzler.mm in Sources */,
Added: trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/InsertTextAlternatives.mm (0 => 261897)
--- trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/InsertTextAlternatives.mm (rev 0)
+++ trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/InsertTextAlternatives.mm 2020-05-20 00:05:46 UTC (rev 261897)
@@ -0,0 +1,382 @@
+/*
+ * Copyright (C) 2020 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if PLATFORM(IOS_FAMILY)
+
+#import "PlatformUtilities.h"
+#import "TestInputDelegate.h"
+#import "TestWKWebView.h"
+#import "UIKitSPI.h"
+#import "WKWebViewConfigurationExtras.h"
+#import <WebKit/WKWebViewPrivate.h>
+#import <wtf/unicode/CharacterNames.h>
+
+namespace TestWebKitAPI {
+
+TEST(InsertTextAlternatives, Simple)
+{
+ auto *configuration = [WKWebViewConfiguration _test_configurationWithTestPlugInClassName:@"WebProcessPlugInWithInternals" configureJSCForTesting:YES];
+ auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 300, 300) configuration:configuration]);
+ auto inputDelegate = adoptNS([[TestInputDelegate alloc] init]);
+ [inputDelegate setFocusStartsInputSessionPolicyHandler:[] (WKWebView *, id <_WKFocusedElementInfo>) { return _WKFocusStartsInputSessionPolicyAllow; }];
+ [webView _setInputDelegate:inputDelegate.get()];
+
+ [webView synchronouslyLoadHTMLString:@"<body contenteditable='true'>A big </body>"];
+ [webView evaluateJavaScriptAndWaitForInputSessionToChange:@"document.body.focus()"];
+ [webView _synchronouslyExecuteEditCommand:@"MoveToEndOfDocument" argument:nil];
+ [[webView textInputContentView] insertText:@"hello" alternatives:@[@"yellow"] style:UITextAlternativeStyleNone];
+ [webView _synchronouslyExecuteEditCommand:@"MoveToBeginningOfDocument" argument:nil];
+ [webView waitForNextPresentationUpdate];
+
+ EXPECT_FALSE([[webView objectByEvaluatingJavaScript:@"internals.hasDictationAlternativesMarker(0, 1)"] boolValue]); // A
+ EXPECT_FALSE([[webView objectByEvaluatingJavaScript:@"internals.hasDictationAlternativesMarker(2, 3)"] boolValue]); // big
+ EXPECT_FALSE([[webView objectByEvaluatingJavaScript:@"internals.hasDictationAlternativesMarker(0, 6)"] boolValue]); // "A big "
+ EXPECT_TRUE([[webView objectByEvaluatingJavaScript:@"internals.hasDictationAlternativesMarker(6, 5)"] boolValue]); // hello
+}
+
+TEST(InsertTextAlternatives, InsertLeadingSpace)
+{
+ auto *configuration = [WKWebViewConfiguration _test_configurationWithTestPlugInClassName:@"WebProcessPlugInWithInternals" configureJSCForTesting:YES];
+ auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 300, 300) configuration:configuration]);
+ auto inputDelegate = adoptNS([[TestInputDelegate alloc] init]);
+ [inputDelegate setFocusStartsInputSessionPolicyHandler:[] (WKWebView *, id <_WKFocusedElementInfo>) { return _WKFocusStartsInputSessionPolicyAllow; }];
+ [webView _setInputDelegate:inputDelegate.get()];
+
+ [webView synchronouslyLoadHTMLString:@"<body contenteditable='true'></body>"];
+ [webView evaluateJavaScriptAndWaitForInputSessionToChange:@"document.body.focus()"];
+ [[webView textInputContentView] insertText:@"hello" alternatives:@[@"yellow"] style:UITextAlternativeStyleNone];
+ [webView waitForNextPresentationUpdate];
+
+ [webView _synchronouslyExecuteEditCommand:@"MoveToBeginningOfDocument" argument:nil];
+ [[webView textInputContentView] insertText:@" "];
+ [webView _synchronouslyExecuteEditCommand:@"MoveToBeginningOfDocument" argument:nil];
+ [webView waitForNextPresentationUpdate];
+
+ EXPECT_FALSE([[webView objectByEvaluatingJavaScript:@"internals.hasDictationAlternativesMarker(0, 1)"] boolValue]); // <space>
+ EXPECT_TRUE([[webView objectByEvaluatingJavaScript:@"internals.hasDictationAlternativesMarker(1, 5)"] boolValue]); // hello
+}
+
+TEST(InsertTextAlternatives, InsertLeadingNewline)
+{
+ auto *configuration = [WKWebViewConfiguration _test_configurationWithTestPlugInClassName:@"WebProcessPlugInWithInternals" configureJSCForTesting:YES];
+ auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 300, 300) configuration:configuration]);
+ auto inputDelegate = adoptNS([[TestInputDelegate alloc] init]);
+ [inputDelegate setFocusStartsInputSessionPolicyHandler:[] (WKWebView *, id <_WKFocusedElementInfo>) { return _WKFocusStartsInputSessionPolicyAllow; }];
+ [webView _setInputDelegate:inputDelegate.get()];
+
+ // Set CSS white-space and user-modify so that \n is inserted literally. Otherwise, it would be converted into a <br> if <body>
+ // only had "contenteditable='true'" because it is considered richly editable.
+ [webView synchronouslyLoadHTMLString:@"<body style='white-space: pre; -webkit-user-modify: read-write-plaintext-only'></body>"];
+ [webView evaluateJavaScriptAndWaitForInputSessionToChange:@"document.body.focus()"];
+ [[webView textInputContentView] insertText:@"hello" alternatives:@[@"yellow"] style:UITextAlternativeStyleNone];
+ [webView _synchronouslyExecuteEditCommand:@"MoveToBeginningOfDocument" argument:nil];
+ [[webView textInputContentView] insertText:@"\n"];
+ [webView _synchronouslyExecuteEditCommand:@"MoveToBeginningOfDocument" argument:nil];
+ [webView waitForNextPresentationUpdate];
+
+ EXPECT_FALSE([[webView objectByEvaluatingJavaScript:@"internals.hasDictationAlternativesMarker(0, 1)"] boolValue]); // \n
+ [webView _synchronouslyExecuteEditCommand:@"MoveDown" argument:nil];
+ [webView waitForNextPresentationUpdate];
+ EXPECT_TRUE([[webView objectByEvaluatingJavaScript:@"internals.hasDictationAlternativesMarker(0, 5)"] boolValue]); // hello
+}
+
+static inline NSString *makeNSStringWithCharacter(unichar c)
+{
+ return [NSString stringWithCharacters:&c length:1];
+};
+
+TEST(InsertTextAlternatives, InsertLeadingNoBreakSpace_ExpectedFailure)
+{
+ auto *configuration = [WKWebViewConfiguration _test_configurationWithTestPlugInClassName:@"WebProcessPlugInWithInternals" configureJSCForTesting:YES];
+ auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 300, 300) configuration:configuration]);
+ auto inputDelegate = adoptNS([[TestInputDelegate alloc] init]);
+ [inputDelegate setFocusStartsInputSessionPolicyHandler:[] (WKWebView *, id <_WKFocusedElementInfo>) { return _WKFocusStartsInputSessionPolicyAllow; }];
+ [webView _setInputDelegate:inputDelegate.get()];
+
+ [webView synchronouslyLoadHTMLString:@"<body contenteditable='true'></body>"];
+ [webView evaluateJavaScriptAndWaitForInputSessionToChange:@"document.body.focus()"];
+ [[webView textInputContentView] insertText:@"hello" alternatives:@[@"yellow"] style:UITextAlternativeStyleNone];
+ [webView waitForNextPresentationUpdate];
+
+ [webView _synchronouslyExecuteEditCommand:@"MoveToBeginningOfDocument" argument:nil];
+ [[webView textInputContentView] insertText:makeNSStringWithCharacter(noBreakSpace)];
+ [webView _synchronouslyExecuteEditCommand:@"MoveToBeginningOfDocument" argument:nil];
+ [webView waitForNextPresentationUpdate];
+
+ EXPECT_FALSE([[webView objectByEvaluatingJavaScript:@"internals.hasDictationAlternativesMarker(0, 1)"] boolValue]); // <no-break space>
+ // FIXME: Change this to EXPECT_TRUE() once leading no-break spaces do not cause removal of alternatives.
+ // See <https://webkit.org/b/212098>.
+ EXPECT_FALSE([[webView objectByEvaluatingJavaScript:@"internals.hasDictationAlternativesMarker(1, 5)"] boolValue]); // hello
+}
+
+TEST(InsertTextAlternatives, InsertTrailingSpace)
+{
+ auto *configuration = [WKWebViewConfiguration _test_configurationWithTestPlugInClassName:@"WebProcessPlugInWithInternals" configureJSCForTesting:YES];
+ auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 300, 300) configuration:configuration]);
+ auto inputDelegate = adoptNS([[TestInputDelegate alloc] init]);
+ [inputDelegate setFocusStartsInputSessionPolicyHandler:[] (WKWebView *, id <_WKFocusedElementInfo>) { return _WKFocusStartsInputSessionPolicyAllow; }];
+ [webView _setInputDelegate:inputDelegate.get()];
+
+ // Set CSS white-space so that ' ' is inserted literally. Otherwise, it would be converted into a no-break space.
+ [webView synchronouslyLoadHTMLString:@"<body contenteditable='true' style='white-space: pre'></body>"];
+ [webView evaluateJavaScriptAndWaitForInputSessionToChange:@"document.body.focus()"];
+ [[webView textInputContentView] insertText:@"hello" alternatives:@[@"yellow"] style:UITextAlternativeStyleNone];
+ [[webView textInputContentView] insertText:@" "];
+ [webView _synchronouslyExecuteEditCommand:@"MoveToBeginningOfDocument" argument:nil];
+ [webView waitForNextPresentationUpdate];
+
+ EXPECT_TRUE([[webView objectByEvaluatingJavaScript:@"internals.hasDictationAlternativesMarker(0, 5)"] boolValue]); // hello
+ EXPECT_FALSE([[webView objectByEvaluatingJavaScript:@"internals.hasDictationAlternativesMarker(5, 1)"] boolValue]); // <space>
+}
+
+TEST(InsertTextAlternatives, InsertTrailingSpaceWhitespaceRebalance_ExpectedFailure)
+{
+ auto *configuration = [WKWebViewConfiguration _test_configurationWithTestPlugInClassName:@"WebProcessPlugInWithInternals" configureJSCForTesting:YES];
+ auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 300, 300) configuration:configuration]);
+ auto inputDelegate = adoptNS([[TestInputDelegate alloc] init]);
+ [inputDelegate setFocusStartsInputSessionPolicyHandler:[] (WKWebView *, id <_WKFocusedElementInfo>) { return _WKFocusStartsInputSessionPolicyAllow; }];
+ [webView _setInputDelegate:inputDelegate.get()];
+
+ [webView synchronouslyLoadHTMLString:@"<body contenteditable='true'></body>"];
+ [webView evaluateJavaScriptAndWaitForInputSessionToChange:@"document.body.focus()"];
+ [[webView textInputContentView] insertText:@"hello" alternatives:@[@"yellow"] style:UITextAlternativeStyleNone];
+ [[webView textInputContentView] insertText:@" "];
+ [webView waitForNextPresentationUpdate];
+
+ // FIXME: Change this to EXPECT_TRUE() once <https://webkit.org/b/212093> is fixed.
+ EXPECT_FALSE([[webView objectByEvaluatingJavaScript:@"internals.hasDictationAlternativesMarker(0, 5)"] boolValue]); // hello
+}
+
+TEST(InsertTextAlternatives, InsertTrailingNewline)
+{
+ auto *configuration = [WKWebViewConfiguration _test_configurationWithTestPlugInClassName:@"WebProcessPlugInWithInternals" configureJSCForTesting:YES];
+ auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 300, 300) configuration:configuration]);
+ auto inputDelegate = adoptNS([[TestInputDelegate alloc] init]);
+ [inputDelegate setFocusStartsInputSessionPolicyHandler:[] (WKWebView *, id <_WKFocusedElementInfo>) { return _WKFocusStartsInputSessionPolicyAllow; }];
+ [webView _setInputDelegate:inputDelegate.get()];
+
+ // Set CSS white-space and user-modify so that \n is inserted literally. Otherwise, it would be converted into a <br> if <body>
+ // only had "contenteditable='true'" because it is considered richly editable.
+ [webView synchronouslyLoadHTMLString:@"<body style='white-space: pre; -webkit-user-modify: read-write-plaintext-only'></body>"];
+ [webView evaluateJavaScriptAndWaitForInputSessionToChange:@"document.body.focus()"];
+ [[webView textInputContentView] insertText:@"hello" alternatives:@[@"yellow"] style:UITextAlternativeStyleNone];
+ [[webView textInputContentView] insertText:@"\n"];
+ [webView _synchronouslyExecuteEditCommand:@"MoveToBeginningOfDocument" argument:nil];
+ [webView waitForNextPresentationUpdate];
+
+ EXPECT_TRUE([[webView objectByEvaluatingJavaScript:@"internals.hasDictationAlternativesMarker(0, 5)"] boolValue]); // hello
+ [webView _synchronouslyExecuteEditCommand:@"MoveDown" argument:nil];
+ [webView waitForNextPresentationUpdate];
+ EXPECT_FALSE([[webView objectByEvaluatingJavaScript:@"internals.hasDictationAlternativesMarker(0, 1)"] boolValue]); // \n
+}
+
+TEST(InsertTextAlternatives, InsertTrailingNoBreakSpace_ExpectedFailure)
+{
+ auto *configuration = [WKWebViewConfiguration _test_configurationWithTestPlugInClassName:@"WebProcessPlugInWithInternals" configureJSCForTesting:YES];
+ auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 300, 300) configuration:configuration]);
+ auto inputDelegate = adoptNS([[TestInputDelegate alloc] init]);
+ [inputDelegate setFocusStartsInputSessionPolicyHandler:[] (WKWebView *, id <_WKFocusedElementInfo>) { return _WKFocusStartsInputSessionPolicyAllow; }];
+ [webView _setInputDelegate:inputDelegate.get()];
+
+ [webView synchronouslyLoadHTMLString:@"<body contenteditable='true'></body>"];
+ [webView evaluateJavaScriptAndWaitForInputSessionToChange:@"document.body.focus()"];
+ [[webView textInputContentView] insertText:@"hello" alternatives:@[@"yellow"] style:UITextAlternativeStyleNone];
+ [[webView textInputContentView] insertText:makeNSStringWithCharacter(noBreakSpace)];
+ [webView _synchronouslyExecuteEditCommand:@"MoveToBeginningOfDocument" argument:nil];
+ [webView waitForNextPresentationUpdate];
+
+ // FIXME: Change first EXPECT to EXPECT_TRUE() once trailing no-break spaces do not cause removal of alternatives.
+ // See <https://webkit.org/b/212098>.
+ EXPECT_FALSE([[webView objectByEvaluatingJavaScript:@"internals.hasDictationAlternativesMarker(0, 5)"] boolValue]); // hello
+ EXPECT_FALSE([[webView objectByEvaluatingJavaScript:@"internals.hasDictationAlternativesMarker(5, 1)"] boolValue]); // <no-break space>
+}
+
+TEST(InsertTextAlternatives, InsertSpaceInMiddle)
+{
+ auto *configuration = [WKWebViewConfiguration _test_configurationWithTestPlugInClassName:@"WebProcessPlugInWithInternals" configureJSCForTesting:YES];
+ auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 300, 300) configuration:configuration]);
+ auto inputDelegate = adoptNS([[TestInputDelegate alloc] init]);
+ [inputDelegate setFocusStartsInputSessionPolicyHandler:[] (WKWebView *, id <_WKFocusedElementInfo>) { return _WKFocusStartsInputSessionPolicyAllow; }];
+ [webView _setInputDelegate:inputDelegate.get()];
+
+ [webView synchronouslyLoadHTMLString:@"<body contenteditable='true'></body>"];
+ [webView evaluateJavaScriptAndWaitForInputSessionToChange:@"document.body.focus()"];
+ [[webView textInputContentView] insertText:@"hello" alternatives:@[@"yellow"] style:UITextAlternativeStyleNone];
+ [webView waitForNextPresentationUpdate];
+
+ [webView stringByEvaluatingJavaScript:@"getSelection().setPosition(document.body.firstChild, 2)"];
+ [[webView textInputContentView] insertText:@" "];
+ [webView _synchronouslyExecuteEditCommand:@"MoveToBeginningOfDocument" argument:nil];
+ [webView waitForNextPresentationUpdate];
+
+ EXPECT_FALSE([[webView objectByEvaluatingJavaScript:@"internals.hasDictationAlternativesMarker(0, 2)"] boolValue]); // he
+ EXPECT_FALSE([[webView objectByEvaluatingJavaScript:@"internals.hasDictationAlternativesMarker(2, 1)"] boolValue]); // <space>
+ EXPECT_FALSE([[webView objectByEvaluatingJavaScript:@"internals.hasDictationAlternativesMarker(3, 3)"] boolValue]); // llo
+}
+
+TEST(InsertTextAlternatives, InsertNewlineInMiddle_ExpectedFailure)
+{
+ auto *configuration = [WKWebViewConfiguration _test_configurationWithTestPlugInClassName:@"WebProcessPlugInWithInternals" configureJSCForTesting:YES];
+ auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 300, 300) configuration:configuration]);
+ auto inputDelegate = adoptNS([[TestInputDelegate alloc] init]);
+ [inputDelegate setFocusStartsInputSessionPolicyHandler:[] (WKWebView *, id <_WKFocusedElementInfo>) { return _WKFocusStartsInputSessionPolicyAllow; }];
+ [webView _setInputDelegate:inputDelegate.get()];
+
+ // Set CSS white-space and user-modify so that \n is inserted literally. Otherwise, it would be converted into a <br> if <body>
+ // only had "contenteditable='true'" because it is considered richly editable.
+ [webView synchronouslyLoadHTMLString:@"<body style='white-space: pre; -webkit-user-modify: read-write-plaintext-only'></body>"];
+ [webView evaluateJavaScriptAndWaitForInputSessionToChange:@"document.body.focus()"];
+ [[webView textInputContentView] insertText:@"hello" alternatives:@[@"yellow"] style:UITextAlternativeStyleNone];
+ [webView waitForNextPresentationUpdate];
+
+ [webView stringByEvaluatingJavaScript:@"getSelection().setPosition(document.body.firstChild, 2)"];
+ [[webView textInputContentView] insertText:@"\n"];
+ [webView _synchronouslyExecuteEditCommand:@"MoveToBeginningOfDocument" argument:nil];
+ [webView waitForNextPresentationUpdate];
+
+ // FIXME: These results coincidentally match UIKit, but they just feel weird: they are asymmetric to
+ // what happens when a space is inserted in the middle. Marker should be removed when marker is split.
+ EXPECT_TRUE([[webView objectByEvaluatingJavaScript:@"internals.hasDictationAlternativesMarker(0, 2)"] boolValue]); // he
+ [webView _synchronouslyExecuteEditCommand:@"MoveDown" argument:nil];
+ EXPECT_TRUE([[webView objectByEvaluatingJavaScript:@"internals.hasDictationAlternativesMarker(0, 3)"] boolValue]); // llo
+}
+
+TEST(InsertTextAlternatives, InsertNoBreakSpaceInMiddle)
+{
+ auto *configuration = [WKWebViewConfiguration _test_configurationWithTestPlugInClassName:@"WebProcessPlugInWithInternals" configureJSCForTesting:YES];
+ auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 300, 300) configuration:configuration]);
+ auto inputDelegate = adoptNS([[TestInputDelegate alloc] init]);
+ [inputDelegate setFocusStartsInputSessionPolicyHandler:[] (WKWebView *, id <_WKFocusedElementInfo>) { return _WKFocusStartsInputSessionPolicyAllow; }];
+ [webView _setInputDelegate:inputDelegate.get()];
+
+ // Set CSS white-space and user-modify so that \n is inserted literally. Otherwise, it would be converted into a <br> if <body>
+ // only had "contenteditable='true'" because it is considered richly editable.
+ [webView synchronouslyLoadHTMLString:@"<body style='white-space: pre; -webkit-user-modify: read-write-plaintext-only'></body>"];
+ [webView evaluateJavaScriptAndWaitForInputSessionToChange:@"document.body.focus()"];
+ [[webView textInputContentView] insertText:@"hello" alternatives:@[@"yellow"] style:UITextAlternativeStyleNone];
+ [webView waitForNextPresentationUpdate];
+
+ [webView stringByEvaluatingJavaScript:@"getSelection().setPosition(document.body.firstChild, 2)"];
+ [[webView textInputContentView] insertText:makeNSStringWithCharacter(noBreakSpace)];
+ [webView _synchronouslyExecuteEditCommand:@"MoveToBeginningOfDocument" argument:nil];
+ [webView waitForNextPresentationUpdate];
+
+ EXPECT_FALSE([[webView objectByEvaluatingJavaScript:@"internals.hasDictationAlternativesMarker(0, 2)"] boolValue]); // he
+ EXPECT_FALSE([[webView objectByEvaluatingJavaScript:@"internals.hasDictationAlternativesMarker(2, 1)"] boolValue]); // <no-break space>
+ EXPECT_FALSE([[webView objectByEvaluatingJavaScript:@"internals.hasDictationAlternativesMarker(3, 3)"] boolValue]); // llo
+}
+
+TEST(InsertTextAlternatives, InsertLeadingNonWhitespaceCharacter)
+{
+ auto *configuration = [WKWebViewConfiguration _test_configurationWithTestPlugInClassName:@"WebProcessPlugInWithInternals" configureJSCForTesting:YES];
+ auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 300, 300) configuration:configuration]);
+ auto inputDelegate = adoptNS([[TestInputDelegate alloc] init]);
+ [inputDelegate setFocusStartsInputSessionPolicyHandler:[] (WKWebView *, id <_WKFocusedElementInfo>) { return _WKFocusStartsInputSessionPolicyAllow; }];
+ [webView _setInputDelegate:inputDelegate.get()];
+
+ [webView synchronouslyLoadHTMLString:@"<body contenteditable='true'></body>"];
+ [webView evaluateJavaScriptAndWaitForInputSessionToChange:@"document.body.focus()"];
+ [[webView textInputContentView] insertText:@"hello" alternatives:@[@"yellow"] style:UITextAlternativeStyleNone];
+ [webView waitForNextPresentationUpdate];
+
+ [webView _synchronouslyExecuteEditCommand:@"MoveToBeginningOfDocument" argument:nil];
+ [[webView textInputContentView] insertText:@"a"];
+ [webView _synchronouslyExecuteEditCommand:@"MoveToBeginningOfDocument" argument:nil];
+ [webView waitForNextPresentationUpdate];
+
+ EXPECT_FALSE([[webView objectByEvaluatingJavaScript:@"internals.hasDictationAlternativesMarker(0, 1)"] boolValue]); // a
+ EXPECT_FALSE([[webView objectByEvaluatingJavaScript:@"internals.hasDictationAlternativesMarker(1, 5)"] boolValue]); // hello
+}
+
+TEST(InsertTextAlternatives, InsertTrailingNonWhitespaceCharacter)
+{
+ auto *configuration = [WKWebViewConfiguration _test_configurationWithTestPlugInClassName:@"WebProcessPlugInWithInternals" configureJSCForTesting:YES];
+ auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 300, 300) configuration:configuration]);
+ auto inputDelegate = adoptNS([[TestInputDelegate alloc] init]);
+ [inputDelegate setFocusStartsInputSessionPolicyHandler:[] (WKWebView *, id <_WKFocusedElementInfo>) { return _WKFocusStartsInputSessionPolicyAllow; }];
+ [webView _setInputDelegate:inputDelegate.get()];
+
+ [webView synchronouslyLoadHTMLString:@"<body contenteditable='true'></body>"];
+ [webView evaluateJavaScriptAndWaitForInputSessionToChange:@"document.body.focus()"];
+ [[webView textInputContentView] insertText:@"hello" alternatives:@[@"yellow"] style:UITextAlternativeStyleNone];
+ [[webView textInputContentView] insertText:@"a"];
+ [webView _synchronouslyExecuteEditCommand:@"MoveToBeginningOfDocument" argument:nil];
+ [webView waitForNextPresentationUpdate];
+
+ EXPECT_FALSE([[webView objectByEvaluatingJavaScript:@"internals.hasDictationAlternativesMarker(0, 5)"] boolValue]); // hello
+ EXPECT_FALSE([[webView objectByEvaluatingJavaScript:@"internals.hasDictationAlternativesMarker(5, 1)"] boolValue]); // a
+}
+
+TEST(InsertTextAlternatives, InsertNonWhitespaceCharacterInMiddle)
+{
+ auto *configuration = [WKWebViewConfiguration _test_configurationWithTestPlugInClassName:@"WebProcessPlugInWithInternals" configureJSCForTesting:YES];
+ auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 300, 300) configuration:configuration]);
+ auto inputDelegate = adoptNS([[TestInputDelegate alloc] init]);
+ [inputDelegate setFocusStartsInputSessionPolicyHandler:[] (WKWebView *, id <_WKFocusedElementInfo>) { return _WKFocusStartsInputSessionPolicyAllow; }];
+ [webView _setInputDelegate:inputDelegate.get()];
+
+ [webView synchronouslyLoadHTMLString:@"<body contenteditable='true'></body>"];
+ [webView evaluateJavaScriptAndWaitForInputSessionToChange:@"document.body.focus()"];
+ [[webView textInputContentView] insertText:@"hello" alternatives:@[@"yellow"] style:UITextAlternativeStyleNone];
+ [webView waitForNextPresentationUpdate];
+
+ [webView stringByEvaluatingJavaScript:@"getSelection().setPosition(document.body.firstChild, 2)"];
+ [[webView textInputContentView] insertText:@"a"];
+ [webView _synchronouslyExecuteEditCommand:@"MoveToBeginningOfDocument" argument:nil];
+ [webView waitForNextPresentationUpdate];
+
+ EXPECT_FALSE([[webView objectByEvaluatingJavaScript:@"internals.hasDictationAlternativesMarker(0, 2)"] boolValue]); // he
+ EXPECT_FALSE([[webView objectByEvaluatingJavaScript:@"internals.hasDictationAlternativesMarker(2, 1)"] boolValue]); // a
+ EXPECT_FALSE([[webView objectByEvaluatingJavaScript:@"internals.hasDictationAlternativesMarker(3, 3)"] boolValue]); // llo
+}
+
+TEST(InsertTextAlternatives, InsertMultipleWordsWithAlternatives)
+{
+ auto *configuration = [WKWebViewConfiguration _test_configurationWithTestPlugInClassName:@"WebProcessPlugInWithInternals" configureJSCForTesting:YES];
+ auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 300, 300) configuration:configuration]);
+ auto inputDelegate = adoptNS([[TestInputDelegate alloc] init]);
+ [inputDelegate setFocusStartsInputSessionPolicyHandler:[] (WKWebView *, id <_WKFocusedElementInfo>) { return _WKFocusStartsInputSessionPolicyAllow; }];
+ [webView _setInputDelegate:inputDelegate.get()];
+
+ // Set CSS white-space so that ' ' is inserted literally. Otherwise, it would be converted into a no-break space.
+ [webView synchronouslyLoadHTMLString:@"<body contenteditable='true' style='white-space: pre'></body>"];
+ [webView evaluateJavaScriptAndWaitForInputSessionToChange:@"document.body.focus()"];
+ [[webView textInputContentView] insertText:@"hello" alternatives:@[@"yellow"] style:UITextAlternativeStyleNone];
+ [[webView textInputContentView] insertText:@" "];
+ [[webView textInputContentView] insertText:@"worlds" alternatives:@[@"worm"] style:UITextAlternativeStyleNone];
+ [webView _synchronouslyExecuteEditCommand:@"MoveToBeginningOfDocument" argument:nil];
+ [webView waitForNextPresentationUpdate];
+
+ EXPECT_TRUE([[webView objectByEvaluatingJavaScript:@"internals.hasDictationAlternativesMarker(0, 5)"] boolValue]); // hello
+ EXPECT_FALSE([[webView objectByEvaluatingJavaScript:@"internals.hasDictationAlternativesMarker(5, 1)"] boolValue]); // <space>
+ EXPECT_TRUE([[webView objectByEvaluatingJavaScript:@"internals.hasDictationAlternativesMarker(6, 5)"] boolValue]); // worlds
+}
+
+} // namespace TestWebKitAPI
+
+#endif // PLATFORM(IOS_FAMILY)
+