summaryrefslogtreecommitdiffstats
path: root/accessible/tests/mochitest/jsat/jsatcommon.js
diff options
context:
space:
mode:
Diffstat (limited to 'accessible/tests/mochitest/jsat/jsatcommon.js')
-rw-r--r--accessible/tests/mochitest/jsat/jsatcommon.js739
1 files changed, 739 insertions, 0 deletions
diff --git a/accessible/tests/mochitest/jsat/jsatcommon.js b/accessible/tests/mochitest/jsat/jsatcommon.js
new file mode 100644
index 000000000..aa7ee74e4
--- /dev/null
+++ b/accessible/tests/mochitest/jsat/jsatcommon.js
@@ -0,0 +1,739 @@
+// A common module to run tests on the AccessFu module
+
+'use strict';
+
+/*global isDeeply, getMainChromeWindow, SimpleTest, SpecialPowers, Logger,
+ AccessFu, Utils, addMessageListener, currentTabDocument, currentBrowser*/
+
+/**
+ * A global variable holding an array of test functions.
+ */
+var gTestFuncs = [];
+/**
+ * A global Iterator for the array of test functions.
+ */
+var gIterator;
+
+Components.utils.import('resource://gre/modules/Services.jsm');
+Components.utils.import("resource://gre/modules/accessibility/Utils.jsm");
+Components.utils.import("resource://gre/modules/accessibility/EventManager.jsm");
+Components.utils.import("resource://gre/modules/accessibility/Gestures.jsm");
+
+var AccessFuTest = {
+
+ addFunc: function AccessFuTest_addFunc(aFunc) {
+ if (aFunc) {
+ gTestFuncs.push(aFunc);
+ }
+ },
+
+ _registerListener: function AccessFuTest__registerListener(aWaitForMessage, aListenerFunc) {
+ var listener = {
+ observe: function observe(aMessage) {
+ // Ignore unexpected messages.
+ if (!(aMessage instanceof Components.interfaces.nsIConsoleMessage)) {
+ return;
+ }
+ if (aMessage.message.indexOf(aWaitForMessage) < 0) {
+ return;
+ }
+ aListenerFunc.apply(listener);
+ }
+ };
+ Services.console.registerListener(listener);
+ return listener;
+ },
+
+ on_log: function AccessFuTest_on_log(aWaitForMessage, aListenerFunc) {
+ return this._registerListener(aWaitForMessage, aListenerFunc);
+ },
+
+ off_log: function AccessFuTest_off_log(aListener) {
+ Services.console.unregisterListener(aListener);
+ },
+
+ once_log: function AccessFuTest_once_log(aWaitForMessage, aListenerFunc) {
+ return this._registerListener(aWaitForMessage,
+ function listenAndUnregister() {
+ Services.console.unregisterListener(this);
+ aListenerFunc();
+ });
+ },
+
+ _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, aWaitForData, "Data is correct");
+ aListener.apply(listener);
+ };
+ Services.obs.addObserver(listener, 'accessibility-output', false);
+ return listener;
+ },
+
+ on: function AccessFuTest_on(aWaitForData, aListener) {
+ return this._addObserver(aWaitForData, aListener);
+ },
+
+ off: function AccessFuTest_off(aListener) {
+ Services.obs.removeObserver(aListener, 'accessibility-output');
+ },
+
+ once: function AccessFuTest_once(aWaitForData, aListener) {
+ return this._addObserver(aWaitForData, function observerAndRemove() {
+ Services.obs.removeObserver(this, 'accessibility-output');
+ aListener();
+ });
+ },
+
+ _waitForExplicitFinish: false,
+
+ waitForExplicitFinish: function AccessFuTest_waitForExplicitFinish() {
+ this._waitForExplicitFinish = true;
+ },
+
+ finish: function AccessFuTest_finish() {
+ // Disable the console service logging.
+ Logger.test = false;
+ Logger.logLevel = Logger.INFO;
+ // Reset Gesture Settings.
+ GestureSettings.dwellThreshold = this.dwellThreshold =
+ this.originalDwellThreshold;
+ GestureSettings.swipeMaxDuration = this.swipeMaxDuration =
+ this.originalSwipeMaxDuration;
+ GestureSettings.maxGestureResolveTimeout =
+ this.maxGestureResolveTimeout =
+ this.originalMaxGestureResolveTimeout;
+ // Finish through idle callback to let AccessFu._disable complete.
+ SimpleTest.executeSoon(function () {
+ AccessFu.detach();
+ SimpleTest.finish();
+ });
+ },
+
+ nextTest: function AccessFuTest_nextTest() {
+ var result = gIterator.next();
+ if (result.done) {
+ this.finish();
+ return;
+ }
+ var testFunc = result.value;
+ testFunc();
+ },
+
+ runTests: function AccessFuTest_runTests(aAdditionalPrefs) {
+ if (gTestFuncs.length === 0) {
+ ok(false, "No tests specified!");
+ SimpleTest.finish();
+ return;
+ }
+
+ // Create an Iterator for gTestFuncs array.
+ gIterator = (function*() {
+ for (var testFunc of gTestFuncs) {
+ yield testFunc;
+ }
+ })();
+
+ // Start AccessFu and put it in stand-by.
+ Components.utils.import("resource://gre/modules/accessibility/AccessFu.jsm");
+
+ AccessFu.attach(getMainChromeWindow(window));
+
+ AccessFu.readyCallback = function readyCallback() {
+ // Enable logging to the console service.
+ Logger.test = true;
+ Logger.logLevel = Logger.DEBUG;
+ };
+
+ var prefs = [['accessibility.accessfu.notify_output', 1],
+ ['dom.mozSettings.enabled', true]];
+ prefs.push.apply(prefs, aAdditionalPrefs);
+
+ this.originalDwellThreshold = GestureSettings.dwellThreshold;
+ this.originalSwipeMaxDuration = GestureSettings.swipeMaxDuration;
+ this.originalMaxGestureResolveTimeout =
+ GestureSettings.maxGestureResolveTimeout;
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=1001945 - sometimes
+ // SimpleTest.executeSoon timeout is bigger than the timer settings in
+ // GestureSettings that causes intermittents.
+ this.dwellThreshold = GestureSettings.dwellThreshold =
+ GestureSettings.dwellThreshold * 10;
+ this.swipeMaxDuration = GestureSettings.swipeMaxDuration =
+ GestureSettings.swipeMaxDuration * 10;
+ this.maxGestureResolveTimeout = GestureSettings.maxGestureResolveTimeout =
+ GestureSettings.maxGestureResolveTimeout * 10;
+
+ SpecialPowers.pushPrefEnv({ 'set': prefs }, function () {
+ if (AccessFuTest._waitForExplicitFinish) {
+ // Run all test functions asynchronously.
+ AccessFuTest.nextTest();
+ } else {
+ // Run all test functions synchronously.
+ gTestFuncs.forEach(testFunc => testFunc());
+ AccessFuTest.finish();
+ }
+ });
+ }
+};
+
+function AccessFuContentTest(aFuncResultPairs) {
+ this.queue = aFuncResultPairs;
+}
+
+AccessFuContentTest.prototype = {
+ expected: [],
+ currentAction: null,
+ actionNum: -1,
+
+ start: function(aFinishedCallback) {
+ Logger.logLevel = Logger.DEBUG;
+ this.finishedCallback = aFinishedCallback;
+ var self = this;
+
+ // Get top content message manager, and set it up.
+ this.mms = [Utils.getMessageManager(currentBrowser())];
+ this.setupMessageManager(this.mms[0], function () {
+ // Get child message managers and set them up
+ var frames = currentTabDocument().querySelectorAll('iframe');
+ if (frames.length === 0) {
+ self.pump();
+ return;
+ }
+
+ var toSetup = 0;
+ for (var i = 0; i < frames.length; i++ ) {
+ var mm = Utils.getMessageManager(frames[i]);
+ if (mm) {
+ toSetup++;
+ self.mms.push(mm);
+ self.setupMessageManager(mm, function () {
+ if (--toSetup === 0) {
+ // All message managers are loaded and ready to go.
+ self.pump();
+ }
+ });
+ }
+ }
+ });
+ },
+
+ finish: function() {
+ Logger.logLevel = Logger.INFO;
+ for (var mm of this.mms) {
+ mm.sendAsyncMessage('AccessFu:Stop');
+ mm.removeMessageListener('AccessFu:Present', this);
+ mm.removeMessageListener('AccessFu:Input', this);
+ mm.removeMessageListener('AccessFu:CursorCleared', this);
+ mm.removeMessageListener('AccessFu:Focused', this);
+ mm.removeMessageListener('AccessFu:AriaHidden', this);
+ mm.removeMessageListener('AccessFu:Ready', this);
+ mm.removeMessageListener('AccessFu:ContentStarted', this);
+ }
+ if (this.finishedCallback) {
+ this.finishedCallback();
+ }
+ },
+
+ setupMessageManager: function (aMessageManager, aCallback) {
+ function contentScript() {
+ addMessageListener('AccessFuTest:Focus', function (aMessage) {
+ var elem = content.document.querySelector(aMessage.json.selector);
+ if (elem) {
+ if (aMessage.json.blur) {
+ elem.blur();
+ } else {
+ elem.focus();
+ }
+ }
+ });
+ }
+
+ aMessageManager.addMessageListener('AccessFu:Present', this);
+ aMessageManager.addMessageListener('AccessFu:Input', this);
+ aMessageManager.addMessageListener('AccessFu:CursorCleared', this);
+ aMessageManager.addMessageListener('AccessFu:Focused', this);
+ aMessageManager.addMessageListener('AccessFu:AriaHidden', this);
+ aMessageManager.addMessageListener('AccessFu:Ready', function () {
+ aMessageManager.addMessageListener('AccessFu:ContentStarted', aCallback);
+ aMessageManager.sendAsyncMessage('AccessFu:Start',
+ { buildApp: 'browser',
+ androidSdkVersion: Utils.AndroidSdkVersion,
+ logLevel: 'DEBUG',
+ inTest: true });
+ });
+
+ aMessageManager.loadFrameScript(
+ 'chrome://global/content/accessibility/content-script.js', false);
+ aMessageManager.loadFrameScript(
+ 'data:,(' + contentScript.toString() + ')();', false);
+ },
+
+ pump: function() {
+ this.expected.shift();
+ if (this.expected.length) {
+ return;
+ }
+
+ var currentPair = this.queue.shift();
+
+ if (currentPair) {
+ this.actionNum++;
+ this.currentAction = currentPair[0];
+ if (typeof this.currentAction === 'function') {
+ this.currentAction(this.mms[0]);
+ } else if (this.currentAction) {
+ this.mms[0].sendAsyncMessage(this.currentAction.name,
+ this.currentAction.json);
+ }
+
+ this.expected = currentPair.slice(1, currentPair.length);
+
+ if (!this.expected[0]) {
+ this.pump();
+ }
+ } else {
+ this.finish();
+ }
+ },
+
+ receiveMessage: function(aMessage) {
+ var expected = this.expected[0];
+
+ if (!expected) {
+ return;
+ }
+
+ var actionsString = typeof this.currentAction === 'function' ?
+ this.currentAction.name + '()' : JSON.stringify(this.currentAction);
+
+ if (typeof expected === 'string') {
+ ok(true, 'Got ' + expected + ' after ' + actionsString);
+ this.pump();
+ } else if (expected.ignore && !expected.ignore(aMessage)) {
+ expected.is(aMessage.json, 'after ' + actionsString +
+ ' (' + this.actionNum + ')');
+ expected.is_correct_focus();
+ this.pump();
+ }
+ }
+};
+
+// Common content messages
+
+var ContentMessages = {
+ simpleMoveFirst: {
+ name: 'AccessFu:MoveCursor',
+ json: {
+ action: 'moveFirst',
+ rule: 'Simple',
+ inputType: 'gesture',
+ origin: 'top'
+ }
+ },
+
+ simpleMoveLast: {
+ name: 'AccessFu:MoveCursor',
+ json: {
+ action: 'moveLast',
+ rule: 'Simple',
+ inputType: 'gesture',
+ origin: 'top'
+ }
+ },
+
+ simpleMoveNext: {
+ name: 'AccessFu:MoveCursor',
+ json: {
+ action: 'moveNext',
+ rule: 'Simple',
+ inputType: 'gesture',
+ origin: 'top'
+ }
+ },
+
+ simpleMovePrevious: {
+ name: 'AccessFu:MoveCursor',
+ json: {
+ action: 'movePrevious',
+ rule: 'Simple',
+ inputType: 'gesture',
+ origin: 'top'
+ }
+ },
+
+ clearCursor: {
+ name: 'AccessFu:ClearCursor',
+ json: {
+ origin: 'top'
+ }
+ },
+
+ moveOrAdjustUp: function moveOrAdjustUp(aRule) {
+ return {
+ name: 'AccessFu:MoveCursor',
+ json: {
+ origin: 'top',
+ action: 'movePrevious',
+ inputType: 'gesture',
+ rule: (aRule || 'Simple'),
+ adjustRange: true
+ }
+ }
+ },
+
+ moveOrAdjustDown: function moveOrAdjustUp(aRule) {
+ return {
+ name: 'AccessFu:MoveCursor',
+ json: {
+ origin: 'top',
+ action: 'moveNext',
+ inputType: 'gesture',
+ rule: (aRule || 'Simple'),
+ adjustRange: true
+ }
+ }
+ },
+
+ androidScrollForward: function adjustUp() {
+ return {
+ name: 'AccessFu:AndroidScroll',
+ json: { origin: 'top', direction: 'forward' }
+ };
+ },
+
+ androidScrollBackward: function adjustDown() {
+ return {
+ name: 'AccessFu:AndroidScroll',
+ json: { origin: 'top', direction: 'backward' }
+ };
+ },
+
+ focusSelector: function focusSelector(aSelector, aBlur) {
+ return {
+ name: 'AccessFuTest:Focus',
+ json: {
+ selector: aSelector,
+ blur: aBlur
+ }
+ };
+ },
+
+ activateCurrent: function activateCurrent(aOffset) {
+ return {
+ name: 'AccessFu:Activate',
+ json: {
+ origin: 'top',
+ offset: aOffset
+ }
+ };
+ },
+
+ moveNextBy: function moveNextBy(aGranularity) {
+ return {
+ name: 'AccessFu:MoveByGranularity',
+ json: {
+ direction: 'Next',
+ granularity: this._granularityMap[aGranularity]
+ }
+ };
+ },
+
+ movePreviousBy: function movePreviousBy(aGranularity) {
+ return {
+ name: 'AccessFu:MoveByGranularity',
+ json: {
+ direction: 'Previous',
+ granularity: this._granularityMap[aGranularity]
+ }
+ };
+ },
+
+ moveCaretNextBy: function moveCaretNextBy(aGranularity) {
+ return {
+ name: 'AccessFu:MoveCaret',
+ json: {
+ direction: 'Next',
+ granularity: this._granularityMap[aGranularity]
+ }
+ };
+ },
+
+ moveCaretPreviousBy: function moveCaretPreviousBy(aGranularity) {
+ return {
+ name: 'AccessFu:MoveCaret',
+ json: {
+ direction: 'Previous',
+ granularity: this._granularityMap[aGranularity]
+ }
+ };
+ },
+
+ _granularityMap: {
+ 'character': 1, // MOVEMENT_GRANULARITY_CHARACTER
+ 'word': 2, // MOVEMENT_GRANULARITY_WORD
+ 'paragraph': 8 // MOVEMENT_GRANULARITY_PARAGRAPH
+ }
+};
+
+function ExpectedMessage (aName, aOptions) {
+ this.name = aName;
+ this.options = aOptions || {};
+ this.json = {};
+}
+
+ExpectedMessage.prototype.lazyCompare = function(aReceived, aExpected, aInfo) {
+ if (aExpected && !aReceived) {
+ return [false, 'Expected something but got nothing -- ' + aInfo];
+ }
+
+ var matches = true;
+ var delta = [];
+ for (var attr in aExpected) {
+ var expected = aExpected[attr];
+ var received = aReceived[attr];
+ if (typeof expected === 'object') {
+ var [childMatches, childDelta] = this.lazyCompare(received, expected);
+ if (!childMatches) {
+ delta.push(attr + ' [ ' + childDelta + ' ]');
+ matches = false;
+ }
+ } else {
+ if (received !== expected) {
+ delta.push(
+ attr + ' [ expected ' + JSON.stringify(expected) +
+ ' got ' + JSON.stringify(received) + ' ]');
+ matches = false;
+ }
+ }
+ }
+
+ var msg = delta.length ? delta.join(' ') : 'Structures lazily match';
+ return [matches, msg + ' -- ' + aInfo];
+};
+
+ExpectedMessage.prototype.is = function(aReceived, aInfo) {
+ var checkFunc = this.options.todo ? 'todo' : 'ok';
+ SimpleTest[checkFunc].apply(
+ SimpleTest, this.lazyCompare(aReceived, this.json, aInfo));
+};
+
+ExpectedMessage.prototype.is_correct_focus = function(aInfo) {
+ if (!this.options.focused) {
+ return;
+ }
+
+ var checkFunc = this.options.focused_todo ? 'todo_is' : 'is';
+ var doc = currentTabDocument();
+ SimpleTest[checkFunc].apply(SimpleTest,
+ [ doc.activeElement, doc.querySelector(this.options.focused),
+ 'Correct element is focused: ' + this.options.focused + ' -- ' + aInfo ]);
+};
+
+ExpectedMessage.prototype.ignore = function(aMessage) {
+ return aMessage.name !== this.name;
+};
+
+function ExpectedPresent(aB2g, aAndroid, aOptions) {
+ ExpectedMessage.call(this, 'AccessFu:Present', aOptions);
+ if (aB2g) {
+ this.json.b2g = aB2g;
+ }
+
+ if (aAndroid) {
+ this.json.android = aAndroid;
+ }
+}
+
+ExpectedPresent.prototype = Object.create(ExpectedMessage.prototype);
+
+ExpectedPresent.prototype.is = function(aReceived, aInfo) {
+ var received = this.extract_presenters(aReceived);
+
+ for (var presenter of ['b2g', 'android']) {
+ if (!this.options['no_' + presenter]) {
+ var todo = this.options.todo || this.options[presenter + '_todo']
+ SimpleTest[todo ? 'todo' : 'ok'].apply(
+ SimpleTest, this.lazyCompare(received[presenter],
+ this.json[presenter], aInfo + ' (' + presenter + ')'));
+ }
+ }
+};
+
+ExpectedPresent.prototype.extract_presenters = function(aReceived) {
+ var received = { count: 0 };
+ for (var presenter of aReceived) {
+ if (presenter) {
+ received[presenter.type.toLowerCase()] = presenter.details;
+ received.count++;
+ }
+ }
+
+ return received
+};
+
+ExpectedPresent.prototype.ignore = function(aMessage) {
+ if (ExpectedMessage.prototype.ignore.call(this, aMessage)) {
+ return true;
+ }
+
+ var received = this.extract_presenters(aMessage.json);
+ return received.count === 0 ||
+ (received.visual && received.visual.eventType === 'viewport-change') ||
+ (received.android &&
+ received.android[0].eventType === AndroidEvent.VIEW_SCROLLED);
+};
+
+function ExpectedCursorChange(aSpeech, aOptions) {
+ ExpectedPresent.call(this, {
+ eventType: 'vc-change',
+ data: aSpeech
+ }, [{
+ eventType: 0x8000, // VIEW_ACCESSIBILITY_FOCUSED
+ }], aOptions);
+}
+
+ExpectedCursorChange.prototype = Object.create(ExpectedPresent.prototype);
+
+function ExpectedCursorTextChange(aSpeech, aStartOffset, aEndOffset, aOptions) {
+ ExpectedPresent.call(this, {
+ eventType: 'vc-change',
+ data: aSpeech
+ }, [{
+ eventType: AndroidEvent.VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY,
+ fromIndex: aStartOffset,
+ toIndex: aEndOffset
+ }], aOptions);
+
+ // bug 980509
+ this.options.b2g_todo = true;
+}
+
+ExpectedCursorTextChange.prototype =
+ Object.create(ExpectedCursorChange.prototype);
+
+function ExpectedClickAction(aOptions) {
+ ExpectedPresent.call(this, {
+ eventType: 'action',
+ data: [{ string: 'clickAction' }]
+ }, [{
+ eventType: AndroidEvent.VIEW_CLICKED
+ }], aOptions);
+}
+
+ExpectedClickAction.prototype = Object.create(ExpectedPresent.prototype);
+
+function ExpectedCheckAction(aChecked, aOptions) {
+ ExpectedPresent.call(this, {
+ eventType: 'action',
+ data: [{ string: aChecked ? 'checkAction' : 'uncheckAction' }]
+ }, [{
+ eventType: AndroidEvent.VIEW_CLICKED,
+ checked: aChecked
+ }], aOptions);
+}
+
+ExpectedCheckAction.prototype = Object.create(ExpectedPresent.prototype);
+
+function ExpectedSwitchAction(aSwitched, aOptions) {
+ ExpectedPresent.call(this, {
+ eventType: 'action',
+ data: [{ string: aSwitched ? 'onAction' : 'offAction' }]
+ }, [{
+ eventType: AndroidEvent.VIEW_CLICKED,
+ checked: aSwitched
+ }], aOptions);
+}
+
+ExpectedSwitchAction.prototype = Object.create(ExpectedPresent.prototype);
+
+function ExpectedNameChange(aName, aOptions) {
+ ExpectedPresent.call(this, {
+ eventType: 'name-change',
+ data: aName
+ }, null, aOptions);
+}
+
+ExpectedNameChange.prototype = Object.create(ExpectedPresent.prototype);
+
+function ExpectedValueChange(aValue, aOptions) {
+ ExpectedPresent.call(this, {
+ eventType: 'value-change',
+ data: aValue
+ }, null, aOptions);
+}
+
+ExpectedValueChange.prototype = Object.create(ExpectedPresent.prototype);
+
+function ExpectedTextChanged(aValue, aOptions) {
+ ExpectedPresent.call(this, {
+ eventType: 'text-change',
+ data: aValue
+ }, null, aOptions);
+}
+
+ExpectedTextChanged.prototype = Object.create(ExpectedPresent.prototype);
+
+function ExpectedEditState(aEditState, aOptions) {
+ ExpectedMessage.call(this, 'AccessFu:Input', aOptions);
+ this.json = aEditState;
+}
+
+ExpectedEditState.prototype = Object.create(ExpectedMessage.prototype);
+
+function ExpectedTextSelectionChanged(aStart, aEnd, aOptions) {
+ ExpectedPresent.call(this, null, [{
+ eventType: AndroidEvent.VIEW_TEXT_SELECTION_CHANGED,
+ brailleOutput: {
+ selectionStart: aStart,
+ selectionEnd: aEnd
+ }}], aOptions);
+}
+
+ExpectedTextSelectionChanged.prototype =
+ Object.create(ExpectedPresent.prototype);
+
+function ExpectedTextCaretChanged(aFrom, aTo, aOptions) {
+ ExpectedPresent.call(this, null, [{
+ eventType: AndroidEvent.VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY,
+ fromIndex: aFrom,
+ toIndex: aTo
+ }], aOptions);
+}
+
+ExpectedTextCaretChanged.prototype = Object.create(ExpectedPresent.prototype);
+
+function ExpectedAnnouncement(aAnnouncement, aOptions) {
+ ExpectedPresent.call(this, null, [{
+ eventType: AndroidEvent.ANNOUNCEMENT,
+ text: [ aAnnouncement],
+ addedCount: aAnnouncement.length
+ }], aOptions);
+}
+
+ExpectedAnnouncement.prototype = Object.create(ExpectedPresent.prototype);
+
+function ExpectedNoMove(aOptions) {
+ ExpectedPresent.call(this, {eventType: 'no-move' }, null, aOptions);
+}
+
+ExpectedNoMove.prototype = Object.create(ExpectedPresent.prototype);
+
+var AndroidEvent = {
+ VIEW_CLICKED: 0x01,
+ VIEW_LONG_CLICKED: 0x02,
+ VIEW_SELECTED: 0x04,
+ VIEW_FOCUSED: 0x08,
+ VIEW_TEXT_CHANGED: 0x10,
+ WINDOW_STATE_CHANGED: 0x20,
+ VIEW_HOVER_ENTER: 0x80,
+ VIEW_HOVER_EXIT: 0x100,
+ VIEW_SCROLLED: 0x1000,
+ VIEW_TEXT_SELECTION_CHANGED: 0x2000,
+ ANNOUNCEMENT: 0x4000,
+ VIEW_ACCESSIBILITY_FOCUSED: 0x8000,
+ VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY: 0x20000
+};