Bug 1020726 - Do not store the active input iframe in a global variable. r=fabrice, a=2.0+
authorYuan Xulei <xyuan@mozilla.com>
Fri, 20 Jun 2014 09:48:49 -0700
changeset 207929 00c4fd216e93971c532c9f024735cfcd6bb79bae
parent 207928 002aee8237a327c0a4d2549ca6fe49e10ae92775
child 207930 1f50cc1bb505f84a34cd9a7fc7d42cebd552d58b
push id3741
push userasasaki@mozilla.com
push dateMon, 21 Jul 2014 20:25:18 +0000
treeherdermozilla-beta@4d6f46f5af68 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfabrice, 2
bugs1020726
milestone32.0a2
Bug 1020726 - Do not store the active input iframe in a global variable. r=fabrice, a=2.0+
dom/browser-element/BrowserElementParent.jsm
dom/browser-element/mochitest/browserElement_SetInputMethodActive.js
--- a/dom/browser-element/BrowserElementParent.jsm
+++ b/dom/browser-element/BrowserElementParent.jsm
@@ -60,20 +60,16 @@ function visibilityChangeHandler(e) {
 }
 
 this.BrowserElementParentBuilder = {
   create: function create(frameLoader, hasRemoteFrame, isPendingFrame) {
     return new BrowserElementParent(frameLoader, hasRemoteFrame);
   }
 }
 
-
-// The active input method iframe.
-let activeInputFrame = null;
-
 function BrowserElementParent(frameLoader, hasRemoteFrame, isPendingFrame) {
   debug("Creating new BrowserElementParent object for " + frameLoader);
   this._domRequestCounter = 0;
   this._pendingDOMRequests = {};
   this._pendingSetInputMethodActive = [];
   this._hasRemoteFrame = hasRemoteFrame;
   this._nextPaintListeners = [];
 
@@ -108,22 +104,17 @@ function BrowserElementParent(frameLoade
       if (self._isAlive()) {
         fn.apply(self, arguments);
       }
     };
   };
 
   let defineDOMRequestMethod = function(domName, msgName) {
     XPCNativeWrapper.unwrap(self._frameElement)[domName] = function() {
-      if (!self._mm) {
-        return self._queueDOMRequest;
-      }
-      if (self._isAlive()) {
-        return self._sendDOMRequest(msgName);
-      }
+      return self._sendDOMRequest(msgName);
     };
   }
 
   // Define methods on the frame element.
   defineNoReturnMethod('setVisible', this._setVisible);
   defineDOMRequestMethod('getVisible', 'get-visible');
   defineNoReturnMethod('sendMouseEvent', this._sendMouseEvent);
 
