Add new certificateProvider extension API.
[chromium-blink-merge.git] / chrome / browser / resources / chromeos / chromevox / braille / braille_display_manager.js
blob1f09ad4083ea7ecfad815a56ec2672f6ff838310
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
3 // found in the LICENSE file.
5 /**
6  * @fileoverview Puts text on a braille display.
7  *
8  */
10 goog.provide('cvox.BrailleDisplayManager');
12 goog.require('cvox.BrailleCaptionsBackground');
13 goog.require('cvox.BrailleDisplayState');
14 goog.require('cvox.ExpandingBrailleTranslator');
15 goog.require('cvox.LibLouis');
16 goog.require('cvox.NavBraille');
17 goog.require('cvox.PanStrategy');
20 /**
21  * @param {!cvox.BrailleTranslatorManager} translatorManager Keeps track
22  *     of the current translator to use.
23  * @constructor
24  */
25 cvox.BrailleDisplayManager = function(translatorManager) {
26   /**
27    * @type {!cvox.BrailleTranslatorManager}
28    * @private
29    */
30   this.translatorManager_ = translatorManager;
31   /**
32    * @type {!cvox.NavBraille}
33    * @private
34    */
35   this.content_ = new cvox.NavBraille({});
36   /**
37    * @type {!cvox.ExpandingBrailleTranslator.ExpansionType} valueExpansion
38    * @private
39    */
40   this.expansionType_ =
41       cvox.ExpandingBrailleTranslator.ExpansionType.SELECTION;
42   /**
43    * @type {!ArrayBuffer}
44    * @private
45    */
46   this.translatedContent_ = new ArrayBuffer(0);
47   /**
48    * @type {!ArrayBuffer}
49    * @private
50    */
51   this.displayedContent_ = this.translatedContent_;
52   /**
53    * @type {cvox.PanStrategy}
54    * @private
55    */
56   this.panStrategy_ = new cvox.WrappingPanStrategy();
57   /**
58    * @type {function(!cvox.BrailleKeyEvent, !cvox.NavBraille)}
59    * @private
60    */
61   this.commandListener_ = function() {};
62   /**
63    * Current display state used for width calculations.  This is different from
64    * realDisplayState_ if the braille captions feature is enabled and there is
65    * no hardware display connected.  Otherwise, it is the same object
66    * as realDisplayState_.
67    * @type {!cvox.BrailleDisplayState}
68    * @private
69    */
70   this.displayState_ = {available: false, textCellCount: undefined};
71   /**
72    * State reported from the chrome api, reflecting a real hardware
73    * display.
74    * @type {!cvox.BrailleDisplayState}
75    * @private
76    */
77   this.realDisplayState_ = this.displayState_;
78   /**
79    * @type {!Array<number>}
80    * @private
81    */
82   this.textToBraille_ = [];
83   /**
84    * @type {!Array<number>}
85    * @private
86    */
87   this.brailleToText_ = [];
89   translatorManager.addChangeListener(function() {
90     this.translateContent_(this.content_, this.expansionType_);
91   }.bind(this));
93   chrome.storage.onChanged.addListener(function(changes, area) {
94     if (area == 'local' && 'brailleWordWrap' in changes) {
95       this.updatePanStrategy_(changes.brailleWordWrap.newValue);
96     }
97   }.bind(this));
98   chrome.storage.local.get({brailleWordWrap: true}, function(items) {
99     this.updatePanStrategy_(items.brailleWordWrap);
100   }.bind(this));
102   cvox.BrailleCaptionsBackground.init(goog.bind(
103       this.onCaptionsStateChanged_, this));
104   if (goog.isDef(chrome.brailleDisplayPrivate)) {
105     var onDisplayStateChanged = goog.bind(this.refreshDisplayState_, this);
106     chrome.brailleDisplayPrivate.getDisplayState(onDisplayStateChanged);
107     chrome.brailleDisplayPrivate.onDisplayStateChanged.addListener(
108         onDisplayStateChanged);
109     chrome.brailleDisplayPrivate.onKeyEvent.addListener(
110         goog.bind(this.onKeyEvent_, this));
111   } else {
112     // Get the initial captions state since we won't refresh the display
113     // state in an API callback in this case.
114     this.onCaptionsStateChanged_();
115   }
120  * Dots representing a cursor.
121  * @const
122  * @private
123  */
124 cvox.BrailleDisplayManager.CURSOR_DOTS_ = 1 << 6 | 1 << 7;
128  * @param {!cvox.NavBraille} content Content to send to the braille display.
129  * @param {!cvox.ExpandingBrailleTranslator.ExpansionType} expansionType
130  *     If the text has a {@code ValueSpan}, this indicates how that part
131  *     of the display content is expanded when translating to braille.
132  *     (See {@code cvox.ExpandingBrailleTranslator}).
133  */
134 cvox.BrailleDisplayManager.prototype.setContent = function(
135     content, expansionType) {
136   this.translateContent_(content, expansionType);
141  * Sets the command listener.  When a command is invoked, the listener will be
142  * called with the BrailleKeyEvent corresponding to the command and the content
143  * that was present on the display when the command was invoked.  The content
144  * is guaranteed to be identical to an object previously used as the parameter
145  * to cvox.BrailleDisplayManager.setContent, or null if no content was set.
146  * @param {function(!cvox.BrailleKeyEvent, !cvox.NavBraille)} func The listener.
147  */
148 cvox.BrailleDisplayManager.prototype.setCommandListener = function(func) {
149   this.commandListener_ = func;
154  * @param {!cvox.BrailleDisplayState} newState Display state reported
155  *     by the extension API.
156  * @private
157  */
158 cvox.BrailleDisplayManager.prototype.refreshDisplayState_ = function(
159     newState) {
160   var oldSize = this.displayState_.textCellCount || 0;
161   this.realDisplayState_ = newState;
162   if (newState.available) {
163     this.displayState_ = newState;
164   } else {
165     this.displayState_ =
166         cvox.BrailleCaptionsBackground.getVirtualDisplayState();
167   }
168   var newSize = this.displayState_.textCellCount || 0;
169   if (oldSize != newSize) {
170     this.panStrategy_.setDisplaySize(newSize);
171   }
172   this.refresh_();
177  * Called when the state of braille captions changes.
178  * @private
179  */
180 cvox.BrailleDisplayManager.prototype.onCaptionsStateChanged_ = function() {
181   // Force reevaluation of the display state based on our stored real
182   // hardware display state, meaning that if a real display is connected,
183   // that takes precedence over the state from the captions 'virtual' display.
184   this.refreshDisplayState_(this.realDisplayState_);
188 /** @private */
189 cvox.BrailleDisplayManager.prototype.refresh_ = function() {
190   if (!this.displayState_.available) {
191     return;
192   }
193   var viewPort = this.panStrategy_.viewPort;
194   var buf = this.displayedContent_.slice(viewPort.start, viewPort.end);
195   if (this.realDisplayState_.available) {
196     chrome.brailleDisplayPrivate.writeDots(buf);
197   }
198   if (cvox.BrailleCaptionsBackground.isEnabled()) {
199     var start = this.brailleToTextPosition_(viewPort.start);
200     var end = this.brailleToTextPosition_(viewPort.end);
201     cvox.BrailleCaptionsBackground.setContent(
202         this.content_.text.toString().substring(start, end), buf);
203   }
208  * @param {!cvox.NavBraille} newContent New display content.
209  * @param {cvox.ExpandingBrailleTranslator.ExpansionType} newExpansionType
210  *     How the value part of of the new content should be expanded
211  *     with regards to contractions.
212  * @private
213  */
214 cvox.BrailleDisplayManager.prototype.translateContent_ = function(
215     newContent, newExpansionType) {
216   var writeTranslatedContent = function(cells, textToBraille, brailleToText) {
217     this.content_ = newContent;
218     this.expansionType_ = newExpansionType;
219     this.textToBraille_ = textToBraille;
220     this.brailleToText_ = brailleToText;
221     var startIndex = this.content_.startIndex;
222     var endIndex = this.content_.endIndex;
223     var targetPosition;
224     if (startIndex >= 0) {
225       var translatedStartIndex;
226       var translatedEndIndex;
227       if (startIndex >= textToBraille.length) {
228         // Allow the cells to be extended with one extra cell for
229         // a carret after the last character.
230         var extCells = new ArrayBuffer(cells.byteLength + 1);
231         new Uint8Array(extCells).set(new Uint8Array(cells));
232         // Last byte is initialized to 0.
233         cells = extCells;
234         translatedStartIndex = cells.byteLength - 1;
235       } else {
236         translatedStartIndex = textToBraille[startIndex];
237       }
238       if (endIndex >= textToBraille.length) {
239         // endIndex can't be past-the-end of the last cell unless
240         // startIndex is too, so we don't have to do another
241         // extension here.
242         translatedEndIndex = cells.byteLength;
243       } else {
244         translatedEndIndex = textToBraille[endIndex];
245       }
246       this.translatedContent_ = cells;
247       // Copy the transalted content to a separate buffer and  add the cursor
248       // to it.
249       this.displayedContent_ = new ArrayBuffer(cells.byteLength);
250       new Uint8Array(this.displayedContent_).set(new Uint8Array(cells));
251       this.writeCursor_(this.displayedContent_,
252                         translatedStartIndex, translatedEndIndex);
253       targetPosition = translatedStartIndex;
254     } else {
255       this.translatedContent_ = this.displayedContent_ = cells;
256       targetPosition = 0;
257     }
258     this.panStrategy_.setContent(this.translatedContent_, targetPosition);
259     this.refresh_();
260   }.bind(this);
262   var translator = this.translatorManager_.getExpandingTranslator();
263   if (!translator) {
264     writeTranslatedContent(new ArrayBuffer(0), [], []);
265   } else {
266     translator.translate(
267         newContent.text,
268         newExpansionType,
269         writeTranslatedContent);
270   }
275  * @param {cvox.BrailleKeyEvent} event The key event.
276  * @private
277  */
278 cvox.BrailleDisplayManager.prototype.onKeyEvent_ = function(event) {
279   switch (event.command) {
280     case cvox.BrailleKeyCommand.PAN_LEFT:
281       this.panLeft_();
282       break;
283     case cvox.BrailleKeyCommand.PAN_RIGHT:
284       this.panRight_();
285       break;
286     case cvox.BrailleKeyCommand.ROUTING:
287       event.displayPosition = this.brailleToTextPosition_(
288           event.displayPosition + this.panStrategy_.viewPort.start);
289       // fall through
290     default:
291       this.commandListener_(event, this.content_);
292       break;
293   }
298  * Shift the display by one full display size and refresh the content.
299  * Sends the appropriate command if the display is already at the leftmost
300  * position.
301  * @private
302  */
303 cvox.BrailleDisplayManager.prototype.panLeft_ = function() {
304   if (this.panStrategy_.previous()) {
305     this.refresh_();
306   } else {
307     this.commandListener_({
308       command: cvox.BrailleKeyCommand.PAN_LEFT
309     }, this.content_);
310   }
315  * Shifts the display position to the right by one full display size and
316  * refreshes the content.  Sends the appropriate command if the display is
317  * already at its rightmost position.
318  * @private
319  */
320 cvox.BrailleDisplayManager.prototype.panRight_ = function() {
321   if (this.panStrategy_.next()) {
322     this.refresh_();
323   } else {
324     this.commandListener_({
325       command: cvox.BrailleKeyCommand.PAN_RIGHT
326     }, this.content_);
327   }
332  * Writes a cursor in the specified range into translated content.
333  * @param {ArrayBuffer} buffer Buffer to add cursor to.
334  * @param {number} startIndex The start index to place the cursor.
335  * @param {number} endIndex The end index to place the cursor (exclusive).
336  * @private
337  */
338 cvox.BrailleDisplayManager.prototype.writeCursor_ = function(
339     buffer, startIndex, endIndex) {
340   if (startIndex < 0 || startIndex >= buffer.byteLength ||
341       endIndex < startIndex || endIndex > buffer.byteLength) {
342     return;
343   }
344   if (startIndex == endIndex) {
345     endIndex = startIndex + 1;
346   }
347   var dataView = new DataView(buffer);
348   while (startIndex < endIndex) {
349     var value = dataView.getUint8(startIndex);
350     value |= cvox.BrailleDisplayManager.CURSOR_DOTS_;
351     dataView.setUint8(startIndex, value);
352     startIndex++;
353   }
358  * Returns the text position corresponding to an absolute braille position,
359  * that is not accounting for the current pan position.
360  * @private
361  * @param {number} braillePosition Braille position relative to the startof
362  *        the translated content.
363  * @return {number} The mapped position in code units.
364  */
365 cvox.BrailleDisplayManager.prototype.brailleToTextPosition_ =
366     function(braillePosition) {
367   var mapping = this.brailleToText_;
368   if (braillePosition < 0) {
369     // This shouldn't happen.
370     console.error('WARNING: Braille position < 0: ' + braillePosition);
371     return 0;
372   } else if (braillePosition >= mapping.length) {
373     // This happens when the user clicks on the right part of the display
374     // when it is not entirely filled with content.  Allow addressing the
375     // position after the last character.
376     return this.content_.text.getLength();
377   } else {
378     return mapping[braillePosition];
379   }
384  * @param {boolean} wordWrap
385  * @private
386  */
387 cvox.BrailleDisplayManager.prototype.updatePanStrategy_ = function(wordWrap) {
388   var newStrategy = wordWrap ? new cvox.WrappingPanStrategy() :
389       new cvox.FixedPanStrategy();
390   newStrategy.setDisplaySize(this.displayState_.textCellCount || 0);
391   newStrategy.setContent(this.translatedContent_,
392                          this.panStrategy_.viewPort.start);
393   this.panStrategy_ = newStrategy;
394   this.refresh_();