dom/inputmethod/Keyboard.jsm

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

michael@0 1 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
michael@0 3 * You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 4
michael@0 5 'use strict';
michael@0 6
michael@0 7 this.EXPORTED_SYMBOLS = ['Keyboard'];
michael@0 8
michael@0 9 const Cu = Components.utils;
michael@0 10 const Cc = Components.classes;
michael@0 11 const Ci = Components.interfaces;
michael@0 12
michael@0 13 Cu.import('resource://gre/modules/Services.jsm');
michael@0 14 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
michael@0 15
michael@0 16 XPCOMUtils.defineLazyServiceGetter(this, "ppmm",
michael@0 17 "@mozilla.org/parentprocessmessagemanager;1", "nsIMessageBroadcaster");
michael@0 18
michael@0 19 XPCOMUtils.defineLazyModuleGetter(this, "SystemAppProxy",
michael@0 20 "resource://gre/modules/SystemAppProxy.jsm");
michael@0 21
michael@0 22 this.Keyboard = {
michael@0 23 _formMM: null, // The current web page message manager.
michael@0 24 _keyboardMM: null, // The keyboard app message manager.
michael@0 25 _systemMessageName: [
michael@0 26 'SetValue', 'RemoveFocus', 'SetSelectedOption', 'SetSelectedOptions'
michael@0 27 ],
michael@0 28
michael@0 29 _messageNames: [
michael@0 30 'SetSelectionRange', 'ReplaceSurroundingText', 'ShowInputMethodPicker',
michael@0 31 'SwitchToNextInputMethod', 'HideInputMethod',
michael@0 32 'GetText', 'SendKey', 'GetContext',
michael@0 33 'SetComposition', 'EndComposition',
michael@0 34 'Register', 'Unregister'
michael@0 35 ],
michael@0 36
michael@0 37 get formMM() {
michael@0 38 if (this._formMM && !Cu.isDeadWrapper(this._formMM))
michael@0 39 return this._formMM;
michael@0 40
michael@0 41 return null;
michael@0 42 },
michael@0 43
michael@0 44 set formMM(mm) {
michael@0 45 this._formMM = mm;
michael@0 46 },
michael@0 47
michael@0 48 sendToForm: function(name, data) {
michael@0 49 try {
michael@0 50 this.formMM.sendAsyncMessage(name, data);
michael@0 51 } catch(e) { }
michael@0 52 },
michael@0 53
michael@0 54 sendToKeyboard: function(name, data) {
michael@0 55 try {
michael@0 56 this._keyboardMM.sendAsyncMessage(name, data);
michael@0 57 } catch(e) { }
michael@0 58 },
michael@0 59
michael@0 60 init: function keyboardInit() {
michael@0 61 Services.obs.addObserver(this, 'inprocess-browser-shown', false);
michael@0 62 Services.obs.addObserver(this, 'remote-browser-shown', false);
michael@0 63 Services.obs.addObserver(this, 'oop-frameloader-crashed', false);
michael@0 64
michael@0 65 for (let name of this._messageNames) {
michael@0 66 ppmm.addMessageListener('Keyboard:' + name, this);
michael@0 67 }
michael@0 68
michael@0 69 for (let name of this._systemMessageName) {
michael@0 70 ppmm.addMessageListener('System:' + name, this);
michael@0 71 }
michael@0 72 },
michael@0 73
michael@0 74 observe: function keyboardObserve(subject, topic, data) {
michael@0 75 let frameLoader = subject.QueryInterface(Ci.nsIFrameLoader);
michael@0 76 let mm = frameLoader.messageManager;
michael@0 77
michael@0 78 if (topic == 'oop-frameloader-crashed') {
michael@0 79 if (this.formMM == mm) {
michael@0 80 // The application has been closed unexpectingly. Let's tell the
michael@0 81 // keyboard app that the focus has been lost.
michael@0 82 this.sendToKeyboard('Keyboard:FocusChange', { 'type': 'blur' });
michael@0 83 }
michael@0 84 } else {
michael@0 85 // Ignore notifications that aren't from a BrowserOrApp
michael@0 86 if (!frameLoader.ownerIsBrowserOrAppFrame) {
michael@0 87 return;
michael@0 88 }
michael@0 89 this.initFormsFrameScript(mm);
michael@0 90 }
michael@0 91 },
michael@0 92
michael@0 93 initFormsFrameScript: function(mm) {
michael@0 94 mm.addMessageListener('Forms:Input', this);
michael@0 95 mm.addMessageListener('Forms:SelectionChange', this);
michael@0 96 mm.addMessageListener('Forms:GetText:Result:OK', this);
michael@0 97 mm.addMessageListener('Forms:GetText:Result:Error', this);
michael@0 98 mm.addMessageListener('Forms:SetSelectionRange:Result:OK', this);
michael@0 99 mm.addMessageListener('Forms:SetSelectionRange:Result:Error', this);
michael@0 100 mm.addMessageListener('Forms:ReplaceSurroundingText:Result:OK', this);
michael@0 101 mm.addMessageListener('Forms:ReplaceSurroundingText:Result:Error', this);
michael@0 102 mm.addMessageListener('Forms:SendKey:Result:OK', this);
michael@0 103 mm.addMessageListener('Forms:SendKey:Result:Error', this);
michael@0 104 mm.addMessageListener('Forms:SequenceError', this);
michael@0 105 mm.addMessageListener('Forms:GetContext:Result:OK', this);
michael@0 106 mm.addMessageListener('Forms:SetComposition:Result:OK', this);
michael@0 107 mm.addMessageListener('Forms:EndComposition:Result:OK', this);
michael@0 108 },
michael@0 109
michael@0 110 receiveMessage: function keyboardReceiveMessage(msg) {
michael@0 111 // If we get a 'Keyboard:XXX'/'System:XXX' message, check that the sender
michael@0 112 // has the required permission.
michael@0 113 let mm;
michael@0 114 let isKeyboardRegistration = msg.name == "Keyboard:Register" ||
michael@0 115 msg.name == "Keyboard:Unregister";
michael@0 116 if (msg.name.indexOf("Keyboard:") === 0 ||
michael@0 117 msg.name.indexOf("System:") === 0) {
michael@0 118 if (!this.formMM && !isKeyboardRegistration) {
michael@0 119 return;
michael@0 120 }
michael@0 121
michael@0 122 try {
michael@0 123 mm = msg.target.QueryInterface(Ci.nsIFrameLoaderOwner)
michael@0 124 .frameLoader.messageManager;
michael@0 125 } catch(e) {
michael@0 126 mm = msg.target;
michael@0 127 }
michael@0 128
michael@0 129 // That should never happen.
michael@0 130 if (!mm) {
michael@0 131 dump("!! No message manager found for " + msg.name);
michael@0 132 return;
michael@0 133 }
michael@0 134
michael@0 135 let testing = false;
michael@0 136 try {
michael@0 137 testing = Services.prefs.getBoolPref("dom.mozInputMethod.testing");
michael@0 138 } catch (e) {
michael@0 139 }
michael@0 140
michael@0 141 let perm = (msg.name.indexOf("Keyboard:") === 0) ? "input"
michael@0 142 : "input-manage";
michael@0 143 if (!isKeyboardRegistration && !testing &&
michael@0 144 !mm.assertPermission(perm)) {
michael@0 145 dump("Keyboard message " + msg.name +
michael@0 146 " from a content process with no '" + perm + "' privileges.");
michael@0 147 return;
michael@0 148 }
michael@0 149 }
michael@0 150
michael@0 151 switch (msg.name) {
michael@0 152 case 'Forms:Input':
michael@0 153 this.handleFocusChange(msg);
michael@0 154 break;
michael@0 155 case 'Forms:SelectionChange':
michael@0 156 case 'Forms:GetText:Result:OK':
michael@0 157 case 'Forms:GetText:Result:Error':
michael@0 158 case 'Forms:SetSelectionRange:Result:OK':
michael@0 159 case 'Forms:ReplaceSurroundingText:Result:OK':
michael@0 160 case 'Forms:SendKey:Result:OK':
michael@0 161 case 'Forms:SendKey:Result:Error':
michael@0 162 case 'Forms:SequenceError':
michael@0 163 case 'Forms:GetContext:Result:OK':
michael@0 164 case 'Forms:SetComposition:Result:OK':
michael@0 165 case 'Forms:EndComposition:Result:OK':
michael@0 166 case 'Forms:SetSelectionRange:Result:Error':
michael@0 167 case 'Forms:ReplaceSurroundingText:Result:Error':
michael@0 168 let name = msg.name.replace(/^Forms/, 'Keyboard');
michael@0 169 this.forwardEvent(name, msg);
michael@0 170 break;
michael@0 171
michael@0 172 case 'System:SetValue':
michael@0 173 this.setValue(msg);
michael@0 174 break;
michael@0 175 case 'Keyboard:RemoveFocus':
michael@0 176 case 'System:RemoveFocus':
michael@0 177 this.removeFocus();
michael@0 178 break;
michael@0 179 case 'System:SetSelectedOption':
michael@0 180 this.setSelectedOption(msg);
michael@0 181 break;
michael@0 182 case 'System:SetSelectedOptions':
michael@0 183 this.setSelectedOption(msg);
michael@0 184 break;
michael@0 185 case 'Keyboard:SetSelectionRange':
michael@0 186 this.setSelectionRange(msg);
michael@0 187 break;
michael@0 188 case 'Keyboard:ReplaceSurroundingText':
michael@0 189 this.replaceSurroundingText(msg);
michael@0 190 break;
michael@0 191 case 'Keyboard:SwitchToNextInputMethod':
michael@0 192 this.switchToNextInputMethod();
michael@0 193 break;
michael@0 194 case 'Keyboard:ShowInputMethodPicker':
michael@0 195 this.showInputMethodPicker();
michael@0 196 break;
michael@0 197 case 'Keyboard:GetText':
michael@0 198 this.getText(msg);
michael@0 199 break;
michael@0 200 case 'Keyboard:SendKey':
michael@0 201 this.sendKey(msg);
michael@0 202 break;
michael@0 203 case 'Keyboard:GetContext':
michael@0 204 this.getContext(msg);
michael@0 205 break;
michael@0 206 case 'Keyboard:SetComposition':
michael@0 207 this.setComposition(msg);
michael@0 208 break;
michael@0 209 case 'Keyboard:EndComposition':
michael@0 210 this.endComposition(msg);
michael@0 211 break;
michael@0 212 case 'Keyboard:Register':
michael@0 213 this._keyboardMM = mm;
michael@0 214 break;
michael@0 215 case 'Keyboard:Unregister':
michael@0 216 this._keyboardMM = null;
michael@0 217 break;
michael@0 218 }
michael@0 219 },
michael@0 220
michael@0 221 forwardEvent: function keyboardForwardEvent(newEventName, msg) {
michael@0 222 this.formMM = msg.target.QueryInterface(Ci.nsIFrameLoaderOwner)
michael@0 223 .frameLoader.messageManager;
michael@0 224
michael@0 225 this.sendToKeyboard(newEventName, msg.data);
michael@0 226 },
michael@0 227
michael@0 228 handleFocusChange: function keyboardHandleFocusChange(msg) {
michael@0 229 this.forwardEvent('Keyboard:FocusChange', msg);
michael@0 230
michael@0 231 // Chrome event, used also to render value selectors; that's why we need
michael@0 232 // the info about choices / min / max here as well...
michael@0 233 SystemAppProxy.dispatchEvent({
michael@0 234 type: 'inputmethod-contextchange',
michael@0 235 inputType: msg.data.type,
michael@0 236 value: msg.data.value,
michael@0 237 choices: JSON.stringify(msg.data.choices),
michael@0 238 min: msg.data.min,
michael@0 239 max: msg.data.max
michael@0 240 });
michael@0 241 },
michael@0 242
michael@0 243 setSelectedOption: function keyboardSetSelectedOption(msg) {
michael@0 244 this.sendToForm('Forms:Select:Choice', msg.data);
michael@0 245 },
michael@0 246
michael@0 247 setSelectedOptions: function keyboardSetSelectedOptions(msg) {
michael@0 248 this.sendToForm('Forms:Select:Choice', msg.data);
michael@0 249 },
michael@0 250
michael@0 251 setSelectionRange: function keyboardSetSelectionRange(msg) {
michael@0 252 this.sendToForm('Forms:SetSelectionRange', msg.data);
michael@0 253 },
michael@0 254
michael@0 255 setValue: function keyboardSetValue(msg) {
michael@0 256 this.sendToForm('Forms:Input:Value', msg.data);
michael@0 257 },
michael@0 258
michael@0 259 removeFocus: function keyboardRemoveFocus() {
michael@0 260 this.sendToForm('Forms:Select:Blur', {});
michael@0 261 },
michael@0 262
michael@0 263 replaceSurroundingText: function keyboardReplaceSurroundingText(msg) {
michael@0 264 this.sendToForm('Forms:ReplaceSurroundingText', msg.data);
michael@0 265 },
michael@0 266
michael@0 267 showInputMethodPicker: function keyboardShowInputMethodPicker() {
michael@0 268 SystemAppProxy.dispatchEvent({
michael@0 269 type: "inputmethod-showall"
michael@0 270 });
michael@0 271 },
michael@0 272
michael@0 273 switchToNextInputMethod: function keyboardSwitchToNextInputMethod() {
michael@0 274 SystemAppProxy.dispatchEvent({
michael@0 275 type: "inputmethod-next"
michael@0 276 });
michael@0 277 },
michael@0 278
michael@0 279 getText: function keyboardGetText(msg) {
michael@0 280 this.sendToForm('Forms:GetText', msg.data);
michael@0 281 },
michael@0 282
michael@0 283 sendKey: function keyboardSendKey(msg) {
michael@0 284 this.sendToForm('Forms:Input:SendKey', msg.data);
michael@0 285 },
michael@0 286
michael@0 287 getContext: function keyboardGetContext(msg) {
michael@0 288 if (this._layouts) {
michael@0 289 this.sendToKeyboard('Keyboard:LayoutsChange', this._layouts);
michael@0 290 }
michael@0 291
michael@0 292 this.sendToForm('Forms:GetContext', msg.data);
michael@0 293 },
michael@0 294
michael@0 295 setComposition: function keyboardSetComposition(msg) {
michael@0 296 this.sendToForm('Forms:SetComposition', msg.data);
michael@0 297 },
michael@0 298
michael@0 299 endComposition: function keyboardEndComposition(msg) {
michael@0 300 this.sendToForm('Forms:EndComposition', msg.data);
michael@0 301 },
michael@0 302
michael@0 303 /**
michael@0 304 * Get the number of keyboard layouts active from keyboard_manager
michael@0 305 */
michael@0 306 _layouts: null,
michael@0 307 setLayouts: function keyboardSetLayoutCount(layouts) {
michael@0 308 // The input method plugins may not have loaded yet,
michael@0 309 // cache the layouts so on init we can respond immediately instead
michael@0 310 // of going back and forth between keyboard_manager
michael@0 311 this._layouts = layouts;
michael@0 312
michael@0 313 this.sendToKeyboard('Keyboard:LayoutsChange', layouts);
michael@0 314 }
michael@0 315 };
michael@0 316
michael@0 317 this.Keyboard.init();

mercurial