Bug 1446253 - Make EventUtils.synthesizeComposition() dispatch keydown and keyup event in default r=smaug

We'll start to dispatch keydown event and keyup event even during composition.
So, for testing those events won't break our UI, we should make
EventUtils.synhtesizeComposition() and EventUtils.synthesizeCompositionChange()
dispatch keydown event and keyup event even if callers don't specify keyboard
event explicitly.

Typically, "keydown" event is marked as "processed by IME", i.e., keyCode
value is set to DOM_VK_PROCESSKEY and key is set to "Process", with our
widget which handles native IME and key input.  On the other hand, "keyup"
is NOT marked as so.

Therefore, this patch makes TextInputProcessor emulates this behavior without
any new special flags.  And for making possible to emulate special cases,
this patch adds two flags to nsITextInputProcessor.  One is
KEY_DONT_MARK_KEYDOWN_AS_PROCESSED.  The other is KEY_MARK_KEYUP_AS_PROCESSED.
Unfortunately, those flags have opposite meaning but this must be better than
making necessary to one flag for emulating usual keydown/keyup events.

Finally, this makes some tests specify better keyboard information to
synthesizeComposition() and synthesizeCompositionChange() to emulate
actual keyboard events during composition.

MozReview-Commit-ID: ItYaXILkNQE

--HG--
extra : rebase_source : e50cc77c1efbc12686d7ea334d41926c7392b30d
This commit is contained in:
Masayuki Nakano 2018-03-16 22:35:07 +09:00
parent 720853bca4
commit 1c1a60c08d
12 changed files with 639 additions and 225 deletions

View File

