diff options
Diffstat (limited to 'dom/browser-element/mochitest/browserElement_SetInputMethodActive.js')
-rw-r--r-- | dom/browser-element/mochitest/browserElement_SetInputMethodActive.js | 323 |
1 files changed, 323 insertions, 0 deletions
diff --git a/dom/browser-element/mochitest/browserElement_SetInputMethodActive.js b/dom/browser-element/mochitest/browserElement_SetInputMethodActive.js new file mode 100644 index 000000000..2b94f5cda --- /dev/null +++ b/dom/browser-element/mochitest/browserElement_SetInputMethodActive.js @@ -0,0 +1,323 @@ +/* Any copyright is dedicated to the public domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Bug 905573 - Add setInputMethodActive to browser elements to allow gaia +// system set the active IME app. +'use strict'; + +SimpleTest.waitForExplicitFinish(); +browserElementTestHelpers.setEnabledPref(true); + +// We'll need to get the appId from the current document, +// it's either SpecialPowers.Ci.nsIScriptSecurityManager.NO_APP_ID when +// we are not running inside an app (e.g. Firefox Desktop), +// or the appId of Mochitest app when we are running inside that app +// (e.g. Emulator). +var currentAppId = SpecialPowers.wrap(document).nodePrincipal.appId; +var inApp = + currentAppId !== SpecialPowers.Ci.nsIScriptSecurityManager.NO_APP_ID; +// We will also need the manifest URL and set it on iframes. +var currentAppManifestURL; + +if (inApp) { + let appsService = SpecialPowers.Cc["@mozilla.org/AppsService;1"] + .getService(SpecialPowers.Ci.nsIAppsService); + + currentAppManifestURL = appsService.getManifestURLByLocalId(currentAppId); +}; + +info('appId=' + currentAppId); +info('manifestURL=' + currentAppManifestURL); + +function setup() { + let appInfo = SpecialPowers.Cc['@mozilla.org/xre/app-info;1'] + .getService(SpecialPowers.Ci.nsIXULAppInfo); + if (appInfo.name != 'B2G') { + SpecialPowers.Cu.import("resource://gre/modules/Keyboard.jsm", window); + } + + SpecialPowers.setBoolPref("dom.mozInputMethod.enabled", true); + SpecialPowers.addPermission('input-manage', true, document); +} + +function tearDown() { + SpecialPowers.setBoolPref("dom.mozInputMethod.enabled", false); + SpecialPowers.removePermission('input-manage', document); + SimpleTest.finish(); +} + +function runTest() { + createFrames(); +} + +var gInputMethodFrames = []; +var gInputFrame; + +function createFrames() { + // Create two input method iframes. + let loadendCount = 0; + let countLoadend = function() { + loadendCount++; + if (loadendCount === 3) { + setPermissions(); + } + }; + + // Create an input field to receive string from input method iframes. + gInputFrame = document.createElement('iframe'); + gInputFrame.setAttribute('mozbrowser', 'true'); + gInputFrame.src = 'file_browserElement_SetInputMethodActive.html'; + document.body.appendChild(gInputFrame); + gInputFrame.addEventListener('mozbrowserloadend', countLoadend); + + for (let i = 0; i < 2; i++) { + let frame = gInputMethodFrames[i] = document.createElement('iframe'); + frame.setAttribute('mozbrowser', 'true'); + if (currentAppManifestURL) { + frame.setAttribute('mozapp', currentAppManifestURL); + } + frame.addEventListener('mozbrowserloadend', countLoadend); + frame.src = 'file_empty.html#' + i; + document.body.appendChild(frame); + } + } + +function setPermissions() { + let permissions = [{ + type: 'input', + allow: true, + context: { + url: SimpleTest.getTestFileURL('/file_empty.html'), + originAttributes: { + appId: currentAppId, + inIsolatedMozBrowser: true + } + } + }]; + + if (inApp) { + // The current document would also need to be given access for IPC to + // recognize our permission (why)? + permissions.push({ + type: 'input', allow: true, context: document }); + } + + SpecialPowers.pushPermissions(permissions, + SimpleTest.waitForFocus.bind(SimpleTest, startTest)); +} + + function startTest() { + // The frame script running in the frame where the input is hosted. + let appFrameScript = function appFrameScript() { + let input = content.document.body.firstElementChild; + input.oninput = function() { + sendAsyncMessage('test:InputMethod:oninput', { + from: 'input', + value: this.value + }); + }; + + input.onblur = function() { + // "Expected" lost of focus since the test is finished. + if (input.value === 'hello#0#1') { + return; + } + + sendAsyncMessage('test:InputMethod:oninput', { + from: 'input', + error: true, + value: 'Unexpected lost of focus on the input frame!' + }); + }; + + input.focus(); + }; + + // Inject frame script to receive input. + let mm = SpecialPowers.getBrowserFrameMessageManager(gInputFrame); + mm.loadFrameScript('data:,(' + encodeURIComponent(appFrameScript.toString()) + ')();', false); + mm.addMessageListener("test:InputMethod:oninput", next); + + gInputMethodFrames.forEach((frame) => { + ok(frame.setInputMethodActive, 'Can access setInputMethodActive.'); + + // The frame script running in the input method frames. + let appFrameScript = function appFrameScript() { + let im = content.navigator.mozInputMethod; + im.oninputcontextchange = function() { + let ctx = im.inputcontext; + // Report back to parent frame on status of ctx gotten. + // (A setTimeout() here to ensure this always happens after + // DOMRequest succeed.) + content.setTimeout(() => { + sendAsyncMessage('test:InputMethod:imFrameMessage', { + from: 'im', + value: content.location.hash + !!ctx + }); + }); + + // If there is a context, send out the hash. + if (ctx) { + ctx.replaceSurroundingText(content.location.hash); + } + }; + }; + + // Inject frame script to receive message. + let mm = SpecialPowers.getBrowserFrameMessageManager(frame); + mm.loadFrameScript('data:,(' + encodeURIComponent(appFrameScript.toString()) + ')();', false); + mm.addMessageListener("test:InputMethod:imFrameMessage", next); + }); + + // Set focus to the input field and wait for input methods' inputting. + SpecialPowers.DOMWindowUtils.focus(gInputFrame); + + let req0 = gInputMethodFrames[0].setInputMethodActive(true); + req0.onsuccess = function() { + ok(true, 'setInputMethodActive succeeded (0).'); + }; + + req0.onerror = function() { + ok(false, 'setInputMethodActive failed (0): ' + this.error.name); + }; +} + +var gCount = 0; + +var gFrameMsgCounts = { + 'input': 0, + 'im0': 0, + 'im1': 0 +}; + +function next(msg) { + let wrappedMsg = SpecialPowers.wrap(msg); + let from = wrappedMsg.data.from; + let value = wrappedMsg.data.value; + + if (wrappedMsg.data.error) { + ok(false, wrappedMsg.data.value); + + return; + } + + let fromId = from; + if (from === 'im') { + fromId += value[1]; + } + gFrameMsgCounts[fromId]++; + + // The texts sent from the first and the second input method are '#0' and + // '#1' respectively. + switch (gCount) { + case 0: + switch (fromId) { + case 'im0': + if (gFrameMsgCounts.im0 === 1) { + is(value, '#0true', 'First frame should get the context first.'); + } else { + ok(false, 'Unexpected multiple messages from im0.') + } + + break; + + case 'im1': + is(false, 'Shouldn\'t be hearing anything from second frame.'); + + break; + + case 'input': + if (gFrameMsgCounts.input === 1) { + is(value, '#0hello', + 'Failed to get correct input from the first iframe.'); + } else { + ok(false, 'Unexpected multiple messages from input.') + } + + break; + } + + if (gFrameMsgCounts.input !== 1 || + gFrameMsgCounts.im0 !== 1 || + gFrameMsgCounts.im1 !== 0) { + return; + } + + gCount++; + + let req0 = gInputMethodFrames[0].setInputMethodActive(false); + req0.onsuccess = function() { + ok(true, 'setInputMethodActive succeeded (0).'); + }; + req0.onerror = function() { + ok(false, 'setInputMethodActive failed (0): ' + this.error.name); + }; + let req1 = gInputMethodFrames[1].setInputMethodActive(true); + req1.onsuccess = function() { + ok(true, 'setInputMethodActive succeeded (1).'); + }; + req1.onerror = function() { + ok(false, 'setInputMethodActive failed (1): ' + this.error.name); + }; + + break; + + case 1: + switch (fromId) { + case 'im0': + if (gFrameMsgCounts.im0 === 2) { + is(value, '#0false', 'First frame should have the context removed.'); + } else { + ok(false, 'Unexpected multiple messages from im0.') + } + break; + + case 'im1': + if (gFrameMsgCounts.im1 === 1) { + is(value, '#1true', 'Second frame should get the context.'); + } else { + ok(false, 'Unexpected multiple messages from im0.') + } + + break; + + case 'input': + if (gFrameMsgCounts.input === 2) { + is(value, '#0#1hello', + 'Failed to get correct input from the second iframe.'); + } else { + ok(false, 'Unexpected multiple messages from input.') + } + break; + } + + if (gFrameMsgCounts.input !== 2 || + gFrameMsgCounts.im0 !== 2 || + gFrameMsgCounts.im1 !== 1) { + return; + } + + gCount++; + + // Receive the second input from the second iframe. + // Deactive the second iframe. + let req3 = gInputMethodFrames[1].setInputMethodActive(false); + req3.onsuccess = function() { + ok(true, 'setInputMethodActive(false) succeeded (2).'); + }; + req3.onerror = function() { + ok(false, 'setInputMethodActive(false) failed (2): ' + this.error.name); + }; + break; + + case 2: + is(fromId, 'im1', 'Message sequence unexpected (3).'); + is(value, '#1false', 'Second frame should have the context removed.'); + + tearDown(); + break; + } +} + +setup(); +addEventListener('testready', runTest); |