Skip to content

Commit adff4e0

Browse files
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. 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: http://svn.webkit.org/repository/webkit/trunk@208143 268f45cc-cd09-0410-ab3c-d52691b4dbfc
1 parent 36c108f commit adff4e0

20 files changed

+292
-2
lines changed

LayoutTests/ChangeLog

+20
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,23 @@
1+
2016-10-31 Wenson Hsieh <[email protected]>
2+
3+
Holding down a key to choose an accented character should fire "insertReplacementText" input events
4+
https://bugs.webkit.org/show_bug.cgi?id=164209
5+
<rdar://problem/29019305>
6+
7+
Reviewed by Darin Adler.
8+
9+
Adds 2 new layout tests to verify that inserting replacement text fires input events of inputType
10+
"insertReplacementText" instead of the generic "insertText", and that calling preventDefault() on the
11+
beforeinput event prevents text from being inserted. Also checks that inserting replacement text in
12+
contenteditable areas causes the dataTransfer attribute to be populated, and that the data attribute is null.
13+
14+
* fast/events/before-input-prevent-insert-replacement-expected.txt: Added.
15+
* fast/events/before-input-prevent-insert-replacement.html: Added.
16+
* fast/events/input-event-insert-replacement-expected.txt: Added.
17+
* fast/events/input-event-insert-replacement.html: Added.
18+
* platform/ios-simulator/TestExpectations:
19+
* platform/mac-wk1/TestExpectations:
20+
121
2016-10-30 Gyuyoung Kim <[email protected]>
222

