Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1368)

Side by Side Diff: chrome/renderer/extensions/automation_internal_custom_bindings.cc

Issue 1155183006: Reimplement automation API on top of C++-backed AXTree. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@automation_faster_2
Patch Set: Rebase, delete some code that shouldn't have been commented out Created 5 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
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
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698