c937a697647ad9dfb3424312404e6e62417cc398
[platform/framework/web/crosswalk.git] / src / content / shell / renderer / test_runner / accessibility_controller.cc
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
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
71 AccessibilityControllerBindings::AccessibilityControllerBindings(
72     base::WeakPtr<AccessibilityController> controller)
73   : controller_(controller) {
74 }
75
76 AccessibilityControllerBindings::~AccessibilityControllerBindings() {
77 }
78
79 gin::ObjectTemplateBuilder
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
103 void AccessibilityControllerBindings::LogAccessibilityEvents() {
104   if (controller_)
105     controller_->LogAccessibilityEvents();
106 }
107
108 void AccessibilityControllerBindings::SetNotificationListener(
109     v8::Handle<v8::Function> callback) {
110   if (controller_)
111     controller_->SetNotificationListener(callback);
112 }
113
114 void AccessibilityControllerBindings::UnsetNotificationListener() {
115   if (controller_)
116     controller_->UnsetNotificationListener();
117 }
118
119 v8::Handle<v8::Object> AccessibilityControllerBindings::FocusedElement() {
120   return controller_ ? controller_->FocusedElement() : v8::Handle<v8::Object>();
121 }
122
123 v8::Handle<v8::Object> AccessibilityControllerBindings::RootElement() {
124   return controller_ ? controller_->RootElement() : v8::Handle<v8::Object>();
125 }
126
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
133 AccessibilityController::AccessibilityController()
134     : log_accessibility_events_(false),
135       weak_factory_(this) {
136 }
137
138 AccessibilityController::~AccessibilityController() {}
139
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
148 void AccessibilityController::Install(blink::WebFrame* frame) {
149   blink::WebAXObject::enableAccessibility();
150   blink::WebAXObject::enableInlineTextBoxAccessibility();
151   AccessibilityControllerBindings::Install(weak_factory_.GetWeakPtr(), frame);
152 }
153
154 void AccessibilityController::SetFocusedElement(
155     const blink::WebAXObject& focused_element) {
156   focused_element_ = focused_element;
157 }
158
159 bool AccessibilityController::ShouldLogAccessibilityEvents() {
160   return log_accessibility_events_;
161 }
162
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
205 void AccessibilityController::SetDelegate(WebTestDelegate* delegate) {
206   delegate_ = delegate;
207 }
208
209 void AccessibilityController::SetWebView(blink::WebView* web_view) {
210   web_view_ = web_view;
211 }
212
213 void AccessibilityController::LogAccessibilityEvents() {
214   log_accessibility_events_ = true;
215 }
216
217 void AccessibilityController::SetNotificationListener(
218     v8::Handle<v8::Function> callback) {
219   v8::Isolate* isolate = blink::mainThreadIsolate();
220   notification_callback_.Reset(isolate, callback);
221 }
222
223 void AccessibilityController::UnsetNotificationListener() {
224   notification_callback_.Reset();
225 }
226
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
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>
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>
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