Holding down a key to choose an accented character should fire "insertReplacementText...
authorwenson_hsieh@apple.com <wenson_hsieh@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 31 Oct 2016 15:12:00 +0000 (15:12 +0000)
committerwenson_hsieh@apple.com <wenson_hsieh@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 31 Oct 2016 15:12:00 +0000 (15:12 +0000)
https://bugs.webkit.org/show_bug.cgi?id=164209
<rdar://problem/29019305>

Reviewed by Darin Adler.

Source/WebCore:

For TypingCommands that correspond to "insertReplacementText" inputTypes, vend dataTransfers for resulting
beforeinput and input events if the edited area is not an input field or textarea. To do this, convert the plain
text representation of the content to be inserted to HTML text using a helper function,
MarkupAccumulator::appendCharactersReplacingEntities, that is used when creating markup for Text nodes.

Tests: fast/events/before-input-prevent-insert-replacement.html
       fast/events/input-event-insert-replacement.html

* editing/TypingCommand.cpp:
(WebCore::TypingCommand::inputEventData):
(WebCore::TypingCommand::inputEventDataTransfer):
* editing/TypingCommand.h:

Source/WebKit2:

When replacing text, call Editor::insertText with the correct TextEventInputType so that WebCore will know to
use EditActionInsertReplacement when creating and applying the corresponding TypingCommand. Additional minor
changes in order to support testing replacement text insertion.

* UIProcess/API/Cocoa/WKWebView.mm:
(-[WKWebView _insertText:replacementRange:]):
* UIProcess/API/Cocoa/WKWebViewPrivate.h:
* WebProcess/WebPage/WebPage.cpp:
(WebKit::WebPage::insertTextAsync):

Tools:

Adds test support for inserting replacement text on Mac. This is equivalent to holding down a vowel key (e.g.
'a') to bring up the menu containing accented version of the character, then selecting an accented character to
insert in place of the typed character. This is exposed via UIScriptController.insertText, which takes a string
and an insertion range.

* DumpRenderTree/mac/UIScriptControllerMac.mm:
(WTR::UIScriptController::insertText):
* TestRunnerShared/UIScriptContext/Bindings/UIScriptController.idl:

Note that there is no callback argument to insertText, since UIScriptController::insertText is synchronous in
the UI process. The tests end when corresponding input events fired as a result of insertText have been received
in the web process. Please see the new layout tests for more detail.

* TestRunnerShared/UIScriptContext/UIScriptController.cpp:
(WTR::UIScriptController::insertText):
* TestRunnerShared/UIScriptContext/UIScriptController.h:
* WebKitTestRunner/mac/UIScriptControllerMac.mm:
(WTR::nsStringFromJSString):
(WTR::UIScriptController::insertText):

LayoutTests:

Adds 2 new layout tests to verify that inserting replacement text fires input events of inputType
"insertReplacementText" instead of the generic "insertText", and that calling preventDefault() on the
beforeinput event prevents text from being inserted. Also checks that inserting replacement text in
contenteditable areas causes the dataTransfer attribute to be populated, and that the data attribute is null.

* fast/events/before-input-prevent-insert-replacement-expected.txt: Added.
* fast/events/before-input-prevent-insert-replacement.html: Added.
* fast/events/input-event-insert-replacement-expected.txt: Added.
* fast/events/input-event-insert-replacement.html: Added.
* platform/ios-simulator/TestExpectations:
* platform/mac-wk1/TestExpectations:

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

20 files changed:
LayoutTests/ChangeLog
LayoutTests/fast/events/before-input-prevent-insert-replacement-expected.txt [new file with mode: 0644]
LayoutTests/fast/events/before-input-prevent-insert-replacement.html [new file with mode: 0644]
LayoutTests/fast/events/input-event-insert-replacement-expected.txt [new file with mode: 0644]
LayoutTests/fast/events/input-event-insert-replacement.html [new file with mode: 0644]
LayoutTests/platform/ios-simulator/TestExpectations
LayoutTests/platform/mac-wk1/TestExpectations
Source/WebCore/ChangeLog
Source/WebCore/editing/TypingCommand.cpp
Source/WebCore/editing/TypingCommand.h
Source/WebKit2/ChangeLog
Source/WebKit2/UIProcess/API/Cocoa/WKWebView.mm
Source/WebKit2/UIProcess/API/Cocoa/WKWebViewPrivate.h
Source/WebKit2/WebProcess/WebPage/WebPage.cpp
Tools/ChangeLog
Tools/DumpRenderTree/mac/UIScriptControllerMac.mm
Tools/TestRunnerShared/UIScriptContext/Bindings/UIScriptController.idl
Tools/TestRunnerShared/UIScriptContext/UIScriptController.cpp
Tools/TestRunnerShared/UIScriptContext/UIScriptController.h
Tools/WebKitTestRunner/mac/UIScriptControllerMac.mm