323
[EFL] Skip media tests because timeout happens on many media tests.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
a
2+
Typing 'a'...
3+
(editable): type=beforeinput, inputType=insertText, data=`a`, dataTransfer=`null`
4+
(editable): type=input, inputType=insertText, data=`a`, dataTransfer=`null`
5+
Attempting to replace 'a' with 'b'...
6+
(editable): type=beforeinput, inputType=insertReplacementText, data=`null`, dataTransfer=`plain:"b", html:"b"`
7+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<body>
4+
<div id="editable" onbeforeinput=handleInputEvent(event) oninput=handleInputEvent(event) contenteditable></div>
5+
<div id="output"></div>
6+
<script type="text/javascript">
7+
let write = s => output.innerHTML += s + "<br>";
8+
var progress = 0;
9+
editable.focus();
10+
11+
if (window.internals && window.testRunner) {
12+
internals.settings.setInputEventsEnabled(true);
13+
testRunner.dumpAsText();
14+
testRunner.waitUntilDone();
15+
if (window.eventSender && testRunner.runUIScript) {
16+
write("Typing 'a'...");
17+
eventSender.keyDown("a");
18+
write("Attempting to replace 'a' with 'b'...");
19+
testRunner.runUIScript(getUIScript(), (result) => incrementProgress());
20+
}
21+
} else {
22+
write("To manually test, press and hold down 'a' and select one of the accented characters.");
23+
write("You should observe that the replacement accented character does not replace 'a'.");
24+
}
25+
26+
function incrementProgress()
27+
{
28+
progress++;
29+
if (progress == 2)
30+
testRunner.notifyDone();
31+
}
32+
33+
function handleInputEvent(event)
34+
{
35+
write(`(${event.target.id}): type=${event.type}, inputType=${event.inputType}, data=\`${event.data}\`, dataTransfer=\`${dataTransferAsString(event.dataTransfer)}\``);
36+
if (event.inputType === "insertReplacementText") {
37+
event.preventDefault();
38+
incrementProgress();
39+
}
40+
}
41+
42+
function dataTransferAsString(dataTransfer)
43+
{
44+
if (!dataTransfer)
45+
return "null";
46+
47+
return `plain:"${dataTransfer.getData('text/plain')}", html:"${dataTransfer.getData('text/html')}"`;
48+
}
49+
50+
function getUIScript()
51+
{
52+
return `
53+
(function() {
54+
uiController.insertText("b", 0, 1);
55+
uiController.uiScriptComplete();
56+
})();`
57+
}
58+
</script>
59+
</body>
60+
</html>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
2+
Typing 'a'...
3+
(editable): type=beforeinput, inputType=insertText, data=`a`, dataTransfer=`null`
4+
(editable): type=input, inputType=insertText, data=`a`, dataTransfer=`null`
5+
The value of the input is now: a
6+
7+
Replacing 'a' with 'b'...
8+
(editable): type=beforeinput, inputType=insertReplacementText, data=`b`, dataTransfer=`null`
9+
(editable): type=input, inputType=insertReplacementText, data=`b`, dataTransfer=`null`
10+
The value of the input is now: b
11+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<body>
4+
<input id="editable" onbeforeinput=logInputEvent(event) oninput=logInputEvent(event)></input>
5+
<div id="output"></div>
6+
<script type="text/javascript">
7+
let write = s => output.innerHTML += s + "<br>";
8+
var progress = 0;
9+
editable.focus();
10+
11+
if (window.internals && window.testRunner) {
12+
internals.settings.setInputEventsEnabled(true);
13+
testRunner.dumpAsText();
14+
testRunner.waitUntilDone();
15+
if (window.eventSender && testRunner.runUIScript) {
16+
write("Typing 'a'...");
17+
eventSender.keyDown("a");
18+
write(`The value of the input is now: ${editable.value}`);
19+
write("");
20+
write("Replacing 'a' with 'b'...");
21+
testRunner.runUIScript(getUIScript(), (result) => incrementProgress());
22+
}
23+
} else {
24+
write("To manually test, press and hold down 'a' and select one of the accented characters.");
25+
write("You should observe a pair of beforeinput/input events for both 'a' and the replacement accented character.");
26+
write("Importantly, the inputType of the last two events should be 'insertReplacementText'.");
27+
}
28+
29+
function incrementProgress()
30+
{
31+
progress++;
32+
if (progress != 5)
33+
return;
34+
35+
write(`The value of the input is now: ${editable.value}`);
36+
testRunner.notifyDone();
37+
}
38+
39+
function logInputEvent(event)
40+
{
41+
write(`(${event.target.id}): type=${event.type}, inputType=${event.inputType}, data=\`${event.data}\`, dataTransfer=\`${event.dataTransfer}\``);
42+
incrementProgress();
43+
}
44+
45+
function getUIScript()
46+
{
47+
return `
48+
(function() {
49+
uiController.insertText("b", 0, 1);
50+
uiController.uiScriptComplete();
51+
})();`
52+
}
53+
</script>
54+
</body>
55+
</html>

LayoutTests/platform/ios-simulator/TestExpectations

+2
Original file line numberDiff line numberDiff line change
@@ -1202,6 +1202,7 @@ fast/events/frame-scroll-fake-mouse-move.html [ Failure ]
12021202
fast/events/frame-tab-focus.html [ Failure ]
12031203
fast/events/ime-composition-events-001.html [ Failure ]
12041204
fast/events/inputText-never-fired-on-keydown-cancel.html [ Failure ]
1205+
fast/events/input-event-insert-replacement.html [ Failure ]
12051206
fast/events/input-events-drag-and-drop.html [ Failure ]
12061207
fast/events/input-events-insert-by-drop.html [ Failure ]
12071208
fast/events/input-events-fired-when-typing.html [ Failure ]
@@ -1212,6 +1213,7 @@ fast/events/input-events-ime-recomposition.html [ Failure ]
12121213
fast/events/input-events-ime-composition.html [ Failure ]
12131214
fast/events/input-events-paste-rich-datatransfer.html [ Failure ]
12141215
fast/events/input-events-spell-checking-datatransfer.html [ Failure ]
1216+
fast/events/before-input-prevent-insert-replacement.html [ Failure ]
12151217
fast/events/before-input-events-prevent-default.html [ Failure ]
12161218
fast/events/before-input-events-prevent-default-in-textfield.html [ Failure ]
12171219
fast/events/before-input-events-different-start-end-elements.html [ Failure ]

