Bug 1030470 - (part 1) moved the non-android localization part out of AccessFu to be handled in Gaia. r=eeejay
authorYura Zenevich <yzenevich@mozilla.com>
Wed, 06 Aug 2014 09:38:50 -0400
changeset 198190 d6059e806022b7b93748c32ba8fbb1e4911b6bfa
parent 198189 d99f8aaeb32b93aff5a4b0c447a0ccb5aea9b334
child 198191 860146d2669faa35a8e1014fd38a297c70ea685c
push id27264
push usernigelbabu@gmail.com
push dateThu, 07 Aug 2014 03:31:37 +0000
treeherdermozilla-central@afcb3af79d09 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerseeejay
bugs1030470
milestone34.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1030470 - (part 1) moved the non-android localization part out of AccessFu to be handled in Gaia. r=eeejay --- accessible/jsat/AccessFu.jsm | 176 ++----- accessible/jsat/OutputGenerator.jsm | 337 +++++-------- accessible/jsat/Presentation.jsm | 193 ++++---- accessible/jsat/Utils.jsm | 43 +- accessible/tests/mochitest/jsat/jsatcommon.js | 19 +- accessible/tests/mochitest/jsat/output.js | 12 +- accessible/tests/mochitest/jsat/test_alive.html | 22 +- .../mochitest/jsat/test_content_integration.html | 95 ++-- .../tests/mochitest/jsat/test_content_text.html | 8 +- .../tests/mochitest/jsat/test_explicit_names.html | 73 +-- .../tests/mochitest/jsat/test_landmarks.html | 125 +++-- .../tests/mochitest/jsat/test_live_regions.html | 144 +++--- accessible/tests/mochitest/jsat/test_output.html | 526 +++++++++++---------- accessible/tests/mochitest/jsat/test_tables.html | 492 ++++++++++++++----- .../en-US/chrome/accessibility/AccessFu.properties | 103 +++- 15 files changed, 1306 insertions(+), 1062 deletions(-)
accessible/jsat/AccessFu.jsm
accessible/jsat/OutputGenerator.jsm
accessible/jsat/Presentation.jsm
accessible/jsat/Utils.jsm
accessible/tests/mochitest/jsat/jsatcommon.js
accessible/tests/mochitest/jsat/output.js
accessible/tests/mochitest/jsat/test_alive.html
accessible/tests/mochitest/jsat/test_content_integration.html
accessible/tests/mochitest/jsat/test_content_text.html
accessible/tests/mochitest/jsat/test_explicit_names.html
accessible/tests/mochitest/jsat/test_landmarks.html
accessible/tests/mochitest/jsat/test_live_regions.html
accessible/tests/mochitest/jsat/test_output.html
accessible/tests/mochitest/jsat/test_tables.html
dom/locales/en-US/chrome/accessibility/AccessFu.properties
--- a/accessible/jsat/AccessFu.jsm
+++ b/accessible/jsat/AccessFu.jsm
@@ -7,17 +7,16 @@
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cu = Components.utils;
 const Cr = Components.results;
 
 this.EXPORTED_SYMBOLS = ['AccessFu'];
 
 Cu.import('resource://gre/modules/Services.jsm');
-
 Cu.import('resource://gre/modules/accessibility/Utils.jsm');
 
 const ACCESSFU_DISABLE = 0;
 const ACCESSFU_ENABLE = 1;
 const ACCESSFU_AUTO = 2;
 
 const SCREENREADER_SETTING = 'accessibility.screenreader';
 
