summaryrefslogtreecommitdiffstats
path: root/content/browser/accessibility/browser_accessibility_manager_android.cc
blob: 5b57b482caaf98ad26b5f4a27dc1d4af1d2b1435 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
// Copyright 2013 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/browser/accessibility/browser_accessibility_manager_android.h"

#include <cmath>

#include "base/android/jni_android.h"
#include "base/android/jni_string.h"
#include "base/strings/string_number_conversions.h"
#include "base/utf_string_conversions.h"
#include "base/values.h"
#include "content/browser/accessibility/browser_accessibility_android.h"
#include "content/common/accessibility_messages.h"

using base::android::AttachCurrentThread;
using base::android::ScopedJavaLocalRef;

namespace {

// Restricts |val| to the range [min, max].
int Clamp(int val, int min, int max) {
  return std::min(std::max(val, min), max);
}

}  // anonymous namespace

namespace content {

namespace aria_strings {
  const char kAriaLivePolite[] = "polite";
  const char kAriaLiveAssertive[] = "assertive";
}

// static
BrowserAccessibilityManager* BrowserAccessibilityManager::Create(
    const AccessibilityNodeData& src,
    BrowserAccessibilityDelegate* delegate,
    BrowserAccessibilityFactory* factory) {
  return new BrowserAccessibilityManagerAndroid(ScopedJavaLocalRef<jobject>(),
                                                src, delegate, factory);
}

BrowserAccessibilityManagerAndroid::BrowserAccessibilityManagerAndroid(
    ScopedJavaLocalRef<jobject> content_view_core,
    const AccessibilityNodeData& src,
    BrowserAccessibilityDelegate* delegate,
    BrowserAccessibilityFactory* factory)
    : BrowserAccessibilityManager(src, delegate, factory) {
  if (content_view_core.is_null())
    return;

  // TODO(aboxhall): set up Java references
}

BrowserAccessibilityManagerAndroid::~BrowserAccessibilityManagerAndroid() {
  JNIEnv* env = base::android::AttachCurrentThread();
  ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
  if (obj.is_null())
    return;

  // TODO(aboxhall): tear down Java references
}

// static
AccessibilityNodeData BrowserAccessibilityManagerAndroid::GetEmptyDocument() {
  AccessibilityNodeData empty_document;
  empty_document.id = 0;
  empty_document.role = AccessibilityNodeData::ROLE_ROOT_WEB_AREA;
  empty_document.state = 1 << AccessibilityNodeData::STATE_READONLY;
  return empty_document;
}

void BrowserAccessibilityManagerAndroid::NotifyAccessibilityEvent(
    int type,
    BrowserAccessibility* node) {
  JNIEnv* env = base::android::AttachCurrentThread();
  ScopedJavaLocalRef<jobject> obj = java_ref_.get(env);
  if (obj.is_null())
    return;

  // TODO(aboxhall): call into appropriate Java method for each type of
  // notification
}

jint BrowserAccessibilityManagerAndroid::GetRootId(JNIEnv* env, jobject obj) {
  return static_cast<jint>(root_->renderer_id());
}

jint BrowserAccessibilityManagerAndroid::HitTest(
    JNIEnv* env, jobject obj, jint x, jint y) {
  BrowserAccessibilityAndroid* result =
      static_cast<BrowserAccessibilityAndroid*>(
          root_->BrowserAccessibilityForPoint(gfx::Point(x, y)));

  if (!result)
    return root_->renderer_id();

  if (result->IsFocusable())
    return result->renderer_id();

  // Examine the children of |result| to find the nearest accessibility focus
  // candidate
  BrowserAccessibility* nearest_node = FuzzyHitTest(x, y, result);
  if (nearest_node)
    return nearest_node->renderer_id();

  return root_->renderer_id();
}

BrowserAccessibility* BrowserAccessibilityManagerAndroid::FuzzyHitTest(
    int x, int y, BrowserAccessibility* start_node) {
  BrowserAccessibility* nearest_node = NULL;
  int min_distance = INT_MAX;
  FuzzyHitTestImpl(x, y, start_node, &nearest_node, &min_distance);
  return nearest_node;
}

// static
void BrowserAccessibilityManagerAndroid::FuzzyHitTestImpl(
    int x, int y, BrowserAccessibility* start_node,
    BrowserAccessibility** nearest_candidate, int* nearest_distance) {
  BrowserAccessibilityAndroid* node =
      static_cast<BrowserAccessibilityAndroid*>(start_node);
  int distance = CalculateDistanceSquared(x, y, node);

  if (node->IsFocusable()) {
    if (distance < *nearest_distance) {
      *nearest_candidate = node;
      *nearest_distance = distance;
    }
    // Don't examine any more children of focusable node
    // TODO(aboxhall): what about focusable children?
    return;
  }

  if (!node->ComputeName().empty()) {
    if (distance < *nearest_distance) {
      *nearest_candidate = node;
      *nearest_distance = distance;
    }
    return;
  }

  if (!node->IsLeaf()) {
    for (uint32 i = 0; i < node->child_count(); i++) {
      BrowserAccessibility* child = node->GetChild(i);
      FuzzyHitTestImpl(x, y, child, nearest_candidate, nearest_distance);
    }
  }
}

// static
int BrowserAccessibilityManagerAndroid::CalculateDistanceSquared(
    int x, int y, BrowserAccessibility* node) {
  gfx::Rect node_bounds = node->GetLocalBoundsRect();
  int nearest_x = Clamp(x, node_bounds.x(), node_bounds.right());
  int nearest_y = Clamp(y, node_bounds.y(), node_bounds.bottom());
  int dx = std::abs(x - nearest_x);
  int dy = std::abs(y - nearest_y);
  return dx * dx + dy * dy;
}

jint BrowserAccessibilityManagerAndroid::GetNativeNodeById(
    JNIEnv* env, jobject obj, jint id) {
  return reinterpret_cast<jint>(GetFromRendererID(id));
}

void BrowserAccessibilityManagerAndroid::NotifyRootChanged() {
  // TODO(aboxhall): non-stub implementation
}

bool
BrowserAccessibilityManagerAndroid::UseRootScrollOffsetsWhenComputingBounds() {
  // The Java layer handles the root scroll offset.
  return false;
}

bool RegisterBrowserAccessibilityManager(JNIEnv* env) {
  // TODO(aboxhall): non-stub implementation
  return false;
}

}  // namespace content