<!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>