@@ -130,18 +129,17 @@ this.AccessFu = {
     Utils.win.addEventListener('TabSelect', this);
 
     if (this.readyCallback) {
       this.readyCallback();
       delete this.readyCallback;
     }
 
     if (Utils.MozBuildApp !== 'mobile/android') {
-      this.announce(
-        Utils.stringBundle.GetStringFromName('screenReaderStarted'));
+      this.announce('screenReaderStarted');
     }
   },
 
   /**
    * Disable AccessFu and return to default interaction mode.
    */
   _disable: function _disable() {
     if (!this._enabled)
@@ -149,18 +147,17 @@ this.AccessFu = {
 
     this._enabled = false;
 
     Logger.info('Disabled');
 
     Utils.win.document.removeChild(this.stylesheet.get());
 
     if (Utils.MozBuildApp !== 'mobile/android') {
-      this.announce(
-        Utils.stringBundle.GetStringFromName('screenReaderStopped'));
+      this.announce('screenReaderStopped');
     }
 
     for each (let mm in Utils.AllMessageManagers) {
       mm.sendAsyncMessage('AccessFu:Stop');
       this._removeMessageListeners(mm);
     }
 
     this.Input.stop();
@@ -504,130 +501,51 @@ var Output = {
       braille.text = this.text;
       this.selectionStart = braille.selectionStart = aSelection.selectionStart + this.startOffset;
       this.selectionEnd = braille.selectionEnd = aSelection.selectionEnd + this.startOffset;
 
       return braille;
     }
   },
 
-  speechHelper: {
-    EARCONS: ['virtual_cursor_move.ogg',
-              'virtual_cursor_key.ogg',
-              'clicked.ogg'],
-
-    earconBuffers: {},
-
-    inited: false,
-
-    webspeechEnabled: false,
-
-    deferredOutputs: [],
-
-    init: function init() {
-      let window = Utils.win;
-      this.webspeechEnabled = !!window.speechSynthesis &&
-        !!window.SpeechSynthesisUtterance;
-
-      let settingsToGet = 2;
-      let settingsCallback = (aName, aSetting) => {
-        if (--settingsToGet > 0) {
-          return;
-        }
-
-        this.inited = true;
-
-        for (let actions of this.deferredOutputs) {
-          this.output(actions);
-        }
-      };
-
-      this._volumeSetting = new SettingCache(
-        'accessibility.screenreader-volume', settingsCallback,
-        { defaultValue: 1, callbackNow: true, callbackOnce: true });
-      this._rateSetting = new SettingCache(
-        'accessibility.screenreader-rate', settingsCallback,
-        { defaultValue: 0, callbackNow: true, callbackOnce: true });
-
-      for (let earcon of this.EARCONS) {
-        let earconName = /(^.*)\..*$/.exec(earcon)[1];
-        this.earconBuffers[earconName] = new WeakMap();
-        this.earconBuffers[earconName].set(
-          window, new window.Audio('chrome://global/content/accessibility/' + earcon));
-      }
-    },
-
-    uninit: function uninit() {
-      if (this.inited) {
-        delete this._volumeSetting;
-        delete this._rateSetting;
-      }
-      this.inited = false;
-    },
-
-    output: function output(aActions) {
-      if (!this.inited) {
-        this.deferredOutputs.push(aActions);
-        return;
-      }
-
-      for (let action of aActions) {
-        let window = Utils.win;
-        Logger.debug('tts.' + action.method, '"' + action.data + '"',
-                     JSON.stringify(action.options));
-
-        if (!action.options.enqueue && this.webspeechEnabled) {
-          window.speechSynthesis.cancel();
-        }
-
-        if (action.method === 'speak' && this.webspeechEnabled) {
-          let utterance = new window.SpeechSynthesisUtterance(action.data);
-          let requestedRate = this._rateSetting.value;
-          utterance.volume = this._volumeSetting.value;
-          utterance.rate = requestedRate >= 0 ?
-            requestedRate + 1 : 1 / (Math.abs(requestedRate) + 1);
-          window.speechSynthesis.speak(utterance);
-        } else if (action.method === 'playEarcon') {
-          let audioBufferWeakMap = this.earconBuffers[action.data];
-          if (audioBufferWeakMap) {
-            let node = audioBufferWeakMap.get(window).cloneNode(false);
-            node.volume = this._volumeSetting.value;
-            node.play();
-          }
-        }
-      }
-    }
-  },
-
   start: function start() {
     Cu.import('resource://gre/modules/Geometry.jsm');
-    this.speechHelper.init();
   },
 
   stop: function stop() {
     if (this.highlightBox) {
       Utils.win.document.documentElement.removeChild(this.highlightBox.get());
       delete this.highlightBox;
     }
-
-    if (this.announceBox) {
-      Utils.win.document.documentElement.removeChild(this.announceBox.get());
-      delete this.announceBox;
-    }
-
-    this.speechHelper.uninit();
   },
 
-  Speech: function Speech(aDetails, aBrowser) {
-    this.speechHelper.output(aDetails.actions);
+  B2G: function B2G(aDetails) {
+    let details = {
+      type: 'accessfu-output',
+      details: JSON.stringify(aDetails)
+    };
+    let window = Utils.win;
+    if (window.shell) {
+      // On B2G device.
+      window.shell.sendChromeEvent(details);
+    } else {
+      // Dispatch custom event to have support for desktop and screen reader
+      // emulator add-on.
+      window.dispatchEvent(new window.CustomEvent(details.type, {
+        bubbles: true,
+        cancelable: true,
+        detail: details
+      }));
+    }
   },
 
-  Visual: function Visual(aDetails, aBrowser) {
-    switch (aDetails.method) {
-      case 'showBounds':
+  Visual: function Visual(aDetail, aBrowser) {
+    switch (aDetail.eventType) {
+      case 'viewport-change':
+      case 'vc-change':
       {
         let highlightBox = null;
         if (!this.highlightBox) {
           // Add highlight box
           highlightBox = Utils.win.document.
             createElementNS('http://www.w3.org/1999/xhtml', 'div');
           Utils.win.document.documentElement.appendChild(highlightBox);
           highlightBox.id = 'virtual-cursor-box';
@@ -638,68 +556,36 @@ var Output = {
           inset.id = 'virtual-cursor-inset';
 
           highlightBox.appendChild(inset);
           this.highlightBox = Cu.getWeakReference(highlightBox);
         } else {
           highlightBox = this.highlightBox.get();
         }
 
-        let padding = aDetails.padding;
-        let r = AccessFu.adjustContentBounds(aDetails.bounds, aBrowser, true);
+        let padding = aDetail.padding;
+        let r = AccessFu.adjustContentBounds(aDetail.bounds, aBrowser, true);
 
         // First hide it to avoid flickering when changing the style.
         highlightBox.style.display = 'none';
         highlightBox.style.top = (r.top - padding) + 'px';
         highlightBox.style.left = (r.left - padding) + 'px';
         highlightBox.style.width = (r.width + padding*2) + 'px';
         highlightBox.style.height = (r.height + padding*2) + 'px';
         highlightBox.style.display = 'block';
 
         break;
       }
-      case 'hideBounds':
+      case 'tabstate-change':
       {
         let highlightBox = this.highlightBox ? this.highlightBox.get() : null;
         if (highlightBox)
           highlightBox.style.display = 'none';
         break;
       }
-      case 'showAnnouncement':
-      {
-        let announceBox = this.announceBox ? this.announceBox.get() : null;
-        if (!announceBox) {
-          announceBox = Utils.win.document.
-            createElementNS('http://www.w3.org/1999/xhtml', 'div');
-          announceBox.id = 'announce-box';
-          Utils.win.document.documentElement.appendChild(announceBox);
-          this.announceBox = Cu.getWeakReference(announceBox);
-        }
-
-        announceBox.innerHTML = '<div>' + aDetails.text + '</div>';
-        announceBox.classList.add('showing');
-
-        if (this._announceHideTimeout)
-          Utils.win.clearTimeout(this._announceHideTimeout);
-
-        if (aDetails.duration > 0)
-          this._announceHideTimeout = Utils.win.setTimeout(
-            function () {
-              announceBox.classList.remove('showing');
-              this._announceHideTimeout = 0;
-            }.bind(this), aDetails.duration);
-        break;
-      }
-      case 'hideAnnouncement':
-      {
-        let announceBox = this.announceBox ? this.announceBox.get() : null;
-        if (announceBox)
-          announceBox.classList.remove('showing');
-        break;
-      }
     }
   },
 
   get androidBridge() {
     delete this.androidBridge;
     if (Utils.MozBuildApp === 'mobile/android') {
       this.androidBridge = Services.androidBridge;
     } else {
@@ -731,22 +617,18 @@ var Output = {
         default:
           androidEvent.brailleOutput = this.brailleState.init(androidEvent.brailleOutput);
           break;
       }
       this.androidBridge.handleGeckoMessage(androidEvent);
     }
   },
 
-  Haptic: function Haptic(aDetails, aBrowser) {
-    Utils.win.navigator.vibrate(aDetails.pattern);
-  },
-
-  Braille: function Braille(aDetails, aBrowser) {
-    Logger.debug('Braille output: ' + aDetails.text);
+  Braille: function Braille(aDetails) {
+    Logger.debug('Braille output: ' + aDetails.output);
   }
 };
 
 var Input = {
   editState: {},
 
   start: function start() {
     // XXX: This is too disruptive on desktop for now.
--- a/accessible/jsat/OutputGenerator.jsm
+++ b/accessible/jsat/OutputGenerator.jsm
@@ -21,40 +21,35 @@ const OUTPUT_DESC_LAST = 1;
 
 Cu.import('resource://gre/modules/XPCOMUtils.jsm');
 XPCOMUtils.defineLazyModuleGetter(this, 'Utils',
   'resource://gre/modules/accessibility/Utils.jsm');
 XPCOMUtils.defineLazyModuleGetter(this, 'PrefCache',
   'resource://gre/modules/accessibility/Utils.jsm');
 XPCOMUtils.defineLazyModuleGetter(this, 'Logger',
   'resource://gre/modules/accessibility/Utils.jsm');
-XPCOMUtils.defineLazyModuleGetter(this, 'PluralForm',
-  'resource://gre/modules/PluralForm.jsm');
 XPCOMUtils.defineLazyModuleGetter(this, 'Roles',
   'resource://gre/modules/accessibility/Constants.jsm');
 XPCOMUtils.defineLazyModuleGetter(this, 'States',
   'resource://gre/modules/accessibility/Constants.jsm');
 
 this.EXPORTED_SYMBOLS = ['UtteranceGenerator', 'BrailleGenerator'];
 
 this.OutputGenerator = {
 
   defaultOutputOrder: OUTPUT_DESC_LAST,
 
   /**
    * Generates output for a PivotContext.
    * @param {PivotContext} aContext object that generates and caches
    *    context information for a given accessible and its relationship with
    *    another accessible.
-   * @return {Object} An object that neccessarily has an output property which
-   *    is an array of strings. Depending on the utterance order,
-   *    the strings describe the context for an accessible object either
+   * @return {Object} An array of speech data. Depending on the utterance order,
+   *    the data describes the context for an accessible object either
    *    starting from the accessible's ancestry or accessible's subtree.
-   *    The object may also have properties specific to the type of output
-   *    generated.
    */
   genForContext: function genForContext(aContext) {
     let output = [];
     let self = this;
     let addOutput = function addOutput(aAccessible) {
       output.push.apply(output, self.genForObject(aAccessible, aContext));
     };
     let ignoreSubtree = function ignoreSubtree(aAccessible) {
@@ -77,34 +72,31 @@ this.OutputGenerator = {
         (node of aContext.subtreeGenerator(true, ignoreSubtree))];
     } else {
       [addOutput(node) for
         (node of aContext.subtreeGenerator(false, ignoreSubtree))];
       addOutput(aContext.accessible);
       contextStart.reverse().forEach(addOutput);
     }
 
-    // Clean up the white space.
-    let trimmed;
-    output = [trimmed for (word of output) if (trimmed = word.trim())];
-    return {output: output};
+    return output;
   },
 
 
   /**
    * Generates output for an object.
    * @param {nsIAccessible} aAccessible accessible object to generate output
    *    for.
    * @param {PivotContext} aContext object that generates and caches
    *    context information for a given accessible and its relationship with
    *    another accessible.
-   * @return {Array} Two string array. The first string describes the object
-   *    and its state. The second string is the object's name. Whether the
-   *    object's description or it's role is included is determined by
-   *    {@link roleRuleMap}.
+   * @return {Array} A 2 element array of speech data. The first element
+   *    describes the object and its state. The second element is the object's
+   *    name. Whether the object's description or it's role is included is
+   *    determined by {@link roleRuleMap}.
    */
   genForObject: function genForObject(aAccessible, aContext) {
     let roleString = Utils.AccRetrieval.getStringRole(aAccessible.role);
     let func = this.objectOutputFunctions[
       OutputGenerator._getOutputName(roleString)] ||
       this.objectOutputFunctions.defaultFunc;
 
     let flags = this.roleRuleMap[roleString] || 0;
@@ -117,25 +109,24 @@ this.OutputGenerator = {
   },
 
   /**
    * Generates output for an action performed.
    * @param {nsIAccessible} aAccessible accessible object that the action was
    *    invoked in.
    * @param {string} aActionName the name of the action, one of the keys in
    *    {@link gActionMap}.
-   * @return {Array} A one string array with the action.
+   * @return {Array} A one element array with action data.
    */
   genForAction: function genForAction(aObject, aActionName) {},
 
   /**
-   * Generates output for an announcement. Basically attempts to localize
-   * the announcement string.
+   * Generates output for an announcement.
    * @param {string} aAnnouncement unlocalized announcement.
-   * @return {Array} A one string array with the announcement.
+   * @return {Array} An announcement speech data to be localized.
    */
   genForAnnouncement: function genForAnnouncement(aAnnouncement) {},
 
   /**
    * Generates output for a tab state change.
    * @param {nsIAccessible} aAccessible accessible object of the tab's attached
    *    document.
    * @param {string} aTabState the tab state name, see
@@ -148,16 +139,22 @@ this.OutputGenerator = {
    * Generates output for announcing entering and leaving editing mode.
    * @param {aIsEditing} boolean true if we are in editing mode
    * @return {Array} The mode utterance
    */
   genForEditingMode: function genForEditingMode(aIsEditing) {},
 
   _getContextStart: function getContextStart(aContext) {},
 
+  /**
+   * Adds an accessible name and description to the output if available.
+   * @param {Array} aOutput Output array.
+   * @param {nsIAccessible} aAccessible current accessible object.
+   * @param {Number} aFlags output flags.
+   */
   _addName: function _addName(aOutput, aAccessible, aFlags) {
     let name;
     if ((Utils.getAttributes(aAccessible)['explicit-name'] === 'true' &&
          !(aFlags & IGNORE_EXPLICIT_NAME)) || (aFlags & INCLUDE_NAME)) {
       name = aAccessible.name;
     }
 
     let description = aAccessible.description;
@@ -168,88 +165,72 @@ this.OutputGenerator = {
       if (tmpName && (description !== tmpName)) {
         name = name || '';
         name = this.outputOrder === OUTPUT_DESC_FIRST ?
           description + ' - ' + name :
           name + ' - ' + description;
       }
     }
 
-    if (name) {
-      aOutput[this.outputOrder === OUTPUT_DESC_FIRST ?
-        'push' : 'unshift'](name);
+    if (!name || !name.trim()) {
+      return;
     }
+    aOutput[this.outputOrder === OUTPUT_DESC_FIRST ? 'push' : 'unshift'](name);
   },
 
   /**
    * Adds a landmark role to the output if available.
    * @param {Array} aOutput Output array.
    * @param {nsIAccessible} aAccessible current accessible object.
    */
   _addLandmark: function _addLandmark(aOutput, aAccessible) {
     let landmarkName = Utils.getLandmarkName(aAccessible);
     if (!landmarkName) {
       return;
     }
-
-    let landmark = Utils.stringBundle.GetStringFromName(landmarkName);
-    if (!landmark) {
-      return;
-    }
-
-    aOutput[this.outputOrder === OUTPUT_DESC_FIRST ? 'unshift' : 'push'](
-      landmark);
+    aOutput[this.outputOrder === OUTPUT_DESC_FIRST ? 'unshift' : 'push']({
+      string: landmarkName
+    });
   },
 
   /**
    * Adds an entry type attribute to the description if available.
-   * @param {Array} aDesc Description array.
+   * @param {Array} aOutput Output array.
    * @param {nsIAccessible} aAccessible current accessible object.
    * @param {String} aRoleStr aAccessible's role string.
    */
-  _addType: function _addType(aDesc, aAccessible, aRoleStr) {
+  _addType: function _addType(aOutput, aAccessible, aRoleStr) {
     if (aRoleStr !== 'entry') {
       return;
     }
 
     let typeName = Utils.getAttributes(aAccessible)['text-input-type'];
     // Ignore the the input type="text" case.
     if (!typeName || typeName === 'text') {
       return;
     }
-    typeName = 'textInputType_' + typeName;
-    try {
-      aDesc.push(Utils.stringBundle.GetStringFromName(typeName));
-    } catch (x) {
-      Logger.warning('Failed to get a string from a bundle for', typeName);
-    }
+    aOutput.push({string: 'textInputType_' + typeName});
   },
 
+  _addState: function _addState(aOutput, aState) {},
+
+  _addRole: function _addRole(aOutput, aRoleStr) {},
+
   get outputOrder() {
     if (!this._utteranceOrder) {
       this._utteranceOrder = new PrefCache('accessibility.accessfu.utterance');
     }
     return typeof this._utteranceOrder.value === 'number' ?
       this._utteranceOrder.value : this.defaultOutputOrder;
   },
 
   _getOutputName: function _getOutputName(aName) {
     return aName.replace(' ', '');
   },
 
-  _getLocalizedRole: function _getLocalizedRole(aRoleStr) {},
-
-  _getLocalizedState: function _getLocalizedState(aState) {},
-
-  _getPluralFormString: function _getPluralFormString(aString, aCount) {
-    let str = Utils.stringBundle.GetStringFromName(this._getOutputName(aString));
-    str = PluralForm.get(aCount, str);
-    return str.replace('#1', aCount);
-  },
-
   roleRuleMap: {
     'menubar': INCLUDE_DESC,
     'scrollbar': INCLUDE_DESC,
     'grip': INCLUDE_DESC,
     'alert': INCLUDE_DESC | INCLUDE_NAME,
     'menupopup': INCLUDE_DESC,
     'menuitem': INCLUDE_DESC | NAME_FROM_SUBTREE_RULE,
     'tooltip': INCLUDE_DESC | NAME_FROM_SUBTREE_RULE,
@@ -321,31 +302,24 @@ this.OutputGenerator = {
     'chrome window': IGNORE_EXPLICIT_NAME,
     'app root': IGNORE_EXPLICIT_NAME },
 
   objectOutputFunctions: {
     _generateBaseOutput: function _generateBaseOutput(aAccessible, aRoleStr, aState, aFlags) {
       let output = [];
 
       if (aFlags & INCLUDE_DESC) {
-        let desc = this._getLocalizedState(aState);
-        let roleStr = this._getLocalizedRole(aRoleStr);
-        if (roleStr) {
-          this._addType(desc, aAccessible, aRoleStr);
-          desc.push(roleStr);
-        }
-        output.push(desc.join(' '));
+        this._addState(output, aState);
+        this._addType(output, aAccessible, aRoleStr);
+        this._addRole(output, aRoleStr);
       }
 
-      if (aFlags & INCLUDE_VALUE) {
-        let value = aAccessible.value;
-        if (value) {
-          output[this.outputOrder === OUTPUT_DESC_FIRST ?
-                 'push' : 'unshift'](value);
-        }
+      if (aFlags & INCLUDE_VALUE && aAccessible.value.trim()) {
+        output[this.outputOrder === OUTPUT_DESC_FIRST ? 'push' : 'unshift'](
+          aAccessible.value);
       }
 
       this._addName(output, aAccessible, aFlags);
       this._addLandmark(output, aAccessible);
 
       return output;
     },
 
@@ -362,26 +336,26 @@ this.OutputGenerator = {
 
     entry: function entry(aAccessible, aRoleStr, aState, aFlags) {
       let rolestr = aState.contains(States.MULTI_LINE) ? 'textarea' : 'entry';
       return this.objectOutputFunctions.defaultFunc.apply(
         this, [aAccessible, rolestr, aState, aFlags]);
     },
 
     pagetab: function pagetab(aAccessible, aRoleStr, aState, aFlags) {
-      let localizedRole = this._getLocalizedRole(aRoleStr);
       let itemno = {};
       let itemof = {};
       aAccessible.groupPosition({}, itemof, itemno);
       let output = [];
-      let desc = this._getLocalizedState(aState);
-      desc.push(
-        Utils.stringBundle.formatStringFromName(
-          'objItemOf', [localizedRole, itemno.value, itemof.value], 3));
-      output.push(desc.join(' '));
+      this._addState(output, aState);
+      this._addRole(output, aRoleStr);
+      output.push({
+        string: 'objItemOfN',
+        args: [itemno.value, itemof.value]
+      });
 
       this._addName(output, aAccessible, aFlags);
       this._addLandmark(output, aAccessible);
 
       return output;
     },
 
     table: function table(aAccessible, aRoleStr, aState, aFlags) {
@@ -393,34 +367,35 @@ this.OutputGenerator = {
         Logger.logException(x);
         return output;
       } finally {
         // Check if it's a layout table, and bail out if true.
         // We don't want to speak any table information for layout tables.
         if (table.isProbablyForLayout()) {
           return output;
         }
-        let tableColumnInfo = this._getPluralFormString('tableColumnInfo',
-          table.columnCount);
-        let tableRowInfo = this._getPluralFormString('tableRowInfo',
-          table.rowCount);
-        output.push(Utils.stringBundle.formatStringFromName(
-          this._getOutputName('tableInfo'), [this._getLocalizedRole(aRoleStr),
-            tableColumnInfo, tableRowInfo], 3));
+        this._addRole(output, aRoleStr);
+        output.push.call(output, {
+          string: this._getOutputName('tblColumnInfo'),
+          count: table.columnCount
+        }, {
+          string: this._getOutputName('tblRowInfo'),
+          count: table.rowCount
+        });
         this._addName(output, aAccessible, aFlags);
         this._addLandmark(output, aAccessible);
         return output;
       }
     }
   }
 };
 
 /**
  * Generates speech utterances from objects, actions and state changes.
- * An utterance is an array of strings.
+ * An utterance is an array of speech data.
  *
  * It should not be assumed that flattening an utterance array would create a
  * gramatically correct sentence. For example, {@link genForObject} might
  * return: ['graphic', 'Welcome to my home page'].
  * Each string element in an utterance should be gramatically correct in itself.
  * Another example from {@link genForObject}: ['list item 2 of 5', 'Alabama'].
  *
  * An utterance is ordered from the least to the most important. Speaking the
@@ -446,89 +421,86 @@ this.UtteranceGenerator = {
     collapse: 'collapseAction',
     expand: 'expandAction',
     activate: 'activateAction',
     cycle: 'cycleAction'
   },
 
   //TODO: May become more verbose in the future.
   genForAction: function genForAction(aObject, aActionName) {
-    return [Utils.stringBundle.GetStringFromName(this.gActionMap[aActionName])];
+    return [{string: this.gActionMap[aActionName]}];
   },
 
   genForLiveRegion: function genForLiveRegion(aContext, aIsHide, aModifiedText) {
     let utterance = [];
     if (aIsHide) {
-      utterance.push(Utils.stringBundle.GetStringFromName('hidden'));
+      utterance.push({string: 'hidden'});
     }
-    return utterance.concat(
-      aModifiedText || this.genForContext(aContext).output);
+    return utterance.concat(aModifiedText || this.genForContext(aContext));
   },
 
   genForAnnouncement: function genForAnnouncement(aAnnouncement) {
-    try {
-      return [Utils.stringBundle.GetStringFromName(aAnnouncement)];
-    } catch (x) {
-      return [aAnnouncement];
-    }
+    return [{
+      string: aAnnouncement
+    }];
   },
 
   genForTabStateChange: function genForTabStateChange(aObject, aTabState) {
     switch (aTabState) {
       case 'newtab':
-        return [Utils.stringBundle.GetStringFromName('tabNew')];
+        return [{string: 'tabNew'}];
       case 'loading':
-        return [Utils.stringBundle.GetStringFromName('tabLoading')];
+        return [{string: 'tabLoading'}];
       case 'loaded':
-        return [aObject.name || '',
-                Utils.stringBundle.GetStringFromName('tabLoaded')];
+        return [aObject.name, {string: 'tabLoaded'}];
       case 'loadstopped':
-        return [Utils.stringBundle.GetStringFromName('tabLoadStopped')];
+        return [{string: 'tabLoadStopped'}];
       case 'reload':
-        return [Utils.stringBundle.GetStringFromName('tabReload')];
+        return [{string: 'tabReload'}];
       default:
         return [];
     }
   },
 
   genForEditingMode: function genForEditingMode(aIsEditing) {
-    return [Utils.stringBundle.GetStringFromName(
-      aIsEditing ? 'editingMode' : 'navigationMode')];
+    return [{string: aIsEditing ? 'editingMode' : 'navigationMode'}];
   },
 
   objectOutputFunctions: {
 
     __proto__: OutputGenerator.objectOutputFunctions,
 
     defaultFunc: function defaultFunc(aAccessible, aRoleStr, aState, aFlags) {
       return this.objectOutputFunctions._generateBaseOutput.apply(this, arguments);
     },
 
     heading: function heading(aAccessible, aRoleStr, aState, aFlags) {
       let level = {};
       aAccessible.groupPosition(level, {}, {});
-      let utterance =
-        [Utils.stringBundle.formatStringFromName(
-          'headingLevel', [level.value], 1)];
+      let utterance = [{string: 'headingLevel', args: [level.value]}];
 
       this._addName(utterance, aAccessible, aFlags);
       this._addLandmark(utterance, aAccessible);
 
       return utterance;
     },
 
     listitem: function listitem(aAccessible, aRoleStr, aState, aFlags) {
       let itemno = {};
       let itemof = {};
       aAccessible.groupPosition({}, itemof, itemno);
       let utterance = [];
-      if (itemno.value == 1) // Start of list
-        utterance.push(Utils.stringBundle.GetStringFromName('listStart'));
-      else if (itemno.value == itemof.value) // last item
-        utterance.push(Utils.stringBundle.GetStringFromName('listEnd'));
+      if (itemno.value == 1) {
+        // Start of list
+        utterance.push({string: 'listStart'});
+      }
+      else if (itemno.value == itemof.value) {
+        // last item
+        utterance.push({string: 'listEnd'});
+      }
 
       this._addName(utterance, aAccessible, aFlags);
       this._addLandmark(utterance, aAccessible);
 
       return utterance;
     },
 
     list: function list(aAccessible, aRoleStr, aState, aFlags) {
@@ -549,45 +521,42 @@ this.UtteranceGenerator = {
 
       return [];
     },
 
     cell: function cell(aAccessible, aRoleStr, aState, aFlags, aContext) {
       let utterance = [];
       let cell = aContext.getCellInfo(aAccessible);
       if (cell) {
-        let desc = [];
-        let addCellChanged = function addCellChanged(aDesc, aChanged, aString, aIndex) {
-          if (aChanged) {
-            aDesc.push(Utils.stringBundle.formatStringFromName(aString,
-              [aIndex + 1], 1));
+        let addCellChanged =
+          function addCellChanged(aUtterance, aChanged, aString, aIndex) {
+            if (aChanged) {
+              aUtterance.push({string: aString, args: [aIndex + 1]});
+            }
+          };
+        let addExtent = function addExtent(aUtterance, aExtent, aString) {
+          if (aExtent > 1) {
+            aUtterance.push({string: aString, args: [aExtent]});
           }
         };
-        let addExtent = function addExtent(aDesc, aExtent, aString) {
-          if (aExtent > 1) {
-            aDesc.push(Utils.stringBundle.formatStringFromName(aString,
-              [aExtent], 1));
-          }
-        };
-        let addHeaders = function addHeaders(aDesc, aHeaders) {
+        let addHeaders = function addHeaders(aUtterance, aHeaders) {
           if (aHeaders.length > 0) {
-            aDesc.push.apply(aDesc, aHeaders);
+            aUtterance.push.apply(aUtterance, aHeaders);
           }
         };
 
-        addCellChanged(desc, cell.columnChanged, 'columnInfo', cell.columnIndex);
-        addCellChanged(desc, cell.rowChanged, 'rowInfo', cell.rowIndex);
+        addCellChanged(utterance, cell.columnChanged, 'columnInfo',
+          cell.columnIndex);
+        addCellChanged(utterance, cell.rowChanged, 'rowInfo', cell.rowIndex);
 
-        addExtent(desc, cell.columnExtent, 'spansColumns');
-        addExtent(desc, cell.rowExtent, 'spansRows');
+        addExtent(utterance, cell.columnExtent, 'spansColumns');
+        addExtent(utterance, cell.rowExtent, 'spansRows');
 
-        addHeaders(desc, cell.columnHeaders);
-        addHeaders(desc, cell.rowHeaders);
-
-        utterance.push(desc.join(' '));
+        addHeaders(utterance, cell.columnHeaders);
+        addHeaders(utterance, cell.rowHeaders);
       }
 
       this._addName(utterance, aAccessible, aFlags);
       this._addLandmark(utterance, aAccessible);
 
       return utterance;
     },
 
@@ -607,108 +576,92 @@ this.UtteranceGenerator = {
       return this.objectOutputFunctions.defaultFunc.apply(this, arguments);
     }
   },
 
   _getContextStart: function _getContextStart(aContext) {
     return aContext.newAncestry;
   },
 
-  _getLocalizedRole: function _getLocalizedRole(aRoleStr) {
-    try {
-      return Utils.stringBundle.GetStringFromName(
-        this._getOutputName(aRoleStr));
-    } catch (x) {
-      return '';
-    }
+  _addRole: function _addRole(aOutput, aRoleStr) {
+    aOutput.push({string: this._getOutputName(aRoleStr)});
   },
 
-  _getLocalizedState: function _getLocalizedState(aState) {
-    let stateUtterances = [];
+  _addState: function _addState(aOutput, aState) {
 
     if (aState.contains(States.UNAVAILABLE)) {
-      stateUtterances.push(
-        Utils.stringBundle.GetStringFromName('stateUnavailable'));
+      aOutput.push({string: 'stateUnavailable'});
     }
 
     // Don't utter this in Jelly Bean, we let TalkBack do it for us there.
     // This is because we expose the checked information on the node itself.
-    // XXX: this means the checked state is always appended to the end, regardless
-    // of the utterance ordering preference.
+    // XXX: this means the checked state is always appended to the end,
+    // regardless of the utterance ordering preference.
     if ((Utils.AndroidSdkVersion < 16 || Utils.MozBuildApp === 'browser') &&
       aState.contains(States.CHECKABLE)) {
       let statetr = aState.contains(States.CHECKED) ?
         'stateChecked' : 'stateNotChecked';
-      stateUtterances.push(Utils.stringBundle.GetStringFromName(statetr));
+      aOutput.push({string: statetr});
     }
 
     if (aState.contains(States.PRESSED)) {
-      stateUtterances.push(
-        Utils.stringBundle.GetStringFromName('statePressed'));
+      aOutput.push({string: 'statePressed'});
     }
 
     if (aState.contains(States.EXPANDABLE)) {
       let statetr = aState.contains(States.EXPANDED) ?
         'stateExpanded' : 'stateCollapsed';
-      stateUtterances.push(Utils.stringBundle.GetStringFromName(statetr));
+      aOutput.push({string: statetr});
     }
 
     if (aState.contains(States.REQUIRED)) {
-      stateUtterances.push(
-        Utils.stringBundle.GetStringFromName('stateRequired'));
+      aOutput.push({string: 'stateRequired'});
     }
 
     if (aState.contains(States.TRAVERSED)) {
-      stateUtterances.push(
-        Utils.stringBundle.GetStringFromName('stateTraversed'));
+      aOutput.push({string: 'stateTraversed'});
     }
 
     if (aState.contains(States.HASPOPUP)) {
-      stateUtterances.push(
-        Utils.stringBundle.GetStringFromName('stateHasPopup'));
+      aOutput.push({string: 'stateHasPopup'});
     }
 
     if (aState.contains(States.SELECTED)) {
-      stateUtterances.push(
-        Utils.stringBundle.GetStringFromName('stateSelected'));
+      aOutput.push({string: 'stateSelected'});
     }
-
-    return stateUtterances;
   },
 
   _getListUtterance: function _getListUtterance(aAccessible, aRoleStr, aFlags, aItemCount) {
-    let desc = [];
-    let roleStr = this._getLocalizedRole(aRoleStr);
-    if (roleStr) {
-      desc.push(roleStr);
-    }
-    desc.push(this._getPluralFormString('listItemsCount', aItemCount));
-    let utterance = [desc.join(' ')];
+    let utterance = [];
+    this._addRole(utterance, aRoleStr);
+    utterance.push({
+      string: this._getOutputName('listItemsCount'),
+      count: aItemCount
+    });
 
     this._addName(utterance, aAccessible, aFlags);
     this._addLandmark(utterance, aAccessible);
 
     return utterance;
   }
 };
 
-
 this.BrailleGenerator = {
   __proto__: OutputGenerator,
 
   genForContext: function genForContext(aContext) {
     let output = OutputGenerator.genForContext.apply(this, arguments);
 
     let acc = aContext.accessible;
 
     // add the static text indicating a list item; do this for both listitems or
     // direct first children of listitems, because these are both common browsing
     // scenarios
     let addListitemIndicator = function addListitemIndicator(indicator = '*') {
-      output.output.unshift(indicator);
+      output.unshift(indicator);
     };
 
     if (acc.indexInParent === 1 &&
         acc.parent.role == Roles.LISTITEM &&
         acc.previousSibling.role == Roles.STATICTEXT) {
       if (acc.parent.parent && acc.parent.parent.DOMNode &&
           acc.parent.parent.DOMNode.nodeName == 'UL') {
         addListitemIndicator();
@@ -719,22 +672,16 @@ this.BrailleGenerator = {
                acc.firstChild.role == Roles.STATICTEXT) {
       if (acc.parent.DOMNode.nodeName == 'UL') {
         addListitemIndicator();
       } else {
         addListitemIndicator(acc.firstChild.name.trim());
       }
     }
 
-    if (acc instanceof Ci.nsIAccessibleText) {
-      output.endOffset = this.outputOrder === OUTPUT_DESC_FIRST ?
-                         output.output.join(' ').length : acc.characterCount;
-      output.startOffset = output.endOffset - acc.characterCount;
-    }
-
     return output;
   },
 
   objectOutputFunctions: {
 
     __proto__: OutputGenerator.objectOutputFunctions,
 
     defaultFunc: function defaultFunc(aAccessible, aRoleStr, aState, aFlags) {
@@ -749,30 +696,29 @@ this.BrailleGenerator = {
 
       return braille;
     },
 
     cell: function cell(aAccessible, aRoleStr, aState, aFlags, aContext) {
       let braille = [];
       let cell = aContext.getCellInfo(aAccessible);
       if (cell) {
-        let desc = [];
-        let addHeaders = function addHeaders(aDesc, aHeaders) {
+        let addHeaders = function addHeaders(aBraille, aHeaders) {
           if (aHeaders.length > 0) {
-            aDesc.push.apply(aDesc, aHeaders);
+            aBraille.push.apply(aBraille, aHeaders);
           }
         };
 
-        desc.push(Utils.stringBundle.formatStringFromName(
-          this._getOutputName('cellInfo'), [cell.columnIndex + 1,
-            cell.rowIndex + 1], 2));
+        braille.push({
+          string: this._getOutputName('cellInfo'),
+          args: [cell.columnIndex + 1, cell.rowIndex + 1]
+        });
 
-        addHeaders(desc, cell.columnHeaders);
-        addHeaders(desc, cell.rowHeaders);
-        braille.push(desc.join(' '));
+        addHeaders(braille, cell.columnHeaders);
+        addHeaders(braille, cell.rowHeaders);
       }
 
       this._addName(braille, aAccessible, aFlags);
       this._addLandmark(braille, aAccessible);
       return braille;
     },
 
     columnheader: function columnheader() {
@@ -790,82 +736,61 @@ this.BrailleGenerator = {
         return [];
       }
 
       return this.objectOutputFunctions._useStateNotRole.apply(this, arguments);
     },
 
     _useStateNotRole: function _useStateNotRole(aAccessible, aRoleStr, aState, aFlags) {
       let braille = [];
-
-      let desc = this._getLocalizedState(aState, aAccessible.role);
-      braille.push(desc.join(' '));
-
+      this._addState(braille, aState, aAccessible.role);
       this._addName(braille, aAccessible, aFlags);
       this._addLandmark(braille, aAccessible);
 
       return braille;
     },
 
     checkbutton: function checkbutton(aAccessible, aRoleStr, aState, aFlags) {
       return this.objectOutputFunctions._useStateNotRole.apply(this, arguments);
     },
 
     radiobutton: function radiobutton(aAccessible, aRoleStr, aState, aFlags) {
       return this.objectOutputFunctions._useStateNotRole.apply(this, arguments);
     },
 
-    togglebutton: function radiobutton(aAccessible, aRoleStr, aState, aFlags) {
+    togglebutton: function togglebutton(aAccessible, aRoleStr, aState, aFlags) {
       return this.objectOutputFunctions._useStateNotRole.apply(this, arguments);
     }
   },
 
   _getContextStart: function _getContextStart(aContext) {
     if (aContext.accessible.parent.role == Roles.LINK) {
       return [aContext.accessible.parent];
     }
 
     return [];
   },
 
   _getOutputName: function _getOutputName(aName) {
     return OutputGenerator._getOutputName(aName) + 'Abbr';
   },
 
-  _getLocalizedRole: function _getLocalizedRole(aRoleStr) {
-    try {
-      return Utils.stringBundle.GetStringFromName(
-        this._getOutputName(aRoleStr));
-    } catch (x) {
-      try {
-        return Utils.stringBundle.GetStringFromName(
-          OutputGenerator._getOutputName(aRoleStr));
-      } catch (y) {
-        return '';
-      }
-    }
+  _addRole: function _addRole(aBraille, aRoleStr) {
+    aBraille.push({string: this._getOutputName(aRoleStr)});
   },
 
-  _getLocalizedState: function _getLocalizedState(aState, aRole) {
-    let stateBraille = [];
-
-    let getResultMarker = function getResultMarker(aMarker) {
-      // aMarker is a simple boolean.
-      let resultMarker = [];
-      resultMarker.push('(');
-      resultMarker.push(aMarker ? 'x' : ' ');
-      resultMarker.push(')');
-
-      return resultMarker.join('');
-    };
-
+  _addState: function _addState(aBraille, aState, aRole) {
     if (aState.contains(States.CHECKABLE)) {
-      stateBraille.push(getResultMarker(aState.contains(States.CHECKED)));
-    }
-
-    if (aRole === Roles.TOGGLE_BUTTON) {
-      stateBraille.push(getResultMarker(aState.contains(States.PRESSED)));
+      aBraille.push({
+        string: aState.contains(States.CHECKED) ?
+          this._getOutputName('stateChecked') :
+          this._getOutputName('stateUnchecked')
+      });
     }
-
-    return stateBraille;
+    if (aRole === Roles.TOGGLE_BUTTON) {
+      aBraille.push({
+        string: aState.contains(States.PRESSED) ?
+          this._getOutputName('statePressed') :
+          this._getOutputName('stateUnpressed')
+      });
+    }
   }
-
 };
--- a/accessible/jsat/Presentation.jsm
+++ b/accessible/jsat/Presentation.jsm
@@ -125,17 +125,16 @@ Presenter.prototype = {
    */
   liveRegion: function liveRegionShown(aContext, aIsPolite, aIsHide,
     aModifiedText) {}
 };
 
 /**
  * Visual presenter. Draws a box around the virtual cursor's position.
  */
-
 this.VisualPresenter = function VisualPresenter() {
   this._displayedAccessibles = new WeakMap();
 };
 
 VisualPresenter.prototype = {
   __proto__: Presenter.prototype,
 
   type: 'Visual',
@@ -156,17 +155,17 @@ VisualPresenter.prototype = {
     let end = currentDisplay.endOffset;
     if (Utils.isAliveAndVisible(currentAcc)) {
       let bounds = (start === -1 && end === -1) ? Utils.getBounds(currentAcc) :
                    Utils.getTextBounds(currentAcc, start, end);
 
       return {
         type: this.type,
         details: {
-          method: 'showBounds',
+          eventType: 'viewport-change',
           bounds: bounds,
           padding: this.BORDER_PADDING
         }
       };
     }
 
     return null;
   },
@@ -189,17 +188,17 @@ VisualPresenter.prototype = {
       let bounds = (aContext.startOffset === -1 && aContext.endOffset === -1) ?
             aContext.bounds : Utils.getTextBounds(aContext.accessibleForBounds,
                                                   aContext.startOffset,
                                                   aContext.endOffset);
 
       return {
         type: this.type,
         details: {
-          method: 'showBounds',
+          eventType: 'vc-change',
           bounds: bounds,
           padding: this.BORDER_PADDING
         }
       };
     } catch (e) {
       Logger.logException(e, 'Failed to get bounds');
       return null;
     }
@@ -207,30 +206,19 @@ VisualPresenter.prototype = {
 
   tabSelected: function VisualPresenter_tabSelected(aDocContext, aVCContext) {
     return this.pivotChanged(aVCContext, Ci.nsIAccessiblePivot.REASON_NONE);
   },
 
   tabStateChanged: function VisualPresenter_tabStateChanged(aDocObj,
                                                             aPageState) {
     if (aPageState == 'newdoc')
-      return {type: this.type, details: {method: 'hideBounds'}};
+      return {type: this.type, details: {eventType: 'tabstate-change'}};
 
     return null;
-  },
-
-  announce: function VisualPresenter_announce(aAnnouncement) {
-    return {
-      type: this.type,
-      details: {
-        method: 'showAnnouncement',
-        text: aAnnouncement,
-        duration: 1000
-      }
-    };
   }
 };
 
 /**
  * Android presenter. Fires Android a11y events.
  */
 
 this.AndroidPresenter = function AndroidPresenter() {};
@@ -292,17 +280,18 @@ AndroidPresenter.prototype = {
           fromIndex: adjustedText.startOffset,
           toIndex: adjustedText.endOffset
         });
       }
     } else {
       let state = Utils.getState(aContext.accessible);
       androidEvents.push({eventType: (isExploreByTouch) ?
                            this.ANDROID_VIEW_HOVER_ENTER : focusEventType,
-                         text: UtteranceGenerator.genForContext(aContext).output,
+                         text: Utils.localize(UtteranceGenerator.genForContext(
+                           aContext)),
                          bounds: aContext.bounds,
                          clickable: aContext.accessible.actionCount > 0,
                          checkable: state.contains(States.CHECKABLE),
                          checked: state.contains(States.CHECKED),
                          brailleOutput: brailleOutput});
     }
 
 
@@ -318,31 +307,32 @@ AndroidPresenter.prototype = {
     // Checkable objects will have a state changed event we will use instead.
     if (state.contains(States.CHECKABLE))
       return null;
 
     return {
       type: this.type,
       details: [{
         eventType: this.ANDROID_VIEW_CLICKED,
-        text: UtteranceGenerator.genForAction(aObject, aActionName),
+        text: Utils.localize(UtteranceGenerator.genForAction(aObject,
+          aActionName)),
         checked: state.contains(States.CHECKED)
       }]
     };
   },
 
   tabSelected: function AndroidPresenter_tabSelected(aDocContext, aVCContext) {
     // Send a pivot change message with the full context utterance for this doc.
     return this.pivotChanged(aVCContext, Ci.nsIAccessiblePivot.REASON_NONE);
   },
 
   tabStateChanged: function AndroidPresenter_tabStateChanged(aDocObj,
                                                              aPageState) {
     return this.announce(
-      UtteranceGenerator.genForTabStateChange(aDocObj, aPageState).join(' '));
+      UtteranceGenerator.genForTabStateChange(aDocObj, aPageState));
   },
 
   textChanged: function AndroidPresenter_textChanged(aIsInserted, aStart,
                                                      aLength, aText,
                                                      aModifiedText) {
     let eventDetails = {
       eventType: this.ANDROID_VIEW_TEXT_CHANGED,
       text: [aText],
@@ -416,188 +406,173 @@ AndroidPresenter.prototype = {
         scrollY: aWindow.scrollY,
         maxScrollX: aWindow.scrollMaxX,
         maxScrollY: aWindow.scrollMaxY
       }]
     };
   },
 
   editingModeChanged: function AndroidPresenter_editingModeChanged(aIsEditing) {
-    return this.announce(
-      UtteranceGenerator.genForEditingMode(aIsEditing).join(' '));
+    return this.announce(UtteranceGenerator.genForEditingMode(aIsEditing));
   },
 
   announce: function AndroidPresenter_announce(aAnnouncement) {
+    let localizedAnnouncement = Utils.localize(aAnnouncement).join(' ');
     return {
       type: this.type,
       details: [{
         eventType: (Utils.AndroidSdkVersion >= 16) ?
           this.ANDROID_ANNOUNCEMENT : this.ANDROID_VIEW_TEXT_CHANGED,
-        text: [aAnnouncement],
-        addedCount: aAnnouncement.length,
+        text: [localizedAnnouncement],
+        addedCount: localizedAnnouncement.length,
         removedCount: 0,
         fromIndex: 0
       }]
     };
   },
 
   liveRegion: function AndroidPresenter_liveRegion(aContext, aIsPolite,
     aIsHide, aModifiedText) {
     return this.announce(
-      UtteranceGenerator.genForLiveRegion(aContext, aIsHide,
-        aModifiedText).join(' '));
+      UtteranceGenerator.genForLiveRegion(aContext, aIsHide, aModifiedText));
   }
 };
 
 /**
- * A speech presenter for direct TTS output
+ * A B2G presenter for Gaia.
  */
+this.B2GPresenter = function B2GPresenter() {};
 
-this.SpeechPresenter = function SpeechPresenter() {};
-
-SpeechPresenter.prototype = {
+B2GPresenter.prototype = {
   __proto__: Presenter.prototype,
 
-  type: 'Speech',
+  type: 'B2G',
+
+  /**
+   * A pattern used for haptic feedback.
+   * @type {Array}
+   */
+  PIVOT_CHANGE_HAPTIC_PATTERN: [40],
 
-  pivotChanged: function SpeechPresenter_pivotChanged(aContext, aReason) {
-    if (!aContext.accessible)
+  /**
+   * Pivot move reasons.
+   * @type {Array}
+   */
+  pivotChangedReasons: ['none', 'next', 'prev', 'first', 'last', 'text',
+    'point'],
+
+  pivotChanged: function B2GPresenter_pivotChanged(aContext, aReason) {
+    if (!aContext.accessible) {
       return null;
+    }
 
     return {
       type: this.type,
       details: {
-        actions: [
-          {method: 'playEarcon',
-           data: aContext.accessible.role === Roles.KEY ?
-             'virtual_cursor_key' : 'virtual_cursor_move',
-           options: {}},
-          {method: 'speak',
-            data: UtteranceGenerator.genForContext(aContext).output.join(' '),
-            options: {enqueue: true}}
-        ]
+        eventType: 'vc-change',
+        data: UtteranceGenerator.genForContext(aContext),
+        options: {
+          pattern: this.PIVOT_CHANGE_HAPTIC_PATTERN,
+          isKey: aContext.accessible.role === Roles.KEY,
+          reason: this.pivotChangedReasons[aReason]
+        }
       }
     };
   },
 
-  valueChanged: function SpeechPresenter_valueChanged(aAccessible) {
+  valueChanged: function B2GPresenter_valueChanged(aAccessible) {
     return {
       type: this.type,
       details: {
-        actions: [
-          { method: 'speak',
-            data: aAccessible.value,
-            options: { enqueue: false } }
-        ]
+        eventType: 'value-change',
+        data: aAccessible.value
       }
-    }
+    };
   },
 
-  actionInvoked: function SpeechPresenter_actionInvoked(aObject, aActionName) {
-    let actions = [];
-    if (aActionName === 'click') {
-      actions.push({method: 'playEarcon',
-                    data: 'clicked',
-                    options: {}});
-    } else {
-      actions.push({method: 'speak',
-                    data: UtteranceGenerator.genForAction(aObject, aActionName).join(' '),
-                    options: {enqueue: false}});
-    }
-    return { type: this.type, details: { actions: actions } };
-  },
-
-  liveRegion: function SpeechPresenter_liveRegion(aContext, aIsPolite, aIsHide,
-    aModifiedText) {
+  actionInvoked: function B2GPresenter_actionInvoked(aObject, aActionName) {
     return {
       type: this.type,
       details: {
-        actions: [{
-          method: 'speak',
-          data: UtteranceGenerator.genForLiveRegion(aContext, aIsHide,
-            aModifiedText).join(' '),
-          options: {enqueue: aIsPolite}
-        }]
+        eventType: 'action',
+        data: UtteranceGenerator.genForAction(aObject, aActionName)
       }
     };
   },
 
-  announce: function SpeechPresenter_announce(aAnnouncement) {
+  liveRegion: function
+    B2GPresenter_liveRegion(aContext, aIsPolite, aIsHide, aModifiedText) {
+      return {
+        type: this.type,
+        details: {
+          eventType: 'liveregion-change',
+          data: UtteranceGenerator.genForLiveRegion(aContext, aIsHide,
+            aModifiedText),
+          options: {enqueue: aIsPolite}
+        }
+      };
+    },
+
+  announce: function B2GPresenter_announce(aAnnouncement) {
     return {
       type: this.type,
       details: {
-        actions: [{
-          method: 'speak', data: aAnnouncement, options: { enqueue: false }
-        }]
+        eventType: 'announcement',
+        data: aAnnouncement
       }
     };
   }
 };
 
 /**
- * A haptic presenter
- */
-
-this.HapticPresenter = function HapticPresenter() {};
-
-HapticPresenter.prototype = {
-  __proto__: Presenter.prototype,
-
-  type: 'Haptic',
-
-  PIVOT_CHANGE_PATTERN: [40],
-
-  pivotChanged: function HapticPresenter_pivotChanged(aContext, aReason) {
-    return { type: this.type, details: { pattern: this.PIVOT_CHANGE_PATTERN } };
-  }
-};
-
-/**
  * A braille presenter
  */
-
 this.BraillePresenter = function BraillePresenter() {};
 
 BraillePresenter.prototype = {
   __proto__: Presenter.prototype,
 
   type: 'Braille',
 
   pivotChanged: function BraillePresenter_pivotChanged(aContext, aReason) {
     if (!aContext.accessible) {
       return null;
     }
 
-    let brailleOutput = BrailleGenerator.genForContext(aContext);
-    brailleOutput.output = brailleOutput.output.join(' ');
-    brailleOutput.selectionStart = 0;
-    brailleOutput.selectionEnd = 0;
-
-    return { type: this.type, details: brailleOutput };
+    return {
+      type: this.type,
+      details: {
+        output: Utils.localize(BrailleGenerator.genForContext(aContext)).join(
+          ' '),
+        selectionStart: 0,
+        selectionEnd: 0
+      }
+    };
   },
 
   textSelectionChanged: function BraillePresenter_textSelectionChanged(aText, aStart,
                                                                        aEnd, aOldStart,
                                                                        aOldEnd, aIsFromUser) {
-    return { type: this.type,
-             details: { selectionStart: aStart,
-                        selectionEnd: aEnd } };
-  },
-
-
+    return {
+      type: this.type,
+      details: {
+        selectionStart: aStart,
+        selectionEnd: aEnd
+      }
+    };
+  }
 };
 
 this.Presentation = {
   get presenters() {
     delete this.presenters;
     let presenterMap = {
       'mobile/android': [VisualPresenter, AndroidPresenter],
-      'b2g': [VisualPresenter, SpeechPresenter, HapticPresenter],
-      'browser': [VisualPresenter, SpeechPresenter, HapticPresenter,
-                  AndroidPresenter]
+      'b2g': [VisualPresenter, B2GPresenter],
+      'browser': [VisualPresenter, B2GPresenter, AndroidPresenter]
     };
     this.presenters = [new P() for (P of presenterMap[Utils.MozBuildApp])];
     return this.presenters;
   },
 
   pivotChanged: function Presentation_pivotChanged(aPosition, aOldPosition, aReason,
                                                    aStartOffset, aEndOffset) {
     let context = new PivotContext(aPosition, aOldPosition, aStartOffset, aEndOffset);
@@ -643,17 +618,17 @@ this.Presentation = {
   editingModeChanged: function Presentation_editingModeChanged(aIsEditing) {
     return [p.editingModeChanged(aIsEditing)
               for each (p in this.presenters)];
   },
 
   announce: function Presentation_announce(aAnnouncement) {
     // XXX: Typically each presenter uses the UtteranceGenerator,
     // but there really isn't a point here.
-    return [p.announce(UtteranceGenerator.genForAnnouncement(aAnnouncement)[0])
+    return [p.announce(UtteranceGenerator.genForAnnouncement(aAnnouncement))
               for each (p in this.presenters)];
   },
 
   liveRegion: function Presentation_liveRegion(aAccessible, aIsPolite, aIsHide,
     aModifiedText) {
     let context;
     if (!aModifiedText) {
       context = new PivotContext(aAccessible, null, -1, -1, true,
--- a/accessible/jsat/Utils.jsm
+++ b/accessible/jsat/Utils.jsm
@@ -16,16 +16,18 @@ XPCOMUtils.defineLazyModuleGetter(this, 
 XPCOMUtils.defineLazyModuleGetter(this, 'Roles',
   'resource://gre/modules/accessibility/Constants.jsm');
 XPCOMUtils.defineLazyModuleGetter(this, 'Events',
   'resource://gre/modules/accessibility/Constants.jsm');
 XPCOMUtils.defineLazyModuleGetter(this, 'Relations',
   'resource://gre/modules/accessibility/Constants.jsm');
 XPCOMUtils.defineLazyModuleGetter(this, 'States',
   'resource://gre/modules/accessibility/Constants.jsm');
+XPCOMUtils.defineLazyModuleGetter(this, 'PluralForm',
+  'resource://gre/modules/PluralForm.jsm');
 
 this.EXPORTED_SYMBOLS = ['Utils', 'Logger', 'PivotContext', 'PrefCache', 'SettingCache'];
 
 this.Utils = {
   _buildAppMap: {
     '{3c2e2abc-06d4-11e1-ac3b-374f68613e61}': 'b2g',
     '{ec8030f7-c20a-464f-9b0e-13a3a9e97384}': 'browser',
     '{aa3c5121-dab2-40e2-81ca-7ea25febc110}': 'mobile/android',
@@ -184,20 +186,59 @@ this.Utils = {
 
   get isContentProcess() {
     delete this.isContentProcess;
     this.isContentProcess =
       Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT;
     return this.isContentProcess;
   },
 
+  localize: function localize(aOutput) {
+    let outputArray = Array.isArray(aOutput) ? aOutput : [aOutput];
+    let localized =
+      [this.stringBundle.get(details) for (details of outputArray)]; // jshint ignore:line
+    // Clean up the white space.
+    let trimmed;
+    return [trimmed for (word of localized) if (word && // jshint ignore:line
+      (trimmed = word.trim()))]; // jshint ignore:line
+  },
+
   get stringBundle() {
     delete this.stringBundle;
-    this.stringBundle = Services.strings.createBundle(
+    let bundle = Services.strings.createBundle(
       'chrome://global/locale/AccessFu.properties');
+    this.stringBundle = {
+      get: function stringBundle_get(aDetails = {}) {
+        if (!aDetails || typeof aDetails === 'string') {
+          return aDetails;
+        }
+        let str = '';
+        let string = aDetails.string;
+        if (!string) {
+          return str;
+        }
+        try {
+          let args = aDetails.args;
+          let count = aDetails.count;
+          if (args) {
+            str = bundle.formatStringFromName(string, args, args.length);
+          } else {
+            str = bundle.GetStringFromName(string);
+          }
+          if (count) {
+            str = PluralForm.get(count, str);
+            str = str.replace('#1', count);
+          }
+        } catch (e) {
+          Logger.debug('Failed to get a string from a bundle for', string);
+        } finally {
+          return str;
+        }
+      }
+    };
     return this.stringBundle;
   },
 
   getMessageManager: function getMessageManager(aBrowser) {
     try {
       return aBrowser.QueryInterface(Ci.nsIFrameLoaderOwner).
          frameLoader.messageManager;
     } catch (x) {
--- a/accessible/tests/mochitest/jsat/jsatcommon.js
+++ b/accessible/tests/mochitest/jsat/jsatcommon.js
@@ -73,17 +73,17 @@ var AccessFuTest = {
 
   _addObserver: function AccessFuTest__addObserver(aWaitForData, aListener) {
     var listener = function listener(aSubject, aTopic, aData) {
       var data = JSON.parse(aData)[1];
       // Ignore non-relevant outputs.
       if (!data) {
         return;
       }
-      isDeeply(data.details.actions, aWaitForData, "Data is correct");
+      isDeeply(data.details, aWaitForData, "Data is correct");
       aListener.apply(listener);
     };
     Services.obs.addObserver(listener, 'accessfu-output', false);
     return listener;
   },
 
   on: function AccessFuTest_on(aWaitForData, aListener) {
     return this._addObserver(aWaitForData, aListener);
@@ -283,18 +283,19 @@ AccessFuContentTest.prototype = {
       this.pump();
       return;
     }
 
     var speech = this.extractUtterance(aMessage.json);
     var android = this.extractAndroid(aMessage.json, expected.android);
     if ((speech && expected.speak) || (android && expected.android)) {
       if (expected.speak) {
-        (SimpleTest[expected.speak_checkFunc] || is)(speech, expected.speak,
-          '"' + speech + '" spoken');
+        var checkFunc = SimpleTest[expected.speak_checkFunc] || isDeeply;
+        checkFunc.apply(SimpleTest, [speech, expected.speak,
+          '"' + JSON.stringify(speech) + '" spoken']);
       }
 
       if (expected.android) {
         var checkFunc = SimpleTest[expected.android_checkFunc] || ok;
         checkFunc.apply(SimpleTest,
           this.lazyCompare(android, expected.android));
       }
 
@@ -327,29 +328,31 @@ AccessFuContentTest.prototype = {
   },
 
   extractUtterance: function(aData) {
     if (!aData) {
       return null;
     }
 
     for (var output of aData) {
-      if (output && output.type === 'Speech') {
-        for (var action of output.details.actions) {
-          if (action && action.method == 'speak') {
-            return action.data;
-          }
+      if (output && output.type === 'B2G') {
+        if (output.details && output.details.data[0].string !== 'clickAction') {
+          return output.details.data;
         }
       }
     }
 
     return null;
   },
 
   extractAndroid: function(aData, aExpectedEvents) {
+    if (!aData) {
+      return null;
+    }
+
     for (var output of aData) {
       if (output && output.type === 'Android') {
         for (var i in output.details) {
           // Only extract if event types match expected event types.
           var exp = aExpectedEvents ? aExpectedEvents[i] : null;
           if (!exp || (output.details[i].eventType !== exp.eventType)) {
             return null;
           }
--- a/accessible/tests/mochitest/jsat/output.js
+++ b/accessible/tests/mochitest/jsat/output.js
@@ -17,47 +17,51 @@ Cu.import("resource://gre/modules/access
  * Note: if |aOldAccOrElmOrID| is not provided, the |aAccOrElmOrID| must be
  * scoped to the "root" element in markup.
  */
 function testContextOutput(expected, aAccOrElmOrID, aOldAccOrElmOrID, aGenerator) {
   var accessible = getAccessible(aAccOrElmOrID);
   var oldAccessible = aOldAccOrElmOrID !== null ?
 	getAccessible(aOldAccOrElmOrID || 'root') : null;
   var context = new PivotContext(accessible, oldAccessible);
-  var output = aGenerator.genForContext(context).output;
+  var output = aGenerator.genForContext(context);
 
   // Create a version of the output that has null members where we have
   // null members in the expected output. Those are indexes that are not testable
   // because of the changing nature of the test (different window names), or strings
   // that are inaccessible to us, like the title of parent documents.
   var masked_output = [];
   for (var i=0; i < output.length; i++) {
     if (expected[i] === null) {
       masked_output.push(null);
     } else {
-      masked_output[i] = output[i];
+      masked_output[i] = typeof output[i] === "string" ? output[i].trim() :
+        output[i];
     }
   }
 
   isDeeply(masked_output, expected,
            "Context output is correct for " + aAccOrElmOrID +
-           " (output: " + output.join(", ") + ") ==" +
-           " (expected: " + expected.join(", ") + ")");
+           " (output: " + JSON.stringify(output) + ") ==" +
+           " (expected: " + JSON.stringify(expected) + ")");
 }
 
 /**
  * Test object output generated array that includes names.
  * Note: test ignores outputs without the name.
  *
  * @param aAccOrElmOrID identifier to get an accessible to test.
  * @param aGenerator    the output generator to use when generating accessible
  *                      output
  */
 function testObjectOutput(aAccOrElmOrID, aGenerator) {
   var accessible = getAccessible(aAccOrElmOrID);
+  if (!accessible.name || !accessible.name.trim()) {
+    return;
+  }
   var context = new PivotContext(accessible);
   var output = aGenerator.genForObject(accessible, context);
   var outputOrder;
   try {
     outputOrder = SpecialPowers.getIntPref(PREF_UTTERANCE_ORDER);
   } catch (ex) {
     // PREF_UTTERANCE_ORDER not set.
     outputOrder = 0;
--- a/accessible/tests/mochitest/jsat/test_alive.html
+++ b/accessible/tests/mochitest/jsat/test_alive.html
@@ -18,39 +18,33 @@
       SpecialPowers.setIntPref("accessibility.accessfu.activate", 1);
       AccessFuTest.once_log("EventManager.start", AccessFuTest.nextTest);
     }
 
     // Listen for 'EventManager.stop' and enable AccessFu again.
     function settingsStart() {
       ok(true, "EventManager was stopped.");
       isnot(AccessFu._enabled, true, "AccessFu was disabled.");
-      AccessFuTest.once([{
-        "method": "speak",
-        "data": "Screen reader started",
-        "options": {
-          "enqueue": false
-        }
-      }], AccessFuTest.nextTest);
+      AccessFuTest.once({
+        "eventType": "announcement",
+        "data": [{string: "screenReaderStarted"}]
+      }, AccessFuTest.nextTest);
       // XXX: Bug 978076 - test start with SettingsManager.
       //navigator.mozSettings.createLock().set(
       //  {'accessibility.screenreader': false});
       AccessFu._enable()
     }
 
     // Make sure EventManager is started again.
     function settingsStop() {
       ok(AccessFu._enabled, "AccessFu was enabled again.");
-      AccessFuTest.once([{
-        "method": "speak",
-        "data": "Screen reader stopped",
-        "options": {
-          "enqueue": false
-        }
-      }], AccessFuTest.finish);
+      AccessFuTest.once({
+        "eventType": "announcement",
+        "data": [{string: "screenReaderStopped"}]
+      }, AccessFuTest.finish);
       // XXX: Bug 978076 - test stop with SettingsManager.
       //navigator.mozSettings.createLock().set(
       //  {'accessibility.screenreader': false});
       AccessFu._disable();
     }
 
     // Listen for initial 'EventManager.start' and disable AccessFu.
     function prefStop() {
--- a/accessible/tests/mochitest/jsat/test_content_integration.html
+++ b/accessible/tests/mochitest/jsat/test_content_integration.html
@@ -18,181 +18,188 @@
   <script type="application/javascript" src="../role.js"></script>
   <script type="application/javascript" src="../states.js"></script>
   <script type="application/javascript" src="../layout.js"></script>
   <script type="application/javascript" src="jsatcommon.js"></script>
 
   <script type="application/javascript">
     function doTest() {
       var doc = currentTabDocument();
-      var iframe = doc.createElement("iframe");
+      var iframe = doc.createElement('iframe');
       iframe.mozbrowser = true;
-      iframe.addEventListener("mozbrowserloadend", function () {
+      iframe.addEventListener('mozbrowserloadend', function () {
       var contentTest = new AccessFuContentTest(
         [
           // Simple traversal forward
           [ContentMessages.simpleMoveNext, {
-            speak: 'Phone status bar Traversal Rule test document'
+            speak: ['Phone status bar', 'Traversal Rule test document']
           }],
           [ContentMessages.simpleMoveNext, {
-            speak: 'wow heading level 1 such app'
+            speak: ['wow', {'string': 'headingLevel', 'args': [1]} ,'such app']
           }],
           [ContentMessages.simpleMoveNext, {
-            speak: 'many option not checked check button First item list 1 item'
+            speak: ['many option', {'string': 'stateNotChecked'},
+              {'string': 'checkbutton'}, {'string': 'listStart'},
+              {'string': 'list'}, {'string': 'listItemsCount', 'count': 1}]
           }],
           // check checkbox
           [ContentMessages.activateCurrent(), {
-            speak: 'checked'
+            speak: [{'string': 'checkAction'}]
           }],
           [ContentMessages.simpleMoveNext, {
-            speak: 'much range label'
+            speak: ['much range', {'string': 'label'}]
           }],
           [ContentMessages.simpleMoveNext, {
-            speak: 'much range 5 slider'
+            speak: ['much range', '5', {'string': 'slider'}]
           }],
           [ContentMessages.adjustRangeUp,
-           { speak: '6'}],
+           { speak: ['6']}],
           [ContentMessages.simpleMoveNext, {
-            speak: 'Home button'
+            speak: ['Home', {'string': 'pushbutton'}]
           }],
 
           // Simple traversal backward
           [ContentMessages.simpleMovePrevious, {
-            speak: 'much range 6 slider such app'
+            speak: ['much range', '6', {'string': 'slider'}, 'such app']
           }],
           [ContentMessages.adjustRangeDown,
-           { speak: '5'}],
+           { speak: ['5']}],
           [ContentMessages.simpleMovePrevious, {
-            speak: 'much range label'
+            speak: ['much range', {'string': 'label'}]
           }],
           [ContentMessages.simpleMovePrevious, {
-            speak: 'many option checked check button First item list 1 item'
+            speak: ['many option', {'string': 'stateChecked'},
+              {'string': 'checkbutton'}, {'string': 'listStart'},
+              {'string': 'list'}, {'string': 'listItemsCount', 'count': 1}]
           }],
           // uncheck checkbox
           [ContentMessages.activateCurrent(), {
-            speak: 'unchecked'
+            speak: [{'string': 'uncheckAction'}]
           }],
           [ContentMessages.simpleMovePrevious, {
-            speak: 'wow heading level 1'
+            speak: ['wow', {'string': 'headingLevel', 'args': [1]}]
           }],
           [ContentMessages.simpleMovePrevious, {
-            speak: 'Phone status bar'
+            speak: ['Phone status bar']
           }],
 
           // Moving to the absolute last item from an embedded document
           // fails. Bug 972035.
           [ContentMessages.simpleMoveNext, {
-            speak: 'wow heading level 1 such app'
+            speak: ['wow', {'string': 'headingLevel', 'args': [1]}, 'such app']
           }],
           // Move from an inner frame to the last element in the parent doc
           [ContentMessages.simpleMoveLast, {
-            speak: 'Home button',
+            speak: ['Home', {'string': 'pushbutton'}],
             speak_checkFunc: 'todo_is'
           }],
 
           [ContentMessages.clearCursor, 'AccessFu:CursorCleared'],
 
           // Moving to the absolute first item from an embedded document
           // fails. Bug 972035.
           [ContentMessages.simpleMoveNext, {
-            speak: 'Phone status bar Traversal Rule test document'
+            speak: ['Phone status bar', 'Traversal Rule test document']
+          }],
+          [ContentMessages.simpleMoveNext, {
+            speak: ['wow', {'string': 'headingLevel', 'args': [1]}, 'such app']
           }],
           [ContentMessages.simpleMoveNext, {
-            speak: 'wow heading level 1 such app'
-          }],
-          [ContentMessages.simpleMoveNext, {
-            speak: 'many option not checked check button First item list 1 item'
+            speak: ['many option', {'string': 'stateNotChecked'},
+              {'string': 'checkbutton'}, {'string': 'listStart'},
+              {'string': 'list'}, {'string': 'listItemsCount', 'count': 1}]
           }],
           [ContentMessages.simpleMoveFirst, {
-            speak: 'Phone status bar',
+            speak: ['Phone status bar'],
             speak_checkFunc: 'todo_is'
           }],
 
           // Reset cursors
           [ContentMessages.clearCursor, 'AccessFu:CursorCleared'],
 
           // Move cursor with focus in outside document
           [ContentMessages.simpleMoveNext, {
-            speak: 'Phone status bar Traversal Rule test document'
+            speak: ['Phone status bar', 'Traversal Rule test document']
           }],
           [ContentMessages.focusSelector('button#home', false), {
-            speak: 'Home button'
+            speak: ['Home', {'string': 'pushbutton'}]
           }],
 
           // Blur button and reset cursor
           [ContentMessages.focusSelector('button#home', true), null],
           [ContentMessages.clearCursor, 'AccessFu:CursorCleared'],
 
           // Set focus on element outside of embedded frame while
           // cursor is in frame
           [ContentMessages.simpleMoveNext, {
-            speak: 'Phone status bar Traversal Rule test document'
+            speak: ['Phone status bar', 'Traversal Rule test document']
           }],
           [ContentMessages.simpleMoveNext, {
-            speak: 'wow heading level 1 such app'
+            speak: ['wow', {'string': 'headingLevel', 'args': [1]}, 'such app']
           }],
           [ContentMessages.focusSelector('button#home', false), {
-            speak: 'Home button'
+            speak: ['Home button']
           }]
 
           // Blur button and reset cursor
           [ContentMessages.focusSelector('button#home', true), null],
           [ContentMessages.clearCursor, 'AccessFu:CursorCleared'],
 
           // XXX: Set focus on iframe itself.
           // XXX: Set focus on element in iframe when cursor is outside of it.
           // XXX: Set focus on element in iframe when cursor is in iframe.
 
           // Open dialog in outer doc, while cursor is also in outer doc
           [ContentMessages.simpleMoveNext, {
-            speak: 'Phone status bar Traversal Rule test document'
+            speak: ['Phone status bar', 'Traversal Rule test document']
           }],
           [doc.defaultView.showAlert, {
-            speak: 'This is an alert! heading level 1 dialog'
+            speak: ['This is an alert! heading level 1 dialog']
           }],
 
           [function() {
             doc.defaultView.hideAlert()
           }, {
-            speak: 'wow heading level 1 such app'
+            speak: ['wow', {'string': 'headingLevel', 'args': [1]}, 'such app']
           }],
 
           [ContentMessages.clearCursor, 'AccessFu:CursorCleared'],
 
           // Open dialog in outer doc, while cursor is in inner frame
           [ContentMessages.simpleMoveNext, {
-            speak: 'Phone status bar Traversal Rule test document'
+            speak: ['Phone status bar', 'Traversal Rule test document']
           }],
           [ContentMessages.simpleMoveNext, {
-            speak: 'wow heading level 1 such app'
+            speak: ['wow', {'string': 'headingLevel', 'args': [1]}, 'such app']
           }],
           [doc.defaultView.showAlert, {
-            speak: 'This is an alert! heading level 1 dialog'
+            speak: ['This is an alert! heading level 1 dialog']
           }],
 
           // XXX: Place cursor back where it was.
           [doc.defaultView.hideAlert, {
-            speak: 'many option not checked check button such app'
+            speak: ['many option not checked check button such app']
           }],
 
           [ContentMessages.clearCursor, 'AccessFu:CursorCleared'],
 
           // Open dialog, then focus on something when closing
           [ContentMessages.simpleMoveNext, {
-            speak: 'Phone status bar Traversal Rule test document'
+            speak: ['Phone status bar', 'Traversal Rule test document']
           }],
           [doc.defaultView.showAlert, {
-            speak: 'This is an alert! heading level 1 dialog'
+            speak: ['This is an alert! heading level 1 dialog']
           }],
 
           [function() {
             doc.defaultView.hideAlert();
             doc.querySelector('button#home').focus();
           }, {
-            speak: 'Home button Traversal Rule test document'
+            speak: ['Home', {'string': 'pushbutton'},
+              'Traversal Rule test document']
           }]
         ]);
 
         contentTest.start(function () {
           closeBrowserWindow();
           SimpleTest.finish();
         });
 
@@ -202,27 +209,27 @@
     }
 
     SimpleTest.waitForExplicitFinish();
     addLoadEvent(
       function () {
         openBrowserWindow(
           function () {
             SpecialPowers.pushPrefEnv({
-              "set": [
+              'set': [
                 // TODO: remove this as part of bug 820712
-                ["network.disable.ipc.security", true],
+                ['network.disable.ipc.security', true],
 
 
-                ["dom.ipc.browser_frames.oop_by_default", true],
-                ["dom.mozBrowserFramesEnabled", true],
-                ["browser.pagethumbnails.capturing_disabled", true]
+                ['dom.ipc.browser_frames.oop_by_default', true],
+                ['dom.mozBrowserFramesEnabled', true],
+                ['browser.pagethumbnails.capturing_disabled', true]
               ]
             }, doTest) },
-          getRootDirectory(window.location.href) + "doc_content_integration.html");
+          getRootDirectory(window.location.href) + 'doc_content_integration.html');
         });
   </script>
 </head>
 <body id="body">
 
   <a target="_blank"
      title="Add tests for OOP message handling and general integration"
      href="https://bugzilla.mozilla.org/show_bug.cgi?id=972047">Mozilla Bug 933808</a>
--- a/accessible/tests/mochitest/jsat/test_content_text.html
+++ b/accessible/tests/mochitest/jsat/test_content_text.html
@@ -22,19 +22,19 @@
 
   <script type="application/javascript">
     function doTest() {
       var doc = currentTabDocument();
       var textTest = new AccessFuContentTest(
         [
           // Read-only text tests
           [ContentMessages.simpleMoveFirst, {
-            speak: 'These are my awards, Mother. From Army. ' +
+            speak: ['These are my awards, Mother. From Army. ' +
               'The seal is for marksmanship, and the gorilla is ' +
-              'for sand racing. Text content test document'
+              'for sand racing.', 'Text content test document']
           }],
           [ContentMessages.moveNextBy('word'), {
             speak: 'These',
             speak_checkFunc: 'todo_is', // Bug 980509
             android: [{
               eventType: AndroidEvent.VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY,
               fromIndex: 0,
               toIndex: 5
@@ -137,18 +137,18 @@
               fromIndex: 0,
               toIndex: 6
             }],
             android_checkFunc: 'todo' // Bug 980512
           }],
 
           // Editable text tests.
           [ContentMessages.focusSelector('textarea'), {
-            speak: 'Please refrain from Mayoneggs during this ' +
-              'salmonella scare. text area'
+            speak: ['Please refrain from Mayoneggs during this ' +
+              'salmonella scare.', {string: 'textarea'}]
           }],
           [null, { // When we first focus, caret is at 0.
               android: [{
                 eventType: AndroidEvent.VIEW_TEXT_SELECTION_CHANGED,
                 brailleOutput: {
                   selectionStart: 0,
                   selectionEnd: 0
                 }
--- a/accessible/tests/mochitest/jsat/test_explicit_names.html
+++ b/accessible/tests/mochitest/jsat/test_explicit_names.html
@@ -11,90 +11,109 @@
   <script type="application/javascript"
           src="output.js"></script>
   <script type="application/javascript">
 
     function doTest() {
       // Test the following accOrElmOrID.
       var tests = [{
         accOrElmOrID: "anchor1",
-        expected: ["link", "title"]
+        expected: [{"string": "link"}, "title"]
       }, {
         accOrElmOrID: "anchor2",
-        expected: ["link", "This is a link"]
+        expected: [{"string": "link"}, "This is a link"]
       }, {
         accOrElmOrID: "button1",
-        expected: ["button", "Press me"]
+        expected: [{"string": "pushbutton"}, "Press me"]
       }, {
         accOrElmOrID: "button2",
-        expected: ["button", "Press me"]
+        expected: [{"string": "pushbutton"}, "Press me"]
       }, {
         accOrElmOrID: "textarea1",
-        expected: ["text area", "This is the text area text.", "Test Text Area"]
+        expected: [{"string": "textarea"}, "This is the text area text.",
+          "Test Text Area"]
       }, {
         accOrElmOrID: "textarea2",
-        expected: ["text area", "This is the text area text."]
+        expected: [{"string": "textarea"}, "This is the text area text."]
       }, {
         accOrElmOrID: "heading1",
-        expected: ["heading level 1", "Test heading", "This is the heading."]
+        expected: [{"string": "headingLevel", "args": [1]}, "Test heading",
+          "This is the heading."]
       }, {
         accOrElmOrID: "heading1",
         oldAccOrElmOrID: null,
         expected: [null /* parent doc title */, document.title,
-		   "heading level 1", "Test heading", "This is the heading."]
+          {"string": "headingLevel", "args": [1]}, "Test heading",
+          "This is the heading."]
       }, {
         accOrElmOrID: "heading2",
-        expected: ["heading level 1", "This is the heading."]
+        expected: [{"string": "headingLevel", "args": [1]},
+          "This is the heading."]
       }, {
         accOrElmOrID: "list",
-        expected: ["list 2 items", "Test List", "First item", "Top of the list",
-          "Last item", "2.", "list two"]
+        expected: [{"string": "list"}, {"string": "listItemsCount", "count": 2},
+          "Test List", {"string": "listStart"}, "Top of the list",
+          {"string": "listEnd"}, "2.", "list two"]
       }, {
         accOrElmOrID: "dlist",
-        expected: ["definition list 0.5 items", "Test Definition List",
+        expected: [{"string": "definitionlist"},
+          {"string": "listItemsCount", "count": 0.5}, "Test Definition List",
           "dd one"]
       }, {
         accOrElmOrID: "li_one",
-        expected: ["list 2 items", "Test List", "First item", "Top of the list"]
+        expected: [{"string": "list"}, {"string": "listItemsCount", "count": 2},
+          "Test List", {"string": "listStart"}, "Top of the list"]
       }, {
         accOrElmOrID: "li_two",
-        expected: ["list 2 items", "Test List", "Last item", "2.", "list two"]
+        expected: [{"string": "list"}, {"string": "listItemsCount", "count": 2},
+          "Test List", {"string": "listEnd"}, "2.", "list two"]
       }, {
         accOrElmOrID: "cell",
-        expected: ["table with 1 column and 1 row", "Fruits and vegetables",
-          "Column 1 Row 1", "List of Fruits", "list 4 items", "First item",
-          "link", "Apples", "link", "Bananas", "link", "Peaches", "Last item",
-          "link", "Plums"]
+        expected: [{"string": "table"},
+          {"string": "tblColumnInfo", "count": 1},
+          {"string": "tblRowInfo", "count": 1}, "Fruits and vegetables",
+          {"string": "columnInfo", "args": [1]},
+          {"string": "rowInfo", "args": [1]}, "List of Fruits",
+          {"string": "list"}, {"string": "listItemsCount", "count": 4},
+          {"string": "listStart"}, {"string": "link"}, "Apples",
+          {"string": "link"}, "Bananas",
+          {"string": "link"}, "Peaches", {"string": "listEnd"},
+          {"string": "link"}, "Plums"]
       }, {
         accOrElmOrID: "app.net",
-        expected: ["list 2 items", "First item", "link", "star", "Last item",
-          "link", "repost"]
+        expected: [{"string": "list"}, {"string": "listItemsCount", "count": 2},
+          {"string": "listStart"}, {"string": "link"}, "star",
+          {"string": "listEnd"}, {"string": "link"}, "repost"]
       }, {
         // Test pivot to list from li_one.
         accOrElmOrID: "list",
         oldAccOrElmOrID: "li_one",
-        expected: ["list 2 items", "Test List", "First item", "Top of the list",
-          "Last item", "2.", "list two"]
+        expected: [{"string": "list"}, {"string": "listItemsCount", "count": 2},
+          "Test List", {"string": "listStart"}, "Top of the list",
+          {"string": "listEnd"}, "2.", "list two"]
       }, {
         // Test pivot to li_one from list.
         accOrElmOrID: "li_one",
         oldAccOrElmOrID: "list",
-        expected: ["First item", "Top of the list"]
+        expected: [{"string": "listStart"}, "Top of the list"]
       }, {
         // Test pivot to "apples" link from the table cell.
         accOrElmOrID: "apples",
         oldAccOrElmOrID: "cell",
-        expected: ["list 4 items", "First item", "link", "Apples"]
+        expected: [{"string": "list"}, {"string": "listItemsCount", "count": 4},
+          {"string": "listStart"}, {"string": "link"}, "Apples"]
       }, {
         // Test pivot to the table cell from the "apples" link.
         accOrElmOrID: "cell",
         oldAccOrElmOrID: "apples",
-        expected: ["List of Fruits", "list 4 items", "First item", "link",
-          "Apples", "link", "Bananas", "link", "Peaches", "Last item", "link",
-          "Plums"]
+        expected: ["List of Fruits", {"string": "list"},
+          {"string": "listItemsCount", "count": 4}, {"string": "listStart"},
+          {"string": "link"}, "Apples", {"string": "link"}, "Bananas",
+          {"string": "link"}, "Peaches", {"string": "listEnd"},
+          {"string": "link"}, "Plums"]
       }];
 
       SpecialPowers.setIntPref(PREF_UTTERANCE_ORDER, 0);
 
       // Test various explicit names vs the utterance generated from subtrees.
       tests.forEach(function run(test) {
         testOutput(test.expected, test.accOrElmOrID, test.oldAccOrElmOrID, 1);
       });
--- a/accessible/tests/mochitest/jsat/test_landmarks.html
+++ b/accessible/tests/mochitest/jsat/test_landmarks.html
@@ -11,91 +11,114 @@
   <script type="application/javascript"
           src="output.js"></script>
   <script type="application/javascript">
 
     function doTest() {
       // Test the following accOrElmOrID.
       var tests = [{
         accOrElmOrID: "nav",
-        expectedUtterance: [["navigation", "a nav"], ["a nav", "navigation"]],
-        expectedBraille: [["navigation", "a nav"], ["a nav", "navigation"]]
+        expectedUtterance: [[{"string": "navigation"}, "a nav"],
+                            ["a nav", {"string": "navigation"}]],
+        expectedBraille: [[{"string": "navigation"}, "a nav"],
+                          ["a nav", {"string": "navigation"}]]
       }, {
         accOrElmOrID: "main",
-        expectedUtterance: [["main", "a main area"], ["a main area", "main"]],
-        expectedBraille: [["main", "a main area"], ["a main area", "main"]]
+        expectedUtterance: [[{"string": "main"}, "a main area"],
+                            ["a main area", {"string": "main"}]],
+        expectedBraille: [[{"string": "main"}, "a main area"],
+                          ["a main area", {"string": "main"}]]
       }, {
         accOrElmOrID: "header",
-        expectedUtterance: [["banner", "header", "a header"], ["a header",
-          "header", "banner"]],
-        expectedBraille: [["banner", "header", "a header"], ["a header",
-          "header", "banner"]]
+        expectedUtterance: [
+          [{"string": "banner"}, {"string": "header"}, "a header"],
+          ["a header", {"string": "header"}, {"string": "banner"}]],
+        expectedBraille: [
+          [{"string": "banner"}, {"string": "headerAbbr"}, "a header"],
+          ["a header", {"string": "headerAbbr"}, {"string": "banner"}]]
       }, {
         accOrElmOrID: "footer",
-        expectedUtterance: [["content info", "footer", "a footer"], [
-          "a footer", "footer", "content info"]],
-        expectedBraille: [["content info", "footer", "a footer"], ["a footer",
-          "footer", "content info"]]
+        expectedUtterance: [
+          [{"string": "contentinfo"}, {"string": "footer"}, "a footer"],
+          ["a footer", {"string": "footer"}, {"string": "contentinfo"}]],
+        expectedBraille: [
+          [{"string": "contentinfo"}, {"string": "footerAbbr"}, "a footer"],
+          ["a footer", {"string": "footerAbbr"}, {"string": "contentinfo"}]]
       }, {
         accOrElmOrID: "article_header",
-        expectedUtterance: [["header", "a header within an article"], [
-          "a header within an article", "header"]],
-        expectedBraille: [["header", "a header within an article"], [
-          "a header within an article", "header"]],
+        expectedUtterance: [
+          [{"string": "header"}, "a header within an article"],
+          ["a header within an article", {"string": "header"}]],
+        expectedBraille: [
+          [{"string": "headerAbbr"}, "a header within an article"],
+          ["a header within an article", {"string": "headerAbbr"}]],
       }, {
         accOrElmOrID: "article_footer",
-        expectedUtterance: [["footer", "a footer within an article"], [
-          "a footer within an article", "footer"]],
-        expectedBraille: [["footer", "a footer within an article"], [
-          "a footer within an article", "footer"]]
+        expectedUtterance: [
+          [{"string": "footer"}, "a footer within an article"],
+          ["a footer within an article", {"string": "footer"}]],
+        expectedBraille: [
+          [{"string": "footerAbbr"}, "a footer within an article"],
+          ["a footer within an article", {"string": "footerAbbr"}]]
       }, {
         accOrElmOrID: "section_header",
-        expectedUtterance: [["header", "a header within a section"], [
-          "a header within a section", "header"]],
-        expectedBraille: [["header", "a header within a section"], [
-          "a header within a section", "header"]]
+        expectedUtterance: [[{"string":"header"}, "a header within a section"],
+                            ["a header within a section", {"string":"header"}]],
+        expectedBraille: [
+          [{"string":"headerAbbr"}, "a header within a section"],
+          ["a header within a section", {"string":"headerAbbr"}]]
       }, {
         accOrElmOrID: "section_footer",
-        expectedUtterance: [["footer", "a footer within a section"], [
-          "a footer within a section", "footer"]],
-        expectedBraille: [["footer", "a footer within a section"], [
-          "a footer within a section", "footer"]]
+        expectedUtterance: [
+          [{"string": "footer"}, "a footer within a section"],
+          ["a footer within a section", {"string": "footer"}]],
+        expectedBraille: [
+          [{"string": "footerAbbr"}, "a footer within a section"],
+          ["a footer within a section", {"string": "footerAbbr"}]]
       }, {
         accOrElmOrID: "aside",
-        expectedUtterance: [["complementary", "by the way I am an aside"], [
-          "by the way I am an aside", "complementary"]],
-        expectedBraille: [["complementary", "by the way I am an aside"], [
-          "by the way I am an aside", "complementary"]]
+        expectedUtterance: [
+          [{"string": "complementary"}, "by the way I am an aside"],
+          ["by the way I am an aside", {"string": "complementary"}]],
+        expectedBraille: [
+          [{"string": "complementary"}, "by the way I am an aside"],
+          ["by the way I am an aside", {"string": "complementary"}]]
       }, {
         accOrElmOrID: "main_element",
-        expectedUtterance: [["main", "another main area"], [
-          "another main area", "main"]],
-        expectedBraille: [["main", "another main area"], ["another main area",
-          "main"]]
+        expectedUtterance: [[{"string": "main"}, "another main area"],
+                            ["another main area", {"string": "main"}]],
+        expectedBraille: [[{"string": "main"}, "another main area"],
+                          ["another main area", {"string": "main"}]]
       }, {
         accOrElmOrID: "complementary",
-        expectedUtterance: [["list 1 item", "complementary", "First item",
-          "A complementary"], ["A complementary", "First item",
-          "complementary", "list 1 item"]],
-        expectedBraille: [["*", "complementary", "A complementary"], ["*",
-          "A complementary", "complementary"]]
+        expectedUtterance: [[{"string": "list"},
+          {"string": "listItemsCount", "count": 1}, {"string": "complementary"},
+          {"string": "listStart"}, "A complementary"], ["A complementary",
+          {"string": "listStart"}, {"string": "complementary"},
+          {"string": "list"}, {"string": "listItemsCount", "count": 1}]],
+        expectedBraille: [["*", {"string": "complementary"}, "A complementary"],
+                          ["*", "A complementary", {"string": "complementary"}]]
       }, {
         accOrElmOrID: "parent_main",
-        expectedUtterance: [["main", "a parent main", "complementary",
-          "a child complementary"], ["a parent main", "a child complementary",
-          "complementary", "main"]],
-        expectedBraille: [["main", "a parent main", "complementary",
-          "a child complementary"], ["a parent main", "a child complementary",
-          "complementary", "main"]]
+        expectedUtterance: [[{"string": "main"}, "a parent main",
+          {"string": "complementary"}, "a child complementary"],
+          ["a parent main", "a child complementary",
+          {"string": "complementary"}, {"string": "main"}]],
+        expectedBraille: [[{"string": "main"}, "a parent main",
+          {"string": "complementary"}, "a child complementary"],
+          ["a parent main", "a child complementary",
+          {"string": "complementary"}, {"string": "main"}]]
       }, {
         accOrElmOrID: "child_complementary",
-        expectedUtterance: [["main", "complementary", "a child complementary"],
-          ["a child complementary", "complementary", "main"]],
-        expectedBraille: [["complementary", "a child complementary"],
-          ["a child complementary", "complementary"]]
+        expectedUtterance: [[{"string": "main"}, {"string": "complementary"},
+          "a child complementary"], ["a child complementary",
+          {"string": "complementary"}, {"string": "main"}]],
+        expectedBraille: [[{"string": "complementary"},
+          "a child complementary"], ["a child complementary",
+          {"string": "complementary"}]]
       }];
 
       // Test outputs (utterance and braille) for landmarks.
       tests.forEach(function run(test) {
         var outputOrderValues = [0, 1];
         outputOrderValues.forEach(function testOutputOrder(outputOrder) {
           SpecialPowers.setIntPref(PREF_UTTERANCE_ORDER, outputOrder);
           testOutput(test.expectedUtterance[outputOrder], test.accOrElmOrID,
--- a/accessible/tests/mochitest/jsat/test_live_regions.html
+++ b/accessible/tests/mochitest/jsat/test_live_regions.html
@@ -42,214 +42,214 @@
       udpate(id, text, "textContent");
     }
 
     function updateHTML(id, text) {
       udpate(id, text, "innerHTML");
     }
 
     var tests = [{
-      expected: [{
-        "method": "speak",
-        "data": "hidden I will be hidden",
+      expected: {
+        "eventType": "liveregion-change",
+        "data": [{"string": "hidden"}, "I will be hidden"],
         "options": {
           "enqueue": true
         }
-      }],
+      },
       action: function action() {
         [hide(id) for (id of ["to_hide1", "to_hide2", "to_hide3", "to_hide4"])];
       }
     }, {
-      expected: [{
-        "method": "speak",
-        "data": "hidden I will be hidden",
+      expected: {
+        "eventType": "liveregion-change",
+        "data": [{"string": "hidden"},"I will be hidden"],
         "options": {
           "enqueue": true
         }
-      }],
+      },
       action: function action() {
         [hide(id) for (id of ["to_hide_descendant1", "to_hide_descendant2",
           "to_hide_descendant3", "to_hide_descendant4"])];
       }
     }, {
-      expected: [{
-        "method": "speak",
-        "data": "I will be shown",
+      expected: {
+        "eventType": "liveregion-change",
+        "data": ["I will be shown"],
         "options": {
           "enqueue": true
         }
-      }],
+      },
       action: function action() {
         [show(id) for (id of ["to_show1", "to_show2", "to_show3", "to_show4"])];
       }
     }, {
-      expected: [{
-        "method": "speak",
-        "data": "I will be shown",
+      expected: {
+        "eventType": "liveregion-change",
+        "data": ["I will be shown"],
         "options": {
           "enqueue": true
         }
-      }],
+      },
       action: function action() {
         [show(id) for (id of ["to_show_descendant1", "to_show_descendant2",
           "to_show_descendant3", "to_show_descendant4"])];
       }
     }, {
-      expected: [{
-        "method": "speak",
-        "data": "hidden I will be hidden",
+      expected: {
+        "eventType": "liveregion-change",
+        "data": [{"string": "hidden"}, "I will be hidden"],
         "options": {
           "enqueue": false
         }
-      }],
+      },
       action: function action() {
         hide("to_hide_live_assertive");
       }
     }, {
-      expected: [{
-        "method": "speak",
-        "data": "I will be shown",
+      expected: {
+        "eventType": "liveregion-change",
+        "data": ["I will be shown"],
         "options": {
           "enqueue": false
         }
-      }],
+      },
       action: function action() {
         [show(id) for (id of ["to_show_live_off", "to_show_live_assertive"])];
       }
     }, {
-      expected: [{
-        "method": "speak",
-        "data": "Text Added",
+      expected: {
+        "eventType": "liveregion-change",
+        "data": ["Text Added"],
         "options": {
           "enqueue": false
         }
-      }],
+      },
       action: function action() {
         updateText("text_add", "Text Added");
       }
     }, {
-      expected: [{
-        "method": "speak",
-        "data": "Text Added",
+      expected: {
+        "eventType": "liveregion-change",
+        "data": ["Text Added"],
         "options": {
           "enqueue": false
         }
-      }],
+      },
       action: function action() {
         updateHTML("text_add", "Text Added");
       }
     }, {
-      expected: [{
-        "method": "speak",
-        "data": "hidden Text Removed",
+      expected: {
+        "eventType": "liveregion-change",
+        "data": [{"string": "hidden"}, "Text Removed"],
         "options": {
           "enqueue": true
         }
-      }],
+      },
       action: function action() {
         updateText("text_remove", "");
       }
     }, {
-      expected: [{
-        "method": "speak",
-        "data": "Descendant Text Added",
+      expected: {
+        "eventType": "liveregion-change",
+        "data": ["Descendant Text Added"],
         "options": {
           "enqueue": false
         }
-      }],
+      },
       action: function action() {
         updateText("text_add_descendant", "Descendant Text Added");
       }
     }, {
-      expected: [{
-        "method": "speak",
-        "data": "Descendant Text Added",
+      expected: {
+        "eventType": "liveregion-change",
+        "data": ["Descendant Text Added"],
         "options": {
           "enqueue": false
         }
-      }],
+      },
       action: function action() {
         updateHTML("text_add_descendant", "Descendant Text Added");
       }
     }, {
-      expected: [{
-        "method": "speak",
-        "data": "hidden Descendant Text Removed",
+      expected: {
+        "eventType": "liveregion-change",
+        "data": [{"string": "hidden"}, "Descendant Text Removed"],
         "options": {
           "enqueue": true
         }
-      }],
+      },
       action: function action() {
         updateText("text_remove_descendant", "");
       }
     }, {
-      expected: [{
-        "method": "speak",
-        "data": "Descendant Text Added",
+      expected: {
+        "eventType": "liveregion-change",
+        "data": ["Descendant Text Added"],
         "options": {
           "enqueue": false
         }
-      }],
+      },
       action: function action() {
         updateText("text_add_descendant2", "Descendant Text Added");
       }
     }, {
-      expected: [{
-        "method": "speak",
-        "data": "Descendant Text Added",
+      expected: {
+        "eventType": "liveregion-change",
+        "data": ["Descendant Text Added"],
         "options": {
           "enqueue": false
         }
-      }],
+      },
       action: function action() {
         updateHTML("text_add_descendant2", "Descendant Text Added");
       }
     }, {
-      expected: [{
-        "method": "speak",
-        "data": "hidden Descendant Text Removed",
+      expected: {
+        "eventType": "liveregion-change",
+        "data": [{"string": "hidden"}, "Descendant Text Removed"],
         "options": {
           "enqueue": true
         }
-      }],
+      },
       action: function action() {
         updateText("text_remove_descendant2", "");
       }
     }, {
-      expected: [{
-        "method": "speak",
-        "data": "I am replaced main",
+      expected: {
+        "eventType": "liveregion-change",
+        "data": ["I am replaced", {"string": "main"}],
         "options": {
           "enqueue": true
         }
-      }],
+      },
       action: function action() {
         var region = document.getElementById("to_replace_region");
         var child = document.getElementById("to_replace");
         child.setAttribute("role", "main");
       }
     }, {
-      expected: [{
-        "method": "speak",
-        "data": "I am a replaced text",
+      expected: {
+        "eventType": "liveregion-change",
+        "data": ["I am a replaced text"],
         "options": {
           "enqueue": false
         }
-      }],
+      },
       action: function action() {
         updateText("to_replace_text", "I am a replaced text");
       }
     }, {
-      expected: [{
-        "method": "speak",
-        "data": "I am a replaced text",
+      expected: {
+        "eventType": "liveregion-change",
+        "data": ["I am a replaced text"],
         "options": {
           "enqueue": false
         }
-      }],
+      },
       action: function action() {
         updateHTML("to_replace_text", "I am a replaced text");
       }
     }];
 
     function doTest() {
       AccessFuTest.addFunc(startAccessFu);
       tests.forEach(function addTest(test) {
--- a/accessible/tests/mochitest/jsat/test_output.html
+++ b/accessible/tests/mochitest/jsat/test_output.html
@@ -17,350 +17,382 @@ https://bugzilla.mozilla.org/show_bug.cg
     <script type="application/javascript">
 
       function doTest() {
         // Test the following accOrElmOrID (with optional old accOrElmOrID).
         // Note: each accOrElmOrID entry maps to a unique object utterance
         // generator function within the UtteranceGenerator.
         var tests = [{
           accOrElmOrID: "anchor",
-          expectedUtterance: [["link", "title"], ["title", "link"]],
-          expectedBraille: [["lnk", "title"], ["title", "lnk"]]
+          expectedUtterance: [[{"string": "link"}, "title"],
+            ["title", {"string": "link"}]],
+          expectedBraille: [[{"string": "linkAbbr"}, "title"],
+            ["title", {"string": "linkAbbr"}]]
         }, {
           accOrElmOrID: "anchor_titleandtext",
-          expectedUtterance: [["link", "goes to the tests -", "Tests"],
-                             ["Tests", "- goes to the tests", "link"]],
-          expectedBraille:   [["lnk", "goes to the tests -", "Tests"],
-                             ["Tests", "- goes to the tests", "lnk"]],
+          expectedUtterance: [[{"string": "link"}, "goes to the tests -",
+            "Tests"], ["Tests", "- goes to the tests", {"string": "link"}]],
+          expectedBraille:   [[{"string": "linkAbbr"}, "goes to the tests -",
+            "Tests"], ["Tests", "- goes to the tests", {"string": "linkAbbr"}]],
         }, {
           accOrElmOrID: "anchor_duplicatedtitleandtext",
-          expectedUtterance: [["link", "Tests"], ["Tests", "link"]],
-          expectedBraille: [["lnk", "Tests"], ["Tests", "lnk"]]
+          expectedUtterance: [[{"string": "link"}, "Tests"],
+            ["Tests", {"string": "link"}]],
+          expectedBraille: [[{"string": "linkAbbr"}, "Tests"],
+            ["Tests", {"string": "linkAbbr"}]]
         }, {
           accOrElmOrID: "anchor_arialabelandtext",
-          expectedUtterance: [["link", "goes to the tests - Tests"],
-                              ["Tests - goes to the tests", "link"]],
-          expectedBraille: [["lnk", "goes to the tests - Tests"],
-                              ["Tests - goes to the tests", "lnk"]],
+          expectedUtterance: [[{"string": "link"}, "goes to the tests - Tests"],
+            ["Tests - goes to the tests", {"string": "link"}]],
+          expectedBraille: [[{"string": "linkAbbr"},
+            "goes to the tests - Tests"], ["Tests - goes to the tests",
+            {"string": "linkAbbr"}]],
         }, {
           accOrElmOrID: "textarea",
-          expectedUtterance: [[
-            "text area", "This is the text area text."
-          ], [
-            "This is the text area text.", "text area"
-          ],],
-          expectedBraille: [[
-            "txtarea", "This is the text area text."
-          ], [
-            "This is the text area text.", "txtarea"
-          ],],
+          expectedUtterance: [[{"string": "textarea"},
+            "This is the text area text."], ["This is the text area text.",
+            {"string": "textarea"}]],
+          expectedBraille: [[{"string": "textareaAbbr"},
+            "This is the text area text."], ["This is the text area text.",
+            {"string": "textareaAbbr"}]],
         }, {
           accOrElmOrID: "heading",
-          expectedUtterance: [
-            ["heading level 1", "Test heading"],
-            ["Test heading", "heading level 1"]
-          ],
-          expectedBraille: [
-            ["heading", "Test heading"],
-            ["Test heading", "heading"]
-          ]
+          expectedUtterance: [[{"string": "headingLevel", "args": [1]},
+            "Test heading"], ["Test heading",
+            {"string": "headingLevel", "args": [1]}]],
+          expectedBraille: [[{"string": "headingAbbr"}, "Test heading"],
+                            ["Test heading", {"string": "headingAbbr"}]]
         }, {
           accOrElmOrID: "list",
-          expectedUtterance: [
-            ["list 1 item", "First item", "1.", "list one"],
-            ["1.", "list one", "First item", "list 1 item"]
+          expectedUtterance: [[{"string": "list"},
+            {"string": "listItemsCount", "count":1}, {"string": "listStart"},
+            "1.", "list one"], ["1.", "list one", {"string": "listStart"},
+            {"string": "list"}, {"string": "listItemsCount", "count":1}]
           ],
-          expectedBraille: [
-            ["list", "list one"],
-            ["list one", "list"]
-          ]
+          expectedBraille: [[{"string": "listAbbr"}, "list one"],
+            ["list one", {"string": "listAbbr"}]]
         }, {
           accOrElmOrID: "dlist",
-          expectedUtterance: [
-            ["definition list 0.5 items", "dd one"],
-            ["dd one", "definition list 0.5 items"]
+          expectedUtterance: [[{"string": "definitionlist"},
+            {"string": "listItemsCount", "count": 0.5}, "dd one"], ["dd one",
+            {"string": "definitionlist"},
+            {"string": "listItemsCount", "count": 0.5}]
           ],
-          expectedBraille: [
-            ["definition list", "dd one"],
-            ["dd one", "definition list"]
-          ]
+          expectedBraille: [[{"string": "definitionlistAbbr"}, "dd one"],
+                            ["dd one", {"string": "definitionlistAbbr"}]]
         }, {
           accOrElmOrID: "li_one",
-          expectedUtterance: [
-            ["list 1 item", "First item", "1.", "list one"],
-            ["1.", "list one", "First item", "list 1 item"]
+          expectedUtterance: [[{"string": "list"},
+            {"string": "listItemsCount", "count": 1}, {"string": "listStart"},
+            "1.", "list one"], ["1.", "list one", {"string": "listStart"},
+            {"string": "list"}, {"string": "listItemsCount", "count": 1}]
           ],
-          expectedBraille: [
-            ["1.", "list one"],
-            ["1.", "list one"]
-          ]
+          expectedBraille: [["1.", "list one"], ["1.", "list one"]]
         },
         {
           accOrElmOrID: "li_two",
-          expectedUtterance: [
-            ["list 1 item", "First item", "list two"],
-            ["list two", "First item", "list 1 item"]
+          expectedUtterance: [[{"string": "list"},
+            {"string": "listItemsCount", "count": 1}, {"string": "listStart"},
+            "list two"], ["list two", {"string": "listStart"},
+            {"string": "list"}, {"string": "listItemsCount", "count": 1}]
           ],
-          expectedBraille: [
-            ["*", "list two"],
-            ["*", "list two"]
-          ]
+          expectedBraille: [["*", "list two"], ["*", "list two"]]
         }, {
           accOrElmOrID: "cell",
-          expectedUtterance: [[
-            "table with 1 column and 1 row", "Fruits and vegetables",
-            "Column 1 Row 1", "list 4 items", "First item", "link", "Apples",
-            "link", "Bananas", "link", "Peaches", "Last item", "link", "Plums"
-          ], [
-            "Apples", "link", "First item", "Bananas", "link", "Peaches",
-            "link", "Plums", "link", "Last item", "list 4 items",
-            "Column 1 Row 1", "Fruits and vegetables",
-            "table with 1 column and 1 row"
-          ]],
-          expectedBraille: [[
-            "c1r1", "list", "lnk", "Apples", "lnk", "Bananas", "lnk",
-            "Peaches", "lnk", "Plums"
-          ], [
-            "Apples", "lnk", "Bananas", "lnk", "Peaches", "lnk", "Plums",
-            "lnk", "list", "c1r1"
-          ]]
+          expectedUtterance: [[{"string":"table"},
+            {"string": "tblColumnInfo", "count": 1},
+            {"string": "tblRowInfo", "count": 1}, "Fruits and vegetables",
+            {"string": "columnInfo", "args": [1]},
+            {"string": "rowInfo", "args": [1]}, {"string": "list"},
+            {"string": "listItemsCount", "count": 4}, {"string": "listStart"},
+            {"string": "link"}, "Apples", {"string": "link"}, "Bananas",
+            {"string": "link"}, "Peaches", {"string": "listEnd"},
+            {"string": "link"}, "Plums"], ["Apples", {"string": "link"},
+            {"string": "listStart"}, "Bananas", {"string": "link"}, "Peaches",
+            {"string": "link"}, "Plums", {"string": "link"},
+            {"string": "listEnd"}, {"string": "list"},
+            {"string": "listItemsCount", "count": 4},
+            {"string": "columnInfo", "args": [1]},
+            {"string": "rowInfo", "args": [1]}, "Fruits and vegetables",
+            {"string":"table"}, {"string": "tblColumnInfo", "count": 1},
+            {"string": "tblRowInfo", "count": 1}]],
+          expectedBraille: [[{"string": "cellInfoAbbr", "args": [ 1, 1]},
+            {"string": "listAbbr"}, {"string": "linkAbbr"}, "Apples",
+            {"string": "linkAbbr"}, "Bananas", {"string": "linkAbbr"},
+            "Peaches", {"string": "linkAbbr"}, "Plums"], ["Apples",
+            {"string": "linkAbbr"}, "Bananas", {"string": "linkAbbr"},
+            "Peaches", {"string": "linkAbbr"}, "Plums", {"string": "linkAbbr"},
+            {"string": "listAbbr"},
+            {"string": "cellInfoAbbr", "args": [ 1, 1]}]]
         }, {
           accOrElmOrID: "date",
-          expectedUtterance: [["date entry", "2011-09-29"], ["2011-09-29", "date entry"]],
-          expectedBraille: [["date entry", "2011-09-29"], ["2011-09-29", "date entry"]]
+          expectedUtterance: [[{"string": "textInputType_date"},
+            {"string": "entry"}, "2011-09-29"], ["2011-09-29",
+            {"string": "textInputType_date"}, {"string": "entry"}]],
+          expectedBraille: [[{"string": "textInputType_date"},
+            {"string": "entryAbbr"}, "2011-09-29"], ["2011-09-29",
+            {"string": "textInputType_date"}, {"string": "entryAbbr"}]]
         }, {
           accOrElmOrID: "email",
-          expectedUtterance: [
-            ["e-mail entry", "test@example.com"],
-            ["test@example.com", "e-mail entry"]
-          ],
-          expectedBraille: [
-            ["e-mail entry", "test@example.com"],
-            ["test@example.com", "e-mail entry"]
-          ]
+          expectedUtterance: [[{"string": "textInputType_email"},
+            {"string": "entry"}, "test@example.com"], ["test@example.com",
+            {"string": "textInputType_email"}, {"string": "entry"}]],
+          expectedBraille: [[{"string": "textInputType_email"},
+            {"string": "entryAbbr"}, "test@example.com"], ["test@example.com",
+            {"string": "textInputType_email"}, {"string": "entryAbbr"}]]
         }, {
           accOrElmOrID: "search",
-          expectedUtterance: [
-            ["search entry", "This is a search"],
-            ["This is a search", "search entry"]
-          ],
-          expectedBraille: [
-            ["search entry", "This is a search"],
-            ["This is a search", "search entry"]
-          ]
+          expectedUtterance: [[{"string": "textInputType_search"},
+            {"string": "entry"}, "This is a search"], ["This is a search",
+            {"string": "textInputType_search"}, {"string": "entry"}]],
+          expectedBraille: [[{"string": "textInputType_search"},
+            {"string": "entryAbbr"}, "This is a search"], ["This is a search",
+            {"string": "textInputType_search"}, {"string": "entryAbbr"}]]
         }, {
           accOrElmOrID: "tel",
-          expectedUtterance: [
-            ["telephone entry", "555-5555"], ["555-5555", "telephone entry"]
-          ],
-          expectedBraille: [
-            ["telephone entry", "555-5555"], ["555-5555", "telephone entry"]
-          ]
+          expectedUtterance: [[{"string": "textInputType_tel"},
+            {"string": "entry"}, "555-5555"], ["555-5555",
+            {"string": "textInputType_tel"}, {"string": "entry"}]],
+          expectedBraille: [[{"string": "textInputType_tel"},
+            {"string": "entryAbbr"}, "555-5555"], ["555-5555",
+            {"string": "textInputType_tel"}, {"string": "entryAbbr"}]]
         }, {
           accOrElmOrID: "url",
-          expectedUtterance: [
-            ["URL entry", "http://example.com"],
-            ["http://example.com", "URL entry"]
-          ],
-          expectedBraille: [
-            ["URL entry", "http://example.com"],
-            ["http://example.com", "URL entry"]
-          ]
+          expectedUtterance: [[{"string": "textInputType_url"},
+            {"string": "entry"}, "http://example.com"], ["http://example.com",
+            {"string": "textInputType_url"}, {"string": "entry"}]],
+          expectedBraille: [[{"string": "textInputType_url"},
+            {"string": "entryAbbr"}, "http://example.com"],
+            ["http://example.com", {"string": "textInputType_url"},
+            {"string": "entryAbbr"}]]
         }, {
           accOrElmOrID: "textInput",
-          expectedUtterance: [["entry", "This is text."], ["This is text.", "entry"]],
-          expectedBraille: [["entry", "This is text."], ["This is text.", "entry"]]
+          expectedUtterance: [[{"string": "entry"}, "This is text."],
+                              ["This is text.", {"string": "entry"}]],
+          expectedBraille: [[{"string": "entryAbbr"}, "This is text."],
+                            ["This is text.", {"string": "entryAbbr"}]]
         }, {
           // Test pivot to list from li_one.
           accOrElmOrID: "list",
           oldAccOrElmOrID: "li_one",
-          expectedUtterance: [
-            ["list 1 item", "First item", "1.", "list one"],
-            ["1.", "list one", "First item", "list 1 item"]
+          expectedUtterance: [[{"string": "list"},
+            {"string": "listItemsCount", "count": 1}, {"string": "listStart"},
+            "1.", "list one"], ["1.", "list one", {"string": "listStart"},
+            {"string": "list"}, {"string": "listItemsCount", "count": 1}]
           ],
-          expectedBraille: [
-            ["list", "list one"],
-            ["list one", "list"]
-          ]
+          expectedBraille: [[{"string": "listAbbr"}, "list one"],
+                            ["list one", {"string": "listAbbr"}]]
         }, {
           // Test pivot to "apples" link from the table cell.
           accOrElmOrID: "apples",
           oldAccOrElmOrID: "cell",
-          expectedUtterance: [
-            ["list 4 items", "First item", "link", "Apples"],
-            ["Apples", "link", "First item", "list 4 items"]
+          expectedUtterance: [[{"string": "list"},
+            {"string": "listItemsCount", "count": 4}, {"string": "listStart"},
+            {"string": "link"}, "Apples"], ["Apples", {"string": "link"},
+            {"string": "listStart"}, {"string": "list"},
+            {"string": "listItemsCount", "count": 4}]
           ],
-          expectedBraille: [
-            ["*", "lnk", "Apples"],
-            ["*", "Apples", "lnk"]
-          ]
+          expectedBraille: [["*", {"string": "linkAbbr"}, "Apples"],
+                            ["*", "Apples", {"string": "linkAbbr"}]]
         }, {
-          // Test pivot to 'bananas' link from 'apples' link.
+          // Test pivot to "bananas" link from "apples" link.
           accOrElmOrID: "bananas",
           oldAccOrElmOrID: "apples",
-          expectedUtterance: [["link", "Bananas"], ["Bananas", "link"]],
-          expectedBraille: [["*", "lnk", "Bananas"], ["*", "Bananas", "lnk"]]
+          expectedUtterance: [[{"string": "link"}, "Bananas"],
+                              ["Bananas", {"string": "link"}]],
+          expectedBraille: [["*", {"string": "linkAbbr"}, "Bananas"],
+                            ["*", "Bananas", {"string": "linkAbbr"}]]
         }, {
           // test unavailable state utterance
-          accOrElmOrID: 'unavailableButton',
-          expectedUtterance: [["unavailable button", "I am unavailable"],
-            ["I am unavailable", "unavailable button"]],
-          expectedBraille: [["btn", "I am unavailable"],
-            ["I am unavailable", "btn"]]
+          accOrElmOrID: "unavailableButton",
+          expectedUtterance: [[{"string": "stateUnavailable"},
+            {"string": "pushbutton"}, "I am unavailable"], ["I am unavailable",
+            {"string": "stateUnavailable"}, {"string": "pushbutton"}]],
+          expectedBraille: [[{"string": "pushbuttonAbbr"}, "I am unavailable"],
+                            ["I am unavailable", {"string": "pushbuttonAbbr"}]]
         }, {
           // test expanded state utterance
-          accOrElmOrID: 'expandedButton',
-          expectedUtterance: [["expanded button", "I am expanded"],
-            ["I am expanded", "expanded button"]],
-          expectedBraille: [["btn", "I am expanded"],
-            ["I am expanded", "btn"]]
+          accOrElmOrID: "expandedButton",
+          expectedUtterance: [[{"string": "stateExpanded"},
+            {"string": "pushbutton"}, "I am expanded"], ["I am expanded",
+            {"string": "stateExpanded"}, {"string": "pushbutton"}]],
+          expectedBraille: [[{"string": "pushbuttonAbbr"}, "I am expanded"],
+                            ["I am expanded", {"string": "pushbuttonAbbr"}]]
         }, {
           // test collapsed state utterance
-          accOrElmOrID: 'collapsedButton',
-          expectedUtterance: [["collapsed button", "I am collapsed"],
-            ["I am collapsed", "collapsed button"]],
-          expectedBraille: [["btn", "I am collapsed"],
-            ["I am collapsed", "btn"]]
+          accOrElmOrID: "collapsedButton",
+          expectedUtterance: [[{"string": "stateCollapsed"},
+            {"string": "pushbutton"}, "I am collapsed"], ["I am collapsed",
+            {"string": "stateCollapsed"}, {"string": "pushbutton"}]],
+          expectedBraille: [[{"string": "pushbuttonAbbr"}, "I am collapsed"],
+                            ["I am collapsed", {"string": "pushbuttonAbbr"}]]
         }, {
           // test required state utterance
-          accOrElmOrID: 'requiredInput',
-          expectedUtterance: [["required entry", "I am required"],
-            ["I am required", "required entry"]],
-          expectedBraille: [["entry", "I am required"],
-            ["I am required", "entry"]]
+          accOrElmOrID: "requiredInput",
+          expectedUtterance: [[{"string": "stateRequired"}, {"string": "entry"},
+            "I am required"], ["I am required", {"string": "stateRequired"},
+            {"string": "entry"}]],
+          expectedBraille: [[{"string": "entryAbbr"}, "I am required"],
+                            ["I am required", {"string": "entryAbbr"}]]
         }, {
           // test has popup state utterance
-          accOrElmOrID: 'hasPopupButton',
-          expectedUtterance: [["has pop up button menu", "I have a popup"],
-            ["I have a popup", "has pop up button menu"]],
-          expectedBraille: [["button menu", "I have a popup"],
-            ["I have a popup", "button menu"]]
+          accOrElmOrID: "hasPopupButton",
+          expectedUtterance: [[{"string": "stateHasPopup"},
+            {"string": "buttonmenu"}, "I have a popup"], ["I have a popup",
+            {"string": "stateHasPopup"}, {"string": "buttonmenu"}]],
+          expectedBraille: [[{"string": "buttonmenuAbbr"}, "I have a popup"],
+                            ["I have a popup", {"string": "buttonmenuAbbr"}]]
         }, {
           // Test selected tab
-          accOrElmOrID: 'tab1',
-          expectedUtterance: [['tab list', 'selected tab 1 of 2', 'Account'],
-            ['Account', 'selected tab 1 of 2', 'tab list']],
-          expectedBraille: [['tab 1 of 2', 'Account'],
-            ['Account', 'tab 1 of 2']]
+          accOrElmOrID: "tab1",
+          expectedUtterance: [[{"string": "pagetablist"},
+            {"string": "stateSelected"}, {"string": "pagetab"},
+            {"string": "objItemOfN", "args": [1, 2]}, "Account"], ["Account",
+            {"string": "stateSelected"}, {"string": "pagetab"},
+            {"string": "objItemOfN", "args": [1, 2]}, {"string": "pagetablist"}]
+          ],
+          expectedBraille: [[{"string": "pagetabAbbr"},
+            {"string": "objItemOfN", "args": [1, 2]}, "Account"], ["Account",
+            {"string": "pagetabAbbr"},
+            {"string": "objItemOfN", "args": [1, 2]}]]
         }, {
           // Test unselected tab
-          accOrElmOrID: 'tab2',
-          expectedUtterance: [['tab list', 'tab 2 of 2', 'Advanced'],
-            ['Advanced', 'tab 2 of 2', 'tab list']],
-          expectedBraille: [['tab 2 of 2', 'Advanced'],
-            ['Advanced', 'tab 2 of 2']]
-        },
-
-        {
+          accOrElmOrID: "tab2",
+          expectedUtterance: [[{"string": "pagetablist"}, {"string": "pagetab"},
+            {"string": "objItemOfN", "args": [2, 2]}, "Advanced"], ["Advanced",
+            {"string": "pagetab"}, {"string": "objItemOfN", "args": [2, 2]},
+            {"string": "pagetablist"}]],
+          expectedBraille: [[{"string": "pagetabAbbr"},
+            {"string": "objItemOfN", "args": [2, 2]}, "Advanced"], ["Advanced",
+            {"string": "pagetabAbbr"},
+            {"string": "objItemOfN", "args": [2, 2]}]]
+        }, {
           // Landing on this label should mimic landing on the checkbox.
           accOrElmOrID: "label1",
-          expectedUtterance: [['not checked check button', 'Orange'],
-                     ['Orange', 'not checked check button']],
-          expectedBraille: [['( )', 'Orange'],
-                     ['Orange', '( )']]
-        },
-        {
+          expectedUtterance: [[{"string": "stateNotChecked"},
+            {"string": "checkbutton"}, "Orange"], ["Orange",
+            {"string": "stateNotChecked"}, {"string": "checkbutton"}]],
+          expectedBraille: [[{"string": "stateUncheckedAbbr"}, "Orange"],
+                            ["Orange", {"string": "stateUncheckedAbbr"}]]
+        }, {
           // Here we get a top-level view of the form.
           accOrElmOrID: "form1",
-          expectedUtterance: [['label', 'not checked check button', 'Orange', 'Orange',
-                      'not checked check button', 'Blue', 'label', 'Blue'],
-                     ['Orange', 'not checked check button', 'Orange', 'label',
-                      'Blue', 'not checked check button', 'Blue', 'label']],
-          expectedBraille: [['label', '( )', 'Orange', 'Orange',
-                      '( )', 'Blue', 'label', 'Blue'],
-                     ['Orange', '( )', 'Orange', 'label',
-                      'Blue', '( )', 'Blue', 'label']]
-        },
-        {
+          expectedUtterance: [[{"string": "label"},
+            {"string": "stateNotChecked"}, {"string": "checkbutton"}, "Orange",
+            "Orange", {"string": "stateNotChecked"}, {"string": "checkbutton"},
+            "Blue", {"string": "label"}, "Blue"], ["Orange",
+            {"string": "stateNotChecked"}, {"string": "checkbutton"}, "Orange",
+            {"string": "label"}, "Blue", {"string": "stateNotChecked"},
+            {"string": "checkbutton"}, "Blue", {"string": "label"}]],
+          expectedBraille: [[{"string": "labelAbbr"},
+            {"string": "stateUncheckedAbbr"}, "Orange", "Orange",
+            {"string": "stateUncheckedAbbr"}, "Blue", {"string": "labelAbbr"},
+            "Blue"], ["Orange", {"string": "stateUncheckedAbbr"}, "Orange",
+            {"string": "labelAbbr"}, "Blue", {"string": "stateUncheckedAbbr"},
+            "Blue", {"string": "labelAbbr"}]]
+        }, {
           // This is a non-nesting label.
           accOrElmOrID: "label2",
-          expectedUtterance: [['label', 'Blue'], ['Blue', 'label']],
-          expectedBraille: [['label', 'Blue'], ['Blue', 'label']]
-        },
-        {
+          expectedUtterance: [[{"string": "label"}, "Blue"],
+                              ["Blue", {"string": "label"}]],
+          expectedBraille: [[{"string": "labelAbbr"}, "Blue"],
+                            ["Blue", {"string": "labelAbbr"}]]
+        }, {
           // This is a distinct control.
           accOrElmOrID: "input2",
-          expectedUtterance: [['not checked check button', 'Blue'],
-                     ['Blue', 'not checked check button']],
-          expectedBraille: [['( )', 'Blue'],
-                     ['Blue', '( )']]
-        },
-        {
+          expectedUtterance: [[{"string": "stateNotChecked"},
+            {"string": "checkbutton"}, "Blue"], ["Blue",
+            {"string": "stateNotChecked"}, {"string": "checkbutton"}]],
+          expectedBraille: [[{"string": "stateUncheckedAbbr"}, "Blue"], ["Blue",
+            {"string": "stateUncheckedAbbr"}]]
+        }, {
           // This is a nested control.
           accOrElmOrID: "input1",
-          expectedUtterance: [['not checked check button', 'Orange'],
-                     ['Orange', 'not checked check button']],
-          expectedBraille: [['( )', 'Orange'],
-                     ['Orange', '( )']]
-        },
-        {
+          expectedUtterance: [[{"string": "stateNotChecked"},
+            {"string": "checkbutton"}, "Orange"], ["Orange",
+            {"string": "stateNotChecked"}, {"string": "checkbutton"}]],
+          expectedBraille: [[{"string": "stateUncheckedAbbr"}, "Orange"],
+            ["Orange", {"string": "stateUncheckedAbbr"}]]
+        }, {
           // Landing on this label should mimic landing on the entry.
           accOrElmOrID: "label3",
-          expectedUtterance: [['entry', 'Joe', 'First name:'],
-                     ['First name:', 'Joe', 'entry']],
-          expectedBraille: [['entry', 'Joe', 'First name:'],
-                     ['First name:', 'Joe', 'entry']]
-        },
-        {
+          expectedUtterance: [[{"string": "entry"}, "Joe", "First name:"],
+                             ["First name:", "Joe", {"string": "entry"}]],
+          expectedBraille: [[{"string": "entryAbbr"}, "Joe", "First name:"],
+                            ["First name:", "Joe", {"string": "entryAbbr"}]]
+        }, {
           // This is a nested control with a value.
           accOrElmOrID: "input3",
-          expectedUtterance: [['entry', 'Joe', 'First name:'],
-                     ['First name:', 'Joe', 'entry']],
-          expectedBraille: [['entry', 'Joe', 'First name:'],
-                     ['First name:', 'Joe', 'entry']]
-        },
-        {
+          expectedUtterance: [[{"string": "entry"}, "Joe", "First name:"],
+                              ["First name:", "Joe", {"string": "entry"}]],
+          expectedBraille: [[{"string": "entryAbbr"}, "Joe", "First name:"],
+                            ["First name:", "Joe", {"string": "entryAbbr"}]]
+        }, {
           // This is a nested control with a value.
           accOrElmOrID: "input4",
-          expectedUtterance: [['slider', '3', 'Points:'],
-                     ['Points:', '3', 'slider']],
-          expectedBraille: [['slider', '3', 'Points:'],
-                     ['Points:', '3', 'slider']]
-        },{
+          expectedUtterance: [[{"string": "slider"}, "3", "Points:"],
+                              ["Points:", "3", {"string": "slider"}]],
+          expectedBraille: [[{"string": "sliderAbbr"}, "3", "Points:"],
+                            ["Points:", "3", {"string": "sliderAbbr"}]]
+        }, {
           accOrElmOrID: "password",
-          expectedUtterance: [["password text", "Secret Password"],
-                              ["Secret Password", "password text"]],
-          expectedBraille: [["passwdtxt", "Secret Password"],
-                            ["Secret Password", "passwdtxt"]]
-        },{
+          expectedUtterance: [[{"string": "passwordtext"}, "Secret Password"],
+                              ["Secret Password", {"string": "passwordtext"}]],
+          expectedBraille: [[{"string": "passwordtextAbbr"}, "Secret Password"],
+                            ["Secret Password", {"string": "passwordtextAbbr"}]]
+        }, {
           accOrElmOrID: "input5",
-          expectedUtterance: [["checked check button", "Boring label"],
-                              ["Boring label", "checked check button"]],
-          expectedBraille: [["(x)", "Boring label"],
-                            ["Boring label", "(x)"]]
-        },{
+          expectedUtterance: [[{"string": "stateChecked"},
+            {"string": "checkbutton"}, "Boring label"], ["Boring label",
+            {"string": "stateChecked"}, {"string": "checkbutton"}]],
+          expectedBraille: [[{"string": "stateCheckedAbbr"}, "Boring label"],
+            ["Boring label", {"string": "stateCheckedAbbr"}]]
+        }, {
           accOrElmOrID: "radio_unselected",
-          expectedUtterance: [["not checked radio button", "any old radio button"],
-                              ["any old radio button", "not checked radio button"]],
-          expectedBraille: [["( )", "any old radio button"],
-                            ["any old radio button", "( )"]]
-        },{
+          expectedUtterance: [[{"string": "stateNotChecked"},
+            {"string": "radiobutton"}, "any old radio button"],
+            ["any old radio button", {"string": "stateNotChecked"},
+            {"string": "radiobutton"}]
+          ],
+          expectedBraille: [
+            [{"string": "stateUncheckedAbbr"}, "any old radio button"],
+            ["any old radio button", {"string": "stateUncheckedAbbr"}]]
+        }, {
           accOrElmOrID: "radio_selected",
-          expectedUtterance: [["checked radio button", "a unique radio button"],
-                              ["a unique radio button", "checked radio button"]],
-          expectedBraille: [["(x)", "a unique radio button"],
-                            ["a unique radio button", "(x)"]]
-        },{
+          expectedUtterance: [[{"string": "stateChecked"},
+            {"string": "radiobutton"}, "a unique radio button"],
+            ["a unique radio button", {"string": "stateChecked"},
+            {"string": "radiobutton"}]],
+          expectedBraille: [
+            [{"string": "stateCheckedAbbr"}, "a unique radio button"],
+            ["a unique radio button", {"string": "stateCheckedAbbr"}]]
+        }, {
           accOrElmOrID: "togglebutton_notpressed",
-          expectedUtterance: [["toggle button", "I ain't pressed"],
-                              ["I ain't pressed", "toggle button"]],
-          expectedBraille: [["( )", "I ain't pressed"],
-                            ["I ain't pressed", "( )"]]
-        },{
+          expectedUtterance: [[{"string": "togglebutton"}, "I am not pressed"],
+                              ["I am not pressed", {"string": "togglebutton"}]],
+          expectedBraille: [
+            [{"string": "stateUnpressedAbbr"}, "I am not pressed"],
+            ["I am not pressed", {"string": "stateUnpressedAbbr"}]]
+        }, {
           accOrElmOrID: "togglebutton_pressed",
-          expectedUtterance: [["pressed toggle button", "I am pressed!"],
-                              ["I am pressed!", "pressed toggle button"]],
-          expectedBraille: [["(x)", "I am pressed!"],
-                            ["I am pressed!", "(x)"]]
+          expectedUtterance: [[{"string": "statePressed"},
+            {"string": "togglebutton"}, "I am pressed!"], ["I am pressed!",
+            {"string": "statePressed"}, {"string": "togglebutton"}]],
+          expectedBraille: [[{"string": "statePressedAbbr"}, "I am pressed!"],
+                            ["I am pressed!", {"string": "statePressedAbbr"}]]
         }, {
           accOrElmOrID: "listbox-option",
-          expectedUtterance: [["list box", "option", "Search suggestion"],
-            ["Search suggestion", "option", "list box"]],
-          expectedBraille: [["option", "Search suggestion"],
-            ["Search suggestion", "option"]]
+          expectedUtterance: [[{"string": "listbox"},
+            {"string": "listboxoption"}, "Search suggestion"],
+            ["Search suggestion", {"string": "listboxoption"},
+            {"string": "listbox"}]
+          ],
+          expectedBraille: [
+            [{"string": "listboxoptionAbbr"}, "Search suggestion"],
+            ["Search suggestion", {"string": "listboxoptionAbbr"}]]
         }];
 
         // Test all possible utterance order preference values.
         tests.forEach(function run(test) {
           var utteranceOrderValues = [0, 1];
           utteranceOrderValues.forEach(
             function testUtteranceOrder(utteranceOrder) {
               SpecialPowers.setIntPref(PREF_UTTERANCE_ORDER, utteranceOrder);
@@ -466,16 +498,16 @@ https://bugzilla.mozilla.org/show_bug.cg
       <label for="radio_selected">a unique radio button</label><input id="radio_selected" type="radio" checked></input>
       <input id="date" type="date" value="2011-09-29" />
       <input id="email" type="email" value="test@example.com" />
       <input id="search" type="search" value="This is a search" />
       <input id="tel" type="tel" value="555-5555" />
       <input id="url" type="url" value="http://example.com" />
       <input id="textInput" type="text" value="This is text." />
       <label>Points: <input id="range" type="range" name="points" min="1" max="10" value="3"></label>
-      <div id="togglebutton_notpressed" aria-pressed="false" role="button" tabindex="-1">I ain't pressed</div>
+      <div id="togglebutton_notpressed" aria-pressed="false" role="button" tabindex="-1">I am not pressed</div>
       <div id="togglebutton_pressed" aria-pressed="true" role="button" tabindex="-1">I am pressed!</div>
       <ul role="listbox" style="list-style-type: none;">
         <li role="option" id="listbox-option">Search suggestion</li>
       </ul>
     </div>
   </body>
 </html>
--- a/accessible/tests/mochitest/jsat/test_tables.html
+++ b/accessible/tests/mochitest/jsat/test_tables.html
@@ -11,194 +11,470 @@
   <script type="application/javascript"
           src="output.js"></script>
   <script type="application/javascript">
 
     function doTest() {
       // Test the following accOrElmOrID.
       var tests = [{
         accOrElmOrID: "table1",
-        expectedUtterance: [["table with 2 columns and 2 rows",
-          "Column 1 Row 1", "col1", "Column 2 Row 1", "col2",
-          "Column 1 Row 2 col1", "cell1", "Column 2 Row 2 col2", "cell2"], [
-          "col1", "Column 1 Row 1", "col2", "Column 2 Row 1", "cell1",
-          "Column 1 Row 2 col1", "cell2", "Column 2 Row 2 col2",
-          "table with 2 columns and 2 rows"]],
-        expectedBraille: [["tbl 2c 2r", "c1r1", "col1", "c2r1", "col2",
-          "c1r2 col1", "cell1", "c2r2 col2", "cell2"], ["col1", "c1r1", "col2",
-          "c2r1", "cell1", "c1r2 col1", "cell2", "c2r2 col2", "tbl 2c 2r"]]
+        expectedUtterance: [[
+          {"string": "table"},
+          {"string": "tblColumnInfo", "count": 2},
+          {"string": "tblRowInfo", "count": 2},
+          {"string": "columnInfo", "args": [1]},
+          {"string": "rowInfo", "args": [1]}, "col1",
+          {"string": "columnInfo", "args": [2]},
+          {"string": "rowInfo", "args": [1]}, "col2",
+          {"string": "columnInfo", "args": [1]},
+          {"string": "rowInfo", "args": [2]}, "col1", "cell1",
+          {"string": "columnInfo", "args": [2]},
+          {"string": "rowInfo", "args": [2]}, "col2", "cell2"], ["col1",
+          {"string": "columnInfo", "args": [1]},
+          {"string": "rowInfo", "args": [1]}, "col2",
+          {"string": "columnInfo", "args": [2]},
+          {"string": "rowInfo", "args": [1]}, "cell1",
+          {"string": "columnInfo", "args": [1]},
+          {"string": "rowInfo", "args": [2]}, "col1", "cell2",
+          {"string": "columnInfo", "args": [2]},
+          {"string": "rowInfo", "args": [2]}, "col2", {"string": "table"},
+          {"string": "tblColumnInfo", "count": 2},
+          {"string": "tblRowInfo", "count": 2}]],
+        expectedBraille: [[
+          {"string": "tableAbbr"},
+          {"string": "tblColumnInfoAbbr", "count": 2},
+          {"string": "tblRowInfoAbbr", "count": 2},
+          {"string": "cellInfoAbbr", "args": [1, 1]}, "col1",
+          {"string": "cellInfoAbbr", "args": [2, 1]}, "col2",
+          {"string": "cellInfoAbbr", "args": [1, 2]}, "col1", "cell1",
+          {"string": "cellInfoAbbr", "args": [2, 2]}, "col2", "cell2"], ["col1",
+          {"string": "cellInfoAbbr", "args": [1, 1]}, "col2",
+          {"string": "cellInfoAbbr", "args": [2, 1]}, "cell1",
+          {"string": "cellInfoAbbr", "args": [1, 2]}, "col1", "cell2",
+          {"string": "cellInfoAbbr", "args": [2, 2]}, "col2",
+          {"string": "tableAbbr"},
+          {"string": "tblColumnInfoAbbr", "count": 2},
+          {"string": "tblRowInfoAbbr", "count": 2}]]
       }, {
         accOrElmOrID: "table2",
-        expectedUtterance: [["table with 2 columns and 2 rows",
-          "Column 1 Row 1 col1", "cell1", "Column 2 Row 1 col2",
-          "table with 1 column and 2 rows", "Column 1 Row 1", "colheader",
-          "Column 1 Row 2 colheader", "bla", "Column 1 Row 2", "col1",
-          "Column 2 Row 2", "col2"], ["cell1", "Column 1 Row 1 col1",
-          "colheader", "Column 1 Row 1", "bla", "Column 1 Row 2 colheader",
-          "table with 1 column and 2 rows", "Column 2 Row 1 col2", "col1",
-          "Column 1 Row 2", "col2", "Column 2 Row 2",
-          "table with 2 columns and 2 rows"]],
-        expectedBraille: [["tbl 2c 2r", "c1r1 col1", "cell1", "c2r1 col2",
-          "tbl 1c 2r", "c1r1", "colheader", "c1r2 colheader", "bla", "c1r2",
-          "col1", "c2r2", "col2"], ["cell1", "c1r1 col1", "colheader", "c1r1",
-          "bla", "c1r2 colheader", "tbl 1c 2r", "c2r1 col2", "col1", "c1r2",
-          "col2", "c2r2", "tbl 2c 2r"]]
+        expectedUtterance: [[
+          {"string": "table"},
+          {"string": "tblColumnInfo", "count": 2},
+          {"string": "tblRowInfo", "count": 2},
+          {"string": "columnInfo", "args": [1]},
+          {"string": "rowInfo", "args": [1]}, "col1", "cell1",
+          {"string": "columnInfo", "args": [2]},
+          {"string": "rowInfo", "args": [1]}, "col2",
+          {"string": "table"},
+          {"string": "tblColumnInfo", "count": 1},
+          {"string": "tblRowInfo", "count": 2},
+          {"string": "columnInfo", "args": [1]},
+          {"string": "rowInfo", "args": [1]}, "colheader",
+          {"string": "columnInfo", "args": [1]},
+          {"string": "rowInfo", "args": [2]}, "colheader", "bla",
+          {"string": "columnInfo", "args": [1]},
+          {"string": "rowInfo", "args": [2]}, "col1",
+          {"string": "columnInfo", "args": [2]},
+          {"string": "rowInfo", "args": [2]}, "col2"], ["cell1",
+          {"string": "columnInfo", "args": [1]},
+          {"string": "rowInfo", "args": [1]}, "col1", "colheader",
+          {"string": "columnInfo", "args": [1]},
+          {"string": "rowInfo", "args": [1]}, "bla",
+          {"string": "columnInfo", "args": [1]},
+          {"string": "rowInfo", "args": [2]}, "colheader",
+          {"string": "table"},
+          {"string": "tblColumnInfo", "count": 1},
+          {"string": "tblRowInfo", "count": 2},
+          {"string": "columnInfo", "args": [2]},
+          {"string": "rowInfo", "args": [1]}, "col2", "col1",
+          {"string": "columnInfo", "args": [1]},
+          {"string": "rowInfo", "args": [2]}, "col2",
+          {"string": "columnInfo", "args": [2]},
+          {"string": "rowInfo", "args": [2]}, {"string": "table"},
+          {"string": "tblColumnInfo", "count": 2},
+          {"string": "tblRowInfo", "count": 2}]],
+        expectedBraille: [[{"string": "tableAbbr"},
+          {"string": "tblColumnInfoAbbr", "count": 2},
+          {"string": "tblRowInfoAbbr", "count": 2},
+          {"string": "cellInfoAbbr", "args": [1, 1]}, "col1", "cell1",
+          {"string": "cellInfoAbbr", "args": [2, 1]}, "col2",
+          {"string": "tableAbbr"},
+          {"string": "tblColumnInfoAbbr", "count": 1},
+          {"string": "tblRowInfoAbbr", "count": 2},
+          {"string": "cellInfoAbbr", "args": [1, 1]}, "colheader",
+          {"string": "cellInfoAbbr", "args": [1, 2]}, "colheader", "bla",
+          {"string": "cellInfoAbbr", "args": [1, 2]}, "col1",
+          {"string": "cellInfoAbbr", "args": [2, 2]}, "col2"], ["cell1",
+          {"string": "cellInfoAbbr", "args": [1, 1]}, "col1", "colheader",
+          {"string": "cellInfoAbbr", "args": [1, 1]}, "bla",
+          {"string": "cellInfoAbbr", "args": [1, 2]}, "colheader",
+          {"string": "tableAbbr"},
+          {"string": "tblColumnInfoAbbr", "count": 1},
+          {"string": "tblRowInfoAbbr", "count": 2},
+          {"string": "cellInfoAbbr", "args": [2, 1]}, "col2", "col1",
+          {"string": "cellInfoAbbr", "args": [1, 2]}, "col2",
+          {"string": "cellInfoAbbr", "args": [2, 2]},
+          {"string": "tableAbbr"},
+          {"string": "tblColumnInfoAbbr", "count": 2},
+          {"string": "tblRowInfoAbbr", "count": 2}]]
       }, {
         accOrElmOrID: "table3",
-        expectedUtterance: [["table with 2 columns and 2 rows",
-          "Column 2 Row 1 col2", "table with 1 column and 2 rows",
-          "Column 1 Row 1", "colheader", "Column 1 Row 2 colheader", "bla"], [
-          "colheader", "Column 1 Row 1", "bla", "Column 1 Row 2 colheader",
-          "table with 1 column and 2 rows", "Column 2 Row 1 col2",
-          "table with 2 columns and 2 rows"]],
-        expectedBraille: [["tbl 1c 2r", "c1r1", "colheader", "c1r2 colheader",
-          "bla"], ["colheader", "c1r1", "bla", "c1r2 colheader", "tbl 1c 2r"]]
+        expectedUtterance: [[
+          {"string": "table"},
+          {"string": "tblColumnInfo", "count": 2},
+          {"string": "tblRowInfo", "count": 2},
+          {"string": "columnInfo", "args": [2]},
+          {"string": "rowInfo", "args": [1]}, "col2",
+          {"string": "table"},
+          {"string": "tblColumnInfo", "count": 1},
+          {"string": "tblRowInfo", "count": 2},
+          {"string": "columnInfo", "args": [1]},
+          {"string": "rowInfo", "args": [1]}, "colheader",
+          {"string": "columnInfo", "args": [1]},
+          {"string": "rowInfo", "args": [2]}, "colheader", "bla"], ["colheader",
+          {"string": "columnInfo", "args": [1]},
+          {"string": "rowInfo", "args": [1]}, "bla",
+          {"string": "columnInfo", "args": [1]},
+          {"string": "rowInfo", "args": [2]}, "colheader",
+          {"string": "table"},
+          {"string": "tblColumnInfo", "count": 1},
+          {"string": "tblRowInfo", "count": 2},
+          {"string": "columnInfo", "args": [2]},
+          {"string": "rowInfo", "args": [1]}, "col2",
+          {"string": "table"},
+          {"string": "tblColumnInfo", "count": 2},
+          {"string": "tblRowInfo", "count": 2}]],
+        expectedBraille: [[
+          {"string": "tableAbbr"},
+          {"string": "tblColumnInfoAbbr", "count": 1},
+          {"string": "tblRowInfoAbbr", "count": 2},
+          {"string": "cellInfoAbbr", "args": [1, 1]}, "colheader",
+          {"string": "cellInfoAbbr", "args": [1, 2]}, "colheader", "bla"],
+          ["colheader",
+          {"string": "cellInfoAbbr", "args": [1, 1]}, "bla",
+          {"string": "cellInfoAbbr", "args": [1, 2]}, "colheader",
+          {"string": "tableAbbr"},
+          {"string": "tblColumnInfoAbbr", "count": 1},
+          {"string": "tblRowInfoAbbr", "count": 2}]]
       }, {
         accOrElmOrID: "table4",
-        expectedUtterance: [["table with 4 columns and 3 rows",
-          "Column 1 Row 1", "col1", "Column 2 Row 1", "col2", "Column 3 Row 1",
-          "col3", "Column 1 Row 2 spans 2 columns col1", "row1",
-          "Column 3 Row 2 col3 row1", "cell1",
-          "Column 4 Row 2 spans 2 rows row1", "cell2", "Column 1 Row 3 col1",
-          "row2", "Column 2 Row 3 col2 row2", "cell3",
-          "Column 3 Row 3 col3 row2", "cell4"], ["col1", "Column 1 Row 1",
-          "col2", "Column 2 Row 1", "col3", "Column 3 Row 1", "row1",
-          "Column 1 Row 2 spans 2 columns col1", "cell1",
-          "Column 3 Row 2 col3 row1", "cell2",
-          "Column 4 Row 2 spans 2 rows row1", "row2", "Column 1 Row 3 col1",
-          "cell3", "Column 2 Row 3 col2 row2", "cell4",
-          "Column 3 Row 3 col3 row2", "table with 4 columns and 3 rows"]],
-        expectedBraille: [["tbl 4c 3r", "c1r1", "col1", "c2r1", "col2", "c3r1",
-          "col3", "c1r2 col1", "row1", "c3r2 col3 row1", "cell1", "c4r2 row1",
-          "cell2", "c1r3 col1", "row2", "c2r3 col2 row2", "cell3",
-          "c3r3 col3 row2", "cell4"], ["col1", "c1r1", "col2", "c2r1", "col3",
-          "c3r1", "row1", "c1r2 col1", "cell1", "c3r2 col3 row1", "cell2",
-          "c4r2 row1", "row2", "c1r3 col1", "cell3", "c2r3 col2 row2", "cell4",
-          "c3r3 col3 row2", "tbl 4c 3r"]]
+        expectedUtterance: [[
+          {"string": "table"},
+          {"string": "tblColumnInfo", "count": 4},
+          {"string": "tblRowInfo", "count": 3},
+          {"string": "columnInfo", "args": [1]},
+          {"string": "rowInfo", "args": [1]}, "col1",
+          {"string": "columnInfo", "args": [2]},
+          {"string": "rowInfo", "args": [1]}, "col2",
+          {"string": "columnInfo", "args": [3]},
+          {"string": "rowInfo", "args": [1]}, "col3",
+          {"string": "columnInfo", "args": [1]},
+          {"string": "rowInfo", "args": [2]},
+          {"string": "spansColumns", "args": [2]}, "col1", "row1",
+          {"string": "columnInfo", "args": [3]},
+          {"string": "rowInfo", "args": [2]}, "col3", "row1", "cell1",
+          {"string": "columnInfo", "args": [4]},
+          {"string": "rowInfo", "args": [2]},
+          {"string": "spansRows", "args": [2]}, "row1", "cell2",
+          {"string": "columnInfo", "args": [1]},
+          {"string": "rowInfo", "args": [3]}, "col1", "row2",
+          {"string": "columnInfo", "args": [2]},
+          {"string": "rowInfo", "args": [3]}, "col2", "row2", "cell3",
+          {"string": "columnInfo", "args": [3]},
+          {"string": "rowInfo", "args": [3]}, "col3", "row2", "cell4"], ["col1",
+          {"string": "columnInfo", "args": [1]},
+          {"string": "rowInfo", "args": [1]}, "col2",
+          {"string": "columnInfo", "args": [2]},
+          {"string": "rowInfo", "args": [1]}, "col3",
+          {"string": "columnInfo", "args": [3]},
+          {"string": "rowInfo", "args": [1]}, "row1",
+          {"string": "columnInfo", "args": [1]},
+          {"string": "rowInfo", "args": [2]},
+          {"string": "spansColumns", "args": [2]}, "col1", "cell1",
+          {"string": "columnInfo", "args": [3]},
+          {"string": "rowInfo", "args": [2]}, "col3", "row1", "cell2",
+          {"string": "columnInfo", "args": [4]},
+          {"string": "rowInfo", "args": [2]},
+          {"string": "spansRows", "args": [2]}, "row1", "row2",
+          {"string": "columnInfo", "args": [1]},
+          {"string": "rowInfo", "args": [3]}, "col1", "cell3",
+          {"string": "columnInfo", "args": [2]},
+          {"string": "rowInfo", "args": [3]}, "col2", "row2", "cell4",
+          {"string": "columnInfo", "args": [3]},
+          {"string": "rowInfo", "args": [3]}, "col3", "row2",
+          {"string": "table"},
+          {"string": "tblColumnInfo", "count": 4},
+          {"string": "tblRowInfo", "count": 3}]],
+        expectedBraille: [[
+          {"string": "tableAbbr"},
+          {"string": "tblColumnInfoAbbr", "count": 4},
+          {"string": "tblRowInfoAbbr", "count": 3},
+          {"string": "cellInfoAbbr", "args": [1, 1]}, "col1",
+          {"string": "cellInfoAbbr", "args": [2, 1]}, "col2",
+          {"string": "cellInfoAbbr", "args": [3, 1]}, "col3",
+          {"string": "cellInfoAbbr", "args": [1, 2]}, "col1", "row1",
+          {"string": "cellInfoAbbr", "args": [3, 2]}, "col3", "row1", "cell1",
+          {"string": "cellInfoAbbr", "args": [4, 2]}, "row1", "cell2",
+          {"string": "cellInfoAbbr", "args": [1, 3]}, "col1", "row2",
+          {"string": "cellInfoAbbr", "args": [2, 3]}, "col2", "row2", "cell3",
+          {"string": "cellInfoAbbr", "args": [3, 3]}, "col3", "row2", "cell4"],
+          ["col1",
+          {"string": "cellInfoAbbr", "args": [1, 1]}, "col2",
+          {"string": "cellInfoAbbr", "args": [2, 1]}, "col3",
+          {"string": "cellInfoAbbr", "args": [3, 1]}, "row1",
+          {"string": "cellInfoAbbr", "args": [1, 2]}, "col1", "cell1",
+          {"string": "cellInfoAbbr", "args": [3, 2]}, "col3", "row1", "cell2",
+          {"string": "cellInfoAbbr", "args": [4, 2]}, "row1", "row2",
+          {"string": "cellInfoAbbr", "args": [1, 3]}, "col1", "cell3",
+          {"string": "cellInfoAbbr", "args": [2, 3]}, "col2", "row2", "cell4",
+          {"string": "cellInfoAbbr", "args": [3, 3]}, "col3", "row2",
+          {"string": "tableAbbr"},
+          {"string": "tblColumnInfoAbbr", "count": 4},
+          {"string": "tblRowInfoAbbr", "count": 3}]]
       }, {
         accOrElmOrID: "table5",
         expectedUtterance: [["Row1", "Row2"], ["Row1", "Row2"]],
         expectedBraille: [["Row1", "Row2"], ["Row1", "Row2"]]
       }, {
         // Test pivot to table1_th1 from table1.
         accOrElmOrID: "table1_th1",
         oldAccOrElmOrID: "table1",
-        expectedUtterance: [["Column 1 Row 1", "col1"], ["col1",
-          "Column 1 Row 1"]],
-        expectedBraille: [["c1r1", "col1"], ["col1", "c1r1"]]
+        expectedUtterance: [[
+          {"string": "columnInfo", "args": [1]},
+          {"string": "rowInfo", "args": [1]}, "col1"], ["col1",
+          {"string": "columnInfo", "args": [1]},
+          {"string": "rowInfo", "args": [1]}]],
+        expectedBraille: [[
+          {"string": "cellInfoAbbr", "args": [1, 1]}, "col1"], ["col1",
+          {"string": "cellInfoAbbr", "args": [1, 1]}]]
       }, {
         // Test pivot to table1_td2 from table1.
         accOrElmOrID: "table1_td2",
         oldAccOrElmOrID: "table1",
-        expectedUtterance: [["Column 2 Row 2 col2", "cell2"], ["cell2",
-          "Column 2 Row 2 col2"]],
-        expectedBraille: [["c2r2 col2", "cell2"], ["cell2", "c2r2 col2"]]
+        expectedUtterance: [[
+          {"string": "columnInfo", "args": [2]},
+          {"string": "rowInfo", "args": [2]}, "col2", "cell2"], ["cell2",
+          {"string": "columnInfo", "args": [2]},
+          {"string": "rowInfo", "args": [2]}, "col2"]],
+        expectedBraille: [
+          [{"string": "cellInfoAbbr", "args": [2, 2]}, "col2", "cell2"],
+          ["cell2", {"string": "cellInfoAbbr", "args": [2, 2]}, "col2"]]
       }, {
         // Test pivot to table1_td2 from table1_th1.
         accOrElmOrID: "table1_td2",
         oldAccOrElmOrID: "table1_th1",
-        expectedUtterance: [["Column 2 Row 2 col2", "cell2"], ["cell2",
-          "Column 2 Row 2 col2"]],
-        expectedBraille: [["c2r2 col2", "cell2"], ["cell2", "c2r2 col2"]]
+        expectedUtterance: [[
+          {"string": "columnInfo", "args": [2]},
+          {"string": "rowInfo", "args": [2]}, "col2", "cell2"], ["cell2",
+          {"string": "columnInfo", "args": [2]},
+          {"string": "rowInfo", "args": [2]}, "col2"]],
+        expectedBraille: [
+          [{"string": "cellInfoAbbr", "args": [2, 2]}, "col2", "cell2"],
+          ["cell2", {"string": "cellInfoAbbr", "args": [2, 2]}, "col2"]]
       }, {
         // Test pivot to table1_td2 from table1_td1.
         accOrElmOrID: "table1_td2",
         oldAccOrElmOrID: "table1_td1",
-        expectedUtterance: [["Column 2 col2", "cell2"], ["cell2",
-          "Column 2 col2"]],
-        expectedBraille: [["c2r2 col2", "cell2"], ["cell2", "c2r2 col2"]]
+        expectedUtterance: [[
+          {"string": "columnInfo", "args": [2]}, "col2", "cell2"], ["cell2",
+          {"string": "columnInfo", "args": [2]}, "col2"]],
+        expectedBraille: [
+          [{"string": "cellInfoAbbr", "args": [2, 2]}, "col2", "cell2"],
+          ["cell2", {"string": "cellInfoAbbr", "args": [2, 2]}, "col2"]]
       }, {
         // Test pivot to table2_cell_1 from table2.
         accOrElmOrID: "table2_cell_1",
         oldAccOrElmOrID: "table2",
-        expectedUtterance: [["Column 1 Row 1 col1", "cell1"], ["cell1",
-          "Column 1 Row 1 col1"]],
-        expectedBraille: [["c1r1 col1", "cell1"], ["cell1", "c1r1 col1"]]
+        expectedUtterance: [[
+          {"string": "columnInfo", "args": [1]},
+          {"string": "rowInfo", "args": [1]}, "col1", "cell1"], ["cell1",
+          {"string": "columnInfo", "args": [1]},
+          {"string": "rowInfo", "args": [1]}, "col1"]],
+        expectedBraille: [
+          [{"string": "cellInfoAbbr", "args": [1, 1]}, "col1", "cell1"],
+          ["cell1", {"string": "cellInfoAbbr", "args": [1, 1]}, "col1"]]
       }, {
         // Test pivot to table2_cell_2 from table2.
         accOrElmOrID: "table2_cell_2",
         oldAccOrElmOrID: "table2",
-        expectedUtterance: [["Column 2 Row 1 col2",
-          "table with 1 column and 2 rows", "Column 1 Row 1", "colheader",
-          "Column 1 Row 2 colheader", "bla"], ["colheader", "Column 1 Row 1",
-          "bla", "Column 1 Row 2 colheader", "table with 1 column and 2 rows",
-          "Column 2 Row 1 col2"]],
-        expectedBraille: [["c2r1 col2", "tbl 1c 2r", "c1r1", "colheader",
-          "c1r2 colheader", "bla"], ["colheader", "c1r1", "bla",
-          "c1r2 colheader", "tbl 1c 2r", "c2r1 col2"]]
+        expectedUtterance: [[
+          {"string": "columnInfo", "args": [2]},
+          {"string": "rowInfo", "args": [1]}, "col2",
+          {"string": "table"},
+          {"string": "tblColumnInfo", "count": 1},
+          {"string": "tblRowInfo", "count": 2},
+          {"string": "columnInfo", "args": [1]},
+          {"string": "rowInfo", "args": [1]}, "colheader",
+          {"string": "columnInfo", "args": [1]},
+          {"string": "rowInfo", "args": [2]}, "colheader", "bla"], ["colheader",
+          {"string": "columnInfo", "args": [1]},
+          {"string": "rowInfo", "args": [1]}, "bla",
+          {"string": "columnInfo", "args": [1]},
+          {"string": "rowInfo", "args": [2]}, "colheader",
+          {"string": "table"},
+          {"string": "tblColumnInfo", "count": 1},
+          {"string": "tblRowInfo", "count": 2},
+          {"string": "columnInfo", "args": [2]},
+          {"string": "rowInfo", "args": [1]}, "col2"]],
+        expectedBraille: [[
+          {"string": "cellInfoAbbr", "args": [2, 1]}, "col2",
+          {"string": "tableAbbr"},
+          {"string": "tblColumnInfoAbbr", "count": 1},
+          {"string": "tblRowInfoAbbr", "count": 2},
+          {"string": "cellInfoAbbr", "args": [1, 1]}, "colheader",
+          {"string": "cellInfoAbbr", "args": [1, 2]}, "colheader", "bla"],
+          ["colheader",
+          {"string": "cellInfoAbbr", "args": [1, 1]}, "bla",
+          {"string": "cellInfoAbbr", "args": [1, 2]}, "colheader",
+          {"string": "tableAbbr"},
+          {"string": "tblColumnInfoAbbr", "count": 1},
+          {"string": "tblRowInfoAbbr", "count": 2},
+          {"string": "cellInfoAbbr", "args": [2, 1]}, "col2"]]
       }, {
         // Test pivot to table2_cell_1 from table2_cell_2.
         accOrElmOrID: "table2_cell_1",
         oldAccOrElmOrID: "table2_cell_2",
-        expectedUtterance: [["Column 1 col1", "cell1"], ["cell1",
-          "Column 1 col1"]],
-        expectedBraille: [["c1r1 col1", "cell1"], ["cell1", "c1r1 col1"]]
+        expectedUtterance: [[
+          {"string": "columnInfo", "args": [1]}, "col1", "cell1"], ["cell1",
+          {"string": "columnInfo", "args": [1]}, "col1"]],
+        expectedBraille: [
+          [{"string": "cellInfoAbbr", "args": [1, 1]}, "col1", "cell1"],
+          ["cell1", {"string": "cellInfoAbbr", "args": [1, 1]}, "col1"]]
       }, {
         // Test pivot to table3_cell from table2.
         accOrElmOrID: "table3_cell",
         oldAccOrElmOrID: "table2",
-        expectedUtterance: [["Column 2 Row 1 col2",
-          "table with 1 column and 2 rows", "Column 1 Row 2 colheader",
-          "bla"], ["bla", "Column 1 Row 2 colheader",
-          "table with 1 column and 2 rows", "Column 2 Row 1 col2"]],
-        expectedBraille: [["c1r2 colheader", "bla"], ["bla", "c1r2 colheader"]]
+        expectedUtterance: [[
+          {"string": "columnInfo", "args": [2]},
+          {"string": "rowInfo", "args": [1]}, "col2",
+          {"string": "table"},
+          {"string": "tblColumnInfo", "count": 1},
+          {"string": "tblRowInfo", "count": 2},
+          {"string": "columnInfo", "args": [1]},
+          {"string": "rowInfo", "args": [2]}, "colheader", "bla"], ["bla",
+          {"string": "columnInfo", "args": [1]},
+          {"string": "rowInfo", "args": [2]}, "colheader",
+          {"string": "table"},
+          {"string": "tblColumnInfo", "count": 1},
+          {"string": "tblRowInfo", "count": 2},
+          {"string": "columnInfo", "args": [2]},
+          {"string": "rowInfo", "args": [1]}, "col2"]],
+        expectedBraille: [
+          [{"string": "cellInfoAbbr", "args": [1, 2]}, "colheader", "bla"],
+          ["bla", {"string": "cellInfoAbbr", "args": [1, 2]}, "colheader"]]
       }, {
         // Test pivot to table3_cell from table2_cell_1.
         accOrElmOrID: "table3_cell",
         oldAccOrElmOrID: "table2_cell_1",
-        expectedUtterance: [["Column 2 col2", "table with 1 column and 2 rows",
-          "Column 1 Row 2 colheader", "bla"], ["bla",
-          "Column 1 Row 2 colheader", "table with 1 column and 2 rows",
-          "Column 2 Row 1 col2"]],
-        expectedBraille: [["c1r2 colheader", "bla"], ["bla", "c1r2 colheader"]]
+        expectedUtterance: [[
+          {"string": "columnInfo", "args": [2]}, "col2",
+          {"string": "table"},
+          {"string": "tblColumnInfo", "count": 1},
+          {"string": "tblRowInfo", "count": 2},
+          {"string": "columnInfo", "args": [1]},
+          {"string": "rowInfo", "args": [2]}, "colheader", "bla"], ["bla",
+          {"string": "columnInfo", "args": [1]},
+          {"string": "rowInfo", "args": [2]}, "colheader",
+          {"string": "table"},
+          {"string": "tblColumnInfo", "count": 1},
+          {"string": "tblRowInfo", "count": 2},
+          {"string": "columnInfo", "args": [2]},
+          {"string": "rowInfo", "args": [1]}, "col2"]],
+        expectedBraille: [
+          [{"string": "cellInfoAbbr", "args": [1, 2]}, "colheader", "bla"],
+          ["bla", {"string": "cellInfoAbbr", "args": [1, 2]}, "colheader"]]
       }, {
         // Test pivot to table3_cell from table3_ch.
         accOrElmOrID: "table3_cell",
         oldAccOrElmOrID: "table3_ch",
-        expectedUtterance: [["Row 2", "bla"], ["bla", "Row 2"]],
-        expectedBraille: [["c1r2", "bla"], ["bla", "c1r2"]]
+        expectedUtterance: [[
+          {"string": "rowInfo", "args": [2]}, "bla"], ["bla",
+          {"string": "rowInfo", "args": [2]}]],
+        expectedBraille: [
+          [{"string": "cellInfoAbbr", "args": [1, 2]}, "bla"],
+          ["bla", {"string": "cellInfoAbbr", "args": [1, 2]}]]
       }, {
         // Test pivot to table3_cell from table1_td1.
         accOrElmOrID: "table3_cell",
         oldAccOrElmOrID: "table1_td1",
-        expectedUtterance: [["table with 2 columns and 2 rows",
-          "Column 2 Row 1 col2", "table with 1 column and 2 rows",
-          "Column 1 Row 2 colheader", "bla"], ["bla",
-          "Column 1 Row 2 colheader", "table with 1 column and 2 rows",
-          "Column 2 Row 1 col2", "table with 2 columns and 2 rows"]],
-        expectedBraille: [["c1r2 colheader", "bla"], ["bla", "c1r2 colheader"]]
+        expectedUtterance: [[
+          {"string": "table"},
+          {"string": "tblColumnInfo", "count": 2},
+          {"string": "tblRowInfo", "count": 2},
+          {"string": "columnInfo", "args": [2]},
+          {"string": "rowInfo", "args": [1]}, "col2",
+          {"string": "table"},
+          {"string": "tblColumnInfo", "count": 1},
+          {"string": "tblRowInfo", "count": 2},
+          {"string": "columnInfo", "args": [1]},
+          {"string": "rowInfo", "args": [2]}, "colheader", "bla"], ["bla",
+          {"string": "columnInfo", "args": [1]},
+          {"string": "rowInfo", "args": [2]}, "colheader",
+          {"string": "table"},
+          {"string": "tblColumnInfo", "count": 1},
+          {"string": "tblRowInfo", "count": 2},
+          {"string": "columnInfo", "args": [2]},
+          {"string": "rowInfo", "args": [1]}, "col2",
+          {"string": "table"},
+          {"string": "tblColumnInfo", "count": 2},
+          {"string": "tblRowInfo", "count": 2}]],
+        expectedBraille: [
+          [{"string": "cellInfoAbbr", "args": [1, 2]}, "colheader", "bla"],
+          ["bla", {"string": "cellInfoAbbr", "args": [1, 2]}, "colheader"]]
       }, {
         // Test pivot to table4_ch_3 from table4.
         accOrElmOrID: "table4_ch_3",
         oldAccOrElmOrID: "table4",
-        expectedUtterance: [["Column 3 Row 1", "col3"], ["col3",
-          "Column 3 Row 1"]],
-        expectedBraille: [["c3r1", "col3"], ["col3", "c3r1"]]
+        expectedUtterance: [[
+          {"string": "columnInfo", "args": [3]},
+          {"string": "rowInfo", "args": [1]}, "col3"], ["col3",
+          {"string": "columnInfo", "args": [3]},
+          {"string": "rowInfo", "args": [1]}]],
+        expectedBraille: [
+          [{"string": "cellInfoAbbr", "args": [3, 1]}, "col3"],
+          ["col3", {"string": "cellInfoAbbr", "args": [3, 1]}]]
       }, {
         // Test pivot to table4_rh_1 from table4_ch_3.
         accOrElmOrID: "table4_rh_1",
         oldAccOrElmOrID: "table4_ch_3",
-        expectedUtterance: [["Column 1 Row 2 spans 2 columns col1", "row1"], [
-          "row1", "Column 1 Row 2 spans 2 columns col1"]],
-        expectedBraille: [["c1r2 col1", "row1"], ["row1", "c1r2 col1"]]
+        expectedUtterance: [[
+          {"string": "columnInfo", "args": [1]},
+          {"string": "rowInfo", "args": [2]},
+          {"string": "spansColumns", "args": [2]}, "col1", "row1"], ["row1",
+          {"string": "columnInfo", "args": [1]},
+          {"string": "rowInfo", "args": [2]},
+          {"string": "spansColumns", "args": [2]}, "col1"]],
+        expectedBraille: [
+          [{"string": "cellInfoAbbr", "args": [1, 2]}, "col1", "row1"],
+          ["row1", {"string": "cellInfoAbbr", "args": [1, 2]}, "col1"]]
       }, {
         // Test pivot to table4_cell_3 from table4_rh_1.
         accOrElmOrID: "table4_cell_3",
         oldAccOrElmOrID: "table4_rh_1",
-        expectedUtterance: [["Column 4 spans 2 rows", "cell2"], ["cell2",
-          "Column 4 spans 2 rows"]],
-        expectedBraille: [["c4r2", "cell2"], ["cell2", "c4r2"]]
+        expectedUtterance: [[
+          {"string": "columnInfo", "args": [4]},
+          {"string": "spansRows", "args": [2]}, "cell2"], ["cell2",
+          {"string": "columnInfo", "args": [4]},
+          {"string": "spansRows", "args": [2]}]],
+        expectedBraille: [
+          [{"string": "cellInfoAbbr", "args": [4, 2]}, "cell2"],
+          ["cell2", {"string": "cellInfoAbbr", "args": [4, 2]}]]
       }, {
         // Test pivot to table4_cell_5 from table4_cell_3.
         accOrElmOrID: "table4_cell_5",
         oldAccOrElmOrID: "table4_cell_3",
-        expectedUtterance: [["Column 2 Row 3 col2 row2", "cell3"], ["cell3",
-          "Column 2 Row 3 col2 row2"]],
-        expectedBraille: [["c2r3 col2 row2", "cell3"], ["cell3",
-          "c2r3 col2 row2"]]
+        expectedUtterance: [[
+          {"string": "columnInfo", "args": [2]},
+          {"string": "rowInfo", "args": [3]}, "col2", "row2", "cell3"],
+          ["cell3",
+          {"string": "columnInfo", "args": [2]},
+          {"string": "rowInfo", "args": [3]}, "col2", "row2"]],
+        expectedBraille: [
+          [{"string": "cellInfoAbbr", "args": [2, 3]}, "col2", "row2", "cell3"],
+          ["cell3", {"string": "cellInfoAbbr", "args": [2, 3]}, "col2", "row2"]]
       }];
 
       SpecialPowers.setIntPref(PREF_UTTERANCE_ORDER, 0);
 
       // Test outputs (utterance and braille) for tables including their
       // headers and cells.
       tests.forEach(function run(test) {
         var outputOrderValues = [0, 1];
--- a/dom/locales/en-US/chrome/accessibility/AccessFu.properties
+++ b/dom/locales/en-US/chrome/accessibility/AccessFu.properties
@@ -89,41 +89,37 @@ headingLevel   =       heading level %S
 
 # more sophisticated list announcement
 listStart      =       First item
 listEnd        =       Last item
 # LOCALIZATION NOTE (listItemsCount): Semi-colon list of plural forms.
 # See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
 listItemsCount =       1 item;#1 items
 
-# LOCALIZATION NOTE: %1$S is the item's role name (e.g. "tab"),
-# %2$S is the position of the item n the set.
-# %3$S is the total number of such items in the set.
-# An expanded example would read "tab 2 of 5".
-objItemOf      =       %1$S %2$S of %3$S
+# LOCALIZATION NOTE: # %1$S is the position of the item n the set.
+# %2$S is the total number of such items in the set.
+# An expanded example would read "2 of 5".
+objItemOfN      =       %1$S of %2$S
 
 # Landmark announcements
 banner         =       banner
 complementary  =       complementary
 contentinfo    =       content info
 main           =       main
 navigation     =       navigation
 search         =       search
 
-# Description of a table or grid:
-# 1 is a dynamically retrieved localized role of either 'table' or 'grid'.
-# 2 is the number of columns within the table.
-# 3 is the number of rows within the table or grid.
-tableInfo = %S with %S and %S
-# LOCALIZATION NOTE (tableColumnInfo): Semi-colon list of plural forms.
+# LOCALIZATION NOTE (tblColumnInfo): Semi-colon list of plural forms.
+# Number of columns within the table.
 # See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
-tableColumnInfo = 1 column;#1 columns
-# LOCALIZATION NOTE (tableRowInfo): Semi-colon list of plural forms.
+tblColumnInfo = with 1 column;with #1 columns
+# LOCALIZATION NOTE (tblRowInfo): Semi-colon list of plural forms.
+# Number of rows within the table or grid.
 # See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
-tableRowInfo = 1 row;#1 rows
+tblRowInfo = and 1 row;and #1 rows
 
 # table or grid cell information
 columnInfo = Column %S
 rowInfo = Row %S
 spansColumns = spans %S columns
 spansRows = spans %S rows
 
 # Invoked actions
@@ -185,23 +181,90 @@ quicknav_Link        = Links
 quicknav_List        = Lists
 quicknav_PageTab     = Page tabs
 quicknav_RadioButton = Radio buttons
 quicknav_Separator   = Separators
 quicknav_Table       = Tables
 quicknav_Checkbox    = Check boxes
 
 # Shortened role names for braille
-linkAbbr           =       lnk
-pushbuttonAbbr     =       btn
-passwordtextAbbr   =       passwdtxt
-imagemapAbbr       =       imgmap
-figureAbbr         =       fig
-textareaAbbr       =       txtarea
+menubarAbbr        =       menu bar
+scrollbarAbbr      =       scroll bar
+gripAbbr           =       grip
+alertAbbr          =       alert
+menupopupAbbr      =       menu popup
+documentAbbr       =       document
+paneAbbr           =       pane
+dialogAbbr         =       dialog
+separatorAbbr      =       separator
+toolbarAbbr        =       toolbar
+statusbarAbbr      =       status bar
 tableAbbr          =       tbl
-tableInfoAbbr = %S %S %S
-# LOCALIZATION NOTE (tableColumnInfoAbbr): Semi-colon list of plural forms.
+columnheaderAbbr   =       column header
+rowheaderAbbr      =       row header
+columnAbbr         =       column
+rowAbbr            =       row
+cellAbbr           =       cell
+linkAbbr           =       lnk
+listAbbr           =       list
+listitemAbbr       =       list item
+outlineAbbr        =       outline
+outlineitemAbbr    =       outline item
+pagetabAbbr        =       tab
+propertypageAbbr   =       property page
+graphicAbbr        =       graphic
+pushbuttonAbbr     =       btn
+checkbuttonAbbr    =       check button
+radiobuttonAbbr    =       radio button
+comboboxAbbr       =       combo box
+progressbarAbbr    =       progress bar
+sliderAbbr         =       slider
+spinbuttonAbbr     =       spin button
+diagramAbbr        =       diagram
+animationAbbr      =       animation
+equationAbbr       =       equation
+buttonmenuAbbr     =       button menu
+whitespaceAbbr     =       white space
+pagetablistAbbr    =       tab list
+canvasAbbr         =       canvas
+checkmenuitemAbbr  =       check menu item
+labelAbbr          =       label
+passwordtextAbbr   =       passwdtxt
+radiomenuitemAbbr  =       radio menu item
+textcontainerAbbr  =       text container
+togglebuttonAbbr   =       toggle button
+treetableAbbr      =       tree table
+headerAbbr         =       header
+footerAbbr         =       footer
+paragraphAbbr      =       paragraph
+entryAbbr          =       entry
+captionAbbr        =       caption
+headingAbbr        =       heading
+sectionAbbr        =       section
+formAbbr           =       form
+comboboxlistAbbr   =       combo box list
+comboboxoptionAbbr =       combo box option
+imagemapAbbr       =       imgmap
+listboxoptionAbbr  =       option
+listboxAbbr        =       list box
+flatequationAbbr   =       flat equation
+gridcellAbbr       =       gridcell
+noteAbbr           =       note
+figureAbbr         =       fig
+definitionlistAbbr =       definition list
+termAbbr           =       term
+definitionAbbr     =       definition
+textareaAbbr       =       txtarea
+
+# LOCALIZATION NOTE (tblColumnInfoAbbr): Semi-colon list of plural forms.
+# Number of columns within the table.
 # See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
-tableColumnInfoAbbr = #1c;#1c
-# LOCALIZATION NOTE (tableRowInfoAbbr): Semi-colon list of plural forms.
+tblColumnInfoAbbr = #1c;#1c
+# LOCALIZATION NOTE (tblRowInfoAbbr): Semi-colon list of plural forms.
+# Number of rows within the table or grid.
 # See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
-tableRowInfoAbbr = #1r;#1r
+tblRowInfoAbbr = #1r;#1r
 cellInfoAbbr = c%Sr%S
+
+stateCheckedAbbr = (x)
+stateUncheckedAbbr = ( )
+statePressedAbbr = (x)
+stateUnpressedAbbr = ( )