Update CrOS OOBE throbber to MD throbber; delete old asset
[chromium-blink-merge.git] / chrome / renderer / extensions / automation_internal_custom_bindings.cc
blob55543d3e703b68aab93f0a8e18f4d52444af4106
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 "chrome/renderer/extensions/automation_internal_custom_bindings.h"
7 #include "base/bind.h"
8 #include "base/memory/scoped_ptr.h"
9 #include "base/thread_task_runner_handle.h"
10 #include "base/values.h"
11 #include "chrome/common/extensions/chrome_extension_messages.h"
12 #include "chrome/common/extensions/manifest_handlers/automation.h"
13 #include "content/public/renderer/render_frame.h"
14 #include "content/public/renderer/render_thread.h"
15 #include "content/public/renderer/render_view.h"
16 #include "extensions/common/extension.h"
17 #include "extensions/common/manifest.h"
18 #include "extensions/renderer/script_context.h"
19 #include "ipc/message_filter.h"
20 #include "ui/accessibility/ax_enums.h"
21 #include "ui/accessibility/ax_node.h"
23 namespace {
25 // Helper to convert an enum to a V8 object.
26 template <typename EnumType>
27 v8::Local<v8::Object> ToEnumObject(v8::Isolate* isolate,
28 EnumType start_after,
29 EnumType end_at) {
30 v8::Local<v8::Object> object = v8::Object::New(isolate);
31 for (int i = start_after + 1; i <= end_at; ++i) {
32 v8::Local<v8::String> value = v8::String::NewFromUtf8(
33 isolate, ui::ToString(static_cast<EnumType>(i)).c_str());
34 object->Set(value, value);
36 return object;
39 } // namespace
41 namespace extensions {
43 TreeCache::TreeCache() {}
44 TreeCache::~TreeCache() {}
46 class AutomationMessageFilter : public IPC::MessageFilter {
47 public:
48 explicit AutomationMessageFilter(AutomationInternalCustomBindings* owner)
49 : owner_(owner),
50 removed_(false) {
51 DCHECK(owner);
52 content::RenderThread::Get()->AddFilter(this);
53 task_runner_ = base::ThreadTaskRunnerHandle::Get();
56 void Detach() {
57 owner_ = nullptr;
58 Remove();
61 // IPC::MessageFilter
62 bool OnMessageReceived(const IPC::Message& message) override {
63 task_runner_->PostTask(
64 FROM_HERE,
65 base::Bind(
66 &AutomationMessageFilter::OnMessageReceivedOnRenderThread,
67 this, message));
69 // Always return false in case there are multiple
70 // AutomationInternalCustomBindings instances attached to the same thread.
71 return false;
74 void OnFilterRemoved() override {
75 removed_ = true;
78 private:
79 void OnMessageReceivedOnRenderThread(const IPC::Message& message) {
80 if (owner_)
81 owner_->OnMessageReceived(message);
84 ~AutomationMessageFilter() override {
85 Remove();
88 void Remove() {
89 if (!removed_) {
90 removed_ = true;
91 content::RenderThread::Get()->RemoveFilter(this);
95 AutomationInternalCustomBindings* owner_;
96 bool removed_;
97 scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
99 DISALLOW_COPY_AND_ASSIGN(AutomationMessageFilter);
102 AutomationInternalCustomBindings::AutomationInternalCustomBindings(
103 ScriptContext* context) : ObjectBackedNativeHandler(context) {
104 // It's safe to use base::Unretained(this) here because these bindings
105 // will only be called on a valid AutomationInternalCustomBindings instance
106 // and none of the functions have any side effects.
107 #define ROUTE_FUNCTION(FN) \
108 RouteFunction(#FN, \
109 base::Bind(&AutomationInternalCustomBindings::FN, \
110 base::Unretained(this)))
112 ROUTE_FUNCTION(IsInteractPermitted);
113 ROUTE_FUNCTION(GetSchemaAdditions);
114 ROUTE_FUNCTION(GetRoutingID);
115 ROUTE_FUNCTION(StartCachingAccessibilityTrees);
116 ROUTE_FUNCTION(DestroyAccessibilityTree);
117 ROUTE_FUNCTION(GetRootID);
118 ROUTE_FUNCTION(GetParentID);
119 ROUTE_FUNCTION(GetChildCount);
120 ROUTE_FUNCTION(GetChildIDAtIndex);
121 ROUTE_FUNCTION(GetIndexInParent);
122 ROUTE_FUNCTION(GetState);
123 ROUTE_FUNCTION(GetRole);
124 ROUTE_FUNCTION(GetLocation);
125 ROUTE_FUNCTION(GetStringAttribute);
126 ROUTE_FUNCTION(GetBoolAttribute);
127 ROUTE_FUNCTION(GetIntAttribute);
128 ROUTE_FUNCTION(GetFloatAttribute);
129 ROUTE_FUNCTION(GetIntListAttribute);
130 ROUTE_FUNCTION(GetHtmlAttribute);
132 #undef ROUTE_FUNCTION
135 AutomationInternalCustomBindings::~AutomationInternalCustomBindings() {
136 if (message_filter_)
137 message_filter_->Detach();
138 STLDeleteContainerPairSecondPointers(tree_id_to_tree_cache_map_.begin(),
139 tree_id_to_tree_cache_map_.end());
142 void AutomationInternalCustomBindings::OnMessageReceived(
143 const IPC::Message& message) {
144 IPC_BEGIN_MESSAGE_MAP(AutomationInternalCustomBindings, message)
145 IPC_MESSAGE_HANDLER(ExtensionMsg_AccessibilityEvent, OnAccessibilityEvent)
146 IPC_END_MESSAGE_MAP()
149 void AutomationInternalCustomBindings::IsInteractPermitted(
150 const v8::FunctionCallbackInfo<v8::Value>& args) {
151 const Extension* extension = context()->extension();
152 CHECK(extension);
153 const AutomationInfo* automation_info = AutomationInfo::Get(extension);
154 CHECK(automation_info);
155 args.GetReturnValue().Set(
156 v8::Boolean::New(GetIsolate(), automation_info->interact));
159 void AutomationInternalCustomBindings::GetRoutingID(
160 const v8::FunctionCallbackInfo<v8::Value>& args) {
161 int routing_id = context()->GetRenderFrame()->GetRenderView()->GetRoutingID();
162 args.GetReturnValue().Set(v8::Integer::New(GetIsolate(), routing_id));
165 void AutomationInternalCustomBindings::StartCachingAccessibilityTrees(
166 const v8::FunctionCallbackInfo<v8::Value>& args) {
167 if (!message_filter_)
168 message_filter_ = new AutomationMessageFilter(this);
171 void AutomationInternalCustomBindings::GetSchemaAdditions(
172 const v8::FunctionCallbackInfo<v8::Value>& args) {
173 v8::Local<v8::Object> additions = v8::Object::New(GetIsolate());
175 additions->Set(
176 v8::String::NewFromUtf8(GetIsolate(), "EventType"),
177 ToEnumObject(GetIsolate(), ui::AX_EVENT_NONE, ui::AX_EVENT_LAST));
179 additions->Set(
180 v8::String::NewFromUtf8(GetIsolate(), "RoleType"),
181 ToEnumObject(GetIsolate(), ui::AX_ROLE_NONE, ui::AX_ROLE_LAST));
183 additions->Set(
184 v8::String::NewFromUtf8(GetIsolate(), "StateType"),
185 ToEnumObject(GetIsolate(), ui::AX_STATE_NONE, ui::AX_STATE_LAST));
187 additions->Set(
188 v8::String::NewFromUtf8(GetIsolate(), "TreeChangeType"),
189 ToEnumObject(GetIsolate(), ui::AX_MUTATION_NONE, ui::AX_MUTATION_LAST));
191 args.GetReturnValue().Set(additions);
194 void AutomationInternalCustomBindings::DestroyAccessibilityTree(
195 const v8::FunctionCallbackInfo<v8::Value>& args) {
196 if (args.Length() != 1 || !args[0]->IsNumber()) {
197 ThrowInvalidArgumentsException(args);
198 return;
201 int tree_id = args[0]->Int32Value();
202 auto iter = tree_id_to_tree_cache_map_.find(tree_id);
203 if (iter == tree_id_to_tree_cache_map_.end())
204 return;
206 TreeCache* cache = iter->second;
207 tree_id_to_tree_cache_map_.erase(tree_id);
208 axtree_to_tree_cache_map_.erase(&cache->tree);
209 delete cache;
213 // Access the cached accessibility trees and properties of their nodes.
216 void AutomationInternalCustomBindings::GetRootID(
217 const v8::FunctionCallbackInfo<v8::Value>& args) {
218 if (args.Length() != 1 || !args[0]->IsNumber()) {
219 ThrowInvalidArgumentsException(args);
220 return;
223 int tree_id = args[0]->Int32Value();
224 const auto iter = tree_id_to_tree_cache_map_.find(tree_id);
225 if (iter == tree_id_to_tree_cache_map_.end())
226 return;
228 TreeCache* cache = iter->second;
229 ui::AXNode* root = cache->tree.root();
231 // The root can be null if this is called from an onTreeChange callback.
232 if (!root)
233 return;
235 int root_id = root->id();
236 args.GetReturnValue().Set(v8::Integer::New(GetIsolate(), root_id));
239 void AutomationInternalCustomBindings::GetParentID(
240 const v8::FunctionCallbackInfo<v8::Value>& args) {
241 ui::AXNode* node = nullptr;
242 if (!GetNodeHelper(args, nullptr, &node))
243 return;
245 if (!node->parent())
246 return;
248 int parent_id = node->parent()->id();
249 args.GetReturnValue().Set(v8::Integer::New(GetIsolate(), parent_id));
252 void AutomationInternalCustomBindings::GetChildCount(
253 const v8::FunctionCallbackInfo<v8::Value>& args) {
254 ui::AXNode* node = nullptr;
255 if (!GetNodeHelper(args, nullptr, &node))
256 return;
258 int child_count = node->child_count();
259 args.GetReturnValue().Set(v8::Integer::New(GetIsolate(), child_count));
262 void AutomationInternalCustomBindings::GetChildIDAtIndex(
263 const v8::FunctionCallbackInfo<v8::Value>& args) {
264 if (args.Length() < 3 || !args[2]->IsNumber()) {
265 ThrowInvalidArgumentsException(args);
266 return;
269 ui::AXNode* node = nullptr;
270 if (!GetNodeHelper(args, nullptr, &node))
271 return;
273 int index = args[2]->Int32Value();
274 if (index < 0 || index >= node->child_count())
275 return;
277 int child_id = node->children()[index]->id();
278 args.GetReturnValue().Set(v8::Integer::New(GetIsolate(), child_id));
281 void AutomationInternalCustomBindings::GetIndexInParent(
282 const v8::FunctionCallbackInfo<v8::Value>& args) {
283 ui::AXNode* node = nullptr;
284 if (!GetNodeHelper(args, nullptr, &node))
285 return;
287 int index_in_parent = node->index_in_parent();
288 args.GetReturnValue().Set(v8::Integer::New(GetIsolate(), index_in_parent));
291 void AutomationInternalCustomBindings::GetState(
292 const v8::FunctionCallbackInfo<v8::Value>& args) {
293 ui::AXNode* node = nullptr;
294 if (!GetNodeHelper(args, nullptr, &node))
295 return;
297 v8::Local<v8::Object> state(v8::Object::New(GetIsolate()));
298 uint32 state_pos = 0, state_shifter = node->data().state;
299 while (state_shifter) {
300 if (state_shifter & 1) {
301 std::string key = ToString(static_cast<ui::AXState>(state_pos));
302 state->Set(CreateV8String(key),
303 v8::Boolean::New(GetIsolate(), true));
305 state_shifter = state_shifter >> 1;
306 state_pos++;
309 args.GetReturnValue().Set(state);
312 void AutomationInternalCustomBindings::GetRole(
313 const v8::FunctionCallbackInfo<v8::Value>& args) {
314 ui::AXNode* node = nullptr;
315 if (!GetNodeHelper(args, nullptr, &node))
316 return;
318 std::string role_name = ui::ToString(node->data().role);
319 args.GetReturnValue().Set(
320 v8::String::NewFromUtf8(GetIsolate(), role_name.c_str()));
323 void AutomationInternalCustomBindings::GetLocation(
324 const v8::FunctionCallbackInfo<v8::Value>& args) {
325 TreeCache* cache;
326 ui::AXNode* node = nullptr;
327 if (!GetNodeHelper(args, &cache, &node))
328 return;
330 v8::Local<v8::Object> location_obj(v8::Object::New(GetIsolate()));
331 gfx::Rect location = node->data().location;
332 location.Offset(cache->location_offset);
333 location_obj->Set(CreateV8String("left"),
334 v8::Integer::New(GetIsolate(), location.x()));
335 location_obj->Set(CreateV8String("top"),
336 v8::Integer::New(GetIsolate(), location.y()));
337 location_obj->Set(CreateV8String("width"),
338 v8::Integer::New(GetIsolate(), location.width()));
339 location_obj->Set(CreateV8String("height"),
340 v8::Integer::New(GetIsolate(), location.height()));
341 args.GetReturnValue().Set(location_obj);
344 void AutomationInternalCustomBindings::GetStringAttribute(
345 const v8::FunctionCallbackInfo<v8::Value>& args) {
346 ui::AXNode* node = nullptr;
347 std::string attribute_name;
348 if (!GetAttributeHelper(args, &node, &attribute_name))
349 return;
351 ui::AXStringAttribute attribute = ui::ParseAXStringAttribute(attribute_name);
352 std::string attr_value;
353 if (!node->data().GetStringAttribute(attribute, &attr_value))
354 return;
356 args.GetReturnValue().Set(
357 v8::String::NewFromUtf8(GetIsolate(), attr_value.c_str()));
360 void AutomationInternalCustomBindings::GetBoolAttribute(
361 const v8::FunctionCallbackInfo<v8::Value>& args) {
362 ui::AXNode* node = nullptr;
363 std::string attribute_name;
364 if (!GetAttributeHelper(args, &node, &attribute_name))
365 return;
367 ui::AXBoolAttribute attribute = ui::ParseAXBoolAttribute(attribute_name);
368 bool attr_value;
369 if (!node->data().GetBoolAttribute(attribute, &attr_value))
370 return;
372 args.GetReturnValue().Set(v8::Boolean::New(GetIsolate(), attr_value));
375 void AutomationInternalCustomBindings::GetIntAttribute(
376 const v8::FunctionCallbackInfo<v8::Value>& args) {
377 ui::AXNode* node = nullptr;
378 std::string attribute_name;
379 if (!GetAttributeHelper(args, &node, &attribute_name))
380 return;
382 ui::AXIntAttribute attribute = ui::ParseAXIntAttribute(attribute_name);
383 int attr_value;
384 if (!node->data().GetIntAttribute(attribute, &attr_value))
385 return;
387 args.GetReturnValue().Set(v8::Integer::New(GetIsolate(), attr_value));
390 void AutomationInternalCustomBindings::GetFloatAttribute(
391 const v8::FunctionCallbackInfo<v8::Value>& args) {
392 ui::AXNode* node = nullptr;
393 std::string attribute_name;
394 if (!GetAttributeHelper(args, &node, &attribute_name))
395 return;
397 ui::AXFloatAttribute attribute = ui::ParseAXFloatAttribute(attribute_name);
398 float attr_value;
400 if (!node->data().GetFloatAttribute(attribute, &attr_value))
401 return;
403 args.GetReturnValue().Set(v8::Number::New(GetIsolate(), attr_value));
406 void AutomationInternalCustomBindings::GetIntListAttribute(
407 const v8::FunctionCallbackInfo<v8::Value>& args) {
408 ui::AXNode* node = nullptr;
409 std::string attribute_name;
410 if (!GetAttributeHelper(args, &node, &attribute_name))
411 return;
413 ui::AXIntListAttribute attribute =
414 ui::ParseAXIntListAttribute(attribute_name);
415 if (!node->data().HasIntListAttribute(attribute))
416 return;
417 const std::vector<int32>& attr_value =
418 node->data().GetIntListAttribute(attribute);
420 v8::Local<v8::Array> result(v8::Array::New(GetIsolate(), attr_value.size()));
421 for (size_t i = 0; i < attr_value.size(); ++i)
422 result->Set(static_cast<uint32>(i),
423 v8::Integer::New(GetIsolate(), attr_value[i]));
424 args.GetReturnValue().Set(result);
427 void AutomationInternalCustomBindings::GetHtmlAttribute(
428 const v8::FunctionCallbackInfo<v8::Value>& args) {
429 ui::AXNode* node = nullptr;
430 std::string attribute_name;
431 if (!GetAttributeHelper(args, &node, &attribute_name))
432 return;
434 std::string attr_value;
435 if (!node->data().GetHtmlAttribute(attribute_name.c_str(), &attr_value))
436 return;
438 args.GetReturnValue().Set(
439 v8::String::NewFromUtf8(GetIsolate(), attr_value.c_str()));
443 // Helper functions.
446 void AutomationInternalCustomBindings::ThrowInvalidArgumentsException(
447 const v8::FunctionCallbackInfo<v8::Value>& args) {
448 GetIsolate()->ThrowException(
449 v8::String::NewFromUtf8(
450 GetIsolate(),
451 "Invalid arguments to AutomationInternalCustomBindings function",
452 v8::NewStringType::kNormal).ToLocalChecked());
454 LOG(FATAL)
455 << "Invalid arguments to AutomationInternalCustomBindings function"
456 << context()->GetStackTraceAsString();
459 bool AutomationInternalCustomBindings::GetNodeHelper(
460 const v8::FunctionCallbackInfo<v8::Value>& args,
461 TreeCache** out_cache,
462 ui::AXNode** out_node) {
463 if (args.Length() < 2 || !args[0]->IsNumber() || !args[1]->IsNumber()) {
464 ThrowInvalidArgumentsException(args);
465 return false;
468 int tree_id = args[0]->Int32Value();
469 int node_id = args[1]->Int32Value();
471 const auto iter = tree_id_to_tree_cache_map_.find(tree_id);
472 if (iter == tree_id_to_tree_cache_map_.end())
473 return false;
475 TreeCache* cache = iter->second;
476 ui::AXNode* node = cache->tree.GetFromId(node_id);
478 if (out_cache)
479 *out_cache = cache;
480 if (out_node)
481 *out_node = node;
483 return node != nullptr;
486 bool AutomationInternalCustomBindings::GetAttributeHelper(
487 const v8::FunctionCallbackInfo<v8::Value>& args,
488 ui::AXNode** out_node,
489 std::string* out_attribute_name) {
490 if (args.Length() != 3 ||
491 !args[2]->IsString()) {
492 ThrowInvalidArgumentsException(args);
493 return false;
496 TreeCache* cache = nullptr;
497 if (!GetNodeHelper(args, &cache, out_node))
498 return false;
500 *out_attribute_name = *v8::String::Utf8Value(args[2]);
501 return true;
504 v8::Local<v8::Value> AutomationInternalCustomBindings::CreateV8String(
505 const char* str) {
506 return v8::String::NewFromUtf8(
507 GetIsolate(), str, v8::String::kNormalString, strlen(str));
510 v8::Local<v8::Value> AutomationInternalCustomBindings::CreateV8String(
511 const std::string& str) {
512 return v8::String::NewFromUtf8(
513 GetIsolate(), str.c_str(), v8::String::kNormalString, str.length());
517 // Handle accessibility events from the browser process.
520 void AutomationInternalCustomBindings::OnAccessibilityEvent(
521 const ExtensionMsg_AccessibilityEventParams& params) {
522 int tree_id = params.tree_id;
523 TreeCache* cache;
524 auto iter = tree_id_to_tree_cache_map_.find(tree_id);
525 if (iter == tree_id_to_tree_cache_map_.end()) {
526 cache = new TreeCache();
527 cache->tab_id = -1;
528 cache->tree_id = params.tree_id;
529 cache->tree.SetDelegate(this);
530 tree_id_to_tree_cache_map_.insert(std::make_pair(tree_id, cache));
531 axtree_to_tree_cache_map_.insert(std::make_pair(&cache->tree, cache));
532 } else {
533 cache = iter->second;
536 cache->location_offset = params.location_offset;
537 if (!cache->tree.Unserialize(params.update)) {
538 LOG(ERROR) << cache->tree.error();
539 return;
542 v8::HandleScope handle_scope(GetIsolate());
543 v8::Context::Scope context_scope(context()->v8_context());
544 v8::Local<v8::Array> args(v8::Array::New(GetIsolate(), 1U));
545 v8::Local<v8::Object> event_params(v8::Object::New(GetIsolate()));
546 event_params->Set(CreateV8String("treeID"),
547 v8::Integer::New(GetIsolate(), params.tree_id));
548 event_params->Set(CreateV8String("targetID"),
549 v8::Integer::New(GetIsolate(), params.id));
550 event_params->Set(CreateV8String("eventType"),
551 CreateV8String(ToString(params.event_type)));
552 args->Set(0U, event_params);
553 context()->DispatchEvent("automationInternal.onAccessibilityEvent", args);
556 void AutomationInternalCustomBindings::OnNodeWillBeDeleted(ui::AXTree* tree,
557 ui::AXNode* node) {
558 SendTreeChangeEvent(
559 api::automation::TREE_CHANGE_TYPE_NODEREMOVED,
560 tree, node);
563 void AutomationInternalCustomBindings::OnSubtreeWillBeDeleted(
564 ui::AXTree* tree,
565 ui::AXNode* node) {
566 // This isn't strictly needed, as OnNodeWillBeDeleted will already be
567 // called. We could send a JS event for this only if it turns out to
568 // be needed for something.
571 void AutomationInternalCustomBindings::OnNodeCreated(ui::AXTree* tree,
572 ui::AXNode* node) {
573 // Not needed, this is called in the middle of an update so it's not
574 // safe to trigger JS from here. Wait for the notification in
575 // OnAtomicUpdateFinished instead.
578 void AutomationInternalCustomBindings::OnNodeChanged(ui::AXTree* tree,
579 ui::AXNode* node) {
580 // Not needed, this is called in the middle of an update so it's not
581 // safe to trigger JS from here. Wait for the notification in
582 // OnAtomicUpdateFinished instead.
585 void AutomationInternalCustomBindings::OnAtomicUpdateFinished(
586 ui::AXTree* tree,
587 bool root_changed,
588 const std::vector<ui::AXTreeDelegate::Change>& changes) {
589 auto iter = axtree_to_tree_cache_map_.find(tree);
590 if (iter == axtree_to_tree_cache_map_.end())
591 return;
593 for (auto change : changes) {
594 ui::AXNode* node = change.node;
595 switch (change.type) {
596 case NODE_CREATED:
597 SendTreeChangeEvent(
598 api::automation::TREE_CHANGE_TYPE_NODECREATED,
599 tree, node);
600 break;
601 case SUBTREE_CREATED:
602 SendTreeChangeEvent(
603 api::automation::TREE_CHANGE_TYPE_SUBTREECREATED,
604 tree, node);
605 break;
606 case NODE_CHANGED:
607 SendTreeChangeEvent(
608 api::automation::TREE_CHANGE_TYPE_NODECHANGED,
609 tree, node);
610 break;
615 void AutomationInternalCustomBindings::SendTreeChangeEvent(
616 api::automation::TreeChangeType change_type,
617 ui::AXTree* tree,
618 ui::AXNode* node) {
619 auto iter = axtree_to_tree_cache_map_.find(tree);
620 if (iter == axtree_to_tree_cache_map_.end())
621 return;
623 int tree_id = iter->second->tree_id;
625 v8::HandleScope handle_scope(GetIsolate());
626 v8::Context::Scope context_scope(context()->v8_context());
627 v8::Local<v8::Array> args(v8::Array::New(GetIsolate(), 3U));
628 args->Set(0U, v8::Integer::New(GetIsolate(), tree_id));
629 args->Set(1U, v8::Integer::New(GetIsolate(), node->id()));
630 args->Set(2U, CreateV8String(ToString(change_type)));
631 context()->DispatchEvent("automationInternal.onTreeChange", args);
634 } // namespace extensions