index 0ee72e6..37bd9e5 100644 (file)
@@ -1,3 +1,23 @@
+2016-10-31  Wenson Hsieh  <wenson_hsieh@apple.com>
+
+        Holding down a key to choose an accented character should fire "insertReplacementText" input events
+        https://bugs.webkit.org/show_bug.cgi?id=164209
+        <rdar://problem/29019305>
+
+        Reviewed by Darin Adler.
+
+        Adds 2 new layout tests to verify that inserting replacement text fires input events of inputType
+        "insertReplacementText" instead of the generic "insertText", and that calling preventDefault() on the
+        beforeinput event prevents text from being inserted. Also checks that inserting replacement text in
+        contenteditable areas causes the dataTransfer attribute to be populated, and that the data attribute is null.
+
+        * fast/events/before-input-prevent-insert-replacement-expected.txt: Added.
+        * fast/events/before-input-prevent-insert-replacement.html: Added.
+        * fast/events/input-event-insert-replacement-expected.txt: Added.
+        * fast/events/input-event-insert-replacement.html: Added.
+        * platform/ios-simulator/TestExpectations:
+        * platform/mac-wk1/TestExpectations:
+
 2016-10-30  Gyuyoung Kim  <gyuyoung.kim@webkit.org>
 
         [EFL] Skip media tests because timeout happens on many media tests.
