dom/inputmethod/MozKeyboard.js

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 const Cc = Components.classes;
michael@0 8 const Ci = Components.interfaces;
michael@0 9 const Cu = Components.utils;
michael@0 10
michael@0 11 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
michael@0 12 Cu.import("resource://gre/modules/Services.jsm");
michael@0 13 Cu.import("resource://gre/modules/DOMRequestHelper.jsm");
michael@0 14
michael@0 15 XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
michael@0 16 "@mozilla.org/childprocessmessagemanager;1", "nsIMessageSender");
michael@0 17
michael@0 18 XPCOMUtils.defineLazyServiceGetter(this, "tm",
michael@0 19 "@mozilla.org/thread-manager;1", "nsIThreadManager");
michael@0 20
michael@0 21 /*
michael@0 22 * A WeakMap to map input method iframe window to its active status.
michael@0 23 */
michael@0 24 let WindowMap = {
michael@0 25 // WeakMap of <window, boolean> pairs.
michael@0 26 _map: null,
michael@0 27
michael@0 28 /*
michael@0 29 * Check if the given window is active.
michael@0 30 */
michael@0 31 isActive: function(win) {
michael@0 32 if (!this._map || !win) {
michael@0 33 return false;
michael@0 34 }
michael@0 35 return this._map.get(win, false);
michael@0 36 },
michael@0 37
michael@0 38 /*
michael@0 39 * Set the active status of the given window.
michael@0 40 */
michael@0 41 setActive: function(win, isActive) {
michael@0 42 if (!win) {
michael@0 43 return;
michael@0 44 }
michael@0 45 if (!this._map) {
michael@0 46 this._map = new WeakMap();
michael@0 47 }
michael@0 48 this._map.set(win, isActive);
michael@0 49 }
michael@0 50 };
michael@0 51
michael@0 52 /**
michael@0 53 * ==============================================
michael@0 54 * InputMethodManager
michael@0 55 * ==============================================
michael@0 56 */
michael@0 57 function MozInputMethodManager(win) {
michael@0 58 this._window = win;
michael@0 59 }
michael@0 60
michael@0 61 MozInputMethodManager.prototype = {
michael@0 62 _supportsSwitching: false,
michael@0 63 _window: null,
michael@0 64
michael@0 65 classID: Components.ID("{7e9d7280-ef86-11e2-b778-0800200c9a66}"),
michael@0 66
michael@0 67 QueryInterface: XPCOMUtils.generateQI([]),
michael@0 68
michael@0 69 showAll: function() {
michael@0 70 if (!WindowMap.isActive(this._window)) {
michael@0 71 return;
michael@0 72 }
michael@0 73 cpmm.sendAsyncMessage('Keyboard:ShowInputMethodPicker', {});
michael@0 74 },
michael@0 75
michael@0 76 next: function() {
michael@0 77 if (!WindowMap.isActive(this._window)) {
michael@0 78 return;
michael@0 79 }
michael@0 80 cpmm.sendAsyncMessage('Keyboard:SwitchToNextInputMethod', {});
michael@0 81 },
michael@0 82
michael@0 83 supportsSwitching: function() {
michael@0 84 if (!WindowMap.isActive(this._window)) {
michael@0 85 return false;
michael@0 86 }
michael@0 87 return this._supportsSwitching;
michael@0 88 },
michael@0 89
michael@0 90 hide: function() {
michael@0 91 if (!WindowMap.isActive(this._window)) {
michael@0 92 return;
michael@0 93 }
michael@0 94 cpmm.sendAsyncMessage('Keyboard:RemoveFocus', {});
michael@0 95 }
michael@0 96 };
michael@0 97
michael@0 98 /**
michael@0 99 * ==============================================
michael@0 100 * InputMethod
michael@0 101 * ==============================================
michael@0 102 */
michael@0 103 function MozInputMethod() { }
michael@0 104
michael@0 105 MozInputMethod.prototype = {
michael@0 106 _inputcontext: null,
michael@0 107 _layouts: {},
michael@0 108 _window: null,
michael@0 109 _isSystem: false,
michael@0 110 _isKeyboard: true,
michael@0 111
michael@0 112 classID: Components.ID("{4607330d-e7d2-40a4-9eb8-43967eae0142}"),
michael@0 113
michael@0 114 QueryInterface: XPCOMUtils.generateQI([
michael@0 115 Ci.nsIDOMGlobalPropertyInitializer,
michael@0 116 Ci.nsIObserver,
michael@0 117 Ci.nsISupportsWeakReference
michael@0 118 ]),
michael@0 119
michael@0 120 init: function mozInputMethodInit(win) {
michael@0 121 this._window = win;
michael@0 122 this._mgmt = new MozInputMethodManager(win);
michael@0 123 this.innerWindowID = win.QueryInterface(Ci.nsIInterfaceRequestor)
michael@0 124 .getInterface(Ci.nsIDOMWindowUtils)
michael@0 125 .currentInnerWindowID;
michael@0 126
michael@0 127 Services.obs.addObserver(this, "inner-window-destroyed", false);
michael@0 128
michael@0 129 let principal = win.document.nodePrincipal;
michael@0 130 let perm = Services.perms.testExactPermissionFromPrincipal(principal,
michael@0 131 "input-manage");
michael@0 132 if (perm === Ci.nsIPermissionManager.ALLOW_ACTION) {
michael@0 133 this._isSystem = true;
michael@0 134 }
michael@0 135
michael@0 136 // Check if we can use keyboard related APIs.
michael@0 137 let testing = false;
michael@0 138 try {
michael@0 139 testing = Services.prefs.getBoolPref("dom.mozInputMethod.testing");
michael@0 140 } catch (e) {
michael@0 141 }
michael@0 142 perm = Services.perms.testExactPermissionFromPrincipal(principal, "input");
michael@0 143 if (!testing && perm !== Ci.nsIPermissionManager.ALLOW_ACTION) {
michael@0 144 this._isKeyboard = false;
michael@0 145 return;
michael@0 146 }
michael@0 147
michael@0 148 cpmm.addWeakMessageListener('Keyboard:FocusChange', this);
michael@0 149 cpmm.addWeakMessageListener('Keyboard:SelectionChange', this);
michael@0 150 cpmm.addWeakMessageListener('Keyboard:GetContext:Result:OK', this);
michael@0 151 cpmm.addWeakMessageListener('Keyboard:LayoutsChange', this);
michael@0 152 },
michael@0 153
michael@0 154 uninit: function mozInputMethodUninit() {
michael@0 155 this._window = null;
michael@0 156 this._mgmt = null;
michael@0 157 Services.obs.removeObserver(this, "inner-window-destroyed");
michael@0 158 if (!this._isKeyboard) {
michael@0 159 return;
michael@0 160 }
michael@0 161
michael@0 162 cpmm.removeWeakMessageListener('Keyboard:FocusChange', this);
michael@0 163 cpmm.removeWeakMessageListener('Keyboard:SelectionChange', this);
michael@0 164 cpmm.removeWeakMessageListener('Keyboard:GetContext:Result:OK', this);
michael@0 165 cpmm.removeWeakMessageListener('Keyboard:LayoutsChange', this);
michael@0 166 this.setActive(false);
michael@0 167 },
michael@0 168
michael@0 169 receiveMessage: function mozInputMethodReceiveMsg(msg) {
michael@0 170 if (!WindowMap.isActive(this._window)) {
michael@0 171 return;
michael@0 172 }
michael@0 173
michael@0 174 let json = msg.json;
michael@0 175
michael@0 176 switch(msg.name) {
michael@0 177 case 'Keyboard:FocusChange':
michael@0 178 if (json.type !== 'blur') {
michael@0 179 // XXX Bug 904339 could receive 'text' event twice
michael@0 180 this.setInputContext(json);
michael@0 181 }
michael@0 182 else {
michael@0 183 this.setInputContext(null);
michael@0 184 }
michael@0 185 break;
michael@0 186 case 'Keyboard:SelectionChange':
michael@0 187 if (this.inputcontext) {
michael@0 188 this._inputcontext.updateSelectionContext(json);
michael@0 189 }
michael@0 190 break;
michael@0 191 case 'Keyboard:GetContext:Result:OK':
michael@0 192 this.setInputContext(json);
michael@0 193 break;
michael@0 194 case 'Keyboard:LayoutsChange':
michael@0 195 this._layouts = json;
michael@0 196 break;
michael@0 197 }
michael@0 198 },
michael@0 199
michael@0 200 observe: function mozInputMethodObserve(subject, topic, data) {
michael@0 201 let wId = subject.QueryInterface(Ci.nsISupportsPRUint64).data;
michael@0 202 if (wId == this.innerWindowID)
michael@0 203 this.uninit();
michael@0 204 },
michael@0 205
michael@0 206 get mgmt() {
michael@0 207 return this._mgmt;
michael@0 208 },
michael@0 209
michael@0 210 get inputcontext() {
michael@0 211 if (!WindowMap.isActive(this._window)) {
michael@0 212 return null;
michael@0 213 }
michael@0 214 return this._inputcontext;
michael@0 215 },
michael@0 216
michael@0 217 set oninputcontextchange(handler) {
michael@0 218 this.__DOM_IMPL__.setEventHandler("oninputcontextchange", handler);
michael@0 219 },
michael@0 220
michael@0 221 get oninputcontextchange() {
michael@0 222 return this.__DOM_IMPL__.getEventHandler("oninputcontextchange");
michael@0 223 },
michael@0 224
michael@0 225 setInputContext: function mozKeyboardContextChange(data) {
michael@0 226 if (this._inputcontext) {
michael@0 227 this._inputcontext.destroy();
michael@0 228 this._inputcontext = null;
michael@0 229 this._mgmt._supportsSwitching = false;
michael@0 230 }
michael@0 231
michael@0 232 if (data) {
michael@0 233 this._mgmt._supportsSwitching = this._layouts[data.type] ?
michael@0 234 this._layouts[data.type] > 1 :
michael@0 235 false;
michael@0 236
michael@0 237 this._inputcontext = new MozInputContext(data);
michael@0 238 this._inputcontext.init(this._window);
michael@0 239 }
michael@0 240
michael@0 241 let event = new this._window.Event("inputcontextchange",
michael@0 242 Cu.cloneInto({}, this._window));
michael@0 243 this.__DOM_IMPL__.dispatchEvent(event);
michael@0 244 },
michael@0 245
michael@0 246 setActive: function mozInputMethodSetActive(isActive) {
michael@0 247 if (WindowMap.isActive(this._window) === isActive) {
michael@0 248 return;
michael@0 249 }
michael@0 250
michael@0 251 WindowMap.setActive(this._window, isActive);
michael@0 252
michael@0 253 if (isActive) {
michael@0 254 // Activate current input method.
michael@0 255 // If there is already an active context, then this will trigger
michael@0 256 // a GetContext:Result:OK event, and we can initialize ourselves.
michael@0 257 // Otherwise silently ignored.
michael@0 258 cpmm.sendAsyncMessage('Keyboard:Register', {});
michael@0 259 cpmm.sendAsyncMessage("Keyboard:GetContext", {});
michael@0 260 } else {
michael@0 261 // Deactive current input method.
michael@0 262 cpmm.sendAsyncMessage('Keyboard:Unregister', {});
michael@0 263 if (this._inputcontext) {
michael@0 264 this.setInputContext(null);
michael@0 265 }
michael@0 266 }
michael@0 267 },
michael@0 268
michael@0 269 setValue: function(value) {
michael@0 270 this._ensureIsSystem();
michael@0 271 cpmm.sendAsyncMessage('System:SetValue', {
michael@0 272 'value': value
michael@0 273 });
michael@0 274 },
michael@0 275
michael@0 276 setSelectedOption: function(index) {
michael@0 277 this._ensureIsSystem();
michael@0 278 cpmm.sendAsyncMessage('System:SetSelectedOption', {
michael@0 279 'index': index
michael@0 280 });
michael@0 281 },
michael@0 282
michael@0 283 setSelectedOptions: function(indexes) {
michael@0 284 this._ensureIsSystem();
michael@0 285 cpmm.sendAsyncMessage('System:SetSelectedOptions', {
michael@0 286 'indexes': indexes
michael@0 287 });
michael@0 288 },
michael@0 289
michael@0 290 removeFocus: function() {
michael@0 291 this._ensureIsSystem();
michael@0 292 cpmm.sendAsyncMessage('System:RemoveFocus', {});
michael@0 293 },
michael@0 294
michael@0 295 _ensureIsSystem: function() {
michael@0 296 if (!this._isSystem) {
michael@0 297 throw new this._window.DOMError("Security",
michael@0 298 "Should have 'input-manage' permssion.");
michael@0 299 }
michael@0 300 }
michael@0 301 };
michael@0 302
michael@0 303 /**
michael@0 304 * ==============================================
michael@0 305 * InputContext
michael@0 306 * ==============================================
michael@0 307 */
michael@0 308 function MozInputContext(ctx) {
michael@0 309 this._context = {
michael@0 310 inputtype: ctx.type,
michael@0 311 inputmode: ctx.inputmode,
michael@0 312 lang: ctx.lang,
michael@0 313 type: ["textarea", "contenteditable"].indexOf(ctx.type) > -1 ?
michael@0 314 ctx.type :
michael@0 315 "text",
michael@0 316 selectionStart: ctx.selectionStart,
michael@0 317 selectionEnd: ctx.selectionEnd,
michael@0 318 textBeforeCursor: ctx.textBeforeCursor,
michael@0 319 textAfterCursor: ctx.textAfterCursor
michael@0 320 };
michael@0 321
michael@0 322 this._contextId = ctx.contextId;
michael@0 323 }
michael@0 324
michael@0 325 MozInputContext.prototype = {
michael@0 326 __proto__: DOMRequestIpcHelper.prototype,
michael@0 327
michael@0 328 _window: null,
michael@0 329 _context: null,
michael@0 330 _contextId: -1,
michael@0 331
michael@0 332 classID: Components.ID("{1e38633d-d08b-4867-9944-afa5c648adb6}"),
michael@0 333
michael@0 334 QueryInterface: XPCOMUtils.generateQI([
michael@0 335 Ci.nsIObserver,
michael@0 336 Ci.nsISupportsWeakReference
michael@0 337 ]),
michael@0 338
michael@0 339 init: function ic_init(win) {
michael@0 340 this._window = win;
michael@0 341 this._utils = win.QueryInterface(Ci.nsIInterfaceRequestor)
michael@0 342 .getInterface(Ci.nsIDOMWindowUtils);
michael@0 343 this.initDOMRequestHelper(win,
michael@0 344 ["Keyboard:GetText:Result:OK",
michael@0 345 "Keyboard:GetText:Result:Error",
michael@0 346 "Keyboard:SetSelectionRange:Result:OK",
michael@0 347 "Keyboard:ReplaceSurroundingText:Result:OK",
michael@0 348 "Keyboard:SendKey:Result:OK",
michael@0 349 "Keyboard:SendKey:Result:Error",
michael@0 350 "Keyboard:SetComposition:Result:OK",
michael@0 351 "Keyboard:EndComposition:Result:OK",
michael@0 352 "Keyboard:SequenceError"]);
michael@0 353 },
michael@0 354
michael@0 355 destroy: function ic_destroy() {
michael@0 356 let self = this;
michael@0 357
michael@0 358 // All requests that are still pending need to be invalidated
michael@0 359 // because the context is no longer valid.
michael@0 360 this.forEachPromiseResolver(function(k) {
michael@0 361 self.takePromiseResolver(k).reject("InputContext got destroyed");
michael@0 362 });
michael@0 363 this.destroyDOMRequestHelper();
michael@0 364
michael@0 365 // A consuming application might still hold a cached version of
michael@0 366 // this object. After destroying all methods will throw because we
michael@0 367 // cannot create new promises anymore, but we still hold
michael@0 368 // (outdated) information in the context. So let's clear that out.
michael@0 369 for (var k in this._context) {
michael@0 370 if (this._context.hasOwnProperty(k)) {
michael@0 371 this._context[k] = null;
michael@0 372 }
michael@0 373 }
michael@0 374
michael@0 375 this._window = null;
michael@0 376 },
michael@0 377
michael@0 378 receiveMessage: function ic_receiveMessage(msg) {
michael@0 379 if (!msg || !msg.json) {
michael@0 380 dump('InputContext received message without data\n');
michael@0 381 return;
michael@0 382 }
michael@0 383
michael@0 384 let json = msg.json;
michael@0 385 let resolver = this.takePromiseResolver(json.requestId);
michael@0 386
michael@0 387 if (!resolver) {
michael@0 388 return;
michael@0 389 }
michael@0 390
michael@0 391 switch (msg.name) {
michael@0 392 case "Keyboard:SendKey:Result:OK":
michael@0 393 resolver.resolve();
michael@0 394 break;
michael@0 395 case "Keyboard:SendKey:Result:Error":
michael@0 396 resolver.reject(json.error);
michael@0 397 break;
michael@0 398 case "Keyboard:GetText:Result:OK":
michael@0 399 resolver.resolve(json.text);
michael@0 400 break;
michael@0 401 case "Keyboard:GetText:Result:Error":
michael@0 402 resolver.reject(json.error);
michael@0 403 break;
michael@0 404 case "Keyboard:SetSelectionRange:Result:OK":
michael@0 405 case "Keyboard:ReplaceSurroundingText:Result:OK":
michael@0 406 resolver.resolve(
michael@0 407 Cu.cloneInto(json.selectioninfo, this._window));
michael@0 408 break;
michael@0 409 case "Keyboard:SequenceError":
michael@0 410 // Occurs when a new element got focus, but the inputContext was
michael@0 411 // not invalidated yet...
michael@0 412 resolver.reject("InputContext has expired");
michael@0 413 break;
michael@0 414 case "Keyboard:SetComposition:Result:OK": // Fall through.
michael@0 415 case "Keyboard:EndComposition:Result:OK":
michael@0 416 resolver.resolve();
michael@0 417 break;
michael@0 418 default:
michael@0 419 dump("Could not find a handler for " + msg.name);
michael@0 420 resolver.reject();
michael@0 421 break;
michael@0 422 }
michael@0 423 },
michael@0 424
michael@0 425 updateSelectionContext: function ic_updateSelectionContext(ctx) {
michael@0 426 if (!this._context) {
michael@0 427 return;
michael@0 428 }
michael@0 429
michael@0 430 let selectionDirty = this._context.selectionStart !== ctx.selectionStart ||
michael@0 431 this._context.selectionEnd !== ctx.selectionEnd;
michael@0 432 let surroundDirty = this._context.textBeforeCursor !== ctx.textBeforeCursor ||
michael@0 433 this._context.textAfterCursor !== ctx.textAfterCursor;
michael@0 434
michael@0 435 this._context.selectionStart = ctx.selectionStart;
michael@0 436 this._context.selectionEnd = ctx.selectionEnd;
michael@0 437 this._context.textBeforeCursor = ctx.textBeforeCursor;
michael@0 438 this._context.textAfterCursor = ctx.textAfterCursor;
michael@0 439
michael@0 440 if (selectionDirty) {
michael@0 441 this._fireEvent("selectionchange", {
michael@0 442 selectionStart: ctx.selectionStart,
michael@0 443 selectionEnd: ctx.selectionEnd
michael@0 444 });
michael@0 445 }
michael@0 446
michael@0 447 if (surroundDirty) {
michael@0 448 this._fireEvent("surroundingtextchange", {
michael@0 449 beforeString: ctx.textBeforeCursor,
michael@0 450 afterString: ctx.textAfterCursor
michael@0 451 });
michael@0 452 }
michael@0 453 },
michael@0 454
michael@0 455 _fireEvent: function ic_fireEvent(eventName, aDetail) {
michael@0 456 let detail = {
michael@0 457 detail: aDetail
michael@0 458 };
michael@0 459
michael@0 460 let event = new this._window.Event(eventName,
michael@0 461 Cu.cloneInto(aDetail, this._window));
michael@0 462 this.__DOM_IMPL__.dispatchEvent(event);
michael@0 463 },
michael@0 464
michael@0 465 // tag name of the input field
michael@0 466 get type() {
michael@0 467 return this._context.type;
michael@0 468 },
michael@0 469
michael@0 470 // type of the input field
michael@0 471 get inputType() {
michael@0 472 return this._context.inputtype;
michael@0 473 },
michael@0 474
michael@0 475 get inputMode() {
michael@0 476 return this._context.inputmode;
michael@0 477 },
michael@0 478
michael@0 479 get lang() {
michael@0 480 return this._context.lang;
michael@0 481 },
michael@0 482
michael@0 483 getText: function ic_getText(offset, length) {
michael@0 484 let self = this;
michael@0 485 return this._sendPromise(function(resolverId) {
michael@0 486 cpmm.sendAsyncMessage('Keyboard:GetText', {
michael@0 487 contextId: self._contextId,
michael@0 488 requestId: resolverId,
michael@0 489 offset: offset,
michael@0 490 length: length
michael@0 491 });
michael@0 492 });
michael@0 493 },
michael@0 494
michael@0 495 get selectionStart() {
michael@0 496 return this._context.selectionStart;
michael@0 497 },
michael@0 498
michael@0 499 get selectionEnd() {
michael@0 500 return this._context.selectionEnd;
michael@0 501 },
michael@0 502
michael@0 503 get textBeforeCursor() {
michael@0 504 return this._context.textBeforeCursor;
michael@0 505 },
michael@0 506
michael@0 507 get textAfterCursor() {
michael@0 508 return this._context.textAfterCursor;
michael@0 509 },
michael@0 510
michael@0 511 setSelectionRange: function ic_setSelectionRange(start, length) {
michael@0 512 let self = this;
michael@0 513 return this._sendPromise(function(resolverId) {
michael@0 514 cpmm.sendAsyncMessage("Keyboard:SetSelectionRange", {
michael@0 515 contextId: self._contextId,
michael@0 516 requestId: resolverId,
michael@0 517 selectionStart: start,
michael@0 518 selectionEnd: start + length
michael@0 519 });
michael@0 520 });
michael@0 521 },
michael@0 522
michael@0 523 get onsurroundingtextchange() {
michael@0 524 return this.__DOM_IMPL__.getEventHandler("onsurroundingtextchange");
michael@0 525 },
michael@0 526
michael@0 527 set onsurroundingtextchange(handler) {
michael@0 528 this.__DOM_IMPL__.setEventHandler("onsurroundingtextchange", handler);
michael@0 529 },
michael@0 530
michael@0 531 get onselectionchange() {
michael@0 532 return this.__DOM_IMPL__.getEventHandler("onselectionchange");
michael@0 533 },
michael@0 534
michael@0 535 set onselectionchange(handler) {
michael@0 536 this.__DOM_IMPL__.setEventHandler("onselectionchange", handler);
michael@0 537 },
michael@0 538
michael@0 539 replaceSurroundingText: function ic_replaceSurrText(text, offset, length) {
michael@0 540 let self = this;
michael@0 541 return this._sendPromise(function(resolverId) {
michael@0 542 cpmm.sendAsyncMessage('Keyboard:ReplaceSurroundingText', {
michael@0 543 contextId: self._contextId,
michael@0 544 requestId: resolverId,
michael@0 545 text: text,
michael@0 546 offset: offset || 0,
michael@0 547 length: length || 0
michael@0 548 });
michael@0 549 });
michael@0 550 },
michael@0 551
michael@0 552 deleteSurroundingText: function ic_deleteSurrText(offset, length) {
michael@0 553 return this.replaceSurroundingText(null, offset, length);
michael@0 554 },
michael@0 555
michael@0 556 sendKey: function ic_sendKey(keyCode, charCode, modifiers, repeat) {
michael@0 557 let self = this;
michael@0 558 return this._sendPromise(function(resolverId) {
michael@0 559 cpmm.sendAsyncMessage('Keyboard:SendKey', {
michael@0 560 contextId: self._contextId,
michael@0 561 requestId: resolverId,
michael@0 562 keyCode: keyCode,
michael@0 563 charCode: charCode,
michael@0 564 modifiers: modifiers,
michael@0 565 repeat: repeat
michael@0 566 });
michael@0 567 });
michael@0 568 },
michael@0 569
michael@0 570 setComposition: function ic_setComposition(text, cursor, clauses) {
michael@0 571 let self = this;
michael@0 572 return this._sendPromise(function(resolverId) {
michael@0 573 cpmm.sendAsyncMessage('Keyboard:SetComposition', {
michael@0 574 contextId: self._contextId,
michael@0 575 requestId: resolverId,
michael@0 576 text: text,
michael@0 577 cursor: cursor || text.length,
michael@0 578 clauses: clauses || null
michael@0 579 });
michael@0 580 });
michael@0 581 },
michael@0 582
michael@0 583 endComposition: function ic_endComposition(text) {
michael@0 584 let self = this;
michael@0 585 return this._sendPromise(function(resolverId) {
michael@0 586 cpmm.sendAsyncMessage('Keyboard:EndComposition', {
michael@0 587 contextId: self._contextId,
michael@0 588 requestId: resolverId,
michael@0 589 text: text || ''
michael@0 590 });
michael@0 591 });
michael@0 592 },
michael@0 593
michael@0 594 _sendPromise: function(callback) {
michael@0 595 let self = this;
michael@0 596 return this.createPromise(function(resolve, reject) {
michael@0 597 let resolverId = self.getPromiseResolverId({ resolve: resolve, reject: reject });
michael@0 598 if (!WindowMap.isActive(self._window)) {
michael@0 599 self.removePromiseResolver(resolverId);
michael@0 600 reject('Input method is not active.');
michael@0 601 return;
michael@0 602 }
michael@0 603 callback(resolverId);
michael@0 604 });
michael@0 605 }
michael@0 606 };
michael@0 607
michael@0 608 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([MozInputMethod]);

mercurial