1 // Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
2 // reserved. Use of this source code is governed by a BSD-style license that
3 // can be found in the LICENSE file.
4
5 #include "include/cef_dom.h"
6 #include "tests/ceftests/test_handler.h"
7 #include "tests/gtest/include/gtest/gtest.h"
8 #include "tests/shared/renderer/client_app_renderer.h"
9
10 using client::ClientAppRenderer;
11
12 namespace {
13
14 const char* kTestUrl = "http://tests/DOMTest.Test";
15 const char* kTestMessage = "DOMTest.Message";
16
17 enum DOMTestType {
18 DOM_TEST_STRUCTURE,
19 DOM_TEST_MODIFY,
20 };
21
22 class TestDOMVisitor : public CefDOMVisitor {
23 public:
TestDOMVisitor(CefRefPtr<CefBrowser> browser,DOMTestType test_type)24 explicit TestDOMVisitor(CefRefPtr<CefBrowser> browser, DOMTestType test_type)
25 : browser_(browser), test_type_(test_type) {}
26
TestHeadNodeStructure(CefRefPtr<CefDOMNode> headNode)27 void TestHeadNodeStructure(CefRefPtr<CefDOMNode> headNode) {
28 EXPECT_TRUE(headNode.get());
29 EXPECT_TRUE(headNode->IsElement());
30 EXPECT_FALSE(headNode->IsText());
31 EXPECT_EQ(headNode->GetName(), "HEAD");
32 EXPECT_EQ(headNode->GetElementTagName(), "HEAD");
33
34 EXPECT_TRUE(headNode->HasChildren());
35 EXPECT_FALSE(headNode->HasElementAttributes());
36
37 CefRefPtr<CefDOMNode> titleNode = headNode->GetFirstChild();
38 EXPECT_TRUE(titleNode.get());
39 EXPECT_TRUE(titleNode->IsElement());
40 EXPECT_FALSE(titleNode->IsText());
41 EXPECT_EQ(titleNode->GetName(), "TITLE");
42 EXPECT_EQ(titleNode->GetElementTagName(), "TITLE");
43 EXPECT_TRUE(titleNode->GetParent()->IsSame(headNode));
44
45 EXPECT_FALSE(titleNode->GetNextSibling().get());
46 EXPECT_FALSE(titleNode->GetPreviousSibling().get());
47 EXPECT_TRUE(titleNode->HasChildren());
48 EXPECT_FALSE(titleNode->HasElementAttributes());
49
50 CefRefPtr<CefDOMNode> textNode = titleNode->GetFirstChild();
51 EXPECT_TRUE(textNode.get());
52 EXPECT_FALSE(textNode->IsElement());
53 EXPECT_TRUE(textNode->IsText());
54 EXPECT_EQ(textNode->GetValue(), "The Title");
55 EXPECT_TRUE(textNode->GetParent()->IsSame(titleNode));
56
57 EXPECT_FALSE(textNode->GetNextSibling().get());
58 EXPECT_FALSE(textNode->GetPreviousSibling().get());
59 EXPECT_FALSE(textNode->HasChildren());
60 }
61
TestBodyNodeStructure(CefRefPtr<CefDOMNode> bodyNode)62 void TestBodyNodeStructure(CefRefPtr<CefDOMNode> bodyNode) {
63 EXPECT_TRUE(bodyNode.get());
64 EXPECT_TRUE(bodyNode->IsElement());
65 EXPECT_FALSE(bodyNode->IsText());
66 EXPECT_EQ(bodyNode->GetName(), "BODY");
67 EXPECT_EQ(bodyNode->GetElementTagName(), "BODY");
68
69 EXPECT_TRUE(bodyNode->HasChildren());
70 EXPECT_FALSE(bodyNode->HasElementAttributes());
71
72 CefRefPtr<CefDOMNode> h1Node = bodyNode->GetFirstChild();
73 EXPECT_TRUE(h1Node.get());
74 EXPECT_TRUE(h1Node->IsElement());
75 EXPECT_FALSE(h1Node->IsText());
76 EXPECT_EQ(h1Node->GetName(), "H1");
77 EXPECT_EQ(h1Node->GetElementTagName(), "H1");
78
79 EXPECT_TRUE(h1Node->GetNextSibling().get());
80 EXPECT_FALSE(h1Node->GetPreviousSibling().get());
81 EXPECT_TRUE(h1Node->HasChildren());
82 EXPECT_FALSE(h1Node->HasElementAttributes());
83
84 CefRefPtr<CefDOMNode> textNode = h1Node->GetFirstChild();
85 EXPECT_TRUE(textNode.get());
86 EXPECT_FALSE(textNode->IsElement());
87 EXPECT_TRUE(textNode->IsText());
88 EXPECT_EQ(textNode->GetValue(), "Hello From");
89
90 EXPECT_FALSE(textNode->GetPreviousSibling().get());
91 EXPECT_FALSE(textNode->HasChildren());
92
93 CefRefPtr<CefDOMNode> brNode = textNode->GetNextSibling();
94 EXPECT_TRUE(brNode.get());
95 EXPECT_TRUE(brNode->IsElement());
96 EXPECT_FALSE(brNode->IsText());
97 EXPECT_EQ(brNode->GetName(), "BR");
98 EXPECT_EQ(brNode->GetElementTagName(), "BR");
99
100 EXPECT_FALSE(brNode->HasChildren());
101
102 EXPECT_TRUE(brNode->HasElementAttributes());
103 EXPECT_TRUE(brNode->HasElementAttribute("class"));
104 EXPECT_EQ(brNode->GetElementAttribute("class"), "some_class");
105 EXPECT_TRUE(brNode->HasElementAttribute("id"));
106 EXPECT_EQ(brNode->GetElementAttribute("id"), "some_id");
107 EXPECT_FALSE(brNode->HasElementAttribute("no_existing"));
108
109 CefDOMNode::AttributeMap map;
110 brNode->GetElementAttributes(map);
111 ASSERT_EQ(map.size(), (size_t)2);
112 EXPECT_EQ(map["class"], "some_class");
113 EXPECT_EQ(map["id"], "some_id");
114
115 // Can also retrieve by ID.
116 brNode = bodyNode->GetDocument()->GetElementById("some_id");
117 EXPECT_TRUE(brNode.get());
118 EXPECT_TRUE(brNode->IsElement());
119 EXPECT_FALSE(brNode->IsText());
120 EXPECT_EQ(brNode->GetName(), "BR");
121 EXPECT_EQ(brNode->GetElementTagName(), "BR");
122
123 textNode = brNode->GetNextSibling();
124 EXPECT_TRUE(textNode.get());
125 EXPECT_FALSE(textNode->IsElement());
126 EXPECT_TRUE(textNode->IsText());
127 EXPECT_EQ(textNode->GetValue(), "Main Frame");
128
129 EXPECT_FALSE(textNode->GetNextSibling().get());
130 EXPECT_FALSE(textNode->HasChildren());
131
132 CefRefPtr<CefDOMNode> divNode = h1Node->GetNextSibling();
133 EXPECT_TRUE(divNode.get());
134 EXPECT_TRUE(divNode->IsElement());
135 EXPECT_FALSE(divNode->IsText());
136 CefRect divRect = divNode->GetElementBounds();
137 EXPECT_EQ(divRect.width, 50);
138 EXPECT_EQ(divRect.height, 25);
139 EXPECT_EQ(divRect.x, 150);
140 EXPECT_EQ(divRect.y, 100);
141 EXPECT_FALSE(divNode->GetNextSibling().get());
142 }
143
144 // Test document structure by iterating through the DOM tree.
TestStructure(CefRefPtr<CefDOMDocument> document)145 void TestStructure(CefRefPtr<CefDOMDocument> document) {
146 EXPECT_EQ(document->GetTitle(), "The Title");
147 EXPECT_EQ(document->GetBaseURL(), kTestUrl);
148 EXPECT_EQ(document->GetCompleteURL("foo.html"), "http://tests/foo.html");
149
150 // Navigate the complete document structure.
151 CefRefPtr<CefDOMNode> docNode = document->GetDocument();
152 EXPECT_TRUE(docNode.get());
153 EXPECT_FALSE(docNode->IsElement());
154 EXPECT_FALSE(docNode->IsText());
155
156 CefRefPtr<CefDOMNode> htmlNode = docNode->GetFirstChild();
157 EXPECT_TRUE(htmlNode.get());
158 EXPECT_TRUE(htmlNode->IsElement());
159 EXPECT_FALSE(htmlNode->IsText());
160 EXPECT_EQ(htmlNode->GetName(), "HTML");
161 EXPECT_EQ(htmlNode->GetElementTagName(), "HTML");
162
163 EXPECT_TRUE(htmlNode->HasChildren());
164 EXPECT_FALSE(htmlNode->HasElementAttributes());
165
166 CefRefPtr<CefDOMNode> headNode = htmlNode->GetFirstChild();
167 TestHeadNodeStructure(headNode);
168
169 CefRefPtr<CefDOMNode> bodyNode = headNode->GetNextSibling();
170 TestBodyNodeStructure(bodyNode);
171
172 // Retrieve the head node directly.
173 headNode = document->GetHead();
174 TestHeadNodeStructure(headNode);
175
176 // Retrieve the body node directly.
177 bodyNode = document->GetBody();
178 TestBodyNodeStructure(bodyNode);
179 }
180
181 // Test document modification by changing the H1 tag.
TestModify(CefRefPtr<CefDOMDocument> document)182 void TestModify(CefRefPtr<CefDOMDocument> document) {
183 CefRefPtr<CefDOMNode> bodyNode = document->GetBody();
184 CefRefPtr<CefDOMNode> h1Node = bodyNode->GetFirstChild();
185
186 ASSERT_EQ(h1Node->GetAsMarkup(),
187 "<h1>Hello From<br class=\"some_class\" id=\"some_id\">"
188 "Main Frame</h1>");
189
190 CefRefPtr<CefDOMNode> textNode = h1Node->GetFirstChild();
191 ASSERT_EQ(textNode->GetValue(), "Hello From");
192 ASSERT_TRUE(textNode->SetValue("A Different Message From"));
193 ASSERT_EQ(textNode->GetValue(), "A Different Message From");
194
195 CefRefPtr<CefDOMNode> brNode = textNode->GetNextSibling();
196 EXPECT_EQ(brNode->GetElementAttribute("class"), "some_class");
197 EXPECT_TRUE(brNode->SetElementAttribute("class", "a_different_class"));
198 EXPECT_EQ(brNode->GetElementAttribute("class"), "a_different_class");
199
200 ASSERT_EQ(h1Node->GetAsMarkup(),
201 "<h1>A Different Message From<br class=\"a_different_class\" "
202 "id=\"some_id\">Main Frame</h1>");
203
204 ASSERT_FALSE(h1Node->SetValue("Something Different"));
205 }
206
Visit(CefRefPtr<CefDOMDocument> document)207 void Visit(CefRefPtr<CefDOMDocument> document) override {
208 if (test_type_ == DOM_TEST_STRUCTURE)
209 TestStructure(document);
210 else if (test_type_ == DOM_TEST_MODIFY)
211 TestModify(document);
212
213 DestroyTest();
214 }
215
216 protected:
217 // Return from the test.
DestroyTest()218 void DestroyTest() {
219 // Check if the test has failed.
220 bool result = !TestFailed();
221
222 // Return the result to the browser process.
223 CefRefPtr<CefProcessMessage> return_msg =
224 CefProcessMessage::Create(kTestMessage);
225 EXPECT_TRUE(return_msg->GetArgumentList()->SetBool(0, result));
226 browser_->GetMainFrame()->SendProcessMessage(PID_BROWSER, return_msg);
227 }
228
229 CefRefPtr<CefBrowser> browser_;
230 DOMTestType test_type_;
231
232 IMPLEMENT_REFCOUNTING(TestDOMVisitor);
233 };
234
235 // Used in the render process.
236 class DOMRendererTest : public ClientAppRenderer::Delegate {
237 public:
DOMRendererTest()238 DOMRendererTest() {}
239
OnProcessMessageReceived(CefRefPtr<ClientAppRenderer> app,CefRefPtr<CefBrowser> browser,CefRefPtr<CefFrame> frame,CefProcessId source_process,CefRefPtr<CefProcessMessage> message)240 bool OnProcessMessageReceived(CefRefPtr<ClientAppRenderer> app,
241 CefRefPtr<CefBrowser> browser,
242 CefRefPtr<CefFrame> frame,
243 CefProcessId source_process,
244 CefRefPtr<CefProcessMessage> message) override {
245 if (message->GetName() == kTestMessage) {
246 EXPECT_EQ(message->GetArgumentList()->GetSize(), (size_t)1);
247 int test_type = message->GetArgumentList()->GetInt(0);
248
249 browser->GetMainFrame()->VisitDOM(
250 new TestDOMVisitor(browser, static_cast<DOMTestType>(test_type)));
251 return true;
252 }
253 return false;
254 }
255
256 private:
257 IMPLEMENT_REFCOUNTING(DOMRendererTest);
258 };
259
260 // Used in the browser process.
261 class TestDOMHandler : public TestHandler {
262 public:
TestDOMHandler(DOMTestType test)263 explicit TestDOMHandler(DOMTestType test) : test_type_(test) {}
264
RunTest()265 void RunTest() override {
266 std::stringstream mainHtml;
267 mainHtml << "<html>"
268 "<head><title>The Title</title></head>"
269 "<body>"
270 "<h1>Hello From<br class=\"some_class\"/ id=\"some_id\"/>"
271 "Main Frame</h1>"
272 "<div id=\"sized_element\" style=\"width: 50px; height: 25px; "
273 "position: fixed; top: 100px; left: 150px;\"/>"
274 "</body>"
275 "</html>";
276
277 AddResource(kTestUrl, mainHtml.str(), "text/html");
278 CreateBrowser(kTestUrl);
279
280 // Time out the test after a reasonable period of time.
281 SetTestTimeout();
282 }
283
OnLoadEnd(CefRefPtr<CefBrowser> browser,CefRefPtr<CefFrame> frame,int httpStatusCode)284 void OnLoadEnd(CefRefPtr<CefBrowser> browser,
285 CefRefPtr<CefFrame> frame,
286 int httpStatusCode) override {
287 if (frame->IsMain()) {
288 // Start the test in the render process.
289 CefRefPtr<CefProcessMessage> message(
290 CefProcessMessage::Create(kTestMessage));
291 message->GetArgumentList()->SetInt(0, test_type_);
292 frame->SendProcessMessage(PID_RENDERER, message);
293 }
294 }
295
OnProcessMessageReceived(CefRefPtr<CefBrowser> browser,CefRefPtr<CefFrame> frame,CefProcessId source_process,CefRefPtr<CefProcessMessage> message)296 bool OnProcessMessageReceived(CefRefPtr<CefBrowser> browser,
297 CefRefPtr<CefFrame> frame,
298 CefProcessId source_process,
299 CefRefPtr<CefProcessMessage> message) override {
300 EXPECT_STREQ(message->GetName().ToString().c_str(), kTestMessage);
301
302 got_message_.yes();
303
304 if (message->GetArgumentList()->GetBool(0))
305 got_success_.yes();
306
307 // Test is complete.
308 DestroyTest();
309
310 return true;
311 }
312
313 DOMTestType test_type_;
314 TrackCallback got_message_;
315 TrackCallback got_success_;
316
317 IMPLEMENT_REFCOUNTING(TestDOMHandler);
318 };
319
320 } // namespace
321
322 // Test DOM structure reading.
TEST(DOMTest,Read)323 TEST(DOMTest, Read) {
324 CefRefPtr<TestDOMHandler> handler = new TestDOMHandler(DOM_TEST_STRUCTURE);
325 handler->ExecuteTest();
326
327 EXPECT_TRUE(handler->got_message_);
328 EXPECT_TRUE(handler->got_success_);
329
330 ReleaseAndWaitForDestructor(handler);
331 }
332
333 // Test DOM modifications.
TEST(DOMTest,Modify)334 TEST(DOMTest, Modify) {
335 CefRefPtr<TestDOMHandler> handler = new TestDOMHandler(DOM_TEST_MODIFY);
336 handler->ExecuteTest();
337
338 EXPECT_TRUE(handler->got_message_);
339 EXPECT_TRUE(handler->got_success_);
340
341 ReleaseAndWaitForDestructor(handler);
342 }
343
344 // Entry point for creating DOM renderer test objects.
345 // Called from client_app_delegates.cc.
CreateDOMRendererTests(ClientAppRenderer::DelegateSet & delegates)346 void CreateDOMRendererTests(ClientAppRenderer::DelegateSet& delegates) {
347 delegates.insert(new DOMRendererTest);
348 }
349