dom/inputmethod/MozKeyboard.js

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/dom/inputmethod/MozKeyboard.js	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,608 @@
     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 +const Cc = Components.classes;
    1.11 +const Ci = Components.interfaces;
    1.12 +const Cu = Components.utils;
    1.13 +
    1.14 +Cu.import("resource://gre/modules/XPCOMUtils.jsm");
    1.15 +Cu.import("resource://gre/modules/Services.jsm");
    1.16 +Cu.import("resource://gre/modules/DOMRequestHelper.jsm");
    1.17 +
    1.18 +XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
    1.19 +  "@mozilla.org/childprocessmessagemanager;1", "nsIMessageSender");
    1.20 +
    1.21 +XPCOMUtils.defineLazyServiceGetter(this, "tm",
    1.22 +  "@mozilla.org/thread-manager;1", "nsIThreadManager");
    1.23 +
    1.24 +/*
    1.25 + * A WeakMap to map input method iframe window to its active status.
    1.26 + */
    1.27 +let WindowMap = {
    1.28 +  // WeakMap of <window, boolean> pairs.
    1.29 +  _map: null,
    1.30 +
    1.31 +  /*
    1.32 +   * Check if the given window is active.
    1.33 +   */
    1.34 +  isActive: function(win) {
    1.35 +    if (!this._map || !win) {
    1.36 +      return false;
    1.37 +    }
    1.38 +    return this._map.get(win, false);
    1.39 +  },
    1.40 +
    1.41 +  /*
    1.42 +   * Set the active status of the given window.
    1.43 +   */
    1.44 +  setActive: function(win, isActive) {
    1.45 +    if (!win) {
    1.46 +      return;
    1.47 +    }
    1.48 +    if (!this._map) {
    1.49 +      this._map = new WeakMap();
    1.50 +    }
    1.51 +    this._map.set(win, isActive);
    1.52 +  }
    1.53 +};
    1.54 +
    1.55 +/**
    1.56 + * ==============================================
    1.57 + * InputMethodManager
    1.58 + * ==============================================
    1.59 + */
    1.60 +function MozInputMethodManager(win) {
    1.61 +  this._window = win;
    1.62 +}
    1.63 +
    1.64 +MozInputMethodManager.prototype = {
    1.65 +  _supportsSwitching: false,
    1.66 +  _window: null,
    1.67 +
    1.68 +  classID: Components.ID("{7e9d7280-ef86-11e2-b778-0800200c9a66}"),
    1.69 +
    1.70 +  QueryInterface: XPCOMUtils.generateQI([]),
    1.71 +
    1.72 +  showAll: function() {
    1.73 +    if (!WindowMap.isActive(this._window)) {
    1.74 +      return;
    1.75 +    }
    1.76 +    cpmm.sendAsyncMessage('Keyboard:ShowInputMethodPicker', {});
    1.77 +  },
    1.78 +
    1.79 +  next: function() {
    1.80 +    if (!WindowMap.isActive(this._window)) {
    1.81 +      return;
    1.82 +    }
    1.83 +    cpmm.sendAsyncMessage('Keyboard:SwitchToNextInputMethod', {});
    1.84 +  },
    1.85 +
    1.86 +  supportsSwitching: function() {
    1.87 +    if (!WindowMap.isActive(this._window)) {
    1.88 +      return false;
    1.89 +    }
    1.90 +    return this._supportsSwitching;
    1.91 +  },
    1.92 +
    1.93 +  hide: function() {
    1.94 +    if (!WindowMap.isActive(this._window)) {
    1.95 +      return;
    1.96 +    }
    1.97 +    cpmm.sendAsyncMessage('Keyboard:RemoveFocus', {});
    1.98 +  }
    1.99 +};
   1.100 +
   1.101 +/**
   1.102 + * ==============================================
   1.103 + * InputMethod
   1.104 + * ==============================================
   1.105 + */
   1.106 +function MozInputMethod() { }
   1.107 +
   1.108 +MozInputMethod.prototype = {
   1.109 +  _inputcontext: null,
   1.110 +  _layouts: {},
   1.111 +  _window: null,
   1.112 +  _isSystem: false,
   1.113 +  _isKeyboard: true,
   1.114 +
   1.115 +  classID: Components.ID("{4607330d-e7d2-40a4-9eb8-43967eae0142}"),
   1.116 +
   1.117 +  QueryInterface: XPCOMUtils.generateQI([
   1.118 +    Ci.nsIDOMGlobalPropertyInitializer,
   1.119 +    Ci.nsIObserver,
   1.120 +    Ci.nsISupportsWeakReference
   1.121 +  ]),
   1.122 +
   1.123 +  init: function mozInputMethodInit(win) {
   1.124 +    this._window = win;
   1.125 +    this._mgmt = new MozInputMethodManager(win);
   1.126 +    this.innerWindowID = win.QueryInterface(Ci.nsIInterfaceRequestor)
   1.127 +                            .getInterface(Ci.nsIDOMWindowUtils)
   1.128 +                            .currentInnerWindowID;
   1.129 +
   1.130 +    Services.obs.addObserver(this, "inner-window-destroyed", false);
   1.131 +
   1.132 +    let principal = win.document.nodePrincipal;
   1.133 +    let perm = Services.perms.testExactPermissionFromPrincipal(principal,
   1.134 +                                                               "input-manage");
   1.135 +    if (perm === Ci.nsIPermissionManager.ALLOW_ACTION) {
   1.136 +      this._isSystem = true;
   1.137 +    }
   1.138 +
   1.139 +    // Check if we can use keyboard related APIs.
   1.140 +    let testing = false;
   1.141 +    try {
   1.142 +      testing = Services.prefs.getBoolPref("dom.mozInputMethod.testing");
   1.143 +    } catch (e) {
   1.144 +    }
   1.145 +    perm = Services.perms.testExactPermissionFromPrincipal(principal, "input");
   1.146 +    if (!testing && perm !== Ci.nsIPermissionManager.ALLOW_ACTION) {
   1.147 +      this._isKeyboard = false;
   1.148 +      return;
   1.149 +    }
   1.150 +
   1.151 +    cpmm.addWeakMessageListener('Keyboard:FocusChange', this);
   1.152 +    cpmm.addWeakMessageListener('Keyboard:SelectionChange', this);
   1.153 +    cpmm.addWeakMessageListener('Keyboard:GetContext:Result:OK', this);
   1.154 +    cpmm.addWeakMessageListener('Keyboard:LayoutsChange', this);
   1.155 +  },
   1.156 +
   1.157 +  uninit: function mozInputMethodUninit() {
   1.158 +    this._window = null;
   1.159 +    this._mgmt = null;
   1.160 +    Services.obs.removeObserver(this, "inner-window-destroyed");
   1.161 +    if (!this._isKeyboard) {
   1.162 +      return;
   1.163 +    }
   1.164 +
   1.165 +    cpmm.removeWeakMessageListener('Keyboard:FocusChange', this);
   1.166 +    cpmm.removeWeakMessageListener('Keyboard:SelectionChange', this);
   1.167 +    cpmm.removeWeakMessageListener('Keyboard:GetContext:Result:OK', this);
   1.168 +    cpmm.removeWeakMessageListener('Keyboard:LayoutsChange', this);
   1.169 +    this.setActive(false);
   1.170 +  },
   1.171 +
   1.172 +  receiveMessage: function mozInputMethodReceiveMsg(msg) {
   1.173 +    if (!WindowMap.isActive(this._window)) {
   1.174 +      return;
   1.175 +    }
   1.176 +
   1.177 +    let json = msg.json;
   1.178 +
   1.179 +    switch(msg.name) {
   1.180 +      case 'Keyboard:FocusChange':
   1.181 +        if (json.type !== 'blur') {
   1.182 +          // XXX Bug 904339 could receive 'text' event twice
   1.183 +          this.setInputContext(json);
   1.184 +        }
   1.185 +        else {
   1.186 +          this.setInputContext(null);
   1.187 +        }
   1.188 +        break;
   1.189 +      case 'Keyboard:SelectionChange':
   1.190 +        if (this.inputcontext) {
   1.191 +          this._inputcontext.updateSelectionContext(json);
   1.192 +        }
   1.193 +        break;
   1.194 +      case 'Keyboard:GetContext:Result:OK':
   1.195 +        this.setInputContext(json);
   1.196 +        break;
   1.197 +      case 'Keyboard:LayoutsChange':
   1.198 +        this._layouts = json;
   1.199 +        break;
   1.200 +    }
   1.201 +  },
   1.202 +
   1.203 +  observe: function mozInputMethodObserve(subject, topic, data) {
   1.204 +    let wId = subject.QueryInterface(Ci.nsISupportsPRUint64).data;
   1.205 +    if (wId == this.innerWindowID)
   1.206 +      this.uninit();
   1.207 +  },
   1.208 +
   1.209 +  get mgmt() {
   1.210 +    return this._mgmt;
   1.211 +  },
   1.212 +
   1.213 +  get inputcontext() {
   1.214 +    if (!WindowMap.isActive(this._window)) {
   1.215 +      return null;
   1.216 +    }
   1.217 +    return this._inputcontext;
   1.218 +  },
   1.219 +
   1.220 +  set oninputcontextchange(handler) {
   1.221 +    this.__DOM_IMPL__.setEventHandler("oninputcontextchange", handler);
   1.222 +  },
   1.223 +
   1.224 +  get oninputcontextchange() {
   1.225 +    return this.__DOM_IMPL__.getEventHandler("oninputcontextchange");
   1.226 +  },
   1.227 +
   1.228 +  setInputContext: function mozKeyboardContextChange(data) {
   1.229 +    if (this._inputcontext) {
   1.230 +      this._inputcontext.destroy();
   1.231 +      this._inputcontext = null;
   1.232 +      this._mgmt._supportsSwitching = false;
   1.233 +    }
   1.234 +
   1.235 +    if (data) {
   1.236 +      this._mgmt._supportsSwitching = this._layouts[data.type] ?
   1.237 +        this._layouts[data.type] > 1 :
   1.238 +        false;
   1.239 +
   1.240 +      this._inputcontext = new MozInputContext(data);
   1.241 +      this._inputcontext.init(this._window);
   1.242 +    }
   1.243 +
   1.244 +    let event = new this._window.Event("inputcontextchange",
   1.245 +                                       Cu.cloneInto({}, this._window));
   1.246 +    this.__DOM_IMPL__.dispatchEvent(event);
   1.247 +  },
   1.248 +
   1.249 +  setActive: function mozInputMethodSetActive(isActive) {
   1.250 +    if (WindowMap.isActive(this._window) === isActive) {
   1.251 +      return;
   1.252 +    }
   1.253 +
   1.254 +    WindowMap.setActive(this._window, isActive);
   1.255 +
   1.256 +    if (isActive) {
   1.257 +      // Activate current input method.
   1.258 +      // If there is already an active context, then this will trigger
   1.259 +      // a GetContext:Result:OK event, and we can initialize ourselves.
   1.260 +      // Otherwise silently ignored.
   1.261 +      cpmm.sendAsyncMessage('Keyboard:Register', {});
   1.262 +      cpmm.sendAsyncMessage("Keyboard:GetContext", {});
   1.263 +    } else {
   1.264 +      // Deactive current input method.
   1.265 +      cpmm.sendAsyncMessage('Keyboard:Unregister', {});
   1.266 +      if (this._inputcontext) {
   1.267 +        this.setInputContext(null);
   1.268 +      }
   1.269 +    }
   1.270 +  },
   1.271 +
   1.272 +  setValue: function(value) {
   1.273 +    this._ensureIsSystem();
   1.274 +    cpmm.sendAsyncMessage('System:SetValue', {
   1.275 +      'value': value
   1.276 +    });
   1.277 +  },
   1.278 +
   1.279 +  setSelectedOption: function(index) {
   1.280 +    this._ensureIsSystem();
   1.281 +    cpmm.sendAsyncMessage('System:SetSelectedOption', {
   1.282 +      'index': index
   1.283 +    });
   1.284 +  },
   1.285 +
   1.286 +  setSelectedOptions: function(indexes) {
   1.287 +    this._ensureIsSystem();
   1.288 +    cpmm.sendAsyncMessage('System:SetSelectedOptions', {
   1.289 +      'indexes': indexes
   1.290 +    });
   1.291 +  },
   1.292 +
   1.293 +  removeFocus: function() {
   1.294 +    this._ensureIsSystem();
   1.295 +    cpmm.sendAsyncMessage('System:RemoveFocus', {});
   1.296 +  },
   1.297 +
   1.298 +  _ensureIsSystem: function() {
   1.299 +    if (!this._isSystem) {
   1.300 +      throw new this._window.DOMError("Security",
   1.301 +                                      "Should have 'input-manage' permssion.");
   1.302 +    }
   1.303 +  }
   1.304 +};
   1.305 +
   1.306 + /**
   1.307 + * ==============================================
   1.308 + * InputContext
   1.309 + * ==============================================
   1.310 + */
   1.311 +function MozInputContext(ctx) {
   1.312 +  this._context = {
   1.313 +    inputtype: ctx.type,
   1.314 +    inputmode: ctx.inputmode,
   1.315 +    lang: ctx.lang,
   1.316 +    type: ["textarea", "contenteditable"].indexOf(ctx.type) > -1 ?
   1.317 +              ctx.type :
   1.318 +              "text",
   1.319 +    selectionStart: ctx.selectionStart,
   1.320 +    selectionEnd: ctx.selectionEnd,
   1.321 +    textBeforeCursor: ctx.textBeforeCursor,
   1.322 +    textAfterCursor: ctx.textAfterCursor
   1.323 +  };
   1.324 +
   1.325 +  this._contextId = ctx.contextId;
   1.326 +}
   1.327 +
   1.328 +MozInputContext.prototype = {
   1.329 +  __proto__: DOMRequestIpcHelper.prototype,
   1.330 +
   1.331 +  _window: null,
   1.332 +  _context: null,
   1.333 +  _contextId: -1,
   1.334 +
   1.335 +  classID: Components.ID("{1e38633d-d08b-4867-9944-afa5c648adb6}"),
   1.336 +
   1.337 +  QueryInterface: XPCOMUtils.generateQI([
   1.338 +    Ci.nsIObserver,
   1.339 +    Ci.nsISupportsWeakReference
   1.340 +  ]),
   1.341 +
   1.342 +  init: function ic_init(win) {
   1.343 +    this._window = win;
   1.344 +    this._utils = win.QueryInterface(Ci.nsIInterfaceRequestor)
   1.345 +                     .getInterface(Ci.nsIDOMWindowUtils);
   1.346 +    this.initDOMRequestHelper(win,
   1.347 +      ["Keyboard:GetText:Result:OK",
   1.348 +       "Keyboard:GetText:Result:Error",
   1.349 +       "Keyboard:SetSelectionRange:Result:OK",
   1.350 +       "Keyboard:ReplaceSurroundingText:Result:OK",
   1.351 +       "Keyboard:SendKey:Result:OK",
   1.352 +       "Keyboard:SendKey:Result:Error",
   1.353 +       "Keyboard:SetComposition:Result:OK",
   1.354 +       "Keyboard:EndComposition:Result:OK",
   1.355 +       "Keyboard:SequenceError"]);
   1.356 +  },
   1.357 +
   1.358 +  destroy: function ic_destroy() {
   1.359 +    let self = this;
   1.360 +
   1.361 +    // All requests that are still pending need to be invalidated
   1.362 +    // because the context is no longer valid.
   1.363 +    this.forEachPromiseResolver(function(k) {
   1.364 +      self.takePromiseResolver(k).reject("InputContext got destroyed");
   1.365 +    });
   1.366 +    this.destroyDOMRequestHelper();
   1.367 +
   1.368 +    // A consuming application might still hold a cached version of
   1.369 +    // this object. After destroying all methods will throw because we
   1.370 +    // cannot create new promises anymore, but we still hold
   1.371 +    // (outdated) information in the context. So let's clear that out.
   1.372 +    for (var k in this._context) {
   1.373 +      if (this._context.hasOwnProperty(k)) {
   1.374 +        this._context[k] = null;
   1.375 +      }
   1.376 +    }
   1.377 +
   1.378 +    this._window = null;
   1.379 +  },
   1.380 +
   1.381 +  receiveMessage: function ic_receiveMessage(msg) {
   1.382 +    if (!msg || !msg.json) {
   1.383 +      dump('InputContext received message without data\n');
   1.384 +      return;
   1.385 +    }
   1.386 +
   1.387 +    let json = msg.json;
   1.388 +    let resolver = this.takePromiseResolver(json.requestId);
   1.389 +
   1.390 +    if (!resolver) {
   1.391 +      return;
   1.392 +    }
   1.393 +
   1.394 +    switch (msg.name) {
   1.395 +      case "Keyboard:SendKey:Result:OK":
   1.396 +        resolver.resolve();
   1.397 +        break;
   1.398 +      case "Keyboard:SendKey:Result:Error":
   1.399 +        resolver.reject(json.error);
   1.400 +        break;
   1.401 +      case "Keyboard:GetText:Result:OK":
   1.402 +        resolver.resolve(json.text);
   1.403 +        break;
   1.404 +      case "Keyboard:GetText:Result:Error":
   1.405 +        resolver.reject(json.error);
   1.406 +        break;
   1.407 +      case "Keyboard:SetSelectionRange:Result:OK":
   1.408 +      case "Keyboard:ReplaceSurroundingText:Result:OK":
   1.409 +        resolver.resolve(
   1.410 +          Cu.cloneInto(json.selectioninfo, this._window));
   1.411 +        break;
   1.412 +      case "Keyboard:SequenceError":
   1.413 +        // Occurs when a new element got focus, but the inputContext was
   1.414 +        // not invalidated yet...
   1.415 +        resolver.reject("InputContext has expired");
   1.416 +        break;
   1.417 +      case "Keyboard:SetComposition:Result:OK": // Fall through.
   1.418 +      case "Keyboard:EndComposition:Result:OK":
   1.419 +        resolver.resolve();
   1.420 +        break;
   1.421 +      default:
   1.422 +        dump("Could not find a handler for " + msg.name);
   1.423 +        resolver.reject();
   1.424 +        break;
   1.425 +    }
   1.426 +  },
   1.427 +
   1.428 +  updateSelectionContext: function ic_updateSelectionContext(ctx) {
   1.429 +    if (!this._context) {
   1.430 +      return;
   1.431 +    }
   1.432 +
   1.433 +    let selectionDirty = this._context.selectionStart !== ctx.selectionStart ||
   1.434 +          this._context.selectionEnd !== ctx.selectionEnd;
   1.435 +    let surroundDirty = this._context.textBeforeCursor !== ctx.textBeforeCursor ||
   1.436 +          this._context.textAfterCursor !== ctx.textAfterCursor;
   1.437 +
   1.438 +    this._context.selectionStart = ctx.selectionStart;
   1.439 +    this._context.selectionEnd = ctx.selectionEnd;
   1.440 +    this._context.textBeforeCursor = ctx.textBeforeCursor;
   1.441 +    this._context.textAfterCursor = ctx.textAfterCursor;
   1.442 +
   1.443 +    if (selectionDirty) {
   1.444 +      this._fireEvent("selectionchange", {
   1.445 +        selectionStart: ctx.selectionStart,
   1.446 +        selectionEnd: ctx.selectionEnd
   1.447 +      });
   1.448 +    }
   1.449 +
   1.450 +    if (surroundDirty) {
   1.451 +      this._fireEvent("surroundingtextchange", {
   1.452 +        beforeString: ctx.textBeforeCursor,
   1.453 +        afterString: ctx.textAfterCursor
   1.454 +      });
   1.455 +    }
   1.456 +  },
   1.457 +
   1.458 +  _fireEvent: function ic_fireEvent(eventName, aDetail) {
   1.459 +    let detail = {
   1.460 +      detail: aDetail
   1.461 +    };
   1.462 +
   1.463 +    let event = new this._window.Event(eventName,
   1.464 +                                       Cu.cloneInto(aDetail, this._window));
   1.465 +    this.__DOM_IMPL__.dispatchEvent(event);
   1.466 +  },
   1.467 +
   1.468 +  // tag name of the input field
   1.469 +  get type() {
   1.470 +    return this._context.type;
   1.471 +  },
   1.472 +
   1.473 +  // type of the input field
   1.474 +  get inputType() {
   1.475 +    return this._context.inputtype;
   1.476 +  },
   1.477 +
   1.478 +  get inputMode() {
   1.479 +    return this._context.inputmode;
   1.480 +  },
   1.481 +
   1.482 +  get lang() {
   1.483 +    return this._context.lang;
   1.484 +  },
   1.485 +
   1.486 +  getText: function ic_getText(offset, length) {
   1.487 +    let self = this;
   1.488 +    return this._sendPromise(function(resolverId) {
   1.489 +      cpmm.sendAsyncMessage('Keyboard:GetText', {
   1.490 +        contextId: self._contextId,
   1.491 +        requestId: resolverId,
   1.492 +        offset: offset,
   1.493 +        length: length
   1.494 +      });
   1.495 +    });
   1.496 +  },
   1.497 +
   1.498 +  get selectionStart() {
   1.499 +    return this._context.selectionStart;
   1.500 +  },
   1.501 +
   1.502 +  get selectionEnd() {
   1.503 +    return this._context.selectionEnd;
   1.504 +  },
   1.505 +
   1.506 +  get textBeforeCursor() {
   1.507 +    return this._context.textBeforeCursor;
   1.508 +  },
   1.509 +
   1.510 +  get textAfterCursor() {
   1.511 +    return this._context.textAfterCursor;
   1.512 +  },
   1.513 +
   1.514 +  setSelectionRange: function ic_setSelectionRange(start, length) {
   1.515 +    let self = this;
   1.516 +    return this._sendPromise(function(resolverId) {
   1.517 +      cpmm.sendAsyncMessage("Keyboard:SetSelectionRange", {
   1.518 +        contextId: self._contextId,
   1.519 +        requestId: resolverId,
   1.520 +        selectionStart: start,
   1.521 +        selectionEnd: start + length
   1.522 +      });
   1.523 +    });
   1.524 +  },
   1.525 +
   1.526 +  get onsurroundingtextchange() {
   1.527 +    return this.__DOM_IMPL__.getEventHandler("onsurroundingtextchange");
   1.528 +  },
   1.529 +
   1.530 +  set onsurroundingtextchange(handler) {
   1.531 +    this.__DOM_IMPL__.setEventHandler("onsurroundingtextchange", handler);
   1.532 +  },
   1.533 +
   1.534 +  get onselectionchange() {
   1.535 +    return this.__DOM_IMPL__.getEventHandler("onselectionchange");
   1.536 +  },
   1.537 +
   1.538 +  set onselectionchange(handler) {
   1.539 +    this.__DOM_IMPL__.setEventHandler("onselectionchange", handler);
   1.540 +  },
   1.541 +
   1.542 +  replaceSurroundingText: function ic_replaceSurrText(text, offset, length) {
   1.543 +    let self = this;
   1.544 +    return this._sendPromise(function(resolverId) {
   1.545 +      cpmm.sendAsyncMessage('Keyboard:ReplaceSurroundingText', {
   1.546 +        contextId: self._contextId,
   1.547 +        requestId: resolverId,
   1.548 +        text: text,
   1.549 +        offset: offset || 0,
   1.550 +        length: length || 0
   1.551 +      });
   1.552 +    });
   1.553 +  },
   1.554 +
   1.555 +  deleteSurroundingText: function ic_deleteSurrText(offset, length) {
   1.556 +    return this.replaceSurroundingText(null, offset, length);
   1.557 +  },
   1.558 +
   1.559 +  sendKey: function ic_sendKey(keyCode, charCode, modifiers, repeat) {
   1.560 +    let self = this;
   1.561 +    return this._sendPromise(function(resolverId) {
   1.562 +      cpmm.sendAsyncMessage('Keyboard:SendKey', {
   1.563 +        contextId: self._contextId,
   1.564 +        requestId: resolverId,
   1.565 +        keyCode: keyCode,
   1.566 +        charCode: charCode,
   1.567 +        modifiers: modifiers,
   1.568 +        repeat: repeat
   1.569 +      });
   1.570 +    });
   1.571 +  },
   1.572 +
   1.573 +  setComposition: function ic_setComposition(text, cursor, clauses) {
   1.574 +    let self = this;
   1.575 +    return this._sendPromise(function(resolverId) {
   1.576 +      cpmm.sendAsyncMessage('Keyboard:SetComposition', {
   1.577 +        contextId: self._contextId,
   1.578 +        requestId: resolverId,
   1.579 +        text: text,
   1.580 +        cursor: cursor || text.length,
   1.581 +        clauses: clauses || null
   1.582 +      });
   1.583 +    });
   1.584 +  },
   1.585 +
   1.586 +  endComposition: function ic_endComposition(text) {
   1.587 +    let self = this;
   1.588 +    return this._sendPromise(function(resolverId) {
   1.589 +      cpmm.sendAsyncMessage('Keyboard:EndComposition', {
   1.590 +        contextId: self._contextId,
   1.591 +        requestId: resolverId,
   1.592 +        text: text || ''
   1.593 +      });
   1.594 +    });
   1.595 +  },
   1.596 +
   1.597 +  _sendPromise: function(callback) {
   1.598 +    let self = this;
   1.599 +    return this.createPromise(function(resolve, reject) {
   1.600 +      let resolverId = self.getPromiseResolverId({ resolve: resolve, reject: reject });
   1.601 +      if (!WindowMap.isActive(self._window)) {
   1.602 +        self.removePromiseResolver(resolverId);
   1.603 +        reject('Input method is not active.');
   1.604 +        return;
   1.605 +      }
   1.606 +      callback(resolverId);
   1.607 +    });
   1.608 +  }
   1.609 +};
   1.610 +
   1.611 +this.NSGetFactory = XPCOMUtils.generateNSGetFactory([MozInputMethod]);

mercurial