2012-06-20 14:07:51 -07:00
|
|
|
/* 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/. */
|
|
|
|
|
|
|
|
'use strict';
|
|
|
|
|
|
|
|
const Cu = Components.utils;
|
|
|
|
const Cc = Components.classes;
|
|
|
|
const Ci = Components.interfaces;
|
|
|
|
|
2013-07-01 20:50:39 -07:00
|
|
|
const EVENT_STATE_CHANGE = Ci.nsIAccessibleEvent.EVENT_STATE_CHANGE;
|
|
|
|
|
|
|
|
const ROLE_CELL = Ci.nsIAccessibleRole.ROLE_CELL;
|
|
|
|
const ROLE_COLUMNHEADER = Ci.nsIAccessibleRole.ROLE_COLUMNHEADER;
|
|
|
|
const ROLE_ROWHEADER = Ci.nsIAccessibleRole.ROLE_ROWHEADER;
|
|
|
|
|
2013-05-24 11:57:28 -07:00
|
|
|
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
|
|
|
XPCOMUtils.defineLazyModuleGetter(this, 'Services',
|
|
|
|
'resource://gre/modules/Services.jsm');
|
|
|
|
XPCOMUtils.defineLazyModuleGetter(this, 'Rect',
|
|
|
|
'resource://gre/modules/Geometry.jsm');
|
2012-06-20 14:07:51 -07:00
|
|
|
|
2013-05-24 11:57:28 -07:00
|
|
|
this.EXPORTED_SYMBOLS = ['Utils', 'Logger', 'PivotContext', 'PrefCache'];
|
2012-06-20 14:07:51 -07:00
|
|
|
|
2012-10-31 09:13:28 -07:00
|
|
|
this.Utils = {
|
2012-07-20 09:46:54 -07:00
|
|
|
_buildAppMap: {
|
|
|
|
'{3c2e2abc-06d4-11e1-ac3b-374f68613e61}': 'b2g',
|
|
|
|
'{ec8030f7-c20a-464f-9b0e-13a3a9e97384}': 'browser',
|
|
|
|
'{aa3c5121-dab2-40e2-81ca-7ea25febc110}': 'mobile/android',
|
|
|
|
'{a23983c0-fd0e-11dc-95ff-0800200c9a66}': 'mobile/xul'
|
|
|
|
},
|
|
|
|
|
2013-03-15 06:50:55 -07:00
|
|
|
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);
|
|
|
|
},
|
|
|
|
|
2013-04-23 10:39:15 -07:00
|
|
|
uninit: function Utils_uninit() {
|
|
|
|
if (!this._win) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
delete this._win;
|
|
|
|
},
|
|
|
|
|
2013-03-15 06:50:55 -07:00
|
|
|
get win() {
|
2013-05-21 11:16:49 -07:00
|
|
|
if (!this._win) {
|
|
|
|
return null;
|
|
|
|
}
|
2013-03-15 06:50:55 -07:00
|
|
|
return this._win.get();
|
|
|
|
},
|
|
|
|
|
2012-08-26 20:14:42 -07:00
|
|
|
get AccRetrieval() {
|
|
|
|
if (!this._AccRetrieval) {
|
|
|
|
this._AccRetrieval = Cc['@mozilla.org/accessibleRetrieval;1'].
|
|
|
|
getService(Ci.nsIAccessibleRetrieval);
|
|
|
|
}
|
|
|
|
|
|
|
|
return this._AccRetrieval;
|
|
|
|
},
|
|
|
|
|
2012-10-01 13:33:26 -07:00
|
|
|
set MozBuildApp(value) {
|
|
|
|
this._buildApp = value;
|
|
|
|
},
|
|
|
|
|
2012-07-20 09:46:54 -07:00
|
|
|
get MozBuildApp() {
|
|
|
|
if (!this._buildApp)
|
|
|
|
this._buildApp = this._buildAppMap[Services.appinfo.ID];
|
|
|
|
return this._buildApp;
|
|
|
|
},
|
|
|
|
|
2012-06-20 14:07:51 -07:00
|
|
|
get OS() {
|
|
|
|
if (!this._OS)
|
|
|
|
this._OS = Services.appinfo.OS;
|
|
|
|
return this._OS;
|
|
|
|
},
|
|
|
|
|
2013-06-12 10:47:25 -07:00
|
|
|
get widgetToolkit() {
|
|
|
|
if (!this._widgetToolkit)
|
|
|
|
this._widgetToolkit = Services.appinfo.widgetToolkit;
|
|
|
|
return this._widgetToolkit;
|
|
|
|
},
|
|
|
|
|
2012-10-01 13:33:26 -07:00
|
|
|
get ScriptName() {
|
|
|
|
if (!this._ScriptName)
|
|
|
|
this._ScriptName =
|
|
|
|
(Services.appinfo.processType == 2) ? 'AccessFuContent' : 'AccessFu';
|
|
|
|
return this._ScriptName;
|
|
|
|
},
|
|
|
|
|
2012-06-20 14:07:51 -07:00
|
|
|
get AndroidSdkVersion() {
|
|
|
|
if (!this._AndroidSdkVersion) {
|
2012-10-10 08:10:39 -07:00
|
|
|
if (Services.appinfo.OS == 'Android') {
|
|
|
|
this._AndroidSdkVersion = Services.sysinfo.getPropertyAsInt32('version');
|
|
|
|
} else {
|
|
|
|
// Most useful in desktop debugging.
|
|
|
|
this._AndroidSdkVersion = 15;
|
|
|
|
}
|
2012-06-20 14:07:51 -07:00
|
|
|
}
|
|
|
|
return this._AndroidSdkVersion;
|
|
|
|
},
|
|
|
|
|
|
|
|
set AndroidSdkVersion(value) {
|
|
|
|
// When we want to mimic another version.
|
|
|
|
this._AndroidSdkVersion = value;
|
|
|
|
},
|
|
|
|
|
2013-03-15 06:50:55 -07:00
|
|
|
get BrowserApp() {
|
2013-05-21 11:16:49 -07:00
|
|
|
if (!this.win) {
|
|
|
|
return null;
|
|
|
|
}
|
2012-07-20 09:46:54 -07:00
|
|
|
switch (this.MozBuildApp) {
|
|
|
|
case 'mobile/android':
|
2013-03-15 06:50:55 -07:00
|
|
|
return this.win.BrowserApp;
|
2012-07-20 09:46:54 -07:00
|
|
|
case 'browser':
|
2013-03-15 06:50:55 -07:00
|
|
|
return this.win.gBrowser;
|
2012-07-20 09:46:54 -07:00
|
|
|
case 'b2g':
|
2013-03-15 06:50:55 -07:00
|
|
|
return this.win.shell;
|
2012-07-20 09:46:54 -07:00
|
|
|
default:
|
|
|
|
return null;
|
2012-06-20 14:07:51 -07:00
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2013-03-15 06:50:55 -07:00
|
|
|
get CurrentBrowser() {
|
2013-05-21 11:16:49 -07:00
|
|
|
if (!this.BrowserApp) {
|
|
|
|
return null;
|
|
|
|
}
|
2012-10-01 13:33:26 -07:00
|
|
|
if (this.MozBuildApp == 'b2g')
|
2013-03-15 06:50:55 -07:00
|
|
|
return this.BrowserApp.contentBrowser;
|
|
|
|
return this.BrowserApp.selectedBrowser;
|
2012-10-01 13:33:26 -07:00
|
|
|
},
|
|
|
|
|
2013-03-15 06:50:55 -07:00
|
|
|
get CurrentContentDoc() {
|
|
|
|
let browser = this.CurrentBrowser;
|
2012-10-19 10:06:07 -07:00
|
|
|
return browser ? browser.contentDocument : null;
|
2012-10-01 13:33:26 -07:00
|
|
|
},
|
|
|
|
|
2013-03-15 06:50:55 -07:00
|
|
|
get AllMessageManagers() {
|
2012-10-01 13:33:26 -07:00
|
|
|
let messageManagers = [];
|
|
|
|
|
2013-03-15 06:50:55 -07:00
|
|
|
for (let i = 0; i < this.win.messageManager.childCount; i++)
|
|
|
|
messageManagers.push(this.win.messageManager.getChildAt(i));
|
2012-10-01 13:33:26 -07:00
|
|
|
|
2013-03-15 06:50:55 -07:00
|
|
|
let document = this.CurrentContentDoc;
|
2012-10-01 13:33:26 -07:00
|
|
|
|
2012-10-19 10:06:07 -07:00
|
|
|
if (document) {
|
2013-05-21 11:16:50 -07:00
|
|
|
let remoteframes = document.querySelectorAll('iframe');
|
|
|
|
|
|
|
|
for (let i = 0; i < remoteframes.length; ++i) {
|
|
|
|
let mm = this.getMessageManager(remoteframes[i]);
|
|
|
|
if (mm) {
|
|
|
|
messageManagers.push(mm);
|
|
|
|
}
|
|
|
|
}
|
2012-10-01 13:33:26 -07:00
|
|
|
|
2012-10-19 10:06:07 -07:00
|
|
|
}
|
2012-10-01 13:33:26 -07:00
|
|
|
|
|
|
|
return messageManagers;
|
2012-08-17 15:49:34 -07:00
|
|
|
},
|
|
|
|
|
2013-05-21 11:16:50 -07:00
|
|
|
get isContentProcess() {
|
|
|
|
delete this.isContentProcess;
|
|
|
|
this.isContentProcess =
|
|
|
|
Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT;
|
|
|
|
return this.isContentProcess;
|
|
|
|
},
|
|
|
|
|
2013-03-15 06:50:55 -07:00
|
|
|
getMessageManager: function getMessageManager(aBrowser) {
|
|
|
|
try {
|
|
|
|
return aBrowser.QueryInterface(Ci.nsIFrameLoaderOwner).
|
|
|
|
frameLoader.messageManager;
|
|
|
|
} catch (x) {
|
|
|
|
Logger.logException(x);
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2012-06-20 14:07:51 -07:00
|
|
|
getViewport: function getViewport(aWindow) {
|
2012-07-20 09:46:54 -07:00
|
|
|
switch (this.MozBuildApp) {
|
|
|
|
case 'mobile/android':
|
2012-06-20 14:07:51 -07:00
|
|
|
return aWindow.BrowserApp.selectedTab.getViewport();
|
|
|
|
default:
|
|
|
|
return null;
|
|
|
|
}
|
2012-07-10 16:10:15 -07:00
|
|
|
},
|
|
|
|
|
|
|
|
getStates: function getStates(aAccessible) {
|
|
|
|
if (!aAccessible)
|
|
|
|
return [0, 0];
|
|
|
|
|
|
|
|
let state = {};
|
|
|
|
let extState = {};
|
|
|
|
aAccessible.getState(state, extState);
|
|
|
|
return [state.value, extState.value];
|
2012-07-20 09:46:54 -07:00
|
|
|
},
|
|
|
|
|
2013-05-28 10:51:44 -07:00
|
|
|
getAttributes: function getAttributes(aAccessible) {
|
|
|
|
let attributes = {};
|
|
|
|
|
2013-06-27 14:15:36 -07:00
|
|
|
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;
|
|
|
|
}
|
2013-05-28 10:51:44 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
return attributes;
|
|
|
|
},
|
|
|
|
|
2012-07-20 09:46:54 -07:00
|
|
|
getVirtualCursor: function getVirtualCursor(aDocument) {
|
|
|
|
let doc = (aDocument instanceof Ci.nsIAccessible) ? aDocument :
|
2012-08-26 20:14:42 -07:00
|
|
|
this.AccRetrieval.getAccessibleFor(aDocument);
|
2012-07-20 09:46:54 -07:00
|
|
|
|
2013-05-21 11:16:50 -07:00
|
|
|
return doc.QueryInterface(Ci.nsIAccessibleDocument).virtualCursor;
|
|
|
|
},
|
2012-07-20 09:46:54 -07:00
|
|
|
|
2013-07-03 15:02:44 -07:00
|
|
|
getBounds: function getBounds(aAccessible) {
|
|
|
|
let objX = {}, objY = {}, objW = {}, objH = {};
|
|
|
|
aAccessible.getBounds(objX, objY, objW, objH);
|
|
|
|
return new Rect(objX.value, objY.value, objW.value, objH.value);
|
|
|
|
},
|
|
|
|
|
|
|
|
inHiddenSubtree: function inHiddenSubtree(aAccessible) {
|
|
|
|
for (let acc=aAccessible; acc; acc=acc.parent) {
|
2013-07-16 11:45:17 -07:00
|
|
|
let hidden = Utils.getAttributes(acc).hidden;
|
|
|
|
if (hidden && JSON.parse(hidden)) {
|
2013-07-03 15:02:44 -07:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
},
|
|
|
|
|
|
|
|
isAliveAndVisible: function isAliveAndVisible(aAccessible) {
|
|
|
|
if (!aAccessible) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
let extstate = {};
|
|
|
|
let state = {};
|
|
|
|
aAccessible.getState(state, extstate);
|
|
|
|
if (extstate.value & Ci.nsIAccessibleStates.EXT_STATE_DEFUNCT ||
|
|
|
|
state.value & Ci.nsIAccessibleStates.STATE_INVISIBLE ||
|
|
|
|
Utils.inHiddenSubtree(aAccessible)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
} catch (x) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
2013-07-11 13:42:11 -07:00
|
|
|
},
|
|
|
|
|
|
|
|
getLandmarkName: function getLandmarkName(aAccessible) {
|
|
|
|
const landmarks = [
|
|
|
|
'banner',
|
|
|
|
'complementary',
|
|
|
|
'contentinfo',
|
|
|
|
'main',
|
|
|
|
'navigation',
|
|
|
|
'search'
|
|
|
|
];
|
|
|
|
let roles = this.getAttributes(aAccessible)['xml-roles'];
|
|
|
|
if (!roles) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Looking up a role that would match a landmark.
|
|
|
|
for (let landmark of landmarks) {
|
|
|
|
if (roles.indexOf(landmark) > -1) {
|
|
|
|
return landmark;
|
|
|
|
}
|
|
|
|
}
|
2012-06-20 14:07:51 -07:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2012-10-31 09:13:28 -07:00
|
|
|
this.Logger = {
|
2012-06-20 14:07:51 -07:00
|
|
|
DEBUG: 0,
|
|
|
|
INFO: 1,
|
|
|
|
WARNING: 2,
|
|
|
|
ERROR: 3,
|
|
|
|
_LEVEL_NAMES: ['DEBUG', 'INFO', 'WARNING', 'ERROR'],
|
|
|
|
|
|
|
|
logLevel: 1, // INFO;
|
|
|
|
|
2013-04-23 10:39:15 -07:00
|
|
|
test: false,
|
|
|
|
|
2012-06-20 14:07:51 -07:00
|
|
|
log: function log(aLogLevel) {
|
|
|
|
if (aLogLevel < this.logLevel)
|
|
|
|
return;
|
|
|
|
|
|
|
|
let message = Array.prototype.slice.call(arguments, 1).join(' ');
|
2013-04-23 10:39:15 -07:00
|
|
|
message = '[' + Utils.ScriptName + '] ' + this._LEVEL_NAMES[aLogLevel] +
|
|
|
|
' ' + 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.
|
|
|
|
}
|
|
|
|
}
|
2012-06-20 14:07:51 -07:00
|
|
|
},
|
|
|
|
|
|
|
|
info: function info() {
|
|
|
|
this.log.apply(
|
|
|
|
this, [this.INFO].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)));
|
|
|
|
},
|
|
|
|
|
2013-06-27 14:15:36 -07:00
|
|
|
logException: function logException(
|
|
|
|
aException, aErrorMessage = 'An exception has occured') {
|
2012-10-19 13:39:36 -07:00
|
|
|
try {
|
2013-06-27 14:15:36 -07:00
|
|
|
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);
|
2012-10-19 13:39:36 -07:00
|
|
|
} catch (x) {
|
|
|
|
this.error(x);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2012-06-20 14:07:51 -07:00
|
|
|
accessibleToString: function accessibleToString(aAccessible) {
|
|
|
|
let str = '[ defunct ]';
|
|
|
|
try {
|
2012-08-26 20:14:42 -07:00
|
|
|
str = '[ ' + Utils.AccRetrieval.getStringRole(aAccessible.role) +
|
2012-06-20 14:07:51 -07:00
|
|
|
' | ' + aAccessible.name + ' ]';
|
|
|
|
} catch (x) {
|
|
|
|
}
|
|
|
|
|
|
|
|
return str;
|
|
|
|
},
|
|
|
|
|
|
|
|
eventToString: function eventToString(aEvent) {
|
2012-08-26 20:14:42 -07:00
|
|
|
let str = Utils.AccRetrieval.getStringEventType(aEvent.eventType);
|
2013-07-01 20:50:39 -07:00
|
|
|
if (aEvent.eventType == EVENT_STATE_CHANGE) {
|
2012-06-20 14:07:51 -07:00
|
|
|
let event = aEvent.QueryInterface(Ci.nsIAccessibleStateChangeEvent);
|
2013-01-19 07:38:01 -08:00
|
|
|
let stateStrings = event.isExtraState ?
|
2012-08-26 20:14:42 -07:00
|
|
|
Utils.AccRetrieval.getStringStates(0, event.state) :
|
|
|
|
Utils.AccRetrieval.getStringStates(event.state, 0);
|
2012-06-20 14:07:51 -07:00
|
|
|
str += ' (' + stateStrings.item(0) + ')';
|
|
|
|
}
|
|
|
|
|
|
|
|
return str;
|
2012-07-20 09:46:54 -07:00
|
|
|
},
|
|
|
|
|
|
|
|
statesToString: function statesToString(aAccessible) {
|
|
|
|
let [state, extState] = Utils.getStates(aAccessible);
|
|
|
|
let stringArray = [];
|
2012-08-26 20:14:42 -07:00
|
|
|
let stateStrings = Utils.AccRetrieval.getStringStates(state, extState);
|
2012-07-20 09:46:54 -07:00
|
|
|
for (var i=0; i < stateStrings.length; i++)
|
|
|
|
stringArray.push(stateStrings.item(i));
|
|
|
|
return stringArray.join(' ');
|
|
|
|
},
|
|
|
|
|
|
|
|
dumpTree: function dumpTree(aLogLevel, aRootAccessible) {
|
|
|
|
if (aLogLevel < this.logLevel)
|
|
|
|
return;
|
|
|
|
|
|
|
|
this._dumpTreeInternal(aLogLevel, aRootAccessible, 0);
|
|
|
|
},
|
|
|
|
|
|
|
|
_dumpTreeInternal: function _dumpTreeInternal(aLogLevel, aAccessible, aIndent) {
|
|
|
|
let indentStr = '';
|
|
|
|
for (var i=0; i < aIndent; i++)
|
|
|
|
indentStr += ' ';
|
|
|
|
this.log(aLogLevel, indentStr,
|
|
|
|
this.accessibleToString(aAccessible),
|
|
|
|
'(' + this.statesToString(aAccessible) + ')');
|
|
|
|
for (var i=0; i < aAccessible.childCount; i++)
|
|
|
|
this._dumpTreeInternal(aLogLevel, aAccessible.getChildAt(i), aIndent + 1);
|
|
|
|
}
|
2012-06-20 14:07:51 -07:00
|
|
|
};
|
2013-04-04 15:16:37 -07:00
|
|
|
|
|
|
|
/**
|
|
|
|
* PivotContext: An object that generates and caches context information
|
|
|
|
* for a given accessible and its relationship with another accessible.
|
|
|
|
*/
|
|
|
|
this.PivotContext = function PivotContext(aAccessible, aOldAccessible) {
|
|
|
|
this._accessible = aAccessible;
|
|
|
|
this._oldAccessible =
|
|
|
|
this._isDefunct(aOldAccessible) ? null : aOldAccessible;
|
|
|
|
}
|
|
|
|
|
|
|
|
PivotContext.prototype = {
|
|
|
|
get accessible() {
|
|
|
|
return this._accessible;
|
|
|
|
},
|
|
|
|
|
|
|
|
get oldAccessible() {
|
|
|
|
return this._oldAccessible;
|
|
|
|
},
|
|
|
|
|
2013-06-27 14:15:36 -07:00
|
|
|
/**
|
|
|
|
* 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;
|
|
|
|
while (parent && (parent = parent.parent)) {
|
|
|
|
ancestry.push(parent);
|
|
|
|
}
|
|
|
|
return ancestry.reverse();
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A list of the old accessible's ancestry.
|
|
|
|
*/
|
|
|
|
get oldAncestry() {
|
|
|
|
if (!this._oldAncestry) {
|
|
|
|
if (!this._oldAccessible) {
|
|
|
|
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._getAncestry(this._accessible);
|
|
|
|
}
|
|
|
|
return this._currentAncestry;
|
|
|
|
},
|
|
|
|
|
2013-04-04 15:16:37 -07:00
|
|
|
/*
|
|
|
|
* 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) {
|
2013-06-27 14:15:36 -07:00
|
|
|
this._newAncestry = [currentAncestor for (
|
|
|
|
[index, currentAncestor] of Iterator(this.currentAncestry)) if (
|
|
|
|
currentAncestor !== this.oldAncestry[index])];
|
2013-04-04 15:16:37 -07:00
|
|
|
}
|
|
|
|
return this._newAncestry;
|
|
|
|
},
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Traverse the accessible's subtree in pre or post order.
|
|
|
|
* It only includes the accessible's visible chidren.
|
2013-06-10 13:31:17 -07:00
|
|
|
* Note: needSubtree is a function argument that can be used to determine
|
|
|
|
* whether aAccessible's subtree is required.
|
2013-04-04 15:16:37 -07:00
|
|
|
*/
|
2013-06-10 13:31:17 -07:00
|
|
|
_traverse: function _traverse(aAccessible, aPreorder, aStop) {
|
|
|
|
if (aStop && aStop(aAccessible)) {
|
|
|
|
return;
|
|
|
|
}
|
2013-04-04 15:16:37 -07:00
|
|
|
let child = aAccessible.firstChild;
|
|
|
|
while (child) {
|
|
|
|
let state = {};
|
|
|
|
child.getState(state, {});
|
|
|
|
if (!(state.value & Ci.nsIAccessibleStates.STATE_INVISIBLE)) {
|
2013-06-10 13:31:17 -07:00
|
|
|
if (aPreorder) {
|
|
|
|
yield child;
|
|
|
|
[yield node for (node of this._traverse(child, aPreorder, aStop))];
|
|
|
|
} else {
|
|
|
|
[yield node for (node of this._traverse(child, aPreorder, aStop))];
|
|
|
|
yield child;
|
|
|
|
}
|
2013-04-04 15:16:37 -07:00
|
|
|
}
|
|
|
|
child = child.nextSibling;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
/*
|
2013-06-10 13:31:17 -07:00
|
|
|
* A subtree generator function, used to generate a flattened
|
|
|
|
* list of the accessible's subtree in pre or post order.
|
2013-04-04 15:16:37 -07:00
|
|
|
* It only includes the accessible's visible chidren.
|
2013-06-10 13:31:17 -07:00
|
|
|
* @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.
|
2013-04-04 15:16:37 -07:00
|
|
|
*/
|
2013-06-10 13:31:17 -07:00
|
|
|
subtreeGenerator: function subtreeGenerator(aPreorder, aStop) {
|
|
|
|
return this._traverse(this._accessible, aPreorder, aStop);
|
2013-04-04 15:16:37 -07:00
|
|
|
},
|
|
|
|
|
2013-06-27 14:15:36 -07:00
|
|
|
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;
|
|
|
|
}
|
2013-07-01 20:50:39 -07:00
|
|
|
if ([ROLE_CELL, ROLE_COLUMNHEADER, ROLE_ROWHEADER].indexOf(
|
|
|
|
aAccessible.role) < 0) {
|
|
|
|
return null;
|
2013-06-27 14:15:36 -07:00
|
|
|
}
|
|
|
|
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 !==
|
2013-07-01 20:50:39 -07:00
|
|
|
ROLE_COLUMNHEADER) {
|
2013-06-27 14:15:36 -07:00
|
|
|
cellInfo.columnHeaders = [headers for (headers of getHeaders(
|
|
|
|
cellInfo.current.columnHeaderCells))];
|
|
|
|
}
|
|
|
|
cellInfo.rowHeaders = [];
|
2013-07-01 20:50:39 -07:00
|
|
|
if (cellInfo.rowChanged && cellInfo.current.role === ROLE_CELL) {
|
2013-06-27 14:15:36 -07:00
|
|
|
cellInfo.rowHeaders = [headers for (headers of getHeaders(
|
|
|
|
cellInfo.current.rowHeaderCells))];
|
|
|
|
}
|
|
|
|
|
|
|
|
this._cells.set(domNode, cellInfo);
|
|
|
|
return cellInfo;
|
|
|
|
},
|
|
|
|
|
2013-04-04 15:16:37 -07:00
|
|
|
get bounds() {
|
|
|
|
if (!this._bounds) {
|
2013-07-03 15:02:44 -07:00
|
|
|
this._bounds = Utils.getBounds(this._accessible);
|
2013-04-04 15:16:37 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
return this._bounds.clone();
|
|
|
|
},
|
|
|
|
|
|
|
|
_isDefunct: function _isDefunct(aAccessible) {
|
|
|
|
try {
|
|
|
|
let extstate = {};
|
|
|
|
aAccessible.getState({}, extstate);
|
2013-07-03 15:02:44 -07:00
|
|
|
return !!(extstate.value & Ci.nsIAccessibleStates.EXT_STATE_DEFUNCT);
|
2013-04-04 15:16:37 -07:00
|
|
|
} catch (x) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
2013-05-24 11:57:28 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
this.PrefCache = function PrefCache(aName, aCallback, aRunCallbackNow) {
|
|
|
|
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);
|
|
|
|
} catch (x) {
|
|
|
|
Logger.logException(x);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
branch.addObserver(aName, this, true);
|
|
|
|
};
|
|
|
|
|
|
|
|
PrefCache.prototype = {
|
|
|
|
_getValue: function _getValue(aBranch) {
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
observe: function observe(aSubject, aTopic, aData) {
|
|
|
|
this.value = this._getValue(aSubject.QueryInterface(Ci.nsIPrefBranch));
|
|
|
|
if (this.callback) {
|
|
|
|
try {
|
|
|
|
this.callback(this.name, this.value);
|
|
|
|
} catch (x) {
|
|
|
|
Logger.logException(x);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
QueryInterface : XPCOMUtils.generateQI([Ci.nsIObserver,
|
|
|
|
Ci.nsISupportsWeakReference])
|
2013-07-17 13:41:29 -07:00
|
|
|
};
|