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/thread_task_runner_handle.h" | 9 #include "base/thread_task_runner_handle.h" |
10 #include "base/values.h" | 10 #include "base/values.h" |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
56 return v8::String::NewFromUtf8(isolate, str, v8::String::kNormalString, | 56 return v8::String::NewFromUtf8(isolate, str, v8::String::kNormalString, |
57 strlen(str)); | 57 strlen(str)); |
58 } | 58 } |
59 | 59 |
60 v8::Local<v8::Value> CreateV8String(v8::Isolate* isolate, | 60 v8::Local<v8::Value> CreateV8String(v8::Isolate* isolate, |
61 const std::string& str) { | 61 const std::string& str) { |
62 return v8::String::NewFromUtf8(isolate, str.c_str(), | 62 return v8::String::NewFromUtf8(isolate, str.c_str(), |
63 v8::String::kNormalString, str.length()); | 63 v8::String::kNormalString, str.length()); |
64 } | 64 } |
65 | 65 |
66 v8::Local<v8::Object> RectToV8Object(v8::Isolate* isolate, | |
67 const gfx::Rect& rect) { | |
68 v8::Local<v8::Object> result(v8::Object::New(isolate)); | |
69 result->Set(CreateV8String(isolate, "left"), | |
70 v8::Integer::New(isolate, rect.x())); | |
71 result->Set(CreateV8String(isolate, "top"), | |
72 v8::Integer::New(isolate, rect.y())); | |
73 result->Set(CreateV8String(isolate, "width"), | |
74 v8::Integer::New(isolate, rect.width())); | |
75 result->Set(CreateV8String(isolate, "height"), | |
76 v8::Integer::New(isolate, rect.height())); | |
77 return result; | |
78 } | |
79 | |
80 // Compute the bounding box of a node, fixing nodes with empty bounds by | |
81 // unioning the bounds of their children. | |
82 static gfx::Rect ComputeLocalNodeBounds(TreeCache* cache, ui::AXNode* node) { | |
83 gfx::Rect bounds = node->data().location; | |
84 if (bounds.width() > 0 && bounds.height() > 0) | |
85 return bounds; | |
86 | |
87 // Compute the bounds of each child. | |
88 for (size_t i = 0; i < node->children().size(); i++) { | |
89 ui::AXNode* child = node->children()[i]; | |
90 gfx::Rect child_bounds = ComputeLocalNodeBounds(cache, child); | |
91 | |
92 // Ignore children that don't have valid bounds themselves. | |
93 if (child_bounds.width() == 0 || child_bounds.height() == 0) | |
94 continue; | |
95 | |
96 // For the first valid child, just set the bounds to that child's bounds. | |
97 if (bounds.width() == 0 || bounds.height() == 0) { | |
98 bounds = child_bounds; | |
99 continue; | |
100 } | |
101 | |
102 // Union each additional child's bounds. | |
103 bounds.Union(child_bounds); | |
104 } | |
105 | |
106 return bounds; | |
107 } | |
108 | |
109 // Compute the bounding box of a node in global coordinates, walking up the | |
110 // parent hierarchy to offset by frame offsets and scroll offsets. | |
111 static gfx::Rect ComputeGlobalNodeBounds(TreeCache* cache, ui::AXNode* node) { | |
112 gfx::Rect bounds = ComputeLocalNodeBounds(cache, node); | |
113 ui::AXNode* parent = node->parent(); | |
114 bool need_to_offset_web_area = node->data().role == ui::AX_ROLE_WEB_AREA || | |
115 node->data().role == ui::AX_ROLE_ROOT_WEB_AREA; | |
116 while (parent) { | |
117 if (bounds.IsEmpty()) { | |
118 bounds = parent->data().location; | |
119 } else if (need_to_offset_web_area && parent->data().location.width() > 0 && | |
120 parent->data().location.height() > 0) { | |
121 bounds.Offset(parent->data().location.x(), parent->data().location.y()); | |
122 need_to_offset_web_area = false; | |
123 } | |
124 | |
125 if (parent->data().role == ui::AX_ROLE_WEB_AREA || | |
126 parent->data().role == ui::AX_ROLE_ROOT_WEB_AREA) { | |
127 int sx = 0; | |
128 int sy = 0; | |
129 if (parent->data().GetIntAttribute(ui::AX_ATTR_SCROLL_X, &sx) && | |
130 parent->data().GetIntAttribute(ui::AX_ATTR_SCROLL_Y, &sy)) { | |
131 bounds.Offset(-sx, -sy); | |
132 } | |
133 need_to_offset_web_area = true; | |
134 } | |
135 parent = parent->parent(); | |
136 } | |
137 | |
138 return bounds; | |
139 } | |
140 | |
66 // | 141 // |
67 // Helper class that helps implement bindings for a JavaScript function | 142 // Helper class that helps implement bindings for a JavaScript function |
68 // that takes a single input argument consisting of a Tree ID. Looks up | 143 // that takes a single input argument consisting of a Tree ID. Looks up |
69 // the TreeCache and passes it to the function passed to the constructor. | 144 // the TreeCache and passes it to the function passed to the constructor. |
70 // | 145 // |
71 | 146 |
72 typedef void (*TreeIDFunction)(v8::Isolate* isolate, | 147 typedef void (*TreeIDFunction)(v8::Isolate* isolate, |
73 v8::ReturnValue<v8::Value> result, | 148 v8::ReturnValue<v8::Value> result, |
74 TreeCache* cache); | 149 TreeCache* cache); |
75 | 150 |
(...skipping 119 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
195 | 270 |
196 private: | 271 private: |
197 virtual ~NodeIDPlusAttributeWrapper() {} | 272 virtual ~NodeIDPlusAttributeWrapper() {} |
198 | 273 |
199 friend class base::RefCountedThreadSafe<NodeIDPlusAttributeWrapper>; | 274 friend class base::RefCountedThreadSafe<NodeIDPlusAttributeWrapper>; |
200 | 275 |
201 AutomationInternalCustomBindings* automation_bindings_; | 276 AutomationInternalCustomBindings* automation_bindings_; |
202 NodeIDPlusAttributeFunction function_; | 277 NodeIDPlusAttributeFunction function_; |
203 }; | 278 }; |
204 | 279 |
280 // | |
281 // Helper class that helps implement bindings for a JavaScript function | |
282 // that takes four input arguments: a tree ID, node ID, and integer start | |
283 // and end indices. Looks up the TreeCache and the AXNode and passes them | |
284 // to the function passed to the constructor. | |
285 // | |
286 | |
287 typedef void (*NodeIDPlusRangeFunction)(v8::Isolate* isolate, | |
288 v8::ReturnValue<v8::Value> result, | |
289 TreeCache* cache, | |
290 ui::AXNode* node, | |
291 int start, | |
292 int end); | |
293 | |
294 class NodeIDPlusRangeWrapper | |
295 : public base::RefCountedThreadSafe<NodeIDPlusRangeWrapper> { | |
296 public: | |
297 NodeIDPlusRangeWrapper(AutomationInternalCustomBindings* automation_bindings, | |
298 NodeIDPlusRangeFunction function) | |
299 : automation_bindings_(automation_bindings), function_(function) {} | |
300 | |
301 void Run(const v8::FunctionCallbackInfo<v8::Value>& args) { | |
302 v8::Isolate* isolate = automation_bindings_->GetIsolate(); | |
303 if (args.Length() < 4 || !args[0]->IsNumber() || !args[1]->IsNumber() || | |
304 !args[2]->IsNumber() || !args[3]->IsNumber()) { | |
305 ThrowInvalidArgumentsException(automation_bindings_); | |
306 } | |
307 | |
308 int tree_id = args[0]->Int32Value(); | |
309 int node_id = args[1]->Int32Value(); | |
310 int start = args[2]->Int32Value(); | |
311 int end = args[3]->Int32Value(); | |
312 | |
313 TreeCache* cache = automation_bindings_->GetTreeCacheFromTreeID(tree_id); | |
314 if (!cache) | |
315 return; | |
316 | |
317 ui::AXNode* node = cache->tree.GetFromId(node_id); | |
318 if (!node) | |
319 return; | |
320 | |
321 function_(isolate, args.GetReturnValue(), cache, node, start, end); | |
322 } | |
323 | |
324 private: | |
325 virtual ~NodeIDPlusRangeWrapper() {} | |
326 | |
327 friend class base::RefCountedThreadSafe<NodeIDPlusRangeWrapper>; | |
328 | |
329 AutomationInternalCustomBindings* automation_bindings_; | |
330 NodeIDPlusRangeFunction function_; | |
331 }; | |
332 | |
205 } // namespace | 333 } // namespace |
206 | 334 |
207 TreeCache::TreeCache() {} | 335 TreeCache::TreeCache() {} |
208 TreeCache::~TreeCache() {} | 336 TreeCache::~TreeCache() {} |
209 | 337 |
210 class AutomationMessageFilter : public IPC::MessageFilter { | 338 class AutomationMessageFilter : public IPC::MessageFilter { |
211 public: | 339 public: |
212 explicit AutomationMessageFilter(AutomationInternalCustomBindings* owner) | 340 explicit AutomationMessageFilter(AutomationInternalCustomBindings* owner) |
213 : owner_(owner), | 341 : owner_(owner), |
214 removed_(false) { | 342 removed_(false) { |
(...skipping 156 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
371 }); | 499 }); |
372 RouteNodeIDFunction( | 500 RouteNodeIDFunction( |
373 "GetRole", [](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result, | 501 "GetRole", [](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result, |
374 TreeCache* cache, ui::AXNode* node) { | 502 TreeCache* cache, ui::AXNode* node) { |
375 std::string role_name = ui::ToString(node->data().role); | 503 std::string role_name = ui::ToString(node->data().role); |
376 result.Set(v8::String::NewFromUtf8(isolate, role_name.c_str())); | 504 result.Set(v8::String::NewFromUtf8(isolate, role_name.c_str())); |
377 }); | 505 }); |
378 RouteNodeIDFunction( | 506 RouteNodeIDFunction( |
379 "GetLocation", [](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result, | 507 "GetLocation", [](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result, |
380 TreeCache* cache, ui::AXNode* node) { | 508 TreeCache* cache, ui::AXNode* node) { |
381 v8::Local<v8::Object> location_obj(v8::Object::New(isolate)); | 509 gfx::Rect location = ComputeGlobalNodeBounds(cache, node); |
382 gfx::Rect location = node->data().location; | |
383 location.Offset(cache->location_offset); | 510 location.Offset(cache->location_offset); |
384 location_obj->Set(CreateV8String(isolate, "left"), | 511 result.Set(RectToV8Object(isolate, location)); |
385 v8::Integer::New(isolate, location.x())); | |
386 location_obj->Set(CreateV8String(isolate, "top"), | |
387 v8::Integer::New(isolate, location.y())); | |
388 location_obj->Set(CreateV8String(isolate, "width"), | |
389 v8::Integer::New(isolate, location.width())); | |
390 location_obj->Set(CreateV8String(isolate, "height"), | |
391 v8::Integer::New(isolate, location.height())); | |
392 result.Set(location_obj); | |
393 }); | 512 }); |
394 | 513 |
395 // Bindings that take a Tree ID and Node ID and string attribute name | 514 // Bindings that take a Tree ID and Node ID and string attribute name |
515 // and return a property of the node. | |
516 | |
517 RouteNodeIDPlusRangeFunction( | |
518 "GetBoundsForRange", | |
519 [](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result, | |
520 TreeCache* cache, ui::AXNode* node, int start, int end) { | |
521 gfx::Rect location = ComputeGlobalNodeBounds(cache, node); | |
522 location.Offset(cache->location_offset); | |
523 if (node->data().role == ui::AX_ROLE_INLINE_TEXT_BOX) { | |
524 std::string name = node->data().GetStringAttribute(ui::AX_ATTR_NAME); | |
525 std::vector<int> character_offsets = | |
526 node->data().GetIntListAttribute(ui::AX_ATTR_CHARACTER_OFFSETS); | |
527 int len = | |
528 static_cast<int>(std::min(name.size(), character_offsets.size())); | |
529 if (start >= 0 && start <= len && end >= 0 && end <= len && | |
530 end >= start) { | |
531 int start_offset = start > 0 ? character_offsets[start - 1] : 0; | |
532 int end_offset = end > 0 ? character_offsets[end - 1] : 0; | |
533 location.set_x(location.x() + start_offset); | |
Peter Lundblad
2015/11/20 11:15:03
Could this need to be extended for top-to-bottom t
dmazzoni
2015/11/20 23:35:14
Yes! We were already exposing this info, wasn't ha
| |
534 location.set_width(end_offset - start_offset); | |
535 } | |
536 } | |
537 result.Set(RectToV8Object(isolate, location)); | |
538 }); | |
539 | |
540 // Bindings that take a Tree ID and Node ID and string attribute name | |
396 // and return a property of the node. | 541 // and return a property of the node. |
397 | 542 |
398 RouteNodeIDPlusAttributeFunction( | 543 RouteNodeIDPlusAttributeFunction( |
399 "GetStringAttribute", | 544 "GetStringAttribute", |
400 [](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result, | 545 [](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result, |
401 ui::AXNode* node, const std::string& attribute_name) { | 546 ui::AXNode* node, const std::string& attribute_name) { |
402 ui::AXStringAttribute attribute = | 547 ui::AXStringAttribute attribute = |
403 ui::ParseAXStringAttribute(attribute_name); | 548 ui::ParseAXStringAttribute(attribute_name); |
404 std::string attr_value; | 549 std::string attr_value; |
405 if (!node->data().GetStringAttribute(attribute, &attr_value)) | 550 if (!node->data().GetStringAttribute(attribute, &attr_value)) |
(...skipping 179 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
585 } | 730 } |
586 | 731 |
587 void AutomationInternalCustomBindings::RouteNodeIDPlusAttributeFunction( | 732 void AutomationInternalCustomBindings::RouteNodeIDPlusAttributeFunction( |
588 const std::string& name, | 733 const std::string& name, |
589 NodeIDPlusAttributeFunction callback) { | 734 NodeIDPlusAttributeFunction callback) { |
590 scoped_refptr<NodeIDPlusAttributeWrapper> wrapper = | 735 scoped_refptr<NodeIDPlusAttributeWrapper> wrapper = |
591 new NodeIDPlusAttributeWrapper(this, callback); | 736 new NodeIDPlusAttributeWrapper(this, callback); |
592 RouteFunction(name, base::Bind(&NodeIDPlusAttributeWrapper::Run, wrapper)); | 737 RouteFunction(name, base::Bind(&NodeIDPlusAttributeWrapper::Run, wrapper)); |
593 } | 738 } |
594 | 739 |
740 void AutomationInternalCustomBindings::RouteNodeIDPlusRangeFunction( | |
741 const std::string& name, | |
742 NodeIDPlusRangeFunction callback) { | |
743 scoped_refptr<NodeIDPlusRangeWrapper> wrapper = | |
744 new NodeIDPlusRangeWrapper(this, callback); | |
745 RouteFunction(name, base::Bind(&NodeIDPlusRangeWrapper::Run, wrapper)); | |
746 } | |
747 | |
595 void AutomationInternalCustomBindings::GetChildIDAtIndex( | 748 void AutomationInternalCustomBindings::GetChildIDAtIndex( |
596 const v8::FunctionCallbackInfo<v8::Value>& args) { | 749 const v8::FunctionCallbackInfo<v8::Value>& args) { |
597 if (args.Length() < 3 || !args[2]->IsNumber()) { | 750 if (args.Length() < 3 || !args[2]->IsNumber()) { |
598 ThrowInvalidArgumentsException(this); | 751 ThrowInvalidArgumentsException(this); |
599 return; | 752 return; |
600 } | 753 } |
601 | 754 |
602 int tree_id = args[0]->Int32Value(); | 755 int tree_id = args[0]->Int32Value(); |
603 int node_id = args[1]->Int32Value(); | 756 int node_id = args[1]->Int32Value(); |
604 | 757 |
(...skipping 144 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
749 v8::HandleScope handle_scope(isolate); | 902 v8::HandleScope handle_scope(isolate); |
750 v8::Context::Scope context_scope(context()->v8_context()); | 903 v8::Context::Scope context_scope(context()->v8_context()); |
751 v8::Local<v8::Array> args(v8::Array::New(GetIsolate(), 3U)); | 904 v8::Local<v8::Array> args(v8::Array::New(GetIsolate(), 3U)); |
752 args->Set(0U, v8::Integer::New(GetIsolate(), tree_id)); | 905 args->Set(0U, v8::Integer::New(GetIsolate(), tree_id)); |
753 args->Set(1U, v8::Integer::New(GetIsolate(), node->id())); | 906 args->Set(1U, v8::Integer::New(GetIsolate(), node->id())); |
754 args->Set(2U, CreateV8String(isolate, ToString(change_type))); | 907 args->Set(2U, CreateV8String(isolate, ToString(change_type))); |
755 context()->DispatchEvent("automationInternal.onTreeChange", args); | 908 context()->DispatchEvent("automationInternal.onTreeChange", args); |
756 } | 909 } |
757 | 910 |
758 } // namespace extensions | 911 } // namespace extensions |
OLD | NEW |