Commit 2854d5e5 authored by mhashmi's avatar mhashmi Committed by Commit bot

Hook up views/widgets to AshDevToolsDOMAgent

- This CL adds AshDevToolsDOMAgent as an observer to all widgets and views.
The WidgetRemovalObserver is specifically to remove the RootView subtree,
while the ViewObserver is to update any other descendant views.
AddViewTree was created so that whenever any |view| is added, it can
be pushed to the frontend.
- Add tests for when views are inserted, removed, rearranged,
removed & inserted.
- Add test to check if a widget owned by a native widget is represented
properly in the DOM.

BUG=648701

Review-Url: https://codereview.chromium.org/2537163002
Cr-Commit-Position: refs/heads/master@{#435065}
parent 0b12a6f8
...@@ -66,6 +66,19 @@ WmWindow* FindPreviousSibling(WmWindow* window) { ...@@ -66,6 +66,19 @@ WmWindow* FindPreviousSibling(WmWindow* window) {
return it == siblings.begin() ? nullptr : *std::prev(it); return it == siblings.begin() ? nullptr : *std::prev(it);
} }
views::View* FindPreviousSibling(views::View* view) {
views::View* parent = view->parent();
int view_index = -1;
for (int i = 0, count = parent->child_count(); i < count; i++) {
if (view == parent->child_at(i)) {
view_index = i;
break;
}
}
DCHECK_GE(view_index, 0);
return view_index == 0 ? nullptr : parent->child_at(view_index - 1);
}
} // namespace } // namespace
AshDevToolsDOMAgent::AshDevToolsDOMAgent(ash::WmShell* shell) : shell_(shell) { AshDevToolsDOMAgent::AshDevToolsDOMAgent(ash::WmShell* shell) : shell_(shell) {
...@@ -73,7 +86,7 @@ AshDevToolsDOMAgent::AshDevToolsDOMAgent(ash::WmShell* shell) : shell_(shell) { ...@@ -73,7 +86,7 @@ AshDevToolsDOMAgent::AshDevToolsDOMAgent(ash::WmShell* shell) : shell_(shell) {
} }
AshDevToolsDOMAgent::~AshDevToolsDOMAgent() { AshDevToolsDOMAgent::~AshDevToolsDOMAgent() {
RemoveObserverFromAllWindows(); RemoveObservers();
} }
ui::devtools::protocol::Response AshDevToolsDOMAgent::disable() { ui::devtools::protocol::Response AshDevToolsDOMAgent::disable() {
...@@ -118,6 +131,26 @@ void AshDevToolsDOMAgent::OnWindowStackingChanged(WmWindow* window) { ...@@ -118,6 +131,26 @@ void AshDevToolsDOMAgent::OnWindowStackingChanged(WmWindow* window) {
AddWindowTree(window); AddWindowTree(window);
} }
void AshDevToolsDOMAgent::OnWillRemoveView(views::Widget* widget,
views::View* view) {
if (view == widget->GetRootView())
RemoveViewTree(view, nullptr, true);
}
void AshDevToolsDOMAgent::OnChildViewRemoved(views::View* view,
views::View* parent) {
RemoveViewTree(view, parent, true);
}
void AshDevToolsDOMAgent::OnChildViewAdded(views::View* view) {
AddViewTree(view);
}
void AshDevToolsDOMAgent::OnChildViewReordered(views::View* view) {
RemoveViewTree(view, view->parent(), false);
AddViewTree(view);
}
WmWindow* AshDevToolsDOMAgent::GetWindowFromNodeId(int nodeId) { WmWindow* AshDevToolsDOMAgent::GetWindowFromNodeId(int nodeId) {
return node_id_to_window_map_.count(nodeId) ? node_id_to_window_map_[nodeId] return node_id_to_window_map_.count(nodeId) ? node_id_to_window_map_[nodeId]
: nullptr; : nullptr;
...@@ -182,7 +215,8 @@ std::unique_ptr<DOM::Node> AshDevToolsDOMAgent::BuildTreeForRootWidget( ...@@ -182,7 +215,8 @@ std::unique_ptr<DOM::Node> AshDevToolsDOMAgent::BuildTreeForRootWidget(
children->addItem(BuildTreeForView(widget->GetRootView())); children->addItem(BuildTreeForView(widget->GetRootView()));
std::unique_ptr<ui::devtools::protocol::DOM::Node> node = std::unique_ptr<ui::devtools::protocol::DOM::Node> node =
BuildNode("Widget", GetAttributes(widget), std::move(children)); BuildNode("Widget", GetAttributes(widget), std::move(children));
// TODO(mhashmi): Add WidgetRemovalsObserver here if (!widget->HasRemovalsObserver(this))
widget->AddRemovalsObserver(this);
widget_to_node_id_map_[widget] = node->getNodeId(); widget_to_node_id_map_[widget] = node->getNodeId();
node_id_to_widget_map_[node->getNodeId()] = widget; node_id_to_widget_map_[node->getNodeId()] = widget;
return node; return node;
...@@ -196,7 +230,8 @@ std::unique_ptr<DOM::Node> AshDevToolsDOMAgent::BuildTreeForView( ...@@ -196,7 +230,8 @@ std::unique_ptr<DOM::Node> AshDevToolsDOMAgent::BuildTreeForView(
children->addItem(BuildTreeForView(view->child_at(i))); children->addItem(BuildTreeForView(view->child_at(i)));
std::unique_ptr<ui::devtools::protocol::DOM::Node> node = std::unique_ptr<ui::devtools::protocol::DOM::Node> node =
BuildNode("View", GetAttributes(view), std::move(children)); BuildNode("View", GetAttributes(view), std::move(children));
// TODO(mhashmi): Add ViewObserver here if (!view->HasObserver(this))
view->AddObserver(this);
view_to_node_id_map_[view] = node->getNodeId(); view_to_node_id_map_[view] = node->getNodeId();
node_id_to_view_map_[node->getNodeId()] = view; node_id_to_view_map_[node->getNodeId()] = view;
return node; return node;
...@@ -238,6 +273,7 @@ void AshDevToolsDOMAgent::RemoveWindowNode(WmWindow* window, ...@@ -238,6 +273,7 @@ void AshDevToolsDOMAgent::RemoveWindowNode(WmWindow* window,
if (remove_observer) if (remove_observer)
window->RemoveObserver(this); window->RemoveObserver(this);
node_id_to_window_map_.erase(node_id_to_window_it); node_id_to_window_map_.erase(node_id_to_window_it);
window_to_node_id_map_.erase(window_to_node_id_it); window_to_node_id_map_.erase(window_to_node_id_it);
frontend()->childNodeRemoved(parent_id, node_id); frontend()->childNodeRemoved(parent_id, node_id);
...@@ -261,8 +297,8 @@ void AshDevToolsDOMAgent::RemoveWidgetNode(views::Widget* widget, ...@@ -261,8 +297,8 @@ void AshDevToolsDOMAgent::RemoveWidgetNode(views::Widget* widget,
int parent_id = int parent_id =
GetNodeIdFromWindow(WmLookup::Get()->GetWindowForWidget(widget)); GetNodeIdFromWindow(WmLookup::Get()->GetWindowForWidget(widget));
// TODO(mhashmi): Add WidgetRemovalsObserver and remove it here based on if (remove_observer)
// |remove_observer| widget->RemoveRemovalsObserver(this);
NodeIdToWidgetMap::iterator node_id_to_widget_it = NodeIdToWidgetMap::iterator node_id_to_widget_it =
node_id_to_widget_map_.find(node_id); node_id_to_widget_map_.find(node_id);
...@@ -273,6 +309,15 @@ void AshDevToolsDOMAgent::RemoveWidgetNode(views::Widget* widget, ...@@ -273,6 +309,15 @@ void AshDevToolsDOMAgent::RemoveWidgetNode(views::Widget* widget,
frontend()->childNodeRemoved(parent_id, node_id); frontend()->childNodeRemoved(parent_id, node_id);
} }
void AshDevToolsDOMAgent::AddViewTree(views::View* view) {
DCHECK(view_to_node_id_map_.count(view->parent()));
views::View* prev_sibling = FindPreviousSibling(view);
frontend()->childNodeInserted(
view_to_node_id_map_[view->parent()],
prev_sibling ? view_to_node_id_map_[prev_sibling] : 0,
BuildTreeForView(view));
}
void AshDevToolsDOMAgent::RemoveViewTree(views::View* view, void AshDevToolsDOMAgent::RemoveViewTree(views::View* view,
views::View* parent, views::View* parent,
bool remove_observer) { bool remove_observer) {
...@@ -296,8 +341,8 @@ void AshDevToolsDOMAgent::RemoveViewNode(views::View* view, ...@@ -296,8 +341,8 @@ void AshDevToolsDOMAgent::RemoveViewNode(views::View* view,
else // views::RootView else // views::RootView
parent_id = GetNodeIdFromWidget(view->GetWidget()); parent_id = GetNodeIdFromWidget(view->GetWidget());
// TODO(mhashmi): Add ViewObserver and remove it here based on if (remove_observer)
// |remove_observer| view->RemoveObserver(this);
NodeIdToViewMap::iterator node_id_to_view_it = NodeIdToViewMap::iterator node_id_to_view_it =
node_id_to_view_map_.find(node_id); node_id_to_view_map_.find(node_id);
...@@ -308,13 +353,17 @@ void AshDevToolsDOMAgent::RemoveViewNode(views::View* view, ...@@ -308,13 +353,17 @@ void AshDevToolsDOMAgent::RemoveViewNode(views::View* view,
frontend()->childNodeRemoved(parent_id, node_id); frontend()->childNodeRemoved(parent_id, node_id);
} }
void AshDevToolsDOMAgent::RemoveObserverFromAllWindows() { void AshDevToolsDOMAgent::RemoveObservers() {
for (auto& pair : window_to_node_id_map_) for (auto& pair : window_to_node_id_map_)
pair.first->RemoveObserver(this); pair.first->RemoveObserver(this);
for (auto& pair : widget_to_node_id_map_)
pair.first->RemoveRemovalsObserver(this);
for (auto& pair : view_to_node_id_map_)
pair.first->RemoveObserver(this);
} }
void AshDevToolsDOMAgent::Reset() { void AshDevToolsDOMAgent::Reset() {
RemoveObserverFromAllWindows(); RemoveObservers();
window_to_node_id_map_.clear(); window_to_node_id_map_.clear();
widget_to_node_id_map_.clear(); widget_to_node_id_map_.clear();
view_to_node_id_map_.clear(); view_to_node_id_map_.clear();
......
...@@ -11,7 +11,9 @@ ...@@ -11,7 +11,9 @@
#include "components/ui_devtools/DOM.h" #include "components/ui_devtools/DOM.h"
#include "components/ui_devtools/devtools_base_agent.h" #include "components/ui_devtools/devtools_base_agent.h"
#include "ui/views/view.h" #include "ui/views/view.h"
#include "ui/views/view_observer.h"
#include "ui/views/widget/widget.h" #include "ui/views/widget/widget.h"
#include "ui/views/widget/widget_removals_observer.h"
namespace ash { namespace ash {
namespace devtools { namespace devtools {
...@@ -19,7 +21,9 @@ namespace devtools { ...@@ -19,7 +21,9 @@ namespace devtools {
class ASH_EXPORT AshDevToolsDOMAgent class ASH_EXPORT AshDevToolsDOMAgent
: public NON_EXPORTED_BASE(ui::devtools::UiDevToolsBaseAgent< : public NON_EXPORTED_BASE(ui::devtools::UiDevToolsBaseAgent<
ui::devtools::protocol::DOM::Metainfo>), ui::devtools::protocol::DOM::Metainfo>),
public WmWindowObserver { public WmWindowObserver,
public views::WidgetRemovalsObserver,
public views::ViewObserver {
public: public:
explicit AshDevToolsDOMAgent(ash::WmShell* shell); explicit AshDevToolsDOMAgent(ash::WmShell* shell);
~AshDevToolsDOMAgent() override; ~AshDevToolsDOMAgent() override;
...@@ -36,6 +40,14 @@ class ASH_EXPORT AshDevToolsDOMAgent ...@@ -36,6 +40,14 @@ class ASH_EXPORT AshDevToolsDOMAgent
const TreeChangeParams& params) override; const TreeChangeParams& params) override;
void OnWindowStackingChanged(WmWindow* window) override; void OnWindowStackingChanged(WmWindow* window) override;
// views::WidgetRemovalsObserver
void OnWillRemoveView(views::Widget* widget, views::View* view) override;
// views::ViewObserver
void OnChildViewRemoved(views::View* view, views::View* parent) override;
void OnChildViewAdded(views::View* view) override;
void OnChildViewReordered(views::View*) override;
WmWindow* GetWindowFromNodeId(int nodeId); WmWindow* GetWindowFromNodeId(int nodeId);
views::Widget* GetWidgetFromNodeId(int nodeId); views::Widget* GetWidgetFromNodeId(int nodeId);
views::View* GetViewFromNodeId(int nodeId); views::View* GetViewFromNodeId(int nodeId);
...@@ -61,9 +73,12 @@ class ASH_EXPORT AshDevToolsDOMAgent ...@@ -61,9 +73,12 @@ class ASH_EXPORT AshDevToolsDOMAgent
void RemoveWindowTree(WmWindow* window, bool remove_observer); void RemoveWindowTree(WmWindow* window, bool remove_observer);
void RemoveWindowNode(WmWindow* window, bool remove_observer); void RemoveWindowNode(WmWindow* window, bool remove_observer);
// Don't need AddWidgetTree because |widget| will always be inside a window,
// so when windows are created, their widget nodes are created as well.
void RemoveWidgetTree(views::Widget* widget, bool remove_observer); void RemoveWidgetTree(views::Widget* widget, bool remove_observer);
void RemoveWidgetNode(views::Widget* widget, bool remove_observer); void RemoveWidgetNode(views::Widget* widget, bool remove_observer);
void AddViewTree(views::View* view);
void RemoveViewTree(views::View* view, void RemoveViewTree(views::View* view,
views::View* parent, views::View* parent,
bool remove_observer); bool remove_observer);
...@@ -71,7 +86,7 @@ class ASH_EXPORT AshDevToolsDOMAgent ...@@ -71,7 +86,7 @@ class ASH_EXPORT AshDevToolsDOMAgent
views::View* parent, views::View* parent,
bool remove_observer); bool remove_observer);
void RemoveObserverFromAllWindows(); void RemoveObservers();
void Reset(); void Reset();
ash::WmShell* shell_; ash::WmShell* shell_;
......
...@@ -6,9 +6,11 @@ ...@@ -6,9 +6,11 @@
#include "ash/common/test/ash_test.h" #include "ash/common/test/ash_test.h"
#include "ash/common/wm_lookup.h" #include "ash/common/wm_lookup.h"
#include "ash/common/wm_root_window_controller.h"
#include "ash/common/wm_shell.h" #include "ash/common/wm_shell.h"
#include "ash/common/wm_window.h" #include "ash/common/wm_window.h"
#include "base/strings/stringprintf.h" #include "base/strings/stringprintf.h"
#include "ui/views/widget/native_widget_private.h"
#include "ui/views/widget/widget.h" #include "ui/views/widget/widget.h"
namespace ash { namespace ash {
...@@ -120,6 +122,19 @@ class AshDevToolsTest : public AshTest { ...@@ -120,6 +122,19 @@ class AshDevToolsTest : public AshTest {
AshDevToolsTest() {} AshDevToolsTest() {}
~AshDevToolsTest() override {} ~AshDevToolsTest() override {}
views::internal::NativeWidgetPrivate* CreateTestNativeWidget() {
views::Widget* widget = new views::Widget;
views::Widget::InitParams params;
params.ownership = views::Widget::InitParams::NATIVE_WIDGET_OWNS_WIDGET;
WmShell::Get()
->GetPrimaryRootWindow()
->GetRootWindowController()
->ConfigureWidgetInitParamsForContainer(
widget, kShellWindowId_DefaultContainer, &params);
widget->Init(params);
return widget->native_widget_private();
}
void SetUp() override { void SetUp() override {
AshTest::SetUp(); AshTest::SetUp();
fake_frontend_channel_ = base::MakeUnique<FakeFrontendChannel>(); fake_frontend_channel_ = base::MakeUnique<FakeFrontendChannel>();
...@@ -196,6 +211,23 @@ TEST_F(AshDevToolsTest, GetDocumentWithWindowWidgetView) { ...@@ -196,6 +211,23 @@ TEST_F(AshDevToolsTest, GetDocumentWithWindowWidgetView) {
Compare(child_view, widget_children->get(0)->getChildren(nullptr)->get(1)); Compare(child_view, widget_children->get(0)->getChildren(nullptr)->get(1));
} }
TEST_F(AshDevToolsTest, GetDocumentNativeWidgetOwnsWidget) {
views::internal::NativeWidgetPrivate* native_widget_private =
CreateTestNativeWidget();
views::Widget* widget = native_widget_private->GetWidget();
WmWindow* parent_window = WmLookup::Get()->GetWindowForWidget(widget);
std::unique_ptr<ui::devtools::protocol::DOM::Node> root;
dom_agent()->getDocument(&root);
DOM::Node* parent_node = FindInRoot(parent_window, root.get());
ASSERT_TRUE(parent_node);
DOM::Node* widget_node = parent_node->getChildren(nullptr)->get(0);
Compare(widget, widget_node);
// Destroy NativeWidget followed by |widget|
widget->CloseNow();
}
TEST_F(AshDevToolsTest, WindowAddedChildNodeInserted) { TEST_F(AshDevToolsTest, WindowAddedChildNodeInserted) {
// Initialize DOMAgent // Initialize DOMAgent
std::unique_ptr<ui::devtools::protocol::DOM::Node> root; std::unique_ptr<ui::devtools::protocol::DOM::Node> root;
...@@ -311,4 +343,135 @@ TEST_F(AshDevToolsTest, WindowStackingChangedChildNodeRemovedAndInserted) { ...@@ -311,4 +343,135 @@ TEST_F(AshDevToolsTest, WindowStackingChangedChildNodeRemovedAndInserted) {
ExpectChildNodeInserted(parent_id, sibling_node->getNodeId()); ExpectChildNodeInserted(parent_id, sibling_node->getNodeId());
} }
TEST_F(AshDevToolsTest, ViewInserted) {
std::unique_ptr<views::Widget> widget(
CreateTestWidget(gfx::Rect(1, 1, 1, 1)));
WmWindow* window = WmLookup::Get()->GetWindowForWidget(widget.get());
widget->Show();
// Initialize DOMAgent
std::unique_ptr<ui::devtools::protocol::DOM::Node> root;
dom_agent()->getDocument(&root);
DOM::Node* parent_node = FindInRoot(window, root.get());
ASSERT_TRUE(parent_node);
DOM::Node* widget_node = parent_node->getChildren(nullptr)->get(0);
DOM::Node* root_view_node = widget_node->getChildren(nullptr)->get(0);
Array<DOM::Node>* root_view_children = root_view_node->getChildren(nullptr);
ASSERT_TRUE(root_view_children);
DOM::Node* sibling_view_node =
root_view_children->get(root_view_children->length() - 1);
widget->GetRootView()->AddChildView(new views::View);
ExpectChildNodeInserted(root_view_node->getNodeId(),
sibling_view_node->getNodeId());
}
TEST_F(AshDevToolsTest, ViewRemoved) {
std::unique_ptr<views::Widget> widget(
CreateTestWidget(gfx::Rect(1, 1, 1, 1)));
// Need to store |view| in unique_ptr because it is removed from the widget
// and needs to be destroyed independently
std::unique_ptr<views::View> child_view = base::MakeUnique<views::View>();
WmWindow* window = WmLookup::Get()->GetWindowForWidget(widget.get());
widget->Show();
views::View* root_view = widget->GetRootView();
root_view->AddChildView(child_view.get());
// Initialize DOMAgent
std::unique_ptr<ui::devtools::protocol::DOM::Node> root;
dom_agent()->getDocument(&root);
DOM::Node* parent_node = FindInRoot(window, root.get());
ASSERT_TRUE(parent_node);
DOM::Node* widget_node = parent_node->getChildren(nullptr)->get(0);
DOM::Node* root_view_node = widget_node->getChildren(nullptr)->get(0);
Array<DOM::Node>* root_view_children = root_view_node->getChildren(nullptr);
ASSERT_TRUE(root_view_children);
DOM::Node* child_view_node =
root_view_children->get(root_view_children->length() - 1);
Compare(child_view.get(), child_view_node);
root_view->RemoveChildView(child_view.get());
ExpectChildNodeRemoved(root_view_node->getNodeId(),
child_view_node->getNodeId());
}
TEST_F(AshDevToolsTest, ViewRearranged) {
std::unique_ptr<views::Widget> widget(
CreateTestWidget(gfx::Rect(1, 1, 1, 1)));
WmWindow* window = WmLookup::Get()->GetWindowForWidget(widget.get());
widget->Show();
views::View* root_view = widget->GetRootView();
views::View* parent_view = new views::View;
views::View* target_view = new views::View;
views::View* child_view = new views::View;
root_view->AddChildView(parent_view);
root_view->AddChildView(target_view);
parent_view->AddChildView(child_view);
// Initialize DOMAgent
std::unique_ptr<ui::devtools::protocol::DOM::Node> root;
dom_agent()->getDocument(&root);
DOM::Node* parent_node = FindInRoot(window, root.get());
ASSERT_TRUE(parent_node);
DOM::Node* widget_node = parent_node->getChildren(nullptr)->get(0);
DOM::Node* root_view_node = widget_node->getChildren(nullptr)->get(0);
Array<DOM::Node>* root_view_children = root_view_node->getChildren(nullptr);
ASSERT_TRUE(root_view_children);
size_t root_children_size = root_view_children->length();
ASSERT_TRUE(root_children_size >= 2);
DOM::Node* parent_view_node = root_view_children->get(root_children_size - 2);
DOM::Node* target_view_node = root_view_children->get(root_children_size - 1);
DOM::Node* child_view_node = parent_view_node->getChildren(nullptr)->get(0);
Compare(parent_view, parent_view_node);
Compare(target_view, target_view_node);
Compare(child_view, child_view_node);
target_view->AddChildView(child_view);
ExpectChildNodeRemoved(parent_view_node->getNodeId(),
child_view_node->getNodeId());
ExpectChildNodeInserted(target_view_node->getNodeId(), 0);
}
TEST_F(AshDevToolsTest, ViewRearrangedRemovedAndInserted) {
std::unique_ptr<views::Widget> widget(
CreateTestWidget(gfx::Rect(1, 1, 1, 1)));
WmWindow* window = WmLookup::Get()->GetWindowForWidget(widget.get());
widget->Show();
views::View* root_view = widget->GetRootView();
views::View* parent_view = new views::View;
views::View* target_view = new views::View;
views::View* child_view = new views::View;
root_view->AddChildView(parent_view);
root_view->AddChildView(target_view);
parent_view->AddChildView(child_view);
// Initialize DOMAgent
std::unique_ptr<ui::devtools::protocol::DOM::Node> root;
dom_agent()->getDocument(&root);
DOM::Node* parent_node = FindInRoot(window, root.get());
ASSERT_TRUE(parent_node);
DOM::Node* widget_node = parent_node->getChildren(nullptr)->get(0);
DOM::Node* root_view_node = widget_node->getChildren(nullptr)->get(0);
Array<DOM::Node>* root_view_children = root_view_node->getChildren(nullptr);
ASSERT_TRUE(root_view_children);
size_t root_children_size = root_view_children->length();
ASSERT_TRUE(root_children_size >= 2);
DOM::Node* parent_view_node = root_view_children->get(root_children_size - 2);
DOM::Node* target_view_node = root_view_children->get(root_children_size - 1);
DOM::Node* child_view_node = parent_view_node->getChildren(nullptr)->get(0);
Compare(parent_view, parent_view_node);
Compare(target_view, target_view_node);
Compare(child_view, child_view_node);
parent_view->RemoveChildView(child_view);
target_view->AddChildView(child_view);
ExpectChildNodeRemoved(parent_view_node->getNodeId(),
child_view_node->getNodeId());
ExpectChildNodeInserted(target_view_node->getNodeId(), 0);
}
} // namespace ash } // namespace ash
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment