1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "content/shell/renderer/test_runner/accessibility_controller.h"
6
7 #include "gin/handle.h"
8 #include "gin/object_template_builder.h"
9 #include "gin/wrappable.h"
10 #include "third_party/WebKit/public/web/WebElement.h"
11 #include "third_party/WebKit/public/web/WebFrame.h"
12 #include "third_party/WebKit/public/web/WebKit.h"
13 #include "third_party/WebKit/public/web/WebView.h"
14
15 namespace content {
16
17 class AccessibilityControllerBindings
18 : public gin::Wrappable<AccessibilityControllerBindings> {
19 public:
20 static gin::WrapperInfo kWrapperInfo;
21
22 static void Install(base::WeakPtr<AccessibilityController> controller,
23 blink::WebFrame* frame);
24
25 private:
26 explicit AccessibilityControllerBindings(
27 base::WeakPtr<AccessibilityController> controller);
28 virtual ~AccessibilityControllerBindings();
29
30 // gin::Wrappable:
31 virtual gin::ObjectTemplateBuilder GetObjectTemplateBuilder(
32 v8::Isolate* isolate) OVERRIDE;
33
34 void LogAccessibilityEvents();
35 void SetNotificationListener(v8::Handle<v8::Function> callback);
36 void UnsetNotificationListener();
37 v8::Handle<v8::Object> FocusedElement();
38 v8::Handle<v8::Object> RootElement();
39 v8::Handle<v8::Object> AccessibleElementById(const std::string& id);
40
41 base::WeakPtr<AccessibilityController> controller_;
42
43 DISALLOW_COPY_AND_ASSIGN(AccessibilityControllerBindings);
44 };
45
46 gin::WrapperInfo AccessibilityControllerBindings::kWrapperInfo = {
47 gin::kEmbedderNativeGin};
48
49 // static
Install(base::WeakPtr<AccessibilityController> controller,blink::WebFrame * frame)50 void AccessibilityControllerBindings::Install(
51 base::WeakPtr<AccessibilityController> controller,
52 blink::WebFrame* frame) {
53 v8::Isolate* isolate = blink::mainThreadIsolate();
54 v8::HandleScope handle_scope(isolate);
55 v8::Handle<v8::Context> context = frame->mainWorldScriptContext();
56 if (context.IsEmpty())
57 return;
58
59 v8::Context::Scope context_scope(context);
60
61 gin::Handle<AccessibilityControllerBindings> bindings =
62 gin::CreateHandle(isolate,
63 new AccessibilityControllerBindings(controller));
64 if (bindings.IsEmpty())
65 return;
66 v8::Handle<v8::Object> global = context->Global();
67 global->Set(gin::StringToV8(isolate, "accessibilityController"),
68 bindings.ToV8());
69 }
70
AccessibilityControllerBindings(base::WeakPtr<AccessibilityController> controller)71 AccessibilityControllerBindings::AccessibilityControllerBindings(
72 base::WeakPtr<AccessibilityController> controller)
73 : controller_(controller) {
74 }
75
~AccessibilityControllerBindings()76 AccessibilityControllerBindings::~AccessibilityControllerBindings() {
77 }
78
79 gin::ObjectTemplateBuilder
GetObjectTemplateBuilder(v8::Isolate * isolate)80 AccessibilityControllerBindings::GetObjectTemplateBuilder(
81 v8::Isolate* isolate) {
82 return gin::Wrappable<AccessibilityControllerBindings>::
83 GetObjectTemplateBuilder(isolate)
84 .SetMethod("logAccessibilityEvents",
85 &AccessibilityControllerBindings::LogAccessibilityEvents)
86 .SetMethod("setNotificationListener",
87 &AccessibilityControllerBindings::SetNotificationListener)
88 .SetMethod("unsetNotificationListener",
89 &AccessibilityControllerBindings::UnsetNotificationListener)
90 .SetProperty("focusedElement",
91 &AccessibilityControllerBindings::FocusedElement)
92 .SetProperty("rootElement",
93 &AccessibilityControllerBindings::RootElement)
94 .SetMethod("accessibleElementById",
95 &AccessibilityControllerBindings::AccessibleElementById)
96 // TODO(hajimehoshi): These are for backward compatibility. Remove them.
97 .SetMethod("addNotificationListener",
98 &AccessibilityControllerBindings::SetNotificationListener)
99 .SetMethod("removeNotificationListener",
100 &AccessibilityControllerBindings::UnsetNotificationListener);
101 }
102
LogAccessibilityEvents()103 void AccessibilityControllerBindings::LogAccessibilityEvents() {
104 if (controller_)
105 controller_->LogAccessibilityEvents();
106 }
107
SetNotificationListener(v8::Handle<v8::Function> callback)108 void AccessibilityControllerBindings::SetNotificationListener(
109 v8::Handle<v8::Function> callback) {
110 if (controller_)
111 controller_->SetNotificationListener(callback);
112 }
113
UnsetNotificationListener()114 void AccessibilityControllerBindings::UnsetNotificationListener() {
115 if (controller_)
116 controller_->UnsetNotificationListener();
117 }
118
FocusedElement()119 v8::Handle<v8::Object> AccessibilityControllerBindings::FocusedElement() {
120 return controller_ ? controller_->FocusedElement() : v8::Handle<v8::Object>();
121 }
122
RootElement()123 v8::Handle<v8::Object> AccessibilityControllerBindings::RootElement() {
124 return controller_ ? controller_->RootElement() : v8::Handle<v8::Object>();
125 }
126
AccessibleElementById(const std::string & id)127 v8::Handle<v8::Object> AccessibilityControllerBindings::AccessibleElementById(
128 const std::string& id) {
129 return controller_ ? controller_->AccessibleElementById(id)
130 : v8::Handle<v8::Object>();
131 }
132
AccessibilityController()133 AccessibilityController::AccessibilityController()
134 : log_accessibility_events_(false),
135 weak_factory_(this) {
136 }
137
~AccessibilityController()138 AccessibilityController::~AccessibilityController() {}
139
Reset()140 void AccessibilityController::Reset() {
141 root_element_ = blink::WebAXObject();
142 focused_element_ = blink::WebAXObject();
143 elements_.Clear();
144 notification_callback_.Reset();
145 log_accessibility_events_ = false;
146 }
147
Install(blink::WebFrame * frame)148 void AccessibilityController::Install(blink::WebFrame* frame) {
149 blink::WebAXObject::enableAccessibility();
150 blink::WebAXObject::enableInlineTextBoxAccessibility();
151 AccessibilityControllerBindings::Install(weak_factory_.GetWeakPtr(), frame);
152 }
153
SetFocusedElement(const blink::WebAXObject & focused_element)154 void AccessibilityController::SetFocusedElement(
155 const blink::WebAXObject& focused_element) {
156 focused_element_ = focused_element;
157 }
158
ShouldLogAccessibilityEvents()159 bool AccessibilityController::ShouldLogAccessibilityEvents() {
160 return log_accessibility_events_;
161 }
162
NotificationReceived(const blink::WebAXObject & target,const std::string & notification_name)163 void AccessibilityController::NotificationReceived(
164 const blink::WebAXObject& target, const std::string& notification_name) {
165 v8::Isolate* isolate = blink::mainThreadIsolate();
166 v8::HandleScope handle_scope(isolate);
167
168 blink::WebFrame* frame = web_view_->mainFrame();
169 if (!frame)
170 return;
171
172 v8::Handle<v8::Context> context = frame->mainWorldScriptContext();
173 if (context.IsEmpty())
174 return;
175
176 v8::Context::Scope context_scope(context);
177
178 // Call notification listeners on the element.
179 v8::Handle<v8::Object> element_handle = elements_.GetOrCreate(target);
180 if (element_handle.IsEmpty())
181 return;
182
183 WebAXObjectProxy* element;
184 bool result = gin::ConvertFromV8(isolate, element_handle, &element);
185 DCHECK(result);
186 element->NotificationReceived(frame, notification_name);
187
188 if (notification_callback_.IsEmpty())
189 return;
190
191 // Call global notification listeners.
192 v8::Handle<v8::Value> argv[] = {
193 element_handle,
194 v8::String::NewFromUtf8(isolate, notification_name.data(),
195 v8::String::kNormalString,
196 notification_name.size()),
197 };
198 frame->callFunctionEvenIfScriptDisabled(
199 v8::Local<v8::Function>::New(isolate, notification_callback_),
200 context->Global(),
201 arraysize(argv),
202 argv);
203 }
204
SetDelegate(WebTestDelegate * delegate)205 void AccessibilityController::SetDelegate(WebTestDelegate* delegate) {
206 delegate_ = delegate;
207 }
208
SetWebView(blink::WebView * web_view)209 void AccessibilityController::SetWebView(blink::WebView* web_view) {
210 web_view_ = web_view;
211 }
212
LogAccessibilityEvents()213 void AccessibilityController::LogAccessibilityEvents() {
214 log_accessibility_events_ = true;
215 }
216
SetNotificationListener(v8::Handle<v8::Function> callback)217 void AccessibilityController::SetNotificationListener(
218 v8::Handle<v8::Function> callback) {
219 v8::Isolate* isolate = blink::mainThreadIsolate();
220 notification_callback_.Reset(isolate, callback);
221 }
222
UnsetNotificationListener()223 void AccessibilityController::UnsetNotificationListener() {
224 notification_callback_.Reset();
225 }
226
FocusedElement()227 v8::Handle<v8::Object> AccessibilityController::FocusedElement() {
228 if (focused_element_.isNull())
229 focused_element_ = web_view_->accessibilityObject();
230 return elements_.GetOrCreate(focused_element_);
231 }
232
RootElement()233 v8::Handle<v8::Object> AccessibilityController::RootElement() {
234 if (root_element_.isNull())
235 root_element_ = web_view_->accessibilityObject();
236 return elements_.CreateRoot(root_element_);
237 }
238
239 v8::Handle<v8::Object>
AccessibleElementById(const std::string & id)240 AccessibilityController::AccessibleElementById(const std::string& id) {
241 if (root_element_.isNull())
242 root_element_ = web_view_->accessibilityObject();
243
244 if (!root_element_.updateBackingStoreAndCheckValidity())
245 return v8::Handle<v8::Object>();
246
247 return FindAccessibleElementByIdRecursive(
248 root_element_, blink::WebString::fromUTF8(id.c_str()));
249 }
250
251 v8::Handle<v8::Object>
FindAccessibleElementByIdRecursive(const blink::WebAXObject & obj,const blink::WebString & id)252 AccessibilityController::FindAccessibleElementByIdRecursive(
253 const blink::WebAXObject& obj, const blink::WebString& id) {
254 if (obj.isNull() || obj.isDetached())
255 return v8::Handle<v8::Object>();
256
257 blink::WebNode node = obj.node();
258 if (!node.isNull() && node.isElementNode()) {
259 blink::WebElement element = node.to<blink::WebElement>();
260 element.getAttribute("id");
261 if (element.getAttribute("id") == id)
262 return elements_.GetOrCreate(obj);
263 }
264
265 unsigned childCount = obj.childCount();
266 for (unsigned i = 0; i < childCount; i++) {
267 v8::Handle<v8::Object> result =
268 FindAccessibleElementByIdRecursive(obj.childAt(i), id);
269 if (*result)
270 return result;
271 }
272
273 return v8::Handle<v8::Object>();
274 }
275
276 } // namespace content
277