Skip to content

Commit

Permalink
Refactor the way that Mozilla NVDAObject overlay classes are found. T…
Browse files Browse the repository at this point in the history
…he code is now isolated in the NVDAObjects.IAccessible.mozilla module.

Aside from cleaner code, the following practical enhancements were made:
	* Handle changes in the way that object descriptions are exposed by Gecko 2.0 (in Firefox 4). This means that object descriptions can now be read in Firefox 4.
	* Better handle focus and reporting of XUL applications such as the Add-ons Manager in Firefox 4.
	* Interrupt speech when a window (such as a dialog) pops up in Firefox 4.

Fixes #630.
  • Loading branch information
jcsteh committed Jul 28, 2010
2 parents d8525a7 + ebd3927 commit 60c9860
Show file tree
Hide file tree
Showing 3 changed files with 97 additions and 49 deletions.
15 changes: 2 additions & 13 deletions source/NVDAObjects/IAccessible/__init__.py
Expand Up @@ -377,8 +377,8 @@ def findOverlayClasses(self,clsList):
from . import adobeFlash
adobeFlash.findExtraOverlayClasses(self, clsList)
elif windowClassName.startswith('Mozilla'):
from .mozilla import Mozilla
clsList.append( Mozilla)
from . import mozilla
mozilla.findExtraOverlayClasses(self, clsList)
elif windowClassName.startswith('bosa_sdm'):
from .msOffice import SDM
clsList.append(SDM)
Expand Down Expand Up @@ -1450,17 +1450,6 @@ def _get_lastChild(self):
("TrayClockWClass",oleacc.ROLE_SYSTEM_CLIENT):"TrayClockWClass",
("TRxRichEdit",oleacc.ROLE_SYSTEM_CLIENT):"delphi.TRxRichEdit",
(None,oleacc.ROLE_SYSTEM_OUTLINEITEM):"OutlineItem",
("MozillaDialogClass",oleacc.ROLE_SYSTEM_ALERT):"Dialog",
("MozillaContentWindowClass",oleacc.ROLE_SYSTEM_COMBOBOX):"mozilla.ComboBox",
("MozillaContentWindowClass",oleacc.ROLE_SYSTEM_LIST):"mozilla.List",
("MozillaWindowClass",oleacc.ROLE_SYSTEM_LISTITEM):"mozilla.ListItem",
("MozillaContentWindowClass",oleacc.ROLE_SYSTEM_LISTITEM):"mozilla.ListItem",
("MozillaContentWindowClass",oleacc.ROLE_SYSTEM_DOCUMENT):"mozilla.Document",
("MozillaWindowClass",oleacc.ROLE_SYSTEM_DOCUMENT):"mozilla.Document",
("MozillaUIWindowClass",oleacc.ROLE_SYSTEM_TABLE):"mozilla.Table",
("MozillaUIWindowClass",oleacc.ROLE_SYSTEM_OUTLINE):"mozilla.Tree",
("MozillaContentWindowClass",IAccessibleHandler.IA2_ROLE_EMBEDDED_OBJECT):"mozilla.EmbeddedObject",
("MozillaContentWindowClass","embed"):"mozilla.EmbeddedObject",
("ConsoleWindowClass",oleacc.ROLE_SYSTEM_WINDOW):"ConsoleWindowClass",
(None,oleacc.ROLE_SYSTEM_LIST):"List",
(None,oleacc.ROLE_SYSTEM_COMBOBOX):"ComboBox",
Expand Down
130 changes: 94 additions & 36 deletions source/NVDAObjects/IAccessible/mozilla.py
@@ -1,16 +1,16 @@
#NVDAObjects/IAccessible/mozilla.py
#A part of NonVisual Desktop Access (NVDA)
#Copyright (C) 2006-2007 NVDA Contributors <http://www.nvda-project.org/>
#This file is covered by the GNU General Public License.
#See the file COPYING for more details.
#Copyright (C) 2006-2010 Michael Curran <mick@kulgan.net>, James Teh <jamie@jantrid.net>

import IAccessibleHandler
import oleacc
import winUser
from comtypes import IServiceProvider, COMError
import eventHandler
import controlTypes
from . import IAccessible
import textInfos
from . import IAccessible, Dialog
from logHandler import log

class Mozilla(IAccessible):
Expand All @@ -22,13 +22,6 @@ def _get_beTransparentToMouse(self):
return True
return super(Mozilla,self).beTransparentToMouse

def _get_description(self):
rawDescription=super(Mozilla,self).description
if isinstance(rawDescription,basestring) and rawDescription.startswith('Description: '):
return rawDescription[13:]
else:
return ""

def _get_parent(self):
#Special code to support Mozilla node_child_of relation (for comboboxes)
res=IAccessibleHandler.accNavigate(self.IAccessibleObject,self.IAccessibleChildID,IAccessibleHandler.NAVRELATION_NODE_CHILD_OF)
Expand All @@ -38,36 +31,54 @@ def _get_parent(self):
return newObj
return super(Mozilla,self).parent

def _get_states(self):
states = super(Mozilla, self).states
if self.IAccessibleStates & oleacc.STATE_SYSTEM_MARQUEED:
states.add(controlTypes.STATE_CHECKABLE)
return states

class Gecko1_9(Mozilla):

def _get_description(self):
rawDescription=super(Mozilla,self).description
if isinstance(rawDescription,basestring) and rawDescription.startswith('Description: '):
return rawDescription[13:]
else:
return ""

