accessible/tests/mochitest/jsat/jsatcommon.js

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

     1 // A common module to run tests on the AccessFu module
     3 'use strict';
     5 /*global isDeeply, getMainChromeWindow, SimpleTest, SpecialPowers, Logger,
     6   AccessFu, Utils, addMessageListener, currentTabDocument, currentBrowser*/
     8 /**
     9   * A global variable holding an array of test functions.
    10   */
    11 var gTestFuncs = [];
    12 /**
    13   * A global Iterator for the array of test functions.
    14   */
    15 var gIterator;
    17 Components.utils.import('resource://gre/modules/Services.jsm');
    18 Components.utils.import("resource://gre/modules/accessibility/Utils.jsm");
    19 Components.utils.import("resource://gre/modules/accessibility/EventManager.jsm");
    20 Components.utils.import("resource://gre/modules/accessibility/Gestures.jsm");
    22 const dwellThreshold = GestureSettings.dwellThreshold;
    23 const swipeMaxDuration = GestureSettings.swipeMaxDuration;
    24 const maxConsecutiveGestureDelay = GestureSettings.maxConsecutiveGestureDelay;
    26 // https://bugzilla.mozilla.org/show_bug.cgi?id=1001945 - sometimes
    27 // SimpleTest.executeSoon timeout is bigger than the timer settings in
    28 // GestureSettings that causes intermittents.
    29 GestureSettings.dwellThreshold = dwellThreshold * 10;
    30 GestureSettings.swipeMaxDuration = swipeMaxDuration * 10;
    31 GestureSettings.maxConsecutiveGestureDelay = maxConsecutiveGestureDelay * 10;
    33 var AccessFuTest = {
    35   addFunc: function AccessFuTest_addFunc(aFunc) {
    36     if (aFunc) {
    37       gTestFuncs.push(aFunc);
    38     }
    39   },
    41   _registerListener: function AccessFuTest__registerListener(aWaitForMessage, aListenerFunc) {
    42     var listener = {
    43       observe: function observe(aMessage) {
    44         // Ignore unexpected messages.
    45         if (!(aMessage instanceof Components.interfaces.nsIConsoleMessage)) {
    46           return;
    47         }
    48         if (aMessage.message.indexOf(aWaitForMessage) < 0) {
    49           return;
    50         }
    51         aListenerFunc.apply(listener);
    52       }
    53     };
    54     Services.console.registerListener(listener);
    55     return listener;
    56   },
    58   on_log: function AccessFuTest_on_log(aWaitForMessage, aListenerFunc) {
    59     return this._registerListener(aWaitForMessage, aListenerFunc);
    60   },
    62   off_log: function AccessFuTest_off_log(aListener) {
    63     Services.console.unregisterListener(aListener);
    64   },
    66   once_log: function AccessFuTest_once_log(aWaitForMessage, aListenerFunc) {
    67     return this._registerListener(aWaitForMessage,
    68       function listenAndUnregister() {
    69         Services.console.unregisterListener(this);
    70         aListenerFunc();
    71       });
    72   },
    74   _addObserver: function AccessFuTest__addObserver(aWaitForData, aListener) {
    75     var listener = function listener(aSubject, aTopic, aData) {
    76       var data = JSON.parse(aData)[1];
    77       // Ignore non-relevant outputs.
    78       if (!data) {
    79         return;
    80       }
    81       isDeeply(data.details.actions, aWaitForData, "Data is correct");
    82       aListener.apply(listener);
    83     };
    84     Services.obs.addObserver(listener, 'accessfu-output', false);
    85     return listener;
    86   },
    88   on: function AccessFuTest_on(aWaitForData, aListener) {
    89     return this._addObserver(aWaitForData, aListener);
    90   },
    92   off: function AccessFuTest_off(aListener) {
    93     Services.obs.removeObserver(aListener, 'accessfu-output');
    94   },
    96   once: function AccessFuTest_once(aWaitForData, aListener) {
    97     return this._addObserver(aWaitForData, function observerAndRemove() {
    98       Services.obs.removeObserver(this, 'accessfu-output');
    99       aListener();
   100     });
   101   },
   103   _waitForExplicitFinish: false,
   105   waitForExplicitFinish: function AccessFuTest_waitForExplicitFinish() {
   106     this._waitForExplicitFinish = true;
   107   },
   109   finish: function AccessFuTest_finish() {
   110     // Disable the console service logging.
   111     Logger.test = false;
   112     Logger.logLevel = Logger.INFO;
   113     // Reset Gesture Settings.
   114     GestureSettings.dwellThreshold = dwellThreshold;
   115     GestureSettings.swipeMaxDuration = swipeMaxDuration;
   116     GestureSettings.maxConsecutiveGestureDelay = maxConsecutiveGestureDelay;
   117     // Finish through idle callback to let AccessFu._disable complete.
   118     SimpleTest.executeSoon(function () {
   119       AccessFu.detach();
   120       SimpleTest.finish();
   121     });
   122   },
   124   nextTest: function AccessFuTest_nextTest() {
   125     var testFunc;
   126     try {
   127       // Get the next test function from the iterator. If none left,
   128       // StopIteration exception is thrown.
   129       testFunc = gIterator.next()[1];
   130     } catch (ex) {
   131       // StopIteration exception.
   132       this.finish();
   133       return;
   134     }
   135     testFunc();
   136   },
   138   runTests: function AccessFuTest_runTests() {
   139     if (gTestFuncs.length === 0) {
   140       ok(false, "No tests specified!");
   141       SimpleTest.finish();
   142       return;
   143     }
   145     // Create an Iterator for gTestFuncs array.
   146     gIterator = Iterator(gTestFuncs); // jshint ignore:line
   148     // Start AccessFu and put it in stand-by.
   149     Components.utils.import("resource://gre/modules/accessibility/AccessFu.jsm");
   151     AccessFu.attach(getMainChromeWindow(window));
   153     AccessFu.readyCallback = function readyCallback() {
   154       // Enable logging to the console service.
   155       Logger.test = true;
   156       Logger.logLevel = Logger.DEBUG;
   157     };
   159     SpecialPowers.pushPrefEnv({
   160       'set': [['accessibility.accessfu.notify_output', 1],
   161               ['dom.mozSettings.enabled', true]]
   162     }, function () {
   163       if (AccessFuTest._waitForExplicitFinish) {
   164         // Run all test functions asynchronously.
   165         AccessFuTest.nextTest();
   166       } else {
   167         // Run all test functions synchronously.
   168         [testFunc() for (testFunc of gTestFuncs)]; // jshint ignore:line
   169         AccessFuTest.finish();
   170       }
   171     });
   172   }
   173 };
   175 function AccessFuContentTest(aFuncResultPairs) {
   176   this.queue = aFuncResultPairs;
   177 }
   179 AccessFuContentTest.prototype = {
   180   currentPair: null,
   182   start: function(aFinishedCallback) {
   183     Logger.logLevel = Logger.DEBUG;
   184     this.finishedCallback = aFinishedCallback;
   185     var self = this;
   187     // Get top content message manager, and set it up.
   188     this.mms = [Utils.getMessageManager(currentBrowser())];
   189     this.setupMessageManager(this.mms[0], function () {
   190       // Get child message managers and set them up
   191       var frames = currentTabDocument().querySelectorAll('iframe');
   192       if (frames.length === 0) {
   193         self.pump();
   194         return;
   195       }
   197       var toSetup = 0;
   198       for (var i = 0; i < frames.length; i++ ) {
   199         var mm = Utils.getMessageManager(frames[i]);
   200         if (mm) {
   201           toSetup++;
   202           self.mms.push(mm);
   203           self.setupMessageManager(mm, function () {
   204             if (--toSetup === 0) {
   205               // All message managers are loaded and ready to go.
   206               self.pump();
   207             }
   208           });
   209         }
   210       }
   211     });
   212   },
   214   finish: function() {
   215     Logger.logLevel = Logger.INFO;
   216     for (var mm of this.mms) {
   217         mm.sendAsyncMessage('AccessFu:Stop');
   218       }
   219     if (this.finishedCallback) {
   220       this.finishedCallback();
   221     }
   222   },
   224   setupMessageManager:  function (aMessageManager, aCallback) {
   225     function contentScript() {
   226       addMessageListener('AccessFuTest:Focus', function (aMessage) {
   227         var elem = content.document.querySelector(aMessage.json.selector);
   228         if (elem) {
   229           if (aMessage.json.blur) {
   230             elem.blur();
   231           } else {
   232             elem.focus();
   233           }
   234         }
   235       });
   236     }
   238     aMessageManager.addMessageListener('AccessFu:Present', this);
   239     aMessageManager.addMessageListener('AccessFu:CursorCleared', this);
   240     aMessageManager.addMessageListener('AccessFu:Ready', function () {
   241       aMessageManager.addMessageListener('AccessFu:ContentStarted', aCallback);
   242       aMessageManager.sendAsyncMessage('AccessFu:Start',
   243         { buildApp: 'browser',
   244           androidSdkVersion: Utils.AndroidSdkVersion,
   245           logLevel: 'DEBUG' });
   246     });
   248     aMessageManager.loadFrameScript(
   249       'chrome://global/content/accessibility/content-script.js', false);
   250     aMessageManager.loadFrameScript(
   251       'data:,(' + contentScript.toString() + ')();', false);
   252   },
   254   pump: function() {
   255     this.currentPair = this.queue.shift();
   257     if (this.currentPair) {
   258       if (this.currentPair[0] instanceof Function) {
   259         this.currentPair[0](this.mms[0]);
   260       } else if (this.currentPair[0]) {
   261         this.mms[0].sendAsyncMessage(this.currentPair[0].name,
   262          this.currentPair[0].json);
   263       }
   265       if (!this.currentPair[1]) {
   266        this.pump();
   267      }
   268     } else {
   269       this.finish();
   270     }
   271   },
   273   receiveMessage: function(aMessage) {
   274     if (!this.currentPair) {
   275       return;
   276     }
   278     var expected = this.currentPair[1] || {};
   280     // |expected| can simply be a name of a message, no more further testing.
   281     if (aMessage.name === expected) {
   282       ok(true, 'Received ' + expected);
   283       this.pump();
   284       return;
   285     }
   287     var speech = this.extractUtterance(aMessage.json);
   288     var android = this.extractAndroid(aMessage.json, expected.android);
   289     if ((speech && expected.speak) || (android && expected.android)) {
   290       if (expected.speak) {
   291         (SimpleTest[expected.speak_checkFunc] || is)(speech, expected.speak,
   292           '"' + speech + '" spoken');
   293       }
   295       if (expected.android) {
   296         var checkFunc = SimpleTest[expected.android_checkFunc] || ok;
   297         checkFunc.apply(SimpleTest,
   298           this.lazyCompare(android, expected.android));
   299       }
   301       this.pump();
   302     }
   304   },
   306   lazyCompare: function lazyCompare(aReceived, aExpected) {
   307     var matches = true;
   308     var delta = [];
   309     for (var attr in aExpected) {
   310       var expected = aExpected[attr];
   311       var received = aReceived !== undefined ? aReceived[attr] : null;
   312       if (typeof expected === 'object') {
   313         var [childMatches, childDelta] = this.lazyCompare(received, expected);
   314         if (!childMatches) {
   315           delta.push(attr + ' [ ' + childDelta + ' ]');
   316           matches = false;
   317         }
   318       } else {
   319         if (received !== expected) {
   320           delta.push(
   321             attr + ' [ expected ' + expected + ' got ' + received + ' ]');
   322           matches = false;
   323         }
   324       }
   325     }
   326     return [matches, delta.join(' ')];
   327   },
   329   extractUtterance: function(aData) {
   330     if (!aData) {
   331       return null;
   332     }
   334     for (var output of aData) {
   335       if (output && output.type === 'Speech') {
   336         for (var action of output.details.actions) {
   337           if (action && action.method == 'speak') {
   338             return action.data;
   339           }
   340         }
   341       }
   342     }
   344     return null;
   345   },
   347   extractAndroid: function(aData, aExpectedEvents) {
   348     for (var output of aData) {
   349       if (output && output.type === 'Android') {
   350         for (var i in output.details) {
   351           // Only extract if event types match expected event types.
   352           var exp = aExpectedEvents ? aExpectedEvents[i] : null;
   353           if (!exp || (output.details[i].eventType !== exp.eventType)) {
   354             return null;
   355           }
   356         }
   357         return output.details;
   358       }
   359     }
   361     return null;
   362   }
   363 };
   365 // Common content messages
   367 var ContentMessages = {
   368   simpleMoveFirst: {
   369     name: 'AccessFu:MoveCursor',
   370     json: {
   371       action: 'moveFirst',
   372       rule: 'Simple',
   373       inputType: 'gesture',
   374       origin: 'top'
   375     }
   376   },
   378   simpleMoveLast: {
   379     name: 'AccessFu:MoveCursor',
   380     json: {
   381       action: 'moveLast',
   382       rule: 'Simple',
   383       inputType: 'gesture',
   384       origin: 'top'
   385     }
   386   },
   388   simpleMoveNext: {
   389     name: 'AccessFu:MoveCursor',
   390     json: {
   391       action: 'moveNext',
   392       rule: 'Simple',
   393       inputType: 'gesture',
   394       origin: 'top'
   395     }
   396   },
   398   simpleMovePrevious: {
   399     name: 'AccessFu:MoveCursor',
   400     json: {
   401       action: 'movePrevious',
   402       rule: 'Simple',
   403       inputType: 'gesture',
   404       origin: 'top'
   405     }
   406   },
   408   clearCursor: {
   409     name: 'AccessFu:ClearCursor',
   410     json: {
   411       origin: 'top'
   412     }
   413   },
   415   adjustRangeUp: {
   416     name: 'AccessFu:AdjustRange',
   417     json: {
   418       origin: 'top',
   419       direction: 'backward'
   420     }
   421   },
   423   adjustRangeDown: {
   424     name: 'AccessFu:AdjustRange',
   425     json: {
   426       origin: 'top',
   427       direction: 'forward'
   428     }
   429   },
   431   focusSelector: function focusSelector(aSelector, aBlur) {
   432     return {
   433       name: 'AccessFuTest:Focus',
   434       json: {
   435         selector: aSelector,
   436         blur: aBlur
   437       }
   438     };
   439   },
   441   activateCurrent: function activateCurrent(aOffset) {
   442     return {
   443       name: 'AccessFu:Activate',
   444       json: {
   445         origin: 'top',
   446         offset: aOffset
   447       }
   448     };
   449   },
   451   moveNextBy: function moveNextBy(aGranularity) {
   452     return {
   453       name: 'AccessFu:MoveByGranularity',
   454       json: {
   455         direction: 'Next',
   456         granularity: this._granularityMap[aGranularity]
   457       }
   458     };
   459   },
   461   movePreviousBy: function movePreviousBy(aGranularity) {
   462     return {
   463       name: 'AccessFu:MoveByGranularity',
   464       json: {
   465         direction: 'Previous',
   466         granularity: this._granularityMap[aGranularity]
   467       }
   468     };
   469   },
   471   moveCaretNextBy: function moveCaretNextBy(aGranularity) {
   472     return {
   473       name: 'AccessFu:MoveCaret',
   474       json: {
   475         direction: 'Next',
   476         granularity: this._granularityMap[aGranularity]
   477       }
   478     };
   479   },
   481   moveCaretPreviousBy: function moveCaretPreviousBy(aGranularity) {
   482     return {
   483       name: 'AccessFu:MoveCaret',
   484       json: {
   485         direction: 'Previous',
   486         granularity: this._granularityMap[aGranularity]
   487       }
   488     };
   489   },
   491   _granularityMap: {
   492     'character': 1, // MOVEMENT_GRANULARITY_CHARACTER
   493     'word': 2, // MOVEMENT_GRANULARITY_WORD
   494     'paragraph': 8 // MOVEMENT_GRANULARITY_PARAGRAPH
   495   }
   496 };
   498 var AndroidEvent = {
   499   VIEW_CLICKED: 0x01,
   500   VIEW_LONG_CLICKED: 0x02,
   501   VIEW_SELECTED: 0x04,
   502   VIEW_FOCUSED: 0x08,
   503   VIEW_TEXT_CHANGED: 0x10,
   504   WINDOW_STATE_CHANGED: 0x20,
   505   VIEW_HOVER_ENTER: 0x80,
   506   VIEW_HOVER_EXIT: 0x100,
   507   VIEW_SCROLLED: 0x1000,
   508   VIEW_TEXT_SELECTION_CHANGED: 0x2000,
   509   ANNOUNCEMENT: 0x4000,
   510   VIEW_ACCESSIBILITY_FOCUSED: 0x8000,
   511   VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY: 0x20000
   512 };

mercurial