diff options
Diffstat (limited to 'dom/inputmethod/mochitest/test_bug1137557.html')
-rw-r--r-- | dom/inputmethod/mochitest/test_bug1137557.html | 1799 |
1 files changed, 1799 insertions, 0 deletions
diff --git a/dom/inputmethod/mochitest/test_bug1137557.html b/dom/inputmethod/mochitest/test_bug1137557.html new file mode 100644 index 000000000..1f5053662 --- /dev/null +++ b/dom/inputmethod/mochitest/test_bug1137557.html @@ -0,0 +1,1799 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=1137557 +--> +<head> + <title>Test for new API arguments accepting D3E properties</title> + <script type="application/javascript;version=1.7" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript;version=1.7" src="inputmethod_common.js"></script> + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1137557">Mozilla Bug 1137557</a> +<p id="display"></p> +<pre id="test"> +<script class="testbody" type="application/javascript;version=1.7"> + +inputmethod_setup(function() { + runTest(); +}); + +let gEventDetails = []; +let gCurrentValue = ''; +let gTestDescription = ''; + +let appFrameScript = function appFrameScript() { + let input = content.document.body.firstElementChild; + + input.focus(); + + function sendEventDetail(evt) { + var eventDetail; + + switch (evt.type) { + case 'compositionstart': + case 'compositionupdate': + case 'compositionend': + eventDetail = { + type: evt.type, + value: input.value, + data: evt.data + }; + break; + + case 'input': + eventDetail = { + type: evt.type, + value: input.value + }; + break; + + default: // keyboard events + eventDetail = { + type: evt.type, + charCode: evt.charCode, + keyCode: evt.keyCode, + key: evt.key, + code: evt.code, + location: evt.location, + repeat: evt.repeat, + value: input.value, + shift: evt.getModifierState('Shift'), + capsLock: evt.getModifierState('CapsLock'), + control: evt.getModifierState('Control'), + alt: evt.getModifierState('Alt') + }; + break; + } + + sendAsyncMessage('test:eventDetail', eventDetail); + } + + input.addEventListener('compositionstart', sendEventDetail); + input.addEventListener('compositionupdate', sendEventDetail); + input.addEventListener('compositionend', sendEventDetail); + input.addEventListener('input', sendEventDetail); + input.addEventListener('keydown', sendEventDetail); + input.addEventListener('keypress', sendEventDetail); + input.addEventListener('keyup', sendEventDetail); +}; + +function waitForInputContextChange() { + return new Promise((resolve) => { + navigator.mozInputMethod.oninputcontextchange = resolve; + }); +} + +function assertEventDetail(expectedDetails, testName) { + is(gEventDetails.length, expectedDetails.length, + testName + ' expects ' + expectedDetails.map(d => d.type).join(', ') + ' events, got ' + gEventDetails.map(d => d.type).join(', ')); + + expectedDetails.forEach((expectedDetail, j) => { + for (let key in expectedDetail) { + is(gEventDetails[j][key], expectedDetail[key], + testName + ' expects ' + key + ' of ' + gEventDetails[j].type + ' to be equal to ' + expectedDetail[key]); + } + }); +} + +function sendKeyAndAssertResult(testdata) { + var dict = testdata.dict; + var testName = gTestDescription + 'sendKey(' + JSON.stringify(dict) + ')'; + var promise = navigator.mozInputMethod.inputcontext.sendKey(dict); + + if (testdata.expectedReject) { + promise = promise + .then(() => { + ok(false, testName + ' should not resolve.'); + }, (e) => { + ok(true, testName + ' rejects.'); + ok(e instanceof testdata.expectedReject, 'Reject with type.'); + }) + + return promise; + } + + promise = promise + .then((res) => { + is(res, true, + testName + ' should resolve to true.'); + + var expectedEventDetail = []; + + var expectedValues = testdata.expectedValues; + + expectedEventDetail.push({ + type: 'keydown', + key: expectedValues.key, + charCode: 0, + code: expectedValues.code || '', + keyCode: expectedValues.keyCode || 0, + location: expectedValues.location ? expectedValues.location : 0, + repeat: expectedValues.repeat || false, + value: gCurrentValue, + shift: false, + capsLock: false, + control: false, + alt: false + }); + + if (testdata.expectedKeypress) { + expectedEventDetail.push({ + type: 'keypress', + key: expectedValues.key, + charCode: expectedValues.charCode, + code: expectedValues.code || '', + keyCode: expectedValues.charCode ? 0 : expectedValues.keyCode, + location: expectedValues.location ? expectedValues.location : 0, + repeat: expectedValues.repeat || false, + value: gCurrentValue, + shift: false, + capsLock: false, + control: false, + alt: false + }); + } + + if (testdata.expectedInput) { + switch (testdata.expectedInput) { + case 'Enter': + gCurrentValue += '\n'; + break; + case 'Backspace': + gCurrentValue = + gCurrentValue.substr(0, gCurrentValue.length - 1); + break; + default: + gCurrentValue += testdata.expectedInput; + break; + } + + expectedEventDetail.push({ + type: 'input', + value: gCurrentValue + }); + } + + if (!testdata.expectedRepeat) { + expectedEventDetail.push({ + type: 'keyup', + key: expectedValues.key, + charCode: 0, + code: expectedValues.code || '', + keyCode: expectedValues.keyCode || 0, + location: expectedValues.location ? expectedValues.location : 0, + repeat: expectedValues.repeat || false, + value: gCurrentValue, + shift: false, + capsLock: false, + control: false, + alt: false + }); + } + + assertEventDetail(expectedEventDetail, testName); + gEventDetails = []; + }, (e) => { + ok(false, testName + ' should not reject. ' + e); + }); + + return promise; +} + +function runSendKeyAlphabetTests() { + gTestDescription = 'runSendKeyAlphabetTests(): '; + var promiseQueue = Promise.resolve(); + + // Test the plain alphabets + var codeA = 'A'.charCodeAt(0); + for (var i = 0; i < 26; i++) { + // callbacks in then() are deferred; must only reference these block-scoped + // variable instead of i. + let keyCode = codeA + i; + let code = 'Key' + String.fromCharCode(keyCode); + + [String.fromCharCode(keyCode), + String.fromCharCode(keyCode).toLowerCase()] + .forEach((chr) => { + // Test plain alphabet + promiseQueue = promiseQueue.then(() => { + return sendKeyAndAssertResult({ + dict: { + key: chr + }, + expectedKeypress: true, + expectedInput: chr, + expectedValues: { + key: chr, code: '', + keyCode: keyCode, + charCode: chr.charCodeAt(0) + } + }); + }); + + // Test plain alphabet with keyCode set + promiseQueue = promiseQueue.then(() => { + return sendKeyAndAssertResult({ + dict: { + key: chr, + keyCode: keyCode + }, + expectedKeypress: true, + expectedInput: chr, + expectedValues: { + key: chr, code: '', + keyCode: keyCode, + charCode: chr.charCodeAt(0) + } + }); + }); + + // Test plain alphabet with keyCode set to keyCode + 1, + // expects keyCode to follow key value and ignore the incorrect value. + promiseQueue = promiseQueue.then(() => { + return sendKeyAndAssertResult({ + dict: { + key: chr, + keyCode: keyCode + 1 + }, + expectedKeypress: true, + expectedInput: chr, + expectedValues: { + key: chr, code: '', + keyCode: keyCode, + charCode: chr.charCodeAt(0) + } + }); + }); + + // Test plain alphabet with code set + promiseQueue = promiseQueue.then(() => { + return sendKeyAndAssertResult({ + dict: { + key: chr, + code: code + }, + expectedKeypress: true, + expectedInput: chr, + expectedValues: { + key: chr, code: code, + keyCode: keyCode, + charCode: chr.charCodeAt(0) + } + }); + }); + + // Test plain alphabet with code set to Digit1, + // expects keyCode to follow key value. + promiseQueue = promiseQueue.then(() => { + return sendKeyAndAssertResult({ + dict: { + key: chr, + code: 'Digit1' + }, + expectedKeypress: true, + expectedInput: chr, + expectedValues: { + key: chr, code: 'Digit1', + keyCode: keyCode, + charCode: chr.charCodeAt(0) + } + }); + }); + + // Test plain alphabet with keyCode set to DOM_VK_1, + // expects keyCode to follow key value. + promiseQueue = promiseQueue.then(() => { + return sendKeyAndAssertResult({ + dict: { + key: chr, + keyCode: KeyboardEvent.DOM_VK_1 + }, + expectedKeypress: true, + expectedInput: chr, + expectedValues: { + key: chr, code: '', + keyCode: keyCode, + charCode: chr.charCodeAt(0) + } + }); + }); + + // Test plain alphabet with code set to Digit1 + // and keyCode set to DOM_VK_1, + // expects keyCode to follow key value. + promiseQueue = promiseQueue.then(() => { + return sendKeyAndAssertResult({ + dict: { + key: chr, + code: 'Digit1', + keyCode: KeyboardEvent.DOM_VK_1 + }, + expectedKeypress: true, + expectedInput: chr, + expectedValues: { + key: chr, code: 'Digit1', + keyCode: keyCode, + charCode: chr.charCodeAt(0) + } + }); + }); + }); + } + + return promiseQueue; +} + +function runSendKeyNumberTests() { + gTestDescription = 'runSendKeyNumberTests(): '; + var promiseQueue = Promise.resolve(); + + // Test numbers + var code0 = '0'.charCodeAt(0); + for (var i = 0; i < 10; i++) { + // callbacks in then() are deferred; must only reference these block-scoped + // variable instead of i. + let keyCode = code0 + i; + let chr = String.fromCharCode(keyCode); + + // Test plain number + promiseQueue = promiseQueue.then(() => { + return sendKeyAndAssertResult({ + dict: { + key: chr + }, + expectedKeypress: true, + expectedInput: chr, + expectedValues: { + key: chr, code: '', + keyCode: keyCode, + charCode: chr.charCodeAt(0) + } + }); + }); + + // Test plain number with keyCode set + promiseQueue = promiseQueue.then(() => { + return sendKeyAndAssertResult({ + dict: { + key: chr, + keyCode: keyCode + }, + expectedKeypress: true, + expectedInput: chr, + expectedValues: { + key: chr, code: '', + keyCode: keyCode, + charCode: chr.charCodeAt(0) + } + }); + }); + + // Test plain number with keyCode set to keyCode + 1, + // expects keyCode to follow key value and ignore the incorrect value. + promiseQueue = promiseQueue.then(() => { + return sendKeyAndAssertResult({ + dict: { + key: chr, + keyCode: keyCode + 1 + }, + expectedKeypress: true, + expectedInput: chr, + expectedValues: { + key: chr, code: '', + keyCode: keyCode, + charCode: chr.charCodeAt(0) + } + }); + }); + + // Test plain number with code set + promiseQueue = promiseQueue.then(() => { + return sendKeyAndAssertResult({ + dict: { + key: chr, + code: 'Digit' + chr + }, + expectedKeypress: true, + expectedInput: chr, + expectedValues: { + key: chr, code: 'Digit' + chr, + keyCode: keyCode, + charCode: chr.charCodeAt(0) + } + }); + }); + + // Test plain upper caps alphabet with code set to KeyA, + // expects keyCode to follow key value. + promiseQueue = promiseQueue.then(() => { + return sendKeyAndAssertResult({ + dict: { + key: chr, + code: 'KeyA' + }, + expectedKeypress: true, + expectedInput: chr, + expectedValues: { + key: chr, code: 'KeyA', + keyCode: keyCode, + charCode: chr.charCodeAt(0) + } + }); + }); + + // Test plain upper caps alphabet with code set to KeyA, + // and keyCode set to DOM_VK_A. + // expects keyCode to follow key value. + promiseQueue = promiseQueue.then(() => { + return sendKeyAndAssertResult({ + dict: { + key: chr, + code: 'KeyA', + keyCode: KeyboardEvent.DOM_VK_A + }, + expectedKeypress: true, + expectedInput: chr, + expectedValues: { + key: chr, code: 'KeyA', + keyCode: keyCode, + charCode: chr.charCodeAt(0) + } + }); + }); + } + + return promiseQueue; +} + +function runSendKeyDvorakTests() { + gTestDescription = 'runSendKeyDvorakTests(): '; + var promiseQueue = Promise.resolve(); + + // Test Dvorak layout emulation + var qwertyCodeForDvorakKeys = [ + 'KeyR', 'KeyT', 'KeyY', 'KeyU', 'KeyI', 'KeyO', 'KeyP', + 'KeyA', 'KeyS', 'KeyD', 'KeyF', 'KeyG', + 'KeyH', 'KeyJ', 'KeyK', 'KeyL', 'Semicolon', + 'KeyX', 'KeyC', 'KeyV', 'KeyB', 'KeyN', + 'KeyM', 'Comma', 'Period', 'Slash']; + var dvorakKeys = 'PYFGCRL' + + 'AOEUIDHTNS' + + 'QJKXBMWVZ'; + for (var i = 0; i < dvorakKeys.length; i++) { + // callbacks in then() are deferred; must only reference these block-scoped + // variable instead of i. + let keyCode = dvorakKeys.charCodeAt(i); + let code = qwertyCodeForDvorakKeys[i]; + + [dvorakKeys.charAt(i), dvorakKeys.charAt(i).toLowerCase()] + .forEach((chr) => { + // Test alphabet with code set to Qwerty code, + // expects keyCode to follow key value. + // (This is *NOT* the expected scenario for emulating a Dvorak keyboard, + // even though expected results are the same.) + promiseQueue = promiseQueue.then(() => { + return sendKeyAndAssertResult({ + dict: { + key: chr, + code: code + }, + expectedKeypress: true, + expectedInput: chr, + expectedValues: { + key: chr, code: code, + keyCode: keyCode, + charCode: chr.charCodeAt(0) + } + }); + }); + + // Test alphabet with code set to Qwerty code and keyCode set, + // expects keyCode to follow key/keyCode value. + // (This is the expected scenario for emulating a Dvorak keyboard) + promiseQueue = promiseQueue.then(() => { + return sendKeyAndAssertResult({ + dict: { + key: chr, + keyCode: keyCode, + code: code + }, + expectedKeypress: true, + expectedInput: chr, + expectedValues: { + key: chr, code: code, + keyCode: keyCode, + charCode: chr.charCodeAt(0) + } + }); + }); + }); + } + + var qwertyCodeForDvorakSymbols = [ + 'Minus', 'Equal', + 'KeyQ', 'KeyW', 'KeyE', 'BracketLeft', 'BracketRight', 'Backslash', + 'Quote', 'KeyZ']; + + var shiftDvorakSymbols = '{}\"<>?+|_:'; + var dvorakSymbols = '[]\',./=\\-;'; + var dvorakSymbolsKeyCodes = [ + KeyboardEvent.DOM_VK_OPEN_BRACKET, + KeyboardEvent.DOM_VK_CLOSE_BRACKET, + KeyboardEvent.DOM_VK_QUOTE, + KeyboardEvent.DOM_VK_COMMA, + KeyboardEvent.DOM_VK_PERIOD, + KeyboardEvent.DOM_VK_SLASH, + KeyboardEvent.DOM_VK_EQUALS, + KeyboardEvent.DOM_VK_BACK_SLASH, + KeyboardEvent.DOM_VK_HYPHEN_MINUS, + KeyboardEvent.DOM_VK_SEMICOLON + ]; + + for (var i = 0; i < dvorakSymbols.length; i++) { + // callbacks in then() are deferred; must only reference these block-scoped + // variable instead of i. + let keyCode = dvorakSymbolsKeyCodes[i]; + let code = qwertyCodeForDvorakSymbols[i]; + + [dvorakSymbols.charAt(i), shiftDvorakSymbols.charAt(i)] + .forEach((chr) => { + // Test symbols with code set to Qwerty code, + // expects keyCode to be 0. + // (This is *NOT* the expected scenario for emulating a Dvorak keyboard) + promiseQueue = promiseQueue.then(() => { + return sendKeyAndAssertResult({ + dict: { + key: chr, + code: code + }, + expectedKeypress: true, + expectedInput: chr, + expectedValues: { + key: chr, code: code, + keyCode: 0, + charCode: chr.charCodeAt(0) + } + }); + }); + + // Test alphabet with code set to Qwerty code and keyCode set, + // expects keyCode to follow keyCode value. + // (This is the expected scenario for emulating a Dvorak keyboard) + promiseQueue = promiseQueue.then(() => { + return sendKeyAndAssertResult({ + dict: { + key: chr, + keyCode: keyCode, + code: code + }, + expectedKeypress: true, + expectedInput: chr, + expectedValues: { + key: chr, code: code, + keyCode: keyCode, + charCode: chr.charCodeAt(0) + } + }); + }); + }); + } + + return promiseQueue; +} + +function runSendKeyDigitKeySymbolsTests() { + gTestDescription = 'runSendKeyDigitKeySymbolsTests(): '; + var promiseQueue = Promise.resolve(); + + var digitKeySymbols = ')!@#$%^&*('; + for (var i = 0; i < digitKeySymbols.length; i++) { + // callbacks in then() are deferred; must only reference these block-scoped + // variable instead of i. + let keyCode = KeyboardEvent['DOM_VK_' + i]; + let chr = digitKeySymbols.charAt(i); + let code = 'Digit' + i; + + // Test plain symbol + promiseQueue = promiseQueue.then(() => { + return sendKeyAndAssertResult({ + dict: { + key: chr + }, + expectedKeypress: true, + expectedInput: chr, + expectedValues: { + key: chr, code: '', keyCode: 0, + charCode: chr.charCodeAt(0) + } + }); + }); + + // Test plain symbol with keyCode set + promiseQueue = promiseQueue.then(() => { + return sendKeyAndAssertResult({ + dict: { + key: chr, + keyCode: keyCode + }, + expectedKeypress: true, + expectedInput: chr, + expectedValues: { + key: chr, code: '', + keyCode: keyCode, + charCode: chr.charCodeAt(0) + } + }); + }); + + // Test plain symbol with code set + promiseQueue = promiseQueue.then(() => { + return sendKeyAndAssertResult({ + dict: { + key: chr, + code: code + }, + expectedKeypress: true, + expectedInput: chr, + expectedValues: { + key: chr, code: code, + keyCode: 0, + charCode: chr.charCodeAt(0) + } + }); + }); + + // Test plain symbol with code set to KeyA, + // expects keyCode to be 0. + promiseQueue = promiseQueue.then(() => { + return sendKeyAndAssertResult({ + dict: { + key: chr, + code: 'KeyA' + }, + expectedKeypress: true, + expectedInput: chr, + expectedValues: { + key: chr, code: 'KeyA', + keyCode: 0, + charCode: chr.charCodeAt(0) + } + }); + }); + + // Test plain symbol with keyCode set to DOM_VK_A, + // expects keyCode to follow the keyCode set. + promiseQueue = promiseQueue.then(() => { + return sendKeyAndAssertResult({ + dict: { + key: chr, + keyCode: KeyboardEvent.DOM_VK_A + }, + expectedKeypress: true, + expectedInput: chr, + expectedValues: { + key: chr, code: '', + keyCode: KeyboardEvent.DOM_VK_A, + charCode: chr.charCodeAt(0) + } + }); + }); + + // Test plain symbol with code set to KeyA + // expects keyCode to follow the keyCode set. + promiseQueue = promiseQueue.then(() => { + return sendKeyAndAssertResult({ + dict: { + key: chr, + code: 'KeyA', + keyCode: KeyboardEvent.DOM_VK_A + }, + expectedKeypress: true, + expectedInput: chr, + expectedValues: { + key: chr, code: 'KeyA', + keyCode: KeyboardEvent.DOM_VK_A, + charCode: chr.charCodeAt(0) + } + }); + }); + } + + return promiseQueue; +} + +function runSendKeyUSKeyboardSymbolsTests() { + gTestDescription = 'runSendKeyUSKeyboardSymbolsTests(): '; + var promiseQueue = Promise.resolve(); + + // Test printable symbols on US Keyboard + var symbols = ' ;:=+,<-_.>/?`~[{\\|]}\'\"'; + var symbolKeyCodes = [ + KeyboardEvent.DOM_VK_SPACE, + KeyboardEvent.DOM_VK_SEMICOLON, + KeyboardEvent.DOM_VK_SEMICOLON, + KeyboardEvent.DOM_VK_EQUALS, + KeyboardEvent.DOM_VK_EQUALS, + KeyboardEvent.DOM_VK_COMMA, + KeyboardEvent.DOM_VK_COMMA, + KeyboardEvent.DOM_VK_HYPHEN_MINUS, + KeyboardEvent.DOM_VK_HYPHEN_MINUS, + KeyboardEvent.DOM_VK_PERIOD, + KeyboardEvent.DOM_VK_PERIOD, + KeyboardEvent.DOM_VK_SLASH, + KeyboardEvent.DOM_VK_SLASH, + KeyboardEvent.DOM_VK_BACK_QUOTE, + KeyboardEvent.DOM_VK_BACK_QUOTE, + KeyboardEvent.DOM_VK_OPEN_BRACKET, + KeyboardEvent.DOM_VK_OPEN_BRACKET, + KeyboardEvent.DOM_VK_BACK_SLASH, + KeyboardEvent.DOM_VK_BACK_SLASH, + KeyboardEvent.DOM_VK_CLOSE_BRACKET, + KeyboardEvent.DOM_VK_CLOSE_BRACKET, + KeyboardEvent.DOM_VK_QUOTE, + KeyboardEvent.DOM_VK_QUOTE + ]; + var symbolCodes = [ + 'Space', + 'Semicolon', + 'Semicolon', + 'Equal', + 'Equal', + 'Comma', + 'Comma', + 'Minus', + 'Minus', + 'Period', + 'Period', + 'Slash', + 'Slash', + 'Backquote', + 'Backquote', + 'BracketLeft', + 'BracketLeft', + 'Backslash', + 'Backslash', + 'BracketRight', + 'BracketRight', + 'Quote', + 'Quote' + ]; + for (var i = 0; i < symbols.length; i++) { + // callbacks in then() are deferred; must only reference these block-scoped + // variable instead of i. + let keyCode = symbolKeyCodes[i]; + let chr = symbols.charAt(i); + let code = symbolCodes[i]; + + // Test plain symbol + promiseQueue = promiseQueue.then(() => { + return sendKeyAndAssertResult({ + dict: { + key: chr + }, + expectedKeypress: true, + expectedInput: chr, + expectedValues: { + key: chr, code: '', + keyCode: 0, + charCode: chr.charCodeAt(0) + } + }); + }); + + // Test plain symbol with keyCode set + promiseQueue = promiseQueue.then(() => { + return sendKeyAndAssertResult({ + dict: { + key: chr, + keyCode: keyCode + }, + expectedKeypress: true, + expectedInput: chr, + expectedValues: { + key: chr, code: '', + keyCode: keyCode, + charCode: chr.charCodeAt(0) + } + }); + }); + + // Test plain symbol with code set + // expects keyCode to be 0. + promiseQueue = promiseQueue.then(() => { + return sendKeyAndAssertResult({ + dict: { + key: chr, + code: code + }, + expectedKeypress: true, + expectedInput: chr, + expectedValues: { + key: chr, code: code, + keyCode: 0, + charCode: chr.charCodeAt(0) + } + }); + }); + + // Test plain symbol with code set to KeyA, + // expects keyCode to be 0. + promiseQueue = promiseQueue.then(() => { + return sendKeyAndAssertResult({ + dict: { + key: chr, + code: 'KeyA' + }, + expectedKeypress: true, + expectedInput: chr, + expectedValues: { + key: chr, code: 'KeyA', + keyCode: 0, + charCode: chr.charCodeAt(0) + } + }); + }); + + // Test plain symbol with keyCode set to DOM_VK_A, + // expects keyCode to follow the keyCode set. + promiseQueue = promiseQueue.then(() => { + return sendKeyAndAssertResult({ + dict: { + key: chr, + keyCode: KeyboardEvent.DOM_VK_A + }, + expectedKeypress: true, + expectedInput: chr, + expectedValues: { + key: chr, code: '', + keyCode: KeyboardEvent.DOM_VK_A, + charCode: chr.charCodeAt(0) + } + }); + }); + + // Test plain symbol with code set to KeyA + // expects keyCode to follow the keyCode set. + promiseQueue = promiseQueue.then(() => { + return sendKeyAndAssertResult({ + dict: { + key: chr, + code: 'KeyA', + keyCode: KeyboardEvent.DOM_VK_A + }, + expectedKeypress: true, + expectedInput: chr, + expectedValues: { + key: chr, code: 'KeyA', + keyCode: KeyboardEvent.DOM_VK_A, + charCode: chr.charCodeAt(0) + } + }); + }); + } + + return promiseQueue; +} + +function runSendKeyGreekLettersTests() { + gTestDescription = 'runSendKeyGreekLettersTests(): '; + var promiseQueue = Promise.resolve(); + + // Test Greek letters + var greekLetters = + '\u0391\u0392\u0393\u0394\u0395\u0396\u0397\u0398\u0399\u039a\u039b\u039c' + + '\u039d\u039e\u039f\u03a0\u03a1\u03a3\u03a4\u03a5\u03a6\u03a7\u03a8\u03a9' + + '\u03b1\u03b2\u03b3\u03b4\u03b5\u03b6\u03b7\u03b8\u03b9\u03ba\u03bb\u03bc' + + '\u03bd\u03be\u03bf\u03c0\u03c1\u03c3\u03c4\u03c5\u03c6\u03c7\u03c8\u03c9' + + '\u03c2'; + var greekLettersLayoutMap = + 'ABGDEZHUIKLMNJOPRSTYFXCVABGDEZHUIKLMNJOPRSTYFXCVQ'; + for (var i = 0; i < greekLetters.length; i++) { + // callbacks in then() are deferred; must only reference these block-scoped + // variable instead of i. + let keyCode = greekLettersLayoutMap.charCodeAt(i); + let chr = greekLetters.charAt(i); + let code = 'Key' + greekLettersLayoutMap.charAt(i); + + // Test plain alphabet + promiseQueue = promiseQueue.then(() => { + return sendKeyAndAssertResult({ + dict: { + key: chr + }, + expectedKeypress: true, + expectedInput: chr, + expectedValues: { + key: chr, code: '', + keyCode: 0, + charCode: chr.charCodeAt(0) + } + }); + }); + + // Test plain alphabet with keyCode set + promiseQueue = promiseQueue.then(() => { + return sendKeyAndAssertResult({ + dict: { + key: chr, + keyCode: keyCode + }, + expectedKeypress: true, + expectedInput: chr, + expectedValues: { + key: chr, code: '', + keyCode: keyCode, + charCode: chr.charCodeAt(0) + } + }); + }); + + // Test plain alphabet with code set, + // expects keyCode to be 0. + promiseQueue = promiseQueue.then(() => { + return sendKeyAndAssertResult({ + dict: { + key: chr, + code: code + }, + expectedKeypress: true, + expectedInput: chr, + expectedValues: { + key: chr, code: code, + keyCode: 0, + charCode: chr.charCodeAt(0) + } + }); + }); + + // Test plain alphabet with code set to Digit1, + // expects keyCode to be 0. + promiseQueue = promiseQueue.then(() => { + return sendKeyAndAssertResult({ + dict: { + key: chr, + code: 'Digit1' + }, + expectedKeypress: true, + expectedInput: chr, + expectedValues: { + key: chr, code: 'Digit1', + keyCode: 0, + charCode: chr.charCodeAt(0) + } + }); + }); + + // Test plain alphabet with code set to Digit1, + // and keyCode set to DOM_VK_A. + // expects keyCode to follow the keyCode set. + promiseQueue = promiseQueue.then(() => { + return sendKeyAndAssertResult({ + dict: { + key: chr, + code: 'Digit1', + keyCode: KeyboardEvent.DOM_VK_A + }, + expectedKeypress: true, + expectedInput: chr, + expectedValues: { + key: chr, code: 'Digit1', + keyCode: KeyboardEvent.DOM_VK_A, + charCode: chr.charCodeAt(0) + } + }); + }); + } + + return promiseQueue; +} + +function runSendKeyEnterTests() { + gTestDescription = 'runSendKeyEnterTests(): '; + var promiseQueue = Promise.resolve(); + + // Test Enter with code unset + promiseQueue = promiseQueue.then(() => { + return sendKeyAndAssertResult({ + dict: { + key: 'Enter' + }, + expectedKeypress: true, + expectedInput: '\n', + expectedValues: { + key: 'Enter', code: '', + keyCode: KeyboardEvent.DOM_VK_RETURN, + charCode: 0 + } + }); + }); + + // Test Enter with code set + promiseQueue = promiseQueue.then(() => { + return sendKeyAndAssertResult({ + dict: { + key: 'Enter', + code: 'Enter' + }, + expectedKeypress: true, + expectedInput: '\n', + expectedValues: { + key: 'Enter', code: 'Enter', + keyCode: KeyboardEvent.DOM_VK_RETURN, + charCode: 0 + } + }); + }); + + // Test Enter with keyCode explict set to zero + promiseQueue = promiseQueue.then(() => { + return sendKeyAndAssertResult({ + dict: { + key: 'Enter', + keyCode: 0 + }, + expectedKeypress: true, + expectedValues: { + key: 'Enter', code: '', + keyCode: 0, + charCode: 0 + } + }); + }); + + return promiseQueue; +} + +function runSendKeyNumpadTests() { + gTestDescription = 'runSendKeyNumpadTests(): '; + var promiseQueue = Promise.resolve(); + + var tests = []; + ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'] + .forEach(function(key) { + let charCode = key.charCodeAt(0); + + tests.push({ + dict: { + key: key, + code: 'Numpad' + key + }, + expectedKeypress: true, + expectedInput: key, + expectedValues: { + key: key, code: 'Numpad' + key, + keyCode: charCode, charCode: charCode, + location: KeyboardEvent.DOM_KEY_LOCATION_NUMPAD + } + }); + }); + + [['+', 'NumpadAdd'], + [',', 'NumpadComma'], + ['.', 'NumpadDecimal'], + ['.', 'NumpadComma'], // Locale-specific NumpadComma + [',', 'NumpadDecimal'], // Locale-specific NumpadDecimal + ['/', 'NumpadDivide'], + ['=', 'NumpadEqual'], + // ['#', 'NumpadHash'], // Not supported yet. + ['*', 'NumpadMultiply'], + ['(', 'NumpadParenLeft'], + [')', 'NumpadParenRight'], + // ['*', 'NumpadStar'], // Not supported yet. + ['-', 'NumpadSubtract']].forEach(function([key, code]) { + tests.push({ + dict: { + key: key, + code: code + }, + expectedKeypress: true, + expectedInput: key, + expectedValues: { + key: key, code: code, keyCode: 0, charCode: key.charCodeAt(0), + location: KeyboardEvent.DOM_KEY_LOCATION_NUMPAD + } + }); + }); + + [ + 'NumpadComma', // Locale-specific NumpadComma -- outputs nothing + 'NumpadClear', + 'NumpadClearEntry', + 'NumpadMemoryAdd', + 'NumpadMemoryClear', + 'NumpadMemoryRecall', + 'NumpadMemoryStore', + 'NumpadMemorySubtract' + ].forEach(function(code) { + tests.push({ + dict: { + key: 'Unidentified', + code: code + }, + expectedKeypress: true, + expectedValues: { + key: 'Unidentified', code: code, keyCode: 0, charCode: 0, + location: KeyboardEvent.DOM_KEY_LOCATION_NUMPAD + } + }); + }); + + tests.push({ + dict: { + key: 'Enter', + code: 'NumpadEnter' + }, + expectedKeypress: true, + expectedInput: '\n', + expectedValues: { + key: 'Enter', code: 'NumpadEnter', + keyCode: KeyboardEvent.DOM_VK_RETURN, charCode: 0, + location: KeyboardEvent.DOM_KEY_LOCATION_NUMPAD + } + }); + + tests.push({ + dict: { + key: 'Backspace', + code: 'NumpadBackspace' + }, + expectedKeypress: true, + expectedInput: 'Backspace', // Special value + expectedValues: { + key: 'Backspace', code: 'NumpadBackspace', + keyCode: KeyboardEvent.DOM_VK_BACK_SPACE, charCode: 0, + location: KeyboardEvent.DOM_KEY_LOCATION_NUMPAD + } + }); + + tests.forEach((test) => { + promiseQueue = promiseQueue.then(() => { + return sendKeyAndAssertResult(test); + }); + }); + + return promiseQueue; +} + +function runSendKeyRejectionTests() { + gTestDescription = 'runSendKeyRejectionTests(): '; + var promiseQueue = Promise.resolve(); + + promiseQueue = promiseQueue.then(() => { + return sendKeyAndAssertResult({ + dict: undefined, + expectedReject: TypeError + }); + }); + + return promiseQueue; +} + +function setCompositionAndAssertResult(testdata) { + var dict = testdata.dict; + var testName; + var promise; + + if (dict) { + testName = gTestDescription + + 'setComposition(' + testdata.text + + ', undefined, undefined, ' + + JSON.stringify(dict) + ')'; + promise = navigator.mozInputMethod.inputcontext + .setComposition(testdata.text, undefined, undefined, dict); + } else { + testName = gTestDescription + + 'setComposition(' + testdata.text + ')'; + promise = navigator.mozInputMethod.inputcontext + .setComposition(testdata.text); + } + + if (testdata.expectedReject) { + promise = promise + .then(() => { + ok(false, testName + ' should not resolve.'); + }, (e) => { + ok(true, testName + ' rejects.'); + ok(e instanceof testdata.expectedReject, 'Reject with type.'); + }) + + return promise; + } + + promise = promise + .then((res) => { + is(res, true, + testName + ' should resolve to true.'); + + var expectedEventDetail = []; + + var expectedValues = testdata.expectedValues; + + if (testdata.expectsKeyEvents && + (testdata.startsComposition || + testdata.dispatchKeyboardEventDuringComposition)) { + expectedEventDetail.push({ + type: 'keydown', + key: expectedValues.key, + charCode: 0, + code: expectedValues.code || '', + keyCode: expectedValues.keyCode || 0, + location: 0, + repeat: expectedValues.repeat || false, + value: gCurrentValue, + shift: false, + capsLock: false, + control: false, + alt: false + }); + } + + if (testdata.startsComposition) { + expectedEventDetail.push({ + type: 'compositionstart', + data: '', + value: gCurrentValue + }); + } + + expectedEventDetail.push({ + type: 'compositionupdate', + data: testdata.text, + value: gCurrentValue + }); + + expectedEventDetail.push({ + type: 'input', + value: gCurrentValue += testdata.expectedInput + }); + + if (testdata.expectsKeyEvents && + testdata.dispatchKeyboardEventDuringComposition) { + expectedEventDetail.push({ + type: 'keyup', + key: expectedValues.key, + charCode: 0, + code: expectedValues.code || '', + keyCode: expectedValues.keyCode || 0, + location: 0, + repeat: expectedValues.repeat || false, + value: gCurrentValue, + shift: false, + capsLock: false, + control: false, + alt: false + }); + } + + assertEventDetail(expectedEventDetail, testName); + gEventDetails = []; + }, (e) => { + ok(false, testName + ' should not reject. ' + e); + }); + + return promise; +} + +function endCompositionAndAssertResult(testdata) { + var dict = testdata.dict; + var testName; + var promise; + if (dict) { + testName = gTestDescription + + 'endComposition(' + testdata.text + ', ' + JSON.stringify(dict) + ')'; + promise = navigator.mozInputMethod.inputcontext + .endComposition(testdata.text, dict); + } else { + testName = gTestDescription + + 'endComposition(' + testdata.text + ')'; + promise = navigator.mozInputMethod.inputcontext + .endComposition(testdata.text); + } + + if (testdata.expectedReject) { + promise = promise + .then(() => { + ok(false, testName + ' should not resolve.'); + }, (e) => { + ok(true, testName + ' rejects.'); + ok(e instanceof testdata.expectedReject, 'Reject with type.'); + }) + + return promise; + } + + promise = promise + .then((res) => { + is(res, true, + testName + ' should resolve to true.'); + + var expectedEventDetail = []; + + var expectedValues = testdata.expectedValues; + + if (testdata.expectsKeyEvents && + testdata.dispatchKeyboardEventDuringComposition) { + expectedEventDetail.push({ + type: 'keydown', + key: expectedValues.key, + charCode: 0, + code: expectedValues.code || '', + keyCode: expectedValues.keyCode || 0, + location: 0, + repeat: expectedValues.repeat || false, + value: gCurrentValue, + shift: false, + capsLock: false, + control: false, + alt: false + }); + } + + expectedEventDetail.push({ + type: 'compositionend', + data: testdata.text, + value: gCurrentValue + }); + + expectedEventDetail.push({ + type: 'input', + value: gCurrentValue + }); + + if (testdata.expectsKeyEvents) { + expectedEventDetail.push({ + type: 'keyup', + key: expectedValues.key, + charCode: 0, + code: expectedValues.code || '', + keyCode: expectedValues.keyCode || 0, + location: 0, + repeat: expectedValues.repeat || false, + value: gCurrentValue, + shift: false, + capsLock: false, + control: false, + alt: false + }); + } + + assertEventDetail(expectedEventDetail, testName); + gEventDetails = []; + }, (e) => { + ok(false, testName + ' should not reject. ' + e); + }); + + return promise; +} + +function runCompositionWithKeyEventTests() { + var promiseQueue = Promise.resolve(); + + [true, false].forEach((dispatchKeyboardEventDuringComposition) => { + gTestDescription = 'runCompositionWithKeyEventTests() (dispatchKeyboardEvent =' + dispatchKeyboardEventDuringComposition + '): '; + + promiseQueue = promiseQueue + .then(() => { + SpecialPowers.setBoolPref( + 'dom.keyboardevent.dispatch_during_composition', + dispatchKeyboardEventDuringComposition); + }) + .then(() => { + return setCompositionAndAssertResult({ + text: 'foo', + expectsKeyEvents: true, + startsComposition: true, + dispatchKeyboardEventDuringComposition: dispatchKeyboardEventDuringComposition, + expectedInput: 'foo', + dict: { + key: 'a', + code: 'KeyA', + keyCode: KeyboardEvent.DOM_VK_A + }, + expectedValues: { + key: 'a', + code: 'KeyA', + keyCode: KeyboardEvent.DOM_VK_A + } + }); + }) + .then(() => { + return setCompositionAndAssertResult({ + text: 'foobar', + expectsKeyEvents: true, + startsComposition: false, + dispatchKeyboardEventDuringComposition: dispatchKeyboardEventDuringComposition, + expectedInput: 'bar', + dict: { + key: 'a', + code: 'KeyA', + keyCode: KeyboardEvent.DOM_VK_A + }, + expectedValues: { + key: 'a', + code: 'KeyA', + keyCode: KeyboardEvent.DOM_VK_A + } + }); + }) + .then(() => { + return endCompositionAndAssertResult({ + text: 'foobar', + expectsKeyEvents: true, + dispatchKeyboardEventDuringComposition: dispatchKeyboardEventDuringComposition, + expectedInput: '', + dict: { + key: 'a', + code: 'KeyA', + keyCode: KeyboardEvent.DOM_VK_A + }, + expectedValues: { + key: 'a', + code: 'KeyA', + keyCode: KeyboardEvent.DOM_VK_A + } + }); + }) + .then(() => { + SpecialPowers.clearUserPref( + 'dom.keyboardevent.dispatch_during_composition'); + }); + }); + + return promiseQueue; +} + +function runCompositionWithoutKeyEventTests() { + var promiseQueue = Promise.resolve(); + + gTestDescription = 'runCompositionWithoutKeyEventTests(): '; + + promiseQueue = promiseQueue + .then(() => { + return setCompositionAndAssertResult({ + text: 'foo', + expectsKeyEvents: false, + startsComposition: true, + expectedInput: 'foo' + }); + }) + .then(() => { + return setCompositionAndAssertResult({ + text: 'foobar', + expectsKeyEvents: false, + startsComposition: false, + expectedInput: 'bar' + }); + }) + .then(() => { + return endCompositionAndAssertResult({ + text: 'foobar', + expectsKeyEvents: false, + expectedInput: '' + }); + }); + + return promiseQueue; +} + +function keydownAndAssertResult(testdata) { + var dict = testdata.dict; + var testName = gTestDescription + 'keydown(' + JSON.stringify(dict) + ')'; + var promise = navigator.mozInputMethod.inputcontext.keydown(dict); + + if (testdata.expectedReject) { + promise = promise + .then(() => { + ok(false, testName + ' should not resolve.'); + }, (e) => { + ok(true, testName + ' rejects.'); + ok(e instanceof testdata.expectedReject, 'Reject with type.'); + }) + + return promise; + } + + promise = promise + .then((res) => { + is(res, true, + testName + ' should resolve to true.'); + + var expectedEventDetail = []; + + var expectedValues = testdata.expectedValues; + + expectedEventDetail.push({ + type: 'keydown', + key: expectedValues.key, + charCode: 0, + code: expectedValues.code || '', + keyCode: expectedValues.keyCode || 0, + location: 0, + repeat: expectedValues.repeat || false, + value: gCurrentValue, + shift: false, + capsLock: false, + control: false, + alt: false + }); + + if (testdata.expectedKeypress) { + expectedEventDetail.push({ + type: 'keypress', + key: expectedValues.key, + charCode: expectedValues.charCode, + code: expectedValues.code || '', + keyCode: expectedValues.charCode ? 0 : expectedValues.keyCode, + location: 0, + repeat: expectedValues.repeat || false, + value: gCurrentValue, + shift: false, + capsLock: false, + control: false, + alt: false + }); + } + + if (testdata.expectedInput) { + expectedEventDetail.push({ + type: 'input', + value: gCurrentValue += testdata.expectedInput + }); + } + + assertEventDetail(expectedEventDetail, testName); + gEventDetails = []; + }, (e) => { + ok(false, testName + ' should not reject. ' + e); + }); + + return promise; +} + +function keyupAndAssertResult(testdata) { + var dict = testdata.dict; + var testName = gTestDescription + 'keyup(' + JSON.stringify(dict) + ')'; + var promise = navigator.mozInputMethod.inputcontext.keyup(dict); + + if (testdata.expectedReject) { + promise = promise + .then(() => { + ok(false, testName + ' should not resolve.'); + }, (e) => { + ok(true, testName + ' rejects.'); + ok(e instanceof testdata.expectedReject, 'Reject with type.'); + }) + + return promise; + } + + promise = promise + .then((res) => { + is(res, true, + testName + ' should resolve to true.'); + + var expectedEventDetail = []; + + var expectedValues = testdata.expectedValues; + + expectedEventDetail.push({ + type: 'keyup', + key: expectedValues.key, + charCode: 0, + code: expectedValues.code || '', + keyCode: expectedValues.keyCode || 0, + location: 0, + repeat: expectedValues.repeat || false, + value: gCurrentValue, + shift: false, + capsLock: false, + control: false, + alt: false + }); + + assertEventDetail(expectedEventDetail, testName); + gEventDetails = []; + }, (e) => { + ok(false, testName + ' should not reject. ' + e); + }); + + return promise; +} + +function runKeyDownUpTests() { + gTestDescription = 'runKeyDownUpTests(): '; + var promiseQueue = Promise.resolve(); + + let chr = 'a'; + let code = 'KeyA'; + let keyCode = KeyboardEvent.DOM_VK_A; + + promiseQueue = promiseQueue + .then(() => { + return keydownAndAssertResult({ + dict: { + key: chr, + code: code, + keyCode: keyCode + }, + expectedKeypress: true, + expectedInput: chr, + expectedValues: { + key: chr, code: code, + keyCode: keyCode, + charCode: chr.charCodeAt(0) + } + }); + }) + .then(() => { + return keyupAndAssertResult({ + dict: { + key: chr, + code: code, + keyCode: keyCode + }, + expectedValues: { + key: chr, code: code, + keyCode: keyCode, + charCode: chr.charCodeAt(0) + } + }); + }); + + return promiseQueue; +} + +function runKeyDownUpRejectionTests() { + gTestDescription = 'runKeyDownUpRejectionTests(): '; + var promiseQueue = Promise.resolve(); + + promiseQueue = promiseQueue.then(() => { + return keydownAndAssertResult({ + dict: undefined, + expectedReject: TypeError + }); + }); + + promiseQueue = promiseQueue.then(() => { + return keyupAndAssertResult({ + dict: undefined, + expectedReject: TypeError + }); + }); + + return promiseQueue; +} + +function runRepeatTests() { + gTestDescription = 'runRepeatTests(): '; + var promiseQueue = Promise.resolve(); + + // Test repeat + promiseQueue = promiseQueue + .then(() => { + return sendKeyAndAssertResult({ + dict: { + key: 'A', + repeat: true + }, + expectedKeypress: true, + expectedRepeat: true, + expectedInput: 'A', + expectedValues: { + repeat: true, + key: 'A', code: '', + keyCode: KeyboardEvent.DOM_VK_A, + charCode: 'A'.charCodeAt(0) + } + }); + }) + .then(() => { + return keyupAndAssertResult({ + dict: { + key: 'A' + }, + expectedKeypress: true, + expectedRepeat: true, + expectedInput: 'A', + expectedValues: { + key: 'A', code: '', + keyCode: KeyboardEvent.DOM_VK_A, + charCode: 'A'.charCodeAt(0) + } + }); + }); + + return promiseQueue; +} + +function runTest() { + let im = navigator.mozInputMethod; + + // Set current page as an input method. + SpecialPowers.wrap(im).setActive(true); + + let iframe = document.createElement('iframe'); + iframe.src = 'file_test_bug1137557.html'; + iframe.setAttribute('mozbrowser', true); + document.body.appendChild(iframe); + + let mm = SpecialPowers.getBrowserFrameMessageManager(iframe); + + iframe.addEventListener('mozbrowserloadend', function() { + mm.addMessageListener('test:eventDetail', function(msg) { + gEventDetails.push(msg.data); + }); + mm.loadFrameScript('data:,(' + encodeURIComponent(appFrameScript.toString()) + ')();', false); + }); + + waitForInputContextChange() + .then(() => { + var inputcontext = navigator.mozInputMethod.inputcontext; + + ok(!!inputcontext, 'Receving the first input context'); + }) + .then(() => runSendKeyAlphabetTests()) + .then(() => runSendKeyNumberTests()) + .then(() => runSendKeyDvorakTests()) + .then(() => runSendKeyDigitKeySymbolsTests()) + .then(() => runSendKeyUSKeyboardSymbolsTests()) + .then(() => runSendKeyGreekLettersTests()) + .then(() => runSendKeyEnterTests()) + .then(() => runSendKeyNumpadTests()) + .then(() => runSendKeyRejectionTests()) + .then(() => runCompositionWithKeyEventTests()) + .then(() => runCompositionWithoutKeyEventTests()) + .then(() => runKeyDownUpTests()) + .then(() => runKeyDownUpRejectionTests()) + .then(() => runRepeatTests()) + .catch((err) => { + console.error(err); + is(false, err.message); + }) + .then(() => { + var p = waitForInputContextChange(); + + // Revoke our right from using the IM API. + SpecialPowers.wrap(im).setActive(false); + + return p; + }) + .then(() => { + var inputcontext = navigator.mozInputMethod.inputcontext; + + is(inputcontext, null, 'Receving null input context'); + + inputmethod_cleanup(); + }) + .catch((err) => { + console.error(err); + is(false, err.message); + }); +} + +</script> +</pre> +</body> +</html> + |