OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 'use strict'; | 5 'use strict'; |
6 | 6 |
7 /** | 7 /** |
8 * @fileoverview Braille hardware keyboard input method. | 8 * @fileoverview Braille hardware keyboard input method. |
9 * | 9 * |
10 * This method is automatically enabled when a braille display is connected | 10 * This method is automatically enabled when a braille display is connected |
11 * and ChromeVox is turned on. Most of the braille input and editing logic | 11 * and ChromeVox is turned on. Most of the braille input and editing logic |
12 * is located in ChromeVox where the braille translation library is available. | 12 * is located in ChromeVox where the braille translation library is available. |
13 * This IME connects to ChromeVox and communicates using messages as follows: | 13 * This IME connects to ChromeVox and communicates using messages as follows: |
14 * | 14 * |
15 * Sent from this IME to ChromeVox: | 15 * Sent from this IME to ChromeVox: |
16 * {type: 'activeState', active: boolean} | 16 * {type: 'activeState', active: boolean} |
17 * {type: 'inputContext', context: InputContext} | 17 * {type: 'inputContext', context: InputContext} |
18 * Sent on focus/blur to inform ChromeVox of the type of the current field. | 18 * Sent on focus/blur to inform ChromeVox of the type of the current field. |
19 * In the latter case (blur), context is null. | 19 * In the latter case (blur), context is null. |
20 * {type: 'reset'} | 20 * {type: 'reset'} |
21 * Sent when the {code onReset} IME event fires. | 21 * Sent when the {code onReset} IME event fires. |
22 * {type: 'brailleDots', dots: number} | 22 * {type: 'brailleDots', dots: number} |
23 * Sent when the user typed a braille cell using the standard keyboard. | 23 * Sent when the user typed a braille cell using the standard keyboard. |
24 * ChromeVox treats this similarly to entering braille input using the | 24 * ChromeVox treats this similarly to entering braille input using the |
25 * braille display. | 25 * braille display. |
| 26 * {type: 'backspace', requestId: string} |
| 27 * Sent when the user presses the backspace key. |
| 28 * ChromeVox must respond with a {@code keyEventHandled} message |
| 29 * with the same request id. |
26 * | 30 * |
27 * Sent from ChromeVox to this IME: | 31 * Sent from ChromeVox to this IME: |
28 * {type: 'replaceText', contextID: number, deleteBefore: number, | 32 * {type: 'replaceText', contextID: number, deleteBefore: number, |
29 * newText: string} | 33 * newText: string} |
30 * Deletes {@code deleteBefore} characters before the cursor (or selection) | 34 * Deletes {@code deleteBefore} characters before the cursor (or selection) |
31 * and inserts {@code newText}. {@code contextID} identifies the text field | 35 * and inserts {@code newText}. {@code contextID} identifies the text field |
32 * to apply the update to (no change will happen if focus has moved to a | 36 * to apply the update to (no change will happen if focus has moved to a |
33 * different field). | 37 * different field). |
| 38 * {type: 'keyEventHandled', requestId: string, result: boolean} |
| 39 * Response to a {@code backspace} message indicating whether the |
| 40 * backspace was handled by ChromeVox or should be allowed to propagate |
| 41 * through the normal event handling pipeline. |
34 */ | 42 */ |
35 | 43 |
36 /** | 44 /** |
37 * @constructor | 45 * @constructor |
38 */ | 46 */ |
39 var BrailleIme = function() {}; | 47 var BrailleIme = function() {}; |
40 | 48 |
41 BrailleIme.prototype = { | 49 BrailleIme.prototype = { |
42 /** | 50 /** |
43 * Whether to enable extra debug logging for the IME. | 51 * Whether to enable extra debug logging for the IME. |
(...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
125 /** | 133 /** |
126 * Registers event listeners in the chrome IME API. | 134 * Registers event listeners in the chrome IME API. |
127 */ | 135 */ |
128 init: function() { | 136 init: function() { |
129 chrome.input.ime.onActivate.addListener(this.onActivate_.bind(this)); | 137 chrome.input.ime.onActivate.addListener(this.onActivate_.bind(this)); |
130 chrome.input.ime.onDeactivated.addListener(this.onDeactivated_.bind(this)); | 138 chrome.input.ime.onDeactivated.addListener(this.onDeactivated_.bind(this)); |
131 chrome.input.ime.onFocus.addListener(this.onFocus_.bind(this)); | 139 chrome.input.ime.onFocus.addListener(this.onFocus_.bind(this)); |
132 chrome.input.ime.onBlur.addListener(this.onBlur_.bind(this)); | 140 chrome.input.ime.onBlur.addListener(this.onBlur_.bind(this)); |
133 chrome.input.ime.onInputContextUpdate.addListener( | 141 chrome.input.ime.onInputContextUpdate.addListener( |
134 this.onInputContextUpdate_.bind(this)); | 142 this.onInputContextUpdate_.bind(this)); |
135 chrome.input.ime.onKeyEvent.addListener(this.onKeyEvent_.bind(this)); | 143 chrome.input.ime.onKeyEvent.addListener(this.onKeyEvent_.bind(this), |
| 144 ['async']); |
136 chrome.input.ime.onReset.addListener(this.onReset_.bind(this)); | 145 chrome.input.ime.onReset.addListener(this.onReset_.bind(this)); |
137 chrome.input.ime.onMenuItemActivated.addListener( | 146 chrome.input.ime.onMenuItemActivated.addListener( |
138 this.onMenuItemActivated_.bind(this)); | 147 this.onMenuItemActivated_.bind(this)); |
139 this.connectChromeVox_(); | 148 this.connectChromeVox_(); |
140 }, | 149 }, |
141 | 150 |
142 /** | 151 /** |
143 * Called by the IME framework when this IME is activated. | 152 * Called by the IME framework when this IME is activated. |
144 * @param {string} engineID Engine ID, should be 'braille'. | 153 * @param {string} engineID Engine ID, should be 'braille'. |
145 * @private | 154 * @private |
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
196 */ | 205 */ |
197 onInputContextUpdate_: function(context) { | 206 onInputContextUpdate_: function(context) { |
198 this.log_('onInputContextUpdate', JSON.stringify(context)); | 207 this.log_('onInputContextUpdate', JSON.stringify(context)); |
199 this.sendInputContext_(context); | 208 this.sendInputContext_(context); |
200 }, | 209 }, |
201 | 210 |
202 /** | 211 /** |
203 * Called by the system when this IME is active and a key event is generated. | 212 * Called by the system when this IME is active and a key event is generated. |
204 * @param {string} engineID Engine ID, should be 'braille'. | 213 * @param {string} engineID Engine ID, should be 'braille'. |
205 * @param {!ChromeKeyboardEvent} event The keyboard event. | 214 * @param {!ChromeKeyboardEvent} event The keyboard event. |
206 * @return {boolean} Whether the event was handled by this IME (true) or | |
207 * should be allowed to propagate. | |
208 * @private | 215 * @private |
209 */ | 216 */ |
210 onKeyEvent_: function(engineID, event) { | 217 onKeyEvent_: function(engineID, event) { |
211 this.log_('onKeyEvent', engineID + ', ' + JSON.stringify(event)); | 218 this.log_('onKeyEvent', engineID + ', ' + JSON.stringify(event)); |
212 return this.processKey_(event); | 219 var result = this.processKey_(event); |
| 220 if (result !== undefined) { |
| 221 chrome.input.ime.keyEventHandled(event.requestId, result); |
| 222 } |
213 }, | 223 }, |
214 | 224 |
215 /** | 225 /** |
216 * Called when chrome ends the current text input session. | 226 * Called when chrome ends the current text input session. |
217 * @param {string} engineID Engine ID, should be 'braille'. | 227 * @param {string} engineID Engine ID, should be 'braille'. |
218 * @private | 228 * @private |
219 */ | 229 */ |
220 onReset_: function(engineID) { | 230 onReset_: function(engineID) { |
221 this.log_('onReset', engineID); | 231 this.log_('onReset', engineID); |
222 this.engineID_ = engineID; | 232 this.engineID_ = engineID; |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
255 return; | 265 return; |
256 } | 266 } |
257 if (this.DEBUG) { | 267 if (this.DEBUG) { |
258 console.log('BrailleIme.' + func + ': ' + message); | 268 console.log('BrailleIme.' + func + ': ' + message); |
259 } | 269 } |
260 }, | 270 }, |
261 | 271 |
262 /** | 272 /** |
263 * Handles a qwerty key on the home row as a braille key. | 273 * Handles a qwerty key on the home row as a braille key. |
264 * @param {!ChromeKeyboardEvent} event Keyboard event. | 274 * @param {!ChromeKeyboardEvent} event Keyboard event. |
265 * @return {boolean} Whether the key event was handled or not. | 275 * @return {boolean|undefined} Whether the event was handled, or |
| 276 * {@code undefined} if handling was delegated to ChromeVox. |
266 * @private | 277 * @private |
267 */ | 278 */ |
268 processKey_: function(event) { | 279 processKey_: function(event) { |
269 if (!this.useStandardKeyboard_) { | 280 if (!this.useStandardKeyboard_) { |
270 return false; | 281 return false; |
271 } | 282 } |
| 283 if (event.code === 'Backspace' && event.type === 'keydown') { |
| 284 this.pressed_ = 0; |
| 285 this.accumulated_ = 0; |
| 286 this.sendToChromeVox_( |
| 287 {type: 'backspace', requestId: event.requestId}); |
| 288 return undefined; |
| 289 } |
272 var dot = this.CODE_TO_DOT_[event.code]; | 290 var dot = this.CODE_TO_DOT_[event.code]; |
273 if (!dot || event.altKey || event.ctrlKey || event.shiftKey || | 291 if (!dot || event.altKey || event.ctrlKey || event.shiftKey || |
274 event.capsLock) { | 292 event.capsLock) { |
275 this.pressed_ = 0; | 293 this.pressed_ = 0; |
276 this.accumulated_ = 0; | 294 this.accumulated_ = 0; |
277 return false; | 295 return false; |
278 } | 296 } |
279 if (event.type === 'keydown') { | 297 if (event.type === 'keydown') { |
280 this.pressed_ |= dot; | 298 this.pressed_ |= dot; |
281 this.accumulated_ |= this.pressed_; | 299 this.accumulated_ |= this.pressed_; |
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
329 case 'replaceText': | 347 case 'replaceText': |
330 message = | 348 message = |
331 /** | 349 /** |
332 * @type {{contextID: number, deleteBefore: number, | 350 * @type {{contextID: number, deleteBefore: number, |
333 * newText: string}} | 351 * newText: string}} |
334 */ | 352 */ |
335 (message); | 353 (message); |
336 this.replaceText_(message.contextID, message.deleteBefore, | 354 this.replaceText_(message.contextID, message.deleteBefore, |
337 message.newText); | 355 message.newText); |
338 break; | 356 break; |
| 357 case 'keyEventHandled': |
| 358 message = |
| 359 /** @type {{requestId: string, result: boolean}} */ (message); |
| 360 chrome.input.ime.keyEventHandled(message.requestId, message.result); |
| 361 break; |
| 362 default: |
| 363 console.error('Unknown message from ChromeVox: ' + |
| 364 JSON.stringify(message)); |
| 365 break; |
339 } | 366 } |
340 }, | 367 }, |
341 | 368 |
342 /** | 369 /** |
343 * Handles a disconnect event from the ChromeVox side. | 370 * Handles a disconnect event from the ChromeVox side. |
344 * @private | 371 * @private |
345 */ | 372 */ |
346 onChromeVoxDisconnect_: function() { | 373 onChromeVoxDisconnect_: function() { |
347 this.port_ = null; | 374 this.port_ = null; |
348 this.log_('onChromeVoxDisconnect', | 375 this.log_('onChromeVoxDisconnect', |
(...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
421 label: 'Use standard keyboard for braille', | 448 label: 'Use standard keyboard for braille', |
422 style: 'check', | 449 style: 'check', |
423 visible: true, | 450 visible: true, |
424 checked: this.useStandardKeyboard_, | 451 checked: this.useStandardKeyboard_, |
425 enabled: true | 452 enabled: true |
426 } | 453 } |
427 ] | 454 ] |
428 }); | 455 }); |
429 } | 456 } |
430 }; | 457 }; |
OLD | NEW |