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.
5 #include "components/test_runner/accessibility_controller.h"
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/WebSettings.h"
14 #include "third_party/WebKit/public/web/WebView.h"
16 namespace test_runner
{
18 class AccessibilityControllerBindings
19 : public gin::Wrappable
<AccessibilityControllerBindings
> {
21 static gin::WrapperInfo kWrapperInfo
;
23 static void Install(base::WeakPtr
<AccessibilityController
> controller
,
24 blink::WebFrame
* frame
);
27 explicit AccessibilityControllerBindings(
28 base::WeakPtr
<AccessibilityController
> controller
);
29 ~AccessibilityControllerBindings() override
;
32 gin::ObjectTemplateBuilder
GetObjectTemplateBuilder(
33 v8::Isolate
* isolate
) override
;
35 void LogAccessibilityEvents();
36 void SetNotificationListener(v8::Local
<v8::Function
> callback
);
37 void UnsetNotificationListener();
38 v8::Local
<v8::Object
> FocusedElement();
39 v8::Local
<v8::Object
> RootElement();
40 v8::Local
<v8::Object
> AccessibleElementById(const std::string
& id
);
42 base::WeakPtr
<AccessibilityController
> controller_
;
44 DISALLOW_COPY_AND_ASSIGN(AccessibilityControllerBindings
);
47 gin::WrapperInfo
AccessibilityControllerBindings::kWrapperInfo
= {
48 gin::kEmbedderNativeGin
};
51 void AccessibilityControllerBindings::Install(
52 base::WeakPtr
<AccessibilityController
> controller
,
53 blink::WebFrame
* frame
) {
54 v8::Isolate
* isolate
= blink::mainThreadIsolate();
55 v8::HandleScope
handle_scope(isolate
);
56 v8::Local
<v8::Context
> context
= frame
->mainWorldScriptContext();
57 if (context
.IsEmpty())
60 v8::Context::Scope
context_scope(context
);
62 gin::Handle
<AccessibilityControllerBindings
> bindings
=
63 gin::CreateHandle(isolate
,
64 new AccessibilityControllerBindings(controller
));
65 if (bindings
.IsEmpty())
67 v8::Local
<v8::Object
> global
= context
->Global();
68 global
->Set(gin::StringToV8(isolate
, "accessibilityController"),
72 AccessibilityControllerBindings::AccessibilityControllerBindings(
73 base::WeakPtr
<AccessibilityController
> controller
)
74 : controller_(controller
) {
77 AccessibilityControllerBindings::~AccessibilityControllerBindings() {
80 gin::ObjectTemplateBuilder
81 AccessibilityControllerBindings::GetObjectTemplateBuilder(
82 v8::Isolate
* isolate
) {
83 return gin::Wrappable
<AccessibilityControllerBindings
>::
84 GetObjectTemplateBuilder(isolate
)
85 .SetMethod("logAccessibilityEvents",
86 &AccessibilityControllerBindings::LogAccessibilityEvents
)
87 .SetMethod("setNotificationListener",
88 &AccessibilityControllerBindings::SetNotificationListener
)
89 .SetMethod("unsetNotificationListener",
90 &AccessibilityControllerBindings::UnsetNotificationListener
)
91 .SetProperty("focusedElement",
92 &AccessibilityControllerBindings::FocusedElement
)
93 .SetProperty("rootElement",
94 &AccessibilityControllerBindings::RootElement
)
95 .SetMethod("accessibleElementById",
96 &AccessibilityControllerBindings::AccessibleElementById
)
97 // TODO(hajimehoshi): These are for backward compatibility. Remove them.
98 .SetMethod("addNotificationListener",
99 &AccessibilityControllerBindings::SetNotificationListener
)
100 .SetMethod("removeNotificationListener",
101 &AccessibilityControllerBindings::UnsetNotificationListener
);
104 void AccessibilityControllerBindings::LogAccessibilityEvents() {
106 controller_
->LogAccessibilityEvents();
109 void AccessibilityControllerBindings::SetNotificationListener(
110 v8::Local
<v8::Function
> callback
) {
112 controller_
->SetNotificationListener(callback
);
115 void AccessibilityControllerBindings::UnsetNotificationListener() {
117 controller_
->UnsetNotificationListener();
120 v8::Local
<v8::Object
> AccessibilityControllerBindings::FocusedElement() {
121 return controller_
? controller_
->FocusedElement() : v8::Local
<v8::Object
>();
124 v8::Local
<v8::Object
> AccessibilityControllerBindings::RootElement() {
125 return controller_
? controller_
->RootElement() : v8::Local
<v8::Object
>();
128 v8::Local
<v8::Object
> AccessibilityControllerBindings::AccessibleElementById(
129 const std::string
& id
) {
130 return controller_
? controller_
->AccessibleElementById(id
)
131 : v8::Local
<v8::Object
>();
134 AccessibilityController::AccessibilityController()
135 : log_accessibility_events_(false),
136 weak_factory_(this) {
139 AccessibilityController::~AccessibilityController() {}
141 void AccessibilityController::Reset() {
142 root_element_
= blink::WebAXObject();
143 focused_element_
= blink::WebAXObject();
145 notification_callback_
.Reset();
146 log_accessibility_events_
= false;
149 void AccessibilityController::Install(blink::WebFrame
* frame
) {
150 frame
->view()->settings()->setAccessibilityEnabled(true);
151 frame
->view()->settings()->setInlineTextBoxAccessibilityEnabled(true);
153 AccessibilityControllerBindings::Install(weak_factory_
.GetWeakPtr(), frame
);
156 void AccessibilityController::SetFocusedElement(
157 const blink::WebAXObject
& focused_element
) {
158 focused_element_
= focused_element
;
161 bool AccessibilityController::ShouldLogAccessibilityEvents() {
162 return log_accessibility_events_
;
165 void AccessibilityController::NotificationReceived(
166 const blink::WebAXObject
& target
, const std::string
& notification_name
) {
167 v8::Isolate
* isolate
= blink::mainThreadIsolate();
168 v8::HandleScope
handle_scope(isolate
);
170 blink::WebFrame
* frame
= web_view_
->mainFrame();
171 if (!frame
|| frame
->isWebRemoteFrame())
174 v8::Local
<v8::Context
> context
= frame
->mainWorldScriptContext();
175 if (context
.IsEmpty())
178 v8::Context::Scope
context_scope(context
);
180 // Call notification listeners on the element.
181 v8::Local
<v8::Object
> element_handle
= elements_
.GetOrCreate(target
);
182 if (element_handle
.IsEmpty())
185 WebAXObjectProxy
* element
;
186 bool result
= gin::ConvertFromV8(isolate
, element_handle
, &element
);
188 element
->NotificationReceived(frame
, notification_name
);
190 if (notification_callback_
.IsEmpty())
193 // Call global notification listeners.
194 v8::Local
<v8::Value
> argv
[] = {
196 v8::String::NewFromUtf8(isolate
, notification_name
.data(),
197 v8::String::kNormalString
,
198 notification_name
.size()),
200 frame
->callFunctionEvenIfScriptDisabled(
201 v8::Local
<v8::Function
>::New(isolate
, notification_callback_
),
207 void AccessibilityController::SetDelegate(WebTestDelegate
* delegate
) {
208 delegate_
= delegate
;
211 void AccessibilityController::SetWebView(blink::WebView
* web_view
) {
212 web_view_
= web_view
;
215 void AccessibilityController::LogAccessibilityEvents() {
216 log_accessibility_events_
= true;
219 void AccessibilityController::SetNotificationListener(
220 v8::Local
<v8::Function
> callback
) {
221 v8::Isolate
* isolate
= blink::mainThreadIsolate();
222 notification_callback_
.Reset(isolate
, callback
);
225 void AccessibilityController::UnsetNotificationListener() {
226 notification_callback_
.Reset();
229 v8::Local
<v8::Object
> AccessibilityController::FocusedElement() {
230 if (focused_element_
.isNull())
231 focused_element_
= web_view_
->accessibilityObject();
232 return elements_
.GetOrCreate(focused_element_
);
235 v8::Local
<v8::Object
> AccessibilityController::RootElement() {
236 if (root_element_
.isNull())
237 root_element_
= web_view_
->accessibilityObject();
238 return elements_
.GetOrCreate(root_element_
);
241 v8::Local
<v8::Object
>
242 AccessibilityController::AccessibleElementById(const std::string
& id
) {
243 if (root_element_
.isNull())
244 root_element_
= web_view_
->accessibilityObject();
246 if (!root_element_
.updateLayoutAndCheckValidity())
247 return v8::Local
<v8::Object
>();
249 return FindAccessibleElementByIdRecursive(
250 root_element_
, blink::WebString::fromUTF8(id
.c_str()));
253 v8::Local
<v8::Object
>
254 AccessibilityController::FindAccessibleElementByIdRecursive(
255 const blink::WebAXObject
& obj
, const blink::WebString
& id
) {
256 if (obj
.isNull() || obj
.isDetached())
257 return v8::Local
<v8::Object
>();
259 blink::WebNode node
= obj
.node();
260 if (!node
.isNull() && node
.isElementNode()) {
261 blink::WebElement element
= node
.to
<blink::WebElement
>();
262 element
.getAttribute("id");
263 if (element
.getAttribute("id") == id
)
264 return elements_
.GetOrCreate(obj
);
267 unsigned childCount
= obj
.childCount();
268 for (unsigned i
= 0; i
< childCount
; i
++) {
269 v8::Local
<v8::Object
> result
=
270 FindAccessibleElementByIdRecursive(obj
.childAt(i
), id
);
275 return v8::Local
<v8::Object
>();
278 } // namespace test_runner