accessible/src/jsat/Presentation.jsm

Wed, 31 Dec 2014 07:16:47 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 07:16:47 +0100
branch
TOR_BUG_9701
changeset 3
141e0f1194b1
permissions
-rw-r--r--

Revert simplistic fix pending revisit of Mozilla integration attempt.

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 const Cr = Components.results;
michael@0 11
michael@0 12 Cu.import('resource://gre/modules/XPCOMUtils.jsm');
michael@0 13 XPCOMUtils.defineLazyModuleGetter(this, 'Utils',
michael@0 14 'resource://gre/modules/accessibility/Utils.jsm');
michael@0 15 XPCOMUtils.defineLazyModuleGetter(this, 'Logger',
michael@0 16 'resource://gre/modules/accessibility/Utils.jsm');
michael@0 17 XPCOMUtils.defineLazyModuleGetter(this, 'PivotContext',
michael@0 18 'resource://gre/modules/accessibility/Utils.jsm');
michael@0 19 XPCOMUtils.defineLazyModuleGetter(this, 'UtteranceGenerator',
michael@0 20 'resource://gre/modules/accessibility/OutputGenerator.jsm');
michael@0 21 XPCOMUtils.defineLazyModuleGetter(this, 'BrailleGenerator',
michael@0 22 'resource://gre/modules/accessibility/OutputGenerator.jsm');
michael@0 23 XPCOMUtils.defineLazyModuleGetter(this, 'Roles',
michael@0 24 'resource://gre/modules/accessibility/Constants.jsm');
michael@0 25 XPCOMUtils.defineLazyModuleGetter(this, 'States',
michael@0 26 'resource://gre/modules/accessibility/Constants.jsm');
michael@0 27
michael@0 28 this.EXPORTED_SYMBOLS = ['Presentation'];
michael@0 29
michael@0 30 /**
michael@0 31 * The interface for all presenter classes. A presenter could be, for example,
michael@0 32 * a speech output module, or a visual cursor indicator.
michael@0 33 */
michael@0 34 function Presenter() {}
michael@0 35
michael@0 36 Presenter.prototype = {
michael@0 37 /**
michael@0 38 * The type of presenter. Used for matching it with the appropriate output method.
michael@0 39 */
michael@0 40 type: 'Base',
michael@0 41
michael@0 42 /**
michael@0 43 * The virtual cursor's position changed.
michael@0 44 * @param {PivotContext} aContext the context object for the new pivot
michael@0 45 * position.
michael@0 46 * @param {int} aReason the reason for the pivot change.
michael@0 47 * See nsIAccessiblePivot.
michael@0 48 */
michael@0 49 pivotChanged: function pivotChanged(aContext, aReason) {},
michael@0 50
michael@0 51 /**
michael@0 52 * An object's action has been invoked.
michael@0 53 * @param {nsIAccessible} aObject the object that has been invoked.
michael@0 54 * @param {string} aActionName the name of the action.
michael@0 55 */
michael@0 56 actionInvoked: function actionInvoked(aObject, aActionName) {},
michael@0 57
michael@0 58 /**
michael@0 59 * Text has changed, either by the user or by the system. TODO.
michael@0 60 */
michael@0 61 textChanged: function textChanged(aIsInserted, aStartOffset,
michael@0 62 aLength, aText,
michael@0 63 aModifiedText) {},
michael@0 64
michael@0 65 /**
michael@0 66 * Text selection has changed. TODO.
michael@0 67 */
michael@0 68 textSelectionChanged: function textSelectionChanged(aText, aStart, aEnd, aOldStart, aOldEnd, aIsFromUser) {},
michael@0 69
michael@0 70 /**
michael@0 71 * Selection has changed. TODO.
michael@0 72 * @param {nsIAccessible} aObject the object that has been selected.
michael@0 73 */
michael@0 74 selectionChanged: function selectionChanged(aObject) {},
michael@0 75
michael@0 76 /**
michael@0 77 * Value has changed.
michael@0 78 * @param {nsIAccessible} aAccessible the object whose value has changed.
michael@0 79 */
michael@0 80 valueChanged: function valueChanged(aAccessible) {},
michael@0 81
michael@0 82 /**
michael@0 83 * The tab, or the tab's document state has changed.
michael@0 84 * @param {nsIAccessible} aDocObj the tab document accessible that has had its
michael@0 85 * state changed, or null if the tab has no associated document yet.
michael@0 86 * @param {string} aPageState the state name for the tab, valid states are:
michael@0 87 * 'newtab', 'loading', 'newdoc', 'loaded', 'stopped', and 'reload'.
michael@0 88 */
michael@0 89 tabStateChanged: function tabStateChanged(aDocObj, aPageState) {},
michael@0 90
michael@0 91 /**
michael@0 92 * The current tab has changed.
michael@0 93 * @param {PivotContext} aDocContext context object for tab's
michael@0 94 * document.
michael@0 95 * @param {PivotContext} aVCContext context object for tab's current
michael@0 96 * virtual cursor position.
michael@0 97 */
michael@0 98 tabSelected: function tabSelected(aDocContext, aVCContext) {},
michael@0 99
michael@0 100 /**
michael@0 101 * The viewport has changed, either a scroll, pan, zoom, or
michael@0 102 * landscape/portrait toggle.
michael@0 103 * @param {Window} aWindow window of viewport that changed.
michael@0 104 */
michael@0 105 viewportChanged: function viewportChanged(aWindow) {},
michael@0 106
michael@0 107 /**
michael@0 108 * We have entered or left text editing mode.
michael@0 109 */
michael@0 110 editingModeChanged: function editingModeChanged(aIsEditing) {},
michael@0 111
michael@0 112 /**
michael@0 113 * Announce something. Typically an app state change.
michael@0 114 */
michael@0 115 announce: function announce(aAnnouncement) {},
michael@0 116
michael@0 117
michael@0 118
michael@0 119 /**
michael@0 120 * Announce a live region.
michael@0 121 * @param {PivotContext} aContext context object for an accessible.
michael@0 122 * @param {boolean} aIsPolite A politeness level for a live region.
michael@0 123 * @param {boolean} aIsHide An indicator of hide/remove event.
michael@0 124 * @param {string} aModifiedText Optional modified text.
michael@0 125 */
michael@0 126 liveRegion: function liveRegionShown(aContext, aIsPolite, aIsHide,
michael@0 127 aModifiedText) {}
michael@0 128 };
michael@0 129
michael@0 130 /**
michael@0 131 * Visual presenter. Draws a box around the virtual cursor's position.
michael@0 132 */
michael@0 133
michael@0 134 this.VisualPresenter = function VisualPresenter() {
michael@0 135 this._displayedAccessibles = new WeakMap();
michael@0 136 };
michael@0 137
michael@0 138 VisualPresenter.prototype = {
michael@0 139 __proto__: Presenter.prototype,
michael@0 140
michael@0 141 type: 'Visual',
michael@0 142
michael@0 143 /**
michael@0 144 * The padding in pixels between the object and the highlight border.
michael@0 145 */
michael@0 146 BORDER_PADDING: 2,
michael@0 147
michael@0 148 viewportChanged: function VisualPresenter_viewportChanged(aWindow) {
michael@0 149 let currentDisplay = this._displayedAccessibles.get(aWindow);
michael@0 150 if (!currentDisplay) {
michael@0 151 return null;
michael@0 152 }
michael@0 153
michael@0 154 let currentAcc = currentDisplay.accessible;
michael@0 155 let start = currentDisplay.startOffset;
michael@0 156 let end = currentDisplay.endOffset;
michael@0 157 if (Utils.isAliveAndVisible(currentAcc)) {
michael@0 158 let bounds = (start === -1 && end === -1) ? Utils.getBounds(currentAcc) :
michael@0 159 Utils.getTextBounds(currentAcc, start, end);
michael@0 160
michael@0 161 return {
michael@0 162 type: this.type,
michael@0 163 details: {
michael@0 164 method: 'showBounds',
michael@0 165 bounds: bounds,
michael@0 166 padding: this.BORDER_PADDING
michael@0 167 }
michael@0 168 };
michael@0 169 }
michael@0 170
michael@0 171 return null;
michael@0 172 },
michael@0 173
michael@0 174 pivotChanged: function VisualPresenter_pivotChanged(aContext, aReason) {
michael@0 175 if (!aContext.accessible) {
michael@0 176 // XXX: Don't hide because another vc may be using the highlight.
michael@0 177 return null;
michael@0 178 }
michael@0 179
michael@0 180 this._displayedAccessibles.set(aContext.accessible.document.window,
michael@0 181 { accessible: aContext.accessibleForBounds,
michael@0 182 startOffset: aContext.startOffset,
michael@0 183 endOffset: aContext.endOffset });
michael@0 184
michael@0 185 try {
michael@0 186 aContext.accessibleForBounds.scrollTo(
michael@0 187 Ci.nsIAccessibleScrollType.SCROLL_TYPE_ANYWHERE);
michael@0 188
michael@0 189 let bounds = (aContext.startOffset === -1 && aContext.endOffset === -1) ?
michael@0 190 aContext.bounds : Utils.getTextBounds(aContext.accessibleForBounds,
michael@0 191 aContext.startOffset,
michael@0 192 aContext.endOffset);
michael@0 193
michael@0 194 return {
michael@0 195 type: this.type,
michael@0 196 details: {
michael@0 197 method: 'showBounds',
michael@0 198 bounds: bounds,
michael@0 199 padding: this.BORDER_PADDING
michael@0 200 }
michael@0 201 };
michael@0 202 } catch (e) {
michael@0 203 Logger.logException(e, 'Failed to get bounds');
michael@0 204 return null;
michael@0 205 }
michael@0 206 },
michael@0 207
michael@0 208 tabSelected: function VisualPresenter_tabSelected(aDocContext, aVCContext) {
michael@0 209 return this.pivotChanged(aVCContext, Ci.nsIAccessiblePivot.REASON_NONE);
michael@0 210 },
michael@0 211
michael@0 212 tabStateChanged: function VisualPresenter_tabStateChanged(aDocObj,
michael@0 213 aPageState) {
michael@0 214 if (aPageState == 'newdoc')
michael@0 215 return {type: this.type, details: {method: 'hideBounds'}};
michael@0 216
michael@0 217 return null;
michael@0 218 },
michael@0 219
michael@0 220 announce: function VisualPresenter_announce(aAnnouncement) {
michael@0 221 return {
michael@0 222 type: this.type,
michael@0 223 details: {
michael@0 224 method: 'showAnnouncement',
michael@0 225 text: aAnnouncement,
michael@0 226 duration: 1000
michael@0 227 }
michael@0 228 };
michael@0 229 }
michael@0 230 };
michael@0 231
michael@0 232 /**
michael@0 233 * Android presenter. Fires Android a11y events.
michael@0 234 */
michael@0 235
michael@0 236 this.AndroidPresenter = function AndroidPresenter() {};
michael@0 237
michael@0 238 AndroidPresenter.prototype = {
michael@0 239 __proto__: Presenter.prototype,
michael@0 240
michael@0 241 type: 'Android',
michael@0 242
michael@0 243 // Android AccessibilityEvent type constants.
michael@0 244 ANDROID_VIEW_CLICKED: 0x01,
michael@0 245 ANDROID_VIEW_LONG_CLICKED: 0x02,
michael@0 246 ANDROID_VIEW_SELECTED: 0x04,
michael@0 247 ANDROID_VIEW_FOCUSED: 0x08,
michael@0 248 ANDROID_VIEW_TEXT_CHANGED: 0x10,
michael@0 249 ANDROID_WINDOW_STATE_CHANGED: 0x20,
michael@0 250 ANDROID_VIEW_HOVER_ENTER: 0x80,
michael@0 251 ANDROID_VIEW_HOVER_EXIT: 0x100,
michael@0 252 ANDROID_VIEW_SCROLLED: 0x1000,
michael@0 253 ANDROID_VIEW_TEXT_SELECTION_CHANGED: 0x2000,
michael@0 254 ANDROID_ANNOUNCEMENT: 0x4000,
michael@0 255 ANDROID_VIEW_ACCESSIBILITY_FOCUSED: 0x8000,
michael@0 256 ANDROID_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY: 0x20000,
michael@0 257
michael@0 258 pivotChanged: function AndroidPresenter_pivotChanged(aContext, aReason) {
michael@0 259 if (!aContext.accessible)
michael@0 260 return null;
michael@0 261
michael@0 262 let androidEvents = [];
michael@0 263
michael@0 264 let isExploreByTouch = (aReason == Ci.nsIAccessiblePivot.REASON_POINT &&
michael@0 265 Utils.AndroidSdkVersion >= 14);
michael@0 266 let focusEventType = (Utils.AndroidSdkVersion >= 16) ?
michael@0 267 this.ANDROID_VIEW_ACCESSIBILITY_FOCUSED :
michael@0 268 this.ANDROID_VIEW_FOCUSED;
michael@0 269
michael@0 270 if (isExploreByTouch) {
michael@0 271 // This isn't really used by TalkBack so this is a half-hearted attempt
michael@0 272 // for now.
michael@0 273 androidEvents.push({eventType: this.ANDROID_VIEW_HOVER_EXIT, text: []});
michael@0 274 }
michael@0 275
michael@0 276 let brailleOutput = {};
michael@0 277 if (Utils.AndroidSdkVersion >= 16) {
michael@0 278 if (!this._braillePresenter) {
michael@0 279 this._braillePresenter = new BraillePresenter();
michael@0 280 }
michael@0 281 brailleOutput = this._braillePresenter.pivotChanged(aContext, aReason).
michael@0 282 details;
michael@0 283 }
michael@0 284
michael@0 285 if (aReason === Ci.nsIAccessiblePivot.REASON_TEXT) {
michael@0 286 if (Utils.AndroidSdkVersion >= 16) {
michael@0 287 let adjustedText = aContext.textAndAdjustedOffsets;
michael@0 288
michael@0 289 androidEvents.push({
michael@0 290 eventType: this.ANDROID_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY,
michael@0 291 text: [adjustedText.text],
michael@0 292 fromIndex: adjustedText.startOffset,
michael@0 293 toIndex: adjustedText.endOffset
michael@0 294 });
michael@0 295 }
michael@0 296 } else {
michael@0 297 let state = Utils.getState(aContext.accessible);
michael@0 298 androidEvents.push({eventType: (isExploreByTouch) ?
michael@0 299 this.ANDROID_VIEW_HOVER_ENTER : focusEventType,
michael@0 300 text: UtteranceGenerator.genForContext(aContext).output,
michael@0 301 bounds: aContext.bounds,
michael@0 302 clickable: aContext.accessible.actionCount > 0,
michael@0 303 checkable: state.contains(States.CHECKABLE),
michael@0 304 checked: state.contains(States.CHECKED),
michael@0 305 brailleOutput: brailleOutput});
michael@0 306 }
michael@0 307
michael@0 308
michael@0 309 return {
michael@0 310 type: this.type,
michael@0 311 details: androidEvents
michael@0 312 };
michael@0 313 },
michael@0 314
michael@0 315 actionInvoked: function AndroidPresenter_actionInvoked(aObject, aActionName) {
michael@0 316 let state = Utils.getState(aObject);
michael@0 317
michael@0 318 // Checkable objects will have a state changed event we will use instead.
michael@0 319 if (state.contains(States.CHECKABLE))
michael@0 320 return null;
michael@0 321
michael@0 322 return {
michael@0 323 type: this.type,
michael@0 324 details: [{
michael@0 325 eventType: this.ANDROID_VIEW_CLICKED,
michael@0 326 text: UtteranceGenerator.genForAction(aObject, aActionName),
michael@0 327 checked: state.contains(States.CHECKED)
michael@0 328 }]
michael@0 329 };
michael@0 330 },
michael@0 331
michael@0 332 tabSelected: function AndroidPresenter_tabSelected(aDocContext, aVCContext) {
michael@0 333 // Send a pivot change message with the full context utterance for this doc.
michael@0 334 return this.pivotChanged(aVCContext, Ci.nsIAccessiblePivot.REASON_NONE);
michael@0 335 },
michael@0 336
michael@0 337 tabStateChanged: function AndroidPresenter_tabStateChanged(aDocObj,
michael@0 338 aPageState) {
michael@0 339 return this.announce(
michael@0 340 UtteranceGenerator.genForTabStateChange(aDocObj, aPageState).join(' '));
michael@0 341 },
michael@0 342
michael@0 343 textChanged: function AndroidPresenter_textChanged(aIsInserted, aStart,
michael@0 344 aLength, aText,
michael@0 345 aModifiedText) {
michael@0 346 let eventDetails = {
michael@0 347 eventType: this.ANDROID_VIEW_TEXT_CHANGED,
michael@0 348 text: [aText],
michael@0 349 fromIndex: aStart,
michael@0 350 removedCount: 0,
michael@0 351 addedCount: 0
michael@0 352 };
michael@0 353
michael@0 354 if (aIsInserted) {
michael@0 355 eventDetails.addedCount = aLength;
michael@0 356 eventDetails.beforeText =
michael@0 357 aText.substring(0, aStart) + aText.substring(aStart + aLength);
michael@0 358 } else {
michael@0 359 eventDetails.removedCount = aLength;
michael@0 360 eventDetails.beforeText =
michael@0 361 aText.substring(0, aStart) + aModifiedText + aText.substring(aStart);
michael@0 362 }
michael@0 363
michael@0 364 return {type: this.type, details: [eventDetails]};
michael@0 365 },
michael@0 366
michael@0 367 textSelectionChanged: function AndroidPresenter_textSelectionChanged(aText, aStart,
michael@0 368 aEnd, aOldStart,
michael@0 369 aOldEnd, aIsFromUser) {
michael@0 370 let androidEvents = [];
michael@0 371
michael@0 372 if (Utils.AndroidSdkVersion >= 14 && !aIsFromUser) {
michael@0 373 if (!this._braillePresenter) {
michael@0 374 this._braillePresenter = new BraillePresenter();
michael@0 375 }
michael@0 376 let brailleOutput = this._braillePresenter.textSelectionChanged(aText, aStart, aEnd,
michael@0 377 aOldStart, aOldEnd,
michael@0 378 aIsFromUser).details;
michael@0 379
michael@0 380 androidEvents.push({
michael@0 381 eventType: this.ANDROID_VIEW_TEXT_SELECTION_CHANGED,
michael@0 382 text: [aText],
michael@0 383 fromIndex: aStart,
michael@0 384 toIndex: aEnd,
michael@0 385 itemCount: aText.length,
michael@0 386 brailleOutput: brailleOutput
michael@0 387 });
michael@0 388 }
michael@0 389
michael@0 390 if (Utils.AndroidSdkVersion >= 16 && aIsFromUser) {
michael@0 391 let [from, to] = aOldStart < aStart ? [aOldStart, aStart] : [aStart, aOldStart];
michael@0 392 androidEvents.push({
michael@0 393 eventType: this.ANDROID_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY,
michael@0 394 text: [aText],
michael@0 395 fromIndex: from,
michael@0 396 toIndex: to
michael@0 397 });
michael@0 398 }
michael@0 399
michael@0 400 return {
michael@0 401 type: this.type,
michael@0 402 details: androidEvents
michael@0 403 };
michael@0 404 },
michael@0 405
michael@0 406 viewportChanged: function AndroidPresenter_viewportChanged(aWindow) {
michael@0 407 if (Utils.AndroidSdkVersion < 14)
michael@0 408 return null;
michael@0 409
michael@0 410 return {
michael@0 411 type: this.type,
michael@0 412 details: [{
michael@0 413 eventType: this.ANDROID_VIEW_SCROLLED,
michael@0 414 text: [],
michael@0 415 scrollX: aWindow.scrollX,
michael@0 416 scrollY: aWindow.scrollY,
michael@0 417 maxScrollX: aWindow.scrollMaxX,
michael@0 418 maxScrollY: aWindow.scrollMaxY
michael@0 419 }]
michael@0 420 };
michael@0 421 },
michael@0 422
michael@0 423 editingModeChanged: function AndroidPresenter_editingModeChanged(aIsEditing) {
michael@0 424 return this.announce(
michael@0 425 UtteranceGenerator.genForEditingMode(aIsEditing).join(' '));
michael@0 426 },
michael@0 427
michael@0 428 announce: function AndroidPresenter_announce(aAnnouncement) {
michael@0 429 return {
michael@0 430 type: this.type,
michael@0 431 details: [{
michael@0 432 eventType: (Utils.AndroidSdkVersion >= 16) ?
michael@0 433 this.ANDROID_ANNOUNCEMENT : this.ANDROID_VIEW_TEXT_CHANGED,
michael@0 434 text: [aAnnouncement],
michael@0 435 addedCount: aAnnouncement.length,
michael@0 436 removedCount: 0,
michael@0 437 fromIndex: 0
michael@0 438 }]
michael@0 439 };
michael@0 440 },
michael@0 441
michael@0 442 liveRegion: function AndroidPresenter_liveRegion(aContext, aIsPolite,
michael@0 443 aIsHide, aModifiedText) {
michael@0 444 return this.announce(
michael@0 445 UtteranceGenerator.genForLiveRegion(aContext, aIsHide,
michael@0 446 aModifiedText).join(' '));
michael@0 447 }
michael@0 448 };
michael@0 449
michael@0 450 /**
michael@0 451 * A speech presenter for direct TTS output
michael@0 452 */
michael@0 453
michael@0 454 this.SpeechPresenter = function SpeechPresenter() {};
michael@0 455
michael@0 456 SpeechPresenter.prototype = {
michael@0 457 __proto__: Presenter.prototype,
michael@0 458
michael@0 459 type: 'Speech',
michael@0 460
michael@0 461 pivotChanged: function SpeechPresenter_pivotChanged(aContext, aReason) {
michael@0 462 if (!aContext.accessible)
michael@0 463 return null;
michael@0 464
michael@0 465 return {
michael@0 466 type: this.type,
michael@0 467 details: {
michael@0 468 actions: [
michael@0 469 {method: 'playEarcon',
michael@0 470 data: aContext.accessible.role === Roles.KEY ?
michael@0 471 'virtual_cursor_key' : 'virtual_cursor_move',
michael@0 472 options: {}},
michael@0 473 {method: 'speak',
michael@0 474 data: UtteranceGenerator.genForContext(aContext).output.join(' '),
michael@0 475 options: {enqueue: true}}
michael@0 476 ]
michael@0 477 }
michael@0 478 };
michael@0 479 },
michael@0 480
michael@0 481 valueChanged: function SpeechPresenter_valueChanged(aAccessible) {
michael@0 482 return {
michael@0 483 type: this.type,
michael@0 484 details: {
michael@0 485 actions: [
michael@0 486 { method: 'speak',
michael@0 487 data: aAccessible.value,
michael@0 488 options: { enqueue: false } }
michael@0 489 ]
michael@0 490 }
michael@0 491 }
michael@0 492 },
michael@0 493
michael@0 494 actionInvoked: function SpeechPresenter_actionInvoked(aObject, aActionName) {
michael@0 495 let actions = [];
michael@0 496 if (aActionName === 'click') {
michael@0 497 actions.push({method: 'playEarcon',
michael@0 498 data: 'clicked',
michael@0 499 options: {}});
michael@0 500 } else {
michael@0 501 actions.push({method: 'speak',
michael@0 502 data: UtteranceGenerator.genForAction(aObject, aActionName).join(' '),
michael@0 503 options: {enqueue: false}});
michael@0 504 }
michael@0 505 return { type: this.type, details: { actions: actions } };
michael@0 506 },
michael@0 507
michael@0 508 liveRegion: function SpeechPresenter_liveRegion(aContext, aIsPolite, aIsHide,
michael@0 509 aModifiedText) {
michael@0 510 return {
michael@0 511 type: this.type,
michael@0 512 details: {
michael@0 513 actions: [{
michael@0 514 method: 'speak',
michael@0 515 data: UtteranceGenerator.genForLiveRegion(aContext, aIsHide,
michael@0 516 aModifiedText).join(' '),
michael@0 517 options: {enqueue: aIsPolite}
michael@0 518 }]
michael@0 519 }
michael@0 520 };
michael@0 521 },
michael@0 522
michael@0 523 announce: function SpeechPresenter_announce(aAnnouncement) {
michael@0 524 return {
michael@0 525 type: this.type,
michael@0 526 details: {
michael@0 527 actions: [{
michael@0 528 method: 'speak', data: aAnnouncement, options: { enqueue: false }
michael@0 529 }]
michael@0 530 }
michael@0 531 };
michael@0 532 }
michael@0 533 };
michael@0 534
michael@0 535 /**
michael@0 536 * A haptic presenter
michael@0 537 */
michael@0 538
michael@0 539 this.HapticPresenter = function HapticPresenter() {};
michael@0 540
michael@0 541 HapticPresenter.prototype = {
michael@0 542 __proto__: Presenter.prototype,
michael@0 543
michael@0 544 type: 'Haptic',
michael@0 545
michael@0 546 PIVOT_CHANGE_PATTERN: [40],
michael@0 547
michael@0 548 pivotChanged: function HapticPresenter_pivotChanged(aContext, aReason) {
michael@0 549 return { type: this.type, details: { pattern: this.PIVOT_CHANGE_PATTERN } };
michael@0 550 }
michael@0 551 };
michael@0 552
michael@0 553 /**
michael@0 554 * A braille presenter
michael@0 555 */
michael@0 556
michael@0 557 this.BraillePresenter = function BraillePresenter() {};
michael@0 558
michael@0 559 BraillePresenter.prototype = {
michael@0 560 __proto__: Presenter.prototype,
michael@0 561
michael@0 562 type: 'Braille',
michael@0 563
michael@0 564 pivotChanged: function BraillePresenter_pivotChanged(aContext, aReason) {
michael@0 565 if (!aContext.accessible) {
michael@0 566 return null;
michael@0 567 }
michael@0 568
michael@0 569 let brailleOutput = BrailleGenerator.genForContext(aContext);
michael@0 570 brailleOutput.output = brailleOutput.output.join(' ');
michael@0 571 brailleOutput.selectionStart = 0;
michael@0 572 brailleOutput.selectionEnd = 0;
michael@0 573
michael@0 574 return { type: this.type, details: brailleOutput };
michael@0 575 },
michael@0 576
michael@0 577 textSelectionChanged: function BraillePresenter_textSelectionChanged(aText, aStart,
michael@0 578 aEnd, aOldStart,
michael@0 579 aOldEnd, aIsFromUser) {
michael@0 580 return { type: this.type,
michael@0 581 details: { selectionStart: aStart,
michael@0 582 selectionEnd: aEnd } };
michael@0 583 },
michael@0 584
michael@0 585
michael@0 586 };
michael@0 587
michael@0 588 this.Presentation = {
michael@0 589 get presenters() {
michael@0 590 delete this.presenters;
michael@0 591 let presenterMap = {
michael@0 592 'mobile/android': [VisualPresenter, AndroidPresenter],
michael@0 593 'b2g': [VisualPresenter, SpeechPresenter, HapticPresenter],
michael@0 594 'browser': [VisualPresenter, SpeechPresenter, HapticPresenter,
michael@0 595 AndroidPresenter]
michael@0 596 };
michael@0 597 this.presenters = [new P() for (P of presenterMap[Utils.MozBuildApp])];
michael@0 598 return this.presenters;
michael@0 599 },
michael@0 600
michael@0 601 pivotChanged: function Presentation_pivotChanged(aPosition, aOldPosition, aReason,
michael@0 602 aStartOffset, aEndOffset) {
michael@0 603 let context = new PivotContext(aPosition, aOldPosition, aStartOffset, aEndOffset);
michael@0 604 return [p.pivotChanged(context, aReason)
michael@0 605 for each (p in this.presenters)];
michael@0 606 },
michael@0 607
michael@0 608 actionInvoked: function Presentation_actionInvoked(aObject, aActionName) {
michael@0 609 return [p.actionInvoked(aObject, aActionName)
michael@0 610 for each (p in this.presenters)];
michael@0 611 },
michael@0 612
michael@0 613 textChanged: function Presentation_textChanged(aIsInserted, aStartOffset,
michael@0 614 aLength, aText,
michael@0 615 aModifiedText) {
michael@0 616 return [p.textChanged(aIsInserted, aStartOffset, aLength,
michael@0 617 aText, aModifiedText)
michael@0 618 for each (p in this.presenters)];
michael@0 619 },
michael@0 620
michael@0 621 textSelectionChanged: function textSelectionChanged(aText, aStart, aEnd,
michael@0 622 aOldStart, aOldEnd,
michael@0 623 aIsFromUser) {
michael@0 624 return [p.textSelectionChanged(aText, aStart, aEnd,
michael@0 625 aOldStart, aOldEnd, aIsFromUser)
michael@0 626 for each (p in this.presenters)];
michael@0 627 },
michael@0 628
michael@0 629 valueChanged: function valueChanged(aAccessible) {
michael@0 630 return [ p.valueChanged(aAccessible) for (p of this.presenters) ];
michael@0 631 },
michael@0 632
michael@0 633 tabStateChanged: function Presentation_tabStateChanged(aDocObj, aPageState) {
michael@0 634 return [p.tabStateChanged(aDocObj, aPageState)
michael@0 635 for each (p in this.presenters)];
michael@0 636 },
michael@0 637
michael@0 638 viewportChanged: function Presentation_viewportChanged(aWindow) {
michael@0 639 return [p.viewportChanged(aWindow)
michael@0 640 for each (p in this.presenters)];
michael@0 641 },
michael@0 642
michael@0 643 editingModeChanged: function Presentation_editingModeChanged(aIsEditing) {
michael@0 644 return [p.editingModeChanged(aIsEditing)
michael@0 645 for each (p in this.presenters)];
michael@0 646 },
michael@0 647
michael@0 648 announce: function Presentation_announce(aAnnouncement) {
michael@0 649 // XXX: Typically each presenter uses the UtteranceGenerator,
michael@0 650 // but there really isn't a point here.
michael@0 651 return [p.announce(UtteranceGenerator.genForAnnouncement(aAnnouncement)[0])
michael@0 652 for each (p in this.presenters)];
michael@0 653 },
michael@0 654
michael@0 655 liveRegion: function Presentation_liveRegion(aAccessible, aIsPolite, aIsHide,
michael@0 656 aModifiedText) {
michael@0 657 let context;
michael@0 658 if (!aModifiedText) {
michael@0 659 context = new PivotContext(aAccessible, null, -1, -1, true,
michael@0 660 aIsHide ? true : false);
michael@0 661 }
michael@0 662 return [p.liveRegion(context, aIsPolite, aIsHide, aModifiedText) for (
michael@0 663 p of this.presenters)];
michael@0 664 }
michael@0 665 };

mercurial