diff --git a/LayoutTests/fast/events/before-input-prevent-insert-replacement-expected.txt b/LayoutTests/fast/events/before-input-prevent-insert-replacement-expected.txt
new file mode 100644 (file)
index 0000000..95a0219
--- /dev/null
@@ -0,0 +1,7 @@
+a
+Typing 'a'...
+(editable): type=beforeinput, inputType=insertText, data=`a`, dataTransfer=`null`
+(editable): type=input, inputType=insertText, data=`a`, dataTransfer=`null`
+Attempting to replace 'a' with 'b'...
+(editable): type=beforeinput, inputType=insertReplacementText, data=`null`, dataTransfer=`plain:"b", html:"b"`
+
diff --git a/LayoutTests/fast/events/before-input-prevent-insert-replacement.html b/LayoutTests/fast/events/before-input-prevent-insert-replacement.html
new file mode 100644 (file)
index 0000000..0b0e7b5
--- /dev/null
@@ -0,0 +1,60 @@
+<!DOCTYPE html>
+<html>
+<body>
+    <div id="editable" onbeforeinput=handleInputEvent(event) oninput=handleInputEvent(event) contenteditable></div>
+    <div id="output"></div>
+    <script type="text/javascript">
+        let write = s => output.innerHTML += s + "<br>";
+        var progress = 0;
+        editable.focus();
+
+        if (window.internals && window.testRunner) {
+            internals.settings.setInputEventsEnabled(true);
+            testRunner.dumpAsText();
+            testRunner.waitUntilDone();
+            if (window.eventSender && testRunner.runUIScript) {
+                write("Typing 'a'...");
+                eventSender.keyDown("a");
+                write("Attempting to replace 'a' with 'b'...");
+                testRunner.runUIScript(getUIScript(), (result) => incrementProgress());
+            }
+        } else {
+            write("To manually test, press and hold down 'a' and select one of the accented characters.");
+            write("You should observe that the replacement accented character does not replace 'a'.");
+        }
+
+        function incrementProgress()
+        {
+            progress++;
+            if (progress == 2)
+                testRunner.notifyDone();
+        }
+
+        function handleInputEvent(event)
+        {
+            write(`(${event.target.id}): type=${event.type}, inputType=${event.inputType}, data=\`${event.data}\`, dataTransfer=\`${dataTransferAsString(event.dataTransfer)}\``);
+            if (event.inputType === "insertReplacementText") {
+                event.preventDefault();
+                incrementProgress();
+            }
+        }
+
+        function dataTransferAsString(dataTransfer)
+        {
+            if (!dataTransfer)
+                return "null";
+
+            return `plain:"${dataTransfer.getData('text/plain')}", html:"${dataTransfer.getData('text/html')}"`;
+        }
+
+        function getUIScript()
+        {
+            return `
+            (function() {
+                uiController.insertText("b", 0, 1);
+                uiController.uiScriptComplete();
+            })();`
+        }
+    </script>
+</body>
+</html>
\ No newline at end of file
diff --git a/LayoutTests/fast/events/input-event-insert-replacement-expected.txt b/LayoutTests/fast/events/input-event-insert-replacement-expected.txt
new file mode 100644 (file)
index 0000000..cccdab3
--- /dev/null
@@ -0,0 +1,11 @@
+
+Typing 'a'...
+(editable): type=beforeinput, inputType=insertText, data=`a`, dataTransfer=`null`
+(editable): type=input, inputType=insertText, data=`a`, dataTransfer=`null`
+The value of the input is now: a
+
+Replacing 'a' with 'b'...
+(editable): type=beforeinput, inputType=insertReplacementText, data=`b`, dataTransfer=`null`
+(editable): type=input, inputType=insertReplacementText, data=`b`, dataTransfer=`null`
+The value of the input is now: b
+
diff --git a/LayoutTests/fast/events/input-event-insert-replacement.html b/LayoutTests/fast/events/input-event-insert-replacement.html
new file mode 100644 (file)
index 0000000..954bc55
--- /dev/null
@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+<html>
+<body>
+    <input id="editable" onbeforeinput=logInputEvent(event) oninput=logInputEvent(event)></input>
+    <div id="output"></div>
+    <script type="text/javascript">
+        let write = s => output.innerHTML += s + "<br>";
+        var progress = 0;
+        editable.focus();
+
+        if (window.internals && window.testRunner) {
+            internals.settings.setInputEventsEnabled(true);
+            testRunner.dumpAsText();
+            testRunner.waitUntilDone();
+            if (window.eventSender && testRunner.runUIScript) {
+                write("Typing 'a'...");
+                eventSender.keyDown("a");
+                write(`The value of the input is now: ${editable.value}`);
+                write("");
+                write("Replacing 'a' with 'b'...");
+                testRunner.runUIScript(getUIScript(), (result) => incrementProgress());
+            }
+        } else {
+            write("To manually test, press and hold down 'a' and select one of the accented characters.");
+            write("You should observe a pair of beforeinput/input events for both 'a' and the replacement accented character.");
+            write("Importantly, the inputType of the last two events should be 'insertReplacementText'.");
+        }
+
+        function incrementProgress()
+        {
+            progress++;
+            if (progress != 5)
+                return;
+
+            write(`The value of the input is now: ${editable.value}`);
+            testRunner.notifyDone();
+        }
+
+        function logInputEvent(event)
+        {
+            write(`(${event.target.id}): type=${event.type}, inputType=${event.inputType}, data=\`${event.data}\`, dataTransfer=\`${event.dataTransfer}\``);
+            incrementProgress();
+        }
+
+        function getUIScript()
+        {
+            return `
+            (function() {
+                uiController.insertText("b", 0, 1);
+                uiController.uiScriptComplete();
+            })();`
+        }
+    </script>
+</body>
+</html>
\ No newline at end of file
index 185fbdc..a4f55e0 100644 (file)
@@ -1202,6 +1202,7 @@ fast/events/frame-scroll-fake-mouse-move.html [ Failure ]
 fast/events/frame-tab-focus.html [ Failure ]
 fast/events/ime-composition-events-001.html [ Failure ]
 fast/events/inputText-never-fired-on-keydown-cancel.html [ Failure ]
+fast/events/input-event-insert-replacement.html [ Failure ]
 fast/events/input-events-drag-and-drop.html [ Failure ]
 fast/events/input-events-insert-by-drop.html [ Failure ]
 fast/events/input-events-fired-when-typing.html [ Failure ]
@@ -1212,6 +1213,7 @@ fast/events/input-events-ime-recomposition.html [ Failure ]
 fast/events/input-events-ime-composition.html [ Failure ]
 fast/events/input-events-paste-rich-datatransfer.html [ Failure ]
 fast/events/input-events-spell-checking-datatransfer.html [ Failure ]
