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 <= end && end <= len) { |
| 530 int start_offset = start > 0 ? character_offsets[start - 1] : 0; |
| 531 int end_offset = end > 0 ? character_offsets[end - 1] : 0; |
| 532 |
| 533 switch (node->data().GetIntAttribute(ui::AX_ATTR_TEXT_DIRECTION)) { |
| 534 case ui::AX_TEXT_DIRECTION_LTR: |
| 535 default: |
| 536 location.set_x(location.x() + start_offset); |
| 537 location.set_width(end_offset - start_offset); |
| 538 break; |
| 539 case ui::AX_TEXT_DIRECTION_RTL: |
| 540 location.set_x(location.x() + location.width() - end_offset); |
| 541 location.set_width(end_offset - start_offset); |
| 542 break; |
| 543 case ui::AX_TEXT_DIRECTION_TTB: |
| 544 location.set_y(location.y() + start_offset); |
| 545 location.set_height(end_offset - start_offset); |
| 546 break; |
| 547 case ui::AX_TEXT_DIRECTION_BTT: |
| 548 location.set_y(location.y() + location.height() - end_offset); |
| 549 location.set_height(end_offset - start_offset); |
| 550 break; |
| 551 } |
| 552 } |
| 553 } |
| 554 result.Set(RectToV8Object(isolate, location)); |
| 555 }); |
| 556 |
| 557 // Bindings that take a Tree ID and Node ID and string attribute name |
396 // and return a property of the node. | 558 // and return a property of the node. |
397 | 559 |
398 RouteNodeIDPlusAttributeFunction( | 560 RouteNodeIDPlusAttributeFunction( |
399 "GetStringAttribute", | 561 "GetStringAttribute", |
400 [](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result, | 562 [](v8::Isolate* isolate, v8::ReturnValue<v8::Value> result, |
401 ui::AXNode* node, const std::string& attribute_name) { | 563 ui::AXNode* node, const std::string& attribute_name) { |
402 ui::AXStringAttribute attribute = | 564 ui::AXStringAttribute attribute = |
403 ui::ParseAXStringAttribute(attribute_name); | 565 ui::ParseAXStringAttribute(attribute_name); |
404 std::string attr_value; | 566 std::string attr_value; |
405 if (!node->data().GetStringAttribute(attribute, &attr_value)) | 567 if (!node->data().GetStringAttribute(attribute, &attr_value)) |
(...skipping 179 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
585 } | 747 } |
586 | 748 |
587 void AutomationInternalCustomBindings::RouteNodeIDPlusAttributeFunction( | 749 void AutomationInternalCustomBindings::RouteNodeIDPlusAttributeFunction( |
588 const std::string& name, | 750 const std::string& name, |
589 NodeIDPlusAttributeFunction callback) { | 751 NodeIDPlusAttributeFunction callback) { |
590 scoped_refptr<NodeIDPlusAttributeWrapper> wrapper = | 752 scoped_refptr<NodeIDPlusAttributeWrapper> wrapper = |
591 new NodeIDPlusAttributeWrapper(this, callback); | 753 new NodeIDPlusAttributeWrapper(this, callback); |
592 RouteFunction(name, base::Bind(&NodeIDPlusAttributeWrapper::Run, wrapper)); | 754 RouteFunction(name, base::Bind(&NodeIDPlusAttributeWrapper::Run, wrapper)); |
593 } | 755 } |
594 | 756 |
| 757 void AutomationInternalCustomBindings::RouteNodeIDPlusRangeFunction( |
| 758 const std::string& name, |
| 759 NodeIDPlusRangeFunction callback) { |
| 760 scoped_refptr<NodeIDPlusRangeWrapper> wrapper = |
| 761 new NodeIDPlusRangeWrapper(this, callback); |
| 762 RouteFunction(name, base::Bind(&NodeIDPlusRangeWrapper::Run, wrapper)); |
| 763 } |
| 764 |
595 void AutomationInternalCustomBindings::GetChildIDAtIndex( | 765 void AutomationInternalCustomBindings::GetChildIDAtIndex( |
596 const v8::FunctionCallbackInfo<v8::Value>& args) { | 766 const v8::FunctionCallbackInfo<v8::Value>& args) { |
597 if (args.Length() < 3 || !args[2]->IsNumber()) { | 767 if (args.Length() < 3 || !args[2]->IsNumber()) { |
598 ThrowInvalidArgumentsException(this); | 768 ThrowInvalidArgumentsException(this); |
599 return; | 769 return; |
600 } | 770 } |
601 | 771 |
602 int tree_id = args[0]->Int32Value(); | 772 int tree_id = args[0]->Int32Value(); |
603 int node_id = args[1]->Int32Value(); | 773 int node_id = args[1]->Int32Value(); |
604 | 774 |
(...skipping 144 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
749 v8::HandleScope handle_scope(isolate); | 919 v8::HandleScope handle_scope(isolate); |
750 v8::Context::Scope context_scope(context()->v8_context()); | 920 v8::Context::Scope context_scope(context()->v8_context()); |
751 v8::Local<v8::Array> args(v8::Array::New(GetIsolate(), 3U)); | 921 v8::Local<v8::Array> args(v8::Array::New(GetIsolate(), 3U)); |
752 args->Set(0U, v8::Integer::New(GetIsolate(), tree_id)); | 922 args->Set(0U, v8::Integer::New(GetIsolate(), tree_id)); |
753 args->Set(1U, v8::Integer::New(GetIsolate(), node->id())); | 923 args->Set(1U, v8::Integer::New(GetIsolate(), node->id())); |
754 args->Set(2U, CreateV8String(isolate, ToString(change_type))); | 924 args->Set(2U, CreateV8String(isolate, ToString(change_type))); |
755 context()->DispatchEvent("automationInternal.onTreeChange", args); | 925 context()->DispatchEvent("automationInternal.onTreeChange", args); |
756 } | 926 } |
757 | 927 |
758 } // namespace extensions | 928 } // namespace extensions |
OLD | NEW |