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 277