+fast/events/before-input-prevent-insert-replacement.html [ Failure ]
 fast/events/before-input-events-prevent-default.html [ Failure ]
 fast/events/before-input-events-prevent-default-in-textfield.html [ Failure ]
 fast/events/before-input-events-different-start-end-elements.html [ Failure ]
index ce037b3..712b1bb 100644 (file)
@@ -84,6 +84,10 @@ fast/forms/file/open-file-panel.html [ Skip ]
 # WK1 and WK2 mousemove events are subtly different in ways that break this test on WK1.
 fast/events/ghostly-mousemoves-in-subframe.html [ Skip ]
 
+# Test support for inserting special characters is not yet implemented on WK1.
+fast/events/before-input-prevent-insert-replacement.html [ Skip ]
+fast/events/input-event-insert-replacement.html [ Skip ]
+
 # Media Stream API testing is not supported for WK1 yet.
 fast/mediastream
 http/tests/media/media-stream
index f562737..0234e62 100644 (file)
@@ -1,3 +1,24 @@
+2016-10-31  Wenson Hsieh  <wenson_hsieh@apple.com>
+
+        Holding down a key to choose an accented character should fire "insertReplacementText" input events
+        https://bugs.webkit.org/show_bug.cgi?id=164209
+        <rdar://problem/29019305>
+
+        Reviewed by Darin Adler.
+
+        For TypingCommands that correspond to "insertReplacementText" inputTypes, vend dataTransfers for resulting
+        beforeinput and input events if the edited area is not an input field or textarea. To do this, convert the plain
+        text representation of the content to be inserted to HTML text using a helper function,
+        MarkupAccumulator::appendCharactersReplacingEntities, that is used when creating markup for Text nodes.
+
+        Tests: fast/events/before-input-prevent-insert-replacement.html
+               fast/events/input-event-insert-replacement.html
+
+        * editing/TypingCommand.cpp:
+        (WebCore::TypingCommand::inputEventData):
+        (WebCore::TypingCommand::inputEventDataTransfer):
+        * editing/TypingCommand.h:
+
 2016-10-30  Dave Hyatt  <hyatt@apple.com>
 
         [CSS Parser] Miscellaneous bug fixes
index 9e802a4..d933db5 100644 (file)
@@ -28,6 +28,7 @@
 
 #include "AXObjectCache.h"
 #include "BreakBlockquoteCommand.h"
+#include "DataTransfer.h"
 #include "DeleteSelectionCommand.h"
 #include "Document.h"
 #include "Editor.h"
@@ -39,6 +40,7 @@
 #include "InsertParagraphSeparatorCommand.h"
 #include "InsertTextCommand.h"
 #include "Logging.h"
+#include "MarkupAccumulator.h"
 #include "MathMLElement.h"
 #include "RenderElement.h"
 #include "StaticRange.h"
@@ -406,15 +408,26 @@ String TypingCommand::inputEventData() const
 {
     switch (m_currentTypingEditAction) {
     case EditActionTypingInsertText:
-    case EditActionInsertReplacement:
     case EditActionTypingInsertPendingComposition:
     case EditActionTypingInsertFinalComposition:
         return m_currentTextToInsert;
+    case EditActionInsertReplacement:
+        return isEditingTextAreaOrTextInput() ? m_currentTextToInsert : String();
     default:
         return CompositeEditCommand::inputEventData();
     }
 }
 
