[orca] Add support for aria-roledescription
- From: Joanmarie Diggs <joanied src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [orca] Add support for aria-roledescription
- Date: Thu, 10 Sep 2015 01:14:06 +0000 (UTC)
commit 4a8e54df8b6cb85c3206606af9f08d03c5ca1797
Author: Joanmarie Diggs <jdiggs igalia com>
Date: Wed Sep 9 21:10:02 2015 -0400
Add support for aria-roledescription
src/orca/braille_generator.py | 3 +-
src/orca/formatting.py | 8 +-
src/orca/scripts/web/braille_generator.py | 14 +++
src/orca/scripts/web/script_utilities.py | 16 ++++
src/orca/scripts/web/speech_generator.py | 17 ++++
src/orca/speech_generator.py | 3 +-
test/html/aria-roledescription.html | 30 ++++++
.../firefox/aria_roledescription_where_am_i.params | 1 +
.../firefox/aria_roledescription_where_am_i.py | 46 ++++++++++
.../firefox/focus_tracking_roledescriptions.params | 1 +
.../firefox/focus_tracking_roledescriptions.py | 96 ++++++++++++++++++++
.../firefox/line_nav_roledescriptions.params | 1 +
.../firefox/line_nav_roledescriptions.py | 63 +++++++++++++
13 files changed, 292 insertions(+), 7 deletions(-)
---
diff --git a/src/orca/braille_generator.py b/src/orca/braille_generator.py
index 2015504..f4952d6 100644
--- a/src/orca/braille_generator.py
+++ b/src/orca/braille_generator.py
@@ -156,8 +156,7 @@ class BrailleGenerator(generator.Generator):
result.append(self.getLocalizedRoleName(obj, role))
return result
- @staticmethod
- def getLocalizedRoleName(obj, role=None):
+ def getLocalizedRoleName(self, obj, role=None):
"""Returns the localized name of the given Accessible object; the name
is suitable to be brailled.
diff --git a/src/orca/formatting.py b/src/orca/formatting.py
index a93150d..43b2dce 100644
--- a/src/orca/formatting.py
+++ b/src/orca/formatting.py
@@ -343,8 +343,8 @@ formatting = {
'basicWhereAmI': 'labelOrName + roleName + value + percentage + ' + MNEMONIC + ' + accelerator +
required'
},
pyatspi.ROLE_SECTION: {
- 'focused': 'labelOrName + readOnly + textRole + currentLineText + allTextSelection',
- 'unfocused': 'labelOrName + readOnly + textRole + currentLineText + allTextSelection + ' +
MNEMONIC,
+ 'focused': 'labelOrName + currentLineText + allTextSelection + roleName',
+ 'unfocused': 'labelOrName + currentLineText + allTextSelection + roleName + ' + MNEMONIC,
},
pyatspi.ROLE_SLIDER: {
'focused': 'value',
@@ -635,7 +635,9 @@ formatting = {
'unfocused': 'asPageTabOrScrollPane'
},
pyatspi.ROLE_SECTION: {
- 'unfocused': BRAILLE_TEXT
+ 'unfocused': '((substring and ' + BRAILLE_TEXT + ')\
+ or ([Component(obj, asString(labelAndName + roleName))]\
+ + (childWidget and ([Region(" ")] + childWidget))))'
},
#'REAL_ROLE_SCROLL_PANE': 'default'
pyatspi.ROLE_SLIDER: {
diff --git a/src/orca/scripts/web/braille_generator.py b/src/orca/scripts/web/braille_generator.py
index ecf4642..690d0a3 100644
--- a/src/orca/scripts/web/braille_generator.py
+++ b/src/orca/scripts/web/braille_generator.py
@@ -41,9 +41,23 @@ class BrailleGenerator(braille_generator.BrailleGenerator):
def __init__(self, script):
super().__init__(script)
+ def getLocalizedRoleName(self, obj, role=None):
+ if not self._script.utilities.inDocumentContent(obj):
+ return super().getLocalizedRoleName(obj, role)
+
+ roledescription = self._script.utilities.getRoleDescription(obj)
+ if roledescription:
+ return roledescription
+
+ return super().getLocalizedRoleName(obj, role)
+
def _generateRoleName(self, obj, **args):
"""Prevents some roles from being displayed."""
+ roledescription = self._script.utilities.getRoleDescription(obj)
+ if roledescription:
+ return [roledescription]
+
doNotDisplay = [pyatspi.ROLE_FORM,
pyatspi.ROLE_SECTION,
pyatspi.ROLE_PARAGRAPH,
diff --git a/src/orca/scripts/web/script_utilities.py b/src/orca/scripts/web/script_utilities.py
index c05eff3..eed1b83 100644
--- a/src/orca/scripts/web/script_utilities.py
+++ b/src/orca/scripts/web/script_utilities.py
@@ -65,6 +65,7 @@ class Utilities(script_utilities.Utilities):
self._isNonEntryTextWidget = {}
self._isUselessImage = {}
self._inferredLabels = {}
+ self._roleDescription = {}
self._text = {}
self._tag = {}
self._treatAsDiv = {}
@@ -103,6 +104,7 @@ class Utilities(script_utilities.Utilities):
self._isNonEntryTextWidget = {}
self._isUselessImage = {}
self._inferredLabels = {}
+ self._roleDescription = {}
self._tag = {}
self._treatAsDiv = {}
self._cleanupContexts()
@@ -319,6 +321,20 @@ class Utilities(script_utilities.Utilities):
return lastChild
+ def getRoleDescription(self, obj):
+ rv = self._roleDescription.get(hash(obj))
+ if rv is not None:
+ return rv
+
+ try:
+ attrs = dict([attr.split(':', 1) for attr in obj.getAttributes()])
+ except:
+ attrs = {}
+
+ rv = attrs.get('roledescription', '')
+ self._roleDescription[hash(obj)] = rv
+ return rv
+
def _getTag(self, obj):
rv = self._tag.get(hash(obj))
if rv is not None:
diff --git a/src/orca/scripts/web/speech_generator.py b/src/orca/scripts/web/speech_generator.py
index 6965d42..22446a6 100644
--- a/src/orca/scripts/web/speech_generator.py
+++ b/src/orca/scripts/web/speech_generator.py
@@ -191,12 +191,29 @@ class SpeechGenerator(speech_generator.SpeechGenerator):
def _generateTextRole(self, obj, **args):
return self._generateRoleName(obj, **args)
+ def getLocalizedRoleName(self, obj, role=None):
+ if not self._script.utilities.inDocumentContent(obj):
+ return super().getLocalizedRoleName(obj, role)
+
+ roledescription = self._script.utilities.getRoleDescription(obj)
+ if roledescription:
+ return roledescription
+
+ return super().getLocalizedRoleName(obj, role)
+
def _generateRoleName(self, obj, **args):
if not self._script.utilities.inDocumentContent(obj):
return super()._generateRoleName(obj, **args)
result = []
acss = self.voice(speech_generator.SYSTEM)
+
+ roledescription = self._script.utilities.getRoleDescription(obj)
+ if roledescription:
+ result = [roledescription]
+ result.extend(acss)
+ return result
+
role = args.get('role', obj.getRole())
force = args.get('force', False)
diff --git a/src/orca/speech_generator.py b/src/orca/speech_generator.py
index b74b032..3af6fd3 100644
--- a/src/orca/speech_generator.py
+++ b/src/orca/speech_generator.py
@@ -364,8 +364,7 @@ class SpeechGenerator(generator.Generator):
"""
return self._generateRoleName(obj, **args)
- @staticmethod
- def getLocalizedRoleName(obj, role=None):
+ def getLocalizedRoleName(self, obj, role=None):
"""Returns the localized name of the given Accessible object; the name
is suitable to be spoken.
diff --git a/test/html/aria-roledescription.html b/test/html/aria-roledescription.html
new file mode 100644
index 0000000..eb7b948
--- /dev/null
+++ b/test/html/aria-roledescription.html
@@ -0,0 +1,30 @@
+<html>
+<head></head>
+<body>
+<div>Start</div>
+<div tabindex="0">Focus me 1</div>
+<div tabindex="0" aria-roledescription="kill switch">Focus me 2</div>
+<div tabindex="0" role="button">Focus me 3</div>
+<div tabindex="0" role="button" aria-roledescription="kill switch">Focus me 4</div>
+<button>Focus me 5</button>
+<button aria-roledescription="kill switch">Focus me 6</button>
+<div tabindex="0" role="group" aria-label="Presentation" aria-roledescription="slide set">
+ <p>Here are some slides</p>
+ <div tabindex="0" role="region" aria-roledescription="slide" id="slide1" aria-labelledby="slide1heading">
+ <h1 id="slide1heading">First Quarter 2015</h1>
+ <ul>
+ <li>Item 1</li>
+ <li>Item 2</li>
+ </ul>
+ </div>
+ <div tabindex="0" aria-roledescription="slide" id="slide1" aria-labelledby="slide2heading">
+ <h1 id="slide2heading">Second Quarter 2015</h1>
+ <ul>
+ <li>Item 3</li>
+ <li>Item 4</li>
+ </ul>
+ </div>
+</div>
+<div>End</div>
+</body>
+</html>
diff --git a/test/keystrokes/firefox/aria_roledescription_where_am_i.params
b/test/keystrokes/firefox/aria_roledescription_where_am_i.params
new file mode 100644
index 0000000..8271399
--- /dev/null
+++ b/test/keystrokes/firefox/aria_roledescription_where_am_i.params
@@ -0,0 +1 @@
+PARAMS=$TEST_DIR/../../html/aria-roledescription.html
diff --git a/test/keystrokes/firefox/aria_roledescription_where_am_i.py
b/test/keystrokes/firefox/aria_roledescription_where_am_i.py
new file mode 100644
index 0000000..d6ec47a
--- /dev/null
+++ b/test/keystrokes/firefox/aria_roledescription_where_am_i.py
@@ -0,0 +1,46 @@
+#!/usr/bin/python
+
+from macaroon.playback import *
+import utils
+
+sequence = MacroSequence()
+
+# Work around some new quirk in Gecko that causes this test to fail if
+# run via the test harness rather than manually.
+sequence.append(KeyComboAction("<Control>r"))
+sequence.append(KeyComboAction("<Control>Home"))
+sequence.append(KeyComboAction("Tab"))
+sequence.append(KeyComboAction("Tab"))
+
+sequence.append(utils.StartRecordingAction())
+sequence.append(KeyComboAction("KP_Enter"))
+sequence.append(utils.AssertPresentationAction(
+ "1. Basic Where Am I on a div with only a roledescription",
+ ["BRAILLE LINE: 'Focus me 2 kill switch'",
+ " VISIBLE: 'Focus me 2 kill switch', cursor=1",
+ "SPEECH OUTPUT: 'Focus me 2 kill switch'"]))
+
+sequence.append(KeyComboAction("Tab"))
+sequence.append(KeyComboAction("Tab"))
+
+sequence.append(utils.StartRecordingAction())
+sequence.append(KeyComboAction("KP_Enter"))
+sequence.append(utils.AssertPresentationAction(
+ "2. Basic Where Am I on a div with role button and a roledescription",
+ ["BRAILLE LINE: 'Focus me 4 kill switch'",
+ " VISIBLE: 'Focus me 4 kill switch', cursor=1",
+ "SPEECH OUTPUT: 'Focus me 4 kill switch'"]))
+
+sequence.append(KeyComboAction("Tab"))
+sequence.append(KeyComboAction("Tab"))
+
+sequence.append(utils.StartRecordingAction())
+sequence.append(KeyComboAction("KP_Enter"))
+sequence.append(utils.AssertPresentationAction(
+ "3. Basic Where Am I on a button element with a roledescription",
+ ["BRAILLE LINE: 'Focus me 6 kill switch'",
+ " VISIBLE: 'Focus me 6 kill switch', cursor=1",
+ "SPEECH OUTPUT: 'Focus me 6 kill switch'"]))
+
+sequence.append(utils.AssertionSummaryAction())
+sequence.start()
diff --git a/test/keystrokes/firefox/focus_tracking_roledescriptions.params
b/test/keystrokes/firefox/focus_tracking_roledescriptions.params
new file mode 100644
index 0000000..8271399
--- /dev/null
+++ b/test/keystrokes/firefox/focus_tracking_roledescriptions.params
@@ -0,0 +1 @@
+PARAMS=$TEST_DIR/../../html/aria-roledescription.html
diff --git a/test/keystrokes/firefox/focus_tracking_roledescriptions.py
b/test/keystrokes/firefox/focus_tracking_roledescriptions.py
new file mode 100644
index 0000000..219f979
--- /dev/null
+++ b/test/keystrokes/firefox/focus_tracking_roledescriptions.py
@@ -0,0 +1,96 @@
+#!/usr/bin/python
+
+from macaroon.playback import *
+import utils
+
+sequence = MacroSequence()
+
+sequence.append(KeyComboAction("<Control>Home"))
+
+sequence.append(utils.StartRecordingAction())
+sequence.append(KeyComboAction("Tab"))
+sequence.append(utils.AssertPresentationAction(
+ "1. Tab to a div with no roledescription",
+ ["BRAILLE LINE: 'Focus me 1'",
+ " VISIBLE: 'Focus me 1', cursor=1",
+ "BRAILLE LINE: 'Focus me 1'",
+ " VISIBLE: 'Focus me 1', cursor=1",
+ "SPEECH OUTPUT: 'Focus me 1'"]))
+
+sequence.append(utils.StartRecordingAction())
+sequence.append(KeyComboAction("Tab"))
+sequence.append(utils.AssertPresentationAction(
+ "2. Tab to a div with only a roledescription",
+ ["BRAILLE LINE: 'Focus me 2 kill switch'",
+ " VISIBLE: 'Focus me 2 kill switch', cursor=1",
+ "BRAILLE LINE: 'Focus me 2 kill switch'",
+ " VISIBLE: 'Focus me 2 kill switch', cursor=1",
+ "SPEECH OUTPUT: 'Focus me 2 kill switch'"]))
+
+sequence.append(utils.StartRecordingAction())
+sequence.append(KeyComboAction("Tab"))
+sequence.append(utils.AssertPresentationAction(
+ "3. Tab to a div with role button",
+ ["BRAILLE LINE: 'Focus me 3 push button'",
+ " VISIBLE: 'Focus me 3 push button', cursor=1",
+ "SPEECH OUTPUT: 'Focus me 3 push button'"]))
+
+sequence.append(utils.StartRecordingAction())
+sequence.append(KeyComboAction("Tab"))
+sequence.append(utils.AssertPresentationAction(
+ "4. Tab to a div with role button and a roledescription",
+ ["BRAILLE LINE: 'Focus me 4 kill switch'",
+ " VISIBLE: 'Focus me 4 kill switch', cursor=1",
+ "SPEECH OUTPUT: 'Focus me 4 kill switch'"]))
+
+sequence.append(utils.StartRecordingAction())
+sequence.append(KeyComboAction("Tab"))
+sequence.append(utils.AssertPresentationAction(
+ "5. Tab to a button element",
+ ["BRAILLE LINE: 'Focus me 5 push button'",
+ " VISIBLE: 'Focus me 5 push button', cursor=1",
+ "SPEECH OUTPUT: 'Focus me 5 push button'"]))
+
+sequence.append(utils.StartRecordingAction())
+sequence.append(KeyComboAction("Tab"))
+sequence.append(utils.AssertPresentationAction(
+ "6. Tab to a button element with a roledescription",
+ ["BRAILLE LINE: 'Focus me 6 kill switch'",
+ " VISIBLE: 'Focus me 6 kill switch', cursor=1",
+ "SPEECH OUTPUT: 'Focus me 6 kill switch'"]))
+
+sequence.append(utils.StartRecordingAction())
+sequence.append(KeyComboAction("Tab"))
+sequence.append(utils.AssertPresentationAction(
+ "7. Tab to a div with role group, a roledescription, a label, and displayed text",
+ ["BRAILLE LINE: 'Presentation slide set'",
+ " VISIBLE: 'Presentation slide set', cursor=1",
+ "BRAILLE LINE: 'Here are some slides'",
+ " VISIBLE: 'Here are some slides', cursor=1",
+ "SPEECH OUTPUT: 'Presentation slide set'",
+ "SPEECH OUTPUT: 'Here are some slides'"]))
+
+sequence.append(utils.StartRecordingAction())
+sequence.append(KeyComboAction("Tab"))
+sequence.append(utils.AssertPresentationAction(
+ "8. Tab to a div with role region, a roledescription, a labelledby, and displayed text",
+ ["BRAILLE LINE: 'First Quarter 2015 slide'",
+ " VISIBLE: 'First Quarter 2015 slide', cursor=1",
+ "BRAILLE LINE: 'First Quarter 2015 h1'",
+ " VISIBLE: 'First Quarter 2015 h1', cursor=1",
+ "SPEECH OUTPUT: 'First Quarter 2015 slide'",
+ "SPEECH OUTPUT: 'First Quarter 2015 heading level 1'"]))
+
+sequence.append(utils.StartRecordingAction())
+sequence.append(KeyComboAction("Tab"))
+sequence.append(utils.AssertPresentationAction(
+ "9. Tab to a div with a roledescription, a labelledby, and displayed text",
+ ["BRAILLE LINE: 'Second Quarter 2015 slide'",
+ " VISIBLE: 'Second Quarter 2015 slide', cursor=1",
+ "BRAILLE LINE: 'Second Quarter 2015 h1'",
+ " VISIBLE: 'Second Quarter 2015 h1', cursor=1",
+ "SPEECH OUTPUT: 'Second Quarter 2015 slide'",
+ "SPEECH OUTPUT: 'Second Quarter 2015 heading level 1'"]))
+
+sequence.append(utils.AssertionSummaryAction())
+sequence.start()
diff --git a/test/keystrokes/firefox/line_nav_roledescriptions.params
b/test/keystrokes/firefox/line_nav_roledescriptions.params
new file mode 100644
index 0000000..8271399
--- /dev/null
+++ b/test/keystrokes/firefox/line_nav_roledescriptions.params
@@ -0,0 +1 @@
+PARAMS=$TEST_DIR/../../html/aria-roledescription.html
diff --git a/test/keystrokes/firefox/line_nav_roledescriptions.py
b/test/keystrokes/firefox/line_nav_roledescriptions.py
new file mode 100644
index 0000000..1f63f0c
--- /dev/null
+++ b/test/keystrokes/firefox/line_nav_roledescriptions.py
@@ -0,0 +1,63 @@
+#!/usr/bin/python
+
+from macaroon.playback import *
+import utils
+
+sequence = MacroSequence()
+
+# Work around some new quirk in Gecko that causes this test to fail if
+# run via the test harness rather than manually.
+sequence.append(KeyComboAction("<Control>r"))
+sequence.append(KeyComboAction("<Control>Home"))
+
+sequence.append(utils.StartRecordingAction())
+sequence.append(KeyComboAction("Down"))
+sequence.append(utils.AssertPresentationAction(
+ "1. Line Down",
+ ["BRAILLE LINE: 'Focus me 1'",
+ " VISIBLE: 'Focus me 1', cursor=1",
+ "SPEECH OUTPUT: 'Focus me 1'"]))
+
+sequence.append(utils.StartRecordingAction())
+sequence.append(KeyComboAction("Down"))
+sequence.append(utils.AssertPresentationAction(
+ "2. Line Down",
+ ["BRAILLE LINE: 'Focus me 2'",
+ " VISIBLE: 'Focus me 2', cursor=1",
+ "SPEECH OUTPUT: 'Focus me 2 kill switch'"]))
+
+sequence.append(utils.StartRecordingAction())
+sequence.append(KeyComboAction("Down"))
+sequence.append(utils.AssertPresentationAction(
+ "3. Line Down",
+ ["BRAILLE LINE: 'Focus me 3 push button'",
+ " VISIBLE: 'Focus me 3 push button', cursor=1",
+ "SPEECH OUTPUT: 'Focus me 3 push button'"]))
+
+sequence.append(utils.StartRecordingAction())
+sequence.append(KeyComboAction("Down"))
+sequence.append(utils.AssertPresentationAction(
+ "4. Line Down",
+ ["BRAILLE LINE: 'Focus me 4 kill switch'",
+ " VISIBLE: 'Focus me 4 kill switch', cursor=1",
+ "SPEECH OUTPUT: 'Focus me 4 kill switch'"]))
+
+sequence.append(utils.StartRecordingAction())
+sequence.append(KeyComboAction("Down"))
+sequence.append(utils.AssertPresentationAction(
+ "5. Line Down",
+ ["BRAILLE LINE: 'Focus me 5 push button Focus me 6 kill switch'",
+ " VISIBLE: 'Focus me 5 push button Focus me ', cursor=1",
+ "SPEECH OUTPUT: 'Focus me 5 push button'",
+ "SPEECH OUTPUT: 'Focus me 6 kill switch'"]))
+
+sequence.append(utils.StartRecordingAction())
+sequence.append(KeyComboAction("Down"))
+sequence.append(utils.AssertPresentationAction(
+ "6. Line Down",
+ ["BRAILLE LINE: 'Here are some slides'",
+ " VISIBLE: 'Here are some slides', cursor=1",
+ "SPEECH OUTPUT: 'Here are some slides'"]))
+
+sequence.append(utils.AssertionSummaryAction())
+sequence.start()
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]