LayoutTests/platform/mac-wk1/TestExpectations

+4
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,10 @@ fast/forms/file/open-file-panel.html [ Skip ]
8484
# WK1 and WK2 mousemove events are subtly different in ways that break this test on WK1.
8585
fast/events/ghostly-mousemoves-in-subframe.html [ Skip ]
8686

87+
# Test support for inserting special characters is not yet implemented on WK1.
88+
fast/events/before-input-prevent-insert-replacement.html [ Skip ]
89+
fast/events/input-event-insert-replacement.html [ Skip ]
90+
8791
# Media Stream API testing is not supported for WK1 yet.
8892
fast/mediastream
8993
http/tests/media/media-stream

Source/WebCore/ChangeLog

+21
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,24 @@
1+
2016-10-31 Wenson Hsieh <[email protected]>
2+
3+
Holding down a key to choose an accented character should fire "insertReplacementText" input events
4+
https://bugs.webkit.org/show_bug.cgi?id=164209
5+
<rdar://problem/29019305>
6+
7+
Reviewed by Darin Adler.
8+
9+
For TypingCommands that correspond to "insertReplacementText" inputTypes, vend dataTransfers for resulting
10+
beforeinput and input events if the edited area is not an input field or textarea. To do this, convert the plain
11+
text representation of the content to be inserted to HTML text using a helper function,
12+
MarkupAccumulator::appendCharactersReplacingEntities, that is used when creating markup for Text nodes.
13+
14+
Tests: fast/events/before-input-prevent-insert-replacement.html
15+
fast/events/input-event-insert-replacement.html
16+
17+
* editing/TypingCommand.cpp:
18+
(WebCore::TypingCommand::inputEventData):
19+
(WebCore::TypingCommand::inputEventDataTransfer):
20+
* editing/TypingCommand.h:
21+
122
2016-10-30 Dave Hyatt <[email protected]>
223

324
[CSS Parser] Miscellaneous bug fixes

Source/WebCore/editing/TypingCommand.cpp

+14-1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828

2929
#include "AXObjectCache.h"
3030
#include "BreakBlockquoteCommand.h"
31+
#include "DataTransfer.h"
3132
#include "DeleteSelectionCommand.h"
3233
#include "Document.h"
3334
#include "Editor.h"
@@ -39,6 +40,7 @@
3940
#include "InsertParagraphSeparatorCommand.h"
4041
#include "InsertTextCommand.h"
4142
#include "Logging.h"
43+
#include "MarkupAccumulator.h"
4244
#include "MathMLElement.h"
4345
#include "RenderElement.h"
4446
#include "StaticRange.h"
@@ -406,15 +408,26 @@ String TypingCommand::inputEventData() const
406408
{
407409
switch (m_currentTypingEditAction) {
408410
case EditActionTypingInsertText:
409-
case EditActionInsertReplacement:
410411
case EditActionTypingInsertPendingComposition:
411412
case EditActionTypingInsertFinalComposition:
412413
return m_currentTextToInsert;
414+
case EditActionInsertReplacement:
415+
return isEditingTextAreaOrTextInput() ? m_currentTextToInsert : String();
413416
default:
414417
return CompositeEditCommand::inputEventData();
415418
}
416419
}
417420

