Bug 981997 - Fix test for setInputMethodActive and dead object error. r=yxl
authorTim Chien <timdream@gmail.com>
Wed, 12 Mar 2014 08:45:27 -0400
changeset 191398 b37c15e6833b0de57fda696c2b918a37dc89f3e5
parent 191397 03acaf79c507f7cd39022ba9dab4cb710cad7ce4
child 191399 730ba60ac7edb24a5959dd47cafe5071f31f197a
push id474
push userasasaki@mozilla.com
push dateMon, 02 Jun 2014 21:01:02 +0000
treeherdermozilla-release@967f4cf1b31c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersyxl
bugs981997
milestone30.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 981997 - Fix test for setInputMethodActive and dead object error. r=yxl
dom/browser-element/BrowserElementParent.jsm
dom/browser-element/mochitest/browserElement_SetInputMethodActive.js
dom/browser-element/mochitest/file_inputmethod.html
dom/browser-element/mochitest/mochitest-oop.ini
dom/browser-element/mochitest/mochitest.ini
--- a/dom/browser-element/BrowserElementParent.jsm
+++ b/dom/browser-element/BrowserElementParent.jsm
@@ -710,27 +710,34 @@ BrowserElementParent.prototype = {
       throw Components.Exception("Invalid argument",
                                  Cr.NS_ERROR_INVALID_ARG);
     }
 
     let req = Services.DOMRequest.createRequest(this._window);
 
     // Deactivate the old input method if needed.
     if (activeInputFrame && isActive) {
-      let reqOld = XPCNativeWrapper.unwrap(activeInputFrame)
-                                   .setInputMethodActive(false);
-      reqOld.onsuccess = function() {
+      if (Cu.isDeadWrapper(activeInputFrame)) {
+        // If the activeInputFrame is already a dead object,
+        // we should simply set it to null directly.
         activeInputFrame = null;
         this._sendSetInputMethodActiveDOMRequest(req, isActive);
-      }.bind(this);
-      reqOld.onerror = function() {
-        Services.DOMRequest.fireErrorAsync(req,
-          'Failed to deactivate the old input method: ' +
-          reqOld.error + '.');
-      };
+      } else {
+        let reqOld = XPCNativeWrapper.unwrap(activeInputFrame)
+                                     .setInputMethodActive(false);
+        reqOld.onsuccess = function() {
+          activeInputFrame = null;
+          this._sendSetInputMethodActiveDOMRequest(req, isActive);
+        }.bind(this);
+        reqOld.onerror = function() {
+          Services.DOMRequest.fireErrorAsync(req,
+            'Failed to deactivate the old input method: ' +
+            reqOld.error + '.');
+        };
+      }
     } else {
       this._sendSetInputMethodActiveDOMRequest(req, isActive);
     }
     return req;
   },
 
   _sendSetInputMethodActiveDOMRequest: function(req, isActive) {
     let id = 'req_' + this._domRequestCounter++;
--- a/dom/browser-element/mochitest/browserElement_SetInputMethodActive.js
+++ b/dom/browser-element/mochitest/browserElement_SetInputMethodActive.js
@@ -5,92 +5,174 @@
 // system set the active IME app.
 'use strict';
 
 SimpleTest.waitForExplicitFinish();
 browserElementTestHelpers.setEnabledPref(true);
 browserElementTestHelpers.addPermission();
 
 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.setBoolPref("dom.mozInputMethod.testing", true);
   SpecialPowers.addPermission('input-manage', true, document);
 }
 
 function tearDown() {
   SpecialPowers.setBoolPref("dom.mozInputMethod.enabled", false);
   SpecialPowers.setBoolPref("dom.mozInputMethod.testing", false);
-  SpecialPowers.removePermission("input-manage", document);
+  SpecialPowers.removePermission('input-manage', document);
   SimpleTest.finish();
 }
 
 function runTest() {
-  // Create an input field to receive string from input method iframes.
-  let input = document.createElement('input');
-  input.type = 'text';
-  document.body.appendChild(input);
+  let path = location.pathname;
+  let imeUrl = location.protocol + '//' + location.host +
+               path.substring(0, path.lastIndexOf('/')) +
+               '/file_inputmethod.html';
+  SpecialPowers.pushPermissions([{
+    type: 'input',
+    allow: true,
+    context: {
+      url: imeUrl,
+      appId: SpecialPowers.Ci.nsIScriptSecurityManager.NO_APP_ID,
+      isInBrowserElement: true
+    }
+  }], createFrames);
+}
+
+var gFrames = [];
+var gInputFrame;
+
+function createFrames() {
+  // Create two input method iframes.
+  let loadendCount = 0;
+  let countLoadend = function() {
+    ok(this.setInputMethodActive, 'Can access setInputMethodActive.');
+
+    if (this === gInputFrame) {
+      // 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', this.value);
+        };
 
-  // Create two input method iframes.
-  let frames = [];
+        /*
+         * Bug 957213. Sometimes we need to refocus the input field to avoid
+         * intermittent test failure.
+         */
+        content.setInterval(function() {
+          input.focus();
+        }, 500);
+      }
+
+      // Inject frame script to receive input.
+      let mm = SpecialPowers.getBrowserFrameMessageManager(gInputFrame);
+      mm.loadFrameScript('data:,(' + appFrameScript.toString() + ')();', false);
+      mm.addMessageListener("test:InputMethod:oninput", next);
+    }
+
+    loadendCount++;
+    if (loadendCount === 3) {
+      startTest();
+    }
+  };
+
+  // Create an input field to receive string from input method iframes.
+  gInputFrame = document.createElement('iframe');
+  SpecialPowers.wrap(gInputFrame).mozbrowser = true;
+  gInputFrame.src =
+    'data:text/html,<input autofocus value="hello" />' +
+    '<p>This is targetted mozbrowser frame.</p>';
+  document.body.appendChild(gInputFrame);
+  gInputFrame.addEventListener('mozbrowserloadend', countLoadend);
+
   for (let i = 0; i < 2; i++) {
-    frames[i] = document.createElement('iframe');
-    SpecialPowers.wrap(frames[i]).mozbrowser = true;
+    let frame = gFrames[i] = document.createElement('iframe');
+    SpecialPowers.wrap(gFrames[i]).mozbrowser = true;
     // When the input method iframe is activated, it will send the URL
     // hash to current focused element. We set different hash to each
     // iframe so that iframes can be differentiated by their hash.
-    frames[i].src = 'file_inputmethod.html#' + i;
-    frames[i].setAttribute('mozapp', location.href.replace(/[^/]+$/, 'file_inputmethod.webapp'));
-    document.body.appendChild(frames[i]);
+    frame.src = 'file_inputmethod.html#' + i;
+    document.body.appendChild(frame);
+    frame.addEventListener('mozbrowserloadend', countLoadend);
   }
