2012-10-01 13:33:26 -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 Cc = Components.classes;
|
|
|
|
const Ci = Components.interfaces;
|
|
|
|
const Cu = Components.utils;
|
|
|
|
const Cr = Components.results;
|
|
|
|
|
2013-06-27 14:15:37 -07:00
|
|
|
const FILTER_IGNORE = Ci.nsIAccessibleTraversalRule.FILTER_IGNORE;
|
|
|
|
const FILTER_MATCH = Ci.nsIAccessibleTraversalRule.FILTER_MATCH;
|
|
|
|
const FILTER_IGNORE_SUBTREE = Ci.nsIAccessibleTraversalRule.FILTER_IGNORE_SUBTREE;
|
|
|
|
|
2013-07-01 20:50:39 -07:00
|
|
|
const ROLE_MENUITEM = Ci.nsIAccessibleRole.ROLE_MENUITEM;
|
|
|
|
const ROLE_LINK = Ci.nsIAccessibleRole.ROLE_LINK;
|
|
|
|
const ROLE_PAGETAB = Ci.nsIAccessibleRole.ROLE_PAGETAB;
|
|
|
|
const ROLE_GRAPHIC = Ci.nsIAccessibleRole.ROLE_GRAPHIC;
|
|
|
|
const ROLE_STATICTEXT = Ci.nsIAccessibleRole.ROLE_STATICTEXT;
|
|
|
|
const ROLE_TEXT_LEAF = Ci.nsIAccessibleRole.ROLE_TEXT_LEAF;
|
|
|
|
const ROLE_PUSHBUTTON = Ci.nsIAccessibleRole.ROLE_PUSHBUTTON;
|
|
|
|
const ROLE_SPINBUTTON = Ci.nsIAccessibleRole.ROLE_SPINBUTTON;
|
|
|
|
const ROLE_CHECKBUTTON = Ci.nsIAccessibleRole.ROLE_CHECKBUTTON;
|
|
|
|
const ROLE_RADIOBUTTON = Ci.nsIAccessibleRole.ROLE_RADIOBUTTON;
|
|
|
|
const ROLE_COMBOBOX = Ci.nsIAccessibleRole.ROLE_COMBOBOX;
|
|
|
|
const ROLE_PROGRESSBAR = Ci.nsIAccessibleRole.ROLE_PROGRESSBAR;
|
|
|
|
const ROLE_BUTTONDROPDOWN = Ci.nsIAccessibleRole.ROLE_BUTTONDROPDOWN;
|
|
|
|
const ROLE_BUTTONMENU = Ci.nsIAccessibleRole.ROLE_BUTTONMENU;
|
|
|
|
const ROLE_CHECK_MENU_ITEM = Ci.nsIAccessibleRole.ROLE_CHECK_MENU_ITEM;
|
|
|
|
const ROLE_PASSWORD_TEXT = Ci.nsIAccessibleRole.ROLE_PASSWORD_TEXT;
|
|
|
|
const ROLE_RADIO_MENU_ITEM = Ci.nsIAccessibleRole.ROLE_RADIO_MENU_ITEM;
|
|
|
|
const ROLE_TOGGLE_BUTTON = Ci.nsIAccessibleRole.ROLE_TOGGLE_BUTTON;
|
|
|
|
const ROLE_ENTRY = Ci.nsIAccessibleRole.ROLE_ENTRY;
|
|
|
|
const ROLE_LIST = Ci.nsIAccessibleRole.ROLE_LIST;
|
|
|
|
const ROLE_DEFINITION_LIST = Ci.nsIAccessibleRole.ROLE_DEFINITION_LIST;
|
|
|
|
const ROLE_LISTITEM = Ci.nsIAccessibleRole.ROLE_LISTITEM;
|
|
|
|
const ROLE_BUTTONDROPDOWNGRID = Ci.nsIAccessibleRole.ROLE_BUTTONDROPDOWNGRID;
|
|
|
|
const ROLE_LISTBOX = Ci.nsIAccessibleRole.ROLE_LISTBOX;
|
|
|
|
const ROLE_SLIDER = Ci.nsIAccessibleRole.ROLE_SLIDER;
|
|
|
|
const ROLE_HEADING = Ci.nsIAccessibleRole.ROLE_HEADING;
|
|
|
|
const ROLE_TERM = Ci.nsIAccessibleRole.ROLE_TERM;
|
|
|
|
const ROLE_SEPARATOR = Ci.nsIAccessibleRole.ROLE_SEPARATOR;
|
|
|
|
const ROLE_TABLE = Ci.nsIAccessibleRole.ROLE_TABLE;
|
|
|
|
const ROLE_INTERNAL_FRAME = Ci.nsIAccessibleRole.ROLE_INTERNAL_FRAME;
|
2013-07-24 14:52:57 -07:00
|
|
|
const ROLE_PARAGRAPH = Ci.nsIAccessibleRole.ROLE_PARAGRAPH;
|
|
|
|
const ROLE_SECTION = Ci.nsIAccessibleRole.ROLE_SECTION;
|
2013-07-01 20:50:39 -07:00
|
|
|
|
2012-10-31 09:13:28 -07:00
|
|
|
this.EXPORTED_SYMBOLS = ['TraversalRules'];
|
2012-10-01 13:33:26 -07:00
|
|
|
|
|
|
|
Cu.import('resource://gre/modules/accessibility/Utils.jsm');
|
|
|
|
Cu.import('resource://gre/modules/XPCOMUtils.jsm');
|
|
|
|
|
2013-06-17 13:25:24 -07:00
|
|
|
let gSkipEmptyImages = new PrefCache('accessibility.accessfu.skip_empty_images');
|
|
|
|
|
2012-10-01 13:33:26 -07:00
|
|
|
function BaseTraversalRule(aRoles, aMatchFunc) {
|
|
|
|
this._matchRoles = aRoles;
|
|
|
|
this._matchFunc = aMatchFunc;
|
|
|
|
}
|
|
|
|
|
|
|
|
BaseTraversalRule.prototype = {
|
|
|
|
getMatchRoles: function BaseTraversalRule_getmatchRoles(aRules) {
|
|
|
|
aRules.value = this._matchRoles;
|
|
|
|
return aRules.value.length;
|
|
|
|
},
|
|
|
|
|
|
|
|
preFilter: Ci.nsIAccessibleTraversalRule.PREFILTER_DEFUNCT |
|
2013-05-28 10:51:44 -07:00
|
|
|
Ci.nsIAccessibleTraversalRule.PREFILTER_INVISIBLE |
|
|
|
|
Ci.nsIAccessibleTraversalRule.PREFILTER_ARIA_HIDDEN,
|
2012-10-01 13:33:26 -07:00
|
|
|
|
|
|
|
match: function BaseTraversalRule_match(aAccessible)
|
|
|
|
{
|
2013-07-01 20:50:39 -07:00
|
|
|
if (aAccessible.role == ROLE_INTERNAL_FRAME) {
|
2013-05-21 11:16:50 -07:00
|
|
|
return (Utils.getMessageManager(aAccessible.DOMNode)) ?
|
2013-06-27 14:15:37 -07:00
|
|
|
FILTER_MATCH | FILTER_IGNORE_SUBTREE : FILTER_IGNORE;
|
2012-10-01 13:33:26 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
if (this._matchFunc)
|
|
|
|
return this._matchFunc(aAccessible);
|
|
|
|
|
2013-06-27 14:15:37 -07:00
|
|
|
return FILTER_MATCH;
|
2012-10-01 13:33:26 -07:00
|
|
|
},
|
|
|
|
|
|
|
|
QueryInterface: XPCOMUtils.generateQI([Ci.nsIAccessibleTraversalRule])
|
|
|
|
};
|
|
|
|
|
|
|
|
var gSimpleTraversalRoles =
|
2013-07-01 20:50:39 -07:00
|
|
|
[ROLE_MENUITEM,
|
|
|
|
ROLE_LINK,
|
|
|
|
ROLE_PAGETAB,
|
|
|
|
ROLE_GRAPHIC,
|
|
|
|
ROLE_STATICTEXT,
|
|
|
|
ROLE_TEXT_LEAF,
|
|
|
|
ROLE_PUSHBUTTON,
|
|
|
|
ROLE_CHECKBUTTON,
|
|
|
|
ROLE_RADIOBUTTON,
|
|
|
|
ROLE_COMBOBOX,
|
|
|
|
ROLE_PROGRESSBAR,
|
|
|
|
ROLE_BUTTONDROPDOWN,
|
|
|
|
ROLE_BUTTONMENU,
|
|
|
|
ROLE_CHECK_MENU_ITEM,
|
|
|
|
ROLE_PASSWORD_TEXT,
|
|
|
|
ROLE_RADIO_MENU_ITEM,
|
|
|
|
ROLE_TOGGLE_BUTTON,
|
|
|
|
ROLE_ENTRY,
|
2012-10-01 13:33:26 -07:00
|
|
|
// Used for traversing in to child OOP frames.
|
2013-07-01 20:50:39 -07:00
|
|
|
ROLE_INTERNAL_FRAME];
|
2012-10-01 13:33:26 -07:00
|
|
|
|
2012-10-31 09:13:28 -07:00
|
|
|
this.TraversalRules = {
|
2012-10-01 13:33:26 -07:00
|
|
|
Simple: new BaseTraversalRule(
|
|
|
|
gSimpleTraversalRoles,
|
|
|
|
function Simple_match(aAccessible) {
|
|
|
|
switch (aAccessible.role) {
|
2013-07-01 20:50:39 -07:00
|
|
|
case ROLE_COMBOBOX:
|
2012-10-01 13:33:26 -07:00
|
|
|
// We don't want to ignore the subtree because this is often
|
|
|
|
// where the list box hangs out.
|
2013-06-27 14:15:37 -07:00
|
|
|
return FILTER_MATCH;
|
2013-07-01 20:50:39 -07:00
|
|
|
case ROLE_TEXT_LEAF:
|
2012-10-01 13:33:26 -07:00
|
|
|
{
|
|
|
|
// Nameless text leaves are boring, skip them.
|
|
|
|
let name = aAccessible.name;
|
|
|
|
if (name && name.trim())
|
2013-06-27 14:15:37 -07:00
|
|
|
return FILTER_MATCH;
|
2012-10-01 13:33:26 -07:00
|
|
|
else
|
2013-06-27 14:15:37 -07:00
|
|
|
return FILTER_IGNORE;
|
2012-10-01 13:33:26 -07:00
|
|
|
}
|
2013-07-01 20:50:39 -07:00
|
|
|
case ROLE_LINK:
|
2012-10-01 13:33:26 -07:00
|
|
|
// If the link has children we should land on them instead.
|
|
|
|
// Image map links don't have children so we need to match those.
|
|
|
|
if (aAccessible.childCount == 0)
|
2013-06-27 14:15:37 -07:00
|
|
|
return FILTER_MATCH;
|
2012-10-01 13:33:26 -07:00
|
|
|
else
|
2013-06-27 14:15:37 -07:00
|
|
|
return FILTER_IGNORE;
|
2013-07-01 20:50:39 -07:00
|
|
|
case ROLE_STATICTEXT:
|
2012-11-20 11:06:38 -08:00
|
|
|
{
|
|
|
|
let parent = aAccessible.parent;
|
|
|
|
// Ignore prefix static text in list items. They are typically bullets or numbers.
|
|
|
|
if (parent.childCount > 1 && aAccessible.indexInParent == 0 &&
|
2013-07-01 20:50:39 -07:00
|
|
|
parent.role == ROLE_LISTITEM)
|
2013-06-27 14:15:37 -07:00
|
|
|
return FILTER_IGNORE;
|
2012-11-20 11:06:38 -08:00
|
|
|
|
2013-06-27 14:15:37 -07:00
|
|
|
return FILTER_MATCH;
|
2012-11-20 11:06:38 -08:00
|
|
|
}
|
2013-07-01 20:50:39 -07:00
|
|
|
case ROLE_GRAPHIC:
|
2013-06-17 13:25:24 -07:00
|
|
|
return TraversalRules._shouldSkipImage(aAccessible);
|
2012-10-01 13:33:26 -07:00
|
|
|
default:
|
|
|
|
// Ignore the subtree, if there is one. So that we don't land on
|
|
|
|
// the same content that was already presented by its parent.
|
2013-06-27 14:15:37 -07:00
|
|
|
return FILTER_MATCH |
|
|
|
|
FILTER_IGNORE_SUBTREE;
|
2012-10-01 13:33:26 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
),
|
|
|
|
|
|
|
|
SimpleTouch: new BaseTraversalRule(
|
|
|
|
gSimpleTraversalRoles,
|
|
|
|
function Simple_match(aAccessible) {
|
2013-06-27 14:15:37 -07:00
|
|
|
return FILTER_MATCH |
|
|
|
|
FILTER_IGNORE_SUBTREE;
|
2012-10-01 13:33:26 -07:00
|
|
|
}
|
|
|
|
),
|
|
|
|
|
|
|
|
Anchor: new BaseTraversalRule(
|
2013-07-01 20:50:39 -07:00
|
|
|
[ROLE_LINK],
|
2012-10-01 13:33:26 -07:00
|
|
|
function Anchor_match(aAccessible)
|
|
|
|
{
|
|
|
|
// We want to ignore links, only focus named anchors.
|
|
|
|
let state = {};
|
|
|
|
let extraState = {};
|
|
|
|
aAccessible.getState(state, extraState);
|
|
|
|
if (state.value & Ci.nsIAccessibleStates.STATE_LINKED) {
|
2013-06-27 14:15:37 -07:00
|
|
|
return FILTER_IGNORE;
|
2012-10-01 13:33:26 -07:00
|
|
|
} else {
|
2013-06-27 14:15:37 -07:00
|
|
|
return FILTER_MATCH;
|
2012-10-01 13:33:26 -07:00
|
|
|
}
|
|
|
|
}),
|
|
|
|
|
|
|
|
Button: new BaseTraversalRule(
|
2013-07-01 20:50:39 -07:00
|
|
|
[ROLE_PUSHBUTTON,
|
|
|
|
ROLE_SPINBUTTON,
|
|
|
|
ROLE_TOGGLE_BUTTON,
|
|
|
|
ROLE_BUTTONDROPDOWN,
|
|
|
|
ROLE_BUTTONDROPDOWNGRID]),
|
2012-10-01 13:33:26 -07:00
|
|
|
|
|
|
|
Combobox: new BaseTraversalRule(
|
2013-07-01 20:50:39 -07:00
|
|
|
[ROLE_COMBOBOX,
|
|
|
|
ROLE_LISTBOX]),
|
2012-10-01 13:33:26 -07:00
|
|
|
|
2013-07-11 13:42:11 -07:00
|
|
|
Landmark: new BaseTraversalRule(
|
|
|
|
[],
|
|
|
|
function Landmark_match(aAccessible) {
|
|
|
|
return Utils.getLandmarkName(aAccessible) ? FILTER_MATCH :
|
|
|
|
FILTER_IGNORE;
|
|
|
|
}
|
|
|
|
),
|
|
|
|
|
2012-10-01 13:33:26 -07:00
|
|
|
Entry: new BaseTraversalRule(
|
2013-07-01 20:50:39 -07:00
|
|
|
[ROLE_ENTRY,
|
|
|
|
ROLE_PASSWORD_TEXT]),
|
2012-10-01 13:33:26 -07:00
|
|
|
|
|
|
|
FormElement: new BaseTraversalRule(
|
2013-07-01 20:50:39 -07:00
|
|
|
[ROLE_PUSHBUTTON,
|
|
|
|
ROLE_SPINBUTTON,
|
|
|
|
ROLE_TOGGLE_BUTTON,
|
|
|
|
ROLE_BUTTONDROPDOWN,
|
|
|
|
ROLE_BUTTONDROPDOWNGRID,
|
|
|
|
ROLE_COMBOBOX,
|
|
|
|
ROLE_LISTBOX,
|
|
|
|
ROLE_ENTRY,
|
|
|
|
ROLE_PASSWORD_TEXT,
|
|
|
|
ROLE_PAGETAB,
|
|
|
|
ROLE_RADIOBUTTON,
|
|
|
|
ROLE_RADIO_MENU_ITEM,
|
|
|
|
ROLE_SLIDER,
|
|
|
|
ROLE_CHECKBUTTON,
|
|
|
|
ROLE_CHECK_MENU_ITEM]),
|
2012-10-01 13:33:26 -07:00
|
|
|
|
|
|
|
Graphic: new BaseTraversalRule(
|
2013-07-01 20:50:39 -07:00
|
|
|
[ROLE_GRAPHIC],
|
2013-06-17 13:25:24 -07:00
|
|
|
function Graphic_match(aAccessible) {
|
|
|
|
return TraversalRules._shouldSkipImage(aAccessible);
|
|
|
|
}),
|
2012-10-01 13:33:26 -07:00
|
|
|
|
|
|
|
Heading: new BaseTraversalRule(
|
2013-07-01 20:50:39 -07:00
|
|
|
[ROLE_HEADING]),
|
2012-10-01 13:33:26 -07:00
|
|
|
|
|
|
|
ListItem: new BaseTraversalRule(
|
2013-07-01 20:50:39 -07:00
|
|
|
[ROLE_LISTITEM,
|
|
|
|
ROLE_TERM]),
|
2012-10-01 13:33:26 -07:00
|
|
|
|
|
|
|
Link: new BaseTraversalRule(
|
2013-07-01 20:50:39 -07:00
|
|
|
[ROLE_LINK],
|
2012-10-01 13:33:26 -07:00
|
|
|
function Link_match(aAccessible)
|
|
|
|
{
|
|
|
|
// We want to ignore anchors, only focus real links.
|
|
|
|
let state = {};
|
|
|
|
let extraState = {};
|
|
|
|
aAccessible.getState(state, extraState);
|
|
|
|
if (state.value & Ci.nsIAccessibleStates.STATE_LINKED) {
|
2013-06-27 14:15:37 -07:00
|
|
|
return FILTER_MATCH;
|
2012-10-01 13:33:26 -07:00
|
|
|
} else {
|
2013-06-27 14:15:37 -07:00
|
|
|
return FILTER_IGNORE;
|
2012-10-01 13:33:26 -07:00
|
|
|
}
|
|
|
|
}),
|
|
|
|
|
|
|
|
List: new BaseTraversalRule(
|
2013-07-01 20:50:39 -07:00
|
|
|
[ROLE_LIST,
|
|
|
|
ROLE_DEFINITION_LIST]),
|
2012-10-01 13:33:26 -07:00
|
|
|
|
|
|
|
PageTab: new BaseTraversalRule(
|
2013-07-01 20:50:39 -07:00
|
|
|
[ROLE_PAGETAB]),
|
2012-10-01 13:33:26 -07:00
|
|
|
|
2013-07-24 14:52:57 -07:00
|
|
|
Paragraph: new BaseTraversalRule(
|
|
|
|
[ROLE_PARAGRAPH,
|
|
|
|
ROLE_SECTION],
|
|
|
|
function Paragraph_match(aAccessible) {
|
|
|
|
for (let child = aAccessible.firstChild; child; child = child.nextSibling) {
|
|
|
|
if (child.role === ROLE_TEXT_LEAF) {
|
|
|
|
return FILTER_MATCH | FILTER_IGNORE_SUBTREE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return FILTER_IGNORE;
|
|
|
|
}),
|
|
|
|
|
2012-10-01 13:33:26 -07:00
|
|
|
RadioButton: new BaseTraversalRule(
|
2013-07-01 20:50:39 -07:00
|
|
|
[ROLE_RADIOBUTTON,
|
|
|
|
ROLE_RADIO_MENU_ITEM]),
|
2012-10-01 13:33:26 -07:00
|
|
|
|
|
|
|
Separator: new BaseTraversalRule(
|
2013-07-01 20:50:39 -07:00
|
|
|
[ROLE_SEPARATOR]),
|
2012-10-01 13:33:26 -07:00
|
|
|
|
|
|
|
Table: new BaseTraversalRule(
|
2013-07-01 20:50:39 -07:00
|
|
|
[ROLE_TABLE]),
|
2012-10-01 13:33:26 -07:00
|
|
|
|
|
|
|
Checkbox: new BaseTraversalRule(
|
2013-07-01 20:50:39 -07:00
|
|
|
[ROLE_CHECKBUTTON,
|
|
|
|
ROLE_CHECK_MENU_ITEM]),
|
2013-06-17 13:25:24 -07:00
|
|
|
|
|
|
|
_shouldSkipImage: function _shouldSkipImage(aAccessible) {
|
|
|
|
if (gSkipEmptyImages.value && aAccessible.name === '') {
|
2013-06-27 14:15:37 -07:00
|
|
|
return FILTER_IGNORE;
|
2013-06-17 13:25:24 -07:00
|
|
|
}
|
2013-06-27 14:15:37 -07:00
|
|
|
return FILTER_MATCH;
|
2013-06-17 13:25:24 -07:00
|
|
|
}
|
2012-10-01 13:33:26 -07:00
|
|
|
};
|