Sat, 03 Jan 2015 20:18:00 +0100
Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.
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]); |