Chromium Code Reviews| 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 |