Bug 1081720 - Use prefs service to store quicknav state and proxy quicknav prefs to b2g settings. r=yzen
authorEitan Isaacson <eitan@monotonous.org>
Tue, 14 Oct 2014 14:42:50 -0700
changeset 234772 2fdd96ecd3e1ba3131a9bc932965972ee63dbbae
parent 234771 df5e04296d12f7977e8064247a46d14b951ca0c7
child 234773 d154732dd7a60e9c98bc690ddfa4b79e4a98fa8f
push id4311
push userraliiev@mozilla.com
push dateMon, 12 Jan 2015 19:37:41 +0000
treeherdermozilla-beta@150c9fed433b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersyzen
bugs1081720
milestone36.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 1081720 - Use prefs service to store quicknav state and proxy quicknav prefs to b2g settings. r=yzen
accessible/jsat/AccessFu.jsm
accessible/jsat/Utils.jsm
accessible/tests/mochitest/jsat/a11y.ini
accessible/tests/mochitest/jsat/jsatcommon.js
accessible/tests/mochitest/jsat/test_quicknav_modes.html
b2g/app/b2g.js
b2g/chrome/content/settings.js
mobile/android/app/mobile.js
--- a/accessible/jsat/AccessFu.jsm
+++ b/accessible/jsat/AccessFu.jsm
@@ -15,16 +15,18 @@ this.EXPORTED_SYMBOLS = ['AccessFu']; //
 Cu.import('resource://gre/modules/Services.jsm');
 Cu.import('resource://gre/modules/accessibility/Utils.jsm');
 
 const ACCESSFU_DISABLE = 0; // jshint ignore:line
 const ACCESSFU_ENABLE = 1;
 const ACCESSFU_AUTO = 2;
 
 const SCREENREADER_SETTING = 'accessibility.screenreader';
