css_agent_unittest.cc 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339
  1. // 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. #include "components/ui_devtools/css_agent.h"
  5. #include "base/strings/string_number_conversions.h"
  6. #include "base/strings/string_util.h"
  7. #include "base/test/task_environment.h"
  8. #include "components/ui_devtools/agent_util.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. namespace ui_devtools {
  14. class FakeUIElement : public UIElement {
  15. public:
  16. FakeUIElement(UIElementDelegate* ui_element_delegate)
  17. : UIElement(UIElementType::ROOT, ui_element_delegate, nullptr) {}
  18. ~FakeUIElement() override {}
  19. void GetBounds(gfx::Rect* bounds) const override { *bounds = bounds_; }
  20. void SetBounds(const gfx::Rect& bounds) override { bounds_ = bounds; }
  21. void GetVisible(bool* visible) const override { *visible = visible_; }
  22. void SetVisible(bool visible) override { visible_ = visible; }
  23. std::vector<std::string> GetAttributes() const override { return {}; }
  24. std::pair<gfx::NativeWindow, gfx::Rect> GetNodeWindowAndScreenBounds()
  25. const override {
  26. return {};
  27. }
  28. bool visible() const { return visible_; }
  29. gfx::Rect bounds() const { return bounds_; }
  30. void AddSource(std::string path, int line) {
  31. UIElement::AddSource(path, line);
  32. }
  33. private:
  34. gfx::Rect bounds_;
  35. bool visible_ = false;
  36. };
  37. class FakeDOMAgent : public DOMAgent {
  38. public:
  39. void OnUIElementAdded(UIElement* parent, UIElement* child) override {
  40. // nullptr root short circuits everything but adding |child|
  41. // to the node ID map, which is all we need here.
  42. DOMAgent::OnUIElementAdded(nullptr, child);
  43. }
  44. std::unique_ptr<protocol::DOM::Node> BuildTreeForUIElement(
  45. UIElement* root) override {
  46. return BuildDomNodeFromUIElement(root);
  47. }
  48. std::vector<UIElement*> CreateChildrenForRoot() override { return {}; }
  49. };
  50. class CSSAgentTest : public testing::Test {
  51. public:
  52. void SetUp() override {
  53. testing::Test::SetUp();
  54. frontend_channel_ = std::make_unique<FakeFrontendChannel>();
  55. uber_dispatcher_ =
  56. std::make_unique<protocol::UberDispatcher>(frontend_channel_.get());
  57. dom_agent_ = std::make_unique<FakeDOMAgent>();
  58. dom_agent_->Init(uber_dispatcher_.get());
  59. css_agent_ = std::make_unique<CSSAgent>(dom_agent_.get());
  60. css_agent_->Init(uber_dispatcher_.get());
  61. css_agent_->enable();
  62. element_ = std::make_unique<FakeUIElement>(dom_agent_.get());
  63. element()->SetBaseStylesheetId(0);
  64. dom_agent_->OnUIElementAdded(nullptr, element_.get());
  65. }
  66. void TearDown() override {
  67. css_agent_.reset();
  68. dom_agent_.reset();
  69. uber_dispatcher_.reset();
  70. frontend_channel_.reset();
  71. element_.reset();
  72. testing::Test::TearDown();
  73. }
  74. std::string BuildStylesheetUId(int node_id, int stylesheet_id) {
  75. return base::NumberToString(node_id) + "_" +
  76. base::NumberToString(stylesheet_id);
  77. }
  78. protected:
  79. base::test::TaskEnvironment task_environment_;
  80. using StyleArray = protocol::Array<protocol::CSS::CSSStyle>;
  81. std::pair<bool, std::unique_ptr<StyleArray>>
  82. SetStyle(const std::string& style_text, int node_id, int stylesheet_id = 0) {
  83. auto edits = std::make_unique<
  84. protocol::Array<protocol::CSS::StyleDeclarationEdit>>();
  85. auto edit = protocol::CSS::StyleDeclarationEdit::create()
  86. .setStyleSheetId(BuildStylesheetUId(node_id, stylesheet_id))
  87. .setRange(protocol::CSS::SourceRange::create()
  88. .setStartLine(0)
  89. .setStartColumn(0)
  90. .setEndLine(0)
  91. .setEndColumn(0)
  92. .build())
  93. .setText(style_text)
  94. .build();
  95. edits->emplace_back(std::move(edit));
  96. std::unique_ptr<StyleArray> output;
  97. auto response = css_agent_->setStyleTexts(std::move(edits), &output);
  98. return {response.IsSuccess(), std::move(output)};
  99. }
  100. std::string GetValueForProperty(protocol::CSS::CSSStyle* style,
  101. const std::string& property_name) {
  102. for (const auto& property : *(style->getCssProperties())) {
  103. if (property->getName() == property_name) {
  104. return property->getValue();
  105. }
  106. }
  107. return std::string();
  108. }
  109. int GetStyleSheetChangedCount(std::string stylesheet_id) {
  110. return frontend_channel_->CountProtocolNotificationMessage(
  111. "{\"method\":\"CSS.styleSheetChanged\",\"params\":{"
  112. "\"styleSheetId\":\"" +
  113. stylesheet_id + "\"}}");
  114. }
  115. std::pair<bool, std::string> GetSourceForElement() {
  116. std::string output;
  117. auto response = css_agent_->getStyleSheetText(
  118. BuildStylesheetUId(element()->node_id(), 0), &output);
  119. return {response.IsSuccess(), output};
  120. }
  121. CSSAgent* css_agent() { return css_agent_.get(); }
  122. DOMAgent* dom_agent() { return dom_agent_.get(); }
  123. FakeUIElement* element() { return element_.get(); }
  124. private:
  125. std::unique_ptr<CSSAgent> css_agent_;
  126. std::unique_ptr<DOMAgent> dom_agent_;
  127. std::unique_ptr<FakeFrontendChannel> frontend_channel_;
  128. std::unique_ptr<protocol::UberDispatcher> uber_dispatcher_;
  129. std::unique_ptr<FakeUIElement> element_;
  130. };
  131. TEST_F(CSSAgentTest, UnrecognizedNodeFails) {
  132. EXPECT_FALSE(SetStyle("x : 25", 42).first);
  133. }
  134. TEST_F(CSSAgentTest, UnrecognizedKeyFails) {
  135. element()->SetVisible(true);
  136. element()->SetBounds(gfx::Rect(1, 2, 3, 4));
  137. auto result = SetStyle("nonsense : 3.14", element()->node_id());
  138. EXPECT_FALSE(result.first);
  139. EXPECT_FALSE(result.second);
  140. // Ensure element didn't change.
  141. EXPECT_TRUE(element()->visible());
  142. EXPECT_EQ(element()->bounds(), gfx::Rect(1, 2, 3, 4));
  143. }
  144. TEST_F(CSSAgentTest, UnrecognizedValueFails) {
  145. element()->SetVisible(true);
  146. element()->SetBounds(gfx::Rect(1, 2, 3, 4));
  147. auto result = SetStyle("visibility : banana", element()->node_id());
  148. EXPECT_FALSE(result.first);
  149. EXPECT_FALSE(result.second);
  150. // Ensure element didn't change.
  151. EXPECT_TRUE(element()->visible());
  152. EXPECT_EQ(element()->bounds(), gfx::Rect(1, 2, 3, 4));
  153. }
  154. TEST_F(CSSAgentTest, BadInputsFail) {
  155. element()->SetVisible(true);
  156. element()->SetBounds(gfx::Rect(1, 2, 3, 4));
  157. // Input with no property name.
  158. auto result = SetStyle(": 1;", element()->node_id());
  159. EXPECT_FALSE(result.first);
  160. EXPECT_FALSE(result.second);
  161. // Ensure element didn't change.
  162. EXPECT_TRUE(element()->visible());
  163. EXPECT_EQ(element()->bounds(), gfx::Rect(1, 2, 3, 4));
  164. // Input with no property value.
  165. result = SetStyle("visibility:", element()->node_id());
  166. EXPECT_FALSE(result.first);
  167. EXPECT_FALSE(result.second);
  168. // Ensure element didn't change.
  169. EXPECT_TRUE(element()->visible());
  170. EXPECT_EQ(element()->bounds(), gfx::Rect(1, 2, 3, 4));
  171. // Blank input.
  172. result = SetStyle(":", element()->node_id());
  173. EXPECT_FALSE(result.first);
  174. EXPECT_FALSE(result.second);
  175. // Ensure element didn't change.
  176. EXPECT_TRUE(element()->visible());
  177. EXPECT_EQ(element()->bounds(), gfx::Rect(1, 2, 3, 4));
  178. }
  179. TEST_F(CSSAgentTest, SettingVisibility) {
  180. element()->SetVisible(false);
  181. DCHECK(!element()->visible());
  182. auto result = SetStyle("visibility: 1", element()->node_id());
  183. EXPECT_TRUE(result.first);
  184. EXPECT_TRUE(element()->visible());
  185. EXPECT_EQ(result.second->size(), 1U);
  186. std::unique_ptr<protocol::CSS::CSSStyle>& style = result.second->at(0);
  187. EXPECT_EQ(style->getStyleSheetId("default"),
  188. base::NumberToString(element()->node_id()) + "_0");
  189. EXPECT_EQ(GetValueForProperty(style.get(), "visibility"), "true");
  190. }
  191. TEST_F(CSSAgentTest, SettingX) {
  192. DCHECK_EQ(element()->bounds().x(), 0);
  193. auto result = SetStyle("x: 500", element()->node_id());
  194. EXPECT_TRUE(result.first);
  195. EXPECT_EQ(element()->bounds().x(), 500);
  196. EXPECT_EQ(result.second->size(), 1U);
  197. std::unique_ptr<protocol::CSS::CSSStyle>& style = result.second->at(0);
  198. EXPECT_EQ(style->getStyleSheetId("default"),
  199. base::NumberToString(element()->node_id()) + "_0");
  200. EXPECT_EQ(GetValueForProperty(style.get(), "x"), "500");
  201. }
  202. TEST_F(CSSAgentTest, SettingY) {
  203. DCHECK_EQ(element()->bounds().y(), 0);
  204. auto result = SetStyle("y: 100", element()->node_id());
  205. EXPECT_TRUE(result.first);
  206. EXPECT_EQ(element()->bounds().y(), 100);
  207. EXPECT_EQ(result.second->size(), 1U);
  208. std::unique_ptr<protocol::CSS::CSSStyle>& style = result.second->at(0);
  209. EXPECT_EQ(style->getStyleSheetId("default"),
  210. base::NumberToString(element()->node_id()) + "_0");
  211. EXPECT_EQ(GetValueForProperty(style.get(), "y"), "100");
  212. }
  213. TEST_F(CSSAgentTest, SettingWidth) {
  214. DCHECK_EQ(element()->bounds().width(), 0);
  215. auto result = SetStyle("width: 20", element()->node_id());
  216. EXPECT_TRUE(result.first);
  217. EXPECT_EQ(element()->bounds().width(), 20);
  218. EXPECT_EQ(result.second->size(), 1U);
  219. std::unique_ptr<protocol::CSS::CSSStyle>& style = result.second->at(0);
  220. EXPECT_EQ(style->getStyleSheetId("default"),
  221. base::NumberToString(element()->node_id()) + "_0");
  222. EXPECT_EQ(GetValueForProperty(style.get(), "width"), "20");
  223. }
  224. TEST_F(CSSAgentTest, SettingHeight) {
  225. DCHECK_EQ(element()->bounds().height(), 0);
  226. auto result = SetStyle("height: 30", element()->node_id());
  227. EXPECT_TRUE(result.first);
  228. EXPECT_EQ(element()->bounds().height(), 30);
  229. EXPECT_EQ(result.second->size(), 1U);
  230. std::unique_ptr<protocol::CSS::CSSStyle>& style = result.second->at(0);
  231. EXPECT_EQ(style->getStyleSheetId("default"),
  232. base::NumberToString(element()->node_id()) + "_0");
  233. EXPECT_EQ(GetValueForProperty(style.get(), "height"), "30");
  234. }
  235. TEST_F(CSSAgentTest, SettingAll) {
  236. DCHECK(element()->bounds() == gfx::Rect());
  237. element()->SetVisible(true);
  238. DCHECK(element()->visible());
  239. // Throw in odd whitespace while we're at it.
  240. std::string new_text(
  241. "\ny: 25; width: 50; visibility:0; height: 30;\nx: 9000;\n\n");
  242. auto result = SetStyle(new_text, element()->node_id());
  243. EXPECT_TRUE(result.first);
  244. EXPECT_EQ(element()->bounds(), gfx::Rect(9000, 25, 50, 30));
  245. EXPECT_FALSE(element()->visible());
  246. EXPECT_EQ(result.second->size(), 1U);
  247. std::unique_ptr<protocol::CSS::CSSStyle>& style = result.second->at(0);
  248. EXPECT_EQ(style->getStyleSheetId("default"),
  249. base::NumberToString(element()->node_id()) + "_0");
  250. EXPECT_EQ(GetValueForProperty(style.get(), "x"), "9000");
  251. EXPECT_EQ(GetValueForProperty(style.get(), "y"), "25");
  252. EXPECT_EQ(GetValueForProperty(style.get(), "width"), "50");
  253. EXPECT_EQ(GetValueForProperty(style.get(), "height"), "30");
  254. EXPECT_EQ(GetValueForProperty(style.get(), "visibility"), "false");
  255. }
  256. TEST_F(CSSAgentTest, UpdateOnBoundsChange) {
  257. FakeUIElement another_element(dom_agent());
  258. another_element.SetBaseStylesheetId(0);
  259. std::string element_stylesheet_id =
  260. BuildStylesheetUId(element()->node_id(), 0);
  261. std::string another_element_stylesheet_id =
  262. BuildStylesheetUId(another_element.node_id(), 0);
  263. EXPECT_EQ(0, GetStyleSheetChangedCount(element_stylesheet_id));
  264. EXPECT_EQ(0, GetStyleSheetChangedCount(another_element_stylesheet_id));
  265. css_agent()->OnElementBoundsChanged(element());
  266. EXPECT_EQ(1, GetStyleSheetChangedCount(element_stylesheet_id));
  267. EXPECT_EQ(0, GetStyleSheetChangedCount(another_element_stylesheet_id));
  268. css_agent()->OnElementBoundsChanged(&another_element);
  269. EXPECT_EQ(1, GetStyleSheetChangedCount(element_stylesheet_id));
  270. EXPECT_EQ(1, GetStyleSheetChangedCount(another_element_stylesheet_id));
  271. css_agent()->OnElementBoundsChanged(&another_element);
  272. EXPECT_EQ(1, GetStyleSheetChangedCount(element_stylesheet_id));
  273. EXPECT_EQ(2, GetStyleSheetChangedCount(another_element_stylesheet_id));
  274. }
  275. TEST_F(CSSAgentTest, GetSource) {
  276. // Tests CSSAgent::getStyleSheetText() with one source file.
  277. std::string file = "components/test/data/ui_devtools/test_file.cc";
  278. element()->AddSource(file, 0);
  279. auto result = GetSourceForElement();
  280. // Ensure that test_file.cc was read in correctly.
  281. EXPECT_TRUE(result.first);
  282. EXPECT_NE(std::string::npos,
  283. result.second.find("This file is for testing GetSource."));
  284. }
  285. TEST_F(CSSAgentTest, BadPathFails) {
  286. element()->AddSource("not/a/real/path", 0);
  287. auto result = GetSourceForElement();
  288. EXPECT_FALSE(result.first);
  289. EXPECT_EQ(result.second, "");
  290. }
  291. } // namespace ui_devtools