def event_scrollingStart(self):
#Firefox 3.6 fires scrollingStart on leaf nodes which is not useful to us.
#Bounce the event up to the node's parent so that any possible virtualBuffers will detect it.
if self.role==controlTypes.ROLE_EDITABLETEXT and controlTypes.STATE_READONLY in self.states:
eventHandler.queueEvent("scrollingStart",self.parent)

def _get_states(self):
states = super(Mozilla, self).states
if self.IAccessibleStates & oleacc.STATE_SYSTEM_MARQUEED:
states.add(controlTypes.STATE_CHECKABLE)
return states
class BrokenFocusedState(Mozilla):
shouldAllowIAccessibleFocusEvent=True

class RootApplication(Mozilla):
"""Mozilla exposes a root application accessible as the parent of all top level frames.
See MozillaBug:555861.
This is non-standard; the top level accessible should be the top level window.
NVDA expects the standard behaviour, so we never want to see this object.
"""

def __nonzero__(self):
# As far as NVDA is concerned, this is a useless object.
return False

class Document(Mozilla):

shouldAllowIAccessibleFocusEvent=True
value=None

def _get_treeInterceptorClass(self):
states=self.states
if isinstance(self.IAccessibleObject,IAccessibleHandler.IAccessible2) and controlTypes.STATE_READONLY in states and controlTypes.STATE_BUSY not in states and self.windowClassName=="MozillaContentWindowClass":
if controlTypes.STATE_READONLY in states and controlTypes.STATE_BUSY not in states and self.windowClassName=="MozillaContentWindowClass":
import virtualBuffers.gecko_ia2
return virtualBuffers.gecko_ia2.Gecko_ia2
return super(Document,self).treeInterceptorClass

def _get_value(self):
return

class ListItem(Mozilla):

shouldAllowIAccessibleFocusEvent=True

def _get_name(self):
name=super(ListItem,self)._get_name()
if self.IAccessibleStates&oleacc.STATE_SYSTEM_READONLY:
Expand All @@ -82,20 +93,6 @@ def _get_children(self):
del children[0]
return children

class ComboBox(Mozilla):

shouldAllowIAccessibleFocusEvent=True

class List(Mozilla):

shouldAllowIAccessibleFocusEvent=True

class Table(Mozilla):
shouldAllowIAccessibleFocusEvent=True

class Tree(Mozilla):
shouldAllowIAccessibleFocusEvent=True

class EmbeddedObject(Mozilla):

def _get_shouldAllowIAccessibleFocusEvent(self):
Expand All @@ -105,3 +102,64 @@ def _get_shouldAllowIAccessibleFocusEvent(self):
# We don't want to override the focus event fired by the embedded object.
return False
return super(EmbeddedObject, self).shouldAllowIAccessibleFocusEvent

def _getGeckoVersion(obj):
appMod = obj.appModule
try:
return appMod._geckoVersion
except AttributeError:
pass
try:
ver = obj.IAccessibleObject.QueryInterface(IServiceProvider).QueryService(IAccessibleHandler.IAccessibleApplication._iid_, IAccessibleHandler.IAccessibleApplication).toolkitVersion
except COMError:
return None
appMod._geckoVersion = ver
return ver

def findExtraOverlayClasses(obj, clsList):
"""Determine the most appropriate class if this is a Mozilla object.
This works similarly to L{NVDAObjects.NVDAObject.findOverlayClasses} except that it never calls any other findOverlayClasses method.
"""
if not isinstance(obj.IAccessibleObject, IAccessibleHandler.IAccessible2):
# We require IAccessible2; i.e. Gecko >= 1.9.
return

iaRole = obj.IAccessibleRole
cls = None
if iaRole == oleacc.ROLE_SYSTEM_APPLICATION:
try:
if not obj.IAccessibleObject.windowHandle:
cls = RootApplication
except COMError:
pass
if not cls:
cls = _IAccessibleRolesToOverlayClasses.get(iaRole)
if cls:
clsList.append(cls)
if iaRole in _IAccessibleRolesWithBrokenFocusedState:
clsList.append(BrokenFocusedState)

if _getGeckoVersion(obj).startswith("1.9"):
clsList.append(Gecko1_9)

clsList.append(Mozilla)

#: Maps IAccessible roles to NVDAObject overlay classes.
_IAccessibleRolesToOverlayClasses = {
oleacc.ROLE_SYSTEM_ALERT: Dialog,
oleacc.ROLE_SYSTEM_LISTITEM: ListItem,
oleacc.ROLE_SYSTEM_DOCUMENT: Document,
IAccessibleHandler.IA2_ROLE_EMBEDDED_OBJECT: EmbeddedObject,
"embed": EmbeddedObject,
}

#: Roles that mightn't set the focused state when they are focused.
_IAccessibleRolesWithBrokenFocusedState = frozenset((
oleacc.ROLE_SYSTEM_COMBOBOX,
oleacc.ROLE_SYSTEM_LIST,
oleacc.ROLE_SYSTEM_LISTITEM,
oleacc.ROLE_SYSTEM_DOCUMENT,
oleacc.ROLE_SYSTEM_APPLICATION,
oleacc.ROLE_SYSTEM_TABLE,
oleacc.ROLE_SYSTEM_OUTLINE,
))
1 change: 1 addition & 0 deletions source/speech.py
Expand Up @@ -431,6 +431,7 @@ def speakTypedCharacters(ch):
controlTypes.ROLE_RADIOBUTTON,
controlTypes.ROLE_LINK,
controlTypes.ROLE_MENUITEM,
controlTypes.ROLE_APPLICATION,
])

def processPositiveStates(role, states, reason, positiveStates):
Expand Down

0 comments on commit 60c9860

Please sign in to comment.