Bug 1152454 - Made liveregions responsive to name/value change events. r=yzen
authorRoss Ziegler <bugzilla@imross.com>
Tue, 21 Apr 2015 17:06:18 -0400
changeset 240404 940168142fdb7b64c7baa21ef0daf7fb8074313d
parent 240403 02e6a50741a95b36444701bf1ed0c9422d987cec
child 240405 df384f61363ab035fa9177dc330ce38a46d8b6f1
push id28636
push userkwierso@gmail.com
push dateThu, 23 Apr 2015 00:16:12 +0000
treeherdermozilla-central@a5af73b32ac8 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersyzen
bugs1152454
milestone40.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 1152454 - Made liveregions responsive to name/value change events. r=yzen --- accessible/jsat/EventManager.jsm | 12 ++++++++++++ accessible/jsat/Presentation.jsm | 10 ++++++---- .../tests/mochitest/jsat/doc_content_integration.html | 15 +++++++++++++++ accessible/tests/mochitest/jsat/jsatcommon.js | 2 +- .../tests/mochitest/jsat/test_content_integration.html | 16 +++++++++++++--- 5 files changed, 47 insertions(+), 8 deletions(-)
accessible/jsat/EventManager.jsm
accessible/jsat/Presentation.jsm
accessible/tests/mochitest/jsat/doc_content_integration.html
accessible/tests/mochitest/jsat/jsatcommon.js
accessible/tests/mochitest/jsat/test_content_integration.html
--- a/accessible/jsat/EventManager.jsm
+++ b/accessible/jsat/EventManager.jsm
@@ -186,16 +186,22 @@ this.EventManager.prototype = {
         }
         break;
       }
       case Events.NAME_CHANGE:
       {
         let acc = aEvent.accessible;
         if (acc === this.contentControl.vc.position) {
           this.present(Presentation.nameChanged(acc));
+        } else {
+          let {liveRegion, isPolite} = this._handleLiveRegion(aEvent,
+            ['text', 'all']);
+          if (liveRegion) {
+            this.present(Presentation.nameChanged(acc, isPolite));
+          }
         }
         break;
       }
       case Events.SCROLLING_START:
       {
         this.contentControl.autoMove(aEvent.accessible);
         break;
       }
@@ -288,16 +294,22 @@ this.EventManager.prototype = {
       }
       case Events.VALUE_CHANGE:
       {
         let position = this.contentControl.vc.position;
         let target = aEvent.accessible;
         if (position === target ||
             Utils.getEmbeddedControl(position) === target) {
           this.present(Presentation.valueChanged(target));
+        } else {
+          let {liveRegion, isPolite} = this._handleLiveRegion(aEvent,
+            ['text', 'all']);
+          if (liveRegion) {
+            this.present(Presentation.valueChanged(target, isPolite));
+          }
         }
       }
     }
   },
 
   _setEditingMode: function _setEditingMode(aEvent, aCaretOffset) {
     let acc = aEvent.accessible;
     let accText, characterCount;
--- a/accessible/jsat/Presentation.jsm
+++ b/accessible/jsat/Presentation.jsm
@@ -518,39 +518,41 @@ B2GPresenter.prototype.pivotChanged =
           isUserInput: aIsUserInput,
           hints: aContext.interactionHints
         }
       }
     };
   };
 
 B2GPresenter.prototype.nameChanged =
