123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113 |
- /* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtain one at http://mozilla.org/MPL/2.0/. */
- /* global Components, XPCOMUtils, Services, PluralForm, Logger, Rect, Utils,
- States, Relations, Roles, dump, Events, PivotContext, PrefCache */
- /* exported Utils, Logger, PivotContext, PrefCache, SettingCache */
- 'use strict';
- const {classes: Cc, utils: Cu, interfaces: Ci} = Components;
- Cu.import('resource://gre/modules/XPCOMUtils.jsm');
- XPCOMUtils.defineLazyModuleGetter(this, 'Services', // jshint ignore:line
- 'resource://gre/modules/Services.jsm');
- XPCOMUtils.defineLazyModuleGetter(this, 'Rect', // jshint ignore:line
- 'resource://gre/modules/Geometry.jsm');
- XPCOMUtils.defineLazyModuleGetter(this, 'Roles', // jshint ignore:line
- 'resource://gre/modules/accessibility/Constants.jsm');
- XPCOMUtils.defineLazyModuleGetter(this, 'Events', // jshint ignore:line
- 'resource://gre/modules/accessibility/Constants.jsm');
- XPCOMUtils.defineLazyModuleGetter(this, 'Relations', // jshint ignore:line
- 'resource://gre/modules/accessibility/Constants.jsm');
- XPCOMUtils.defineLazyModuleGetter(this, 'States', // jshint ignore:line
- 'resource://gre/modules/accessibility/Constants.jsm');
- XPCOMUtils.defineLazyModuleGetter(this, 'PluralForm', // jshint ignore:line
- 'resource://gre/modules/PluralForm.jsm');
- this.EXPORTED_SYMBOLS = ['Utils', 'Logger', 'PivotContext', 'PrefCache', // jshint ignore:line
- 'SettingCache'];
- this.Utils = { // jshint ignore:line
- _buildAppMap: {
- '{3c2e2abc-06d4-11e1-ac3b-374f68613e61}': 'b2g',
- '{d1bfe7d9-c01e-4237-998b-7b5f960a4314}': 'graphene',
- '{ec8030f7-c20a-464f-9b0e-13a3a9e97384}': 'browser'
- },
- init: function Utils_init(aWindow) {
- if (this._win) {
- // XXX: only supports attaching to one window now.
- throw new Error('Only one top-level window could used with AccessFu');
- }
- this._win = Cu.getWeakReference(aWindow);
- },
- uninit: function Utils_uninit() {
- if (!this._win) {
- return;
- }
- delete this._win;
- },
- get win() {
- if (!this._win) {
- return null;
- }
- return this._win.get();
- },
- get winUtils() {
- let win = this.win;
- if (!win) {
- return null;
- }
- return win.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(
- Ci.nsIDOMWindowUtils);
- },
- get AccService() {
- if (!this._AccService) {
- this._AccService = Cc['@mozilla.org/accessibilityService;1'].
- getService(Ci.nsIAccessibilityService);
- }
- return this._AccService;
- },
- set MozBuildApp(value) {
- this._buildApp = value;
- },
- get MozBuildApp() {
- if (!this._buildApp) {
- this._buildApp = this._buildAppMap[Services.appinfo.ID];
- }
- return this._buildApp;
- },
- get OS() {
- if (!this._OS) {
- this._OS = Services.appinfo.OS;
- }
- return this._OS;
- },
- get widgetToolkit() {
- if (!this._widgetToolkit) {
- this._widgetToolkit = Services.appinfo.widgetToolkit;
- }
- return this._widgetToolkit;
- },
- get ScriptName() {
- if (!this._ScriptName) {
- this._ScriptName =
- (Services.appinfo.processType == 2) ? 'AccessFuContent' : 'AccessFu';
- }
- return this._ScriptName;
- },
- get AndroidSdkVersion() {
- if (!this._AndroidSdkVersion) {
- if (Services.appinfo.OS == 'Android') {
- this._AndroidSdkVersion = Services.sysinfo.getPropertyAsInt32(
- 'version');
- } else {
- // Most useful in desktop debugging.
- this._AndroidSdkVersion = 16;
- }
- }
- return this._AndroidSdkVersion;
- },
- set AndroidSdkVersion(value) {
- // When we want to mimic another version.
- this._AndroidSdkVersion = value;
- },
- get BrowserApp() {
- if (!this.win) {
- return null;
- }
- switch (this.MozBuildApp) {
- case 'mobile/android':
- return this.win.BrowserApp;
- case 'browser':
- return this.win.gBrowser;
- case 'b2g':
- return this.win.shell;
- default:
- return null;
- }
- },
- get CurrentBrowser() {
- if (!this.BrowserApp) {
- return null;
- }
- if (this.MozBuildApp == 'b2g') {
- return this.BrowserApp.contentBrowser;
- }
- return this.BrowserApp.selectedBrowser;
- },
- get CurrentContentDoc() {
- let browser = this.CurrentBrowser;
- return browser ? browser.contentDocument : null;
- },
- get AllMessageManagers() {
- let messageManagers = new Set();
- function collectLeafMessageManagers(mm) {
- for (let i = 0; i < mm.childCount; i++) {
- let childMM = mm.getChildAt(i);
- if ('sendAsyncMessage' in childMM) {
- messageManagers.add(childMM);
- } else {
- collectLeafMessageManagers(childMM);
- }
- }
- }
- collectLeafMessageManagers(this.win.messageManager);
- let document = this.CurrentContentDoc;
- if (document) {
- if (document.location.host === 'b2g') {
- // The document is a b2g app chrome (ie. Mulet).
- let contentBrowser = this.win.content.shell.contentBrowser;
- messageManagers.add(this.getMessageManager(contentBrowser));
- document = contentBrowser.contentDocument;
- }
- let remoteframes = document.querySelectorAll('iframe');
- for (let i = 0; i < remoteframes.length; ++i) {
- let mm = this.getMessageManager(remoteframes[i]);
- if (mm) {
- messageManagers.add(mm);
- }
- }
- }
- return messageManagers;
- },
- 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 =
- outputArray.map(details => this.stringBundle.get(details));
- // Clean up the white space.
- return localized.filter(word => word).map(word => word.trim()).
- filter(trimmed => trimmed);
- },
- get stringBundle() {
- delete this.stringBundle;
- 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) {
- return null;
- }
- },
- getState: function getState(aAccessibleOrEvent) {
- if (aAccessibleOrEvent instanceof Ci.nsIAccessibleStateChangeEvent) {
- return new State(
- aAccessibleOrEvent.isExtraState ? 0 : aAccessibleOrEvent.state,
- aAccessibleOrEvent.isExtraState ? aAccessibleOrEvent.state : 0);
- } else {
- let state = {};
- let extState = {};
- aAccessibleOrEvent.getState(state, extState);
- return new State(state.value, extState.value);
- }
- },
- getAttributes: function getAttributes(aAccessible) {
- let attributes = {};
- if (aAccessible && aAccessible.attributes) {
- let attributesEnum = aAccessible.attributes.enumerate();
- // Populate |attributes| object with |aAccessible|'s attribute key-value
- // pairs.
- while (attributesEnum.hasMoreElements()) {
- let attribute = attributesEnum.getNext().QueryInterface(
- Ci.nsIPropertyElement);
- attributes[attribute.key] = attribute.value;
- }
- }
- return attributes;
- },
- getVirtualCursor: function getVirtualCursor(aDocument) {
- let doc = (aDocument instanceof Ci.nsIAccessible) ? aDocument :
- this.AccService.getAccessibleFor(aDocument);
- return doc.QueryInterface(Ci.nsIAccessibleDocument).virtualCursor;
- },
- getContentResolution: function _getContentResolution(aAccessible) {
- let res = { value: 1 };
- aAccessible.document.window.QueryInterface(
- Ci.nsIInterfaceRequestor).getInterface(
- Ci.nsIDOMWindowUtils).getResolution(res);
- return res.value;
- },
- getBounds: function getBounds(aAccessible, aPreserveContentScale) {
- let objX = {}, objY = {}, objW = {}, objH = {};
- aAccessible.getBounds(objX, objY, objW, objH);
- let scale = aPreserveContentScale ? 1 :
- this.getContentResolution(aAccessible);
- return new Rect(objX.value, objY.value, objW.value, objH.value).scale(
- scale, scale);
- },
- getTextBounds: function getTextBounds(aAccessible, aStart, aEnd,
- aPreserveContentScale) {
- let accText = aAccessible.QueryInterface(Ci.nsIAccessibleText);
- let objX = {}, objY = {}, objW = {}, objH = {};
- accText.getRangeExtents(aStart, aEnd, objX, objY, objW, objH,
- Ci.nsIAccessibleCoordinateType.COORDTYPE_SCREEN_RELATIVE);
- let scale = aPreserveContentScale ? 1 :
- this.getContentResolution(aAccessible);
- return new Rect(objX.value, objY.value, objW.value, objH.value).scale(
- scale, scale);
- },
- /**
- * Get current display DPI.
- */
- get dpi() {
- delete this.dpi;
- this.dpi = this.winUtils.displayDPI;
- return this.dpi;
- },
- isInSubtree: function isInSubtree(aAccessible, aSubTreeRoot) {
- let acc = aAccessible;
- // If aSubTreeRoot is an accessible document, we will only walk up the
- // ancestry of documents and skip everything else.
- if (aSubTreeRoot instanceof Ci.nsIAccessibleDocument) {
- while (acc) {
- let parentDoc = acc instanceof Ci.nsIAccessibleDocument ?
- acc.parentDocument : acc.document;
- if (parentDoc === aSubTreeRoot) {
- return true;
- }
- acc = parentDoc;
- }
- return false;
- }
- while (acc) {
- if (acc == aSubTreeRoot) {
- return true;
- }
- try {
- acc = acc.parent;
- } catch (x) {
- Logger.debug('Failed to get parent:', x);
- acc = null;
- }
- }
- return false;
- },
- isHidden: function isHidden(aAccessible) {
- // Need to account for aria-hidden, so can't just check for INVISIBLE
- // state.
- let hidden = Utils.getAttributes(aAccessible).hidden;
- return hidden && hidden === 'true';
- },
- visibleChildCount: function visibleChildCount(aAccessible) {
- let count = 0;
- for (let child = aAccessible.firstChild; child; child = child.nextSibling) {
- if (!this.isHidden(child)) {
- ++count;
- }
- }
- return count;
- },
- inHiddenSubtree: function inHiddenSubtree(aAccessible) {
- for (let acc=aAccessible; acc; acc=acc.parent) {
- if (this.isHidden(acc)) {
- return true;
- }
- }
- return false;
- },
- isAliveAndVisible: function isAliveAndVisible(aAccessible, aIsOnScreen) {
- if (!aAccessible) {
- return false;
- }
- try {
- let state = this.getState(aAccessible);
- if (state.contains(States.DEFUNCT) || state.contains(States.INVISIBLE) ||
- (aIsOnScreen && state.contains(States.OFFSCREEN)) ||
- Utils.inHiddenSubtree(aAccessible)) {
- return false;
- }
- } catch (x) {
- return false;
- }
- return true;
- },
- matchAttributeValue: function matchAttributeValue(aAttributeValue, values) {
- let attrSet = new Set(aAttributeValue.split(' '));
- for (let value of values) {
- if (attrSet.has(value)) {
- return value;
- }
- }
- },
- getLandmarkName: function getLandmarkName(aAccessible) {
- return this.matchRoles(aAccessible, [
- 'banner',
- 'complementary',
- 'contentinfo',
- 'main',
- 'navigation',
- 'search'
- ]);
- },
- getMathRole: function getMathRole(aAccessible) {
- return this.matchRoles(aAccessible, [
- 'base',
- 'close-fence',
- 'denominator',
- 'numerator',
- 'open-fence',
- 'overscript',
- 'presubscript',
- 'presuperscript',
- 'root-index',
- 'subscript',
- 'superscript',
- 'underscript'
- ]);
- },
- matchRoles: function matchRoles(aAccessible, aRoles) {
- let roles = this.getAttributes(aAccessible)['xml-roles'];
- if (!roles) {
- return;
- }
- // Looking up a role that would match any in the provided roles.
- return this.matchAttributeValue(roles, aRoles);
- },
- getEmbeddedControl: function getEmbeddedControl(aLabel) {
- if (aLabel) {
- let relation = aLabel.getRelationByType(Relations.LABEL_FOR);
- for (let i = 0; i < relation.targetsCount; i++) {
- let target = relation.getTarget(i);
- if (target.parent === aLabel) {
- return target;
- }
- }
- }
- return null;
- },
- isListItemDecorator: function isListItemDecorator(aStaticText,
- aExcludeOrdered) {
- let parent = aStaticText.parent;
- if (aExcludeOrdered && parent.parent.DOMNode.nodeName === 'OL') {
- return false;
- }
- return parent.role === Roles.LISTITEM && parent.childCount > 1 &&
- aStaticText.indexInParent === 0;
- },
- dispatchChromeEvent: function dispatchChromeEvent(aType, aDetails) {
- let details = {
- type: aType,
- details: JSON.stringify(
- typeof aDetails === 'string' ? { eventType : aDetails } : aDetails)
- };
- let window = this.win;
- let shell = window.shell || window.content.shell;
- if (shell) {
- // On B2G device.
- shell.sendChromeEvent(details);
- } else {
- // Dispatch custom event to have support for desktop and screen reader
- // emulator add-on.
- window.dispatchEvent(new window.CustomEvent(aType, {
- bubbles: true,
- cancelable: true,
- detail: details
- }));
- }
- },
- isActivatableOnFingerUp: function isActivatableOnFingerUp(aAccessible) {
- if (aAccessible.role === Roles.KEY) {
- return true;
- }
- let quick_activate = this.getAttributes(aAccessible)['moz-quick-activate'];
- return quick_activate && JSON.parse(quick_activate);
- }
- };
- /**
- * State object used internally to process accessible's states.
- * @param {Number} aBase Base state.
- * @param {Number} aExtended Extended state.
- */
- function State(aBase, aExtended) {
- this.base = aBase;
- this.extended = aExtended;
- }
- State.prototype = {
- contains: function State_contains(other) {
- return !!(this.base & other.base || this.extended & other.extended);
- },
- toString: function State_toString() {
- let stateStrings = Utils.AccService.
- getStringStates(this.base, this.extended);
- let statesArray = new Array(stateStrings.length);
- for (let i = 0; i < statesArray.length; i++) {
- statesArray[i] = stateStrings.item(i);
- }
- return '[' + statesArray.join(', ') + ']';
- }
- };
- this.Logger = { // jshint ignore:line
- GESTURE: -1,
- DEBUG: 0,
- INFO: 1,
- WARNING: 2,
- ERROR: 3,
- _LEVEL_NAMES: ['GESTURE', 'DEBUG', 'INFO', 'WARNING', 'ERROR'],
- logLevel: 1, // INFO;
- test: false,
- log: function log(aLogLevel) {
- if (aLogLevel < this.logLevel) {
- return;
- }
- let args = Array.prototype.slice.call(arguments, 1);
- let message = (typeof(args[0]) === 'function' ? args[0]() : args).join(' ');
- message = '[' + Utils.ScriptName + '] ' + this._LEVEL_NAMES[aLogLevel + 1] +
- ' ' + message + '\n';
- dump(message);
- // Note: used for testing purposes. If |this.test| is true, also log to
- // the console service.
- if (this.test) {
- try {
- Services.console.logStringMessage(message);
- } catch (ex) {
- // There was an exception logging to the console service.
- }
- }
- },
- info: function info() {
- this.log.apply(
- this, [this.INFO].concat(Array.prototype.slice.call(arguments)));
- },
- gesture: function gesture() {
- this.log.apply(
- this, [this.GESTURE].concat(Array.prototype.slice.call(arguments)));
- },
- debug: function debug() {
- this.log.apply(
- this, [this.DEBUG].concat(Array.prototype.slice.call(arguments)));
- },
- warning: function warning() {
- this.log.apply(
- this, [this.WARNING].concat(Array.prototype.slice.call(arguments)));
- },
- error: function error() {
- this.log.apply(
- this, [this.ERROR].concat(Array.prototype.slice.call(arguments)));
- },
- logException: function logException(
- aException, aErrorMessage = 'An exception has occured') {
- try {
- let stackMessage = '';
- if (aException.stack) {
- stackMessage = ' ' + aException.stack.replace(/\n/g, '\n ');
- } else if (aException.location) {
- let frame = aException.location;
- let stackLines = [];
- while (frame && frame.lineNumber) {
- stackLines.push(
- ' ' + frame.name + '@' + frame.filename + ':' + frame.lineNumber);
- frame = frame.caller;
- }
- stackMessage = stackLines.join('\n');
- } else {
- stackMessage =
- '(' + aException.fileName + ':' + aException.lineNumber + ')';
- }
- this.error(aErrorMessage + ':\n ' +
- aException.message + '\n' +
- stackMessage);
- } catch (x) {
- this.error(x);
- }
- },
- accessibleToString: function accessibleToString(aAccessible) {
- if (!aAccessible) {
- return '[ null ]';
- }
- try {
- return'[ ' + Utils.AccService.getStringRole(aAccessible.role) +
- ' | ' + aAccessible.name + ' ]';
- } catch (x) {
- return '[ defunct ]';
- }
- },
- eventToString: function eventToString(aEvent) {
- let str = Utils.AccService.getStringEventType(aEvent.eventType);
- if (aEvent.eventType == Events.STATE_CHANGE) {
- let event = aEvent.QueryInterface(Ci.nsIAccessibleStateChangeEvent);
- let stateStrings = event.isExtraState ?
- Utils.AccService.getStringStates(0, event.state) :
- Utils.AccService.getStringStates(event.state, 0);
- str += ' (' + stateStrings.item(0) + ')';
- }
- if (aEvent.eventType == Events.VIRTUALCURSOR_CHANGED) {
- let event = aEvent.QueryInterface(
- Ci.nsIAccessibleVirtualCursorChangeEvent);
- let pivot = aEvent.accessible.QueryInterface(
- Ci.nsIAccessibleDocument).virtualCursor;
- str += ' (' + this.accessibleToString(event.oldAccessible) + ' -> ' +
- this.accessibleToString(pivot.position) + ')';
- }
- return str;
- },
- statesToString: function statesToString(aAccessible) {
- return Utils.getState(aAccessible).toString();
- },
- dumpTree: function dumpTree(aLogLevel, aRootAccessible) {
- if (aLogLevel < this.logLevel) {
- return;
- }
- this._dumpTreeInternal(aLogLevel, aRootAccessible, 0);
- },
- _dumpTreeInternal:
- function _dumpTreeInternal(aLogLevel, aAccessible, aIndent) {
- let indentStr = '';
- for (let i = 0; i < aIndent; i++) {
- indentStr += ' ';
- }
- this.log(aLogLevel, indentStr,
- this.accessibleToString(aAccessible),
- '(' + this.statesToString(aAccessible) + ')');
- for (let i = 0; i < aAccessible.childCount; i++) {
- this._dumpTreeInternal(aLogLevel, aAccessible.getChildAt(i),
- aIndent + 1);
- }
- }
- };
- /**
- * PivotContext: An object that generates and caches context information
- * for a given accessible and its relationship with another accessible.
- *
- * If the given accessible is a label for a nested control, then this
- * context will represent the nested control instead of the label.
- * With the exception of bounds calculation, which will use the containing
- * label. In this case the |accessible| field would be the embedded control,
- * and the |accessibleForBounds| field would be the label.
- */
- this.PivotContext = function PivotContext(aAccessible, aOldAccessible, // jshint ignore:line
- aStartOffset, aEndOffset, aIgnoreAncestry = false,
- aIncludeInvisible = false) {
- this._accessible = aAccessible;
- this._nestedControl = Utils.getEmbeddedControl(aAccessible);
- this._oldAccessible =
- this._isDefunct(aOldAccessible) ? null : aOldAccessible;
- this.startOffset = aStartOffset;
- this.endOffset = aEndOffset;
- this._ignoreAncestry = aIgnoreAncestry;
- this._includeInvisible = aIncludeInvisible;
- };
- PivotContext.prototype = {
- get accessible() {
- // If the current pivot accessible has a nested control,
- // make this context use it publicly.
- return this._nestedControl || this._accessible;
- },
- get oldAccessible() {
- return this._oldAccessible;
- },
- get isNestedControl() {
- return !!this._nestedControl;
- },
- get accessibleForBounds() {
- return this._accessible;
- },
- get textAndAdjustedOffsets() {
- if (this.startOffset === -1 && this.endOffset === -1) {
- return null;
- }
- if (!this._textAndAdjustedOffsets) {
- let result = {startOffset: this.startOffset,
- endOffset: this.endOffset,
- text: this._accessible.QueryInterface(Ci.nsIAccessibleText).
- getText(0,
- Ci.nsIAccessibleText.TEXT_OFFSET_END_OF_TEXT)};
- let hypertextAcc = this._accessible.QueryInterface(
- Ci.nsIAccessibleHyperText);
- // Iterate through the links in backwards order so text replacements don't
- // affect the offsets of links yet to be processed.
- for (let i = hypertextAcc.linkCount - 1; i >= 0; i--) {
- let link = hypertextAcc.getLinkAt(i);
- let linkText = '';
- if (link instanceof Ci.nsIAccessibleText) {
- linkText = link.QueryInterface(Ci.nsIAccessibleText).
- getText(0,
- Ci.nsIAccessibleText.TEXT_OFFSET_END_OF_TEXT);
- }
- let start = link.startIndex;
- let end = link.endIndex;
- for (let offset of ['startOffset', 'endOffset']) {
- if (this[offset] >= end) {
- result[offset] += linkText.length - (end - start);
- }
- }
- result.text = result.text.substring(0, start) + linkText +
- result.text.substring(end);
- }
- this._textAndAdjustedOffsets = result;
- }
- return this._textAndAdjustedOffsets;
- },
- /**
- * Get a list of |aAccessible|'s ancestry up to the root.
- * @param {nsIAccessible} aAccessible.
- * @return {Array} Ancestry list.
- */
- _getAncestry: function _getAncestry(aAccessible) {
- let ancestry = [];
- let parent = aAccessible;
- try {
- while (parent && (parent = parent.parent)) {
- ancestry.push(parent);
- }
- } catch (x) {
- // A defunct accessible will raise an exception geting parent.
- Logger.debug('Failed to get parent:', x);
- }
- return ancestry.reverse();
- },
- /**
- * A list of the old accessible's ancestry.
- */
- get oldAncestry() {
- if (!this._oldAncestry) {
- if (!this._oldAccessible || this._ignoreAncestry) {
- this._oldAncestry = [];
- } else {
- this._oldAncestry = this._getAncestry(this._oldAccessible);
- this._oldAncestry.push(this._oldAccessible);
- }
- }
- return this._oldAncestry;
- },
- /**
- * A list of the current accessible's ancestry.
- */
- get currentAncestry() {
- if (!this._currentAncestry) {
- this._currentAncestry = this._ignoreAncestry ? [] :
- this._getAncestry(this.accessible);
- }
- return this._currentAncestry;
- },
- /*
- * This is a list of the accessible's ancestry up to the common ancestor
- * of the accessible and the old accessible. It is useful for giving the
- * user context as to where they are in the heirarchy.
- */
- get newAncestry() {
- if (!this._newAncestry) {
- this._newAncestry = this._ignoreAncestry ? [] :
- this.currentAncestry.filter(
- (currentAncestor, i) => currentAncestor !== this.oldAncestry[i]);
- }
- return this._newAncestry;
- },
- /*
- * Traverse the accessible's subtree in pre or post order.
- * It only includes the accessible's visible chidren.
- * Note: needSubtree is a function argument that can be used to determine
- * whether aAccessible's subtree is required.
- */
- _traverse: function* _traverse(aAccessible, aPreorder, aStop) {
- if (aStop && aStop(aAccessible)) {
- return;
- }
- let child = aAccessible.firstChild;
- while (child) {
- let include;
- if (this._includeInvisible) {
- include = true;
- } else {
- include = !Utils.isHidden(child);
- }
- if (include) {
- if (aPreorder) {
- yield child;
- for (let node of this._traverse(child, aPreorder, aStop)) {
- yield node;
- }
- } else {
- for (let node of this._traverse(child, aPreorder, aStop)) {
- yield node;
- }
- yield child;
- }
- }
- child = child.nextSibling;
- }
- },
- /**
- * Get interaction hints for the context ancestry.
- * @return {Array} Array of interaction hints.
- */
- get interactionHints() {
- let hints = [];
- this.newAncestry.concat(this.accessible).reverse().forEach(aAccessible => {
- let hint = Utils.getAttributes(aAccessible)['moz-hint'];
- if (hint) {
- hints.push(hint);
- } else if (aAccessible.actionCount > 0) {
- hints.push({
- string: Utils.AccService.getStringRole(
- aAccessible.role).replace(/\s/g, '') + '-hint'
- });
- }
- });
- return hints;
- },
- /*
- * A subtree generator function, used to generate a flattened
- * list of the accessible's subtree in pre or post order.
- * It only includes the accessible's visible chidren.
- * @param {boolean} aPreorder A flag for traversal order. If true, traverse
- * in preorder; if false, traverse in postorder.
- * @param {function} aStop An optional function, indicating whether subtree
- * traversal should stop.
- */
- subtreeGenerator: function subtreeGenerator(aPreorder, aStop) {
- return this._traverse(this.accessible, aPreorder, aStop);
- },
- getCellInfo: function getCellInfo(aAccessible) {
- if (!this._cells) {
- this._cells = new WeakMap();
- }
- let domNode = aAccessible.DOMNode;
- if (this._cells.has(domNode)) {
- return this._cells.get(domNode);
- }
- let cellInfo = {};
- let getAccessibleCell = function getAccessibleCell(aAccessible) {
- if (!aAccessible) {
- return null;
- }
- if ([
- Roles.CELL,
- Roles.COLUMNHEADER,
- Roles.ROWHEADER,
- Roles.MATHML_CELL
- ].indexOf(aAccessible.role) < 0) {
- return null;
- }
- try {
- return aAccessible.QueryInterface(Ci.nsIAccessibleTableCell);
- } catch (x) {
- Logger.logException(x);
- return null;
- }
- };
- let getHeaders = function* getHeaders(aHeaderCells) {
- let enumerator = aHeaderCells.enumerate();
- while (enumerator.hasMoreElements()) {
- yield enumerator.getNext().QueryInterface(Ci.nsIAccessible).name;
- }
- };
- cellInfo.current = getAccessibleCell(aAccessible);
- if (!cellInfo.current) {
- Logger.warning(aAccessible,
- 'does not support nsIAccessibleTableCell interface.');
- this._cells.set(domNode, null);
- return null;
- }
- let table = cellInfo.current.table;
- if (table.isProbablyForLayout()) {
- this._cells.set(domNode, null);
- return null;
- }
- cellInfo.previous = null;
- let oldAncestry = this.oldAncestry.reverse();
- let ancestor = oldAncestry.shift();
- while (!cellInfo.previous && ancestor) {
- let cell = getAccessibleCell(ancestor);
- if (cell && cell.table === table) {
- cellInfo.previous = cell;
- }
- ancestor = oldAncestry.shift();
- }
- if (cellInfo.previous) {
- cellInfo.rowChanged = cellInfo.current.rowIndex !==
- cellInfo.previous.rowIndex;
- cellInfo.columnChanged = cellInfo.current.columnIndex !==
- cellInfo.previous.columnIndex;
- } else {
- cellInfo.rowChanged = true;
- cellInfo.columnChanged = true;
- }
- cellInfo.rowExtent = cellInfo.current.rowExtent;
- cellInfo.columnExtent = cellInfo.current.columnExtent;
- cellInfo.columnIndex = cellInfo.current.columnIndex;
- cellInfo.rowIndex = cellInfo.current.rowIndex;
- cellInfo.columnHeaders = [];
- if (cellInfo.columnChanged && cellInfo.current.role !==
- Roles.COLUMNHEADER) {
- cellInfo.columnHeaders = [...getHeaders(cellInfo.current.columnHeaderCells)];
- }
- cellInfo.rowHeaders = [];
- if (cellInfo.rowChanged &&
- (cellInfo.current.role === Roles.CELL ||
- cellInfo.current.role === Roles.MATHML_CELL)) {
- cellInfo.rowHeaders = [...getHeaders(cellInfo.current.rowHeaderCells)];
- }
- this._cells.set(domNode, cellInfo);
- return cellInfo;
- },
- get bounds() {
- if (!this._bounds) {
- this._bounds = Utils.getBounds(this.accessibleForBounds);
- }
- return this._bounds.clone();
- },
- _isDefunct: function _isDefunct(aAccessible) {
- try {
- return Utils.getState(aAccessible).contains(States.DEFUNCT);
- } catch (x) {
- return true;
- }
- }
- };
- this.PrefCache = function PrefCache(aName, aCallback, aRunCallbackNow) { // jshint ignore:line
- 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, true);
- } catch (x) {
- Logger.logException(x);
- }
- }
- branch.addObserver(aName, this, true);
- };
- PrefCache.prototype = {
- _getValue: function _getValue(aBranch) {
- try {
- if (!this.type) {
- this.type = aBranch.getPrefType(this.name);
- }
- switch (this.type) {
- case Ci.nsIPrefBranch.PREF_STRING:
- return aBranch.getCharPref(this.name);
- case Ci.nsIPrefBranch.PREF_INT:
- return aBranch.getIntPref(this.name);
- case Ci.nsIPrefBranch.PREF_BOOL:
- return aBranch.getBoolPref(this.name);
- default:
- return null;
- }
- } 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, false);
- } catch (x) {
- Logger.logException(x);
- }
- }
- },
- QueryInterface : XPCOMUtils.generateQI([Ci.nsIObserver,
- Ci.nsISupportsWeakReference])
- };
- this.SettingCache = function SettingCache(aName, aCallback, aOptions = {}) { // jshint ignore:line
- this.value = aOptions.defaultValue;
- let runCallback = () => {
- if (aCallback) {
- aCallback(aName, this.value);
- if (aOptions.callbackOnce) {
- runCallback = () => {};
- }
- }
- };
- let settings = Utils.win.navigator.mozSettings;
- if (!settings) {
- if (aOptions.callbackNow) {
- runCallback();
- }
- return;
- }
- let lock = settings.createLock();
- let req = lock.get(aName);
- req.addEventListener('success', () => {
- this.value = req.result[aName] === undefined ?
- aOptions.defaultValue : req.result[aName];
- if (aOptions.callbackNow) {
- runCallback();
- }
- });
- settings.addObserver(aName,
- (evt) => {
- this.value = evt.settingValue;
- runCallback();
- });
- };
|