+const QUICKNAV_MODES_PREF = 'accessibility.accessfu.quicknav_modes';
+const QUICKNAV_INDEX_PREF = 'accessibility.accessfu.quicknav_index';
 
 this.AccessFu = { // jshint ignore:line
   /**
    * Initialize chrome-layer accessibility functionality.
    * If accessibility is enabled on the platform, then a special accessibility
    * mode is started.
    */
   attach: function attach(aWindow) {
@@ -98,21 +100,28 @@ this.AccessFu = { // jshint ignore:line
     let stylesheet = Utils.win.document.createProcessingInstruction(
       'xml-stylesheet', 'href="' + stylesheetURL + '" type="text/css"');
     Utils.win.document.insertBefore(stylesheet, Utils.win.document.firstChild);
     this.stylesheet = Cu.getWeakReference(stylesheet);
 
 
     // Populate quicknav modes
     this._quicknavModesPref =
-      new PrefCache(
-        'accessibility.accessfu.quicknav_modes',
-        (aName, aValue) => {
-          this.Input.quickNavMode.updateModes(aValue);
-        }, true);
+      new PrefCache(QUICKNAV_MODES_PREF, (aName, aValue, aFirstRun) => {
+        this.Input.quickNavMode.updateModes(aValue);
+        if (!aFirstRun) {
+          // If the modes change, reset the current mode index to 0.
+          Services.prefs.setIntPref(QUICKNAV_INDEX_PREF, 0);
+        }
+      }, true);
+
+    this._quicknavCurrentModePref =
+      new PrefCache(QUICKNAV_INDEX_PREF, (aName, aValue) => {
+        this.Input.quickNavMode.updateCurrentMode(Number(aValue));
+      }, true);
 
     // Check for output notification
     this._notifyOutputPref =
       new PrefCache('accessibility.accessfu.notify_output');
 
 
     this.Input.start();
     Output.start();
@@ -955,31 +964,34 @@ var Input = {
   },
 
   quickNavMode: {
     get current() {
       return this.modes[this._currentIndex];
     },
 
     previous: function quickNavMode_previous() {
-      if (--this._currentIndex < 0) {
-        this._currentIndex = this.modes.length - 1;
-      }
+      Services.prefs.setIntPref(QUICKNAV_INDEX_PREF,
+        this._currentIndex > 0 ?
+          this._currentIndex - 1 : this.modes.length - 1);
     },
 
     next: function quickNavMode_next() {
-      if (++this._currentIndex >= this.modes.length) {
-        this._currentIndex = 0;
-      }
+      Services.prefs.setIntPref(QUICKNAV_INDEX_PREF,
+        this._currentIndex + 1 >= this.modes.length ?
+          0 : this._currentIndex + 1);
     },
 
     updateModes: function updateModes(aModes) {
       if (aModes) {
         this.modes = aModes.split(',');
       } else {
         this.modes = [];
       }
     },
 
-    _currentIndex: -1
+    updateCurrentMode: function updateCurrentMode(aModeIndex) {
+      Logger.debug('Quicknav mode:', this.modes[aModeIndex]);
+      this._currentIndex = aModeIndex;
+    }
   }
 };
 AccessFu.Input = Input;
--- a/accessible/jsat/Utils.jsm
+++ b/accessible/jsat/Utils.jsm
@@ -952,17 +952,17 @@ this.PrefCache = function PrefCache(aNam
   this.name = aName;
   this.callback = aCallback;
 
   let branch = Services.prefs;
   this.value = this._getValue(branch);
 
   if (this.callback && aRunCallbackNow) {
     try {
-      this.callback(this.name, this.value);
+      this.callback(this.name, this.value, true);
     } catch (x) {
       Logger.logException(x);
     }
   }
 
   branch.addObserver(aName, this, true);
 };
 
@@ -985,19 +985,20 @@ PrefCache.prototype = {
     } catch (x) {
       // Pref does not exist.
       return null;
     }
   },
 
   observe: function observe(aSubject) {
     this.value = this._getValue(aSubject.QueryInterface(Ci.nsIPrefBranch));
+    Logger.info('pref changed', this.name, this.value);
     if (this.callback) {
       try {
-        this.callback(this.name, this.value);
+        this.callback(this.name, this.value, false);
       } catch (x) {
         Logger.logException(x);
       }
     }
   },
 
   QueryInterface : XPCOMUtils.generateQI([Ci.nsIObserver,
                                           Ci.nsISupportsWeakReference])
--- a/accessible/tests/mochitest/jsat/a11y.ini
+++ b/accessible/tests/mochitest/jsat/a11y.ini
@@ -13,11 +13,12 @@ support-files =
 skip-if = buildapp == 'mulet'
 [test_content_text.html]
 skip-if = buildapp == 'mulet'
 [test_explicit_names.html]
 [test_gesture_tracker.html]
 [test_landmarks.html]
 [test_live_regions.html]
 [test_output.html]
+[test_quicknav_modes.html]
 [test_tables.html]
 [test_pointer_relay.html]
 [test_traversal.html]
--- a/accessible/tests/mochitest/jsat/jsatcommon.js
+++ b/accessible/tests/mochitest/jsat/jsatcommon.js
@@ -130,17 +130,17 @@ var AccessFuTest = {
     } catch (ex) {
       // StopIteration exception.
       this.finish();
       return;
     }
     testFunc();
   },
 
