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 DCHECK(owner); | 49 DCHECK(owner); |
47 content::RenderThread::Get()->AddFilter(this); | 50 content::RenderThread::Get()->AddFilter(this); |
51 task_runner_ = content::RenderThread::Get()->GetTaskRunner(); | |
48 } | 52 } |
49 | 53 |
50 void Detach() { | 54 void Detach() { |
51 owner_ = nullptr; | 55 owner_ = nullptr; |
52 } | 56 } |
53 | 57 |
54 // IPC::MessageFilter | 58 // IPC::MessageFilter |
55 bool OnMessageReceived(const IPC::Message& message) override { | 59 bool OnMessageReceived(const IPC::Message& message) override { |
56 if (owner_) | 60 task_runner_->PostTask( |
57 return owner_->OnMessageReceived(message); | 61 FROM_HERE, |
58 else | 62 base::Bind( |
59 return false; | 63 &AutomationMessageFilter::OnMessageReceivedOnRenderThread, |
64 this, message)); | |
65 return false; | |
60 } | 66 } |
61 | 67 |
62 private: | 68 private: |
69 void OnMessageReceivedOnRenderThread(const IPC::Message& message) { | |
70 if (owner_) | |
71 owner_->OnMessageReceived(message); | |
72 } | |
73 | |
63 ~AutomationMessageFilter() override { | 74 ~AutomationMessageFilter() override { |
64 content::RenderThread::Get()->RemoveFilter(this); | 75 content::RenderThread::Get()->RemoveFilter(this); |
65 } | 76 } |
66 | 77 |
67 AutomationInternalCustomBindings* owner_; | 78 AutomationInternalCustomBindings* owner_; |
79 scoped_refptr<base::SingleThreadTaskRunner> task_runner_; | |
68 | 80 |
69 DISALLOW_COPY_AND_ASSIGN(AutomationMessageFilter); | 81 DISALLOW_COPY_AND_ASSIGN(AutomationMessageFilter); |
70 }; | 82 }; |
71 | 83 |
72 AutomationInternalCustomBindings::AutomationInternalCustomBindings( | 84 AutomationInternalCustomBindings::AutomationInternalCustomBindings( |
73 ScriptContext* context) : ObjectBackedNativeHandler(context) { | 85 ScriptContext* context) : ObjectBackedNativeHandler(context) { |
74 // It's safe to use base::Unretained(this) here because these bindings | 86 // It's safe to use base::Unretained(this) here because these bindings |
75 // will only be called on a valid AutomationInternalCustomBindings instance | 87 // will only be called on a valid AutomationInternalCustomBindings instance |
76 // and none of the functions have any side effects. | 88 // and none of the functions have any side effects. |
77 RouteFunction( | 89 #define ROUTE_FUNCTION(FN) \ |
78 "IsInteractPermitted", | 90 RouteFunction(#FN, \ |
79 base::Bind(&AutomationInternalCustomBindings::IsInteractPermitted, | 91 base::Bind(&AutomationInternalCustomBindings::FN, \ |
80 base::Unretained(this))); | 92 base::Unretained(this))) |
81 RouteFunction( | 93 |
82 "GetSchemaAdditions", | 94 ROUTE_FUNCTION(IsInteractPermitted); |
83 base::Bind(&AutomationInternalCustomBindings::GetSchemaAdditions, | 95 ROUTE_FUNCTION(GetSchemaAdditions); |
84 base::Unretained(this))); | 96 ROUTE_FUNCTION(GetRoutingID); |
85 RouteFunction( | 97 ROUTE_FUNCTION(DestroyAccessibilityTree); |
86 "GetRoutingID", | 98 ROUTE_FUNCTION(GetRootID); |
87 base::Bind(&AutomationInternalCustomBindings::GetRoutingID, | 99 ROUTE_FUNCTION(GetParentID); |
88 base::Unretained(this))); | 100 ROUTE_FUNCTION(GetChildCount); |
101 ROUTE_FUNCTION(GetChildIDAtIndex); | |
102 ROUTE_FUNCTION(GetIndexInParent); | |
103 ROUTE_FUNCTION(GetState); | |
104 ROUTE_FUNCTION(GetRole); | |
105 ROUTE_FUNCTION(GetLocation); | |
106 ROUTE_FUNCTION(GetStringAttribute); | |
107 ROUTE_FUNCTION(GetBoolAttribute); | |
108 ROUTE_FUNCTION(GetIntAttribute); | |
109 ROUTE_FUNCTION(GetFloatAttribute); | |
110 ROUTE_FUNCTION(GetIntListAttribute); | |
89 | 111 |
90 message_filter_ = new AutomationMessageFilter(this); | 112 message_filter_ = new AutomationMessageFilter(this); |
91 } | 113 } |
92 | 114 |
93 AutomationInternalCustomBindings::~AutomationInternalCustomBindings() { | 115 AutomationInternalCustomBindings::~AutomationInternalCustomBindings() { |
94 message_filter_->Detach(); | 116 message_filter_->Detach(); |
95 } | 117 } |
96 | 118 |
97 bool AutomationInternalCustomBindings::OnMessageReceived( | 119 bool AutomationInternalCustomBindings::OnMessageReceived( |
98 const IPC::Message& message) { | 120 const IPC::Message& message) { |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
137 v8::String::NewFromUtf8(GetIsolate(), "StateType"), | 159 v8::String::NewFromUtf8(GetIsolate(), "StateType"), |
138 ToEnumObject(GetIsolate(), ui::AX_STATE_NONE, ui::AX_STATE_LAST)); | 160 ToEnumObject(GetIsolate(), ui::AX_STATE_NONE, ui::AX_STATE_LAST)); |
139 | 161 |
140 additions->Set( | 162 additions->Set( |
141 v8::String::NewFromUtf8(GetIsolate(), "TreeChangeType"), | 163 v8::String::NewFromUtf8(GetIsolate(), "TreeChangeType"), |
142 ToEnumObject(GetIsolate(), ui::AX_MUTATION_NONE, ui::AX_MUTATION_LAST)); | 164 ToEnumObject(GetIsolate(), ui::AX_MUTATION_NONE, ui::AX_MUTATION_LAST)); |
143 | 165 |
144 args.GetReturnValue().Set(additions); | 166 args.GetReturnValue().Set(additions); |
145 } | 167 } |
146 | 168 |
169 void AutomationInternalCustomBindings::DestroyAccessibilityTree( | |
170 const v8::FunctionCallbackInfo<v8::Value>& args) { | |
171 if (args.Length() != 1) | |
172 return; | |
173 if (!args[0]->IsNumber()) | |
174 return; | |
175 | |
176 int tree_id = args[0]->Int32Value(); | |
177 auto iter = tree_id_to_tree_cache_map_.find(tree_id); | |
178 if (iter == tree_id_to_tree_cache_map_.end()) | |
179 return; | |
180 | |
181 TreeCache* cache = iter->second; | |
182 tree_id_to_tree_cache_map_.erase(tree_id); | |
183 axtree_to_tree_cache_map_.erase(&cache->tree); | |
184 delete cache; | |
185 } | |
186 | |
187 void AutomationInternalCustomBindings::GetRootID( | |
188 const v8::FunctionCallbackInfo<v8::Value>& args) { | |
189 if (args.Length() != 1) | |
David Tseng
2015/06/10 17:48:16
Seems like these cases throughout should cause an
dmazzoni
2015/06/11 19:09:12
Done. I have it throw an exception for invalid arg
aboxhall
2015/06/11 20:32:42
Looks like other custom bindings code uses CHECKs
dmazzoni
2015/06/12 17:51:35
That's pretty hard to debug, though - there's no w
aboxhall
2015/06/12 20:43:10
With a CHECK, it'll be super obvious that somethin
dmazzoni
2015/06/16 18:19:48
OK, solved both problems. Now it grabs a JS stack
aboxhall
2015/06/16 18:22:44
Nice one :)
| |
190 return; | |
191 if (!args[0]->IsNumber()) | |
192 return; | |
193 | |
194 int tree_id = args[0]->Int32Value(); | |
195 const auto& iter = tree_id_to_tree_cache_map_.find(tree_id); | |
196 if (iter == tree_id_to_tree_cache_map_.end()) | |
197 return; | |
198 | |
199 TreeCache* cache = iter->second; | |
200 int root_id = cache->tree.root()->id(); | |
201 args.GetReturnValue().Set(v8::Integer::New(GetIsolate(), root_id)); | |
202 } | |
203 | |
204 bool AutomationInternalCustomBindings::GetNodeHelper( | |
205 const v8::FunctionCallbackInfo<v8::Value>& args, | |
206 TreeCache** out_cache, | |
207 ui::AXNode** out_node) { | |
208 if (args.Length() < 2) | |
209 return false; | |
210 if (!args[0]->IsNumber() || !args[1]->IsNumber()) | |
211 return false; | |
212 | |
213 int tree_id = args[0]->Int32Value(); | |
214 int node_id = args[1]->Int32Value(); | |
215 | |
216 const auto& iter = tree_id_to_tree_cache_map_.find(tree_id); | |
217 if (iter == tree_id_to_tree_cache_map_.end()) | |
218 return false; | |
219 | |
220 TreeCache* cache = iter->second; | |
221 ui::AXNode* node = cache->tree.GetFromId(node_id); | |
222 | |
223 if (out_cache) | |
224 *out_cache = cache; | |
225 if (out_node) | |
226 *out_node = node; | |
227 | |
228 return node != nullptr; | |
229 } | |
230 | |
231 void AutomationInternalCustomBindings::SetReturnValueFromBaseValue( | |
232 const v8::FunctionCallbackInfo<v8::Value>& args, | |
233 base::Value* value) { | |
234 v8::Isolate* isolate = context()->isolate(); | |
235 v8::HandleScope handle_scope(isolate); | |
236 v8::Context::Scope context_scope(context()->v8_context()); | |
237 scoped_ptr<content::V8ValueConverter> converter( | |
238 content::V8ValueConverter::create()); | |
239 v8::Local<v8::Value> v8_value = | |
240 converter->ToV8Value(value, context()->v8_context()); | |
241 args.GetReturnValue().Set(v8_value); | |
242 } | |
243 | |
244 void AutomationInternalCustomBindings::GetParentID( | |
245 const v8::FunctionCallbackInfo<v8::Value>& args) { | |
246 ui::AXNode* node; | |
247 if (!GetNodeHelper(args, nullptr, &node)) | |
248 return; | |
249 | |
250 if (!node->parent()) | |
251 return; | |
252 | |
253 int parent_id = node->parent()->id(); | |
254 args.GetReturnValue().Set(v8::Integer::New(GetIsolate(), parent_id)); | |
255 } | |
256 | |
257 void AutomationInternalCustomBindings::GetChildCount( | |
258 const v8::FunctionCallbackInfo<v8::Value>& args) { | |
259 ui::AXNode* node; | |
260 if (!GetNodeHelper(args, nullptr, &node)) | |
261 return; | |
262 | |
263 int child_count = node->child_count(); | |
264 args.GetReturnValue().Set(v8::Integer::New(GetIsolate(), child_count)); | |
265 } | |
266 | |
267 void AutomationInternalCustomBindings::GetChildIDAtIndex( | |
David Tseng
2015/06/10 17:48:16
These functions should probably get docs; maybe js
dmazzoni
2015/06/11 19:09:11
Added concise docs to the header file now. Does th
| |
268 const v8::FunctionCallbackInfo<v8::Value>& args) { | |
269 ui::AXNode* node; | |
270 if (!GetNodeHelper(args, nullptr, &node)) | |
271 return; | |
272 | |
273 if (args.Length() < 3 || !args[2]->IsNumber()) | |
aboxhall
2015/06/11 15:05:38
Do args validation before getting the node?
dmazzoni
2015/06/11 19:09:12
Done.
| |
274 return; | |
275 | |
276 int index = args[2]->Int32Value(); | |
277 if (index < 0 || index >= node->child_count()) | |
278 return; | |
279 | |
280 int child_id = node->children()[index]->id(); | |
281 args.GetReturnValue().Set(v8::Integer::New(GetIsolate(), child_id)); | |
282 } | |
283 | |
284 void AutomationInternalCustomBindings::GetIndexInParent( | |
285 const v8::FunctionCallbackInfo<v8::Value>& args) { | |
286 ui::AXNode* node; | |
287 if (!GetNodeHelper(args, nullptr, &node)) | |
288 return; | |
289 | |
290 int index_in_parent = node->index_in_parent(); | |
291 args.GetReturnValue().Set(v8::Integer::New(GetIsolate(), index_in_parent)); | |
292 } | |
293 | |
294 void AutomationInternalCustomBindings::GetState( | |
295 const v8::FunctionCallbackInfo<v8::Value>& args) { | |
296 ui::AXNode* node; | |
297 if (!GetNodeHelper(args, nullptr, &node)) | |
298 return; | |
299 | |
300 scoped_ptr<base::DictionaryValue> state_dict(new base::DictionaryValue()); | |
301 uint32 state_pos = 0, state_shifter = node->data().state; | |
302 while (state_shifter) { | |
303 if (state_shifter & 1) { | |
304 state_dict->SetBoolean( | |
305 ToString(static_cast<ui::AXState>(state_pos)), true); | |
306 } | |
307 state_shifter = state_shifter >> 1; | |
308 state_pos++; | |
309 } | |
310 | |
311 SetReturnValueFromBaseValue(args, state_dict.get()); | |
312 } | |
313 | |
314 void AutomationInternalCustomBindings::GetRole( | |
315 const v8::FunctionCallbackInfo<v8::Value>& args) { | |
316 ui::AXNode* node; | |
317 if (!GetNodeHelper(args, nullptr, &node)) | |
318 return; | |
319 | |
320 std::string role_name = ui::ToString(node->data().role); | |
321 args.GetReturnValue().Set( | |
322 v8::String::NewFromUtf8(GetIsolate(), role_name.c_str())); | |
323 } | |
324 | |
325 void AutomationInternalCustomBindings::GetLocation( | |
326 const v8::FunctionCallbackInfo<v8::Value>& args) { | |
327 TreeCache* cache; | |
328 ui::AXNode* node; | |
329 if (!GetNodeHelper(args, &cache, &node)) | |
330 return; | |
331 | |
332 scoped_ptr<base::DictionaryValue> location_dict(new base::DictionaryValue()); | |
333 gfx::Rect location = node->data().location; | |
334 location.Offset(cache->location_offset); | |
335 location_dict->SetInteger("left", location.x()); | |
336 location_dict->SetInteger("top", location.y()); | |
337 location_dict->SetInteger("width", location.width()); | |
338 location_dict->SetInteger("height", location.height()); | |
339 | |
340 SetReturnValueFromBaseValue(args, location_dict.get()); | |
341 } | |
342 | |
343 bool AutomationInternalCustomBindings::GetAttributeHelper( | |
344 const v8::FunctionCallbackInfo<v8::Value>& args, | |
345 ui::AXNode** out_node, | |
346 std::string* out_attribute_name) { | |
347 if (args.Length() != 3) | |
348 return false; | |
349 if (!args[0]->IsNumber() || | |
350 !args[1]->IsNumber() || | |
351 !args[2]->IsString()) { | |
352 return false; | |
353 } | |
354 | |
355 int tree_id = args[0]->Int32Value(); | |
356 int node_id = args[1]->Int32Value(); | |
357 *out_attribute_name = *v8::String::Utf8Value(args[2]); | |
358 | |
359 const auto& iter = tree_id_to_tree_cache_map_.find(tree_id); | |
360 if (iter == tree_id_to_tree_cache_map_.end()) | |
361 return false; | |
362 | |
363 TreeCache* cache = iter->second; | |
364 *out_node = cache->tree.GetFromId(node_id); | |
365 if (!*out_node) | |
David Tseng
2015/06/10 17:48:16
GetNodeHelper?
dmazzoni
2015/06/11 19:09:12
Done.
| |
366 return false; | |
367 | |
368 return true; | |
369 } | |
370 | |
371 void AutomationInternalCustomBindings::GetStringAttribute( | |
372 const v8::FunctionCallbackInfo<v8::Value>& args) { | |
373 ui::AXNode* node; | |
374 std::string attribute_name; | |
375 if (!GetAttributeHelper(args, &node, &attribute_name)) | |
376 return; | |
377 | |
378 ui::AXStringAttribute attribute = ui::ParseAXStringAttribute(attribute_name); | |
379 std::string attr_value; | |
380 if (!node->data().GetStringAttribute(attribute, &attr_value)) | |
381 return; | |
382 | |
383 args.GetReturnValue().Set( | |
384 v8::String::NewFromUtf8(GetIsolate(), attr_value.c_str())); | |
385 } | |
386 | |
387 void AutomationInternalCustomBindings::GetBoolAttribute( | |
388 const v8::FunctionCallbackInfo<v8::Value>& args) { | |
389 ui::AXNode* node; | |
390 std::string attribute_name; | |
391 if (!GetAttributeHelper(args, &node, &attribute_name)) | |
392 return; | |
393 | |
394 ui::AXBoolAttribute attribute = ui::ParseAXBoolAttribute(attribute_name); | |
395 bool attr_value; | |
396 if (!node->data().GetBoolAttribute(attribute, &attr_value)) | |
397 return; | |
David Tseng
2015/06/10 17:48:16
Is return value here undefined in js?
dmazzoni
2015/06/11 19:09:11
Yes, the default will be undefined.
| |
398 | |
399 args.GetReturnValue().Set(v8::Boolean::New(GetIsolate(), attr_value)); | |
400 } | |
401 | |
402 void AutomationInternalCustomBindings::GetIntAttribute( | |
403 const v8::FunctionCallbackInfo<v8::Value>& args) { | |
404 ui::AXNode* node; | |
405 std::string attribute_name; | |
406 if (!GetAttributeHelper(args, &node, &attribute_name)) | |
407 return; | |
408 | |
409 ui::AXIntAttribute attribute = ui::ParseAXIntAttribute(attribute_name); | |
410 int attr_value; | |
411 if (!node->data().GetIntAttribute(attribute, &attr_value)) | |
412 return; | |
413 | |
414 args.GetReturnValue().Set(v8::Integer::New(GetIsolate(), attr_value)); | |
415 } | |
416 | |
417 void AutomationInternalCustomBindings::GetFloatAttribute( | |
418 const v8::FunctionCallbackInfo<v8::Value>& args) { | |
419 ui::AXNode* node; | |
420 std::string attribute_name; | |
421 if (!GetAttributeHelper(args, &node, &attribute_name)) | |
422 return; | |
423 | |
424 ui::AXFloatAttribute attribute = ui::ParseAXFloatAttribute(attribute_name); | |
425 float attr_value; | |
426 | |
427 if (!node->data().GetFloatAttribute(attribute, &attr_value)) | |
428 return; | |
429 | |
430 args.GetReturnValue().Set(v8::Number::New(GetIsolate(), attr_value)); | |
431 } | |
432 | |
433 void AutomationInternalCustomBindings::GetIntListAttribute( | |
434 const v8::FunctionCallbackInfo<v8::Value>& args) { | |
435 ui::AXNode* node; | |
436 std::string attribute_name; | |
437 if (!GetAttributeHelper(args, &node, &attribute_name)) | |
438 return; | |
439 | |
440 ui::AXIntListAttribute attribute = | |
441 ui::ParseAXIntListAttribute(attribute_name); | |
442 if (!node->data().HasIntListAttribute(attribute)) | |
443 return; | |
444 const std::vector<int32>& attr_value = | |
445 node->data().GetIntListAttribute(attribute); | |
446 | |
447 scoped_ptr<base::ListValue> list(new base::ListValue()); | |
448 for (int32 i : attr_value) | |
449 list->AppendInteger(i); | |
450 SetReturnValueFromBaseValue(args, list.get()); | |
451 } | |
452 | |
147 void AutomationInternalCustomBindings::OnAccessibilityEvent( | 453 void AutomationInternalCustomBindings::OnAccessibilityEvent( |
aboxhall
2015/06/11 15:05:38
I can't figure out how this event gets dispatched.
dmazzoni
2015/06/11 19:09:11
It gets called from AutomationWebContentsObserver:
| |
148 const ExtensionMsg_AccessibilityEventParams& params) { | 454 const ExtensionMsg_AccessibilityEventParams& params) { |
149 // TODO(dmazzoni): finish implementing this. | 455 int tree_id = params.tree_id; |
456 TreeCache* cache; | |
457 auto iter = tree_id_to_tree_cache_map_.find(tree_id); | |
458 if (iter == tree_id_to_tree_cache_map_.end()) { | |
aboxhall
2015/06/11 15:05:38
I was trying to figure out where this got called s
dmazzoni
2015/06/11 19:09:11
Yeah, AutomationEventRouter keeps track of exactly
aboxhall
2015/06/11 20:32:42
Ah, I see it now. Technically the permissions chec
dmazzoni
2015/06/12 17:51:35
I'm not sure how to do that because AutomationEven
aboxhall
2015/06/12 20:43:10
Right, I see. I misunderstood the 'has permission
| |
459 cache = new TreeCache(); | |
460 cache->tab_id = -1; | |
461 cache->tree_id = params.tree_id; | |
462 cache->tree.SetDelegate(this); | |
463 tree_id_to_tree_cache_map_.insert(std::make_pair(tree_id, cache)); | |
464 axtree_to_tree_cache_map_.insert(std::make_pair(&cache->tree, cache)); | |
465 } else { | |
466 cache = iter->second; | |
467 } | |
468 | |
469 cache->location_offset = params.location_offset; | |
470 CHECK(cache->tree.Unserialize(params.update)); | |
471 api::automation_internal::AXEventParams event_params; | |
472 event_params.tree_id = params.tree_id; | |
473 event_params.target_id = params.id; | |
474 event_params.event_type = ToString(params.event_type); | |
475 scoped_ptr<base::ListValue> args(new base::ListValue()); | |
476 args->Append(event_params.ToValue()); | |
477 | |
478 v8::Isolate* isolate = context()->isolate(); | |
479 v8::HandleScope handle_scope(isolate); | |
480 v8::Context::Scope context_scope(context()->v8_context()); | |
481 scoped_ptr<content::V8ValueConverter> converter( | |
482 content::V8ValueConverter::create()); | |
483 v8::Local<v8::Value> v8_args = | |
484 converter->ToV8Value(args.get(), context()->v8_context()); | |
485 | |
486 CHECK(v8_args->IsArray()); | |
487 v8::Local<v8::Array> v8_args_array = v8_args.As<v8::Array>(); | |
488 context()->DispatchEvent("automationInternal.onAccessibilityEvent", | |
489 v8_args_array); | |
490 } | |
491 | |
492 void AutomationInternalCustomBindings::OnNodeWillBeDeleted(ui::AXTree* tree, | |
493 ui::AXNode* node) { | |
494 SendTreeChangeEvent("nodeRemoved", tree, node); | |
495 } | |
496 | |
497 void AutomationInternalCustomBindings::OnSubtreeWillBeDeleted( | |
498 ui::AXTree* tree, | |
499 ui::AXNode* node) { | |
500 } | |
David Tseng
2015/06/10 17:48:15
TODO?
dmazzoni
2015/06/11 19:09:12
Not necessarily needed? This was needed to impleme
aboxhall
2015/06/11 20:32:42
Could these null implementations be moved up into
dmazzoni
2015/06/12 17:51:35
To be clear, these are pure virtual functions in A
aboxhall
2015/06/12 20:43:10
Makes sense - I wasn't sure to what extent we gene
| |
501 | |
502 void AutomationInternalCustomBindings::OnNodeCreated(ui::AXTree* tree, | |
503 ui::AXNode* node) { | |
504 | |
David Tseng
2015/06/10 17:48:16
TODO?
dmazzoni
2015/06/11 19:09:11
Not needed, added a comment.
| |
505 } | |
506 | |
507 void AutomationInternalCustomBindings::OnNodeChanged(ui::AXTree* tree, | |
508 ui::AXNode* node) { | |
509 } | |
David Tseng
2015/06/10 17:48:15
TODO?
dmazzoni
2015/06/11 19:09:11
Done.
| |
510 | |
511 void AutomationInternalCustomBindings::OnAtomicUpdateFinished( | |
David Tseng
2015/06/10 17:48:16
What calls this function?
dmazzoni
2015/06/11 19:09:11
AXTree calls this because we implement AXTreeDeleg
| |
512 ui::AXTree* tree, | |
513 bool root_changed, | |
514 const std::vector<ui::AXTreeDelegate::Change>& changes) { | |
515 auto iter = axtree_to_tree_cache_map_.find(tree); | |
516 if (iter == axtree_to_tree_cache_map_.end()) | |
517 return; | |
518 | |
519 for (auto change : changes) { | |
520 ui::AXNode* node = change.node; | |
521 switch (change.type) { | |
522 case NODE_CREATED: | |
523 SendTreeChangeEvent("nodeCreated", tree, node); | |
524 break; | |
525 case SUBTREE_CREATED: | |
526 SendTreeChangeEvent("subtreeCreated", tree, node); | |
527 break; | |
528 case NODE_CHANGED: | |
529 SendTreeChangeEvent("nodeChanged", tree, node); | |
David Tseng
2015/06/10 17:48:16
Define/document these literals.
dmazzoni
2015/06/11 19:09:12
Switched to use the enum and ToString function aut
| |
530 break; | |
531 } | |
532 } | |
533 } | |
534 | |
535 void AutomationInternalCustomBindings::SendTreeChangeEvent( | |
536 const char *change_type, | |
537 ui::AXTree* tree, | |
538 ui::AXNode* node) { | |
539 auto iter = axtree_to_tree_cache_map_.find(tree); | |
540 if (iter == axtree_to_tree_cache_map_.end()) | |
541 return; | |
542 | |
543 int tree_id = iter->second->tree_id; | |
544 scoped_ptr<base::ListValue> args(new base::ListValue()); | |
545 args->AppendInteger(tree_id); | |
546 args->AppendInteger(node->id()); | |
547 args->AppendString(change_type); | |
548 | |
549 v8::Isolate* isolate = context()->isolate(); | |
550 v8::HandleScope handle_scope(isolate); | |
551 v8::Context::Scope context_scope(context()->v8_context()); | |
552 scoped_ptr<content::V8ValueConverter> converter( | |
553 content::V8ValueConverter::create()); | |
554 v8::Local<v8::Value> v8_args = | |
555 converter->ToV8Value(args.get(), context()->v8_context()); | |
556 | |
557 CHECK(v8_args->IsArray()); | |
558 v8::Local<v8::Array> v8_args_array = v8_args.As<v8::Array>(); | |
559 context()->DispatchEvent("automationInternal.onTreeChange", | |
560 v8_args_array); | |
150 } | 561 } |
151 | 562 |
152 } // namespace extensions | 563 } // namespace extensions |
OLD | NEW |