blob: 3aca3a71300416d15e84e9b21bb4a88592060d6a [file] [log] [blame]
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/ui_devtools/views/dom_agent.h"
#include <memory>
#include "components/ui_devtools/devtools_server.h"
#include "components/ui_devtools/views/root_element.h"
#include "components/ui_devtools/views/ui_element.h"
#include "components/ui_devtools/views/view_element.h"
#include "components/ui_devtools/views/widget_element.h"
#include "components/ui_devtools/views/window_element.h"
#include "ui/aura/env.h"
#include "ui/aura/window.h"
#include "ui/aura/window_tree_host.h"
#include "ui/views/view.h"
#include "ui/views/widget/widget.h"
#include "ui/wm/core/window_util.h"
namespace ui_devtools {
namespace {
using namespace ui_devtools::protocol;
// TODO(mhashmi): Make ids reusable
std::unique_ptr<DOM::Node> BuildNode(
const std::string& name,
std::unique_ptr<Array<std::string>> attributes,
std::unique_ptr<Array<DOM::Node>> children,
int node_ids) {
constexpr int kDomElementNodeType = 1;
std::unique_ptr<DOM::Node> node = DOM::Node::create()
.setNodeId(node_ids)
.setBackendNodeId(node_ids)
.setNodeName(name)
.setNodeType(kDomElementNodeType)
.setAttributes(std::move(attributes))
.build();
node->setChildNodeCount(static_cast<int>(children->length()));
node->setChildren(std::move(children));
return node;
}
views::Widget* GetWidgetFromWindow(gfx::NativeWindow window) {
return views::Widget::GetWidgetForNativeView(window);
}
std::unique_ptr<DOM::Node> BuildDomNodeFromUIElement(UIElement* root) {
std::unique_ptr<Array<DOM::Node>> children = Array<DOM::Node>::create();
for (auto* it : root->children())
children->addItem(BuildDomNodeFromUIElement(it));
return BuildNode(root->GetTypeName(), root->GetAttributes(),
std::move(children), root->node_id());
}
} // namespace
DOMAgent::DOMAgent() : is_building_tree_(false) {
aura::Env::GetInstance()->AddObserver(this);
}
DOMAgent::~DOMAgent() {
aura::Env::GetInstance()->RemoveObserver(this);
Reset();
}
Response DOMAgent::disable() {
Reset();
return Response::OK();
}
Response DOMAgent::getDocument(std::unique_ptr<DOM::Node>* out_root) {
*out_root = BuildInitialTree();
return Response::OK();
}
Response DOMAgent::hideHighlight() {
return Response::OK();
}
Response DOMAgent::pushNodesByBackendIdsToFrontend(
std::unique_ptr<protocol::Array<int>> backend_node_ids,
std::unique_ptr<protocol::Array<int>>* result) {
*result = protocol::Array<int>::create();
for (size_t index = 0; index < backend_node_ids->length(); ++index)
(*result)->addItem(backend_node_ids->get(index));
return Response::OK();
}
void DOMAgent::OnUIElementAdded(UIElement* parent, UIElement* child) {
// When parent is null, only need to update |node_id_to_ui_element_|.
if (!parent) {
node_id_to_ui_element_[child->node_id()] = child;
return;
}
// If tree is being built, don't add child to dom tree again.
if (is_building_tree_)
return;
DCHECK(node_id_to_ui_element_.count(parent->node_id()));
const auto& children = parent->children();
auto iter = std::find(children.begin(), children.end(), child);
int prev_node_id =
(iter == children.end() - 1) ? 0 : (*std::next(iter))->node_id();
frontend()->childNodeInserted(parent->node_id(), prev_node_id,
BuildTreeForUIElement(child));
}
void DOMAgent::OnUIElementReordered(UIElement* parent, UIElement* child) {
DCHECK(node_id_to_ui_element_.count(parent->node_id()));
const auto& children = parent->children();
auto iter = std::find(children.begin(), children.end(), child);
int prev_node_id =
(iter == children.begin()) ? 0 : (*std::prev(iter))->node_id();
RemoveDomNode(child);
frontend()->childNodeInserted(parent->node_id(), prev_node_id,
BuildDomNodeFromUIElement(child));
}
void DOMAgent::OnUIElementRemoved(UIElement* ui_element) {
DCHECK(node_id_to_ui_element_.count(ui_element->node_id()));
RemoveDomNode(ui_element);
node_id_to_ui_element_.erase(ui_element->node_id());
}
void DOMAgent::OnUIElementBoundsChanged(UIElement* ui_element) {
for (auto& observer : observers_)
observer.OnElementBoundsChanged(ui_element);
}
void DOMAgent::AddObserver(DOMAgentObserver* observer) {
observers_.AddObserver(observer);
}
void DOMAgent::RemoveObserver(DOMAgentObserver* observer) {
observers_.RemoveObserver(observer);
}
UIElement* DOMAgent::GetElementFromNodeId(int node_id) {
return node_id_to_ui_element_[node_id];
}
int DOMAgent::GetParentIdOfNodeId(int node_id) const {
DCHECK(node_id_to_ui_element_.count(node_id));
const UIElement* element = node_id_to_ui_element_.at(node_id);
if (element->parent() && element->parent() != element_root_.get())
return element->parent()->node_id();
return 0;
}
void DOMAgent::OnHostInitialized(aura::WindowTreeHost* host) {
root_windows_.push_back(host->window());
}
void DOMAgent::OnElementBoundsChanged(UIElement* ui_element) {
for (auto& observer : observers_)
observer.OnElementBoundsChanged(ui_element);
}
std::unique_ptr<DOM::Node> DOMAgent::BuildInitialTree() {
is_building_tree_ = true;
std::unique_ptr<Array<DOM::Node>> children = Array<DOM::Node>::create();
element_root_ = std::make_unique<RootElement>(this);
for (aura::Window* window : root_windows()) {
UIElement* window_element =
new WindowElement(window, this, element_root_.get());
children->addItem(BuildTreeForUIElement(window_element));
element_root_->AddChild(window_element);
}
std::unique_ptr<DOM::Node> root_node =
BuildNode("root", nullptr, std::move(children), element_root_->node_id());
is_building_tree_ = false;
return root_node;
}
std::unique_ptr<DOM::Node> DOMAgent::BuildTreeForUIElement(
UIElement* ui_element) {
if (ui_element->type() == UIElementType::WINDOW) {
return BuildTreeForWindow(
ui_element,
UIElement::GetBackingElement<aura::Window, WindowElement>(ui_element));
} else if (ui_element->type() == UIElementType::WIDGET) {
return BuildTreeForRootWidget(
ui_element,
UIElement::GetBackingElement<views::Widget, WidgetElement>(ui_element));
} else if (ui_element->type() == UIElementType::VIEW) {
return BuildTreeForView(
ui_element,
UIElement::GetBackingElement<views::View, ViewElement>(ui_element));
}
return nullptr;
}
std::unique_ptr<DOM::Node> DOMAgent::BuildTreeForWindow(
UIElement* window_element_root,
aura::Window* window) {
std::unique_ptr<Array<DOM::Node>> children = Array<DOM::Node>::create();
views::Widget* widget = GetWidgetFromWindow(window);
if (widget) {
UIElement* widget_element =
new WidgetElement(widget, this, window_element_root);
children->addItem(BuildTreeForRootWidget(widget_element, widget));
window_element_root->AddChild(widget_element);
}
for (aura::Window* child : window->children()) {
UIElement* window_element =
new WindowElement(child, this, window_element_root);
children->addItem(BuildTreeForWindow(window_element, child));
window_element_root->AddChild(window_element);
}
std::unique_ptr<DOM::Node> node =
BuildNode("Window", window_element_root->GetAttributes(),
std::move(children), window_element_root->node_id());
return node;
}
std::unique_ptr<DOM::Node> DOMAgent::BuildTreeForRootWidget(
UIElement* widget_element,
views::Widget* widget) {
std::unique_ptr<Array<DOM::Node>> children = Array<DOM::Node>::create();
UIElement* view_element =
new ViewElement(widget->GetRootView(), this, widget_element);
children->addItem(BuildTreeForView(view_element, widget->GetRootView()));
widget_element->AddChild(view_element);
std::unique_ptr<DOM::Node> node =
BuildNode("Widget", widget_element->GetAttributes(), std::move(children),
widget_element->node_id());
return node;
}
std::unique_ptr<DOM::Node> DOMAgent::BuildTreeForView(UIElement* view_element,
views::View* view) {
std::unique_ptr<Array<DOM::Node>> children = Array<DOM::Node>::create();
for (auto* child : view->GetChildrenInZOrder()) {
UIElement* view_element_child = new ViewElement(child, this, view_element);
children->addItem(BuildTreeForView(view_element_child, child));
view_element->AddChild(view_element_child);
}
std::unique_ptr<DOM::Node> node =
BuildNode("View", view_element->GetAttributes(), std::move(children),
view_element->node_id());
return node;
}
void DOMAgent::RemoveDomNode(UIElement* ui_element) {
for (auto* child_element : ui_element->children())
RemoveDomNode(child_element);
frontend()->childNodeRemoved(ui_element->parent()->node_id(),
ui_element->node_id());
}
void DOMAgent::Reset() {
is_building_tree_ = false;
element_root_.reset();
node_id_to_ui_element_.clear();
observers_.Clear();
}
} // namespace ui_devtools