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/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