-  runTests: function AccessFuTest_runTests() {
+  runTests: function AccessFuTest_runTests(aAdditionalPrefs) {
     if (gTestFuncs.length === 0) {
       ok(false, "No tests specified!");
       SimpleTest.finish();
       return;
     }
 
     // Create an Iterator for gTestFuncs array.
     gIterator = Iterator(gTestFuncs); // jshint ignore:line
@@ -151,20 +151,21 @@ var AccessFuTest = {
     AccessFu.attach(getMainChromeWindow(window));
 
     AccessFu.readyCallback = function readyCallback() {
       // Enable logging to the console service.
       Logger.test = true;
       Logger.logLevel = Logger.DEBUG;
     };
 
-    SpecialPowers.pushPrefEnv({
-      'set': [['accessibility.accessfu.notify_output', 1],
-              ['dom.mozSettings.enabled', true]]
-    }, function () {
+    var prefs = [['accessibility.accessfu.notify_output', 1],
+      ['dom.mozSettings.enabled', true]];
+    prefs.push.apply(prefs, aAdditionalPrefs);
+
+    SpecialPowers.pushPrefEnv({ 'set': prefs }, function () {
       if (AccessFuTest._waitForExplicitFinish) {
         // Run all test functions asynchronously.
         AccessFuTest.nextTest();
       } else {
         // Run all test functions synchronously.
         [testFunc() for (testFunc of gTestFuncs)]; // jshint ignore:line
         AccessFuTest.finish();
       }
new file mode 100644
--- /dev/null
+++ b/accessible/tests/mochitest/jsat/test_quicknav_modes.html
@@ -0,0 +1,103 @@
+<html>
+
+<head>
+  <title>AccessFu test for enabling</title>
+
+  <link rel="stylesheet" type="text/css"
+        href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript"
+          src="../common.js"></script>
+  <script type="application/javascript"
+          src="./jsatcommon.js"></script>
+  <script type="application/javascript">
+
+    function prefStart() {
+      // Start AccessFu via pref.
+      SpecialPowers.setIntPref("accessibility.accessfu.activate", 1);
+      AccessFuTest.once_log("EventManager.start", AccessFuTest.nextTest);
+    }
+
+    function nextMode(aCurrentMode, aNextMode) {
+      return function() {
+        is(AccessFu.Input.quickNavMode.current, aCurrentMode,
+          'initial current mode is correct');
+        AccessFu.Input.quickNavMode.next();
+        _expectMode(aNextMode, AccessFuTest.nextTest);
+      }
+    }
+
+    function prevMode(aCurrentMode, aNextMode) {
+      return function() {
+        is(AccessFu.Input.quickNavMode.current, aCurrentMode,
+          'initial current mode is correct');
+        AccessFu.Input.quickNavMode.previous();
+        _expectMode(aNextMode, AccessFuTest.nextTest);
+      }
+    }
+
+    function setMode(aModeIndex, aExpectedMode) {
+      return function() {
+        SpecialPowers.setIntPref(
+          'accessibility.accessfu.quicknav_index', aModeIndex);
+        _expectMode(aExpectedMode, AccessFuTest.nextTest);
+      }
+    }
+
+    function reconfigureModes() {
+      SpecialPowers.setCharPref('accessibility.accessfu.quicknav_modes',
+        'Landmark,Button,Entry,Graphic');
+      // When the modes are reconfigured, the current mode should
+      // be set to the first in the new list.
+      _expectMode('Landmark', AccessFuTest.nextTest);
+    }
+
+    function _expectMode(aExpectedMode, aCallback) {
+      if (AccessFu.Input.quickNavMode.current === aExpectedMode) {
+        ok(true, 'correct mode');
+        aCallback();
+      } else {
+        AccessFuTest.once_log('Quicknav mode: ' + aExpectedMode, function() {
+          ok(true, 'correct mode');
+          aCallback();
+        });
+      }
+    }
+
+    // Listen for initial 'EventManager.start' and disable AccessFu.
+    function prefStop() {
+      ok(AccessFu._enabled, "AccessFu was started via preference.");
+      AccessFuTest.once_log("EventManager.stop", AccessFuTest.finish);
+      SpecialPowers.setIntPref("accessibility.accessfu.activate", 0);
+    }
+
+    function doTest() {
+      AccessFuTest.addFunc(prefStart);
+      AccessFuTest.addFunc(nextMode('Link', 'Heading'));
+      AccessFuTest.addFunc(nextMode('Heading', 'FormElement'));
+      AccessFuTest.addFunc(nextMode('FormElement', 'Link'));
+      AccessFuTest.addFunc(nextMode('Link', 'Heading'));
+      AccessFuTest.addFunc(prevMode('Heading', 'Link'));
+      AccessFuTest.addFunc(prevMode('Link', 'FormElement'));
+      AccessFuTest.addFunc(setMode(1, 'Heading'));
+      AccessFuTest.addFunc(reconfigureModes);
+      AccessFuTest.addFunc(prefStop);
+      AccessFuTest.waitForExplicitFinish();
+      AccessFuTest.runTests([   // Will call SimpleTest.finish();
+        ['accessibility.accessfu.quicknav_modes', 'Link,Heading,FormElement']]);
+    }
+
+    SimpleTest.waitForExplicitFinish();
+    addA11yLoadEvent(doTest);
+  </script>
+
+</head>
+<body>
+  <a target="_blank"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=811307"
+     title="[AccessFu] Add mochitest for enabling">
+    Mozilla Bug 811307
+  </a>
+</body>
+</html>
\ No newline at end of file
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -789,16 +789,18 @@ pref("dom.ipc.systemMessageCPULockTimeou
 pref("dom.disable_window_open_dialog_feature", true);
 
 // Enable before keyboard events and after keyboard events.
 pref("dom.beforeAfterKeyboardEvent.enabled", true);
 
 // Screen reader support
 pref("accessibility.accessfu.activate", 2);
 pref("accessibility.accessfu.quicknav_modes", "Link,Heading,FormElement,Landmark,ListItem");
+// Active quicknav mode, index value of list from quicknav_modes
+pref("accessibility.accessfu.quicknav_index", 0);
 // Setting for an utterance order (0 - description first, 1 - description last).
 pref("accessibility.accessfu.utterance", 1);
 // Whether to skip images with empty alt text
 pref("accessibility.accessfu.skip_empty_images", true);
 
 // Enable hit-target fluffing
 pref("ui.touch.radius.enabled", true);
 pref("ui.touch.radius.leftmm", 3);
--- a/b2g/chrome/content/settings.js
+++ b/b2g/chrome/content/settings.js
@@ -452,16 +452,26 @@ SettingsListener.observe("theme.selected
     Services.prefs.setCharPref('dom.mozApps.selected_theme', newTheme);
     Services.prefs.savePrefFile(null);
     Services.obs.notifyObservers(null, 'app-theme-changed', newTheme);
   }
 });
 
 // =================== Various simple mapping  ======================
 let settingsToObserve = {
+  'accessibility.screenreader_quicknav_modes': {
+    prefName: 'accessibility.accessfu.quicknav_modes',
+    resetToPref: true,
+    defaultValue: ''
+  },
+  'accessibility.screenreader_quicknav_index': {
+    prefName: 'accessibility.accessfu.quicknav_index',
+    resetToPref: true,
+    defaultValue: 0
+  },
   'app.update.channel': {
     resetToPref: true
   },
   'app.update.interval': 86400,
   'app.update.url': {
     resetToPref: true
   },
   'apz.force-enable': {
--- a/mobile/android/app/mobile.js
+++ b/mobile/android/app/mobile.js
@@ -706,16 +706,18 @@ pref("ui.scrolling.negate_wheel_scrollY"
 // Determine the dead zone for gamepad joysticks. Higher values result in larger dead zones; use a negative value to
 // auto-detect based on reported hardware values
 pref("ui.scrolling.gamepad_dead_zone", 115);
 
 
 // Enable accessibility mode if platform accessibility is enabled.
 pref("accessibility.accessfu.activate", 2);
 pref("accessibility.accessfu.quicknav_modes", "Link,Heading,FormElement,Landmark,ListItem");
+// Active quicknav mode, index value of list from quicknav_modes
+pref("accessibility.accessfu.quicknav_index", 0);
 // Setting for an utterance order (0 - description first, 1 - description last).
 pref("accessibility.accessfu.utterance", 1);
 // Whether to skip images with empty alt text
 pref("accessibility.accessfu.skip_empty_images", true);
 
 // Transmit UDP busy-work to the LAN when anticipating low latency
 // network reads and on wifi to mitigate 802.11 Power Save Polling delays
 pref("network.tickle-wifi.enabled", true);