421+
RefPtr<DataTransfer> TypingCommand::inputEventDataTransfer() const
422+
{
423+
if (m_currentTypingEditAction != EditActionInsertReplacement || isEditingTextAreaOrTextInput())
424+
return nullptr;
425+
426+
StringBuilder htmlText;
427+
MarkupAccumulator::appendCharactersReplacingEntities(htmlText, m_currentTextToInsert, 0, m_currentTextToInsert.length(), EntityMaskInHTMLPCDATA);
428+
return DataTransfer::createForInputEvent(m_currentTextToInsert, htmlText.toString());
429+
}
430+
418431
void TypingCommand::didApplyCommand()
419432
{
420433
// TypingCommands handle applied editing separately (see TypingCommand::typingAddedToOpenCommand).

Source/WebCore/editing/TypingCommand.h

+1
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ class TypingCommand final : public TextInsertionBaseCommand {
120120

121121
String inputEventTypeName() const final;
122122
String inputEventData() const final;
123+
RefPtr<DataTransfer> inputEventDataTransfer() const final;
123124
bool isBeforeInputEventCancelable() const final;
124125

125126
static void updateSelectionIfDifferentFromCurrentSelection(TypingCommand*, Frame*);

Source/WebKit2/ChangeLog

+18
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,21 @@
1+
2016-10-31 Wenson Hsieh <[email protected]>
2+
3+
Holding down a key to choose an accented character should fire "insertReplacementText" input events
4+
https://bugs.webkit.org/show_bug.cgi?id=164209
5+
<rdar://problem/29019305>
6+
7+
Reviewed by Darin Adler.
8+
9+
When replacing text, call Editor::insertText with the correct TextEventInputType so that WebCore will know to
10+
use EditActionInsertReplacement when creating and applying the corresponding TypingCommand. Additional minor
11+
changes in order to support testing replacement text insertion.
12+
13+
* UIProcess/API/Cocoa/WKWebView.mm:
14+
(-[WKWebView _insertText:replacementRange:]):
15+
* UIProcess/API/Cocoa/WKWebViewPrivate.h:
16+
* WebProcess/WebPage/WebPage.cpp:
17+
(WebKit::WebPage::insertTextAsync):
18+
119
2016-10-30 Darin Adler <[email protected]>
220

321
Move Element, NamedNodeMap, and DOMStringMap from ExceptionCode to Exception

Source/WebKit2/UIProcess/API/Cocoa/WKWebView.mm

+5
Original file line numberDiff line numberDiff line change
@@ -4650,6 +4650,11 @@ - (void)_handleActiveNowPlayingSessionInfoResponse:(BOOL)hasActiveSession title:
46504650
{
46514651
// Overridden by subclasses.
46524652
}
4653+
4654+
- (void)_insertText:(id)string replacementRange:(NSRange)replacementRange
4655+
{
4656+
[self insertText:string replacementRange:replacementRange];
4657+
}
46534658
#endif // PLATFORM(MAC)
46544659

46554660
- (void)_setPageScale:(CGFloat)scale withOrigin:(CGPoint)origin

Source/WebKit2/UIProcess/API/Cocoa/WKWebViewPrivate.h

+2
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,8 @@ typedef NS_ENUM(NSInteger, _WKImmediateActionType) {
290290
@property (nonatomic, readonly) BOOL _shouldRequestCandidates WK_API_AVAILABLE(macosx(WK_MAC_TBA));
291291
- (void)_requestActiveNowPlayingSessionInfo WK_API_AVAILABLE(macosx(WK_MAC_TBA));
292292
- (void)_handleActiveNowPlayingSessionInfoResponse:(BOOL)hasActiveSession title:(NSString *)title duration:(double)duration elapsedTime:(double)elapsedTime WK_API_AVAILABLE(macosx(WK_MAC_TBA));
293+
294+
- (void)_insertText:(id)string replacementRange:(NSRange)replacementRange WK_API_AVAILABLE(macosx(WK_MAC_TBA));
293295
#endif
294296

295297
- (void)_setPageScale:(CGFloat)scale withOrigin:(CGPoint)origin WK_API_AVAILABLE(ios(WK_IOS_TBA));

Source/WebKit2/WebProcess/WebPage/WebPage.cpp

+3-1
Original file line numberDiff line numberDiff line change
@@ -4625,11 +4625,13 @@ void WebPage::insertTextAsync(const String& text, const EditingRange& replacemen
46254625
{
46264626
Frame& frame = m_page->focusController().focusedOrMainFrame();
46274627

4628+
bool replacesText = false;
46284629
if (replacementEditingRange.location != notFound) {
46294630
RefPtr<Range> replacementRange = rangeFromEditingRange(frame, replacementEditingRange, static_cast<EditingRangeIsRelativeTo>(editingRangeIsRelativeTo));
46304631
if (replacementRange) {
46314632
TemporaryChange<bool> isSelectingTextWhileInsertingAsynchronously(m_isSelectingTextWhileInsertingAsynchronously, suppressSelectionUpdate);
46324633
frame.selection().setSelection(VisibleSelection(*replacementRange, SEL_DEFAULT_AFFINITY));
4634+
replacesText = true;
46334635
}
46344636
}
46354637

@@ -4639,7 +4641,7 @@ void WebPage::insertTextAsync(const String& text, const EditingRange& replacemen
46394641
if (!frame.editor().hasComposition()) {
46404642
// An insertText: might be handled by other responders in the chain if we don't handle it.
46414643
// One example is space bar that results in scrolling down the page.
4642-
frame.editor().insertText(text, nullptr);
4644+
frame.editor().insertText(text, nullptr, replacesText ? TextEventInputAutocompletion : TextEventInputKeyboard);
46434645
} else
46444646
frame.editor().confirmComposition(text);
46454647
}

Tools/ChangeLog

+28
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,31 @@
1+
2016-10-31 Wenson Hsieh <[email protected]>
2+
3+
Holding down a key to choose an accented character should fire "insertReplacementText" input events
4+
https://bugs.webkit.org/show_bug.cgi?id=164209
5+
<rdar://problem/29019305>
6+
7+
Reviewed by Darin Adler.
8+
9+
Adds test support for inserting replacement text on Mac. This is equivalent to holding down a vowel key (e.g.
10+
'a') to bring up the menu containing accented version of the character, then selecting an accented character to
11+
insert in place of the typed character. This is exposed via UIScriptController.insertText, which takes a string
12+
and an insertion range.
13+
14+
* DumpRenderTree/mac/UIScriptControllerMac.mm:
15+
(WTR::UIScriptController::insertText):
16+
* TestRunnerShared/UIScriptContext/Bindings/UIScriptController.idl:
17+
18+
Note that there is no callback argument to insertText, since UIScriptController::insertText is synchronous in
19+
the UI process. The tests end when corresponding input events fired as a result of insertText have been received
20+
in the web process. Please see the new layout tests for more detail.
21+
22+
* TestRunnerShared/UIScriptContext/UIScriptController.cpp:
23+
(WTR::UIScriptController::insertText):
24+
* TestRunnerShared/UIScriptContext/UIScriptController.h:
25+
* WebKitTestRunner/mac/UIScriptControllerMac.mm:
26+
(WTR::nsStringFromJSString):
27+
(WTR::UIScriptController::insertText):
28+
129
2016-10-30 Sam Weinig <[email protected]>
230

331
[WebIDL] Restructure IDLParser structs to better match modern WebIDL concepts

Tools/DumpRenderTree/mac/UIScriptControllerMac.mm

+4
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,10 @@
4343
});
4444
}
4545

46+
void UIScriptController::insertText(JSStringRef, int, int)
47+
{
48+
}
49+
4650
}
4751

4852
#endif // PLATFORM(MAC)

Tools/TestRunnerShared/UIScriptContext/Bindings/UIScriptController.idl

+2
Original file line numberDiff line numberDiff line change
@@ -156,5 +156,7 @@ interface UIScriptController {
156156

157157
readonly attribute object selectionRangeViewRects; // An array of objects with 'left', 'top', 'width', and 'height' properties.
158158

159+
void insertText(DOMString text, long location, long length);
160+
159161
void uiScriptComplete(DOMString result);
160162
};

Tools/TestRunnerShared/UIScriptContext/UIScriptController.cpp

+8
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,14 @@ void UIScriptController::platformClearAllCallbacks()
317317
}
318318
#endif
319319

320+
#if !PLATFORM(MAC)
321+
322+
void UIScriptController::insertText(JSStringRef, int, int)
323+
{
324+
}
325+
326+
#endif
327+
320328
void UIScriptController::uiScriptComplete(JSStringRef result)
321329
{
322330
m_context->requestUIScriptCompletion(result);

0 commit comments

Comments
 (0)