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/WebSettings.h" 14 #include "third_party/WebKit/public/web/WebView.h" 15 16 namespace content { 17 18 class AccessibilityControllerBindings 19 : public gin::Wrappable<AccessibilityControllerBindings> { 20 public: 21 static gin::WrapperInfo kWrapperInfo; 22 23 static void Install(base::WeakPtr<AccessibilityController> controller, 24 blink::WebFrame* frame); 25 26 private: 27 explicit AccessibilityControllerBindings( 28 base::WeakPtr<AccessibilityController> controller); 29 virtual ~AccessibilityControllerBindings(); 30 31 // gin::Wrappable: 32 virtual gin::ObjectTemplateBuilder GetObjectTemplateBuilder( 33 v8::Isolate* isolate) OVERRIDE; 34 35 void LogAccessibilityEvents(); 36 void SetNotificationListener(v8::Handle<v8::Function> callback); 37 void UnsetNotificationListener(); 38 v8::Handle<v8::Object> FocusedElement(); 39 v8::Handle<v8::Object> RootElement(); 40 v8::Handle<v8::Object> AccessibleElementById(const std::string& id); 41 42 base::WeakPtr<AccessibilityController> controller_; 43 44 DISALLOW_COPY_AND_ASSIGN(AccessibilityControllerBindings); 45 }; 46 47 gin::WrapperInfo AccessibilityControllerBindings::kWrapperInfo = { 48 gin::kEmbedderNativeGin}; 49 50 // static 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::Handle<v8::Context> context = frame->mainWorldScriptContext(); 57 if (context.IsEmpty()) 58 return; 59 60 v8::Context::Scope context_scope(context); 61 62 gin::Handle<AccessibilityControllerBindings> bindings = 63 gin::CreateHandle(isolate, 64 new AccessibilityControllerBindings(controller)); 65 if (bindings.IsEmpty()) 66 return; 67 v8::Handle<v8::Object> global = context->Global(); 68 global->Set(gin::StringToV8(isolate, "accessibilityController"), 69 bindings.ToV8()); 70 } 71 72 AccessibilityControllerBindings::AccessibilityControllerBindings( 73 base::WeakPtr<AccessibilityController> controller) 74 : controller_(controller) { 75 } 76 77 AccessibilityControllerBindings::~AccessibilityControllerBindings() { 78 } 79 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); 102 } 103 104 void AccessibilityControllerBindings::LogAccessibilityEvents() { 105 if (controller_) 106 controller_->LogAccessibilityEvents(); 107 } 108 109 void AccessibilityControllerBindings::SetNotificationListener( 110 v8::Handle<v8::Function> callback) { 111 if (controller_) 112 controller_->SetNotificationListener(callback); 113 } 114 115 void AccessibilityControllerBindings::UnsetNotificationListener() { 116 if (controller_) 117 controller_->UnsetNotificationListener(); 118 } 119 120 v8::Handle<v8::Object> AccessibilityControllerBindings::FocusedElement() { 121 return controller_ ? controller_->FocusedElement() : v8::Handle<v8::Object>(); 122 } 123 124 v8::Handle<v8::Object> AccessibilityControllerBindings::RootElement() { 125 return controller_ ? controller_->RootElement() : v8::Handle<v8::Object>(); 126 } 127 128 v8::Handle<v8::Object> AccessibilityControllerBindings::AccessibleElementById( 129 const std::string& id) { 130 return controller_ ? controller_->AccessibleElementById(id) 131 : v8::Handle<v8::Object>(); 132 } 133 134 AccessibilityController::AccessibilityController() 135 : log_accessibility_events_(false), 136 weak_factory_(this) { 137 } 138 139 AccessibilityController::~AccessibilityController() {} 140 141 void AccessibilityController::Reset() { 142 root_element_ = blink::WebAXObject(); 143 focused_element_ = blink::WebAXObject(); 144 elements_.Clear(); 145 notification_callback_.Reset(); 146 log_accessibility_events_ = false; 147 } 148 149 void AccessibilityController::Install(blink::WebFrame* frame) { 150 frame->view()->settings()->setAccessibilityEnabled(true); 151 frame->view()->settings()->setInlineTextBoxAccessibilityEnabled(true); 152 153 AccessibilityControllerBindings::Install(weak_factory_.GetWeakPtr(), frame); 154 } 155 156 void AccessibilityController::SetFocusedElement( 157 const blink::WebAXObject& focused_element) { 158 focused_element_ = focused_element; 159 } 160 161 bool AccessibilityController::ShouldLogAccessibilityEvents() { 162 return log_accessibility_events_; 163 } 164 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); 169 170 blink::WebFrame* frame = web_view_->mainFrame(); 171 if (!frame) 172 return; 173 174 v8::Handle<v8::Context> context = frame->mainWorldScriptContext(); 175 if (context.IsEmpty()) 176 return; 177 178 v8::Context::Scope context_scope(context); 179 180 // Call notification listeners on the element. 181 v8::Handle<v8::Object> element_handle = elements_.GetOrCreate(target); 182 if (element_handle.IsEmpty()) 183 return; 184 185 WebAXObjectProxy* element; 186 bool result = gin::ConvertFromV8(isolate, element_handle, &element); 187 DCHECK(result); 188 element->NotificationReceived(frame, notification_name); 189 190 if (notification_callback_.IsEmpty()) 191 return; 192 193 // Call global notification listeners. 194 v8::Handle<v8::Value> argv[] = { 195 element_handle, 196 v8::String::NewFromUtf8(isolate, notification_name.data(), 197 v8::String::kNormalString, 198 notification_name.size()), 199 }; 200 frame->callFunctionEvenIfScriptDisabled( 201 v8::Local<v8::Function>::New(isolate, notification_callback_), 202 context->Global(), 203 arraysize(argv), 204 argv); 205 } 206 207 void AccessibilityController::SetDelegate(WebTestDelegate* delegate) { 208 delegate_ = delegate; 209 } 210 211 void AccessibilityController::SetWebView(blink::WebView* web_view) { 212 web_view_ = web_view; 213 } 214 215 void AccessibilityController::LogAccessibilityEvents() { 216 log_accessibility_events_ = true; 217 } 218 219 void AccessibilityController::SetNotificationListener( 220 v8::Handle<v8::Function> callback) { 221 v8::Isolate* isolate = blink::mainThreadIsolate(); 222 notification_callback_.Reset(isolate, callback); 223 } 224 225 void AccessibilityController::UnsetNotificationListener() { 226 notification_callback_.Reset(); 227 } 228 229 v8::Handle<v8::Object> AccessibilityController::FocusedElement() { 230 if (focused_element_.isNull()) 231 focused_element_ = web_view_->accessibilityObject(); 232 return elements_.GetOrCreate(focused_element_); 233 } 234 235 v8::Handle<v8::Object> AccessibilityController::RootElement() { 236 if (root_element_.isNull()) 237 root_element_ = web_view_->accessibilityObject(); 238 return elements_.CreateRoot(root_element_); 239 } 240 241 v8::Handle<v8::Object> 242 AccessibilityController::AccessibleElementById(const std::string& id) { 243 if (root_element_.isNull()) 244 root_element_ = web_view_->accessibilityObject(); 245 246 if (!root_element_.updateBackingStoreAndCheckValidity()) 247 return v8::Handle<v8::Object>(); 248 249 return FindAccessibleElementByIdRecursive( 250 root_element_, blink::WebString::fromUTF8(id.c_str())); 251 } 252 253 v8::Handle<v8::Object> 254 AccessibilityController::FindAccessibleElementByIdRecursive( 255 const blink::WebAXObject& obj, const blink::WebString& id) { 256 if (obj.isNull() || obj.isDetached()) 257 return v8::Handle<v8::Object>(); 258 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); 265 } 266 267 unsigned childCount = obj.childCount(); 268 for (unsigned i = 0; i < childCount; i++) { 269 v8::Handle<v8::Object> result = 270 FindAccessibleElementByIdRecursive(obj.childAt(i), id); 271 if (*result) 272 return result; 273 } 274 275 return v8::Handle<v8::Object>(); 276 } 277 278 } // namespace content 279