OLD | NEW |
---|---|
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/renderer/extensions/automation_internal_custom_bindings.h" | 5 #include "chrome/renderer/extensions/automation_internal_custom_bindings.h" |
6 | 6 |
7 #include "base/bind.h" | 7 #include "base/bind.h" |
8 #include "base/memory/scoped_ptr.h" | 8 #include "base/memory/scoped_ptr.h" |
9 #include "base/values.h" | 9 #include "base/values.h" |
10 #include "chrome/common/extensions/chrome_extension_messages.h" | 10 #include "chrome/common/extensions/chrome_extension_messages.h" |
(...skipping 21 matching lines...) Expand all Loading... | |
32 isolate, ui::ToString(static_cast<EnumType>(i)).c_str()); | 32 isolate, ui::ToString(static_cast<EnumType>(i)).c_str()); |
33 object->Set(value, value); | 33 object->Set(value, value); |
34 } | 34 } |
35 return object; | 35 return object; |
36 } | 36 } |
37 | 37 |
38 } // namespace | 38 } // namespace |
39 | 39 |
40 namespace extensions { | 40 namespace extensions { |
41 | 41 |
42 TreeCache::TreeCache() {} | |
43 TreeCache::~TreeCache() {} | |
44 | |
42 class AutomationMessageFilter : public IPC::MessageFilter { | 45 class AutomationMessageFilter : public IPC::MessageFilter { |
43 public: | 46 public: |
44 explicit AutomationMessageFilter(AutomationInternalCustomBindings* owner) | 47 explicit AutomationMessageFilter(AutomationInternalCustomBindings* owner) |
45 : owner_(owner), | 48 : owner_(owner), |
46 removed_(false) { | 49 removed_(false) { |
47 DCHECK(owner); | 50 DCHECK(owner); |
48 content::RenderThread::Get()->AddFilter(this); | 51 content::RenderThread::Get()->AddFilter(this); |
52 task_runner_ = content::RenderThread::Get()->GetTaskRunner(); | |
49 } | 53 } |
50 | 54 |
51 void Detach() { | 55 void Detach() { |
52 owner_ = nullptr; | 56 owner_ = nullptr; |
53 Remove(); | 57 Remove(); |
54 } | 58 } |
55 | 59 |
56 // IPC::MessageFilter | 60 // IPC::MessageFilter |
57 bool OnMessageReceived(const IPC::Message& message) override { | 61 bool OnMessageReceived(const IPC::Message& message) override { |
58 if (owner_) | 62 task_runner_->PostTask( |
59 return owner_->OnMessageReceived(message); | 63 FROM_HERE, |
60 else | 64 base::Bind( |
61 return false; | 65 &AutomationMessageFilter::OnMessageReceivedOnRenderThread, |
66 this, message)); | |
67 return false; | |
62 } | 68 } |
63 | 69 |
64 void OnFilterRemoved() override { | 70 void OnFilterRemoved() override { |
65 removed_ = true; | 71 removed_ = true; |
66 } | 72 } |
67 | 73 |
68 private: | 74 private: |
75 void OnMessageReceivedOnRenderThread(const IPC::Message& message) { | |
76 if (owner_) | |
77 owner_->OnMessageReceived(message); | |
78 } | |
79 | |
69 ~AutomationMessageFilter() override { | 80 ~AutomationMessageFilter() override { |
70 Remove(); | 81 Remove(); |
71 } | 82 } |
72 | 83 |
73 void Remove() { | 84 void Remove() { |
74 if (!removed_) { | 85 if (!removed_) { |
75 removed_ = true; | 86 removed_ = true; |
76 content::RenderThread::Get()->RemoveFilter(this); | 87 content::RenderThread::Get()->RemoveFilter(this); |
77 } | 88 } |
78 } | 89 } |
79 | 90 |
80 AutomationInternalCustomBindings* owner_; | 91 AutomationInternalCustomBindings* owner_; |
81 bool removed_; | 92 bool removed_; |
93 scoped_refptr<base::SingleThreadTaskRunner> task_runner_; | |
82 | 94 |
83 DISALLOW_COPY_AND_ASSIGN(AutomationMessageFilter); | 95 DISALLOW_COPY_AND_ASSIGN(AutomationMessageFilter); |
84 }; | 96 }; |
85 | 97 |
86 AutomationInternalCustomBindings::AutomationInternalCustomBindings( | 98 AutomationInternalCustomBindings::AutomationInternalCustomBindings( |
87 ScriptContext* context) : ObjectBackedNativeHandler(context) { | 99 ScriptContext* context) : ObjectBackedNativeHandler(context) { |
88 // It's safe to use base::Unretained(this) here because these bindings | 100 // It's safe to use base::Unretained(this) here because these bindings |
89 // will only be called on a valid AutomationInternalCustomBindings instance | 101 // will only be called on a valid AutomationInternalCustomBindings instance |
90 // and none of the functions have any side effects. | 102 // and none of the functions have any side effects. |
91 RouteFunction( | 103 #define ROUTE_FUNCTION(FN) \ |
92 "IsInteractPermitted", | 104 RouteFunction(#FN, \ |
93 base::Bind(&AutomationInternalCustomBindings::IsInteractPermitted, | 105 base::Bind(&AutomationInternalCustomBindings::FN, \ |
94 base::Unretained(this))); | 106 base::Unretained(this))) |
95 RouteFunction( | |
96 "GetSchemaAdditions", | |
97 base::Bind(&AutomationInternalCustomBindings::GetSchemaAdditions, | |
98 base::Unretained(this))); | |
99 RouteFunction( | |
100 "GetRoutingID", | |
101 base::Bind(&AutomationInternalCustomBindings::GetRoutingID, | |
102 base::Unretained(this))); | |
103 | 107 |
104 message_filter_ = new AutomationMessageFilter(this); | 108 ROUTE_FUNCTION(IsInteractPermitted); |
109 ROUTE_FUNCTION(GetSchemaAdditions); | |
110 ROUTE_FUNCTION(GetRoutingID); | |
111 ROUTE_FUNCTION(StartCachingAccessibilityTrees); | |
112 ROUTE_FUNCTION(DestroyAccessibilityTree); | |
113 ROUTE_FUNCTION(GetRootID); | |
114 ROUTE_FUNCTION(GetParentID); | |
115 ROUTE_FUNCTION(GetChildCount); | |
116 ROUTE_FUNCTION(GetChildIDAtIndex); | |
117 ROUTE_FUNCTION(GetIndexInParent); | |
118 ROUTE_FUNCTION(GetState); | |
119 ROUTE_FUNCTION(GetRole); | |
120 ROUTE_FUNCTION(GetLocation); | |
121 ROUTE_FUNCTION(GetStringAttribute); | |
122 ROUTE_FUNCTION(GetBoolAttribute); | |
123 ROUTE_FUNCTION(GetIntAttribute); | |
124 ROUTE_FUNCTION(GetFloatAttribute); | |
125 ROUTE_FUNCTION(GetIntListAttribute); | |
105 } | 126 } |
dcheng
2015/06/15 21:36:55
#undef ROUTE_FUNCTION
dmazzoni
2015/06/16 18:19:48
Done.
| |
106 | 127 |
107 AutomationInternalCustomBindings::~AutomationInternalCustomBindings() { | 128 AutomationInternalCustomBindings::~AutomationInternalCustomBindings() { |
108 message_filter_->Detach(); | 129 if (message_filter_) |
130 message_filter_->Detach(); | |
109 } | 131 } |
dcheng
2015/06/15 21:36:55
Do we need to delete TreeCache pointers when this
dmazzoni
2015/06/16 18:19:49
Done.
| |
110 | 132 |
111 bool AutomationInternalCustomBindings::OnMessageReceived( | 133 bool AutomationInternalCustomBindings::OnMessageReceived( |
112 const IPC::Message& message) { | 134 const IPC::Message& message) { |
113 IPC_BEGIN_MESSAGE_MAP(AutomationInternalCustomBindings, message) | 135 IPC_BEGIN_MESSAGE_MAP(AutomationInternalCustomBindings, message) |
114 IPC_MESSAGE_HANDLER(ExtensionMsg_AccessibilityEvent, OnAccessibilityEvent) | 136 IPC_MESSAGE_HANDLER(ExtensionMsg_AccessibilityEvent, OnAccessibilityEvent) |
115 IPC_END_MESSAGE_MAP() | 137 IPC_END_MESSAGE_MAP() |
116 | 138 |
117 // Always return false in case there are multiple | 139 // Always return false in case there are multiple |
118 // AutomationInternalCustomBindings instances attached to the same thread. | 140 // AutomationInternalCustomBindings instances attached to the same thread. |
119 return false; | 141 return false; |
120 } | 142 } |
121 | 143 |
122 void AutomationInternalCustomBindings::IsInteractPermitted( | 144 void AutomationInternalCustomBindings::IsInteractPermitted( |
123 const v8::FunctionCallbackInfo<v8::Value>& args) { | 145 const v8::FunctionCallbackInfo<v8::Value>& args) { |
124 const Extension* extension = context()->extension(); | 146 const Extension* extension = context()->extension(); |
125 CHECK(extension); | 147 CHECK(extension); |
126 const AutomationInfo* automation_info = AutomationInfo::Get(extension); | 148 const AutomationInfo* automation_info = AutomationInfo::Get(extension); |
127 CHECK(automation_info); | 149 CHECK(automation_info); |
128 args.GetReturnValue().Set( | 150 args.GetReturnValue().Set( |
129 v8::Boolean::New(GetIsolate(), automation_info->interact)); | 151 v8::Boolean::New(GetIsolate(), automation_info->interact)); |
130 } | 152 } |
131 | 153 |
132 void AutomationInternalCustomBindings::GetRoutingID( | 154 void AutomationInternalCustomBindings::GetRoutingID( |
133 const v8::FunctionCallbackInfo<v8::Value>& args) { | 155 const v8::FunctionCallbackInfo<v8::Value>& args) { |
134 int routing_id = context()->GetRenderView()->GetRoutingID(); | 156 int routing_id = context()->GetRenderView()->GetRoutingID(); |
135 args.GetReturnValue().Set(v8::Integer::New(GetIsolate(), routing_id)); | 157 args.GetReturnValue().Set(v8::Integer::New(GetIsolate(), routing_id)); |
136 } | 158 } |
137 | 159 |
160 void AutomationInternalCustomBindings::StartCachingAccessibilityTrees( | |
161 const v8::FunctionCallbackInfo<v8::Value>& args) { | |
162 if (!message_filter_) | |
163 message_filter_ = new AutomationMessageFilter(this); | |
164 } | |
165 | |
138 void AutomationInternalCustomBindings::GetSchemaAdditions( | 166 void AutomationInternalCustomBindings::GetSchemaAdditions( |
139 const v8::FunctionCallbackInfo<v8::Value>& args) { | 167 const v8::FunctionCallbackInfo<v8::Value>& args) { |
140 v8::Local<v8::Object> additions = v8::Object::New(GetIsolate()); | 168 v8::Local<v8::Object> additions = v8::Object::New(GetIsolate()); |
141 | 169 |
142 additions->Set( | 170 additions->Set( |
143 v8::String::NewFromUtf8(GetIsolate(), "EventType"), | 171 v8::String::NewFromUtf8(GetIsolate(), "EventType"), |
144 ToEnumObject(GetIsolate(), ui::AX_EVENT_NONE, ui::AX_EVENT_LAST)); | 172 ToEnumObject(GetIsolate(), ui::AX_EVENT_NONE, ui::AX_EVENT_LAST)); |
145 | 173 |
146 additions->Set( | 174 additions->Set( |
147 v8::String::NewFromUtf8(GetIsolate(), "RoleType"), | 175 v8::String::NewFromUtf8(GetIsolate(), "RoleType"), |
148 ToEnumObject(GetIsolate(), ui::AX_ROLE_NONE, ui::AX_ROLE_LAST)); | 176 ToEnumObject(GetIsolate(), ui::AX_ROLE_NONE, ui::AX_ROLE_LAST)); |
149 | 177 |
150 additions->Set( | 178 additions->Set( |
151 v8::String::NewFromUtf8(GetIsolate(), "StateType"), | 179 v8::String::NewFromUtf8(GetIsolate(), "StateType"), |
152 ToEnumObject(GetIsolate(), ui::AX_STATE_NONE, ui::AX_STATE_LAST)); | 180 ToEnumObject(GetIsolate(), ui::AX_STATE_NONE, ui::AX_STATE_LAST)); |
153 | 181 |
154 additions->Set( | 182 additions->Set( |
155 v8::String::NewFromUtf8(GetIsolate(), "TreeChangeType"), | 183 v8::String::NewFromUtf8(GetIsolate(), "TreeChangeType"), |
156 ToEnumObject(GetIsolate(), ui::AX_MUTATION_NONE, ui::AX_MUTATION_LAST)); | 184 ToEnumObject(GetIsolate(), ui::AX_MUTATION_NONE, ui::AX_MUTATION_LAST)); |
157 | 185 |
158 args.GetReturnValue().Set(additions); | 186 args.GetReturnValue().Set(additions); |
159 } | 187 } |
160 | 188 |
189 void AutomationInternalCustomBindings::DestroyAccessibilityTree( | |
David Tseng
2015/06/15 21:02:11
nit: can combine with above
| |
190 const v8::FunctionCallbackInfo<v8::Value>& args) { | |
191 if (args.Length() != 1 || !args[0]->IsNumber()) { | |
192 ThrowInvalidArgumentsException(args); | |
193 return; | |
194 } | |
195 | |
196 int tree_id = args[0]->Int32Value(); | |
197 auto iter = tree_id_to_tree_cache_map_.find(tree_id); | |
198 if (iter == tree_id_to_tree_cache_map_.end()) | |
199 return; | |
200 | |
201 TreeCache* cache = iter->second; | |
202 tree_id_to_tree_cache_map_.erase(tree_id); | |
203 axtree_to_tree_cache_map_.erase(&cache->tree); | |
204 delete cache; | |
205 } | |
206 | |
207 // | |
208 // Access the cached accessibility trees and properties of their nodes. | |
209 // | |
210 | |
211 void AutomationInternalCustomBindings::GetRootID( | |
212 const v8::FunctionCallbackInfo<v8::Value>& args) { | |
213 if (args.Length() != 1 || !args[0]->IsNumber()) { | |
214 ThrowInvalidArgumentsException(args); | |
215 return; | |
216 } | |
217 | |
218 int tree_id = args[0]->Int32Value(); | |
219 const auto& iter = tree_id_to_tree_cache_map_.find(tree_id); | |
220 if (iter == tree_id_to_tree_cache_map_.end()) | |
221 return; | |
222 | |
223 TreeCache* cache = iter->second; | |
224 int root_id = cache->tree.root()->id(); | |
225 args.GetReturnValue().Set(v8::Integer::New(GetIsolate(), root_id)); | |
226 } | |
227 | |
228 void AutomationInternalCustomBindings::GetParentID( | |
229 const v8::FunctionCallbackInfo<v8::Value>& args) { | |
230 ui::AXNode* node; | |
231 if (!GetNodeHelper(args, nullptr, &node)) | |
232 return; | |
233 | |
234 if (!node->parent()) | |
235 return; | |
236 | |
237 int parent_id = node->parent()->id(); | |
238 args.GetReturnValue().Set(v8::Integer::New(GetIsolate(), parent_id)); | |
239 } | |
240 | |
241 void AutomationInternalCustomBindings::GetChildCount( | |
242 const v8::FunctionCallbackInfo<v8::Value>& args) { | |
243 ui::AXNode* node; | |
244 if (!GetNodeHelper(args, nullptr, &node)) | |
245 return; | |
246 | |
247 int child_count = node->child_count(); | |
248 args.GetReturnValue().Set(v8::Integer::New(GetIsolate(), child_count)); | |
249 } | |
250 | |
251 void AutomationInternalCustomBindings::GetChildIDAtIndex( | |
252 const v8::FunctionCallbackInfo<v8::Value>& args) { | |
253 if (args.Length() < 3 || !args[2]->IsNumber()) { | |
254 ThrowInvalidArgumentsException(args); | |
255 return; | |
256 } | |
257 | |
258 ui::AXNode* node; | |
259 if (!GetNodeHelper(args, nullptr, &node)) | |
260 return; | |
261 | |
262 int index = args[2]->Int32Value(); | |
263 if (index < 0 || index >= node->child_count()) | |
264 return; | |
265 | |
266 int child_id = node->children()[index]->id(); | |
267 args.GetReturnValue().Set(v8::Integer::New(GetIsolate(), child_id)); | |
268 } | |
269 | |
270 void AutomationInternalCustomBindings::GetIndexInParent( | |
271 const v8::FunctionCallbackInfo<v8::Value>& args) { | |
272 ui::AXNode* node; | |
273 if (!GetNodeHelper(args, nullptr, &node)) | |
274 return; | |
275 | |
276 int index_in_parent = node->index_in_parent(); | |
277 args.GetReturnValue().Set(v8::Integer::New(GetIsolate(), index_in_parent)); | |
278 } | |
279 | |
280 void AutomationInternalCustomBindings::GetState( | |
281 const v8::FunctionCallbackInfo<v8::Value>& args) { | |
282 ui::AXNode* node; | |
283 if (!GetNodeHelper(args, nullptr, &node)) | |
284 return; | |
285 | |
286 scoped_ptr<base::DictionaryValue> state_dict(new base::DictionaryValue()); | |
dcheng
2015/06/15 21:36:55
Just create this directly on the stack.
dmazzoni
2015/06/16 18:19:48
Done.
| |
287 uint32 state_pos = 0, state_shifter = node->data().state; | |
288 while (state_shifter) { | |
289 if (state_shifter & 1) { | |
290 state_dict->SetBoolean( | |
291 ToString(static_cast<ui::AXState>(state_pos)), true); | |
292 } | |
293 state_shifter = state_shifter >> 1; | |
294 state_pos++; | |
295 } | |
296 | |
297 SetReturnValueFromBaseValue(args, state_dict.get()); | |
298 } | |
299 | |
300 void AutomationInternalCustomBindings::GetRole( | |
301 const v8::FunctionCallbackInfo<v8::Value>& args) { | |
302 ui::AXNode* node; | |
303 if (!GetNodeHelper(args, nullptr, &node)) | |
304 return; | |
305 | |
306 std::string role_name = ui::ToString(node->data().role); | |
307 args.GetReturnValue().Set( | |
308 v8::String::NewFromUtf8(GetIsolate(), role_name.c_str())); | |
309 } | |
310 | |
311 void AutomationInternalCustomBindings::GetLocation( | |
312 const v8::FunctionCallbackInfo<v8::Value>& args) { | |
313 TreeCache* cache; | |
314 ui::AXNode* node; | |
315 if (!GetNodeHelper(args, &cache, &node)) | |
316 return; | |
317 | |
318 scoped_ptr<base::DictionaryValue> location_dict(new base::DictionaryValue()); | |
319 gfx::Rect location = node->data().location; | |
320 location.Offset(cache->location_offset); | |
321 location_dict->SetInteger("left", location.x()); | |
322 location_dict->SetInteger("top", location.y()); | |
323 location_dict->SetInteger("width", location.width()); | |
324 location_dict->SetInteger("height", location.height()); | |
325 | |
326 SetReturnValueFromBaseValue(args, location_dict.get()); | |
327 } | |
328 | |
329 void AutomationInternalCustomBindings::GetStringAttribute( | |
330 const v8::FunctionCallbackInfo<v8::Value>& args) { | |
331 ui::AXNode* node; | |
332 std::string attribute_name; | |
333 if (!GetAttributeHelper(args, &node, &attribute_name)) | |
334 return; | |
335 | |
336 ui::AXStringAttribute attribute = ui::ParseAXStringAttribute(attribute_name); | |
337 std::string attr_value; | |
338 if (!node->data().GetStringAttribute(attribute, &attr_value)) | |
339 return; | |
340 | |
341 args.GetReturnValue().Set( | |
342 v8::String::NewFromUtf8(GetIsolate(), attr_value.c_str())); | |
343 } | |
344 | |
345 void AutomationInternalCustomBindings::GetBoolAttribute( | |
346 const v8::FunctionCallbackInfo<v8::Value>& args) { | |
347 ui::AXNode* node; | |
348 std::string attribute_name; | |
349 if (!GetAttributeHelper(args, &node, &attribute_name)) | |
350 return; | |
351 | |
352 ui::AXBoolAttribute attribute = ui::ParseAXBoolAttribute(attribute_name); | |
353 bool attr_value; | |
354 if (!node->data().GetBoolAttribute(attribute, &attr_value)) | |
355 return; | |
356 | |
357 args.GetReturnValue().Set(v8::Boolean::New(GetIsolate(), attr_value)); | |
358 } | |
359 | |
360 void AutomationInternalCustomBindings::GetIntAttribute( | |
361 const v8::FunctionCallbackInfo<v8::Value>& args) { | |
362 ui::AXNode* node; | |
363 std::string attribute_name; | |
364 if (!GetAttributeHelper(args, &node, &attribute_name)) | |
365 return; | |
366 | |
367 ui::AXIntAttribute attribute = ui::ParseAXIntAttribute(attribute_name); | |
368 int attr_value; | |
369 if (!node->data().GetIntAttribute(attribute, &attr_value)) | |
370 return; | |
371 | |
372 args.GetReturnValue().Set(v8::Integer::New(GetIsolate(), attr_value)); | |
373 } | |
374 | |
375 void AutomationInternalCustomBindings::GetFloatAttribute( | |
376 const v8::FunctionCallbackInfo<v8::Value>& args) { | |
377 ui::AXNode* node; | |
378 std::string attribute_name; | |
379 if (!GetAttributeHelper(args, &node, &attribute_name)) | |
380 return; | |
381 | |
382 ui::AXFloatAttribute attribute = ui::ParseAXFloatAttribute(attribute_name); | |
383 float attr_value; | |
384 | |
385 if (!node->data().GetFloatAttribute(attribute, &attr_value)) | |
386 return; | |
387 | |
388 args.GetReturnValue().Set(v8::Number::New(GetIsolate(), attr_value)); | |
389 } | |
390 | |
391 void AutomationInternalCustomBindings::GetIntListAttribute( | |
392 const v8::FunctionCallbackInfo<v8::Value>& args) { | |
393 ui::AXNode* node; | |
394 std::string attribute_name; | |
395 if (!GetAttributeHelper(args, &node, &attribute_name)) | |
396 return; | |
397 | |
398 ui::AXIntListAttribute attribute = | |
399 ui::ParseAXIntListAttribute(attribute_name); | |
400 if (!node->data().HasIntListAttribute(attribute)) | |
401 return; | |
402 const std::vector<int32>& attr_value = | |
403 node->data().GetIntListAttribute(attribute); | |
404 | |
405 scoped_ptr<base::ListValue> list(new base::ListValue()); | |
dcheng
2015/06/15 21:36:55
Create this directly on the stack as well.
dmazzoni
2015/06/16 18:19:49
Done.
| |
406 for (int32 i : attr_value) | |
407 list->AppendInteger(i); | |
408 SetReturnValueFromBaseValue(args, list.get()); | |
409 } | |
410 | |
411 // | |
412 // Helper functions. | |
413 // | |
414 | |
415 void AutomationInternalCustomBindings::ThrowInvalidArgumentsException( | |
416 const v8::FunctionCallbackInfo<v8::Value>& args) { | |
417 args.GetIsolate()->ThrowException( | |
418 v8::String::NewFromUtf8( | |
419 args.GetIsolate(), | |
420 "Invalid arguments to AutomationInternalCustomBindings function", | |
421 v8::NewStringType::kNormal).ToLocalChecked()); | |
422 } | |
423 | |
424 bool AutomationInternalCustomBindings::GetNodeHelper( | |
425 const v8::FunctionCallbackInfo<v8::Value>& args, | |
426 TreeCache** out_cache, | |
427 ui::AXNode** out_node) { | |
428 if (args.Length() < 2 || !args[0]->IsNumber() || !args[1]->IsNumber()) { | |
429 ThrowInvalidArgumentsException(args); | |
430 return false; | |
431 } | |
432 | |
433 int tree_id = args[0]->Int32Value(); | |
434 int node_id = args[1]->Int32Value(); | |
435 | |
436 const auto& iter = tree_id_to_tree_cache_map_.find(tree_id); | |
437 if (iter == tree_id_to_tree_cache_map_.end()) | |
438 return false; | |
439 | |
440 TreeCache* cache = iter->second; | |
441 ui::AXNode* node = cache->tree.GetFromId(node_id); | |
442 | |
443 if (out_cache) | |
444 *out_cache = cache; | |
445 if (out_node) | |
446 *out_node = node; | |
447 | |
448 return node != nullptr; | |
449 } | |
450 | |
451 bool AutomationInternalCustomBindings::GetAttributeHelper( | |
452 const v8::FunctionCallbackInfo<v8::Value>& args, | |
453 ui::AXNode** out_node, | |
454 std::string* out_attribute_name) { | |
455 if (args.Length() != 3 || | |
456 !args[2]->IsString()) { | |
457 ThrowInvalidArgumentsException(args); | |
458 return false; | |
459 } | |
460 | |
461 TreeCache* cache = nullptr; | |
462 if (!GetNodeHelper(args, &cache, out_node)) | |
463 return false; | |
464 | |
465 *out_attribute_name = *v8::String::Utf8Value(args[2]); | |
466 return true; | |
467 } | |
468 | |
469 void AutomationInternalCustomBindings::SetReturnValueFromBaseValue( | |
470 const v8::FunctionCallbackInfo<v8::Value>& args, | |
471 base::Value* value) { | |
dcheng
2015/06/15 21:36:55
Pass this by const reference, since I don't think
dmazzoni
2015/06/16 18:19:49
Done.
| |
472 v8::Isolate* isolate = context()->isolate(); | |
dcheng
2015/06/15 21:36:55
Why do we use context()->isolate(), args.GetIsolat
dmazzoni
2015/06/16 18:19:48
Good idea. Switched to GetIsolate() throughout.
| |
473 v8::HandleScope handle_scope(isolate); | |
474 v8::Context::Scope context_scope(context()->v8_context()); | |
475 scoped_ptr<content::V8ValueConverter> converter( | |
dcheng
2015/06/15 21:36:55
Ditto: just create this on the stack.
dmazzoni
2015/06/16 18:19:48
Can't, there's no public constructor.
| |
476 content::V8ValueConverter::create()); | |
477 v8::Local<v8::Value> v8_value = | |
478 converter->ToV8Value(value, context()->v8_context()); | |
479 args.GetReturnValue().Set(v8_value); | |
480 } | |
481 | |
482 // | |
483 // Handle accessibility events from the browser process. | |
484 // | |
485 | |
161 void AutomationInternalCustomBindings::OnAccessibilityEvent( | 486 void AutomationInternalCustomBindings::OnAccessibilityEvent( |
162 const ExtensionMsg_AccessibilityEventParams& params) { | 487 const ExtensionMsg_AccessibilityEventParams& params) { |
163 // TODO(dmazzoni): finish implementing this. | 488 int tree_id = params.tree_id; |
489 TreeCache* cache; | |
490 auto iter = tree_id_to_tree_cache_map_.find(tree_id); | |
491 if (iter == tree_id_to_tree_cache_map_.end()) { | |
492 cache = new TreeCache(); | |
493 cache->tab_id = -1; | |
494 cache->tree_id = params.tree_id; | |
495 cache->tree.SetDelegate(this); | |
496 tree_id_to_tree_cache_map_.insert(std::make_pair(tree_id, cache)); | |
497 axtree_to_tree_cache_map_.insert(std::make_pair(&cache->tree, cache)); | |
498 } else { | |
499 cache = iter->second; | |
500 } | |
501 | |
502 cache->location_offset = params.location_offset; | |
503 if (!cache->tree.Unserialize(params.update)) { | |
504 LOG(FATAL) << cache->tree.error(); | |
505 return; | |
506 } | |
507 | |
508 api::automation_internal::AXEventParams event_params; | |
509 event_params.tree_id = params.tree_id; | |
510 event_params.target_id = params.id; | |
511 event_params.event_type = ToString(params.event_type); | |
512 scoped_ptr<base::ListValue> args(new base::ListValue()); | |
dcheng
2015/06/15 21:36:55
Create this on the stack.
dmazzoni
2015/06/16 18:19:48
Done.
| |
513 args->Append(event_params.ToValue()); | |
514 | |
515 v8::Isolate* isolate = context()->isolate(); | |
516 v8::HandleScope handle_scope(isolate); | |
517 v8::Context::Scope context_scope(context()->v8_context()); | |
518 scoped_ptr<content::V8ValueConverter> converter( | |
519 content::V8ValueConverter::create()); | |
520 v8::Local<v8::Value> v8_args = | |
521 converter->ToV8Value(args.get(), context()->v8_context()); | |
522 | |
523 CHECK(v8_args->IsArray()); | |
dcheng
2015/06/15 21:36:55
DCHECK? This should basically never be true.
dmazzoni
2015/06/16 18:19:49
Done.
| |
524 v8::Local<v8::Array> v8_args_array = v8_args.As<v8::Array>(); | |
525 context()->DispatchEvent("automationInternal.onAccessibilityEvent", | |
526 v8_args_array); | |
527 } | |
528 | |
529 void AutomationInternalCustomBindings::OnNodeWillBeDeleted(ui::AXTree* tree, | |
530 ui::AXNode* node) { | |
531 SendTreeChangeEvent( | |
532 api::automation::TREE_CHANGE_TYPE_NODEREMOVED, | |
533 tree, node); | |
534 } | |
535 | |
536 void AutomationInternalCustomBindings::OnSubtreeWillBeDeleted( | |
537 ui::AXTree* tree, | |
538 ui::AXNode* node) { | |
539 // This isn't strictly needed, as OnNodeWillBeDeleted will already be | |
540 // called. We could send a JS event for this only if it turns out to | |
541 // be needed for something. | |
542 } | |
543 | |
544 void AutomationInternalCustomBindings::OnNodeCreated(ui::AXTree* tree, | |
545 ui::AXNode* node) { | |
546 // Not needed, this is called in the middle of an update so it's not | |
547 // safe to trigger JS from here. Wait for the notification in | |
548 // OnAtomicUpdateFinished instead. | |
549 } | |
550 | |
551 void AutomationInternalCustomBindings::OnNodeChanged(ui::AXTree* tree, | |
552 ui::AXNode* node) { | |
553 // Not needed, this is called in the middle of an update so it's not | |
554 // safe to trigger JS from here. Wait for the notification in | |
555 // OnAtomicUpdateFinished instead. | |
556 } | |
557 | |
558 void AutomationInternalCustomBindings::OnAtomicUpdateFinished( | |
559 ui::AXTree* tree, | |
560 bool root_changed, | |
561 const std::vector<ui::AXTreeDelegate::Change>& changes) { | |
562 auto iter = axtree_to_tree_cache_map_.find(tree); | |
563 if (iter == axtree_to_tree_cache_map_.end()) | |
564 return; | |
565 | |
566 for (auto change : changes) { | |
567 ui::AXNode* node = change.node; | |
568 switch (change.type) { | |
569 case NODE_CREATED: | |
570 SendTreeChangeEvent( | |
571 api::automation::TREE_CHANGE_TYPE_NODECREATED, | |
572 tree, node); | |
573 break; | |
574 case SUBTREE_CREATED: | |
575 SendTreeChangeEvent( | |
576 api::automation::TREE_CHANGE_TYPE_SUBTREECREATED, | |
577 tree, node); | |
578 break; | |
579 case NODE_CHANGED: | |
580 SendTreeChangeEvent( | |
581 api::automation::TREE_CHANGE_TYPE_NODECHANGED, | |
582 tree, node); | |
583 break; | |
584 } | |
585 } | |
586 } | |
587 | |
588 void AutomationInternalCustomBindings::SendTreeChangeEvent( | |
589 api::automation::TreeChangeType change_type, | |
590 ui::AXTree* tree, | |
591 ui::AXNode* node) { | |
592 auto iter = axtree_to_tree_cache_map_.find(tree); | |
593 if (iter == axtree_to_tree_cache_map_.end()) | |
594 return; | |
595 | |
596 int tree_id = iter->second->tree_id; | |
597 scoped_ptr<base::ListValue> args(new base::ListValue()); | |
dcheng
2015/06/15 21:36:55
Create this on the stack.
dmazzoni
2015/06/16 18:19:48
Done.
| |
598 args->AppendInteger(tree_id); | |
599 args->AppendInteger(node->id()); | |
600 args->AppendString(ToString(change_type).c_str()); | |
601 | |
602 v8::Isolate* isolate = context()->isolate(); | |
603 v8::HandleScope handle_scope(isolate); | |
604 v8::Context::Scope context_scope(context()->v8_context()); | |
605 scoped_ptr<content::V8ValueConverter> converter( | |
606 content::V8ValueConverter::create()); | |
607 v8::Local<v8::Value> v8_args = | |
608 converter->ToV8Value(args.get(), context()->v8_context()); | |
609 | |
610 CHECK(v8_args->IsArray()); | |
dcheng
2015/06/15 21:36:55
Ditto: this should be DCHECK instead of CHECK>
dmazzoni
2015/06/16 18:19:48
Done.
| |
611 v8::Local<v8::Array> v8_args_array = v8_args.As<v8::Array>(); | |
612 context()->DispatchEvent("automationInternal.onTreeChange", | |
613 v8_args_array); | |
164 } | 614 } |
165 | 615 |
166 } // namespace extensions | 616 } // namespace extensions |
OLD | NEW |