summaryrefslogtreecommitdiffstats
path: root/content/browser/accessibility
diff options
context:
space:
mode:
authordmazzoni@chromium.org <dmazzoni@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-03-18 00:09:34 +0000
committerdmazzoni@chromium.org <dmazzoni@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-03-18 00:09:34 +0000
commitf0779b5f9025898b681ea7b33e61e4682f19c224 (patch)
tree4b906ef97ef16698ae60246ccb792f8776c89042 /content/browser/accessibility
parent4e5592998c1a879c85c179543180c65e9dfe4855 (diff)
downloadchromium_src-f0779b5f9025898b681ea7b33e61e4682f19c224.zip
chromium_src-f0779b5f9025898b681ea7b33e61e4682f19c224.tar.gz
chromium_src-f0779b5f9025898b681ea7b33e61e4682f19c224.tar.bz2
Support TalkBack commands to move to next/prev element.
BUG=353051 Review URL: https://codereview.chromium.org/200323006 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@257542 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'content/browser/accessibility')
-rw-r--r--content/browser/accessibility/browser_accessibility_manager.cc34
-rw-r--r--content/browser/accessibility/browser_accessibility_manager.h4
-rw-r--r--content/browser/accessibility/browser_accessibility_manager_android.cc77
-rw-r--r--content/browser/accessibility/browser_accessibility_manager_android.h8
-rw-r--r--content/browser/accessibility/browser_accessibility_manager_unittest.cc49
5 files changed, 172 insertions, 0 deletions
diff --git a/content/browser/accessibility/browser_accessibility_manager.cc b/content/browser/accessibility/browser_accessibility_manager.cc
index c667377..8481b07 100644
--- a/content/browser/accessibility/browser_accessibility_manager.cc
+++ b/content/browser/accessibility/browser_accessibility_manager.cc
@@ -244,6 +244,40 @@ gfx::Rect BrowserAccessibilityManager::GetViewBounds() {
return gfx::Rect();
}
+BrowserAccessibility* BrowserAccessibilityManager::NextInTreeOrder(
+ BrowserAccessibility* node) {
+ if (!node)
+ return NULL;
+
+ if (node->PlatformChildCount() > 0)
+ return node->PlatformGetChild(0);
+ while (node) {
+ if (node->parent() &&
+ node->index_in_parent() <
+ static_cast<int>(node->parent()->PlatformChildCount()) - 1) {
+ return node->parent()->PlatformGetChild(node->index_in_parent() + 1);
+ }
+ node = node->parent();
+ }
+
+ return NULL;
+}
+
+BrowserAccessibility* BrowserAccessibilityManager::PreviousInTreeOrder(
+ BrowserAccessibility* node) {
+ if (!node)
+ return NULL;
+
+ if (node->parent() && node->index_in_parent() > 0) {
+ node = node->parent()->PlatformGetChild(node->index_in_parent() - 1);
+ while (node->PlatformChildCount() > 0)
+ node = node->PlatformGetChild(node->PlatformChildCount() - 1);
+ return node;
+ }
+
+ return node->parent();
+}
+
void BrowserAccessibilityManager::UpdateNodesForTesting(
const ui::AXNodeData& node1,
const ui::AXNodeData& node2 /* = ui::AXNodeData() */,
diff --git a/content/browser/accessibility/browser_accessibility_manager.h b/content/browser/accessibility/browser_accessibility_manager.h
index bd01f39..ffd754b 100644
--- a/content/browser/accessibility/browser_accessibility_manager.h
+++ b/content/browser/accessibility/browser_accessibility_manager.h
@@ -154,6 +154,10 @@ class CONTENT_EXPORT BrowserAccessibilityManager {
// scroll offsets separately.
virtual bool UseRootScrollOffsetsWhenComputingBounds();
+ // Walk the tree.
+ BrowserAccessibility* NextInTreeOrder(BrowserAccessibility* node);
+ BrowserAccessibility* PreviousInTreeOrder(BrowserAccessibility* node);
+
// For testing only: update the given nodes as if they were
// received from the renderer process in OnAccessibilityEvents.
// Takes up to 7 nodes at once so tests don't need to create a vector
diff --git a/content/browser/accessibility/browser_accessibility_manager_android.cc b/content/browser/accessibility/browser_accessibility_manager_android.cc
index 9516efe..c079825 100644
--- a/content/browser/accessibility/browser_accessibility_manager_android.cc
+++ b/content/browser/accessibility/browser_accessibility_manager_android.cc
@@ -26,11 +26,31 @@ enum {
ANDROID_ACCESSIBILITY_EVENT_TYPE_VIEW_TEXT_SELECTION_CHANGED = 8192
};
+enum AndroidHtmlElementType {
+ HTML_ELEMENT_TYPE_SECTION,
+ HTML_ELEMENT_TYPE_LIST,
+ HTML_ELEMENT_TYPE_CONTROL,
+ HTML_ELEMENT_TYPE_ANY
+};
+
// Restricts |val| to the range [min, max].
int Clamp(int val, int min, int max) {
return std::min(std::max(val, min), max);
}
+// These are special unofficial strings sent from TalkBack/BrailleBack
+// to jump to certain categories of web elements.
+AndroidHtmlElementType HtmlElementTypeFromString(base::string16 element_type) {
+ if (element_type == base::ASCIIToUTF16("SECTION"))
+ return HTML_ELEMENT_TYPE_SECTION;
+ else if (element_type == base::ASCIIToUTF16("LIST"))
+ return HTML_ELEMENT_TYPE_LIST;
+ else if (element_type == base::ASCIIToUTF16("CONTROL"))
+ return HTML_ELEMENT_TYPE_CONTROL;
+ else
+ return HTML_ELEMENT_TYPE_ANY;
+}
+
} // anonymous namespace
namespace content {
@@ -444,6 +464,63 @@ int BrowserAccessibilityManagerAndroid::CalculateDistanceSquared(
return dx * dx + dy * dy;
}
+jint BrowserAccessibilityManagerAndroid::FindElementType(
+ JNIEnv* env, jobject obj, jint start_id, jstring element_type_str,
+ jboolean forwards) {
+ BrowserAccessibility* node = GetFromRendererID(start_id);
+ if (!node)
+ return 0;
+
+ AndroidHtmlElementType element_type = HtmlElementTypeFromString(
+ base::android::ConvertJavaStringToUTF16(env, element_type_str));
+
+ node = forwards ? NextInTreeOrder(node) : PreviousInTreeOrder(node);
+ while (node) {
+ switch(element_type) {
+ case HTML_ELEMENT_TYPE_SECTION:
+ if (node->role() == ui::AX_ROLE_ARTICLE ||
+ node->role() == ui::AX_ROLE_APPLICATION ||
+ node->role() == ui::AX_ROLE_BANNER ||
+ node->role() == ui::AX_ROLE_COMPLEMENTARY ||
+ node->role() == ui::AX_ROLE_CONTENT_INFO ||
+ node->role() == ui::AX_ROLE_HEADING ||
+ node->role() == ui::AX_ROLE_MAIN ||
+ node->role() == ui::AX_ROLE_NAVIGATION ||
+ node->role() == ui::AX_ROLE_SEARCH ||
+ node->role() == ui::AX_ROLE_REGION) {
+ return node->renderer_id();
+ }
+ break;
+ case HTML_ELEMENT_TYPE_LIST:
+ if (node->role() == ui::AX_ROLE_LIST ||
+ node->role() == ui::AX_ROLE_GRID ||
+ node->role() == ui::AX_ROLE_TABLE ||
+ node->role() == ui::AX_ROLE_TREE) {
+ return node->renderer_id();
+ }
+ break;
+ case HTML_ELEMENT_TYPE_CONTROL:
+ if (static_cast<BrowserAccessibilityAndroid*>(node)->IsFocusable())
+ return node->renderer_id();
+ break;
+ case HTML_ELEMENT_TYPE_ANY:
+ // In theory, the API says that an accessibility service could
+ // jump to an element by element name, like 'H1' or 'P'. This isn't
+ // currently used by any accessibility service, and we think it's
+ // better to keep them high-level like 'SECTION' or 'CONTROL', so we
+ // just fall back on linear navigation when we don't recognize the
+ // element type.
+ if (static_cast<BrowserAccessibilityAndroid*>(node)->IsClickable())
+ return node->renderer_id();
+ break;
+ }
+
+ node = forwards ? NextInTreeOrder(node) : PreviousInTreeOrder(node);
+ }
+
+ return 0;
+}
+
void BrowserAccessibilityManagerAndroid::NotifyRootChanged() {
JNIEnv* env = AttachCurrentThread();
ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
diff --git a/content/browser/accessibility/browser_accessibility_manager_android.h b/content/browser/accessibility/browser_accessibility_manager_android.h
index 53cb617..e587eda 100644
--- a/content/browser/accessibility/browser_accessibility_manager_android.h
+++ b/content/browser/accessibility/browser_accessibility_manager_android.h
@@ -57,6 +57,14 @@ class CONTENT_EXPORT BrowserAccessibilityManagerAndroid
void Blur(JNIEnv* env, jobject obj);
void ScrollToMakeNodeVisible(JNIEnv* env, jobject obj, int id);
+ // Return the id of the next node in tree order in the direction given by
+ // |forwards|, starting with |start_id|, that matches |element_type|,
+ // where |element_type| is a special uppercase string from TalkBack or
+ // BrailleBack indicating general categories of web content like
+ // "SECTION" or "CONTROL". Return 0 if not found.
+ jint FindElementType(JNIEnv* env, jobject obj, jint start_id,
+ jstring element_type, jboolean forwards);
+
protected:
virtual void NotifyRootChanged() OVERRIDE;
diff --git a/content/browser/accessibility/browser_accessibility_manager_unittest.cc b/content/browser/accessibility/browser_accessibility_manager_unittest.cc
index 36e5d23..22d877b 100644
--- a/content/browser/accessibility/browser_accessibility_manager_unittest.cc
+++ b/content/browser/accessibility/browser_accessibility_manager_unittest.cc
@@ -886,4 +886,53 @@ TEST(BrowserAccessibilityManagerTest, MAYBE_BoundsForRangeOnParentElement) {
root_accessible->GetLocalBoundsForRange(0, 4).ToString());
}
+TEST(BrowserAccessibilityManagerTest, NextPreviousInTreeOrder) {
+ ui::AXNodeData root;
+ root.id = 1;
+ root.role = ui::AX_ROLE_ROOT_WEB_AREA;
+
+ ui::AXNodeData node2;
+ node2.id = 2;
+ root.child_ids.push_back(2);
+
+ ui::AXNodeData node3;
+ node3.id = 3;
+ root.child_ids.push_back(3);
+
+ ui::AXNodeData node4;
+ node4.id = 4;
+ node3.child_ids.push_back(4);
+
+ ui::AXNodeData node5;
+ node5.id = 5;
+ root.child_ids.push_back(5);
+
+ scoped_ptr<BrowserAccessibilityManager> manager(
+ BrowserAccessibilityManager::Create(
+ root,
+ NULL,
+ new CountedBrowserAccessibilityFactory()));
+ manager->UpdateNodesForTesting(node2, node3, node4, node5);
+
+ BrowserAccessibility* root_accessible = manager->GetRoot();
+ BrowserAccessibility* node2_accessible = root_accessible->PlatformGetChild(0);
+ BrowserAccessibility* node3_accessible = root_accessible->PlatformGetChild(1);
+ BrowserAccessibility* node4_accessible =
+ node3_accessible->PlatformGetChild(0);
+ BrowserAccessibility* node5_accessible = root_accessible->PlatformGetChild(2);
+
+ ASSERT_EQ(NULL, manager->NextInTreeOrder(NULL));
+ ASSERT_EQ(node2_accessible, manager->NextInTreeOrder(root_accessible));
+ ASSERT_EQ(node3_accessible, manager->NextInTreeOrder(node2_accessible));
+ ASSERT_EQ(node4_accessible, manager->NextInTreeOrder(node3_accessible));
+ ASSERT_EQ(node5_accessible, manager->NextInTreeOrder(node4_accessible));
+ ASSERT_EQ(NULL, manager->NextInTreeOrder(node5_accessible));
+
+ ASSERT_EQ(NULL, manager->PreviousInTreeOrder(NULL));
+ ASSERT_EQ(node4_accessible, manager->PreviousInTreeOrder(node5_accessible));
+ ASSERT_EQ(node3_accessible, manager->PreviousInTreeOrder(node4_accessible));
+ ASSERT_EQ(node2_accessible, manager->PreviousInTreeOrder(node3_accessible));
+ ASSERT_EQ(root_accessible, manager->PreviousInTreeOrder(node2_accessible));
+}
+
} // namespace content