Home | History | Annotate | Download | only in test_runner
      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