| Index: chrome/renderer/extensions/automation_internal_custom_bindings.cc
|
| diff --git a/chrome/renderer/extensions/automation_internal_custom_bindings.cc b/chrome/renderer/extensions/automation_internal_custom_bindings.cc
|
| index ea8cb5753e795734b4b1a82e534ba7170f712a3a..0cf37f211f024b2e3068d143b84139e2405ac7ec 100644
|
| --- a/chrome/renderer/extensions/automation_internal_custom_bindings.cc
|
| +++ b/chrome/renderer/extensions/automation_internal_custom_bindings.cc
|
| @@ -39,6 +39,9 @@ v8::Local<v8::Object> ToEnumObject(v8::Isolate* isolate,
|
|
|
| namespace extensions {
|
|
|
| +TreeCache::TreeCache() {}
|
| +TreeCache::~TreeCache() {}
|
| +
|
| class AutomationMessageFilter : public IPC::MessageFilter {
|
| public:
|
| explicit AutomationMessageFilter(AutomationInternalCustomBindings* owner)
|
| @@ -46,6 +49,7 @@ class AutomationMessageFilter : public IPC::MessageFilter {
|
| removed_(false) {
|
| DCHECK(owner);
|
| content::RenderThread::Get()->AddFilter(this);
|
| + task_runner_ = content::RenderThread::Get()->GetTaskRunner();
|
| }
|
|
|
| void Detach() {
|
| @@ -55,10 +59,12 @@ class AutomationMessageFilter : public IPC::MessageFilter {
|
|
|
| // IPC::MessageFilter
|
| bool OnMessageReceived(const IPC::Message& message) override {
|
| - if (owner_)
|
| - return owner_->OnMessageReceived(message);
|
| - else
|
| - return false;
|
| + task_runner_->PostTask(
|
| + FROM_HERE,
|
| + base::Bind(
|
| + &AutomationMessageFilter::OnMessageReceivedOnRenderThread,
|
| + this, message));
|
| + return false;
|
| }
|
|
|
| void OnFilterRemoved() override {
|
| @@ -66,6 +72,11 @@ class AutomationMessageFilter : public IPC::MessageFilter {
|
| }
|
|
|
| private:
|
| + void OnMessageReceivedOnRenderThread(const IPC::Message& message) {
|
| + if (owner_)
|
| + owner_->OnMessageReceived(message);
|
| + }
|
| +
|
| ~AutomationMessageFilter() override {
|
| Remove();
|
| }
|
| @@ -79,6 +90,7 @@ private:
|
|
|
| AutomationInternalCustomBindings* owner_;
|
| bool removed_;
|
| + scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
|
|
|
| DISALLOW_COPY_AND_ASSIGN(AutomationMessageFilter);
|
| };
|
| @@ -88,24 +100,34 @@ AutomationInternalCustomBindings::AutomationInternalCustomBindings(
|
| // It's safe to use base::Unretained(this) here because these bindings
|
| // will only be called on a valid AutomationInternalCustomBindings instance
|
| // and none of the functions have any side effects.
|
| - RouteFunction(
|
| - "IsInteractPermitted",
|
| - base::Bind(&AutomationInternalCustomBindings::IsInteractPermitted,
|
| - base::Unretained(this)));
|
| - RouteFunction(
|
| - "GetSchemaAdditions",
|
| - base::Bind(&AutomationInternalCustomBindings::GetSchemaAdditions,
|
| - base::Unretained(this)));
|
| - RouteFunction(
|
| - "GetRoutingID",
|
| - base::Bind(&AutomationInternalCustomBindings::GetRoutingID,
|
| - base::Unretained(this)));
|
| -
|
| - message_filter_ = new AutomationMessageFilter(this);
|
| + #define ROUTE_FUNCTION(FN) \
|
| + RouteFunction(#FN, \
|
| + base::Bind(&AutomationInternalCustomBindings::FN, \
|
| + base::Unretained(this)))
|
| +
|
| + ROUTE_FUNCTION(IsInteractPermitted);
|
| + ROUTE_FUNCTION(GetSchemaAdditions);
|
| + ROUTE_FUNCTION(GetRoutingID);
|
| + ROUTE_FUNCTION(StartCachingAccessibilityTrees);
|
| + ROUTE_FUNCTION(DestroyAccessibilityTree);
|
| + ROUTE_FUNCTION(GetRootID);
|
| + ROUTE_FUNCTION(GetParentID);
|
| + ROUTE_FUNCTION(GetChildCount);
|
| + ROUTE_FUNCTION(GetChildIDAtIndex);
|
| + ROUTE_FUNCTION(GetIndexInParent);
|
| + ROUTE_FUNCTION(GetState);
|
| + ROUTE_FUNCTION(GetRole);
|
| + ROUTE_FUNCTION(GetLocation);
|
| + ROUTE_FUNCTION(GetStringAttribute);
|
| + ROUTE_FUNCTION(GetBoolAttribute);
|
| + ROUTE_FUNCTION(GetIntAttribute);
|
| + ROUTE_FUNCTION(GetFloatAttribute);
|
| + ROUTE_FUNCTION(GetIntListAttribute);
|
| }
|
|
|
| AutomationInternalCustomBindings::~AutomationInternalCustomBindings() {
|
| - message_filter_->Detach();
|
| + if (message_filter_)
|
| + message_filter_->Detach();
|
| }
|
|
|
| bool AutomationInternalCustomBindings::OnMessageReceived(
|
| @@ -135,6 +157,12 @@ void AutomationInternalCustomBindings::GetRoutingID(
|
| args.GetReturnValue().Set(v8::Integer::New(GetIsolate(), routing_id));
|
| }
|
|
|
| +void AutomationInternalCustomBindings::StartCachingAccessibilityTrees(
|
| + const v8::FunctionCallbackInfo<v8::Value>& args) {
|
| + if (!message_filter_)
|
| + message_filter_ = new AutomationMessageFilter(this);
|
| +}
|
| +
|
| void AutomationInternalCustomBindings::GetSchemaAdditions(
|
| const v8::FunctionCallbackInfo<v8::Value>& args) {
|
| v8::Local<v8::Object> additions = v8::Object::New(GetIsolate());
|
| @@ -158,9 +186,402 @@ void AutomationInternalCustomBindings::GetSchemaAdditions(
|
| args.GetReturnValue().Set(additions);
|
| }
|
|
|
| +void AutomationInternalCustomBindings::DestroyAccessibilityTree(
|
| + const v8::FunctionCallbackInfo<v8::Value>& args) {
|
| + if (args.Length() != 1)
|
| + return;
|
| + if (!args[0]->IsNumber())
|
| + return;
|
| +
|
| + int tree_id = args[0]->Int32Value();
|
| + auto iter = tree_id_to_tree_cache_map_.find(tree_id);
|
| + if (iter == tree_id_to_tree_cache_map_.end())
|
| + return;
|
| +
|
| + TreeCache* cache = iter->second;
|
| + tree_id_to_tree_cache_map_.erase(tree_id);
|
| + axtree_to_tree_cache_map_.erase(&cache->tree);
|
| + delete cache;
|
| +}
|
| +
|
| +void AutomationInternalCustomBindings::GetRootID(
|
| + const v8::FunctionCallbackInfo<v8::Value>& args) {
|
| + if (args.Length() != 1)
|
| + return;
|
| + if (!args[0]->IsNumber())
|
| + return;
|
| +
|
| + int tree_id = args[0]->Int32Value();
|
| + const auto& iter = tree_id_to_tree_cache_map_.find(tree_id);
|
| + if (iter == tree_id_to_tree_cache_map_.end())
|
| + return;
|
| +
|
| + TreeCache* cache = iter->second;
|
| + int root_id = cache->tree.root()->id();
|
| + args.GetReturnValue().Set(v8::Integer::New(GetIsolate(), root_id));
|
| +}
|
| +
|
| +bool AutomationInternalCustomBindings::GetNodeHelper(
|
| + const v8::FunctionCallbackInfo<v8::Value>& args,
|
| + TreeCache** out_cache,
|
| + ui::AXNode** out_node) {
|
| + if (args.Length() < 2)
|
| + return false;
|
| + if (!args[0]->IsNumber() || !args[1]->IsNumber())
|
| + return false;
|
| +
|
| + int tree_id = args[0]->Int32Value();
|
| + int node_id = args[1]->Int32Value();
|
| +
|
| + const auto& iter = tree_id_to_tree_cache_map_.find(tree_id);
|
| + if (iter == tree_id_to_tree_cache_map_.end())
|
| + return false;
|
| +
|
| + TreeCache* cache = iter->second;
|
| + ui::AXNode* node = cache->tree.GetFromId(node_id);
|
| +
|
| + if (out_cache)
|
| + *out_cache = cache;
|
| + if (out_node)
|
| + *out_node = node;
|
| +
|
| + return node != nullptr;
|
| +}
|
| +
|
| +void AutomationInternalCustomBindings::SetReturnValueFromBaseValue(
|
| + const v8::FunctionCallbackInfo<v8::Value>& args,
|
| + base::Value* value) {
|
| + v8::Isolate* isolate = context()->isolate();
|
| + v8::HandleScope handle_scope(isolate);
|
| + v8::Context::Scope context_scope(context()->v8_context());
|
| + scoped_ptr<content::V8ValueConverter> converter(
|
| + content::V8ValueConverter::create());
|
| + v8::Local<v8::Value> v8_value =
|
| + converter->ToV8Value(value, context()->v8_context());
|
| + args.GetReturnValue().Set(v8_value);
|
| +}
|
| +
|
| +void AutomationInternalCustomBindings::GetParentID(
|
| + const v8::FunctionCallbackInfo<v8::Value>& args) {
|
| + ui::AXNode* node;
|
| + if (!GetNodeHelper(args, nullptr, &node))
|
| + return;
|
| +
|
| + if (!node->parent())
|
| + return;
|
| +
|
| + int parent_id = node->parent()->id();
|
| + args.GetReturnValue().Set(v8::Integer::New(GetIsolate(), parent_id));
|
| +}
|
| +
|
| +void AutomationInternalCustomBindings::GetChildCount(
|
| + const v8::FunctionCallbackInfo<v8::Value>& args) {
|
| + ui::AXNode* node;
|
| + if (!GetNodeHelper(args, nullptr, &node))
|
| + return;
|
| +
|
| + int child_count = node->child_count();
|
| + args.GetReturnValue().Set(v8::Integer::New(GetIsolate(), child_count));
|
| +}
|
| +
|
| +void AutomationInternalCustomBindings::GetChildIDAtIndex(
|
| + const v8::FunctionCallbackInfo<v8::Value>& args) {
|
| + ui::AXNode* node;
|
| + if (!GetNodeHelper(args, nullptr, &node))
|
| + return;
|
| +
|
| + if (args.Length() < 3 || !args[2]->IsNumber())
|
| + return;
|
| +
|
| + int index = args[2]->Int32Value();
|
| + if (index < 0 || index >= node->child_count())
|
| + return;
|
| +
|
| + int child_id = node->children()[index]->id();
|
| + args.GetReturnValue().Set(v8::Integer::New(GetIsolate(), child_id));
|
| +}
|
| +
|
| +void AutomationInternalCustomBindings::GetIndexInParent(
|
| + const v8::FunctionCallbackInfo<v8::Value>& args) {
|
| + ui::AXNode* node;
|
| + if (!GetNodeHelper(args, nullptr, &node))
|
| + return;
|
| +
|
| + int index_in_parent = node->index_in_parent();
|
| + args.GetReturnValue().Set(v8::Integer::New(GetIsolate(), index_in_parent));
|
| +}
|
| +
|
| +void AutomationInternalCustomBindings::GetState(
|
| + const v8::FunctionCallbackInfo<v8::Value>& args) {
|
| + ui::AXNode* node;
|
| + if (!GetNodeHelper(args, nullptr, &node))
|
| + return;
|
| +
|
| + scoped_ptr<base::DictionaryValue> state_dict(new base::DictionaryValue());
|
| + uint32 state_pos = 0, state_shifter = node->data().state;
|
| + while (state_shifter) {
|
| + if (state_shifter & 1) {
|
| + state_dict->SetBoolean(
|
| + ToString(static_cast<ui::AXState>(state_pos)), true);
|
| + }
|
| + state_shifter = state_shifter >> 1;
|
| + state_pos++;
|
| + }
|
| +
|
| + SetReturnValueFromBaseValue(args, state_dict.get());
|
| +}
|
| +
|
| +void AutomationInternalCustomBindings::GetRole(
|
| + const v8::FunctionCallbackInfo<v8::Value>& args) {
|
| + ui::AXNode* node;
|
| + if (!GetNodeHelper(args, nullptr, &node))
|
| + return;
|
| +
|
| + std::string role_name = ui::ToString(node->data().role);
|
| + args.GetReturnValue().Set(
|
| + v8::String::NewFromUtf8(GetIsolate(), role_name.c_str()));
|
| +}
|
| +
|
| +void AutomationInternalCustomBindings::GetLocation(
|
| + const v8::FunctionCallbackInfo<v8::Value>& args) {
|
| + TreeCache* cache;
|
| + ui::AXNode* node;
|
| + if (!GetNodeHelper(args, &cache, &node))
|
| + return;
|
| +
|
| + scoped_ptr<base::DictionaryValue> location_dict(new base::DictionaryValue());
|
| + gfx::Rect location = node->data().location;
|
| + location.Offset(cache->location_offset);
|
| + location_dict->SetInteger("left", location.x());
|
| + location_dict->SetInteger("top", location.y());
|
| + location_dict->SetInteger("width", location.width());
|
| + location_dict->SetInteger("height", location.height());
|
| +
|
| + SetReturnValueFromBaseValue(args, location_dict.get());
|
| +}
|
| +
|
| +bool AutomationInternalCustomBindings::GetAttributeHelper(
|
| + const v8::FunctionCallbackInfo<v8::Value>& args,
|
| + ui::AXNode** out_node,
|
| + std::string* out_attribute_name) {
|
| + if (args.Length() != 3)
|
| + return false;
|
| + if (!args[0]->IsNumber() ||
|
| + !args[1]->IsNumber() ||
|
| + !args[2]->IsString()) {
|
| + return false;
|
| + }
|
| +
|
| + int tree_id = args[0]->Int32Value();
|
| + int node_id = args[1]->Int32Value();
|
| + *out_attribute_name = *v8::String::Utf8Value(args[2]);
|
| +
|
| + const auto& iter = tree_id_to_tree_cache_map_.find(tree_id);
|
| + if (iter == tree_id_to_tree_cache_map_.end())
|
| + return false;
|
| +
|
| + TreeCache* cache = iter->second;
|
| + *out_node = cache->tree.GetFromId(node_id);
|
| + if (!*out_node)
|
| + return false;
|
| +
|
| + return true;
|
| +}
|
| +
|
| +void AutomationInternalCustomBindings::GetStringAttribute(
|
| + const v8::FunctionCallbackInfo<v8::Value>& args) {
|
| + ui::AXNode* node;
|
| + std::string attribute_name;
|
| + if (!GetAttributeHelper(args, &node, &attribute_name))
|
| + return;
|
| +
|
| + ui::AXStringAttribute attribute = ui::ParseAXStringAttribute(attribute_name);
|
| + std::string attr_value;
|
| + if (!node->data().GetStringAttribute(attribute, &attr_value))
|
| + return;
|
| +
|
| + args.GetReturnValue().Set(
|
| + v8::String::NewFromUtf8(GetIsolate(), attr_value.c_str()));
|
| +}
|
| +
|
| +void AutomationInternalCustomBindings::GetBoolAttribute(
|
| + const v8::FunctionCallbackInfo<v8::Value>& args) {
|
| + ui::AXNode* node;
|
| + std::string attribute_name;
|
| + if (!GetAttributeHelper(args, &node, &attribute_name))
|
| + return;
|
| +
|
| + ui::AXBoolAttribute attribute = ui::ParseAXBoolAttribute(attribute_name);
|
| + bool attr_value;
|
| + if (!node->data().GetBoolAttribute(attribute, &attr_value))
|
| + return;
|
| +
|
| + args.GetReturnValue().Set(v8::Boolean::New(GetIsolate(), attr_value));
|
| +}
|
| +
|
| +void AutomationInternalCustomBindings::GetIntAttribute(
|
| + const v8::FunctionCallbackInfo<v8::Value>& args) {
|
| + ui::AXNode* node;
|
| + std::string attribute_name;
|
| + if (!GetAttributeHelper(args, &node, &attribute_name))
|
| + return;
|
| +
|
| + ui::AXIntAttribute attribute = ui::ParseAXIntAttribute(attribute_name);
|
| + int attr_value;
|
| + if (!node->data().GetIntAttribute(attribute, &attr_value))
|
| + return;
|
| +
|
| + args.GetReturnValue().Set(v8::Integer::New(GetIsolate(), attr_value));
|
| +}
|
| +
|
| +void AutomationInternalCustomBindings::GetFloatAttribute(
|
| + const v8::FunctionCallbackInfo<v8::Value>& args) {
|
| + ui::AXNode* node;
|
| + std::string attribute_name;
|
| + if (!GetAttributeHelper(args, &node, &attribute_name))
|
| + return;
|
| +
|
| + ui::AXFloatAttribute attribute = ui::ParseAXFloatAttribute(attribute_name);
|
| + float attr_value;
|
| +
|
| + if (!node->data().GetFloatAttribute(attribute, &attr_value))
|
| + return;
|
| +
|
| + args.GetReturnValue().Set(v8::Number::New(GetIsolate(), attr_value));
|
| +}
|
| +
|
| +void AutomationInternalCustomBindings::GetIntListAttribute(
|
| + const v8::FunctionCallbackInfo<v8::Value>& args) {
|
| + ui::AXNode* node;
|
| + std::string attribute_name;
|
| + if (!GetAttributeHelper(args, &node, &attribute_name))
|
| + return;
|
| +
|
| + ui::AXIntListAttribute attribute =
|
| + ui::ParseAXIntListAttribute(attribute_name);
|
| + if (!node->data().HasIntListAttribute(attribute))
|
| + return;
|
| + const std::vector<int32>& attr_value =
|
| + node->data().GetIntListAttribute(attribute);
|
| +
|
| + scoped_ptr<base::ListValue> list(new base::ListValue());
|
| + for (int32 i : attr_value)
|
| + list->AppendInteger(i);
|
| + SetReturnValueFromBaseValue(args, list.get());
|
| +}
|
| +
|
| void AutomationInternalCustomBindings::OnAccessibilityEvent(
|
| const ExtensionMsg_AccessibilityEventParams& params) {
|
| - // TODO(dmazzoni): finish implementing this.
|
| + int tree_id = params.tree_id;
|
| + TreeCache* cache;
|
| + auto iter = tree_id_to_tree_cache_map_.find(tree_id);
|
| + if (iter == tree_id_to_tree_cache_map_.end()) {
|
| + cache = new TreeCache();
|
| + cache->tab_id = -1;
|
| + cache->tree_id = params.tree_id;
|
| + cache->tree.SetDelegate(this);
|
| + tree_id_to_tree_cache_map_.insert(std::make_pair(tree_id, cache));
|
| + axtree_to_tree_cache_map_.insert(std::make_pair(&cache->tree, cache));
|
| + } else {
|
| + cache = iter->second;
|
| + }
|
| +
|
| + cache->location_offset = params.location_offset;
|
| + if (!cache->tree.Unserialize(params.update)) {
|
| + LOG(FATAL) << cache->tree.error();
|
| + return;
|
| + }
|
| +
|
| + api::automation_internal::AXEventParams event_params;
|
| + event_params.tree_id = params.tree_id;
|
| + event_params.target_id = params.id;
|
| + event_params.event_type = ToString(params.event_type);
|
| + scoped_ptr<base::ListValue> args(new base::ListValue());
|
| + args->Append(event_params.ToValue());
|
| +
|
| + v8::Isolate* isolate = context()->isolate();
|
| + v8::HandleScope handle_scope(isolate);
|
| + v8::Context::Scope context_scope(context()->v8_context());
|
| + scoped_ptr<content::V8ValueConverter> converter(
|
| + content::V8ValueConverter::create());
|
| + v8::Local<v8::Value> v8_args =
|
| + converter->ToV8Value(args.get(), context()->v8_context());
|
| +
|
| + CHECK(v8_args->IsArray());
|
| + v8::Local<v8::Array> v8_args_array = v8_args.As<v8::Array>();
|
| + context()->DispatchEvent("automationInternal.onAccessibilityEvent",
|
| + v8_args_array);
|
| +}
|
| +
|
| +void AutomationInternalCustomBindings::OnNodeWillBeDeleted(ui::AXTree* tree,
|
| + ui::AXNode* node) {
|
| + SendTreeChangeEvent("nodeRemoved", tree, node);
|
| +}
|
| +
|
| +void AutomationInternalCustomBindings::OnSubtreeWillBeDeleted(
|
| + ui::AXTree* tree,
|
| + ui::AXNode* node) {
|
| +}
|
| +
|
| +void AutomationInternalCustomBindings::OnNodeCreated(ui::AXTree* tree,
|
| + ui::AXNode* node) {
|
| +
|
| +}
|
| +
|
| +void AutomationInternalCustomBindings::OnNodeChanged(ui::AXTree* tree,
|
| + ui::AXNode* node) {
|
| +}
|
| +
|
| +void AutomationInternalCustomBindings::OnAtomicUpdateFinished(
|
| + ui::AXTree* tree,
|
| + bool root_changed,
|
| + const std::vector<ui::AXTreeDelegate::Change>& changes) {
|
| + auto iter = axtree_to_tree_cache_map_.find(tree);
|
| + if (iter == axtree_to_tree_cache_map_.end())
|
| + return;
|
| +
|
| + for (auto change : changes) {
|
| + ui::AXNode* node = change.node;
|
| + switch (change.type) {
|
| + case NODE_CREATED:
|
| + SendTreeChangeEvent("nodeCreated", tree, node);
|
| + break;
|
| + case SUBTREE_CREATED:
|
| + SendTreeChangeEvent("subtreeCreated", tree, node);
|
| + break;
|
| + case NODE_CHANGED:
|
| + SendTreeChangeEvent("nodeChanged", tree, node);
|
| + break;
|
| + }
|
| + }
|
| +}
|
| +
|
| +void AutomationInternalCustomBindings::SendTreeChangeEvent(
|
| + const char *change_type,
|
| + ui::AXTree* tree,
|
| + ui::AXNode* node) {
|
| + auto iter = axtree_to_tree_cache_map_.find(tree);
|
| + if (iter == axtree_to_tree_cache_map_.end())
|
| + return;
|
| +
|
| + int tree_id = iter->second->tree_id;
|
| + scoped_ptr<base::ListValue> args(new base::ListValue());
|
| + args->AppendInteger(tree_id);
|
| + args->AppendInteger(node->id());
|
| + args->AppendString(change_type);
|
| +
|
| + v8::Isolate* isolate = context()->isolate();
|
| + v8::HandleScope handle_scope(isolate);
|
| + v8::Context::Scope context_scope(context()->v8_context());
|
| + scoped_ptr<content::V8ValueConverter> converter(
|
| + content::V8ValueConverter::create());
|
| + v8::Local<v8::Value> v8_args =
|
| + converter->ToV8Value(args.get(), context()->v8_context());
|
| +
|
| + CHECK(v8_args->IsArray());
|
| + v8::Local<v8::Array> v8_args_array = v8_args.As<v8::Array>();
|
| + context()->DispatchEvent("automationInternal.onTreeChange",
|
| + v8_args_array);
|
| }
|
|
|
| } // namespace extensions
|
|
|