• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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