@ -21,10 +21,7 @@ add_task(async function() {
input.focus();
});
await BrowserTestUtils.synthesizeComposition({
type: "compositionstart",
data: ""
}, browser);
// FYI: "compositionstart" will be dispatched automatically.
await BrowserTestUtils.synthesizeCompositionChange({
composition: {
string: "x",

View File

@ -481,8 +481,9 @@ bool
TextInputProcessor::IsValidEventTypeForComposition(
const WidgetKeyboardEvent& aKeyboardEvent) const
{
// The key event type of composition methods must be "" or "keydown".
if (aKeyboardEvent.mMessage == eKeyDown) {
// The key event type of composition methods must be "", "keydown" or "keyup".
if (aKeyboardEvent.mMessage == eKeyDown ||
aKeyboardEvent.mMessage == eKeyUp) {
return true;
}
if (aKeyboardEvent.mMessage == eUnidentifiedEvent &&
@ -511,6 +512,12 @@ TextInputProcessor::MaybeDispatchKeydownForComposition(
return result;
}
// If the mMessage is eKeyUp, the caller doesn't want TIP to dispatch
// eKeyDown event.
if (aKeyboardEvent->mMessage == eKeyUp) {
return result;
}
// Modifier keys are not allowed because managing modifier state in this
// method makes this messy.
if (NS_WARN_IF(aKeyboardEvent->IsModifierKeyEvent())) {
@ -545,7 +552,7 @@ TextInputProcessor::MaybeDispatchKeyupForComposition(
}
// If the mMessage is eKeyDown, the caller doesn't want TIP to dispatch
// keyup event.
// eKeyUp event.
if (aKeyboardEvent->mMessage == eKeyDown) {
return result;
}
@ -1126,6 +1133,12 @@ TextInputProcessor::KeydownInternal(const WidgetKeyboardEvent& aKeyboardEvent,
}
keyEvent.mModifiers = GetActiveModifiers();
if (!aAllowToDispatchKeypress &&
!(aKeyFlags & KEY_DONT_MARK_KEYDOWN_AS_PROCESSED)) {
keyEvent.mKeyCode = NS_VK_PROCESSKEY;
keyEvent.mKeyNameIndex = KEY_NAME_INDEX_Process;
}
RefPtr<TextEventDispatcher> kungFuDeathGrip(mDispatcher);
rv = IsValidStateForComposition();
if (NS_WARN_IF(NS_FAILED(rv))) {
@ -1206,6 +1219,11 @@ TextInputProcessor::KeyupInternal(const WidgetKeyboardEvent& aKeyboardEvent,
}
keyEvent.mModifiers = GetActiveModifiers();
if (aKeyFlags & KEY_MARK_KEYUP_AS_PROCESSED) {
keyEvent.mKeyCode = NS_VK_PROCESSKEY;
keyEvent.mKeyNameIndex = KEY_NAME_INDEX_Process;
}
RefPtr<TextEventDispatcher> kungFuDeathGrip(mDispatcher);
rv = IsValidStateForComposition();
if (NS_WARN_IF(NS_FAILED(rv))) {

View File

@ -305,6 +305,10 @@ interface nsITextInputProcessor : nsISupports
* If its type value is "keydown", this method
* dispatches only keydown event first. Otherwise,
* dispatches keydown first and keyup at last.
* key value and keyCode values of keydown event
* are set to "Process" and DOM_VK_PROCESSKEY
* automatically. You can prevent this behavior
* with KEY_DONT_MARK_KEYDOWN_AS_PROCESSED.
* @param aKeyFlags See KEY_* constants.
* @return Returns true if composition starts normally.
* Otherwise, returns false because it might be
@ -393,6 +397,10 @@ interface nsITextInputProcessor : nsISupports
* If its type value is "keydown", this method
* dispatches only keydown event first. Otherwise,
* dispatches keydown first and keyup at last.
* key value and keyCode values of keydown event
* are set to "Process" and DOM_VK_PROCESSKEY
* automatically. You can prevent this behavior
* with KEY_DONT_MARK_KEYDOWN_AS_PROCESSED.
* @param aKeyFlags See KEY_* constants.
* @return Returns true if there is a composition already or
* starting composition automatically.
@ -413,6 +421,10 @@ interface nsITextInputProcessor : nsISupports
* If its type value is "keydown", this method
* dispatches only keydown event first. Otherwise,
* dispatches keydown first and keyup at last.
* key value and keyCode values of keydown event
* are set to "Process" and DOM_VK_PROCESSKEY
* automatically. You can prevent this behavior
* with KEY_DONT_MARK_KEYDOWN_AS_PROCESSED.
* @param aKeyFlags See KEY_* constants.
*/
[optional_argc]
@ -429,6 +441,10 @@ interface nsITextInputProcessor : nsISupports
* If its type value is "keydown", this method
* dispatches only keydown event first. Otherwise,
* dispatches keydown first and keyup at last.
* key value and keyCode values of keydown event
* are set to "Process" and DOM_VK_PROCESSKEY
* automatically. You can prevent this behavior
* with KEY_DONT_MARK_KEYDOWN_AS_PROCESSED.
* @param aKeyFlags See KEY_* constants.
* @return Returns true if there is a composition already or
* starting composition automatically.
@ -454,6 +470,10 @@ interface nsITextInputProcessor : nsISupports
* If its type value is "keydown", this method
* dispatches only keydown event first. Otherwise,
* dispatches keydown first and keyup at last.
* key value and keyCode values of keydown event
* are set to "Process" and DOM_VK_PROCESSKEY
* automatically. You can prevent this behavior
* with KEY_DONT_MARK_KEYDOWN_AS_PROCESSED.
* @param aKeyFlags See KEY_* constants.
*/
[optional_argc]
@ -488,6 +508,12 @@ interface nsITextInputProcessor : nsISupports
// without dispatching key events. This is useful for testing odd behavior
// or emulating legacy API behavior.
const unsigned long KEY_DONT_DISPATCH_MODIFIER_KEY_EVENT = 0x00000020;
// If KEY_DONT_MARK_KEYDOWN_AS_PROCESSED is specified, key value and keyCode
// value of keydown event are not changed to "Process" and DOM_VK_PROCESSKEY.
const unsigned long KEY_DONT_MARK_KEYDOWN_AS_PROCESSED = 0x00000040;
// If KEY_MARK_KEYUP_AS_PROCESSED is specified, key value and keyCode value
// of keyup event are changed to "Process" and DOM_VK_PROCESSKEY.
const unsigned long KEY_MARK_KEYUP_AS_PROCESSED = 0x00000080;
// These values can be used to do bitwise operation with the return value of
// the keydown() method.

View File

@ -15,7 +15,7 @@ function runTests() {
var plugin = document.getElementById("plugin1");
plugin.focus();
synthesizeComposition({ type: "compositionstart", data: "" });
// FYI: "compositionstart" will be dispatched automatically.
let data = "composition";
synthesizeCompositionChange({
composition: {

View File

@ -36,8 +36,8 @@ SimpleTest.waitForFocus(function() {
is(t.selectionEnd, 3, "Correct end of selection");
// Compose an IME string
synthesizeComposition({ type: "compositionstart" });
var composingString = "\u306B";
// FYI: "compositionstart" will be dispatched automatically.
synthesizeCompositionChange(
{ "composition":
{ "string": composingString,

View File

@ -56,7 +56,7 @@ SimpleTest.waitForFocus(()=>{
aEditor.focus();
aEditor.addEventListener("compositionstart", committer, true);
synthesizeCompositionChange({ composition: { string: "a", clauses: [{length: 1, attr: COMPOSITION_ATTR_RAW_CLAUSE }] },
caret: { start: 1, length: 0 }});
caret: { start: 1, length: 0 }, key: { key: "a" }});
aEditor.removeEventListener("compositionstart", committer, true);
ok(!isComposing(), "composition in " + aEditor.id + " should be committed by compositionstart event handler");
is(value(), "", "composition in " + aEditor.id + " shouldn't insert any text since it's committed at compositionstart");
@ -66,7 +66,7 @@ SimpleTest.waitForFocus(()=>{
aEditor.focus();
aEditor.addEventListener("compositionupdate", committer, true);
synthesizeCompositionChange({ composition: { string: "a", clauses: [{length: 1, attr: COMPOSITION_ATTR_RAW_CLAUSE }] },
caret: { start: 1, length: 0 }});
caret: { start: 1, length: 0 }, key: { key: "a" }});
aEditor.removeEventListener("compositionupdate", committer, true);
ok(!isComposing(), "composition in " + aEditor.id + " should be committed by compositionupdate event handler");
is(value(), "", "composition in " + aEditor.id + " shouldn't have inserted any text since it's committed at first compositionupdate");
@ -76,7 +76,7 @@ SimpleTest.waitForFocus(()=>{
aEditor.focus();
aEditor.addEventListener("text", committer, true);
synthesizeCompositionChange({ composition: { string: "a", clauses: [{length: 1, attr: COMPOSITION_ATTR_RAW_CLAUSE }] },
caret: { start: 1, length: 0 }});
caret: { start: 1, length: 0 }, key: { key: "a" }});
aEditor.removeEventListener("text", committer, true);
ok(!isComposing(), "composition in " + aEditor.id + " should be committed by text event handler");
is(value(), "", "composition in " + aEditor.id + " should have inserted any text since it's committed at first text");
@ -84,14 +84,14 @@ SimpleTest.waitForFocus(()=>{
// Committing at second compositionupdate
aEditor.focus();
synthesizeComposition({ type: "compositionstart" });
// FYI: "compositionstart" will be dispatched automatically.
synthesizeCompositionChange({ composition: { string: "a", clauses: [{length: 1, attr: COMPOSITION_ATTR_RAW_CLAUSE }] },
caret: { start: 1, length: 0 }});
caret: { start: 1, length: 0 }, key: { key: "a" }});
ok(isComposing(), "composition should be in " + aEditor.id + " before dispatching second compositionupdate");
is(value(), "a", "composition in " + aEditor.id + " should be 'a' before dispatching second compositionupdate");
aEditor.addEventListener("compositionupdate", committer, true);
synthesizeCompositionChange({ composition: { string: "ab", clauses: [{length: 2, attr: COMPOSITION_ATTR_RAW_CLAUSE }] },
caret: { start: 2, length: 0 }});
caret: { start: 2, length: 0 }, key: { key: "b" }});
aEditor.removeEventListener("compositionupdate", committer, true);
ok(!isComposing(), "composition in " + aEditor.id + " should be committed by compositionupdate event handler");
todo_is(value(), "a", "composition in " + aEditor.id + " shouldn't have been modified since it's committed at second compositionupdate");
@ -99,14 +99,14 @@ SimpleTest.waitForFocus(()=>{
// Committing at second text (eCompositionChange)
aEditor.focus();
synthesizeComposition({ type: "compositionstart" });
// FYI: "compositionstart" will be dispatched automatically.
synthesizeCompositionChange({ composition: { string: "a", clauses: [{length: 1, attr: COMPOSITION_ATTR_RAW_CLAUSE }] },
caret: { start: 1, length: 0 }});
caret: { start: 1, length: 0 }, key: { key: "a" }});
ok(isComposing(), "composition should be in " + aEditor.id + " before dispatching second text");
is(value(), "a", "composition in " + aEditor.id + " should be 'a' before dispatching second text");
aEditor.addEventListener("text", committer, true);
synthesizeCompositionChange({ composition: { string: "ab", clauses: [{length: 2, attr: COMPOSITION_ATTR_RAW_CLAUSE }] },
caret: { start: 2, length: 0 }});
caret: { start: 2, length: 0 }, key: { key: "b" }});
aEditor.removeEventListener("text", committer, true);
ok(!isComposing(), "composition in " + aEditor.id + " should be committed by text event handler");
todo_is(value(), "a", "composition in " + aEditor.id + " shouldn't have been modified since it's committed at second text");

View File

@ -32,6 +32,8 @@
<script class="testbody" type="text/javascript">
const kStrictKeyPressEvents =
SpecialPowers.getBoolPref("dom.keyboardevent.keypress.dispatch_non_printable_keys_only_system_group_in_content");
const kStrictKeyDownKeyUpEvents =
SpecialPowers.getBoolPref("dom.keyboardevent.dispatch_during_composition");
info("\nProfile::EventUtilsLoadTime: " + (loadTime - start) + "\n");
function starttest() {
@ -40,14 +42,17 @@ function starttest() {
SimpleTest.waitForExplicitFinish();
var startTime = new Date();
var check = false;
function doCheck() {
check = true;
}
/* test send* functions */
$("testMouseEvent").addEventListener("click", function() { check=true; });
$("testMouseEvent").addEventListener("click", doCheck, {once: true});
sendMouseEvent({type:'click'}, "testMouseEvent");
is(check, true, 'sendMouseEvent should dispatch click event');
check = false;
$("testKeyEvent").addEventListener("keypress", function() { check = true; }, {once: true});
$("testKeyEvent").addEventListener("keypress", doCheck, {once: true});
$("testKeyEvent").focus();
sendChar("x");
is($("testKeyEvent").value, "x", "sendChar should work");
@ -110,7 +115,7 @@ function starttest() {
/* test synthesizeKey* */
check = false;
$("testKeyEvent").addEventListener("keypress", function() { check = true; });
$("testKeyEvent").addEventListener("keypress", doCheck, {once:true});
$("testKeyEvent").focus();
sendString("a");
is($("testKeyEvent").value, "a", "synthesizeKey should work");
@ -118,25 +123,72 @@ function starttest() {
$("testKeyEvent").value = "";
check = false;
$("testKeyEvent").addEventListener("keypress", doCheck, {once:true});
synthesizeKeyExpectEvent("a", {}, $("testKeyEvent"), "keypress");
is($("testKeyEvent").value, "a", "synthesizeKey should work");
is(check, true, "synthesizeKey should dispatch keyPress");
$("testKeyEvent").value = "";
/* test synthesizeComposition */
$("textBoxB").focus();
check = false;
window.addEventListener("compositionstart", function() { check = true; });
synthesizeComposition({ type: "compositionstart" });
is(check, true, 'synthesizeComposition() should dispatch compositionstart');
check = false;
window.addEventListener("compositionupdate", function() { check = true; });
synthesizeComposition({ type: "compositionupdate", data: "a" });
is(check, false, 'synthesizeComposition() should not dispatch compositionupdate without error');
var description = "";
var keydownEvent = null;
var keyupEvent = null;
function onKeyDown(aEvent) {
ok(!keydownEvent, description + "keydown should be fired only once" + (keydownEvent ? keydownEvent.key : "") + ", " + (keyupEvent ? keyupEvent.key : ""));
keydownEvent = aEvent;
}
function onKeyUp(aEvent) {
ok(!keyupEvent, description + "keyup should be fired only once");
keyupEvent = aEvent;
}
function resetKeyDownAndKeyUp(aDescription) {
description = aDescription + ": ";
keydownEvent = null;
keyupEvent = null;
check = false;
}
function checkKeyDownAndKeyUp(aKeyDown, aKeyUp) {
if (aKeyDown && (!aKeyDown.inComposition || kStrictKeyDownKeyUpEvents)) {
is(keydownEvent.keyCode, aKeyDown.keyCode,
description + "keydown event should be dispatched (checking keyCode)");
is(keydownEvent.key, aKeyDown.key,
description + "keydown event should be dispatched (checking key)");
} else {
is(keydownEvent, null,
description + "keydown event shouldn't be fired");
}
if (aKeyUp && (!aKeyUp.inComposition || kStrictKeyDownKeyUpEvents)) {
is(keyupEvent.keyCode, aKeyUp.keyCode,
description + "keyup event should be dispatched (checking keyCode)");
is(keyupEvent.key, aKeyUp.key,
description + "keyup event should be dispatched (checking key)");
} else {
is(keyupEvent, null,
description + "keyup event shouldn't be fired");
}
}
$("textBoxB").addEventListener("keydown", onKeyDown);
$("textBoxB").addEventListener("keyup", onKeyUp);
check = false;
window.addEventListener("text", function() { check = true; });
$("textBoxB").focus();
// If key event is not specified, fake keydown and keyup events which are
// marked as "processed by IME" should be fired.
resetKeyDownAndKeyUp("synthesizing eCompositionStart without specifying keyboard event");
window.addEventListener("compositionstart", doCheck, {once: true});
synthesizeComposition({type: "compositionstart"});
ok(check, description + "synthesizeComposition() should dispatch compositionstart");
checkKeyDownAndKeyUp({inComposition: false, keyCode: KeyboardEvent.DOM_VK_PROCESSKEY, key: "Process"},
{inComposition: true, keyCode: KeyboardEvent.DOM_VK_PROCESSKEY, key: "Process"});
resetKeyDownAndKeyUp("trying to synthesize eCompositionUpdate directly without specifying keyboard event");
window.addEventListener("compositionupdate", doCheck, {once: true});
synthesizeComposition({type: "compositionupdate", data: "a"});
ok(!check, description + "synthesizeComposition() should not dispatch compositionupdate without error");
checkKeyDownAndKeyUp(null, null);
resetKeyDownAndKeyUp("synthesizing eCompositionChange without specifying keyboard event");
window.addEventListener("text", doCheck, {once: true});
synthesizeCompositionChange(
{ "composition":
{ "string": "a",
@ -148,8 +200,11 @@ function starttest() {
"caret": { "start": 1, "length": 0 }
}
);
is(check, true, "synthesizeCompositionChange should cause dispatching a DOM text event");
ok(check, description + "synthesizeCompositionChange should cause dispatching a DOM text event");
checkKeyDownAndKeyUp({inComposition: true, keyCode: KeyboardEvent.DOM_VK_PROCESSKEY, key: "Process"},
{inComposition: true, keyCode: KeyboardEvent.DOM_VK_PROCESSKEY, key: "Process"});
resetKeyDownAndKeyUp("synthesizing eCompositionChange for removing clauses without specifying keyboard event");
synthesizeCompositionChange(
{ "composition":
{ "string": "a",
@ -161,14 +216,20 @@ function starttest() {
"caret": { "start": 1, "length": 0 }
}
);
checkKeyDownAndKeyUp({inComposition: true, keyCode: KeyboardEvent.DOM_VK_PROCESSKEY, key: "Process"},
{inComposition: true, keyCode: KeyboardEvent.DOM_VK_PROCESSKEY, key: "Process"});
check = false;
window.addEventListener("compositionend", function() { check = true; });
synthesizeComposition({ type: "compositionend", data: "a" });
is(check, false, 'synthesizeComposition() should not dispatch compositionend');
resetKeyDownAndKeyUp("trying to synthesize eCompositionEnd directly without specifying keyboard event");
window.addEventListener("compositionend", doCheck, {once: true});
synthesizeComposition({type: "compositionend", data: "a"});
ok(!check, description + "synthesizeComposition() should not dispatch compositionend");
checkKeyDownAndKeyUp(null, null);
synthesizeComposition({ type: "compositioncommit", data: "a" });
is(check, true, 'synthesizeComposition() should dispatch compositionend');
resetKeyDownAndKeyUp("synthesizing eCompositionCommit without specifying keyboard event");
synthesizeComposition({type: "compositioncommit", data: "a"});
ok(check, description + "synthesizeComposition() should dispatch compositionend");
checkKeyDownAndKeyUp({inComposition: true, keyCode: KeyboardEvent.DOM_VK_PROCESSKEY, key: "Process"},
{inComposition: false, keyCode: KeyboardEvent.DOM_VK_PROCESSKEY, key: "Process"});
var querySelectedText = synthesizeQuerySelectedText();
ok(querySelectedText, "query selected text event result is null");
@ -188,6 +249,202 @@ function starttest() {
"query selected text event returns wrong selected text");
var endTime = new Date();
info("\nProfile::EventUtilsRunTime: " + (endTime-startTime) + "\n");
// In most cases, automated tests shouldn't try to synthesize
// compositionstart manually. Let's check if synthesizeCompositionChange()
// dispatches compositionstart automatically.
resetKeyDownAndKeyUp("synthesizing eCompositionChange without specifying keyboard event when there is no composition");
window.addEventListener("compositionstart", doCheck, {once: true});
synthesizeCompositionChange(
{ "composition":
{ "string": "a",
"clauses":
[
{ "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
]
},
"caret": { "start": 1, "length": 0 }
}
);
ok(check, description + "synthesizeCompositionChange should dispatch \"compositionstart\" automatically if there is no composition");
checkKeyDownAndKeyUp({inComposition: false, keyCode: KeyboardEvent.DOM_VK_PROCESSKEY, key: "Process"},
{inComposition: true, keyCode: KeyboardEvent.DOM_VK_PROCESSKEY, key: "Process"});
resetKeyDownAndKeyUp("synthesizing eCompositionCommitAsIs without specifying keyboard event");
synthesizeComposition({type: "compositioncommitasis"});
checkKeyDownAndKeyUp({inComposition: true, keyCode: KeyboardEvent.DOM_VK_PROCESSKEY, key: "Process"},
{inComposition: false, keyCode: KeyboardEvent.DOM_VK_PROCESSKEY, key: "Process"});
// If key event is specified, keydown event which is marked as "processed
// by IME" should be fired and keyup event which is NOT marked as so
// should be fired too.
resetKeyDownAndKeyUp("synthesizing eCompositionStart with specifying keyboard event");
synthesizeComposition({type: "compositionstart", key: {key: "a"}});
checkKeyDownAndKeyUp({inComposition: false, keyCode: KeyboardEvent.DOM_VK_PROCESSKEY, key: "Process"},
{inComposition: true, keyCode: KeyboardEvent.DOM_VK_A, key: "a"});
resetKeyDownAndKeyUp("synthesizing eCompositionChange with specifying keyboard event");
synthesizeCompositionChange(
{"composition":
{"string": "b", "clauses": [
{"length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE}
]},
"caret": {"start": 1, "length": 0},
"key": {key: "b"},
}
);
checkKeyDownAndKeyUp({inComposition: true, keyCode: KeyboardEvent.DOM_VK_PROCESSKEY, key: "Process"},
{inComposition: true, keyCode: KeyboardEvent.DOM_VK_B, key: "b"});
resetKeyDownAndKeyUp("synthesizing eCompositionCommit with specifying keyboard event");
synthesizeComposition({type: "compositioncommit", data: "c", key: {key: "KEY_Enter"}});
checkKeyDownAndKeyUp({inComposition: true, keyCode: KeyboardEvent.DOM_VK_PROCESSKEY, key: "Process"},
{inComposition: false, keyCode: KeyboardEvent.DOM_VK_RETURN, key: "Enter"});
// keyup shouldn't be dispatched automatically if type is specified as keydown
resetKeyDownAndKeyUp("synthesizing eCompositionStart with specifying keyboard event whose type is keydown");
synthesizeComposition({type: "compositionstart", key: {key: "a", type: "keydown"}});
checkKeyDownAndKeyUp({inComposition: false, keyCode: KeyboardEvent.DOM_VK_PROCESSKEY, key: "Process"},
null);
resetKeyDownAndKeyUp("synthesizing eCompositionChange with specifying keyboard event whose type is keydown");
synthesizeCompositionChange(
{"composition":
{"string": "b", "clauses": [
{"length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE}
]},
"caret": {"start": 1, "length": 0},
"key": {key: "b", type: "keydown"},
}
);
checkKeyDownAndKeyUp({inComposition: true, keyCode: KeyboardEvent.DOM_VK_PROCESSKEY, key: "Process"},
null);
resetKeyDownAndKeyUp("synthesizing eCompositionCommit with specifying keyboard event whose type is keydown");
synthesizeComposition({type: "compositioncommit", data: "c", key: {key: "KEY_Enter", type: "keydown"}});
checkKeyDownAndKeyUp({inComposition: true, keyCode: KeyboardEvent.DOM_VK_PROCESSKEY, key: "Process"},
null);
// keydown shouldn't be dispatched automatically if type is specified as keyup
resetKeyDownAndKeyUp("synthesizing eCompositionStart with specifying keyboard event whose type is keyup");
synthesizeComposition({type: "compositionstart", key: {key: "a", type: "keyup"}});
checkKeyDownAndKeyUp(null,
{inComposition: true, keyCode: KeyboardEvent.DOM_VK_A, key: "a"});
resetKeyDownAndKeyUp("synthesizing eCompositionChange with specifying keyboard event whose type is keyup");
synthesizeCompositionChange(
{"composition":
{"string": "b", "clauses": [
{"length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE}
]},
"caret": {"start": 1, "length": 0},
"key": {key: "b", type: "keyup"},
}
);
checkKeyDownAndKeyUp(null,
{inComposition: true, keyCode: KeyboardEvent.DOM_VK_B, key: "b"});
resetKeyDownAndKeyUp("synthesizing eCompositionCommit with specifying keyboard event whose type is keyup");
synthesizeComposition({type: "compositioncommit", data: "c", key: {key: "KEY_Enter", type: "keyup"}});
checkKeyDownAndKeyUp(null,
{inComposition: false, keyCode: KeyboardEvent.DOM_VK_RETURN, key: "Enter"});
// keydown event shouldn't be marked as "processed by IME" if doNotMarkKeydownAsProcessed is true
resetKeyDownAndKeyUp("synthesizing eCompositionStart with specifying keyboard event whose doNotMarkKeydownAsProcessed is true");
synthesizeComposition({type: "compositionstart", key: {key: "a", doNotMarkKeydownAsProcessed: true}});
checkKeyDownAndKeyUp({inComposition: false, keyCode: KeyboardEvent.DOM_VK_A, key: "a"},
{inComposition: true, keyCode: KeyboardEvent.DOM_VK_A, key: "a"});
resetKeyDownAndKeyUp("synthesizing eCompositionChange with specifying keyboard event whose doNotMarkKeydownAsProcessed is true");
synthesizeCompositionChange(
{"composition":
{"string": "b", "clauses": [
{"length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE}
]},
"caret": {"start": 1, "length": 0},
"key": {key: "b", doNotMarkKeydownAsProcessed: true},
}
);
checkKeyDownAndKeyUp({inComposition: true, keyCode: KeyboardEvent.DOM_VK_B, key: "b"},
{inComposition: true, keyCode: KeyboardEvent.DOM_VK_B, key: "b"});
resetKeyDownAndKeyUp("synthesizing eCompositionCommit with specifying keyboard event whose doNotMarkKeydownAsProcessed is true");
synthesizeComposition({type: "compositioncommit", data: "c", key: {key: "KEY_Enter", doNotMarkKeydownAsProcessed: true}});
checkKeyDownAndKeyUp({inComposition: true, keyCode: KeyboardEvent.DOM_VK_RETURN, key: "Enter"},
{inComposition: false, keyCode: KeyboardEvent.DOM_VK_RETURN, key: "Enter"});
// keyup event should be marked as "processed by IME" if markKeyupAsProcessed is true
resetKeyDownAndKeyUp("synthesizing eCompositionStart with specifying keyboard event whose markKeyupAsProcessed is true");
synthesizeComposition({type: "compositionstart", key: {key: "a", markKeyupAsProcessed: true}});
checkKeyDownAndKeyUp({inComposition: false, keyCode: KeyboardEvent.DOM_VK_PROCESSKEY, key: "Process"},
{inComposition: true, keyCode: KeyboardEvent.DOM_VK_PROCESSKEY, key: "Process"});
resetKeyDownAndKeyUp("synthesizing eCompositionChange with specifying keyboard event whose markKeyupAsProcessed is true");
synthesizeCompositionChange(
{"composition":
{"string": "b", "clauses": [
{"length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE}
]},
"caret": {"start": 1, "length": 0},
"key": {key: "b", markKeyupAsProcessed: true},
}
);
checkKeyDownAndKeyUp({inComposition: true, keyCode: KeyboardEvent.DOM_VK_PROCESSKEY, key: "Process"},
{inComposition: true, keyCode: KeyboardEvent.DOM_VK_PROCESSKEY, key: "Process"});
resetKeyDownAndKeyUp("synthesizing eCompositionCommit with specifying keyboard event whose markKeyupAsProcessed is true");
synthesizeComposition({type: "compositioncommit", data: "c", key: {key: "KEY_Enter", markKeyupAsProcessed: true}});
checkKeyDownAndKeyUp({inComposition: true, keyCode: KeyboardEvent.DOM_VK_PROCESSKEY, key: "Process"},
{inComposition: false, keyCode: KeyboardEvent.DOM_VK_PROCESSKEY, key: "Process"});
// If key event is explicitly declared with null, keyboard events shouldn't
// be fired for emulating text inputs without keyboard such as voice input or something.
resetKeyDownAndKeyUp("synthesizing eCompositionStart with specifying keyboard event as null");
synthesizeComposition({type: "compositionstart", key: null});
checkKeyDownAndKeyUp(null, null);
resetKeyDownAndKeyUp("synthesizing eCompositionChange with specifying keyboard event as null");
synthesizeCompositionChange(
{"composition":
{"string": "b", "clauses": [
{"length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE}
]},
"caret": {"start": 1, "length": 0},
"key": null,
}
);
checkKeyDownAndKeyUp(null, null);
resetKeyDownAndKeyUp("synthesizing eCompositionCommit with specifying keyboard event as null");
synthesizeComposition({type: "compositioncommit", data: "c", key: null});
checkKeyDownAndKeyUp(null, null);
// If key event is explicitly declared with empty object, keyboard events
// shouldn't be fired for emulating text inputs without keyboard such as
// voice input or something.
resetKeyDownAndKeyUp("synthesizing eCompositionStart with specifying keyboard event as empty");
synthesizeComposition({type: "compositionstart", key: {}});
checkKeyDownAndKeyUp(null, null);
resetKeyDownAndKeyUp("synthesizing eCompositionChange with specifying keyboard event as empty");
synthesizeCompositionChange(
{"composition":
{"string": "b", "clauses": [
{"length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE}
]},
"caret": {"start": 1, "length": 0},
"key": {},
}
);
checkKeyDownAndKeyUp(null, null);
resetKeyDownAndKeyUp("synthesizing eCompositionCommit with specifying keyboard event as empty");
synthesizeComposition({type: "compositioncommit", data: "c", key: {}});
checkKeyDownAndKeyUp(null, null);
$("textBoxB").removeEventListener("keydown", onKeyDown);
$("textBoxB").removeEventListener("keyup", onKeyUp);
SimpleTest.finish();
}
);

View File

@ -1469,6 +1469,8 @@ function _guessKeyNameFromKeyCode(aKeyCode, aWindow = window)
return "Meta";
case KeyboardEvent.DOM_VK_ALTGR:
return "AltGraph";
case KeyboardEvent.DOM_VK_PROCESSKEY:
return "Process";
case KeyboardEvent.DOM_VK_ATTN:
return "Attn";
case KeyboardEvent.DOM_VK_CRSEL:
@ -1781,6 +1783,13 @@ function _createKeyboardEventDictionary(aKey, aKeyEvent, aWindow = window) {
if (locationIsDefined && aKeyEvent.location === 0) {
result.flags |= _EU_Ci.nsITextInputProcessor.KEY_KEEP_KEY_LOCATION_STANDARD;
}
if (aKeyEvent.doNotMarkKeydownAsProcessed) {
result.flags |=
_EU_Ci.nsITextInputProcessor.KEY_DONT_MARK_KEYDOWN_AS_PROCESSED;
}
if (aKeyEvent.markKeyupAsProcessed) {
result.flags |= _EU_Ci.nsITextInputProcessor.KEY_MARK_KEYUP_AS_PROCESSED;
}
result.dictionary = {
key: keyName,
code: code,
@ -1880,22 +1889,47 @@ function _emulateToInactivateModifiers(aTIP, aModifiers, aWindow = window)
}
/**
* Synthesize a composition event.
* Synthesize a composition event and keydown event and keyup events unless
* you prevent to dispatch them explicitly (see aEvent.key's explanation).
*
* Note that you shouldn't call this with "compositionstart" unless you need to
* test compositionstart event which is NOT followed by compositionupdate
* event immediately. Typically, native IME starts composition with
* a pair of keydown and keyup event and dispatch compositionstart and
* compositionupdate (and non-standard text event) between them. So, in most
* cases, you should call synthesizeCompositionChange() directly.
* If you call this with compositionstart, keyup event will be fired
* immediately after compositionstart. In other words, you should use
* "compositionstart" only when you need to emulate IME which just starts
* composition with compositionstart event but does not send composing text to
* us until committing the composition. This is behavior of some Chinese IMEs.
*
* @param aEvent The composition event information. This must
* have |type| member. The value must be
* "compositionstart", "compositionend",
* "compositioncommitasis" or "compositioncommit".
*
* And also this may have |data| and |locale| which
* would be used for the value of each property of
* the composition event. Note that the |data| is
* ignored if the event type is "compositionstart"
* or "compositioncommitasis".
* If |key| is specified, the key event may be
* dispatched. This can emulates changing
* composition state caused by key operation.
* Its key value should start with "KEY_" if the
* value is non-printable key name defined in D3E.
*
* If |key| is undefined, "keydown" and "keyup"
* events which are marked as "processed by IME"
* are dispatched. If |key| is not null, "keydown"
* and/or "keyup" events are dispatched (if the
* |key.type| is specified as "keydown", only
* "keydown" event is dispatched). Otherwise,
* i.e., if |key| is null, neither "keydown" nor
* "keyup" event is dispatched.
*
* If |key.doNotMarkKeydownAsProcessed| is not true,
* key value and keyCode value of "keydown" event
* will be set to "Process" and DOM_VK_PROCESSKEY.
* If |key.markKeyupAsProcessed| is true,
* key value and keyCode value of "keyup" event
* will be set to "Process" and DOM_VK_PROCESSKEY.
* @param aWindow Optional (If null, current |window| will be used)
* @param aCallback Optional (If non-null, use the callback for
* receiving notifications to IME)
@ -1909,15 +1943,20 @@ function synthesizeComposition(aEvent, aWindow = window, aCallback)
var KeyboardEvent = _getKeyboardEvent(aWindow);
var modifiers = _emulateToActivateModifiers(TIP, aEvent.key, aWindow);
var ret = false;
var keyEventDict =
"key" in aEvent ?
_createKeyboardEventDictionary(aEvent.key.key, aEvent.key, aWindow) :
{ dictionary: null, flags: 0 };
var keyEvent =
"key" in aEvent ?
new KeyboardEvent(aEvent.type === "keydown" ? "keydown" : "",
keyEventDict.dictionary) :
null;
var keyEventDict = {dictionary: null, flags: 0};
var keyEvent = null;
if (aEvent.key && typeof aEvent.key.key === "string") {
keyEventDict =
_createKeyboardEventDictionary(aEvent.key.key, aEvent.key, aWindow);
keyEvent = new KeyboardEvent(aEvent.key.type === "keydown" ?
"keydown" :
aEvent.key.type === "keyup" ?
"keyup" : "",
keyEventDict.dictionary)
} else if (aEvent.key === undefined) {
keyEventDict = _createKeyboardEventDictionary("KEY_Process", {}, aWindow);
keyEvent = new KeyboardEvent("", keyEventDict.dictionary)
}
try {
switch (aEvent.type) {
case "compositionstart":
@ -1936,8 +1975,15 @@ function synthesizeComposition(aEvent, aWindow = window, aCallback)
}
}
/**
* Synthesize a compositionchange event which causes a DOM text event and
* compositionupdate event if it's necessary.
* Synthesize eCompositionChange event which causes a DOM text event, may
* cause compositionupdate event, and causes keydown event and keyup event
* unless you prevent to dispatch them explicitly (see aEvent.key's
* explanation).
*
* Note that if you call this when there is no composition, compositionstart
* event will be fired automatically. This is better than you use
* synthesizeComposition("compositionstart") in most cases. See the
* explanation of syntehszeComposition().
*
* @param aEvent The compositionchange event's information, this has
* |composition| and |caret| members. |composition| has
@ -1975,10 +2021,18 @@ function synthesizeComposition(aEvent, aWindow = window, aCallback)
* caret. However, current nsEditor doesn't support wide
* caret, therefore, you should always set 0 now.
*
* If |key| is specified, the key event may be dispatched.
* This can emulates changing composition state caused by key
* operation. Its key value should start with "KEY_" if the
* value is non-printable key name defined in D3E.
* If |key| is undefined, "keydown" and "keyup" events which
* are marked as "processed by IME" are dispatched. If |key|
* is not null, "keydown" and/or "keyup" events are dispatched
* (if the |key.type| is specified as "keydown", only "keydown"
* event is dispatched). Otherwise, i.e., if |key| is null,
* neither "keydown" nor "keyup" event is dispatched.
* If |key.doNotMarkKeydownAsProcessed| is not true, key value
* and keyCode value of "keydown" event will be set to
* "Process" and DOM_VK_PROCESSKEY.
* If |key.markKeyupAsProcessed| is true key value and keyCode
* value of "keyup" event will be set to "Process" and
* DOM_VK_PROCESSKEY.
*
* @param aWindow Optional (If null, current |window| will be used)
* @param aCallback Optional (If non-null, use the callback for receiving
@ -2025,15 +2079,20 @@ function synthesizeCompositionChange(aEvent, aWindow = window, aCallback)
var modifiers = _emulateToActivateModifiers(TIP, aEvent.key, aWindow);
try {
var keyEventDict =
"key" in aEvent ?
_createKeyboardEventDictionary(aEvent.key.key, aEvent.key, aWindow) :
{ dictionary: null, flags: 0 };
var keyEvent =
"key" in aEvent ?
new KeyboardEvent(aEvent.type === "keydown" ? "keydown" : "",
keyEventDict.dictionary) :
null;
var keyEventDict = {dictionary: null, flags: 0};
var keyEvent = null;
if (aEvent.key && typeof aEvent.key.key === "string") {
keyEventDict =
_createKeyboardEventDictionary(aEvent.key.key, aEvent.key, aWindow);
keyEvent = new KeyboardEvent(aEvent.key.type === "keydown" ?
"keydown" :
aEvent.key.type === "keyup" ?
"keyup" : "",
keyEventDict.dictionary)
} else if (aEvent.key === undefined) {
keyEventDict = _createKeyboardEventDictionary("KEY_Process", {}, aWindow);
keyEvent = new KeyboardEvent("", keyEventDict.dictionary)
}
TIP.flushPendingComposition(keyEvent, keyEventDict.flags);
} finally {
_emulateToInactivateModifiers(TIP, modifiers, aWindow);

View File

@ -98,8 +98,7 @@ nsDoTestsForAutoCompleteWithComposition.prototype = {
]
},
"caret": { "start": 1, "length": 0 },
"key": { key: "M", code: "KeyM", keyCode: KeyboardEvent.DOM_VK_M,
shiftKey: true },
"key": { key: "M" },
}, aWindow);
}, popup: false, value: "M", searchString: ""
},
@ -115,15 +114,14 @@ nsDoTestsForAutoCompleteWithComposition.prototype = {
]
},
"caret": { "start": 2, "length": 0 },
"key": { key: "o", code: "KeyO", keyCode: KeyboardEvent.DOM_VK_O },
"key": { key: "o" },
}, aWindow);
}, popup: false, value: "Mo", searchString: ""
},
{ description: "compositionend should open the popup",
completeDefaultIndex: false,
execute(aWindow) {
synthesizeComposition({ type: "compositioncommitasis",
key: { key: "KEY_Enter", code: "Enter" } }, aWindow);
synthesizeComposition({ type: "compositioncommitasis", key: { key: "KEY_Enter" } }, aWindow);
}, popup: true, value: "Mo", searchString: "Mo"
},
// If composition starts when popup is shown, the compositionstart event
@ -140,7 +138,7 @@ nsDoTestsForAutoCompleteWithComposition.prototype = {
]
},
"caret": { "start": 1, "length": 0 },
"key": { key: "z", code: "KeyZ", keyCode: KeyboardEvent.DOM_VK_Z },
"key": { key: "z" },
}, aWindow);
}, popup: false, value: "Moz", searchString: "Mo"
},
@ -156,15 +154,14 @@ nsDoTestsForAutoCompleteWithComposition.prototype = {
]
},
"caret": { "start": 2, "length": 0 },
"key": { key: "i", code: "KeyI", keyCode: KeyboardEvent.DOM_VK_I },
"key": { key: "i" },
}, aWindow);
}, popup: false, value: "Mozi", searchString: "Mo"
},
{ description: "compositionend should research the result and open the popup",
completeDefaultIndex: false,
execute(aWindow) {
synthesizeComposition({ type: "compositioncommitasis",
key: { key: "KEY_Enter", code: "Enter" } }, aWindow);
synthesizeComposition({ type: "compositioncommitasis", key: { key: "KEY_Enter" } }, aWindow);
}, popup: true, value: "Mozi", searchString: "Mozi"
},
// If composition is cancelled, the value shouldn't be changed.
@ -180,7 +177,7 @@ nsDoTestsForAutoCompleteWithComposition.prototype = {
]
},
"caret": { "start": 1, "length": 0 },
"key": { key: "l", code: "KeyL", keyCode: KeyboardEvent.DOM_VK_L },
"key": { key: "l" },
}, aWindow);
}, popup: false, value: "Mozil", searchString: "Mozi"
},
@ -196,7 +193,7 @@ nsDoTestsForAutoCompleteWithComposition.prototype = {
]
},
"caret": { "start": 2, "length": 0 },
"key": { key: "l", code: "KeyL", keyCode: KeyboardEvent.DOM_VK_L },
"key": { key: "l" },
}, aWindow);
}, popup: false, value: "Mozill", searchString: "Mozi"
},
@ -211,15 +208,15 @@ nsDoTestsForAutoCompleteWithComposition.prototype = {
{ "length": 0, "attr": 0 }
]
},
"caret": { "start": 0, "length": 0 }
"caret": { "start": 0, "length": 0 },
"key": { key: "KEY_Backspace" },
}, aWindow);
}, popup: false, value: "Mozi", searchString: "Mozi"
},
{ description: "cancled compositionend should reopen the popup",
completeDefaultIndex: false,
execute(aWindow) {
synthesizeComposition({ type: "compositioncommit", data: "",
key: { key: "KEY_Escape", code: "Escape" } }, aWindow);
synthesizeComposition({ type: "compositioncommit", data: "", key: { key: "KEY_Escape" } }, aWindow);
}, popup: true, value: "Mozi", searchString: "Mozi"
},
// But if composition replaces some characters and canceled, the search
@ -238,7 +235,7 @@ nsDoTestsForAutoCompleteWithComposition.prototype = {
]
},
"caret": { "start": 1, "length": 0 },
"key": { key: "z", code: "KeyZ", keyCode: KeyboardEvent.DOM_VK_Z },
"key": { key: "z" },
}, aWindow);
}, popup: false, value: "Moz", searchString: "Mozi"
},
@ -254,7 +251,7 @@ nsDoTestsForAutoCompleteWithComposition.prototype = {
]
},
"caret": { "start": 2, "length": 0 },
"key": { key: "i", code: "KeyI", keyCode: KeyboardEvent.DOM_VK_I },
"key": { key: "i" },
}, aWindow);
}, popup: false, value: "Mozi", searchString: "Mozi"
},
@ -269,23 +266,23 @@ nsDoTestsForAutoCompleteWithComposition.prototype = {
{ "length": 0, "attr": 0 }
]
},
"caret": { "start": 0, "length": 0 }
"caret": { "start": 0, "length": 0 },
"key": { key: "KEY_Backspace" },
}, aWindow);
}, popup: false, value: "Mo", searchString: "Mozi"
},
{ description: "canceled compositionend should search the result with the latest value",
completeDefaultIndex: false,
execute(aWindow) {
synthesizeComposition({ type: "compositioncommitasis",
key: { key: "KEY_Escape", code: "Escape" } }, aWindow);
synthesizeComposition({ type: "compositioncommitasis", key: { key: "KEY_Escape" } }, aWindow);
}, popup: true, value: "Mo", searchString: "Mo"
},
// If all characters are removed, the popup should be closed.
{ description: "the value becomes empty by backspace, the popup should be closed",
completeDefaultIndex: false,
execute(aWindow) {
synthesizeKey("VK_BACK_SPACE", {}, aWindow);
synthesizeKey("VK_BACK_SPACE", {}, aWindow);
synthesizeKey("KEY_Backspace", {}, aWindow);
synthesizeKey("KEY_Backspace", {}, aWindow);
}, popup: false, value: "", searchString: ""
},
// composition which is canceled shouldn't cause opening the popup.
@ -301,8 +298,7 @@ nsDoTestsForAutoCompleteWithComposition.prototype = {
]
},
"caret": { "start": 1, "length": 0 },
"key": { key: "m", code: "KeyM", keyCode: KeyboardEvent.DOM_VK_M,
shiftKey: true },
"key": { key: "M" },
}, aWindow);
}, popup: false, value: "M", searchString: ""
},
@ -318,7 +314,7 @@ nsDoTestsForAutoCompleteWithComposition.prototype = {
]
},
"caret": { "start": 2, "length": 0 },
"key": { key: "o", code: "KeyO", keyCode: KeyboardEvent.DOM_VK_O },
"key": { key: "o" },
}, aWindow);
}, popup: false, value: "Mo", searchString: ""
},
@ -333,22 +329,22 @@ nsDoTestsForAutoCompleteWithComposition.prototype = {
{ "length": 0, "attr": 0 }
]
},
"caret": { "start": 0, "length": 0 }
"caret": { "start": 0, "length": 0 },
"key": { key: "KEY_Backspace" },
}, aWindow);
}, popup: false, value: "", searchString: ""
},
{ description: "canceled compositionend shouldn't open the popup if it was closed",
completeDefaultIndex: false,
execute(aWindow) {
synthesizeComposition({ type: "compositioncommitasis",
key: { key: "KEY_Escape", code: "Escape" } }, aWindow);
synthesizeComposition({ type: "compositioncommitasis", key: { key: "KEY_Escape" } }, aWindow);
}, popup: false, value: "", searchString: ""
},
// Down key should open the popup even if the editor is empty.
{ description: "DOWN key should open the popup even if the value is empty",
completeDefaultIndex: false,
execute(aWindow) {
synthesizeKey("VK_DOWN", {}, aWindow);
synthesizeKey("KEY_ArrowDown", {}, aWindow);
}, popup: true, value: "", searchString: ""
},
// If popup is open at starting composition, the popup should be reopened
@ -365,8 +361,7 @@ nsDoTestsForAutoCompleteWithComposition.prototype = {
]
},
"caret": { "start": 1, "length": 0 },
"key": { key: "M", code: "KeyM", keyCode: KeyboardEvent.DOM_VK_M,
shiftKey: true },
"key": { key: "M" },
}, aWindow);
}, popup: false, value: "M", searchString: ""
},
@ -382,7 +377,7 @@ nsDoTestsForAutoCompleteWithComposition.prototype = {
]
},
"caret": { "start": 2, "length": 0 },
"key": { key: "o", code: "KeyO", keyCode: KeyboardEvent.DOM_VK_O },
"key": { key: "o" },
}, aWindow);
}, popup: false, value: "Mo", searchString: ""
},
@ -397,24 +392,24 @@ nsDoTestsForAutoCompleteWithComposition.prototype = {
{ "length": 0, "attr": 0 }
]
},
"caret": { "start": 0, "length": 0 }
"caret": { "start": 0, "length": 0 },
"key": { key: "KEY_Backspace" },
}, aWindow);
}, popup: false, value: "", searchString: ""
},
{ description: "canceled compositionend should open the popup if it was opened",
completeDefaultIndex: false,
execute(aWindow) {
synthesizeComposition({ type: "compositioncommitasis",
key: { key: "KEY_Escape", code: "Escape" } }, aWindow);
synthesizeComposition({ type: "compositioncommitasis", key: { key: "KEY_Escape" } }, aWindow);
}, popup: true, value: "", searchString: ""
},
// Type normally, and hit escape, the popup should be closed.
{ description: "ESCAPE should close the popup after typing something",
completeDefaultIndex: false,
execute(aWindow) {
synthesizeKey("M", { shiftKey: true }, aWindow);
synthesizeKey("o", { shiftKey: true }, aWindow);
synthesizeKey("VK_ESCAPE", {}, aWindow);
synthesizeKey("M", {}, aWindow);
synthesizeKey("o", {}, aWindow);
synthesizeKey("KEY_Escape", {}, aWindow);
}, popup: false, value: "Mo", searchString: "Mo"
},
// Even if the popup is closed, composition which is canceled should open
@ -432,7 +427,7 @@ nsDoTestsForAutoCompleteWithComposition.prototype = {
]
},
"caret": { "start": 1, "length": 0 },
"key": { key: "z", code: "KeyZ", keyCode: KeyboardEvent.DOM_VK_Z },
"key": { key: "z" },
}, aWindow);
}, popup: false, value: "Moz", searchString: "Mo"
},
@ -448,7 +443,7 @@ nsDoTestsForAutoCompleteWithComposition.prototype = {
]
},
"caret": { "start": 2, "length": 0 },
"key": { key: "i", code: "KeyI", keyCode: KeyboardEvent.DOM_VK_I },
"key": { key: "i", },
}, aWindow);
}, popup: false, value: "Mozi", searchString: "Mo"
},
@ -463,23 +458,23 @@ nsDoTestsForAutoCompleteWithComposition.prototype = {
{ "length": 0, "attr": 0 }
]
},
"caret": { "start": 0, "length": 0 }
"caret": { "start": 0, "length": 0 },
"key": { key: "KEY_Backspace" },
}, aWindow);
}, popup: false, value: "Mo", searchString: "Mo"
},
{ description: "canceled compositionend shouldn't open the popup if the popup was closed",
completeDefaultIndex: false,
execute(aWindow) {
synthesizeComposition({ type: "compositioncommitasis",
key: { key: "KEY_Escape", code: "Escape" } }, aWindow);
synthesizeComposition({ type: "compositioncommitasis", key: { key: "KEY_Escape" } }, aWindow);
}, popup: true, value: "Mo", searchString: "Mo"
},
// House keeping...
{ description: "house keeping for next tests",
completeDefaultIndex: false,
execute(aWindow) {
synthesizeKey("VK_BACK_SPACE", {}, aWindow);
synthesizeKey("VK_BACK_SPACE", {}, aWindow);
synthesizeKey("KEY_Backspace", {}, aWindow);
synthesizeKey("KEY_Backspace", {}, aWindow);
}, popup: false, value: "", searchString: ""
},
// Testing for nsIAutoCompleteInput.completeDefaultIndex being true.
@ -495,8 +490,7 @@ nsDoTestsForAutoCompleteWithComposition.prototype = {
]
},
"caret": { "start": 1, "length": 0 },
"key": { key: "M", code: "KeyM", keyCode: KeyboardEvent.DOM_VK_M,
shiftKey: true },
"key": { key: "M" },
}, aWindow);
}, popup: false, value: "M", searchString: ""
},
@ -512,27 +506,26 @@ nsDoTestsForAutoCompleteWithComposition.prototype = {
]
},
"caret": { "start": 2, "length": 0 },
"key": { key: "o", code: "KeyO", keyCode: KeyboardEvent.DOM_VK_O },
"key": { key: "o" },
}, aWindow);
}, popup: false, value: "Mo", searchString: ""
},
{ description: "compositionend should open the popup (completeDefaultIndex is true)",
completeDefaultIndex: true,
execute(aWindow) {
synthesizeComposition({ type: "compositioncommitasis",
key: { key: "KEY_Enter", code: "Enter" } }, aWindow);
synthesizeComposition({ type: "compositioncommitasis", key: { key: "KEY_Enter" } }, aWindow);
}, popup: true, value: "Mozilla", searchString: "Mo"
},
// House keeping...
{ description: "house keeping for next tests",
completeDefaultIndex: false,
execute(aWindow) {
synthesizeKey("VK_BACK_SPACE", {}, aWindow);
synthesizeKey("VK_BACK_SPACE", {}, aWindow);
synthesizeKey("VK_BACK_SPACE", {}, aWindow);
synthesizeKey("VK_BACK_SPACE", {}, aWindow);
synthesizeKey("VK_BACK_SPACE", {}, aWindow);
synthesizeKey("VK_BACK_SPACE", {}, aWindow);
synthesizeKey("KEY_Backspace", {}, aWindow);
synthesizeKey("KEY_Backspace", {}, aWindow);
synthesizeKey("KEY_Backspace", {}, aWindow);
synthesizeKey("KEY_Backspace", {}, aWindow);
synthesizeKey("KEY_Backspace", {}, aWindow);
synthesizeKey("KEY_Backspace", {}, aWindow);
}, popup: false, value: "", searchString: ""
}
]

View File

@ -1438,6 +1438,8 @@ WidgetKeyboardEvent::ComputeKeyCodeFromKeyNameIndex(KeyNameIndex aKeyNameIndex)
return dom::KeyboardEventBinding::DOM_VK_META;
case KEY_NAME_INDEX_AltGraph:
return dom::KeyboardEventBinding::DOM_VK_ALTGR;
case KEY_NAME_INDEX_Process:
return dom::KeyboardEventBinding::DOM_VK_PROCESSKEY;
case KEY_NAME_INDEX_Attn:
return dom::KeyboardEventBinding::DOM_VK_ATTN;
case KEY_NAME_INDEX_CrSel:

View File

@ -243,9 +243,9 @@ const kTests = [
]
},
"caret": { "start": 1, "length": 0 },
"key": { key: "a", code: "KeyA", keyCode: KeyboardEvent.DOM_VK_A },
"key": { key: "a" },
});
synthesizeComposition({ type: "compositioncommitasis" });
synthesizeComposition({ type: "compositioncommitasis", key: {} });
setAndObserveCompositionPref(null, runNextTest);
});
return true;
@ -268,10 +268,11 @@ const kTests = [
{ "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
]
},
"caret": { "start": 1, "length": 0 }
"caret": { "start": 1, "length": 0 },
"key": {},
});
synthesizeComposition({ type: "compositioncommitasis",
key: { key: "KEY_Enter", code: "Enter" } });
key: { key: "KEY_Enter" } });
setAndObserveCompositionPref(null, runNextTest);
});
return true;
@ -333,7 +334,7 @@ const kTests = [
dispatchEvent: function () {
document.getElementById(this.targetID).value = "";
document.getElementById(this.targetID).focus();
synthesizeComposition({ type: "compositioncommit", data: "\u306D" });
synthesizeComposition({ type: "compositioncommit", data: "\u306D", key: { key: "," } });
},
canRun: function () {
return true;
@ -345,7 +346,7 @@ const kTests = [
dispatchEvent: function () {
document.getElementById(this.targetID).value = "";
document.getElementById(this.targetID).focus();
synthesizeComposition({ type: "compositioncommit", data: "\u30E9\u30FC\u30E1\u30F3" });
synthesizeComposition({ type: "compositioncommit", data: "\u30E9\u30FC\u30E1\u30F3", key: { key: "KEY_Enter" } });
},
canRun: function () {
return true;
@ -379,7 +380,8 @@ const kTests = [
{ "length": 4, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
]
},
"caret": { "start": 4, "length": 0 }
"caret": { "start": 4, "length": 0 },
"key": { key: "y" },
});
},
canRun: function () {
@ -390,7 +392,7 @@ const kTests = [
{ description: "InternalEditorInputEvent (input at committing)",
targetID: "input-text", eventType: "input",
dispatchEvent: function () {
synthesizeComposition({ type: "compositioncommitasis" });
synthesizeComposition({ type: "compositioncommitasis", key: { key: "KEY_Enter" } });
},
canRun: function () {
return true;

View File

@ -319,7 +319,8 @@ function runUndoRedoTest()
{ "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
]
},
"caret": { "start": 1, "length": 0 }
"caret": { "start": 1, "length": 0 },
"key": { key: "," },
});
synthesizeCompositionChange(
@ -330,7 +331,8 @@ function runUndoRedoTest()
{ "length": 2, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
]
},
"caret": { "start": 2, "length": 0 }
"caret": { "start": 2, "length": 0 },
"key": { key: "b" },
});
// convert
@ -343,11 +345,12 @@ function runUndoRedoTest()
"attr": COMPOSITION_ATTR_SELECTED_CLAUSE }
]
},
"caret": { "start": 1, "length": 0 }
"caret": { "start": 1, "length": 0 },
"key": { key: " " },
});
// commit
synthesizeComposition({ type: "compositioncommitasis" });
synthesizeComposition({ type: "compositioncommitasis", key: { key: "KEY_Enter" } });
// input raw characters
synthesizeCompositionChange(
@ -358,11 +361,12 @@ function runUndoRedoTest()
{ "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
]
},
"caret": { "start": 1, "length": 0 }
"caret": { "start": 1, "length": 0 },
"key": { key: "j" },
});
// cancel the composition
synthesizeComposition({ type: "compositioncommit", data: "" });
synthesizeComposition({ type: "compositioncommit", data: "", key: { key: "KEY_Escape" } });
// input raw characters
synthesizeCompositionChange(
@ -373,7 +377,8 @@ function runUndoRedoTest()
{ "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
]
},
"caret": { "start": 1, "length": 0 }
"caret": { "start": 1, "length": 0 },
"key": { key: "]" },
});
synthesizeCompositionChange(
@ -384,7 +389,8 @@ function runUndoRedoTest()
{ "length": 2, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
]
},
"caret": { "start": 2, "length": 0 }
"caret": { "start": 2, "length": 0 },
"key": { key: "r" },
});
synthesizeCompositionChange(
@ -395,7 +401,8 @@ function runUndoRedoTest()
{ "length": 3, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
]
},
"caret": { "start": 3, "length": 0 }
"caret": { "start": 3, "length": 0 },
"key": { key: "/" },
});
// convert
@ -408,11 +415,12 @@ function runUndoRedoTest()
"attr": COMPOSITION_ATTR_SELECTED_CLAUSE }
]
},
"caret": { "start": 1, "length": 0 }
"caret": { "start": 1, "length": 0 },
"key": { key: " " },
});
// commit
synthesizeComposition({ type: "compositioncommitasis" });
synthesizeComposition({ type: "compositioncommitasis", key: { key: "KEY_Enter" } });
sendString(" meant");
synthesizeKey("KEY_Backspace");
@ -427,7 +435,8 @@ function runUndoRedoTest()
{ "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
]
},
"caret": { "start": 1, "length": 0 }
"caret": { "start": 1, "length": 0 },
"key": { key: "9" },
});
synthesizeCompositionChange(
@ -438,7 +447,8 @@ function runUndoRedoTest()
{ "length": 2, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
]
},
"caret": { "start": 2, "length": 0 }
"caret": { "start": 2, "length": 0 },
"key": { key: "4" },
});
synthesizeCompositionChange(
@ -449,7 +459,8 @@ function runUndoRedoTest()
{ "length": 3, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
]
},
"caret": { "start": 3, "length": 0 }
"caret": { "start": 3, "length": 0 },
"key": { key: "t" },
});
synthesizeCompositionChange(
@ -460,7 +471,8 @@ function runUndoRedoTest()
{ "length": 4, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
]
},
"caret": { "start": 4, "length": 0 }
"caret": { "start": 4, "length": 0 },
"key": { key: "e" },
});
// convert
@ -472,11 +484,12 @@ function runUndoRedoTest()
{ "length": 2, "attr": COMPOSITION_ATTR_SELECTED_CLAUSE }
]
},
"caret": { "start": 2, "length": 0 }
"caret": { "start": 2, "length": 0 },
"key": { key: " " },
});
// commit
synthesizeComposition({ type: "compositioncommitasis" });
synthesizeComposition({ type: "compositioncommitasis", key: { key: "Enter" } });
synthesizeKey("KEY_Backspace", {repeat: 12});
@ -664,12 +677,13 @@ function runCompositionCommitAsIsTest()
{ "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
]
},
"caret": { "start": 1, "length": 0 }
"caret": { "start": 1, "length": 0 },
"key": { key: "a" },
});
is(textarea.value, "\u3042", "runCompositionCommitAsIsTest: textarea doesn't have composition string #1");
clearResult();
synthesizeComposition({ type: "compositioncommitasis" });
synthesizeComposition({ type: "compositioncommitasis", key: { key: "Enter" } });
is(result.compositionupdate, false, "runCompositionCommitAsIsTest: compositionupdate shouldn't be fired after dispatching compositioncommitasis #1");
is(result.compositionend, true, "runCompositionCommitAsIsTest: compositionend should be fired after dispatching compositioncommitasis #1");
@ -687,7 +701,8 @@ function runCompositionCommitAsIsTest()
{ "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
]
},
"caret": { "start": 1, "length": 0 }
"caret": { "start": 1, "length": 0 },
"key": { key: "a" },
});
is(textarea.value, "\u3042", "runCompositionCommitAsIsTest: textarea doesn't have composition string #2");
synthesizeCompositionChange(
@ -698,12 +713,13 @@ function runCompositionCommitAsIsTest()
{ "length": 0, "attr": 0 }
]
},
"caret": { "start": 1, "length": 0 }
"caret": { "start": 1, "length": 0 },
"key": { key: "KEY_Enter", type: "keydown" },
});
is(textarea.value, "\u3042", "runCompositionCommitAsIsTest: textarea doesn't have committed string #2");
clearResult();
synthesizeComposition({ type: "compositioncommitasis" });
synthesizeComposition({ type: "compositioncommitasis", key: { key: "KEY_Enter", type: "keyup" } });
is(result.compositionupdate, false, "runCompositionCommitAsIsTest: compositionupdate shouldn't be fired after dispatching compositioncommitasis #2");
is(result.compositionend, true, "runCompositionCommitAsIsTest: compositionend should be fired after dispatching compositioncommitasis #2");
@ -721,7 +737,8 @@ function runCompositionCommitAsIsTest()
{ "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
]
},
"caret": { "start": 1, "length": 0 }
"caret": { "start": 1, "length": 0 },
"key": { key: "a" },
});
is(textarea.value, "\u3042", "runCompositionCommitAsIsTest: textarea doesn't have composition string #3");
synthesizeCompositionChange(
@ -732,12 +749,13 @@ function runCompositionCommitAsIsTest()
{ "length": 0, "attr": 0 }
]
},
"caret": { "start": 0, "length": 0 }
"caret": { "start": 0, "length": 0 },
"key": { key: "KEY_Escape", type: "keydown" },
});
is(textarea.value, "", "runCompositionCommitAsIsTest: textarea has non-empty composition string #3");
clearResult();
synthesizeComposition({ type: "compositioncommitasis" });
synthesizeComposition({ type: "compositioncommitasis", key: { key: "KEY_Escape", type: "keyup" } });
is(result.compositionupdate, false, "runCompositionCommitAsIsTest: compositionupdate shouldn't be fired after dispatching compositioncommitasis #3");
is(result.compositionend, true, "runCompositionCommitAsIsTest: compositionend should be fired after dispatching compositioncommitasis #3");
@ -781,12 +799,13 @@ function runCompositionCommitTest()
{ "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
]
},
"caret": { "start": 1, "length": 0 }
"caret": { "start": 1, "length": 0 },
"key": { key: "a", type: "keydown" },
});
is(textarea.value, "\u3042", "runCompositionCommitTest: textarea doesn't have composition string #1");
clearResult();
synthesizeComposition({ type: "compositioncommit", data: "\u3043" });
synthesizeComposition({ type: "compositioncommit", data: "\u3043", key: { key: "a", type: "keyup" } });
is(result.compositionupdate, true, "runCompositionCommitTest: compositionupdate should be fired after dispatching compositioncommit #1");
is(result.compositionend, true, "runCompositionCommitTest: compositionend should be fired after dispatching compositioncommit #1");
@ -804,7 +823,8 @@ function runCompositionCommitTest()
{ "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
]
},
"caret": { "start": 1, "length": 0 }
"caret": { "start": 1, "length": 0 },
"key": { key: "a" },
});
is(textarea.value, "\u3042", "runCompositionCommitTest: textarea doesn't have composition string #2");
synthesizeCompositionChange(
@ -815,12 +835,13 @@ function runCompositionCommitTest()
{ "length": 0, "attr": 0 }
]
},
"caret": { "start": 1, "length": 0 }
"caret": { "start": 1, "length": 0 },
"key": { key: "KEY_Enter", type: "keydown" },
});
is(textarea.value, "\u3042", "runCompositionCommitTest: textarea doesn't have committed string #2");
clearResult();
synthesizeComposition({ type: "compositioncommit", data: "\u3043" });
synthesizeComposition({ type: "compositioncommit", data: "\u3043", key: { key: "KEY_Enter", type: "keyup" } });
is(result.compositionupdate, true, "runCompositionCommitTest: compositionupdate should be fired after dispatching compositioncommit #2");
is(result.compositionend, true, "runCompositionCommitTest: compositionend should be fired after dispatching compositioncommit #2");
@ -838,7 +859,8 @@ function runCompositionCommitTest()
{ "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
]
},
"caret": { "start": 1, "length": 0 }
"caret": { "start": 1, "length": 0 },
"key": { key: "a" },
});
is(textarea.value, "\u3042", "runCompositionCommitTest: textarea doesn't have composition string #3");
synthesizeCompositionChange(
@ -849,12 +871,13 @@ function runCompositionCommitTest()
{ "length": 0, "attr": 0 }
]
},
"caret": { "start": 0, "length": 0 }
"caret": { "start": 0, "length": 0 },
"key": { key: "KEY_Enter", type: "keydown" },
});
is(textarea.value, "", "runCompositionCommitTest: textarea has non-empty composition string #3");
clearResult();
synthesizeComposition({ type: "compositioncommit", data: "\u3043" });
synthesizeComposition({ type: "compositioncommit", data: "\u3043", key: { key: "KEY_Enter", type: "keyup" } });
is(result.compositionupdate, true, "runCompositionCommitTest: compositionupdate should be fired after dispatching compositioncommit #3");
is(result.compositionend, true, "runCompositionCommitTest: compositionend should be fired after dispatching compositioncommit #3");
@ -872,12 +895,13 @@ function runCompositionCommitTest()
{ "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
]
},
"caret": { "start": 1, "length": 0 }
"caret": { "start": 1, "length": 0 },
"key": { key: "a" },
});
is(textarea.value, "\u3042", "runCompositionCommitTest: textarea doesn't have composition string #4");
clearResult();
synthesizeComposition({ type: "compositioncommit", data: "" });
synthesizeComposition({ type: "compositioncommit", data: "", key: { key: "KEY_Enter" } });
is(result.compositionupdate, true, "runCompositionCommitTest: compositionupdate should be fired after dispatching compositioncommit #4");
is(result.compositionend, true, "runCompositionCommitTest: compositionend should be fired after dispatching compositioncommit #4");
@ -889,7 +913,7 @@ function runCompositionCommitTest()
textarea.value = "";
clearResult();
synthesizeComposition({ type: "compositioncommit", data: "\u3042" });
synthesizeComposition({ type: "compositioncommit", data: "\u3042", key: { key: "a" } });
is(result.compositionupdate, true, "runCompositionCommitTest: compositionupdate should be fired after dispatching compositioncommit #5");
is(result.compositionend, true, "runCompositionCommitTest: compositionend should be fired after dispatching compositioncommit #5");
@ -907,12 +931,13 @@ function runCompositionCommitTest()
{ "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
]
},
"caret": { "start": 1, "length": 0 }
"caret": { "start": 1, "length": 0 },
"key": { key: "a" },
});
is(textarea.value, "\u3042", "runCompositionCommitTest: textarea doesn't have composition string #5");
clearResult();
synthesizeComposition({ type: "compositioncommit", data: "\u3042" });
synthesizeComposition({ type: "compositioncommit", data: "\u3042", key: { key: "KEY_Enter" } });
is(result.compositionupdate, false, "runCompositionCommitTest: compositionupdate shouldn't be fired after dispatching compositioncommit #5");
is(result.compositionend, true, "runCompositionCommitTest: compositionend should be fired after dispatching compositioncommit #5");
@ -930,7 +955,8 @@ function runCompositionCommitTest()
{ "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
]
},
"caret": { "start": 1, "length": 0 }
"caret": { "start": 1, "length": 0 },
"key": { key: "a" },
});
is(textarea.value, "\u3042", "runCompositionCommitTest: textarea doesn't have composition string #6");
@ -942,12 +968,13 @@ function runCompositionCommitTest()
{ "length": 0, "attr": 0 }
]
},
"caret": { "start": 1, "length": 0 }
"caret": { "start": 1, "length": 0 },
"key": { key: "KEY_Enter", type: "keydown" },
});
is(textarea.value, "\u3042", "runCompositionCommitTest: textarea doesn't have composition string #6");
clearResult();
synthesizeComposition({ type: "compositioncommit", data: "\u3042" });
synthesizeComposition({ type: "compositioncommit", data: "\u3042", key: { key: "KEY_Enter", type: "keyup" } });
is(result.compositionupdate, false, "runCompositionCommitTest: compositionupdate shouldn't be fired after dispatching compositioncommit #6");
is(result.compositionend, true, "runCompositionCommitTest: compositionend should be fired after dispatching compositioncommit #6");
@ -983,7 +1010,8 @@ function runCompositionTest()
{ "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
]
},
"caret": { "start": 1, "length": 0 }
"caret": { "start": 1, "length": 0 },
"key": { key: "o" },
});
if (!checkContent("\u3089", "runCompositionTest", "#1-1") ||
@ -1007,7 +1035,8 @@ function runCompositionTest()
{ "length": 2, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
]
},
"caret": { "start": 2, "length": 0 }
"caret": { "start": 2, "length": 0 },
"key": { key: "\\", code: "IntlYen", keyCode: KeyboardEvent.DOM_VK_BACKSLASH },
});
if (!checkContent("\u3089\u30FC", "runCompositionTest", "#1-2") ||
@ -1040,7 +1069,8 @@ function runCompositionTest()
{ "length": 3, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
]
},
"caret": { "start": 3, "length": 0 }
"caret": { "start": 3, "length": 0 },
"key": { key: "/" },
});
if (!checkContent("\u3089\u30FC\u3081", "runCompositionTest", "#1-3") ||
@ -1073,7 +1103,8 @@ function runCompositionTest()
{ "length": 3, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
]
},
"caret": { "start": 2, "length": 0 }
"caret": { "start": 2, "length": 0 },
"key": { key: "KEY_ArrowLeft" },
});
if (!checkContent("\u3089\u30FC\u3081", "runCompositionTest", "#1-3-1") ||
@ -1107,7 +1138,8 @@ function runCompositionTest()
{ "length": 3, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
]
},
"caret": { "start": 1, "length": 0 }
"caret": { "start": 1, "length": 0 },
"key": { key: "KEY_ArrowLeft" },
});
if (!checkContent("\u3089\u30FC\u3081", "runCompositionTest", "#1-3-2") ||
@ -1140,7 +1172,8 @@ function runCompositionTest()
{ "length": 4, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
]
},
"caret": { "start": 4, "length": 0 }
"caret": { "start": 4, "length": 0 },
"key": { key: "y" },
});
if (!checkContent("\u3089\u30FC\u3081\u3093", "runCompositionTest", "#1-4") ||
@ -1158,7 +1191,8 @@ function runCompositionTest()
{ "length": 3, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
]
},
"caret": { "start": 3, "length": 0 }
"caret": { "start": 3, "length": 0 },
"key": { key: "KEY_Backspace" },
});
if (!checkContent("\u3089\u30FC\u3081", "runCompositionTest", "#1-5") ||
@ -1175,7 +1209,8 @@ function runCompositionTest()
{ "length": 4, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
]
},
"caret": { "start": 4, "length": 0 }
"caret": { "start": 4, "length": 0 },
"key": { key: "y" },
});
if (!checkContent("\u3089\u30FC\u3081\u3093", "runCompositionTest", "#1-6") ||
@ -1191,7 +1226,8 @@ function runCompositionTest()
{ "length": 5, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
]
},
"caret": { "start": 5, "length": 0 }
"caret": { "start": 5, "length": 0 },
"key": { key: "x" },
});
if (!checkContent("\u3089\u30FC\u3081\u3093\u3055", "runCompositionTest", "#1-7") ||
@ -1207,7 +1243,8 @@ function runCompositionTest()
{ "length": 6, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
]
},
"caret": { "start": 6, "length": 0 }
"caret": { "start": 6, "length": 0 },
"key": { key: "e" },
});
if (!checkContent("\u3089\u30FC\u3081\u3093\u3055\u3044", "runCompositionTest", "#1-8") ||
@ -1223,7 +1260,8 @@ function runCompositionTest()
{ "length": 7, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
]
},
"caret": { "start": 7, "length": 0 }
"caret": { "start": 7, "length": 0 },
"key": { key: "b" },
});
if (!checkContent("\u3089\u30FC\u3081\u3093\u3055\u3044\u3053", "runCompositionTest", "#1-8") ||
@ -1239,7 +1277,8 @@ function runCompositionTest()
{ "length": 8, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
]
},
"caret": { "start": 8, "length": 0 }
"caret": { "start": 8, "length": 0 },
"key": { key: "4" },
});
if (!checkContent("\u3089\u30FC\u3081\u3093\u3055\u3044\u3053\u3046",
@ -1260,7 +1299,8 @@ function runCompositionTest()
"attr": COMPOSITION_ATTR_CONVERTED_CLAUSE }
]
},
"caret": { "start": 4, "length": 0 }
"caret": { "start": 4, "length": 0 },
"key": { key: " " },
});
if (!checkContent("\u30E9\u30FC\u30E1\u30F3\u6700\u9AD8",
@ -1281,7 +1321,8 @@ function runCompositionTest()
"attr": COMPOSITION_ATTR_SELECTED_CLAUSE }
]
},
"caret": { "start": 6, "length": 0 }
"caret": { "start": 6, "length": 0 },
"key": { key: "KEY_ArrowLeft", shiftKey: true },
});
if (!checkContent("\u30E9\u30FC\u30E1\u30F3\u6700\u9AD8",
@ -1302,7 +1343,8 @@ function runCompositionTest()
"attr": COMPOSITION_ATTR_CONVERTED_CLAUSE }
]
},
"caret": { "start": 5, "length": 0 }
"caret": { "start": 5, "length": 0 },
"key": { key: "KEY_ArrowRight" },
});
if (!checkContent("\u30E9\u30FC\u30E1\u30F3\u3055\u884C\u3053\u3046",
@ -1352,7 +1394,8 @@ function runCompositionTest()
{ "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
]
},
"caret": { "start": 1, "length": 0 }
"caret": { "start": 1, "length": 0 },
"key": { key: "d" },
});
if (!checkContent("\u30E9\u30FC\u30E1\u30F3\u3055\u884C\u3053\u3046\u3057",
@ -1369,7 +1412,8 @@ function runCompositionTest()
{ "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
]
},
"caret": { "start": 1, "length": 0 }
"caret": { "start": 1, "length": 0 },
"key": { key: "r" },
});
if (!checkContent("\u30E9\u30FC\u30E1\u30F3\u3055\u884C\u3053\u3046\u3058",
@ -1386,7 +1430,8 @@ function runCompositionTest()
{ "length": 2, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
]
},
"caret": { "start": 2, "length": 0 }
"caret": { "start": 2, "length": 0 },
"key": { key: ")", code: "Digit9", keyCode: KeyboardEvent.DOM_VK_9, shiftKey: true },
});
if (!checkContent("\u30E9\u30FC\u30E1\u30F3\u3055\u884C\u3053\u3046\u3058\u3087",
@ -1403,7 +1448,8 @@ function runCompositionTest()
{ "length": 3, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
]
},
"caret": { "start": 3, "length": 0 }
"caret": { "start": 3, "length": 0 },
"key": { key: "4" },
});
if (!checkContent("\u30E9\u30FC\u30E1\u30F3\u3055\u884C\u3053\u3046\u3058\u3087\u3046",
@ -1413,7 +1459,7 @@ function runCompositionTest()
}
// commit the composition string
synthesizeComposition({ type: "compositioncommitasis" });
synthesizeComposition({ type: "compositioncommitasis", key: { key: "KEY_Enter" } });
if (!checkContent("\u30E9\u30FC\u30E1\u30F3\u3055\u884C\u3053\u3046\u3058\u3087\u3046",
"runCompositionTest", "#2-4") ||
@ -1438,7 +1484,8 @@ function runCompositionTest()
{ "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
]
},
"caret": { "start": 1, "length": 0 }
"caret": { "start": 1, "length": 0 },
"key": { key: "6" },
});
if (!checkContent("\u30E9\u30FC\u30E1\u30F3\u304A",
@ -1456,7 +1503,8 @@ function runCompositionTest()
{ "length": 0, "attr": 0 }
]
},
"caret": { "start": 0, "length": 0 }
"caret": { "start": 0, "length": 0 },
"key": { key: "KEY_Backspace" },
});
if (!checkContent("\u30E9\u30FC\u30E1\u30F3",
@ -1474,7 +1522,8 @@ function runCompositionTest()
{ "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
]
},
"caret": { "start": 1, "length": 0 }
"caret": { "start": 1, "length": 0 },
"key": { key: "4" },
});
if (!checkContent("\u30E9\u30FC\u30E1\u30F3\u3046",
@ -1484,7 +1533,7 @@ function runCompositionTest()
}
// cancel the composition
synthesizeComposition({ type: "compositioncommit", data: "" });
synthesizeComposition({ type: "compositioncommit", data: "", key: { key: "KEY_Escape" } });
if (!checkContent("\u30E9\u30FC\u30E1\u30F3",
"runCompositionTest", "#3-5") ||
@ -1503,7 +1552,8 @@ function runCompositionTest()
{ "length": 0, "attr": 0 }
]
},
"caret": { "start": 0, "length": 0 }
"caret": { "start": 0, "length": 0 },
"key": { key: "a" },
});
if (!checkContent("\u30E9\u30FC\u30E1\u30F3",
@ -1520,7 +1570,8 @@ function runCompositionTest()
{ "length": 0, "attr": 0 }
]
},
"caret": { "start": 0, "length": 0 }
"caret": { "start": 0, "length": 0 },
"key": { key: "b" },
});
if (!checkContent("\u30E9\u30FC\u30E1\u30F3",
@ -1529,7 +1580,7 @@ function runCompositionTest()
return;
}
synthesizeComposition({ type: "compositioncommit", data: "\u6700" });
synthesizeComposition({ type: "compositioncommit", data: "\u6700", key: { key: "KEY_Enter" } });
if (!checkContent("\u30E9\u30FC\u30E1\u30F3\u6700",
"runCompositionTest", "#4-3") ||
!checkSelection(5, "", "runCompositionTest", "#4-3")) {
@ -1545,7 +1596,8 @@ function runCompositionTest()
{ "length": 0, "attr": 0 }
]
},
"caret": { "start": 0, "length": 0 }
"caret": { "start": 0, "length": 0 },
"key": { key: "a" },
});
if (!checkContent("\u30E9\u30FC\u30E1\u30F3\u6700",
@ -1554,7 +1606,7 @@ function runCompositionTest()
return;
}
synthesizeComposition({ type: "compositioncommitasis" });
synthesizeComposition({ type: "compositioncommitasis", key: { key: "KEY_Escape" } });
if (!checkContent("\u30E9\u30FC\u30E1\u30F3\u6700",
"runCompositionTest", "#4-6") ||
@ -1573,7 +1625,8 @@ function runCompositionTest()
{ "length": 0, "attr": 0 }
]
},
"caret": { "start": 0, "length": 0 }
"caret": { "start": 0, "length": 0 },
"key": { key: "a" },
});
if (!checkContent("\u30E9\u30FC\u30E1\u30F3",
@ -1582,7 +1635,7 @@ function runCompositionTest()
return;
}
synthesizeComposition({ type: "compositioncommit", data: "\u9AD8" });
synthesizeComposition({ type: "compositioncommit", data: "\u9AD8", key: { key: "KEY_Enter" } });
if (!checkContent("\u30E9\u30FC\u30E1\u30F3\u9AD8",
"runCompositionTest", "#4-9") ||
!checkSelection(5, "", "runCompositionTest", "#4-9")) {
@ -1606,7 +1659,8 @@ function runCompositionTest()
{ "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
]
},
"caret": { "start": 1, "length": 0 }
"caret": { "start": 1, "length": 0 },
"key": { key: "a" },
});
if (!checkContent("\u30E9\u30FC\u30E1\u30F3\u6700",
@ -1623,7 +1677,8 @@ function runCompositionTest()
{ "length": 0, "attr": 0 }
]
},
"caret": { "start": 0, "length": 0 }
"caret": { "start": 0, "length": 0 },
"key": { key: "KEY_Backspace", type: "keydown" },
});
if (!checkContent("\u30E9\u30FC\u30E1\u30F3",
@ -1632,7 +1687,7 @@ function runCompositionTest()
return;
}
synthesizeComposition({ type: "compositioncommitasis" });
synthesizeComposition({ type: "compositioncommitasis", key: { key: "KEY_Backspace", type: "keyup" } });
if (!checkContent("\u30E9\u30FC\u30E1\u30F3",
"runCompositionTest", "#5-3") ||
!checkSelection(4, "", "runCompositionTest", "#5-3")) {
@ -1640,7 +1695,7 @@ function runCompositionTest()
}
// Undo tests for the testcases for bug 23558 and bug 271815
synthesizeKey("Z", { accelKey: true });
synthesizeKey("z", { accelKey: true });
// XXX this is unexpected behavior, see bug 258291
if (!checkContent("\u30E9\u30FC\u30E1\u30F3",
@ -1649,7 +1704,7 @@ function runCompositionTest()
return;
}
synthesizeKey("Z", { accelKey: true });
synthesizeKey("z", { accelKey: true });
if (!checkContent("\u30E9\u30FC\u30E1\u30F3\u9AD8",
"runCompositionTest", "#6-2") ||
@ -1657,7 +1712,7 @@ function runCompositionTest()
return;
}
synthesizeKey("Z", { accelKey: true });
synthesizeKey("z", { accelKey: true });
if (!checkContent("\u30E9\u30FC\u30E1\u30F3\u6700",
"runCompositionTest", "#6-3") ||
@ -1665,7 +1720,7 @@ function runCompositionTest()
return;
}
synthesizeKey("Z", { accelKey: true });
synthesizeKey("z", { accelKey: true });
// XXX this is unexpected behavior, see bug 258291
if (!checkContent("\u30E9\u30FC\u30E1\u30F3\u6700",
@ -1674,7 +1729,7 @@ function runCompositionTest()
return;
}
synthesizeKey("Z", { accelKey: true });
synthesizeKey("z", { accelKey: true });
if (!checkContent("\u30E9\u30FC\u30E1\u30F3",
"runCompositionTest", "#6-5") ||
@ -1777,7 +1832,8 @@ function runCompositionEventTest()
{ "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
]
},
"caret": { "start": 1, "length": 0 }
"caret": { "start": 1, "length": 0 },
"key": { key: "o" },
});
is(windowEventCounts["compositionstart"], 1,
@ -1828,7 +1884,8 @@ function runCompositionEventTest()
{ "length": 2, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
]
},
"caret": { "start": 2, "length": 0 }
"caret": { "start": 2, "length": 0 },
"key": { key: "\\", code: "IntlYen", keyCode: KeyboardEvent.DOM_VK_BACKSLASH },
});
is(windowEventCounts["compositionstart"], 1,
@ -1864,7 +1921,7 @@ function runCompositionEventTest()
kDescription + "value of input element wasn't modified (input) #2");
// text event shouldn't cause composition update, e.g., at committing.
synthesizeComposition({ type: "compositioncommitasis" });
synthesizeComposition({ type: "compositioncommitasis", key: { key: "KEY_Enter" } });
is(windowEventCounts["compositionstart"], 1,
kDescription + "compositionstart has been handled more than once by window #3");
@ -1911,10 +1968,11 @@ function runCompositionEventTest()
{ "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
]
},
"caret": { "start": 1, "length": 0 }
"caret": { "start": 1, "length": 0 },
"key": { key: "o" },
});
synthesizeComposition({ type: "compositioncommitasis" });
synthesizeComposition({ type: "compositioncommitasis", key: { key: "KEY_Enter" } });
is(windowEventCounts["compositionstart"], 1,
kDescription + "compositionstart hasn't been handled by window #4");
@ -1968,7 +2026,7 @@ function runCompositionEventTest()
preventDefault = true;
initResults();
synthesizeKey("A", { accelKey: true }); // Select All
synthesizeKey("a", { accelKey: true }); // Select All
synthesizeCompositionChange(
{ "composition":
@ -1978,7 +2036,8 @@ function runCompositionEventTest()
{ "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
]
},
"caret": { "start": 1, "length": 0 }
"caret": { "start": 1, "length": 0 },
"key": { key: "," },
});
synthesizeComposition({ type: "compositioncommitasis" });
@ -2037,7 +2096,7 @@ function runCompositionEventTest()
stopPropagation = true;
initResults();
synthesizeKey("A", { accelKey: true }); // Select All
synthesizeKey("a", { accelKey: true }); // Select All
synthesizeCompositionChange(
{ "composition":
@ -2047,10 +2106,11 @@ function runCompositionEventTest()
{ "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
]
},
"caret": { "start": 1, "length": 0 }
"caret": { "start": 1, "length": 0 },
"key": { key: "\\", code: "IntlRo", keyCode: KeyboardEvent.DOM_VK_BACKSLASH },
});
synthesizeComposition({ type: "compositioncommitasis" });
synthesizeComposition({ type: "compositioncommitasis", key: { key: "KEY_Enter" } });
is(windowEventCounts["compositionstart"], 1,
kDescription + "compositionstart hasn't been handled by window #6");
@ -2094,7 +2154,7 @@ function runCompositionEventTest()
initResults();
input.value = "value of input";
synthesizeKey("A", { accelKey: true }); // Select All
synthesizeKey("a", { accelKey: true }); // Select All
var compositionstart = document.createEvent("CompositionEvent");
compositionstart.initCompositionEvent("compositionstart",