@@ -337,23 +328,16 @@ BrowserElementParent.prototype = {
       return false;
     }
     return true;
   },
 
   _recvHello: function() {
     debug("recvHello");
 
-    this._ready = true;
-
-    // Handle pending SetInputMethodActive request.
-    while (this._pendingSetInputMethodActive.length > 0) {
-      this._setInputMethodActive(this._pendingSetInputMethodActive.shift());
-    }
-
     // Inform our child if our owner element's document is invisible.  Note
     // that we must do so here, rather than in the BrowserElementParent
     // constructor, because the BrowserElementChild may not be initialized when
     // we run our constructor.
     if (this._window.document.hidden) {
       this._ownerVisibilityChange();
     }
 
@@ -477,60 +461,46 @@ BrowserElementParent.prototype = {
     }
 
     return new this._window.Event('mozbrowser' + evtName,
                                   { bubbles: true,
                                     cancelable: cancelable });
   },
 
   /**
-   * If remote frame haven't been set up, we enqueue a function that get a
-   * DOMRequest until the remote frame is ready and return another DOMRequest
-   * to caller. When we get the real DOMRequest, we will help forward the
-   * success/error callback to the DOMRequest that caller got.
-   */
-  _queueDOMRequest: function(msgName, args) {
-    if (!this._pendingAPICalls) {
-      return;
-    }
-
-    let req = Services.DOMRequest.createRequest(this._window);
-    let self = this;
-    let getRealDOMRequest = function() {
-      let realReq = self._sendDOMRequest(msgName, args);
-      realReq.onsuccess = function(v) {
-        Services.DOMRequest.fireSuccess(req, v);
-      };
-      realReq.onerror = function(v) {
-        Services.DOMRequest.fireError(req, v);
-      };
-    };
-    this._pendingAPICalls.push(getRealDOMRequest);
-    return req;
-  },
-
-  /**
    * Kick off a DOMRequest in the child process.
    *
    * We'll fire an event called |msgName| on the child process, passing along
    * an object with two fields:
    *
    *  - id:  the ID of this request.
    *  - arg: arguments to pass to the child along with this request.
    *
    * We expect the child to pass the ID back to us upon completion of the
    * request.  See _gotDOMRequestResult.
    */
   _sendDOMRequest: function(msgName, args) {
     let id = 'req_' + this._domRequestCounter++;
     let req = Services.DOMRequest.createRequest(this._window);
-    if (this._sendAsyncMsg(msgName, {id: id, args: args})) {
-      this._pendingDOMRequests[id] = req;
+    let self = this;
+    let send = function() {
+      if (!self._isAlive()) {
+        return;
+      }
+      if (self._sendAsyncMsg(msgName, {id: id, args: args})) {
+        self._pendingDOMRequests[id] = req;
+      } else {
+        Services.DOMRequest.fireErrorAsync(req, "fail");
+      }
+    };
+    if (this._mm) {
+      send();
     } else {
-      Services.DOMRequest.fireErrorAsync(req, "fail");
+      // Child haven't been loaded.
+      this._pendingAPICalls.push(send);
     }
     return req;
   },
 
   /**
    * Called when the child process finishes handling a DOMRequest.  data.json
    * must have the fields [id, successRv], if the DOMRequest was successful, or
    * [id, errorMsg], if the request was not successful.
@@ -726,23 +696,16 @@ BrowserElementParent.prototype = {
     let height = parseInt(_height);
     let mimeType = (typeof _mimeType === 'string') ?
       _mimeType.trim().toLowerCase() : 'image/jpeg';
     if (isNaN(width) || isNaN(height) || width < 0 || height < 0) {
       throw Components.Exception("Invalid argument",
                                  Cr.NS_ERROR_INVALID_ARG);
     }
 
-    if (!this._mm) {
-      // Child haven't been loaded.
-      return this._queueDOMRequest('get-screenshot',
-                                   {width: width, height: height,
-                                    mimeType: mimeType});
-    }
-
     return this._sendDOMRequest('get-screenshot',
                                 {width: width, height: height,
                                  mimeType: mimeType});
   },
 
   _recvNextPaint: function(data) {
     let listeners = this._nextPaintListeners;
     this._nextPaintListeners = [];
@@ -795,60 +758,18 @@ BrowserElementParent.prototype = {
   },
 
   _setInputMethodActive: function(isActive) {
     if (typeof isActive !== 'boolean') {
       throw Components.Exception("Invalid argument",
                                  Cr.NS_ERROR_INVALID_ARG);
     }
 
-    // Wait until browserElementChild is initialized.
-    if (!this._ready) {
-      this._pendingSetInputMethodActive.push(isActive);
-      return;
-    }
-
-    let req = Services.DOMRequest.createRequest(this._window);
-
-    // Deactivate the old input method if needed.
-    if (activeInputFrame && isActive) {
-      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);
-        return req;
-      }
-
-      let reqOld = XPCNativeWrapper.unwrap(activeInputFrame)
-                                   .setInputMethodActive(false);
-
-      // We wan't to continue regardless whether this req succeeded
-      reqOld.onsuccess = reqOld.onerror = function() {
-        activeInputFrame = null;
-        this._sendSetInputMethodActiveDOMRequest(req, isActive);
-      }.bind(this);
-    } else {
-      this._sendSetInputMethodActiveDOMRequest(req, isActive);
-    }
-    return req;
-  },
-
-  _sendSetInputMethodActiveDOMRequest: function(req, isActive) {
-    let id = 'req_' + this._domRequestCounter++;
-    let data = {
-      id : id,
-      args: { isActive: isActive }
-    };
-    if (this._sendAsyncMsg('set-input-method-active', data)) {
-      activeInputFrame = this._frameElement;
-      this._pendingDOMRequests[id] = req;
-    } else {
-      Services.DOMRequest.fireErrorAsync(req, 'fail');
-    }
+    return this._sendDOMRequest('set-input-method-active',
+                                {isActive: isActive});
   },
 
   _fireKeyEvent: function(data) {
     let evt = this._window.document.createEvent("KeyboardEvent");
     evt.initKeyEvent(data.json.type, true, true, this._window,
                      false, false, false, false, // modifiers
                      data.json.keyCode,
                      data.json.charCode);
--- a/dom/browser-element/mochitest/browserElement_SetInputMethodActive.js
+++ b/dom/browser-element/mochitest/browserElement_SetInputMethodActive.js
@@ -167,16 +167,23 @@ function next(msg) {
       // Do nothing and wait for the input to show up in input frame.
       break;
 
     case 2:
       is(from, 'input', 'Message sequence unexpected (2).');
       is(value, '#0hello',
          'Failed to get correct input from the first iframe.');
 
+      let req0 = gFrames[0].setInputMethodActive(false);
+      req0.onsuccess = function() {
+        ok(true, 'setInputMethodActive succeeded (0).');
+      };
+      req0.onerror = function() {
+        ok(false, 'setInputMethodActive failed (0): ' + this.error.name);
+      };
       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;