Index: content/public/browser/voice_interaction_helper.cc |
diff --git a/content/public/browser/voice_interaction_helper.cc b/content/public/browser/voice_interaction_helper.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..a038b79db2d7b05b932508753cf9b818a764b62b |
--- /dev/null |
+++ b/content/public/browser/voice_interaction_helper.cc |
@@ -0,0 +1,149 @@ |
+// Copyright 2017 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "content/public/browser/voice_interaction_helper.h" |
+ |
+#include "content/browser/accessibility/browser_accessibility.h" |
+#include "content/browser/web_contents/web_contents_impl.h" |
+#include "content/public/browser/web_contents.h" |
+#include "ui/accessibility/platform/ax_android_constants.h" |
+ |
+namespace content { |
+ |
+namespace { |
+ |
+std::string GetClassName(BrowserAccessibility* node) { |
+ switch (node->GetRole()) { |
+ case ui::AX_ROLE_SEARCH_BOX: |
+ case ui::AX_ROLE_SPIN_BUTTON: |
+ case ui::AX_ROLE_TEXT_FIELD: |
+ return ui::kAXEditTextClassname; |
+ case ui::AX_ROLE_SLIDER: |
+ return ui::kAXSeekBarClassname; |
+ case ui::AX_ROLE_COLOR_WELL: |
+ case ui::AX_ROLE_COMBO_BOX: |
+ case ui::AX_ROLE_DATE: |
+ case ui::AX_ROLE_POP_UP_BUTTON: |
+ case ui::AX_ROLE_INPUT_TIME: |
+ return ui::kAXSpinnerClassname; |
+ case ui::AX_ROLE_BUTTON: |
+ case ui::AX_ROLE_MENU_BUTTON: |
+ return ui::kAXButtonClassname; |
+ case ui::AX_ROLE_CHECK_BOX: |
+ case ui::AX_ROLE_SWITCH: |
+ return ui::kAXCheckBoxClassname; |
+ case ui::AX_ROLE_RADIO_BUTTON: |
+ return ui::kAXRadioButtonClassname; |
+ case ui::AX_ROLE_TOGGLE_BUTTON: |
+ return ui::kAXToggleButtonClassname; |
+ case ui::AX_ROLE_CANVAS: |
+ case ui::AX_ROLE_IMAGE: |
+ case ui::AX_ROLE_SVG_ROOT: |
+ return ui::kAXImageClassname; |
+ case ui::AX_ROLE_METER: |
+ case ui::AX_ROLE_PROGRESS_INDICATOR: |
+ return ui::kAXProgressBarClassname; |
+ case ui::AX_ROLE_TAB_LIST: |
+ return ui::kAXTabWidgetClassname; |
+ case ui::AX_ROLE_GRID: |
+ case ui::AX_ROLE_TABLE: |
+ return ui::kAXGridViewClassname; |
+ case ui::AX_ROLE_LIST: |
+ case ui::AX_ROLE_LIST_BOX: |
+ case ui::AX_ROLE_DESCRIPTION_LIST: |
+ return ui::kAXListViewClassname; |
+ case ui::AX_ROLE_DIALOG: |
+ return ui::kAXDialogClassname; |
+ case ui::AX_ROLE_ROOT_WEB_AREA: |
+ return node->PlatformGetParent() == nullptr ? ui::kAXWebViewClassname |
+ : ui::kAXViewClassname; |
+ case ui::AX_ROLE_MENU_ITEM: |
+ case ui::AX_ROLE_MENU_ITEM_CHECK_BOX: |
+ case ui::AX_ROLE_MENU_ITEM_RADIO: |
+ return ui::kAXMenuItemClassname; |
+ default: |
+ return ui::kAXViewClassname; |
+ } |
+} |
+ |
+// TODO(muyuanli): refactor code in web_contents_android |
+// to use this function as well. |
+std::unique_ptr<AXViewStructure> WalkAXTreeDepthFirst( |
+ BrowserAccessibility* node, |
+ gfx::Rect rect, |
+ const ui::AXTreeUpdate& update) { |
+ auto result = base::MakeUnique<AXViewStructure>(); |
+ result->text = node->GetText(); |
+ result->class_name = GetClassName(node); |
+ |
+ const int text_style = node->GetIntAttribute(ui::AX_ATTR_TEXT_STYLE); |
+ result->bold = (text_style & ui::AX_TEXT_STYLE_BOLD) != 0; |
+ result->italic = (text_style & ui::AX_TEXT_STYLE_ITALIC) != 0; |
+ result->line_through = (text_style & ui::AX_TEXT_STYLE_LINE_THROUGH) != 0; |
+ result->underline = (text_style & ui::AX_TEXT_STYLE_ITALIC) != 0; |
+ |
+ result->text_size = node->GetFloatAttribute(ui::AX_ATTR_FONT_SIZE); |
+ result->bgcolor = node->GetIntAttribute(ui::AX_ATTR_BACKGROUND_COLOR); |
+ |
+ const gfx::Rect& absolute_rect = node->GetPageBoundsRect(); |
+ gfx::Rect parent_relative_rect = absolute_rect; |
+ bool is_root = node->GetParent() == nullptr; |
+ if (!is_root) { |
+ parent_relative_rect.Offset(-rect.OffsetFromOrigin()); |
+ } |
+ result->rect = gfx::Rect(parent_relative_rect.x(), parent_relative_rect.y(), |
+ absolute_rect.width(), absolute_rect.height()); |
+ result->has_selection = false; |
+ |
+ if (node->PlatformIsLeaf()) { |
+ int start_selection = 0; |
+ int end_selection = 0; |
+ if (update.tree_data.sel_anchor_object_id == node->GetId()) { |
+ start_selection = update.tree_data.sel_anchor_offset; |
+ end_selection = node->GetText().length(); |
+ } |
+ if (update.tree_data.sel_focus_object_id == node->GetId()) { |
+ end_selection = update.tree_data.sel_focus_offset; |
+ } |
+ if (end_selection > 0) { |
+ result->has_selection = true; |
+ result->start_selection = start_selection; |
+ result->end_selection = end_selection; |
+ } |
+ } |
+ |
+ for (unsigned int i = 0; i < node->PlatformChildCount(); i++) { |
+ auto child = |
+ WalkAXTreeDepthFirst(node->PlatformGetChild(i), absolute_rect, update); |
+ result->children.push_back(std::move(child)); |
+ } |
+ |
+ return result; |
+} |
+ |
+void AXSnapshotCallback( |
+ const base::Callback<void(std::unique_ptr<AXViewStructure>)>& callback, |
+ const ui::AXTreeUpdate& ax_tree_update) { |
+ auto manager = std::unique_ptr<content::BrowserAccessibilityManager>( |
+ content::BrowserAccessibilityManager::Create(ax_tree_update, nullptr)); |
+ callback.Run( |
+ WalkAXTreeDepthFirst(manager->GetRoot(), gfx::Rect(), ax_tree_update)); |
+} |
+ |
+} // namespace |
+ |
+AXViewStructure::AXViewStructure() = default; |
+ |
+AXViewStructure::~AXViewStructure() = default; |
+ |
+void GetAXViewStructure( |
+ WebContents* web_contents, |
+ const base::Callback<void(std::unique_ptr<AXViewStructure>)>& callback) { |
+ WebContentsImpl* web_contents_impl = |
+ static_cast<WebContentsImpl*>(web_contents); |
+ web_contents_impl->RequestAXTreeSnapshot( |
+ base::Bind(&AXSnapshotCallback, callback)); |
+} |
+ |
+} // namespace content |