blob: 320937d376366c71987b15293fbea73723ecced0 [file] [log] [blame]
Leonard Greybc2b3832018-09-19 15:15:211// Copyright 2018 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "components/ui_devtools/css_agent.h"
6
7#include "base/strings/string_util.h"
8#include "base/strings/stringprintf.h"
9#include "components/ui_devtools/dom_agent.h"
10#include "components/ui_devtools/ui_devtools_unittest_utils.h"
11#include "components/ui_devtools/ui_element.h"
12#include "testing/gtest/include/gtest/gtest.h"
13
14namespace ui_devtools {
15
16class FakeUIElement : public UIElement {
17 public:
18 FakeUIElement(UIElementDelegate* ui_element_delegate)
19 : UIElement(UIElementType::ROOT, ui_element_delegate, nullptr) {}
20
21 ~FakeUIElement() override {}
22 // Return a vector of pairs of properties' names and values.
23 std::vector<std::pair<std::string, std::string>> GetCustomProperties()
24 const override {
25 return {};
26 }
27 void GetBounds(gfx::Rect* bounds) const override { *bounds = bounds_; }
28 void SetBounds(const gfx::Rect& bounds) override { bounds_ = bounds; }
29 void GetVisible(bool* visible) const override { *visible = visible_; }
30 void SetVisible(bool visible) override { visible_ = visible; }
31
32 bool visible() const { return visible_; }
33 gfx::Rect bounds() const { return bounds_; }
34
35 std::pair<gfx::NativeWindow, gfx::Rect> GetNodeWindowAndBounds()
36 const override {
37 return {nullptr, gfx::Rect()};
38 }
39
40 std::unique_ptr<protocol::Array<std::string>> GetAttributes() const override {
41 return protocol::Array<std::string>::create();
42 }
43
44 private:
45 gfx::Rect bounds_;
46 bool visible_;
47};
48
49class FakeDOMAgent : public DOMAgent {
50 public:
51 void OnUIElementAdded(UIElement* parent, UIElement* child) override {
52 // nullptr root short circuits everything but adding |child|
53 // to the node ID map, which is all we need here.
54 DOMAgent::OnUIElementAdded(nullptr, child);
55 }
56
57 std::unique_ptr<protocol::DOM::Node> BuildTreeForUIElement(
58 UIElement* root) override {
59 return BuildDomNodeFromUIElement(root);
60 }
61
62 std::vector<UIElement*> CreateChildrenForRoot() override { return {}; }
63};
64
65class CSSAgentTest : public testing::Test {
66 public:
67 void SetUp() override {
68 testing::Test::SetUp();
69 frontend_channel_ = std::make_unique<FakeFrontendChannel>();
70 uber_dispatcher_ =
71 std::make_unique<protocol::UberDispatcher>(frontend_channel_.get());
72 dom_agent_ = std::make_unique<FakeDOMAgent>();
73 dom_agent_->Init(uber_dispatcher_.get());
74 css_agent_ = std::make_unique<CSSAgent>(dom_agent_.get());
75 css_agent_->Init(uber_dispatcher_.get());
76 css_agent_->enable();
77 element_ = std::make_unique<FakeUIElement>(dom_agent_.get());
78 dom_agent_->OnUIElementAdded(nullptr, element_.get());
79 }
80
81 protected:
82 using StyleArray = protocol::Array<protocol::CSS::CSSStyle>;
83
84 std::pair<bool, std::unique_ptr<StyleArray>> SetStyle(
85 const std::string& style_text,
86 int node_id) {
87 auto edits = protocol::Array<protocol::CSS::StyleDeclarationEdit>::create();
88 auto edit = protocol::CSS::StyleDeclarationEdit::create()
89 .setStyleSheetId(base::IntToString(node_id))
Wei Lia7ff68ad2018-10-15 18:28:2290 .setRange(protocol::CSS::SourceRange::create()
91 .setStartLine(0)
92 .setStartColumn(0)
93 .setEndLine(0)
94 .setEndColumn(0)
95 .build())
Leonard Greybc2b3832018-09-19 15:15:2196 .setText(style_text)
97 .build();
98 edits->addItem(std::move(edit));
99 std::unique_ptr<StyleArray> output;
100 auto response = css_agent_->setStyleTexts(std::move(edits), &output);
101 return {response.isSuccess(), std::move(output)};
102 }
103
104 std::string GetValueForProperty(protocol::CSS::CSSStyle* style,
105 const std::string& property_name) {
106 auto* properties = style->getCssProperties();
107 for (size_t i = 0; i < properties->length(); ++i) {
108 auto* property = properties->get(i);
109 if (property->getName() == property_name) {
110 return property->getValue();
111 }
112 }
113 return std::string();
114 }
115 int GetStyleSheetChangedCount(int node_id) {
116 return frontend_channel_->CountProtocolNotificationMessage(
117 base::StringPrintf("{\"method\":\"CSS.styleSheetChanged\",\"params\":{"
118 "\"styleSheetId\":\"%d\"}}",
119 node_id));
120 }
121
122 CSSAgent* css_agent() { return css_agent_.get(); }
123 DOMAgent* dom_agent() { return dom_agent_.get(); }
124 FakeUIElement* element() { return element_.get(); }
125
126 private:
127 std::unique_ptr<CSSAgent> css_agent_;
128 std::unique_ptr<DOMAgent> dom_agent_;
129 std::unique_ptr<FakeFrontendChannel> frontend_channel_;
130 std::unique_ptr<protocol::UberDispatcher> uber_dispatcher_;
131 std::unique_ptr<FakeUIElement> element_;
132};
133
134TEST_F(CSSAgentTest, UnrecognizedNodeFails) {
135 EXPECT_FALSE(SetStyle("x : 25", 42).first);
136}
137
138TEST_F(CSSAgentTest, UnrecognizedKeyFails) {
139 element()->SetVisible(true);
140 element()->SetBounds(gfx::Rect(1, 2, 3, 4));
141
142 auto result = SetStyle("nonsense : 3.14", element()->node_id());
143
144 EXPECT_FALSE(result.first);
145 EXPECT_FALSE(result.second);
146 // Ensure element didn't change.
147 EXPECT_TRUE(element()->visible());
148 EXPECT_EQ(element()->bounds(), gfx::Rect(1, 2, 3, 4));
149}
150
151TEST_F(CSSAgentTest, UnrecognizedValueFails) {
152 element()->SetVisible(true);
153 element()->SetBounds(gfx::Rect(1, 2, 3, 4));
154
155 auto result = SetStyle("visibility : banana", element()->node_id());
156 EXPECT_FALSE(result.first);
157 EXPECT_FALSE(result.second);
158 // Ensure element didn't change.
159 EXPECT_TRUE(element()->visible());
160 EXPECT_EQ(element()->bounds(), gfx::Rect(1, 2, 3, 4));
161}
162
163TEST_F(CSSAgentTest, SettingVisibility) {
164 element()->SetVisible(false);
165 DCHECK(!element()->visible());
166
167 auto result = SetStyle("visibility: 1", element()->node_id());
168 EXPECT_TRUE(result.first);
169 EXPECT_TRUE(element()->visible());
170
171 EXPECT_EQ(result.second->length(), 1U);
172 protocol::CSS::CSSStyle* style = result.second->get(0);
173 EXPECT_EQ(style->getStyleSheetId("default"),
174 base::IntToString(element()->node_id()));
175 EXPECT_EQ(GetValueForProperty(style, "visibility"), "1");
176}
177
178TEST_F(CSSAgentTest, SettingX) {
179 DCHECK_EQ(element()->bounds().x(), 0);
180
181 auto result = SetStyle("x: 500", element()->node_id());
182 EXPECT_TRUE(result.first);
183 EXPECT_EQ(element()->bounds().x(), 500);
184 EXPECT_EQ(result.second->length(), 1U);
185 protocol::CSS::CSSStyle* style = result.second->get(0);
186 EXPECT_EQ(style->getStyleSheetId("default"),
187 base::IntToString(element()->node_id()));
188 EXPECT_EQ(GetValueForProperty(style, "x"), "500");
189}
190
191TEST_F(CSSAgentTest, SettingY) {
192 DCHECK_EQ(element()->bounds().y(), 0);
193
194 auto result = SetStyle("y: 100", element()->node_id());
195 EXPECT_TRUE(result.first);
196 EXPECT_EQ(element()->bounds().y(), 100);
197 EXPECT_EQ(result.second->length(), 1U);
198 protocol::CSS::CSSStyle* style = result.second->get(0);
199 EXPECT_EQ(style->getStyleSheetId("default"),
200 base::IntToString(element()->node_id()));
201 EXPECT_EQ(GetValueForProperty(style, "y"), "100");
202}
203TEST_F(CSSAgentTest, SettingWidth) {
204 DCHECK_EQ(element()->bounds().width(), 0);
205
206 auto result = SetStyle("width: 20", element()->node_id());
207 EXPECT_TRUE(result.first);
208 EXPECT_EQ(element()->bounds().width(), 20);
209 EXPECT_EQ(result.second->length(), 1U);
210 protocol::CSS::CSSStyle* style = result.second->get(0);
211 EXPECT_EQ(style->getStyleSheetId("default"),
212 base::IntToString(element()->node_id()));
213 EXPECT_EQ(GetValueForProperty(style, "width"), "20");
214}
215TEST_F(CSSAgentTest, SettingHeight) {
216 DCHECK_EQ(element()->bounds().height(), 0);
217
218 auto result = SetStyle("height: 30", element()->node_id());
219 EXPECT_TRUE(result.first);
220 EXPECT_EQ(element()->bounds().height(), 30);
221 EXPECT_EQ(result.second->length(), 1U);
222 protocol::CSS::CSSStyle* style = result.second->get(0);
223 EXPECT_EQ(style->getStyleSheetId("default"),
224 base::IntToString(element()->node_id()));
225 EXPECT_EQ(GetValueForProperty(style, "height"), "30");
226}
227
228TEST_F(CSSAgentTest, SettingAll) {
229 DCHECK(element()->bounds() == gfx::Rect());
230 DCHECK(element()->visible());
231
232 // Throw in odd whitespace while we're at it.
233 std::string new_text(
234 "\ny: 25; width: 50; visibility:0; height: 30;\nx: 9000;\n\n");
235 auto result = SetStyle(new_text, element()->node_id());
236 EXPECT_TRUE(result.first);
237 EXPECT_EQ(element()->bounds(), gfx::Rect(9000, 25, 50, 30));
238 EXPECT_FALSE(element()->visible());
239 EXPECT_EQ(result.second->length(), 1U);
240 protocol::CSS::CSSStyle* style = result.second->get(0);
241 EXPECT_EQ(style->getStyleSheetId("default"),
242 base::IntToString(element()->node_id()));
243 EXPECT_EQ(GetValueForProperty(style, "x"), "9000");
244 EXPECT_EQ(GetValueForProperty(style, "y"), "25");
245 EXPECT_EQ(GetValueForProperty(style, "width"), "50");
246 EXPECT_EQ(GetValueForProperty(style, "height"), "30");
247 EXPECT_EQ(GetValueForProperty(style, "visibility"), "0");
248}
249
250TEST_F(CSSAgentTest, UpdateOnBoundsChange) {
251 FakeUIElement another_element(dom_agent());
252 EXPECT_EQ(0, GetStyleSheetChangedCount(element()->node_id()));
253 EXPECT_EQ(0, GetStyleSheetChangedCount(another_element.node_id()));
254 css_agent()->OnElementBoundsChanged(element());
255 EXPECT_EQ(1, GetStyleSheetChangedCount(element()->node_id()));
256 EXPECT_EQ(0, GetStyleSheetChangedCount(another_element.node_id()));
257 css_agent()->OnElementBoundsChanged(&another_element);
258 EXPECT_EQ(1, GetStyleSheetChangedCount(element()->node_id()));
259 EXPECT_EQ(1, GetStyleSheetChangedCount(another_element.node_id()));
260
261 css_agent()->OnElementBoundsChanged(&another_element);
262 EXPECT_EQ(1, GetStyleSheetChangedCount(element()->node_id()));
263 EXPECT_EQ(2, GetStyleSheetChangedCount(another_element.node_id()));
264}
265} // namespace ui_devtools