+RefPtr<DataTransfer> TypingCommand::inputEventDataTransfer() const
+{
+    if (m_currentTypingEditAction != EditActionInsertReplacement || isEditingTextAreaOrTextInput())
+        return nullptr;
+
+    StringBuilder htmlText;
+    MarkupAccumulator::appendCharactersReplacingEntities(htmlText, m_currentTextToInsert, 0, m_currentTextToInsert.length(), EntityMaskInHTMLPCDATA);
+    return DataTransfer::createForInputEvent(m_currentTextToInsert, htmlText.toString());
+}
+
 void TypingCommand::didApplyCommand()
 {
     // TypingCommands handle applied editing separately (see TypingCommand::typingAddedToOpenCommand).
index b9401b5..4139bd1 100644 (file)
@@ -120,6 +120,7 @@ private:
 
     String inputEventTypeName() const final;
     String inputEventData() const final;
+    RefPtr<DataTransfer> inputEventDataTransfer() const final;
     bool isBeforeInputEventCancelable() const final;
 
     static void updateSelectionIfDifferentFromCurrentSelection(TypingCommand*, Frame*);
index 1676ea9..7196d77 100644 (file)
@@ -1,3 +1,21 @@
+2016-10-31  Wenson Hsieh  <wenson_hsieh@apple.com>
+
+        Holding down a key to choose an accented character should fire "insertReplacementText" input events
+        https://bugs.webkit.org/show_bug.cgi?id=164209
+        <rdar://problem/29019305>
+
+        Reviewed by Darin Adler.
+
+        When replacing text, call Editor::insertText with the correct TextEventInputType so that WebCore will know to
+        use EditActionInsertReplacement when creating and applying the corresponding TypingCommand. Additional minor
+        changes in order to support testing replacement text insertion.
+
+        * UIProcess/API/Cocoa/WKWebView.mm:
+        (-[WKWebView _insertText:replacementRange:]):
+        * UIProcess/API/Cocoa/WKWebViewPrivate.h:
+        * WebProcess/WebPage/WebPage.cpp:
+        (WebKit::WebPage::insertTextAsync):
+
 2016-10-30  Darin Adler  <darin@apple.com>
 
         Move Element, NamedNodeMap, and DOMStringMap from ExceptionCode to Exception
index 448dfcc..fe44317 100644 (file)
@@ -4650,6 +4650,11 @@ static WebCore::UserInterfaceLayoutDirection toUserInterfaceLayoutDirection(UISe
 {
     // Overridden by subclasses.
 }
+
+- (void)_insertText:(id)string replacementRange:(NSRange)replacementRange
+{
+    [self insertText:string replacementRange:replacementRange];
+}
 #endif // PLATFORM(MAC)
 
 - (void)_setPageScale:(CGFloat)scale withOrigin:(CGPoint)origin
index 7e2e627..eab29b4 100644 (file)
@@ -290,6 +290,8 @@ typedef NS_ENUM(NSInteger, _WKImmediateActionType) {
 @property (nonatomic, readonly) BOOL _shouldRequestCandidates WK_API_AVAILABLE(macosx(WK_MAC_TBA));
 - (void)_requestActiveNowPlayingSessionInfo WK_API_AVAILABLE(macosx(WK_MAC_TBA));
 - (void)_handleActiveNowPlayingSessionInfoResponse:(BOOL)hasActiveSession title:(NSString *)title duration:(double)duration elapsedTime:(double)elapsedTime WK_API_AVAILABLE(macosx(WK_MAC_TBA));
+
+- (void)_insertText:(id)string replacementRange:(NSRange)replacementRange WK_API_AVAILABLE(macosx(WK_MAC_TBA));
 #endif
 
 - (void)_setPageScale:(CGFloat)scale withOrigin:(CGPoint)origin WK_API_AVAILABLE(ios(WK_IOS_TBA));
index 849b3a2..d090015 100644 (file)
@@ -4625,11 +4625,13 @@ void WebPage::insertTextAsync(const String& text, const EditingRange& replacemen
 {
     Frame& frame = m_page->focusController().focusedOrMainFrame();
 
+    bool replacesText = false;
     if (replacementEditingRange.location != notFound) {
         RefPtr<Range> replacementRange = rangeFromEditingRange(frame, replacementEditingRange, static_cast<EditingRangeIsRelativeTo>(editingRangeIsRelativeTo));
         if (replacementRange) {
             TemporaryChange<bool> isSelectingTextWhileInsertingAsynchronously(m_isSelectingTextWhileInsertingAsynchronously, suppressSelectionUpdate);
             frame.selection().setSelection(VisibleSelection(*replacementRange, SEL_DEFAULT_AFFINITY));
+            replacesText = true;
         }
     }
     
@@ -4639,7 +4641,7 @@ void WebPage::insertTextAsync(const String& text, const EditingRange& replacemen
     if (!frame.editor().hasComposition()) {
         // An insertText: might be handled by other responders in the chain if we don't handle it.
         // One example is space bar that results in scrolling down the page.
-        frame.editor().insertText(text, nullptr);
+        frame.editor().insertText(text, nullptr, replacesText ? TextEventInputAutocompletion : TextEventInputKeyboard);
     } else
         frame.editor().confirmComposition(text);
 }
index e5f0be0..5120077 100644 (file)
@@ -1,3 +1,31 @@
+2016-10-31  Wenson Hsieh  <wenson_hsieh@apple.com>
+
+        Holding down a key to choose an accented character should fire "insertReplacementText" input events
+        https://bugs.webkit.org/show_bug.cgi?id=164209
+        <rdar://problem/29019305>
+
+        Reviewed by Darin Adler.
+
+        Adds test support for inserting replacement text on Mac. This is equivalent to holding down a vowel key (e.g.
+        'a') to bring up the menu containing accented version of the character, then selecting an accented character to
+        insert in place of the typed character. This is exposed via UIScriptController.insertText, which takes a string
+        and an insertion range.
+
+        * DumpRenderTree/mac/UIScriptControllerMac.mm:
+        (WTR::UIScriptController::insertText):
+        * TestRunnerShared/UIScriptContext/Bindings/UIScriptController.idl:
+
+        Note that there is no callback argument to insertText, since UIScriptController::insertText is synchronous in
+        the UI process. The tests end when corresponding input events fired as a result of insertText have been received
+        in the web process. Please see the new layout tests for more detail.
+
+        * TestRunnerShared/UIScriptContext/UIScriptController.cpp:
+        (WTR::UIScriptController::insertText):
+        * TestRunnerShared/UIScriptContext/UIScriptController.h:
+        * WebKitTestRunner/mac/UIScriptControllerMac.mm:
+        (WTR::nsStringFromJSString):
+        (WTR::UIScriptController::insertText):
+
 2016-10-30  Sam Weinig  <sam@webkit.org>
 
         [WebIDL] Restructure IDLParser structs to better match modern WebIDL concepts
index 56e2077..190ec14 100644 (file)
@@ -43,6 +43,10 @@ void UIScriptController::doAsyncTask(JSValueRef callback)
     });
 }
 
+void UIScriptController::insertText(JSStringRef, int, int)
+{
+}
+
 }
 
 #endif // PLATFORM(MAC)
index 2ec9498..30f617f 100644 (file)
@@ -156,5 +156,7 @@ interface UIScriptController {
 
     readonly attribute object selectionRangeViewRects; // An array of objects with 'left', 'top', 'width', and 'height' properties.
 
+    void insertText(DOMString text, long location, long length);
+
     void uiScriptComplete(DOMString result);
 };
index a448294..1374e58 100644 (file)
@@ -317,6 +317,14 @@ void UIScriptController::platformClearAllCallbacks()
 }
 #endif
 
