1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/dom/inputmethod/Keyboard.jsm Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,317 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this file, 1.6 + * You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +'use strict'; 1.9 + 1.10 +this.EXPORTED_SYMBOLS = ['Keyboard']; 1.11 + 1.12 +const Cu = Components.utils; 1.13 +const Cc = Components.classes; 1.14 +const Ci = Components.interfaces; 1.15 + 1.16 +Cu.import('resource://gre/modules/Services.jsm'); 1.17 +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); 1.18 + 1.19 +XPCOMUtils.defineLazyServiceGetter(this, "ppmm", 1.20 + "@mozilla.org/parentprocessmessagemanager;1", "nsIMessageBroadcaster"); 1.21 + 1.22 +XPCOMUtils.defineLazyModuleGetter(this, "SystemAppProxy", 1.23 + "resource://gre/modules/SystemAppProxy.jsm"); 1.24 + 1.25 +this.Keyboard = { 1.26 + _formMM: null, // The current web page message manager. 1.27 + _keyboardMM: null, // The keyboard app message manager. 1.28 + _systemMessageName: [ 1.29 + 'SetValue', 'RemoveFocus', 'SetSelectedOption', 'SetSelectedOptions' 1.30 + ], 1.31 + 1.32 + _messageNames: [ 1.33 + 'SetSelectionRange', 'ReplaceSurroundingText', 'ShowInputMethodPicker', 1.34 + 'SwitchToNextInputMethod', 'HideInputMethod', 1.35 + 'GetText', 'SendKey', 'GetContext', 1.36 + 'SetComposition', 'EndComposition', 1.37 + 'Register', 'Unregister' 1.38 + ], 1.39 + 1.40 + get formMM() { 1.41 + if (this._formMM && !Cu.isDeadWrapper(this._formMM)) 1.42 + return this._formMM; 1.43 + 1.44 + return null; 1.45 + }, 1.46 + 1.47 + set formMM(mm) { 1.48 + this._formMM = mm; 1.49 + }, 1.50 + 1.51 + sendToForm: function(name, data) { 1.52 + try { 1.53 + this.formMM.sendAsyncMessage(name, data); 1.54 + } catch(e) { } 1.55 + }, 1.56 + 1.57 + sendToKeyboard: function(name, data) { 1.58 + try { 1.59 + this._keyboardMM.sendAsyncMessage(name, data); 1.60 + } catch(e) { } 1.61 + }, 1.62 + 1.63 + init: function keyboardInit() { 1.64 + Services.obs.addObserver(this, 'inprocess-browser-shown', false); 1.65 + Services.obs.addObserver(this, 'remote-browser-shown', false); 1.66 + Services.obs.addObserver(this, 'oop-frameloader-crashed', false); 1.67 + 1.68 + for (let name of this._messageNames) { 1.69 + ppmm.addMessageListener('Keyboard:' + name, this); 1.70 + } 1.71 + 1.72 + for (let name of this._systemMessageName) { 1.73 + ppmm.addMessageListener('System:' + name, this); 1.74 + } 1.75 + }, 1.76 + 1.77 + observe: function keyboardObserve(subject, topic, data) { 1.78 + let frameLoader = subject.QueryInterface(Ci.nsIFrameLoader); 1.79 + let mm = frameLoader.messageManager; 1.80 + 1.81 + if (topic == 'oop-frameloader-crashed') { 1.82 + if (this.formMM == mm) { 1.83 + // The application has been closed unexpectingly. Let's tell the 1.84 + // keyboard app that the focus has been lost. 1.85 + this.sendToKeyboard('Keyboard:FocusChange', { 'type': 'blur' }); 1.86 + } 1.87 + } else { 1.88 + // Ignore notifications that aren't from a BrowserOrApp 1.89 + if (!frameLoader.ownerIsBrowserOrAppFrame) { 1.90 + return; 1.91 + } 1.92 + this.initFormsFrameScript(mm); 1.93 + } 1.94 + }, 1.95 + 1.96 + initFormsFrameScript: function(mm) { 1.97 + mm.addMessageListener('Forms:Input', this); 1.98 + mm.addMessageListener('Forms:SelectionChange', this); 1.99 + mm.addMessageListener('Forms:GetText:Result:OK', this); 1.100 + mm.addMessageListener('Forms:GetText:Result:Error', this); 1.101 + mm.addMessageListener('Forms:SetSelectionRange:Result:OK', this); 1.102 + mm.addMessageListener('Forms:SetSelectionRange:Result:Error', this); 1.103 + mm.addMessageListener('Forms:ReplaceSurroundingText:Result:OK', this); 1.104 + mm.addMessageListener('Forms:ReplaceSurroundingText:Result:Error', this); 1.105 + mm.addMessageListener('Forms:SendKey:Result:OK', this); 1.106 + mm.addMessageListener('Forms:SendKey:Result:Error', this); 1.107 + mm.addMessageListener('Forms:SequenceError', this); 1.108 + mm.addMessageListener('Forms:GetContext:Result:OK', this); 1.109 + mm.addMessageListener('Forms:SetComposition:Result:OK', this); 1.110 + mm.addMessageListener('Forms:EndComposition:Result:OK', this); 1.111 + }, 1.112 + 1.113 + receiveMessage: function keyboardReceiveMessage(msg) { 1.114 + // If we get a 'Keyboard:XXX'/'System:XXX' message, check that the sender 1.115 + // has the required permission. 1.116 + let mm; 1.117 + let isKeyboardRegistration = msg.name == "Keyboard:Register" || 1.118 + msg.name == "Keyboard:Unregister"; 1.119 + if (msg.name.indexOf("Keyboard:") === 0 || 1.120 + msg.name.indexOf("System:") === 0) { 1.121 + if (!this.formMM && !isKeyboardRegistration) { 1.122 + return; 1.123 + } 1.124 + 1.125 + try { 1.126 + mm = msg.target.QueryInterface(Ci.nsIFrameLoaderOwner) 1.127 + .frameLoader.messageManager; 1.128 + } catch(e) { 1.129 + mm = msg.target; 1.130 + } 1.131 + 1.132 + // That should never happen. 1.133 + if (!mm) { 1.134 + dump("!! No message manager found for " + msg.name); 1.135 + return; 1.136 + } 1.137 + 1.138 + let testing = false; 1.139 + try { 1.140 + testing = Services.prefs.getBoolPref("dom.mozInputMethod.testing"); 1.141 + } catch (e) { 1.142 + } 1.143 + 1.144 + let perm = (msg.name.indexOf("Keyboard:") === 0) ? "input" 1.145 + : "input-manage"; 1.146 + if (!isKeyboardRegistration && !testing && 1.147 + !mm.assertPermission(perm)) { 1.148 + dump("Keyboard message " + msg.name + 1.149 + " from a content process with no '" + perm + "' privileges."); 1.150 + return; 1.151 + } 1.152 + } 1.153 + 1.154 + switch (msg.name) { 1.155 + case 'Forms:Input': 1.156 + this.handleFocusChange(msg); 1.157 + break; 1.158 + case 'Forms:SelectionChange': 1.159 + case 'Forms:GetText:Result:OK': 1.160 + case 'Forms:GetText:Result:Error': 1.161 + case 'Forms:SetSelectionRange:Result:OK': 1.162 + case 'Forms:ReplaceSurroundingText:Result:OK': 1.163 + case 'Forms:SendKey:Result:OK': 1.164 + case 'Forms:SendKey:Result:Error': 1.165 + case 'Forms:SequenceError': 1.166 + case 'Forms:GetContext:Result:OK': 1.167 + case 'Forms:SetComposition:Result:OK': 1.168 + case 'Forms:EndComposition:Result:OK': 1.169 + case 'Forms:SetSelectionRange:Result:Error': 1.170 + case 'Forms:ReplaceSurroundingText:Result:Error': 1.171 + let name = msg.name.replace(/^Forms/, 'Keyboard'); 1.172 + this.forwardEvent(name, msg); 1.173 + break; 1.174 + 1.175 + case 'System:SetValue': 1.176 + this.setValue(msg); 1.177 + break; 1.178 + case 'Keyboard:RemoveFocus': 1.179 + case 'System:RemoveFocus': 1.180 + this.removeFocus(); 1.181 + break; 1.182 + case 'System:SetSelectedOption': 1.183 + this.setSelectedOption(msg); 1.184 + break; 1.185 + case 'System:SetSelectedOptions': 1.186 + this.setSelectedOption(msg); 1.187 + break; 1.188 + case 'Keyboard:SetSelectionRange': 1.189 + this.setSelectionRange(msg); 1.190 + break; 1.191 + case 'Keyboard:ReplaceSurroundingText': 1.192 + this.replaceSurroundingText(msg); 1.193 + break; 1.194 + case 'Keyboard:SwitchToNextInputMethod': 1.195 + this.switchToNextInputMethod(); 1.196 + break; 1.197 + case 'Keyboard:ShowInputMethodPicker': 1.198 + this.showInputMethodPicker(); 1.199 + break; 1.200 + case 'Keyboard:GetText': 1.201 + this.getText(msg); 1.202 + break; 1.203 + case 'Keyboard:SendKey': 1.204 + this.sendKey(msg); 1.205 + break; 1.206 + case 'Keyboard:GetContext': 1.207 + this.getContext(msg); 1.208 + break; 1.209 + case 'Keyboard:SetComposition': 1.210 + this.setComposition(msg); 1.211 + break; 1.212 + case 'Keyboard:EndComposition': 1.213 + this.endComposition(msg); 1.214 + break; 1.215 + case 'Keyboard:Register': 1.216 + this._keyboardMM = mm; 1.217 + break; 1.218 + case 'Keyboard:Unregister': 1.219 + this._keyboardMM = null; 1.220 + break; 1.221 + } 1.222 + }, 1.223 + 1.224 + forwardEvent: function keyboardForwardEvent(newEventName, msg) { 1.225 + this.formMM = msg.target.QueryInterface(Ci.nsIFrameLoaderOwner) 1.226 + .frameLoader.messageManager; 1.227 + 1.228 + this.sendToKeyboard(newEventName, msg.data); 1.229 + }, 1.230 + 1.231 + handleFocusChange: function keyboardHandleFocusChange(msg) { 1.232 + this.forwardEvent('Keyboard:FocusChange', msg); 1.233 + 1.234 + // Chrome event, used also to render value selectors; that's why we need 1.235 + // the info about choices / min / max here as well... 1.236 + SystemAppProxy.dispatchEvent({ 1.237 + type: 'inputmethod-contextchange', 1.238 + inputType: msg.data.type, 1.239 + value: msg.data.value, 1.240 + choices: JSON.stringify(msg.data.choices), 1.241 + min: msg.data.min, 1.242 + max: msg.data.max 1.243 + }); 1.244 + }, 1.245 + 1.246 + setSelectedOption: function keyboardSetSelectedOption(msg) { 1.247 + this.sendToForm('Forms:Select:Choice', msg.data); 1.248 + }, 1.249 + 1.250 + setSelectedOptions: function keyboardSetSelectedOptions(msg) { 1.251 + this.sendToForm('Forms:Select:Choice', msg.data); 1.252 + }, 1.253 + 1.254 + setSelectionRange: function keyboardSetSelectionRange(msg) { 1.255 + this.sendToForm('Forms:SetSelectionRange', msg.data); 1.256 + }, 1.257 + 1.258 + setValue: function keyboardSetValue(msg) { 1.259 + this.sendToForm('Forms:Input:Value', msg.data); 1.260 + }, 1.261 + 1.262 + removeFocus: function keyboardRemoveFocus() { 1.263 + this.sendToForm('Forms:Select:Blur', {}); 1.264 + }, 1.265 + 1.266 + replaceSurroundingText: function keyboardReplaceSurroundingText(msg) { 1.267 + this.sendToForm('Forms:ReplaceSurroundingText', msg.data); 1.268 + }, 1.269 + 1.270 + showInputMethodPicker: function keyboardShowInputMethodPicker() { 1.271 + SystemAppProxy.dispatchEvent({ 1.272 + type: "inputmethod-showall" 1.273 + }); 1.274 + }, 1.275 + 1.276 + switchToNextInputMethod: function keyboardSwitchToNextInputMethod() { 1.277 + SystemAppProxy.dispatchEvent({ 1.278 + type: "inputmethod-next" 1.279 + }); 1.280 + }, 1.281 + 1.282 + getText: function keyboardGetText(msg) { 1.283 + this.sendToForm('Forms:GetText', msg.data); 1.284 + }, 1.285 + 1.286 + sendKey: function keyboardSendKey(msg) { 1.287 + this.sendToForm('Forms:Input:SendKey', msg.data); 1.288 + }, 1.289 + 1.290 + getContext: function keyboardGetContext(msg) { 1.291 + if (this._layouts) { 1.292 + this.sendToKeyboard('Keyboard:LayoutsChange', this._layouts); 1.293 + } 1.294 + 1.295 + this.sendToForm('Forms:GetContext', msg.data); 1.296 + }, 1.297 + 1.298 + setComposition: function keyboardSetComposition(msg) { 1.299 + this.sendToForm('Forms:SetComposition', msg.data); 1.300 + }, 1.301 + 1.302 + endComposition: function keyboardEndComposition(msg) { 1.303 + this.sendToForm('Forms:EndComposition', msg.data); 1.304 + }, 1.305 + 1.306 + /** 1.307 + * Get the number of keyboard layouts active from keyboard_manager 1.308 + */ 1.309 + _layouts: null, 1.310 + setLayouts: function keyboardSetLayoutCount(layouts) { 1.311 + // The input method plugins may not have loaded yet, 1.312 + // cache the layouts so on init we can respond immediately instead 1.313 + // of going back and forth between keyboard_manager 1.314 + this._layouts = layouts; 1.315 + 1.316 + this.sendToKeyboard('Keyboard:LayoutsChange', layouts); 1.317 + } 1.318 +}; 1.319 + 1.320 +this.Keyboard.init();