[orca] Add initial Chromium script
- From: Joanmarie Diggs <joanied src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [orca] Add initial Chromium script
- Date: Thu, 1 Nov 2018 13:07:30 +0000 (UTC)
commit 0391f91ad855426edc216f8ff7c41263a1738511
Author: Joanmarie Diggs <jdiggs igalia com>
Date: Thu Nov 1 14:04:20 2018 +0100
Add initial Chromium script
Please note: ATK support in Chromium needs much work. Until that work has
been done, Orca will not be able to provide access to Chromium. This
script is very much a work in progress and not yet ready for end-user
testing.
configure.ac | 1 +
src/orca/scripts/toolkits/Chromium/Makefile.am | 8 +
src/orca/scripts/toolkits/Chromium/__init__.py | 22 ++
.../scripts/toolkits/Chromium/braille_generator.py | 60 ++++
src/orca/scripts/toolkits/Chromium/script.py | 368 +++++++++++++++++++++
.../scripts/toolkits/Chromium/script_utilities.py | 159 +++++++++
.../scripts/toolkits/Chromium/speech_generator.py | 60 ++++
src/orca/scripts/toolkits/Makefile.am | 2 +-
src/orca/scripts/toolkits/__init__.py | 1 +
9 files changed, 680 insertions(+), 1 deletion(-)
---
diff --git a/configure.ac b/configure.ac
index 8d701dd69..2454476e4 100644
--- a/configure.ac
+++ b/configure.ac
@@ -122,6 +122,7 @@ src/orca/scripts/apps/xfwm4/Makefile
src/orca/scripts/terminal/Makefile
src/orca/scripts/web/Makefile
src/orca/scripts/toolkits/Makefile
+src/orca/scripts/toolkits/Chromium/Makefile
src/orca/scripts/toolkits/Gecko/Makefile
src/orca/scripts/toolkits/J2SE-access-bridge/Makefile
src/orca/scripts/toolkits/clutter/Makefile
diff --git a/src/orca/scripts/toolkits/Chromium/Makefile.am b/src/orca/scripts/toolkits/Chromium/Makefile.am
new file mode 100644
index 000000000..64e94b895
--- /dev/null
+++ b/src/orca/scripts/toolkits/Chromium/Makefile.am
@@ -0,0 +1,8 @@
+orca_python_PYTHON = \
+ __init__.py \
+ braille_generator.py \
+ script.py \
+ script_utilities.py \
+ speech_generator.py
+
+orca_pythondir=$(pkgpythondir)/scripts/toolkits/Chromium
diff --git a/src/orca/scripts/toolkits/Chromium/__init__.py b/src/orca/scripts/toolkits/Chromium/__init__.py
new file mode 100644
index 000000000..af00bc874
--- /dev/null
+++ b/src/orca/scripts/toolkits/Chromium/__init__.py
@@ -0,0 +1,22 @@
+# Orca
+#
+# Copyright 2018 Igalia, S.L.
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the
+# Free Software Foundation, Inc., Franklin Street, Fifth Floor,
+# Boston MA 02110-1301 USA.
+
+"""Custom script for Chromium."""
+
+from .script import Script
diff --git a/src/orca/scripts/toolkits/Chromium/braille_generator.py
b/src/orca/scripts/toolkits/Chromium/braille_generator.py
new file mode 100644
index 000000000..891a30a45
--- /dev/null
+++ b/src/orca/scripts/toolkits/Chromium/braille_generator.py
@@ -0,0 +1,60 @@
+# Orca
+#
+# Copyright 2018 Igalia, S.L.
+#
+# Author: Joanmarie Diggs <jdiggs igalia com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the
+# Free Software Foundation, Inc., Franklin Street, Fifth Floor,
+# Boston MA 02110-1301 USA.
+
+"""Custom braille generator for Chromium."""
+
+# Please note: ATK support in Chromium needs much work. Until that work has been
+# done, Orca will not be able to provide access to Chromium. This generator is a
+# work in progress.
+
+__id__ = "$Id$"
+__version__ = "$Revision$"
+__date__ = "$Date$"
+__copyright__ = "Copyright (c) 2018 Igalia, S.L."
+__license__ = "LGPL"
+
+import pyatspi
+
+from orca import debug
+from orca import orca_state
+from orca.scripts import web
+
+
+class BrailleGenerator(web.BrailleGenerator):
+
+ def __init__(self, script):
+ super().__init__(script)
+
+ def generateBraille(self, obj, **args):
+ if self._script.utilities.inDocumentContent(obj):
+ return super().generateBraille(obj, **args)
+
+ oldRole = None
+ if self._script.utilities.treatAsMenu(obj):
+ msg = "CHROMIUM: HACK? Displaying menu item as menu %s" % obj
+ debug.println(debug.LEVEL_INFO, msg, True)
+ oldRole = self._overrideRole(pyatspi.ROLE_MENU, args)
+
+ result = super().generateBraille(obj, **args)
+ if oldRole is not None:
+ self._restoreRole(oldRole, args)
+
+ return result
diff --git a/src/orca/scripts/toolkits/Chromium/script.py b/src/orca/scripts/toolkits/Chromium/script.py
new file mode 100644
index 000000000..f87a0e28a
--- /dev/null
+++ b/src/orca/scripts/toolkits/Chromium/script.py
@@ -0,0 +1,368 @@
+# Orca
+#
+# Copyright 2018 Igalia, S.L.
+#
+# Author: Joanmarie Diggs <jdiggs igalia com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the
+# Free Software Foundation, Inc., Franklin Street, Fifth Floor,
+# Boston MA 02110-1301 USA.
+
+"""Custom script for Chromium."""
+
+# Please note: ATK support in Chromium needs much work. Until that work has been
+# done, Orca will not be able to provide access to Chromium. This script is a
+# work in progress.
+
+__id__ = "$Id$"
+__version__ = "$Revision$"
+__date__ = "$Date$"
+__copyright__ = "Copyright (c) 2018 Igalia, S.L."
+__license__ = "LGPL"
+
+import pyatspi
+
+from orca import debug
+from orca import orca
+from orca.scripts import default
+from orca.scripts import web
+from .braille_generator import BrailleGenerator
+from .script_utilities import Utilities
+from .speech_generator import SpeechGenerator
+
+
+class Script(web.Script):
+
+ def __init__(self, app):
+ super().__init__(app)
+
+ self.presentIfInactive = False
+
+ # Normally we don't cache the name, because we cannot count on apps and
+ # tookits emitting name-changed events (needed by AT-SPI2 to update the
+ # name so that we don't have stale values). However, if we don't cache
+ # the name, we wind up with dead accessibles in (at least) the search bar
+ # popup. For now, cache the name to get things working and clear the cache
+ # for objects we plan to present. http://crbug.com/896706
+ app.setCacheMask(pyatspi.cache.DEFAULT ^ pyatspi.cache.CHILDREN)
+
+ def getBrailleGenerator(self):
+ """Returns the braille generator for this script."""
+
+ return BrailleGenerator(self)
+
+ def getSpeechGenerator(self):
+ """Returns the speech generator for this script."""
+
+ return SpeechGenerator(self)
+
+ def getUtilities(self):
+ """Returns the utilites for this script."""
+
+ return Utilities(self)
+
+ def isActivatableEvent(self, event):
+ """Returns True if this event should activate this script."""
+
+ if event.type == "window:activate":
+ return self.utilities.canBeActiveWindow(event.source)
+
+ return super().isActivatableEvent(event)
+
+ def locusOfFocusChanged(self, event, oldFocus, newFocus):
+ """Handles changes of focus of interest to the script."""
+
+ # Normally we don't cache the name, because we cannot count on apps and
+ # tookits emitting name-changed events (needed by AT-SPI2 to update the
+ # name so that we don't have stale values). However, if we don't cache
+ # the name, we wind up with dead accessibles in (at least) the search bar
+ # popup. For now, cache the name to get things working and clear the cache
+ # for objects we plan to present. Mind you, clearing the cache on objects
+ # with many descendants can cause AT-SPI2 to become non-responsive so try
+ # to guess what NOT to clear the cache for. http://crbug.com/896706
+ doNotClearCacheFor = [pyatspi.ROLE_DIALOG,
+ pyatspi.ROLE_FRAME,
+ pyatspi.ROLE_LIST_BOX,
+ pyatspi.ROLE_MENU,
+ pyatspi.ROLE_REDUNDANT_OBJECT,
+ pyatspi.ROLE_TABLE,
+ pyatspi.ROLE_TREE,
+ pyatspi.ROLE_TREE_TABLE,
+ pyatspi.ROLE_UNKNOWN,
+ pyatspi.ROLE_WINDOW]
+ if newFocus.getRole() not in doNotClearCacheFor:
+ msg = "CHROMIUM: CANNOT CACHE NAME HACK: Clearing cache for %s" % newFocus
+ debug.println(debug.LEVEL_INFO, msg, True)
+ newFocus.clearCache()
+
+ # HACK: Remove this once Chromium has support for input events.
+ if oldFocus and newFocus \
+ and oldFocus.getRole() != pyatspi.ROLE_FRAME \
+ and oldFocus.getApplication() == newFocus.getApplication() == self.app \
+ and not self.utilities.lastInputEventCameFromThisApp():
+ msg = "CHROMIUM: NO INPUT EVENT PRESENTATION INTERRUPT HACK"
+ debug.println(debug.LEVEL_INFO, msg, True)
+ self.presentationInterrupt()
+
+ if super().locusOfFocusChanged(event, oldFocus, newFocus):
+ return
+
+ msg = "CHROMIUM: Passing along event to default script"
+ debug.println(debug.LEVEL_INFO, msg, True)
+ default.Script.locusOfFocusChanged(self, event, oldFocus, newFocus)
+
+ def onActiveChanged(self, event):
+ """Callback for object:state-changed:active accessibility events."""
+
+ if super().onActiveChanged(event):
+ return
+
+ role = event.source.getRole()
+ if event.detail1 and role == pyatspi.ROLE_FRAME \
+ and not self.utilities.canBeActiveWindow(event.source):
+ return
+
+ msg = "CHROMIUM: Passing along event to default script"
+ debug.println(debug.LEVEL_INFO, msg, True)
+ default.Script.onActiveChanged(self, event)
+
+ # HACK: Remove this once Chromium emits focus changes after window activation.
+ if event.detail1 and role == pyatspi.ROLE_FRAME:
+ focusedObject = self.utilities.focusedObject(event.source)
+ msg = "CHROMIUM: NO INITIAL FOCUS HACK. Focused object: %s" % focusedObject
+ debug.println(debug.LEVEL_INFO, msg, True)
+ if focusedObject:
+ orca.setLocusOfFocus(event, focusedObject)
+
+ def onActiveDescendantChanged(self, event):
+ """Callback for object:active-descendant-changed accessibility events."""
+
+ if super().onActiveDescendantChanged(event):
+ return
+
+ msg = "CHROMIUM: Passing along event to default script"
+ debug.println(debug.LEVEL_INFO, msg, True)
+ default.Script.onActiveDescendantChanged(self, event)
+
+ def onBusyChanged(self, event):
+ """Callback for object:state-changed:busy accessibility events."""
+
+ if super().onBusyChanged(event):
+ return
+
+ msg = "CHROMIUM: Passing along event to default script"
+ debug.println(debug.LEVEL_INFO, msg, True)
+ default.Script.onBusyChanged(self, event)
+
+ def onCaretMoved(self, event):
+ """Callback for object:text-caret-moved accessibility events."""
+
+ if super().onCaretMoved(event):
+ return
+
+ msg = "CHROMIUM: Passing along event to default script"
+ debug.println(debug.LEVEL_INFO, msg, True)
+ default.Script.onCaretMoved(self, event)
+
+ def onCheckedChanged(self, event):
+ """Callback for object:state-changed:checked accessibility events."""
+
+ if super().onCheckedChanged(event):
+ return
+
+ msg = "CHROMIUM: Passing along event to default script"
+ debug.println(debug.LEVEL_INFO, msg, True)
+ default.Script.onCheckedChanged(self, event)
+
+ def onChildrenChanged(self, event):
+ """Callback for object:children-changed accessibility events."""
+
+ if super().onChildrenChanged(event):
+ return
+
+ msg = "CHROMIUM: Passing along event to default script"
+ debug.println(debug.LEVEL_INFO, msg, True)
+ default.Script.onChildrenChanged(self, event)
+
+ def onDocumentLoadComplete(self, event):
+ """Callback for document:load-complete accessibility events."""
+
+ if super().onDocumentLoadComplete(event):
+ return
+
+ msg = "CHROMIUM: Passing along event to default script"
+ debug.println(debug.LEVEL_INFO, msg, True)
+ default.Script.onDocumentLoadComplete(self, event)
+
+ def onDocumentLoadStopped(self, event):
+ """Callback for document:load-stopped accessibility events."""
+
+ if super().onDocumentLoadStopped(event):
+ return
+
+ msg = "CHROMIUM: Passing along event to default script"
+ debug.println(debug.LEVEL_INFO, msg, True)
+ default.Script.onDocumentLoadStopped(self, event)
+
+ def onDocumentReload(self, event):
+ """Callback for document:reload accessibility events."""
+
+ if super().onDocumentReload(event):
+ return
+
+ msg = "CHROMIUM: Passing along event to default script"
+ debug.println(debug.LEVEL_INFO, msg, True)
+ default.Script.onDocumentReload(self, event)
+
+ def onFocus(self, event):
+ """Callback for focus: accessibility events."""
+
+ # This event is deprecated. We should get object:state-changed:focused
+ # events instead.
+
+ if super().onFocus(event):
+ return
+
+ msg = "CHROMIUM: Passing along event to default script"
+ debug.println(debug.LEVEL_INFO, msg, True)
+ default.Script.onFocus(self, event)
+
+ def onFocusedChanged(self, event):
+ """Callback for object:state-changed:focused accessibility events."""
+
+ if super().onFocusedChanged(event):
+ return
+
+ msg = "CHROMIUM: Passing along event to default script"
+ debug.println(debug.LEVEL_INFO, msg, True)
+ default.Script.onFocusedChanged(self, event)
+
+ def onMouseButton(self, event):
+ """Callback for mouse:button accessibility events."""
+
+ if super().onMouseButton(event):
+ return
+
+ msg = "CHROMIUM: Passing along event to default script"
+ debug.println(debug.LEVEL_INFO, msg, True)
+ default.Script.onMouseButton(self, event)
+
+ def onNameChanged(self, event):
+ """Callback for object:property-change:accessible-name events."""
+
+ if super().onNameChanged(event):
+ return
+
+ msg = "CHROMIUM: Passing along event to default script"
+ debug.println(debug.LEVEL_INFO, msg, True)
+ default.Script.onNameChanged(self, event)
+
+ def onSelectedChanged(self, event):
+ """Callback for object:state-changed:selected accessibility events."""
+
+ if super().onSelectedChanged(event):
+ return
+
+ # Other apps and toolkits implement the selection interface, which is
+ # what we use to present active-descendanty selection changes, leaving
+ # state-changed:selected for notifications related to toggling the
+ # selected state of the currently-focused item (e.g. pressing ctrl+space
+ # in a file explorer). While handling active-descendanty changes here is
+ # not technically a HACK, once Chromium implements the selection interface,
+ # we should remove this code and defer to Orca's default handling.
+ if event.detail1 and not self.utilities.isLayoutOnly(event.source) \
+ and not "Selection" in pyatspi.listInterfaces(event.source.parent) \
+ and self.utilities.canBeActiveWindow(self.utilities.topLevelObject(event.source)):
+ msg = "CHROMIUM: NO SELECTION IFACE HACK: Setting %s to locusOfFocus" % event.source
+ debug.println(debug.LEVEL_INFO, msg, True)
+ orca.setLocusOfFocus(event, event.source)
+ return
+
+ msg = "CHROMIUM: Passing along event to default script"
+ debug.println(debug.LEVEL_INFO, msg, True)
+ default.Script.onSelectedChanged(self, event)
+
+ def onSelectionChanged(self, event):
+ """Callback for object:selection-changed accessibility events."""
+
+ if super().onSelectionChanged(event):
+ return
+
+ msg = "CHROMIUM: Passing along event to default script"
+ debug.println(debug.LEVEL_INFO, msg, True)
+ default.Script.onSelectionChanged(self, event)
+
+ def onShowingChanged(self, event):
+ """Callback for object:state-changed:showing accessibility events."""
+
+ if super().onShowingChanged(event):
+ return
+
+ msg = "CHROMIUM: Passing along event to default script"
+ debug.println(debug.LEVEL_INFO, msg, True)
+ default.Script.onShowingChanged(self, event)
+
+ def onTextDeleted(self, event):
+ """Callback for object:text-changed:delete accessibility events."""
+
+ if super().onTextDeleted(event):
+ return
+
+ msg = "CHROMIUM: Passing along event to default script"
+ debug.println(debug.LEVEL_INFO, msg, True)
+ default.Script.onTextDeleted(self, event)
+
+ def onTextInserted(self, event):
+ """Callback for object:text-changed:insert accessibility events."""
+
+ if super().onTextInserted(event):
+ return
+
+ msg = "CHROMIUM: Passing along event to default script"
+ debug.println(debug.LEVEL_INFO, msg, True)
+ default.Script.onTextInserted(self, event)
+
+ def onTextSelectionChanged(self, event):
+ """Callback for object:text-selection-changed accessibility events."""
+
+ if super().onTextSelectionChanged(event):
+ return
+
+ msg = "CHROMIUM: Passing along event to default script"
+ debug.println(debug.LEVEL_INFO, msg, True)
+ default.Script.onTextSelectionChanged(self, event)
+
+ def onWindowActivated(self, event):
+ """Callback for window:activate accessibility events."""
+
+ if not self.utilities.canBeActiveWindow(event.source):
+ return
+
+ if super().onWindowActivated(event):
+ return
+
+ msg = "CHROMIUM: Passing along event to default script"
+ debug.println(debug.LEVEL_INFO, msg, True)
+ default.Script.onWindowActivated(self, event)
+
+ def onWindowDeactivated(self, event):
+ """Callback for window:deactivate accessibility events."""
+
+ if super().onWindowDeactivated(event):
+ return
+
+ msg = "CHROMIUM: Passing along event to default script"
+ debug.println(debug.LEVEL_INFO, msg, True)
+ default.Script.onWindowDeactivated(self, event)
+
diff --git a/src/orca/scripts/toolkits/Chromium/script_utilities.py
b/src/orca/scripts/toolkits/Chromium/script_utilities.py
new file mode 100644
index 000000000..54b0ef849
--- /dev/null
+++ b/src/orca/scripts/toolkits/Chromium/script_utilities.py
@@ -0,0 +1,159 @@
+# Orca
+#
+# Copyright 2018 Igalia, S.L.
+#
+# Author: Joanmarie Diggs <jdiggs igalia com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the
+# Free Software Foundation, Inc., Franklin Street, Fifth Floor,
+# Boston MA 02110-1301 USA.
+
+"""Custom script utilities for Chromium"""
+
+# Please note: ATK support in Chromium needs much work. Until that work has been
+# done, Orca will not be able to provide access to Chromium. These utilities are
+# a work in progress.
+
+__id__ = "$Id$"
+__version__ = "$Revision$"
+__date__ = "$Date$"
+__copyright__ = "Copyright (c) 2018 Igalia, S.L."
+__license__ = "LGPL"
+
+import pyatspi
+
+from orca import debug
+from orca import orca_state
+from orca.scripts import web
+
+
+class Utilities(web.Utilities):
+
+ def __init__(self, script):
+ super().__init__(script)
+ self._documentsEmbeddedBy = {} # Needed for HACK
+
+ def clearCachedObjects(self):
+ super().clearCachedObjects()
+ self._documentsEmbeddedBy = {} # Needed for HACK
+
+ # So far, this is only needed by _getDocumentsEmbeddedBy. Hopefully we
+ # can remove this method once we remove the other one.
+ def _isTopLevelDocument(self, obj):
+ if not obj or self.isDead(obj):
+ return False
+
+ if not self.isDocument(obj):
+ return False
+
+ if obj.getRole() == pyatspi.ROLE_DOCUMENT_FRAME:
+ return False
+
+ if obj.parent and obj.parent.getRole() == pyatspi.ROLE_INTERNAL_FRAME:
+ return False
+
+ if not self.isShowingAndVisible(obj):
+ return False
+
+ return True
+
+ def _getDocumentsEmbeddedBy(self, frame):
+ result = super()._getDocumentsEmbeddedBy(frame)
+ if result:
+ return result
+
+ # HACK: This tree dive is not efficient and should be removed once Chromium
+ # implements support for the embeds/embedded-by relation pair.
+ cached = self._documentsEmbeddedBy.get(hash(frame), [])
+ result = list(filter(self._isTopLevelDocument, cached))
+ if not result:
+ documents = pyatspi.findAllDescendants(frame, self.isDocument)
+ result = list(filter(self._isTopLevelDocument, documents))
+ msg = "CHROMIUM: NO EMBEDDED RELATION HACK: %s has %i docs." % (frame, len(result))
+ debug.println(debug.LEVEL_INFO, msg, True)
+
+ self._documentsEmbeddedBy[hash(frame)] = result
+ return result
+
+ def isZombie(self, obj):
+ if not super().isZombie(obj):
+ return False
+
+ # Things (so far) seem to work as expected for document content.
+ if self.inDocumentContent(obj):
+ return True
+
+ # HACK for other items, including (though possibly not limited to) menu items
+ # (e.g. when you press Alt+F and arrow) and the location bar popup.
+ try:
+ index = obj.getIndexInParent()
+ except:
+ msg = "CHROMIUM: Exception getting index in parent for %s" % obj
+ debug.println(debug.LEVEL_INFO, msg, True)
+ return True
+
+ if index == -1 and self.isShowingAndVisible(obj):
+ msg = "CHROMIUM: INDEX IN PARENT OF -1 HACK: Ignoring bad index of %s" % obj
+ debug.println(debug.LEVEL_INFO, msg, True)
+ return False
+
+ return True
+
+ def _isFrameContainerForBrowserUIPopUp(self, frame):
+ if not frame or self.isDead(frame):
+ return False
+
+ # So far, the frame containers which lack the active state also lack names.
+ # Tree diving can be expensive....
+ if frame.name:
+ return False
+
+ roles = [pyatspi.ROLE_LIST_BOX, pyatspi.ROLE_MENU]
+ child = pyatspi.findDescendant(frame, lambda x: x and x.getRole() in roles)
+ return child and not self.inDocumentContent(child)
+
+ def canBeActiveWindow(self, window, clearCache=True):
+ if super().canBeActiveWindow(window, clearCache):
+ return True
+
+ if window and window.toolkitName != "Chromium":
+ return False
+
+ # HACK: Remove this once Chromium adds active state to popup frames.
+ if self._isFrameContainerForBrowserUIPopUp(window):
+ msg = "CHROMIUM: POPUP MISSING STATE ACTIVE HACK: %s can be active window" % window
+ debug.println(debug.LEVEL_INFO, msg, True)
+ return True
+
+ return False
+
+ def treatAsMenu(self, obj):
+ if not obj:
+ return False
+
+ try:
+ role = obj.getRole()
+ state = obj.getState()
+ except:
+ msg = "ERROR: Exception getting role and state for %s" % obj
+ debug.println(debug.LEVEL_INFO, msg, True)
+ return False
+
+ # Unlike other apps and toolkits, submenus in Chromium have the menu item
+ # role rather than the menu role, but we can identify them as submenus via
+ # the has-popup state.
+ if role == pyatspi.ROLE_MENU_ITEM:
+ return state.contains(pyatspi.STATE_HAS_POPUP)
+
+ return False
diff --git a/src/orca/scripts/toolkits/Chromium/speech_generator.py
b/src/orca/scripts/toolkits/Chromium/speech_generator.py
new file mode 100644
index 000000000..c7b2f9daf
--- /dev/null
+++ b/src/orca/scripts/toolkits/Chromium/speech_generator.py
@@ -0,0 +1,60 @@
+# Orca
+#
+# Copyright 2018 Igalia, S.L.
+#
+# Author: Joanmarie Diggs <jdiggs igalia com>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the
+# Free Software Foundation, Inc., Franklin Street, Fifth Floor,
+# Boston MA 02110-1301 USA.
+
+"""Custom speech generator for Chromium."""
+
+# Please note: ATK support in Chromium needs much work. Until that work has been
+# done, Orca will not be able to provide access to Chromium. This generator is a
+# work in progress.
+
+__id__ = "$Id$"
+__version__ = "$Revision$"
+__date__ = "$Date$"
+__copyright__ = "Copyright (c) 2018 Igalia, S.L."
+__license__ = "LGPL"
+
+import pyatspi
+
+from orca import debug
+from orca import orca_state
+from orca.scripts import web
+
+
+class SpeechGenerator(web.SpeechGenerator):
+
+ def __init__(self, script):
+ super().__init__(script)
+
+ def generateSpeech(self, obj, **args):
+ if self._script.utilities.inDocumentContent(obj):
+ return super().generateSpeech(obj, **args)
+
+ oldRole = None
+ if self._script.utilities.treatAsMenu(obj):
+ msg = "CHROMIUM: HACK? Speaking menu item as menu %s" % obj
+ debug.println(debug.LEVEL_INFO, msg, True)
+ oldRole = self._overrideRole(pyatspi.ROLE_MENU, args)
+
+ result = super().generateSpeech(obj, **args)
+ if oldRole is not None:
+ self._restoreRole(oldRole, args)
+
+ return result
diff --git a/src/orca/scripts/toolkits/Makefile.am b/src/orca/scripts/toolkits/Makefile.am
index 9c129108d..54de4c84b 100644
--- a/src/orca/scripts/toolkits/Makefile.am
+++ b/src/orca/scripts/toolkits/Makefile.am
@@ -1,4 +1,4 @@
-SUBDIRS = GAIL Gecko J2SE-access-bridge Qt WebKitGtk clutter gtk
+SUBDIRS = Chromium GAIL Gecko J2SE-access-bridge Qt WebKitGtk clutter gtk
orca_python_PYTHON = \
__init__.py \
diff --git a/src/orca/scripts/toolkits/__init__.py b/src/orca/scripts/toolkits/__init__.py
index 827c989d7..ccc4d839c 100644
--- a/src/orca/scripts/toolkits/__init__.py
+++ b/src/orca/scripts/toolkits/__init__.py
@@ -1,4 +1,5 @@
__all__ = ['clutter',
+ 'Chromium',
'gtk',
'GAIL',
'Gecko',
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]