Skip to content

Commit

Permalink
UIDevtools: Implement query by element identifier
Browse files Browse the repository at this point in the history
This CL re-implement id query using Element Identifier, which is the new
and recommended way for tagging UI element as opposed to using
View.GetID()/SetID()

Doc: https://docs.google.com/document/d/1zcVPqvatmAyNZQZOT_yL8Goj1xckt0eUfAGEGEoOf9g/edit?usp=sharing&resourcekey=0-RJywpBq5u1XqweomTY-NWA
Discussion: https://groups.google.com/a/google.com/g/chrome-desktop-ui/c/9H5BsM0X5_E/m/0yBiFb2YCQAJ?utm_medium=email&utm_source=footer
Bug: 1201243
Change-Id: I12d462fb39065c96724c748614a1cc9085bdf1d7
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3271498
Commit-Queue: Yuheng Huang <yuhengh@chromium.org>
Reviewed-by: Dana Fried <dfried@chromium.org>
Reviewed-by: Wei Li <weili@chromium.org>
Cr-Commit-Position: refs/heads/main@{#940482}
  • Loading branch information
Yuheng Huang authored and Chromium LUCI CQ committed Nov 10, 2021
1 parent 2f5270b commit d1d32e2
Show file tree
Hide file tree
Showing 10 changed files with 71 additions and 54 deletions.
1 change: 1 addition & 0 deletions components/ui_devtools/BUILD.gn
Expand Up @@ -96,6 +96,7 @@ component("ui_devtools") {
"//mojo/public/cpp/system",
"//net",
"//third_party/inspector_protocol:crdtp",
"//ui/base:base",
"//ui/gfx",
]

Expand Down
1 change: 1 addition & 0 deletions components/ui_devtools/DEPS
Expand Up @@ -5,6 +5,7 @@ include_rules = [
"+services/network/public/mojom",
"+third_party/blink/renderer/platform/inspector_protocol",
"+third_party/inspector_protocol",
"+ui/base/interaction",
"+ui/gfx",
"+services/tracing/public",
"+mojo/public",
Expand Down
73 changes: 37 additions & 36 deletions components/ui_devtools/dom_agent.cc
Expand Up @@ -11,9 +11,11 @@

#include "base/containers/adapters.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "components/ui_devtools/devtools_server.h"
#include "components/ui_devtools/root_element.h"
#include "components/ui_devtools/ui_element.h"
#include "ui/base/interaction/element_identifier.h"

namespace {

Expand All @@ -32,17 +34,15 @@ using ui_devtools::protocol::Response;

namespace {

bool FoundMatchInStylesProperty(const std::string& query,
ui_devtools::UIElement* node) {
bool FindMatchInStylesProperty(const std::string& query,
ui_devtools::UIElement* node) {
for (UIElement::ClassProperties& class_properties :
node->GetCustomPropertiesForMatchedStyle()) {
for (UIElement::UIProperty& ui_property : class_properties.properties_) {
protocol::String dataName = ui_property.name_;
protocol::String dataValue = ui_property.value_;
std::transform(dataName.begin(), dataName.end(), dataName.begin(),
::tolower);
std::transform(dataValue.begin(), dataValue.end(), dataValue.begin(),
::tolower);
dataName = base::ToLowerASCII(dataName);
dataValue = base::ToLowerASCII(dataValue);
protocol::String style_property_match = dataName + ": " + dataValue + ";";
if (style_property_match.find(query) != protocol::String::npos) {
return true;
Expand All @@ -52,24 +52,26 @@ bool FoundMatchInStylesProperty(const std::string& query,
return false;
}

bool FoundMatchByID(const std::string& query, ui_devtools::UIElement* node) {
return query == base::NumberToString(node->GetBackingElementID());
bool FindMatchByID(const std::string& query, ui_devtools::UIElement* node) {
auto identifier = ui::ElementIdentifier::FromName(query.c_str());
if (!identifier)
return false;
return node->FindMatchByElementID(identifier);
}

bool FoundMatchInDomProperties(const std::string& query,
const std::string& tag_name_query,
const std::string& attribute_query,
bool exact_attribute_match,
ui_devtools::UIElement* node) {
bool FindMatchInDomProperties(const std::string& query,
const std::string& tag_name_query,
const std::string& attribute_query,
bool exact_attribute_match,
ui_devtools::UIElement* node) {
protocol::String node_name = node->GetTypeName();
std::transform(node_name.begin(), node_name.end(), node_name.begin(),
::tolower);
node_name = base::ToLowerASCII(node_name);
if (node_name.find(query) != protocol::String::npos ||
node_name == tag_name_query) {
return true;
}
for (std::string& data : node->GetAttributes()) {
std::transform(data.begin(), data.end(), data.begin(), ::tolower);
data = base::ToLowerASCII(data);
if (data.find(query) != protocol::String::npos) {
return true;
}
Expand Down Expand Up @@ -280,34 +282,33 @@ void DOMAgent::Reset() {
}

DOMAgent::Query DOMAgent::PreprocessQuery(protocol::String query) {
std::transform(query.begin(), query.end(), query.begin(), ::tolower);
constexpr char kSearchStylesPanelKeyword[] = "style:";
protocol::String tag_name_query = query;
protocol::String attribute_query = query;
constexpr char kSearchIDKeyword[] = "id:";
bool exact_attribute_match = false;
if (query.find(kSearchIDKeyword) == 0) {
// remove whitespaces (if they exist) after the 'id:' keyword
base::TrimWhitespaceASCII(query.substr(strlen(kSearchIDKeyword)),
base::TrimPositions::TRIM_ALL, &query);
return Query(query, query, query, exact_attribute_match,
Query::QueryType::ID);
}

// If it's not query by id, transform query to lower case.
query = base::ToLowerASCII(query);

constexpr char kSearchStylesPanelKeyword[] = "style:";
// Temporary Solution: search on styles panel if the keyword 'style:'
// exists in the beginning of the query.
size_t style_keyword_pos = query.find(kSearchStylesPanelKeyword);
if (style_keyword_pos == 0) {
if (query.find(kSearchStylesPanelKeyword) == 0) {
// remove whitespaces (if they exist) after the 'style:' keyword
base::TrimWhitespaceASCII(query.substr(strlen(kSearchStylesPanelKeyword)),
base::TrimPositions::TRIM_ALL, &query);
return Query(query, tag_name_query, attribute_query, exact_attribute_match,
return Query(query, query, query, exact_attribute_match,
Query::QueryType::Style);
}

constexpr char kSearchIDKeyword[] = "id:";
style_keyword_pos = query.find(kSearchIDKeyword);
if (style_keyword_pos == 0) {
// remove whitespaces (if they exist) after the 'id:' keyword
base::TrimWhitespaceASCII(query.substr(strlen(kSearchIDKeyword)),
base::TrimPositions::TRIM_ALL, &query);
return Query(query, tag_name_query, attribute_query, exact_attribute_match,
Query::QueryType::ID);
}

// Preprocessing for normal dom search.
protocol::String tag_name_query = query;
protocol::String attribute_query = query;
unsigned query_length = query.length();
bool start_tag_found = !query.find('<');
bool end_tag_found = query.rfind('>') + 1 == query_length;
Expand Down Expand Up @@ -348,11 +349,11 @@ void DOMAgent::SearchDomTree(const DOMAgent::Query& query_data,
stack.push_back(child);
bool found_match = false;
if (query_data.query_type_ == Query::QueryType::Style)
found_match = FoundMatchInStylesProperty(query_data.query_, node);
found_match = FindMatchInStylesProperty(query_data.query_, node);
else if (query_data.query_type_ == Query::QueryType::ID)
found_match = FoundMatchByID(query_data.query_, node);
found_match = FindMatchByID(query_data.query_, node);
else
found_match = FoundMatchInDomProperties(
found_match = FindMatchInDomProperties(
query_data.query_, query_data.tag_name_query_,
query_data.attribute_query_, query_data.exact_attribute_match_, node);
if (found_match)
Expand Down
4 changes: 2 additions & 2 deletions components/ui_devtools/ui_element.cc
Expand Up @@ -149,8 +149,8 @@ std::vector<UIElement::Source> UIElement::GetSources() {
return sources_;
}

int UIElement::GetBackingElementID() {
return 0;
bool UIElement::FindMatchByElementID(const ui::ElementIdentifier& identifier) {
return false;
}

bool UIElement::DispatchMouseEvent(protocol::DOM::MouseEvent* event) {
Expand Down
9 changes: 5 additions & 4 deletions components/ui_devtools/ui_element.h
Expand Up @@ -13,6 +13,7 @@
#include "base/macros.h"
#include "components/ui_devtools/DOM.h"
#include "components/ui_devtools/devtools_export.h"
#include "ui/base/interaction/element_identifier.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/native_widget_types.h"

Expand Down Expand Up @@ -140,10 +141,10 @@ class UI_DEVTOOLS_EXPORT UIElement {
// Get the sources for the element.
std::vector<Source> GetSources();

// Get the ID of the backing UI element. This is used to locate
// a UIElement by ID set on the browser side and different than
// node_id().
virtual int GetBackingElementID();
// Whether the Element Identifier matches the backing UI element.
// This is used to locate a UIElement by Element Identifier set
// on the browser side and different than node_id().
virtual bool FindMatchByElementID(const ui::ElementIdentifier& identifier);

virtual bool DispatchMouseEvent(protocol::DOM::MouseEvent* event);

Expand Down
21 changes: 17 additions & 4 deletions components/ui_devtools/views/dom_agent_unittest.cc
Expand Up @@ -14,7 +14,9 @@
#include "components/ui_devtools/views/overlay_agent_views.h"
#include "components/ui_devtools/views/view_element.h"
#include "components/ui_devtools/views/widget_element.h"
#include "ui/base/interaction/element_identifier.h"
#include "ui/views/test/views_test_base.h"
#include "ui/views/view_class_properties.h"
#include "ui/views/widget/native_widget_private.h"
#include "ui/views/widget/widget.h"

Expand Down Expand Up @@ -722,10 +724,13 @@ TEST_F(DOMAgentTest, DomSearchForStylesPanel) {
EXPECT_TRUE(!node_ids);
}

TEST_F(DOMAgentTest, DomSearchForBackingElementID) {
DECLARE_ELEMENT_IDENTIFIER_VALUE(kTestElementID);
DEFINE_ELEMENT_IDENTIFIER_VALUE(kTestElementID);

TEST_F(DOMAgentTest, DomSearchForElementID) {
std::unique_ptr<views::Widget> widget = CreateTestWidget();
views::View* test_view = new views::View;
test_view->SetID(1001);
test_view->SetProperty(views::kElementIdentifierKey, kTestElementID);
widget->GetRootView()->AddChildView(test_view);

std::unique_ptr<DOM::Node> root;
Expand All @@ -736,14 +741,22 @@ TEST_F(DOMAgentTest, DomSearchForBackingElementID) {
std::unique_ptr<protocol::Array<int>> node_ids;

// Match ID for View element.
dom_agent()->performSearch("id: 1001", false, &search_id, &result_count);
dom_agent()->performSearch("id: kTestElementID", false, &search_id,
&result_count);
EXPECT_EQ(result_count, 1);
dom_agent()->getSearchResults(search_id, 0, result_count, &node_ids);
EXPECT_EQ(node_ids->size(), 1u);
node_ids.reset();

// Won't match substring of ID for View element.
dom_agent()->performSearch("id: 100", false, &search_id, &result_count);
dom_agent()->performSearch("id: kTestElement", false, &search_id,
&result_count);
EXPECT_EQ(result_count, 0);
dom_agent()->getSearchResults(search_id, 0, 1, &node_ids);
EXPECT_TRUE(!node_ids);

// Won't match empty query.
dom_agent()->performSearch("id:", false, &search_id, &result_count);
EXPECT_EQ(result_count, 0);
dom_agent()->getSearchResults(search_id, 0, 1, &node_ids);
EXPECT_TRUE(!node_ids);
Expand Down
9 changes: 7 additions & 2 deletions components/ui_devtools/views/view_element.cc
Expand Up @@ -13,9 +13,11 @@
#include "components/ui_devtools/ui_element_delegate.h"
#include "components/ui_devtools/views/devtools_event_util.h"
#include "components/ui_devtools/views/element_utility.h"
#include "ui/base/interaction/element_tracker.h"
#include "ui/base/metadata/metadata_types.h"
#include "ui/gfx/color_utils.h"
#include "ui/views/controls/textfield/textfield.h"
#include "ui/views/interaction/element_tracker_views.h"
#include "ui/views/view_utils.h"
#include "ui/views/widget/widget.h"

Expand Down Expand Up @@ -166,8 +168,11 @@ void ViewElement::PaintRect() const {
view()->SchedulePaint();
}

int ViewElement::GetBackingElementID() {
return view_->GetID();
bool ViewElement::FindMatchByElementID(
const ui::ElementIdentifier& identifier) {
auto result = views::ElementTrackerViews::GetInstance()
->GetAllMatchingViewsInAnyContext(identifier);
return std::find(result.begin(), result.end(), view_) != result.end();
}

bool ViewElement::DispatchMouseEvent(protocol::DOM::MouseEvent* event) {
Expand Down
2 changes: 1 addition & 1 deletion components/ui_devtools/views/view_element.h
Expand Up @@ -42,7 +42,7 @@ class ViewElement : public views::ViewObserver, public UIElementWithMetaData {
const override;
static views::View* From(const UIElement* element);
void PaintRect() const override;
int GetBackingElementID() override;
bool FindMatchByElementID(const ui::ElementIdentifier& identifier) override;
bool DispatchMouseEvent(protocol::DOM::MouseEvent* event) override;
bool DispatchKeyEvent(protocol::DOM::KeyEvent* event) override;

Expand Down
4 changes: 0 additions & 4 deletions components/ui_devtools/views/window_element.cc
Expand Up @@ -137,10 +137,6 @@ void WindowElement::InitSources() {
AddSource("ui/aura/window.h", 0);
}

int WindowElement::GetBackingElementID() {
return window_->GetId();
}

bool WindowElement::DispatchKeyEvent(protocol::DOM::KeyEvent* event) {
ui::KeyEvent key_event = ConvertToUIKeyEvent(event);
// Key events are processed differently based on classes. Character events are
Expand Down
1 change: 0 additions & 1 deletion components/ui_devtools/views/window_element.h
Expand Up @@ -44,7 +44,6 @@ class WindowElement : public aura::WindowObserver,
std::vector<std::string> GetAttributes() const override;
std::pair<gfx::NativeWindow, gfx::Rect> GetNodeWindowAndScreenBounds()
const override;
int GetBackingElementID() override;
bool DispatchKeyEvent(protocol::DOM::KeyEvent* event) override;

static aura::Window* From(const UIElement* element);
Expand Down

0 comments on commit d1d32e2

Please sign in to comment.