Bug 971651 - Keyboard Should only send key press events to the webpage that uses the keyboard r=yxl
authorFabrice Desré <fabrice@mozilla.com>
Wed, 12 Feb 2014 22:29:50 -0800
changeset 169318 4665cf1d88d9f7f37c187acef8e0e4cde4b2b479
parent 169317 9ef8dec0d7c8853ef3b73c36bb23b07d239c432e
child 169319 be90be5717160cde66b4e9724e3017fc8f479a26
push id270
push userpvanderbeken@mozilla.com
push dateThu, 06 Mar 2014 09:24:21 +0000
reviewersyxl
bugs971651
milestone30.0a1
Bug 971651 - Keyboard Should only send key press events to the webpage that uses the keyboard r=yxl
dom/inputmethod/Keyboard.jsm
dom/inputmethod/MozKeyboard.js
--- a/dom/inputmethod/Keyboard.jsm
+++ b/dom/inputmethod/Keyboard.jsm
@@ -12,39 +12,47 @@ const Ci = Components.interfaces;
 
 Cu.import('resource://gre/modules/Services.jsm');
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 XPCOMUtils.defineLazyServiceGetter(this, "ppmm",
   "@mozilla.org/parentprocessmessagemanager;1", "nsIMessageBroadcaster");
 
 this.Keyboard = {
-  _messageManager: null,
+  _formMM: null,     // The current web page message manager.
+  _keyboardMM: null, // The keyboard app message manager.
   _messageNames: [
     'SetValue', 'RemoveFocus', 'SetSelectedOption', 'SetSelectedOptions',
     'SetSelectionRange', 'ReplaceSurroundingText', 'ShowInputMethodPicker',
     'SwitchToNextInputMethod', 'HideInputMethod',
     'GetText', 'SendKey', 'GetContext',
-    'SetComposition', 'EndComposition'
+    'SetComposition', 'EndComposition',
+    'Register', 'Unregister'
   ],
 
-  get messageManager() {
-    if (this._messageManager && !Cu.isDeadWrapper(this._messageManager))
-      return this._messageManager;
+  get formMM() {
+    if (this._formMM && !Cu.isDeadWrapper(this._formMM))
+      return this._formMM;
 
     return null;
   },
 
-  set messageManager(mm) {
-    this._messageManager = mm;
+  set formMM(mm) {
+    this._formMM = mm;
   },
 
-  sendAsyncMessage: function(name, data) {
+  sendToForm: function(name, data) {
     try {
-      this.messageManager.sendAsyncMessage(name, data);
+      this.formMM.sendAsyncMessage(name, data);
+    } catch(e) { }
+  },
+
+  sendToKeyboard: function(name, data) {
+    try {
+      this._keyboardMM.sendAsyncMessage(name, data);
     } catch(e) { }
   },
 
   init: function keyboardInit() {
     Services.obs.addObserver(this, 'in-process-browser-or-app-frame-shown', false);
     Services.obs.addObserver(this, 'remote-browser-frame-shown', false);
     Services.obs.addObserver(this, 'oop-frameloader-crashed', false);
 
@@ -52,20 +60,20 @@ this.Keyboard = {
       ppmm.addMessageListener('Keyboard:' + name, this);
   },
 
   observe: function keyboardObserve(subject, topic, data) {
     let frameLoader = subject.QueryInterface(Ci.nsIFrameLoader);
     let mm = frameLoader.messageManager;
 
     if (topic == 'oop-frameloader-crashed') {
-      if (this.messageManager == mm) {
+      if (this.formMM == mm) {
         // The application has been closed unexpectingly. Let's tell the
         // keyboard app that the focus has been lost.
-        ppmm.broadcastAsyncMessage('Keyboard:FocusChange', { 'type': 'blur' });
+        this.sendToKeyboard('Keyboard:FocusChange', { 'type': 'blur' });
       }
     } else {
       this.initFormsFrameScript(mm);
     }
   },
 
   initFormsFrameScript: function(mm) {
     mm.addMessageListener('Forms:Input', this);
@@ -80,22 +88,24 @@ this.Keyboard = {
     mm.addMessageListener('Forms:GetContext:Result:OK', this);
     mm.addMessageListener('Forms:SetComposition:Result:OK', this);
     mm.addMessageListener('Forms:EndComposition:Result:OK', this);
   },
 
   receiveMessage: function keyboardReceiveMessage(msg) {
     // If we get a 'Keyboard:XXX' message, check that the sender has the
     // input permission.
+    let mm;
+    let isKeyboardRegistration = msg.name == "Keyboard:Register" ||
+                                 msg.name == "Keyboard:Unregister";
     if (msg.name.indexOf("Keyboard:") != -1) {
-      if (!this.messageManager) {
+      if (!this.formMM && !isKeyboardRegistration) {
         return;
       }
 
-      let mm;
       try {
         mm = msg.target.QueryInterface(Ci.nsIFrameLoaderOwner)
                        .frameLoader.messageManager;
       } catch(e) {
         mm = msg.target;
       }
 
       // That should never happen.
@@ -104,17 +114,18 @@ this.Keyboard = {
         return;
       }
 
       let testing = false;
       try {
         testing = Services.prefs.getBoolPref("dom.mozInputMethod.testing");
       } catch (e) {
       }
-      if (!testing && !mm.assertPermission("input")) {
+      if (!isKeyboardRegistration && !testing &&
+          !mm.assertPermission("input")) {
         dump("Keyboard message " + msg.name +
         " from a content process with no 'input' privileges.");
         return;
       }
     }
 
     switch (msg.name) {
       case 'Forms:Input':
@@ -169,24 +180,30 @@ this.Keyboard = {
         this.getContext(msg);
         break;
       case 'Keyboard:SetComposition':
         this.setComposition(msg);
         break;
       case 'Keyboard:EndComposition':
         this.endComposition(msg);
         break;
+      case 'Keyboard:Register':
+        this._keyboardMM = mm;
+        break;
+      case 'Keyboard:Unregister':
+        this._keyboardMM = null;
+        break;
     }
   },
 
   forwardEvent: function keyboardForwardEvent(newEventName, msg) {
-    this.messageManager = msg.target.QueryInterface(Ci.nsIFrameLoaderOwner)
-                             .frameLoader.messageManager;
+    this.formMM = msg.target.QueryInterface(Ci.nsIFrameLoaderOwner)
+                            .frameLoader.messageManager;
 
-    ppmm.broadcastAsyncMessage(newEventName, msg.data);
+    this.sendToKeyboard(newEventName, msg.data);
   },
 
   handleFocusChange: function keyboardHandleFocusChange(msg) {
     this.forwardEvent('Keyboard:FocusChange', msg);
 
     // Chrome event, used also to render value selectors; that's why we need
     // the info about choices / min / max here as well...
     this.sendChromeEvent({
@@ -195,86 +212,86 @@ this.Keyboard = {
       value: msg.data.value,
       choices: JSON.stringify(msg.data.choices),
       min: msg.data.min,
       max: msg.data.max
     });
   },
 
   setSelectedOption: function keyboardSetSelectedOption(msg) {
-    this.sendAsyncMessage('Forms:Select:Choice', msg.data);
+    this.sendToForm('Forms:Select:Choice', msg.data);
   },
 
   setSelectedOptions: function keyboardSetSelectedOptions(msg) {
-    this.sendAsyncMessage('Forms:Select:Choice', msg.data);
+    this.sendToForm('Forms:Select:Choice', msg.data);
   },
 
   setSelectionRange: function keyboardSetSelectionRange(msg) {
-    this.sendAsyncMessage('Forms:SetSelectionRange', msg.data);
+    this.sendToForm('Forms:SetSelectionRange', msg.data);
   },
 
   setValue: function keyboardSetValue(msg) {
-    this.sendAsyncMessage('Forms:Input:Value', msg.data);
+    this.sendToForm('Forms:Input:Value', msg.data);
   },
 
   removeFocus: function keyboardRemoveFocus() {
-    this.sendAsyncMessage('Forms:Select:Blur', {});
+    this.sendToForm('Forms:Select:Blur', {});
   },
 
   replaceSurroundingText: function keyboardReplaceSurroundingText(msg) {
-    this.sendAsyncMessage('Forms:ReplaceSurroundingText', msg.data);
+    this.sendToForm('Forms:ReplaceSurroundingText', msg.data);
   },
 
   showInputMethodPicker: function keyboardShowInputMethodPicker() {
     this.sendChromeEvent({
       type: "inputmethod-showall"
     });
   },
 
   switchToNextInputMethod: function keyboardSwitchToNextInputMethod() {
     this.sendChromeEvent({
       type: "inputmethod-next"
     });
   },
 
   getText: function keyboardGetText(msg) {
-    this.sendAsyncMessage('Forms:GetText', msg.data);
+    this.sendToForm('Forms:GetText', msg.data);
   },
 
   sendKey: function keyboardSendKey(msg) {
-    this.sendAsyncMessage('Forms:Input:SendKey', msg.data);
+    this.sendToForm('Forms:Input:SendKey', msg.data);
   },
 
   getContext: function keyboardGetContext(msg) {
     if (this._layouts) {
-      ppmm.broadcastAsyncMessage('Keyboard:LayoutsChange', this._layouts);
+      this.sendToKeyboard('Keyboard:LayoutsChange', this._layouts);
     }
 
-    this.sendAsyncMessage('Forms:GetContext', msg.data);
+    this.sendToForm('Forms:GetContext', msg.data);
   },
 
   setComposition: function keyboardSetComposition(msg) {
-    this.sendAsyncMessage('Forms:SetComposition', msg.data);
+    this.sendToForm('Forms:SetComposition', msg.data);
   },
 
   endComposition: function keyboardEndComposition(msg) {
-    this.sendAsyncMessage('Forms:EndComposition', msg.data);
+    this.sendToForm('Forms:EndComposition', msg.data);
   },
 
   /**
    * Get the number of keyboard layouts active from keyboard_manager
    */
   _layouts: null,
   setLayouts: function keyboardSetLayoutCount(layouts) {
     // The input method plugins may not have loaded yet,
     // cache the layouts so on init we can respond immediately instead
     // of going back and forth between keyboard_manager
     this._layouts = layouts;
 
-    ppmm.broadcastAsyncMessage('Keyboard:LayoutsChange', layouts);
+    this.sendToKeyboard('Keyboard:LayoutsChange', layouts);
   },
 
   sendChromeEvent: function(event) {
     let browser = Services.wm.getMostRecentWindow("navigator:browser");
     if (browser && browser.shell) {
       browser.shell.sendChromeEvent(event);;
     }
   }
--- a/dom/inputmethod/MozKeyboard.js
+++ b/dom/inputmethod/MozKeyboard.js
@@ -324,16 +324,17 @@ MozInputMethod.prototype = {
     Services.obs.addObserver(this, "inner-window-destroyed", false);
     cpmm.addMessageListener('Keyboard:FocusChange', this);
     cpmm.addMessageListener('Keyboard:SelectionChange', this);
     cpmm.addMessageListener('Keyboard:GetContext:Result:OK', this);
     cpmm.addMessageListener('Keyboard:LayoutsChange', this);
   },
 
   uninit: function mozInputMethodUninit() {
+    this.setActive(false);
     Services.obs.removeObserver(this, "inner-window-destroyed");
     cpmm.removeMessageListener('Keyboard:FocusChange', this);
     cpmm.removeMessageListener('Keyboard:SelectionChange', this);
     cpmm.removeMessageListener('Keyboard:GetContext:Result:OK', this);
     cpmm.removeMessageListener('Keyboard:LayoutsChange', this);
 
     this._window = null;
     this._mgmt = null;
@@ -423,19 +424,21 @@ MozInputMethod.prototype = {
 
     WindowMap.setActive(this._window, isActive);
 
     if (isActive) {
       // Activate current input method.
       // If there is already an active context, then this will trigger
       // a GetContext:Result:OK event, and we can initialize ourselves.
       // Otherwise silently ignored.
+      cpmm.sendAsyncMessage('Keyboard:Register', {});
       cpmm.sendAsyncMessage("Keyboard:GetContext", {});
     } else {
       // Deactive current input method.
+      cpmm.sendAsyncMessage('Keyboard:Unregister', {});
       if (this._inputcontext) {
         this.setInputContext(null);
       }
     }
   }
 };
 
  /**