+}
 
-  let count = 0;
-
+function startTest() {
   // Set focus to the input field and wait for input methods' inputting.
-  SpecialPowers.DOMWindowUtils.focus(input);
-  var timerId = null;
-  input.oninput = function() {
-    // The texts sent from the first and the second input method are '#0' and
-    // '#1' respectively.
-    switch (count) {
-      case 1:
-        is(input.value, '#0', 'Failed to get correct input from the first iframe.');
-        testNextInputMethod();
-        break;
-      case 2:
-        is(input.value, '#0#1', 'Failed to get correct input from the second iframe.');
-        // Do nothing and wait for the next input from the second iframe.
-        count++;
-        break;
-      case 3:
-        is(input.value, '#0#1#1', 'Failed to get correct input from the second iframe.');
-        // Receive the second input from the second iframe.
-        count++;
-        // Deactive the second iframe.
-        frames[1].setInputMethodActive(false);
-        // Wait for a short while to ensure the second iframe is not active any
-        // more.
-        timerId = setTimeout(function() {
-          ok(true, 'Successfully deactivate the second iframe.');
-          tearDown();
-        }, 1000);
-        break;
-      default:
-        ok(false, 'Failed to deactivate the second iframe.');
-        clearTimeout(timerId);
+  SpecialPowers.DOMWindowUtils.focus(gInputFrame);
+
+  let req0 = gFrames[0].setInputMethodActive(true);
+  req0.onsuccess = function() {
+    ok(true, 'setInputMethodActive succeeded (0).');
+  };
+
+  req0.onerror = function() {
+    ok(false, 'setInputMethodActive failed (0): ' + this.error.name);
+  };
+}
+
+var gTimerId = null;
+var gCount = 0;
+
+function next(msg) {
+  gCount++;
+  let wrappedMsg = SpecialPowers.wrap(msg);
+  let value = wrappedMsg.data;
+  // The texts sent from the first and the second input method are '#0' and
+  // '#1' respectively.
+  switch (gCount) {
+    case 1:
+      is(value, '#0hello',
+         'Failed to get correct input from the first iframe.');
+      let req1 = gFrames[1].setInputMethodActive(true);
+      req1.onsuccess = function() {
+       ok(true, 'setInputMethodActive succeeded (1).');
+      };
+      req1.onerror = function() {
+       ok(false, 'setInputMethodActive failed (1): ' + this.error.name);
+      };
+      break;
+
+    case 2:
+      is(value, '#0#1hello',
+         'Failed to get correct input from the second iframe.');
+      // Do nothing and wait for the next input from the second iframe.
+      break;
+
+    case 3:
+      is(value, '#0#1#1hello',
+         'Failed to get correct input from the second iframe.');
+      // Receive the second input from the second iframe.
+      // Deactive the second iframe.
+      let req3 = gFrames[1].setInputMethodActive(false);
+      req3.onsuccess = function() {
+        ok(true, 'setInputMethodActive(false) succeeded (3).');
+      };
+      req3.onerror = function() {
+        ok(false, 'setInputMethodActive(false) failed (3): ' + this.error.name);
+      };
+
+      // Wait for a short while to ensure the second iframe is not active any
+      // more.
+      gTimerId = setTimeout(function() {
+        ok(true, 'Successfully deactivate the second iframe.');
         tearDown();
-        break;
-    }
-  }
-
-  ok(frames[0].setInputMethodActive, 'Cannot access setInputMethodActive.');
+      }, 1000);
+      break;
 
-  function testNextInputMethod() {
-    frames[count++].setInputMethodActive(true);
+    case 4:
+      ok(false, 'Failed to deactivate the second iframe in time.');
+      clearTimeout(gTimerId);
+      tearDown();
+      break;
   }
-
-  // Wait for a short while to let input method get ready.
-  setTimeout(function() {
-    testNextInputMethod();
-  }, 500);
 }
 
 setup();
 addEventListener('testready', runTest);
--- a/dom/browser-element/mochitest/file_inputmethod.html
+++ b/dom/browser-element/mochitest/file_inputmethod.html
@@ -1,21 +1,26 @@
+<!DOCTYPE HTML>
 <html>
 <body>
 <script>
   var im = navigator.mozInputMethod;
   if (im) {
     var intervalId = null;
     // Automatically append location hash to current input field.
     im.oninputcontextchange = function() {
       var ctx = im.inputcontext;
       if (ctx) {
+        dump('inputcontext is received for input method ' + location.hash + '\n');
         intervalId = setInterval(function() {
+          dump('sending text in input method ' + location.hash + '\n');
           ctx.replaceSurroundingText(location.hash);
         }, 500);
       } else {
+        dump('inputcontext is removed for input method ' + location.hash + '\n');
         clearInterval(intervalId);
       }
     };
   }
 </script>
+<p>This frame representing the input method frame.</p>
 </body>
 </html>
--- a/dom/browser-element/mochitest/mochitest-oop.ini
+++ b/dom/browser-element/mochitest/mochitest-oop.ini
@@ -47,16 +47,17 @@ skip-if = toolkit=='gonk'
 [test_browserElement_oop_PurgeHistory.html]
 [test_browserElement_oop_Reload.html]
 [test_browserElement_oop_ReloadPostRequest.html]
 [test_browserElement_oop_RemoveBrowserElement.html]
 [test_browserElement_oop_ScrollEvent.html]
 [test_browserElement_oop_SecurityChange.html]
 skip-if = toolkit == 'android' #TIMED_OUT, bug 766586
 [test_browserElement_oop_SendEvent.html]
+[test_browserElement_oop_SetInputMethodActive.html]
 [test_browserElement_oop_SetVisible.html]
 [test_browserElement_oop_SetVisibleFrames.html]
 [test_browserElement_oop_SetVisibleFrames2.html]
 [test_browserElement_oop_Stop.html]
 [test_browserElement_oop_TargetBlank.html]
 [test_browserElement_oop_TargetTop.html]
 [test_browserElement_oop_Titlechange.html]
 [test_browserElement_oop_TopBarrier.html]
@@ -70,11 +71,8 @@ skip-if = toolkit == 'android' #TIMED_OU
 disabled = bug 930449
 # Disabled until bug 924771 makes them stop timing out
 [test_browserElement_oop_CloseFromOpener.html]
 disabled = bug 924771
 [test_browserElement_oop_CloseApp.html]
 disabled = bug 924771
 [test_browserElement_oop_ExposableURI.html]
 disabled = bug 924771
-# Disabled until we fix bug 906096.
-[test_browserElement_oop_SetInputMethodActive.html]
-disabled = bug 906096
--- a/dom/browser-element/mochitest/mochitest.ini
+++ b/dom/browser-element/mochitest/mochitest.ini
@@ -42,16 +42,17 @@ support-files =
   browserElement_PromptConfirm.js
   browserElement_PurgeHistory.js
   browserElement_Reload.js
   browserElement_ReloadPostRequest.js
   browserElement_RemoveBrowserElement.js
   browserElement_ScrollEvent.js
   browserElement_SecurityChange.js
   browserElement_SendEvent.js
+  browserElement_SetInputMethodActive.js
   browserElement_SetVisible.js
   browserElement_SetVisibleFrames.js
   browserElement_SetVisibleFrames2.js
   browserElement_Stop.js
   browserElement_TargetBlank.js
   browserElement_TargetTop.js
   browserElement_Titlechange.js
   browserElement_TopBarrier.js
@@ -92,16 +93,17 @@ support-files =
   file_browserElement_XFrameOptionsDeny.html
   file_browserElement_XFrameOptionsSameOrigin.html
   file_bug709759.sjs
   file_bug741717.sjs
   file_empty.html
   file_empty_script.js
   file_focus.html
   file_http_401_response.sjs
+  file_inputmethod.html
   file_post_request.html
   file_wyciwyg.html
 
 # Note: browserElementTestHelpers.js looks at the test's filename to determine
 # whether the test should be OOP.  "_oop_" signals OOP, "_inproc_" signals in
 # process.  Default is OOP.
 [test_browserElement_NoAttr.html]
 [test_browserElement_NoPref.html]
@@ -158,35 +160,34 @@ skip-if = (toolkit == 'gonk' && !debug)
 [test_browserElement_inproc_PromptConfirm.html]
 [test_browserElement_inproc_PurgeHistory.html]
 [test_browserElement_inproc_ReloadPostRequest.html]
 [test_browserElement_inproc_RemoveBrowserElement.html]
 [test_browserElement_inproc_ScrollEvent.html]
 [test_browserElement_inproc_SecurityChange.html]
 skip-if = toolkit == 'android' || (toolkit == 'gonk' && !debug) # android(TIMED_OUT, bug 766586) androidx86(TIMED_OUT, bug 766586)
 [test_browserElement_inproc_SendEvent.html]
+# The setInputMethodActive() tests will timed out on Android
+[test_browserElement_inproc_SetInputMethodActive.html]
+skip-if = (os == "android")
 [test_browserElement_inproc_SetVisible.html]
 [test_browserElement_inproc_SetVisibleFrames.html]
 [test_browserElement_inproc_SetVisibleFrames2.html]
 [test_browserElement_inproc_Stop.html]
 [test_browserElement_inproc_TargetBlank.html]
 skip-if = (toolkit == 'gonk' && !debug)
 [test_browserElement_inproc_TargetTop.html]
 [test_browserElement_inproc_Titlechange.html]
 [test_browserElement_inproc_TopBarrier.html]
 [test_browserElement_inproc_VisibilityChange.html]
 [test_browserElement_inproc_XFrameOptions.html]
 [test_browserElement_inproc_XFrameOptionsAllowFrom.html]
 [test_browserElement_inproc_XFrameOptionsDeny.html]
 [test_browserElement_inproc_XFrameOptionsSameOrigin.html]
 [test_browserElement_oop_NextPaint.html]
-# Disabled until we fix bug 906096.
-[test_browserElement_inproc_SetInputMethodActive.html]
-disabled = bug 906096
-support-files = browserElement_SetInputMethodActive.js file_inputmethod.html
 # Disabled due to https://bugzilla.mozilla.org/show_bug.cgi?id=774100
 [test_browserElement_inproc_Reload.html]
 disabled = bug 774100
 # Disabled due to focus issues (no bug that I'm aware of)
 [test_browserElement_oop_KeyEvents.html]
 disabled =
 # Disable due to certificate issue (no bug that I'm aware of)
 [test_browserElement_inproc_ErrorSecurity.html]