From 46acb0203b24c3415bcb5b279410ad78db0e676d Mon Sep 17 00:00:00 2001 From: Eitan Isaacson Date: Wed, 29 Jan 2014 15:08:53 -0800 Subject: [PATCH] Bug 963373 - Update cursor after scroll. r=yzen --- accessible/src/jsat/EventManager.jsm | 19 ++++ accessible/src/jsat/TraversalRules.jsm | 122 +++++++++++++------------ accessible/src/jsat/Utils.jsm | 3 +- 3 files changed, 86 insertions(+), 58 deletions(-) diff --git a/accessible/src/jsat/EventManager.jsm b/accessible/src/jsat/EventManager.jsm index 643d0b8a63e..b32308dcd51 100644 --- a/accessible/src/jsat/EventManager.jsm +++ b/accessible/src/jsat/EventManager.jsm @@ -57,6 +57,7 @@ this.EventManager.prototype = { this.webProgress.addProgressListener(this, (Ci.nsIWebProgress.NOTIFY_STATE_ALL | Ci.nsIWebProgress.NOTIFY_LOCATION)); + this.addEventListener('wheel', this, true); this.addEventListener('scroll', this, true); this.addEventListener('resize', this, true); } @@ -77,6 +78,7 @@ this.EventManager.prototype = { AccessibilityEventObserver.removeListener(this); try { this.webProgress.removeProgressListener(this); + this.removeEventListener('wheel', this, true); this.removeEventListener('scroll', this, true); this.removeEventListener('resize', this, true); } catch (x) { @@ -89,6 +91,23 @@ this.EventManager.prototype = { handleEvent: function handleEvent(aEvent) { try { switch (aEvent.type) { + case 'wheel': + { + let attempts = 0; + let vc = Utils.getVirtualCursor(this.contentScope.content.document); + let intervalId = this.contentScope.content.setInterval(() => { + if (!Utils.isAliveAndVisible(vc.position, true)) { + this.contentScope.content.clearInterval(intervalId); + let delta = aEvent.deltaX || aEvent.deltaY; + this.contentScope.content.setTimeout(() => { + vc[delta > 0 ? 'moveNext' : 'movePrevious'](TraversalRules.SimpleOnScreen); + }, 100); + } else if (++attempts > 5) { + this.contentScope.content.clearInterval(intervalId); + } + }, 150); + break; + } case 'scroll': case 'resize': { diff --git a/accessible/src/jsat/TraversalRules.jsm b/accessible/src/jsat/TraversalRules.jsm index 9830a6aa036..0ad1c4de329 100644 --- a/accessible/src/jsat/TraversalRules.jsm +++ b/accessible/src/jsat/TraversalRules.jsm @@ -22,13 +22,14 @@ XPCOMUtils.defineLazyModuleGetter(this, 'States', let gSkipEmptyImages = new PrefCache('accessibility.accessfu.skip_empty_images'); -function BaseTraversalRule(aRoles, aMatchFunc) { +function BaseTraversalRule(aRoles, aMatchFunc, aPreFilter) { this._explicitMatchRoles = new Set(aRoles); this._matchRoles = aRoles; if (aRoles.indexOf(Roles.LABEL) < 0) { this._matchRoles.push(Roles.LABEL); } this._matchFunc = aMatchFunc || function (acc) { return Filters.MATCH; }; + this.preFilter = aPreFilter || gSimplePreFilter; } BaseTraversalRule.prototype = { @@ -37,11 +38,6 @@ BaseTraversalRule.prototype = { return aRules.value.length; }, - preFilter: Ci.nsIAccessibleTraversalRule.PREFILTER_DEFUNCT | - Ci.nsIAccessibleTraversalRule.PREFILTER_INVISIBLE | - Ci.nsIAccessibleTraversalRule.PREFILTER_ARIA_HIDDEN | - Ci.nsIAccessibleTraversalRule.PREFILTER_TRANSPARENT, - match: function BaseTraversalRule_match(aAccessible) { let role = aAccessible.role; @@ -96,59 +92,71 @@ var gSimpleTraversalRoles = // Used for traversing in to child OOP frames. Roles.INTERNAL_FRAME]; -this.TraversalRules = { - Simple: new BaseTraversalRule( - gSimpleTraversalRoles, - function Simple_match(aAccessible) { - function hasZeroOrSingleChildDescendants () { - for (let acc = aAccessible; acc.childCount > 0; acc = acc.firstChild) { - if (acc.childCount > 1) { - return false; - } - } - - return true; - } - - switch (aAccessible.role) { - case Roles.COMBOBOX: - // We don't want to ignore the subtree because this is often - // where the list box hangs out. - return Filters.MATCH; - case Roles.TEXT_LEAF: - { - // Nameless text leaves are boring, skip them. - let name = aAccessible.name; - if (name && name.trim()) - return Filters.MATCH; - else - return Filters.IGNORE; - } - case Roles.STATICTEXT: - { - let parent = aAccessible.parent; - // Ignore prefix static text in list items. They are typically bullets or numbers. - if (parent.childCount > 1 && aAccessible.indexInParent == 0 && - parent.role == Roles.LISTITEM) - return Filters.IGNORE; - - return Filters.MATCH; - } - case Roles.GRAPHIC: - return TraversalRules._shouldSkipImage(aAccessible); - case Roles.LINK: - case Roles.HEADER: - case Roles.HEADING: - return hasZeroOrSingleChildDescendants() ? - (Filters.MATCH | Filters.IGNORE_SUBTREE) : (Filters.IGNORE); - 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. - return Filters.MATCH | - Filters.IGNORE_SUBTREE; +var gSimpleMatchFunc = function gSimpleMatchFunc(aAccessible) { + function hasZeroOrSingleChildDescendants () { + for (let acc = aAccessible; acc.childCount > 0; acc = acc.firstChild) { + if (acc.childCount > 1) { + return false; } } - ), + + return true; + } + + switch (aAccessible.role) { + case Roles.COMBOBOX: + // We don't want to ignore the subtree because this is often + // where the list box hangs out. + return Filters.MATCH; + case Roles.TEXT_LEAF: + { + // Nameless text leaves are boring, skip them. + let name = aAccessible.name; + if (name && name.trim()) + return Filters.MATCH; + else + return Filters.IGNORE; + } + case Roles.STATICTEXT: + { + let parent = aAccessible.parent; + // Ignore prefix static text in list items. They are typically bullets or numbers. + if (parent.childCount > 1 && aAccessible.indexInParent == 0 && + parent.role == Roles.LISTITEM) + return Filters.IGNORE; + + return Filters.MATCH; + } + case Roles.GRAPHIC: + return TraversalRules._shouldSkipImage(aAccessible); + case Roles.LINK: + case Roles.HEADER: + case Roles.HEADING: + return hasZeroOrSingleChildDescendants() ? + (Filters.MATCH | Filters.IGNORE_SUBTREE) : (Filters.IGNORE); + 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. + return Filters.MATCH | + Filters.IGNORE_SUBTREE; + } +}; + +var gSimplePreFilter = Ci.nsIAccessibleTraversalRule.PREFILTER_DEFUNCT | + Ci.nsIAccessibleTraversalRule.PREFILTER_INVISIBLE | + Ci.nsIAccessibleTraversalRule.PREFILTER_ARIA_HIDDEN | + Ci.nsIAccessibleTraversalRule.PREFILTER_TRANSPARENT; + +this.TraversalRules = { + Simple: new BaseTraversalRule(gSimpleTraversalRoles, gSimpleMatchFunc), + + SimpleOnScreen: new BaseTraversalRule( + gSimpleTraversalRoles, gSimpleMatchFunc, + Ci.nsIAccessibleTraversalRule.PREFILTER_DEFUNCT | + Ci.nsIAccessibleTraversalRule.PREFILTER_INVISIBLE | + Ci.nsIAccessibleTraversalRule.PREFILTER_ARIA_HIDDEN | + Ci.nsIAccessibleTraversalRule.PREFILTER_TRANSPARENT | + Ci.nsIAccessibleTraversalRule.PREFILTER_OFFSCREEN), Anchor: new BaseTraversalRule( [Roles.LINK], diff --git a/accessible/src/jsat/Utils.jsm b/accessible/src/jsat/Utils.jsm index 61ccb961470..e87cd7e470d 100644 --- a/accessible/src/jsat/Utils.jsm +++ b/accessible/src/jsat/Utils.jsm @@ -269,7 +269,7 @@ this.Utils = { return false; }, - isAliveAndVisible: function isAliveAndVisible(aAccessible) { + isAliveAndVisible: function isAliveAndVisible(aAccessible, aIsOnScreen) { if (!aAccessible) { return false; } @@ -277,6 +277,7 @@ this.Utils = { 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; }