Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 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 "ash/common/devtools/ash_devtools_dom_agent.h" | 5 #include "ash/common/devtools/ash_devtools_dom_agent.h" |
| 6 | 6 |
| 7 #include "ash/common/wm_lookup.h" | 7 #include "ash/common/wm_lookup.h" |
| 8 #include "ash/common/wm_root_window_controller.h" | |
| 8 #include "ash/common/wm_window.h" | 9 #include "ash/common/wm_window.h" |
| 10 #include "ash/public/cpp/shell_window_ids.h" | |
| 9 #include "components/ui_devtools/devtools_server.h" | 11 #include "components/ui_devtools/devtools_server.h" |
| 12 #include "third_party/skia/include/core/SkColor.h" | |
| 13 #include "ui/display/display.h" | |
| 14 #include "ui/views/background.h" | |
| 15 #include "ui/views/border.h" | |
| 10 | 16 |
| 11 namespace ash { | 17 namespace ash { |
| 12 namespace devtools { | 18 namespace devtools { |
| 13 | 19 |
| 14 namespace { | 20 namespace { |
| 15 using namespace ui::devtools::protocol; | 21 using namespace ui::devtools::protocol; |
| 16 // TODO(mhashmi): Make ids reusable | 22 // TODO(mhashmi): Make ids reusable |
| 17 DOM::NodeId node_ids = 1; | 23 DOM::NodeId node_ids = 1; |
| 18 | 24 |
| 19 std::unique_ptr<DOM::Node> BuildNode( | 25 std::unique_ptr<DOM::Node> BuildNode( |
| (...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 72 for (int i = 0, count = parent->child_count(); i < count; i++) { | 78 for (int i = 0, count = parent->child_count(); i < count; i++) { |
| 73 if (view == parent->child_at(i)) { | 79 if (view == parent->child_at(i)) { |
| 74 view_index = i; | 80 view_index = i; |
| 75 break; | 81 break; |
| 76 } | 82 } |
| 77 } | 83 } |
| 78 DCHECK_GE(view_index, 0); | 84 DCHECK_GE(view_index, 0); |
| 79 return view_index == 0 ? nullptr : parent->child_at(view_index - 1); | 85 return view_index == 0 ? nullptr : parent->child_at(view_index - 1); |
| 80 } | 86 } |
| 81 | 87 |
| 88 SkColor RGBAToSkColor(DOM::RGBA* rgba) { | |
| 89 if (!rgba) | |
| 90 return SkColorSetARGB(0, 0, 0, 0); | |
| 91 // Default alpha value is 0 (not visibile) and need to convert alpha decimal | |
| 92 // percentage value to hex | |
| 93 return SkColorSetARGB(rgba->getA(0) * 255, rgba->getR(), rgba->getG(), | |
| 94 rgba->getB()); | |
|
sadrul
2016/12/06 20:48:58
Should we sanity check these values?
Sarmad Hashmi
2016/12/06 22:54:34
Done.
| |
| 95 } | |
| 96 | |
| 82 } // namespace | 97 } // namespace |
| 83 | 98 AshDevToolsDOMAgent::AshDevToolsDOMAgent( |
| 84 AshDevToolsDOMAgent::AshDevToolsDOMAgent(ash::WmShell* shell) : shell_(shell) { | 99 ash::WmShell* shell, |
| 100 scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner) | |
| 101 : main_thread_task_runner_(main_thread_task_runner), shell_(shell) { | |
| 85 DCHECK(shell_); | 102 DCHECK(shell_); |
| 103 DCHECK(main_thread_task_runner_); | |
| 86 } | 104 } |
| 87 | 105 |
| 88 AshDevToolsDOMAgent::~AshDevToolsDOMAgent() { | 106 AshDevToolsDOMAgent::~AshDevToolsDOMAgent() { |
| 89 RemoveObservers(); | 107 RemoveObservers(); |
| 90 } | 108 } |
| 91 | 109 |
| 92 ui::devtools::protocol::Response AshDevToolsDOMAgent::disable() { | 110 ui::devtools::protocol::Response AshDevToolsDOMAgent::disable() { |
| 93 Reset(); | 111 Reset(); |
| 94 return ui::devtools::protocol::Response::OK(); | 112 return ui::devtools::protocol::Response::OK(); |
| 95 } | 113 } |
| 96 | 114 |
| 97 ui::devtools::protocol::Response AshDevToolsDOMAgent::getDocument( | 115 ui::devtools::protocol::Response AshDevToolsDOMAgent::getDocument( |
| 98 std::unique_ptr<ui::devtools::protocol::DOM::Node>* out_root) { | 116 std::unique_ptr<ui::devtools::protocol::DOM::Node>* out_root) { |
| 99 *out_root = BuildInitialTree(); | 117 *out_root = BuildInitialTree(); |
| 100 return ui::devtools::protocol::Response::OK(); | 118 return ui::devtools::protocol::Response::OK(); |
| 101 } | 119 } |
| 102 | 120 |
| 121 ui::devtools::protocol::Response AshDevToolsDOMAgent::highlightNode( | |
| 122 std::unique_ptr<ui::devtools::protocol::DOM::HighlightConfig> | |
| 123 highlight_config, | |
| 124 ui::devtools::protocol::Maybe<int> node_id) { | |
| 125 main_thread_task_runner_->PostTask( | |
|
sadrul
2016/12/06 20:48:59
Why do you need to PostTask()?
Sarmad Hashmi
2016/12/06 22:54:34
Removed, as discussed.
| |
| 126 FROM_HERE, | |
| 127 base::Bind(&AshDevToolsDOMAgent::HighlightNode, base::Unretained(this), | |
| 128 base::Passed(std::move(highlight_config)), | |
| 129 node_id.fromJust())); | |
| 130 return ui::devtools::protocol::Response::OK(); | |
| 131 } | |
| 132 | |
| 133 ui::devtools::protocol::Response AshDevToolsDOMAgent::hideHighlight() { | |
| 134 if (widget_for_highlighting_ && widget_for_highlighting_->IsVisible()) { | |
| 135 main_thread_task_runner_->PostTask( | |
| 136 FROM_HERE, | |
| 137 base::Bind(&views::Widget::Hide, | |
| 138 base::Unretained(widget_for_highlighting_.get()))); | |
| 139 } | |
| 140 return ui::devtools::protocol::Response::OK(); | |
| 141 } | |
| 142 | |
| 103 // Handles removing windows. | 143 // Handles removing windows. |
| 104 void AshDevToolsDOMAgent::OnWindowTreeChanging(WmWindow* window, | 144 void AshDevToolsDOMAgent::OnWindowTreeChanging(WmWindow* window, |
| 105 const TreeChangeParams& params) { | 145 const TreeChangeParams& params) { |
| 106 // Only trigger this when window == params.old_parent. | 146 // Only trigger this when window == params.old_parent. |
| 107 // Only removals are handled here. Removing a node can occur as a result of | 147 // Only removals are handled here. Removing a node can occur as a result of |
| 108 // reorganizing a window or just destroying it. OnWindowTreeChanged | 148 // reorganizing a window or just destroying it. OnWindowTreeChanged |
| 109 // is only called if there is a new_parent. The only case this method isn't | 149 // is only called if there is a new_parent. The only case this method isn't |
| 110 // called is when adding a node because old_parent is then null. | 150 // called is when adding a node because old_parent is then null. |
| 111 // Finally, We only trigger this 0 or 1 times as an old_parent will | 151 // Finally, We only trigger this 0 or 1 times as an old_parent will |
| 112 // either exist and only call this callback once, or not at all. | 152 // either exist and only call this callback once, or not at all. |
| (...skipping 103 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 216 return BuildNode("root", nullptr, std::move(children)); | 256 return BuildNode("root", nullptr, std::move(children)); |
| 217 } | 257 } |
| 218 | 258 |
| 219 std::unique_ptr<DOM::Node> AshDevToolsDOMAgent::BuildTreeForWindow( | 259 std::unique_ptr<DOM::Node> AshDevToolsDOMAgent::BuildTreeForWindow( |
| 220 ash::WmWindow* window) { | 260 ash::WmWindow* window) { |
| 221 DCHECK(!window_to_node_id_map_.count(window)); | 261 DCHECK(!window_to_node_id_map_.count(window)); |
| 222 std::unique_ptr<Array<DOM::Node>> children = Array<DOM::Node>::create(); | 262 std::unique_ptr<Array<DOM::Node>> children = Array<DOM::Node>::create(); |
| 223 views::Widget* widget = window->GetInternalWidget(); | 263 views::Widget* widget = window->GetInternalWidget(); |
| 224 if (widget) | 264 if (widget) |
| 225 children->addItem(BuildTreeForRootWidget(widget)); | 265 children->addItem(BuildTreeForRootWidget(widget)); |
| 226 for (ash::WmWindow* child : window->GetChildren()) | 266 for (ash::WmWindow* child : window->GetChildren()) { |
| 227 children->addItem(BuildTreeForWindow(child)); | 267 if (!IsHighlightingWindow(child)) |
| 268 children->addItem(BuildTreeForWindow(child)); | |
| 269 } | |
| 228 | 270 |
| 229 std::unique_ptr<ui::devtools::protocol::DOM::Node> node = | 271 std::unique_ptr<ui::devtools::protocol::DOM::Node> node = |
| 230 BuildNode("Window", GetAttributes(window), std::move(children)); | 272 BuildNode("Window", GetAttributes(window), std::move(children)); |
| 231 if (!window->HasObserver(this)) | 273 if (!window->HasObserver(this)) |
| 232 window->AddObserver(this); | 274 window->AddObserver(this); |
| 233 window_to_node_id_map_[window] = node->getNodeId(); | 275 window_to_node_id_map_[window] = node->getNodeId(); |
| 234 node_id_to_window_map_[node->getNodeId()] = window; | 276 node_id_to_window_map_[node->getNodeId()] = window; |
| 235 return node; | 277 return node; |
| 236 } | 278 } |
| 237 | 279 |
| (...skipping 20 matching lines...) Expand all Loading... | |
| 258 std::unique_ptr<ui::devtools::protocol::DOM::Node> node = | 300 std::unique_ptr<ui::devtools::protocol::DOM::Node> node = |
| 259 BuildNode("View", GetAttributes(view), std::move(children)); | 301 BuildNode("View", GetAttributes(view), std::move(children)); |
| 260 if (!view->HasObserver(this)) | 302 if (!view->HasObserver(this)) |
| 261 view->AddObserver(this); | 303 view->AddObserver(this); |
| 262 view_to_node_id_map_[view] = node->getNodeId(); | 304 view_to_node_id_map_[view] = node->getNodeId(); |
| 263 node_id_to_view_map_[node->getNodeId()] = view; | 305 node_id_to_view_map_[node->getNodeId()] = view; |
| 264 return node; | 306 return node; |
| 265 } | 307 } |
| 266 | 308 |
| 267 void AshDevToolsDOMAgent::AddWindowTree(WmWindow* window) { | 309 void AshDevToolsDOMAgent::AddWindowTree(WmWindow* window) { |
| 310 if (IsHighlightingWindow(window)) | |
| 311 return; | |
| 312 | |
| 268 DCHECK(window_to_node_id_map_.count(window->GetParent())); | 313 DCHECK(window_to_node_id_map_.count(window->GetParent())); |
| 269 WmWindow* prev_sibling = FindPreviousSibling(window); | 314 WmWindow* prev_sibling = FindPreviousSibling(window); |
| 270 frontend()->childNodeInserted( | 315 frontend()->childNodeInserted( |
| 271 window_to_node_id_map_[window->GetParent()], | 316 window_to_node_id_map_[window->GetParent()], |
| 272 prev_sibling ? window_to_node_id_map_[prev_sibling] : 0, | 317 prev_sibling ? window_to_node_id_map_[prev_sibling] : 0, |
| 273 BuildTreeForWindow(window)); | 318 BuildTreeForWindow(window)); |
| 274 } | 319 } |
| 275 | 320 |
| 276 void AshDevToolsDOMAgent::RemoveWindowTree(WmWindow* window, | 321 void AshDevToolsDOMAgent::RemoveWindowTree(WmWindow* window, |
| 277 bool remove_observer) { | 322 bool remove_observer) { |
| 278 DCHECK(window); | 323 DCHECK(window); |
| 324 if (IsHighlightingWindow(window)) | |
| 325 return; | |
| 326 | |
| 279 if (window->GetInternalWidget()) | 327 if (window->GetInternalWidget()) |
| 280 RemoveWidgetTree(window->GetInternalWidget(), remove_observer); | 328 RemoveWidgetTree(window->GetInternalWidget(), remove_observer); |
| 281 | 329 |
| 282 for (ash::WmWindow* child : window->GetChildren()) | 330 for (ash::WmWindow* child : window->GetChildren()) |
| 283 RemoveWindowTree(child, remove_observer); | 331 RemoveWindowTree(child, remove_observer); |
| 284 | 332 |
| 285 RemoveWindowNode(window, remove_observer); | 333 RemoveWindowNode(window, remove_observer); |
| 286 } | 334 } |
| 287 | 335 |
| 288 void AshDevToolsDOMAgent::RemoveWindowNode(WmWindow* window, | 336 void AshDevToolsDOMAgent::RemoveWindowNode(WmWindow* window, |
| (...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 373 | 421 |
| 374 NodeIdToViewMap::iterator node_id_to_view_it = | 422 NodeIdToViewMap::iterator node_id_to_view_it = |
| 375 node_id_to_view_map_.find(node_id); | 423 node_id_to_view_map_.find(node_id); |
| 376 DCHECK(node_id_to_view_it != node_id_to_view_map_.end()); | 424 DCHECK(node_id_to_view_it != node_id_to_view_map_.end()); |
| 377 | 425 |
| 378 view_to_node_id_map_.erase(view_to_node_id_it); | 426 view_to_node_id_map_.erase(view_to_node_id_it); |
| 379 node_id_to_view_map_.erase(node_id_to_view_it); | 427 node_id_to_view_map_.erase(node_id_to_view_it); |
| 380 frontend()->childNodeRemoved(parent_id, node_id); | 428 frontend()->childNodeRemoved(parent_id, node_id); |
| 381 } | 429 } |
| 382 | 430 |
| 431 void AshDevToolsDOMAgent::DestroyHighlightingWidget() { | |
| 432 DCHECK(main_thread_task_runner_->BelongsToCurrentThread()); | |
| 433 widget_for_highlighting_.reset(); | |
| 434 } | |
| 435 | |
| 383 void AshDevToolsDOMAgent::RemoveObservers() { | 436 void AshDevToolsDOMAgent::RemoveObservers() { |
| 384 for (auto& pair : window_to_node_id_map_) | 437 for (auto& pair : window_to_node_id_map_) |
| 385 pair.first->RemoveObserver(this); | 438 pair.first->RemoveObserver(this); |
| 386 for (auto& pair : widget_to_node_id_map_) | 439 for (auto& pair : widget_to_node_id_map_) |
| 387 pair.first->RemoveRemovalsObserver(this); | 440 pair.first->RemoveRemovalsObserver(this); |
| 388 for (auto& pair : view_to_node_id_map_) | 441 for (auto& pair : view_to_node_id_map_) |
| 389 pair.first->RemoveObserver(this); | 442 pair.first->RemoveObserver(this); |
| 390 } | 443 } |
| 391 | 444 |
| 392 void AshDevToolsDOMAgent::Reset() { | 445 void AshDevToolsDOMAgent::Reset() { |
| 393 RemoveObservers(); | 446 RemoveObservers(); |
| 447 main_thread_task_runner_->PostTask( | |
| 448 FROM_HERE, base::Bind(&AshDevToolsDOMAgent::DestroyHighlightingWidget, | |
| 449 base::Unretained(this))); | |
| 394 window_to_node_id_map_.clear(); | 450 window_to_node_id_map_.clear(); |
| 395 widget_to_node_id_map_.clear(); | 451 widget_to_node_id_map_.clear(); |
| 396 view_to_node_id_map_.clear(); | 452 view_to_node_id_map_.clear(); |
| 397 node_id_to_window_map_.clear(); | 453 node_id_to_window_map_.clear(); |
| 398 node_id_to_widget_map_.clear(); | 454 node_id_to_widget_map_.clear(); |
| 399 node_id_to_view_map_.clear(); | 455 node_id_to_view_map_.clear(); |
| 400 node_ids = 1; | 456 node_ids = 1; |
| 401 } | 457 } |
| 402 | 458 |
| 459 AshDevToolsDOMAgent::WindowAndBoundsPair | |
| 460 AshDevToolsDOMAgent::GetNodeWindowAndBounds(int node_id) { | |
| 461 WmWindow* window = GetWindowFromNodeId(node_id); | |
| 462 if (window) | |
| 463 return std::make_pair(window, window->GetBoundsInScreen()); | |
| 464 | |
| 465 views::Widget* widget = GetWidgetFromNodeId(node_id); | |
| 466 if (widget) { | |
| 467 return std::make_pair(WmLookup::Get()->GetWindowForWidget(widget), | |
| 468 widget->GetWindowBoundsInScreen()); | |
| 469 } | |
| 470 | |
| 471 views::View* view = GetViewFromNodeId(node_id); | |
| 472 if (view) { | |
| 473 gfx::Rect bounds = view->GetBoundsInScreen(); | |
| 474 while (view->parent() != nullptr) | |
| 475 view = view->parent(); | |
|
sadrul
2016/12/06 20:48:58
Why is this needed?
Sarmad Hashmi
2016/12/06 22:54:34
Sorry, thought an earlier bug I was encountering w
| |
| 476 return std::make_pair( | |
| 477 WmLookup::Get()->GetWindowForWidget(view->GetWidget()), bounds); | |
| 478 } | |
| 479 | |
| 480 return std::make_pair(nullptr, gfx::Rect()); | |
| 481 } | |
| 482 | |
| 483 void AshDevToolsDOMAgent::InitializeHighlightingWidget() { | |
| 484 DCHECK(!widget_for_highlighting_); | |
| 485 widget_for_highlighting_.reset(new views::Widget); | |
| 486 views::Widget::InitParams params; | |
| 487 params.type = views::Widget::InitParams::TYPE_WINDOW_FRAMELESS; | |
| 488 params.activatable = views::Widget::InitParams::ACTIVATABLE_NO; | |
| 489 params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; | |
| 490 params.opacity = views::Widget::InitParams::WindowOpacity::TRANSLUCENT_WINDOW; | |
| 491 params.name = "HighlightingWidget"; | |
| 492 shell_->GetPrimaryRootWindowController() | |
| 493 ->ConfigureWidgetInitParamsForContainer(widget_for_highlighting_.get(), | |
| 494 kShellWindowId_OverlayContainer, | |
| 495 ¶ms); | |
| 496 params.keep_on_top = true; | |
| 497 params.accept_events = false; | |
| 498 widget_for_highlighting_->Init(params); | |
| 499 } | |
| 500 | |
| 501 void AshDevToolsDOMAgent::UpdateHighlight(WindowAndBoundsPair window_and_bounds, | |
| 502 SkColor background, | |
| 503 SkColor border) { | |
| 504 constexpr int kBorderThickness = 1; | |
| 505 views::View* root_view = widget_for_highlighting_->GetRootView(); | |
| 506 root_view->SetBorder(views::CreateSolidBorder(kBorderThickness, border)); | |
| 507 root_view->set_background( | |
| 508 views::Background::CreateSolidBackground(background)); | |
| 509 WmLookup::Get() | |
| 510 ->GetWindowForWidget(widget_for_highlighting_.get()) | |
| 511 ->SetBoundsInScreen(window_and_bounds.second, | |
| 512 window_and_bounds.first->GetDisplayNearestWindow()); | |
|
sadrul
2016/12/06 20:48:59
Can you just SetBounds() on |widget_for_highlighti
Sarmad Hashmi
2016/12/06 22:54:34
We need to do SetBoundsInScreen so that we can han
| |
| 513 } | |
| 514 | |
| 515 void AshDevToolsDOMAgent::HighlightNode( | |
| 516 std::unique_ptr<ui::devtools::protocol::DOM::HighlightConfig> | |
| 517 highlight_config, | |
| 518 int node_id) { | |
| 519 DCHECK(main_thread_task_runner_->BelongsToCurrentThread()); | |
| 520 if (!widget_for_highlighting_) | |
| 521 InitializeHighlightingWidget(); | |
| 522 | |
| 523 WindowAndBoundsPair window_and_bounds(GetNodeWindowAndBounds(node_id)); | |
| 524 // Invalid node id provided | |
| 525 if (!window_and_bounds.first) | |
| 526 return; | |
| 527 | |
| 528 SkColor border_color = | |
| 529 RGBAToSkColor(highlight_config->getBorderColor(nullptr)); | |
| 530 SkColor content_color = | |
| 531 RGBAToSkColor(highlight_config->getContentColor(nullptr)); | |
| 532 UpdateHighlight(window_and_bounds, content_color, border_color); | |
| 533 | |
| 534 if (!widget_for_highlighting_->IsVisible()) | |
| 535 widget_for_highlighting_->Show(); | |
| 536 } | |
| 537 | |
| 538 bool AshDevToolsDOMAgent::IsHighlightingWindow(WmWindow* window) { | |
| 539 return widget_for_highlighting_ && | |
| 540 window->GetInternalWidget() == widget_for_highlighting_.get(); | |
| 541 } | |
| 542 | |
| 403 } // namespace devtools | 543 } // namespace devtools |
| 404 } // namespace ash | 544 } // namespace ash |
| OLD | NEW |