+#if !PLATFORM(MAC)
+
+void UIScriptController::insertText(JSStringRef, int, int)
+{
+}
+
+#endif
+
 void UIScriptController::uiScriptComplete(JSStringRef result)
 {
     m_context->requestUIScriptCompletion(result);
index d6d7946..1c16133 100644 (file)
@@ -117,6 +117,8 @@ public:
     
     JSObjectRef selectionRangeViewRects() const;
 
+    void insertText(JSStringRef, int location, int length);
+
     void uiScriptComplete(JSStringRef result);
 
 private:
index 4f6cceb..71b7ba5 100644 (file)
 #import "config.h"
 #import "UIScriptController.h"
 
+#import "PlatformWebView.h"
+#import "TestController.h"
+#import "TestRunnerWKWebView.h"
 #import "UIScriptContext.h"
+#import <JavaScriptCore/JSStringRefCF.h>
+#import <WebKit/WKWebViewPrivate.h>
 
 namespace WTR {
 
+NSString *nsString(JSStringRef string)
+{
+    return (NSString *)adoptCF(JSStringCopyCFString(kCFAllocatorDefault, string)).autorelease();
+}
+
 void UIScriptController::doAsyncTask(JSValueRef callback)
 {
     unsigned callbackID = m_context->prepareForAsyncTask(callback, CallbackTypeNonPersistent);
@@ -41,4 +51,19 @@ void UIScriptController::doAsyncTask(JSValueRef callback)
     });
 }
 
+void UIScriptController::insertText(JSStringRef text, int location, int length)
+{
+#if WK_API_ENABLED
+    if (location == -1)
+        location = NSNotFound;
+
+    auto* webView = TestController::singleton().mainWebView()->platformView();
+    [webView _insertText:nsString(text) replacementRange:NSMakeRange(location, length)];
+#else
+    UNUSED_PARAM(text);
+    UNUSED_PARAM(location);
+    UNUSED_PARAM(length);
+#endif
+}
+
 }