-  function B2GPresenter_nameChanged(aAccessible) {
+  function B2GPresenter_nameChanged(aAccessible, aIsPolite = true) {
     return {
       type: this.type,
       details: {
         eventType: 'name-change',
-        data: aAccessible.name
+        data: aAccessible.name,
+        options: {enqueue: aIsPolite}
       }
     };
   };
 
 B2GPresenter.prototype.valueChanged =
-  function B2GPresenter_valueChanged(aAccessible) {
+  function B2GPresenter_valueChanged(aAccessible, aIsPolite = true) {
 
     // the editable value changes are handled in the text changed presenter
     if (Utils.getState(aAccessible).contains(States.EDITABLE)) {
       return null;
     }
 
     return {
       type: this.type,
       details: {
         eventType: 'value-change',
-        data: aAccessible.value
+        data: aAccessible.value,
+        options: {enqueue: aIsPolite}
       }
     };
   };
 
 B2GPresenter.prototype.textChanged = function B2GPresenter_textChanged(
   aAccessible, aIsInserted, aStart, aLength, aText, aModifiedText) {
     let echoSetting = this.keyboardEchoSetting.value;
     let text = '';
--- a/accessible/tests/mochitest/jsat/doc_content_integration.html
+++ b/accessible/tests/mochitest/jsat/doc_content_integration.html
@@ -39,16 +39,27 @@
     function ariaHideIframe() {
       document.getElementById('iframe').setAttribute('aria-hidden', true);
     }
 
     function renameFruit() {
       document.getElementById('fruit').setAttribute('aria-label', 'banana');
     }
 
+    function renameSlider() {
+      document.getElementById('slider').setAttribute(
+        'aria-label', 'mover');
+    }
+
+    function changeSliderValue() {
+      document.getElementById('slider').setAttribute('aria-valuenow', '5');
+      document.getElementById('slider').setAttribute(
+        'aria-valuetext', 'medium');
+    }
+
   </script>
   <style>
     #windows {
       position: relative;
       width: 320px;
       height: 480px;
     }
 
@@ -84,10 +95,14 @@
       <p>Do you agree?</p>
       <button onclick="setTimeout(hideAlert, 500)">Yes</button>
       <button onclick="hideAlert()">No</button>
     </div>
     <div id="appframe"></div>
   </div>
   <button id="home">Home</button>
   <button id="fruit" aria-label="apple"></button>
+  <div id="live" aria-live="polite" aria-label="live">
+    <div id="slider" role="slider" aria-label="slider" aria-valuemin="0"
+      aria-valuemax="10"  aria-valuenow="0"></div>
+  </div>
 </body>
 </html>
--- a/accessible/tests/mochitest/jsat/jsatcommon.js
+++ b/accessible/tests/mochitest/jsat/jsatcommon.js
@@ -624,17 +624,17 @@ function ExpectedNameChange(aName, aOpti
   }, null, aOptions);
 }
 
 ExpectedNameChange.prototype = Object.create(ExpectedPresent.prototype);
 
 function ExpectedValueChange(aValue, aOptions) {
   ExpectedPresent.call(this, {
     eventType: 'value-change',
-    data: [aValue]
+    data: aValue
   }, null, aOptions);
 }
 
 ExpectedValueChange.prototype = Object.create(ExpectedPresent.prototype);
 
 function ExpectedTextChanged(aValue, aOptions) {
   ExpectedPresent.call(this, {
     eventType: 'text-change',
--- a/accessible/tests/mochitest/jsat/test_content_integration.html
+++ b/accessible/tests/mochitest/jsat/test_content_integration.html
@@ -53,19 +53,23 @@
            new ExpectedCursorChange(['much range', {'string': 'label'}])],
           [ContentMessages.simpleMoveNext,
            new ExpectedCursorChange(['much range', '5', {'string': 'slider'}])],
           [ContentMessages.moveOrAdjustUp(), new ExpectedValueChange('6')],
           [ContentMessages.simpleMoveNext,
            new ExpectedCursorChange(['Home', {'string': 'pushbutton'}])],
           [ContentMessages.simpleMoveNext,
            new ExpectedCursorChange(['apple', {'string': 'pushbutton'}])],
+           [ContentMessages.simpleMoveNext,
+           new ExpectedCursorChange(['slider', '0', {'string': 'slider'}])],
 
           // Simple traversal backward
           [ContentMessages.simpleMovePrevious,
+           new ExpectedCursorChange(['apple', {'string': 'pushbutton'}])],
+          [ContentMessages.simpleMovePrevious,
            new ExpectedCursorChange(['Home', {'string': 'pushbutton'}])],
           [ContentMessages.simpleMovePrevious,
            new ExpectedCursorChange(['much range', '6', {'string': 'slider'}, 'such app'])],
           [ContentMessages.moveOrAdjustDown(), new ExpectedValueChange('5')],
           [ContentMessages.simpleMovePrevious,
            new ExpectedCursorChange(['much range', {'string': 'label'}])],
           [ContentMessages.simpleMovePrevious,
            new ExpectedCursorChange(['many option', {'string': 'stateChecked'},
@@ -87,17 +91,17 @@
           // Moving to the absolute last item from an embedded document
           // fails. Bug 972035.
           [ContentMessages.simpleMoveNext,
            new ExpectedCursorChange(
             ['wow', {'string': 'headingLevel', 'args': [1]}, 'such app'])],
           // Move from an inner frame to the last element in the parent doc
           [ContentMessages.simpleMoveLast,
             new ExpectedCursorChange(
-              ['apple', {'string': 'pushbutton'}], { b2g_todo: true })],
+              ['slider', '0', {'string': 'slider'}], { b2g_todo: true })],
 
           [ContentMessages.clearCursor, 'AccessFu:CursorCleared'],
 
           [ContentMessages.simpleMoveNext,
            new ExpectedCursorChange(['Phone status bar', 'Traversal Rule test document'])],
           [ContentMessages.moveOrAdjustDown('FormElement'),
            new ExpectedCursorChange(['Back', {"string": "pushbutton"}])],
           [ContentMessages.moveOrAdjustDown('FormElement'),
@@ -142,16 +146,22 @@
 
           // Current virtual cursor's position's name changes
           [ContentMessages.simpleMoveNext,
            new ExpectedCursorChange(['Phone status bar', 'Traversal Rule test document'])],
           [ContentMessages.focusSelector('button#fruit', false),
            new ExpectedCursorChange(['apple', {'string': 'pushbutton'}])],
           [doc.defaultView.renameFruit, new ExpectedNameChange('banana')],
 
+          // Name and value changes inside a live-region (no cursor present)
+          [doc.defaultView.renameSlider,
+            new ExpectedNameChange('mover')],
+          [doc.defaultView.changeSliderValue,
+            new ExpectedValueChange('medium')],
+
           // Blur button and reset cursor
           [ContentMessages.focusSelector('button#fruit', true), null],
           [ContentMessages.clearCursor, 'AccessFu:CursorCleared'],
 
           // Move cursor with focus in outside document
           [ContentMessages.simpleMoveNext,
            new ExpectedCursorChange(['Phone status bar', 'Traversal Rule test document'])],
           [ContentMessages.focusSelector('button#home', false),
@@ -217,24 +227,24 @@
            new ExpectedCursorChange(
              ["wow", {"string": "headingLevel","args": [1]}, "such app"])],
           [doc.defaultView.ariaShowBack],
           [ContentMessages.focusSelector('button#back', true), null],
           [ContentMessages.clearCursor, 'AccessFu:CursorCleared'],
 
           // Open dialog in outer doc, while cursor is also in outer doc
           [ContentMessages.simpleMoveLast,
-           new ExpectedCursorChange(['banana', {'string': 'pushbutton'}])],
+           new ExpectedCursorChange(['mover'])],
           [doc.defaultView.showAlert,
             new ExpectedCursorChange(['This is an alert!',
               {'string': 'headingLevel', 'args': [1]},
               {'string': 'dialog'}])],
 
           [doc.defaultView.hideAlert,
-           new ExpectedCursorChange(['banana', {'string': 'pushbutton'}])],
+           new ExpectedCursorChange(['mover'])],
 
           [ContentMessages.clearCursor, 'AccessFu:CursorCleared'],
 
           // Open dialog in outer doc, while cursor is in inner frame
           [ContentMessages.simpleMoveNext,
            new ExpectedCursorChange(['Phone status bar', 'Traversal Rule test document'])],
           [ContentMessages.simpleMoveNext,
            new ExpectedCursorChange(["Back", {"string": "pushbutton"}])],