1/*
2 * Copyright (C) 2013 Igalia S.L.
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU Library General Public License
15 * along with this library; see the file COPYING.LIB. If not, write to
16 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18 */
19
20#include "config.h"
21
22#include "WebProcessTest.h"
23#include <gio/gio.h>
24#include <webkit2/webkit-web-extension.h>
25#include <wtf/glib/GUniquePtr.h>
26
27G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
28
29class WebKitDOMNodeTest : public WebProcessTest {
30public:
31 static std::unique_ptr<WebProcessTest> create() { return std::unique_ptr<WebKitDOMNodeTest>(new WebKitDOMNodeTest()); }
32
33private:
34 bool testHierarchyNavigation(WebKitWebPage* page)
35 {
36 WebKitDOMDocument* document = webkit_web_page_get_dom_document(page);
37 g_assert_true(WEBKIT_DOM_IS_DOCUMENT(document));
38 assertObjectIsDeletedWhenTestFinishes(G_OBJECT(document));
39
40 WebKitDOMHTMLHeadElement* head = webkit_dom_document_get_head(document);
41 g_assert_true(WEBKIT_DOM_IS_HTML_HEAD_ELEMENT(head));
42 assertObjectIsDeletedWhenTestFinishes(G_OBJECT(head));
43
44 // Title, head's child.
45 g_assert_true(webkit_dom_node_has_child_nodes(WEBKIT_DOM_NODE(head)));
46 GRefPtr<WebKitDOMNodeList> list = adoptGRef(webkit_dom_node_get_child_nodes(WEBKIT_DOM_NODE(head)));
47 g_assert_true(WEBKIT_DOM_IS_NODE_LIST(list.get()));
48 assertObjectIsDeletedWhenTestFinishes(G_OBJECT(list.get()));
49 g_assert_cmpint(webkit_dom_node_list_get_length(list.get()), ==, 1);
50 WebKitDOMNode* node = webkit_dom_node_list_item(list.get(), 0);
51 g_assert_true(WEBKIT_DOM_IS_HTML_TITLE_ELEMENT(node));
52 assertObjectIsDeletedWhenTestFinishes(G_OBJECT(node));
53
54 // Body, Head sibling.
55 node = webkit_dom_node_get_next_sibling(WEBKIT_DOM_NODE(head));
56 g_assert_true(WEBKIT_DOM_IS_HTML_BODY_ELEMENT(node));
57 assertObjectIsDeletedWhenTestFinishes(G_OBJECT(node));
58 WebKitDOMHTMLBodyElement* body = WEBKIT_DOM_HTML_BODY_ELEMENT(node);
59
60 // There is no third sibling
61 g_assert_null(webkit_dom_node_get_next_sibling(node));
62
63 // Body's previous sibling is Head.
64 node = webkit_dom_node_get_previous_sibling(WEBKIT_DOM_NODE(body));
65 g_assert_true(WEBKIT_DOM_IS_HTML_HEAD_ELEMENT(node));
66 assertObjectIsDeletedWhenTestFinishes(G_OBJECT(node));
67
68 // Body has 3 children.
69 g_assert_true(webkit_dom_node_has_child_nodes(WEBKIT_DOM_NODE(body)));
70 list = adoptGRef(webkit_dom_node_get_child_nodes(WEBKIT_DOM_NODE(body)));
71 g_assert_true(WEBKIT_DOM_IS_NODE_LIST(list.get()));
72 assertObjectIsDeletedWhenTestFinishes(G_OBJECT(list.get()));
73 unsigned long length = webkit_dom_node_list_get_length(list.get());
74 g_assert_cmpint(length, ==, 3);
75
76 // The three of them are P tags.
77 for (unsigned long i = 0; i < length; i++) {
78 node = webkit_dom_node_list_item(list.get(), i);
79 g_assert_true(WEBKIT_DOM_IS_HTML_PARAGRAPH_ELEMENT(node));
80 assertObjectIsDeletedWhenTestFinishes(G_OBJECT(node));
81 }
82
83 // Go backwards
84 unsigned i;
85 for (i = 0; node; node = webkit_dom_node_get_previous_sibling(node), i++) { }
86 g_assert_cmpint(i, ==, 3);
87
88 return true;
89 }
90
91 bool testInsertion(WebKitWebPage* page)
92 {
93 WebKitDOMDocument* document = webkit_web_page_get_dom_document(page);
94 g_assert_true(WEBKIT_DOM_IS_DOCUMENT(document));
95 assertObjectIsDeletedWhenTestFinishes(G_OBJECT(document));
96
97 WebKitDOMHTMLElement* body = webkit_dom_document_get_body(document);
98 g_assert_true(WEBKIT_DOM_IS_HTML_ELEMENT(body));
99 assertObjectIsDeletedWhenTestFinishes(G_OBJECT(body));
100
101 // Body shouldn't have any children at this point.
102 g_assert_false(webkit_dom_node_has_child_nodes(WEBKIT_DOM_NODE(body)));
103
104 // The value of a non-existent attribute should be null, not an empty string
105 g_assert_null(webkit_dom_html_body_element_get_background(WEBKIT_DOM_HTML_BODY_ELEMENT(body)));
106
107 // Insert one P element.
108 WebKitDOMElement* p = webkit_dom_document_create_element(document, "P", 0);
109 g_assert_true(WEBKIT_DOM_IS_HTML_ELEMENT(p));
110 assertObjectIsDeletedWhenTestFinishes(G_OBJECT(p));
111 webkit_dom_node_append_child(WEBKIT_DOM_NODE(body), WEBKIT_DOM_NODE(p), 0);
112
113 // Now it should have one, the same that we inserted.
114 g_assert_true(webkit_dom_node_has_child_nodes(WEBKIT_DOM_NODE(body)));
115 GRefPtr<WebKitDOMNodeList> list = adoptGRef(webkit_dom_node_get_child_nodes(WEBKIT_DOM_NODE(body)));
116 g_assert_true(WEBKIT_DOM_IS_NODE_LIST(list.get()));
117 assertObjectIsDeletedWhenTestFinishes(G_OBJECT(list.get()));
118 g_assert_cmpint(webkit_dom_node_list_get_length(list.get()), ==, 1);
119 WebKitDOMNode* node = webkit_dom_node_list_item(list.get(), 0);
120 g_assert_true(WEBKIT_DOM_IS_HTML_ELEMENT(node));
121 assertObjectIsDeletedWhenTestFinishes(G_OBJECT(node));
122 g_assert_true(webkit_dom_node_is_same_node(WEBKIT_DOM_NODE(p), node));
123
124 // Replace the P tag with a DIV tag.
125 WebKitDOMElement* div = webkit_dom_document_create_element(document, "DIV", 0);
126 g_assert_true(WEBKIT_DOM_IS_HTML_ELEMENT(div));
127 assertObjectIsDeletedWhenTestFinishes(G_OBJECT(div));
128 webkit_dom_node_replace_child(WEBKIT_DOM_NODE(body), WEBKIT_DOM_NODE(div), WEBKIT_DOM_NODE(p), 0);
129 g_assert_true(webkit_dom_node_has_child_nodes(WEBKIT_DOM_NODE(body)));
130 list = adoptGRef(webkit_dom_node_get_child_nodes(WEBKIT_DOM_NODE(body)));
131 g_assert_true(WEBKIT_DOM_IS_NODE_LIST(list.get()));
132 assertObjectIsDeletedWhenTestFinishes(G_OBJECT(list.get()));
133 g_assert_cmpint(webkit_dom_node_list_get_length(list.get()), ==, 1);
134 node = webkit_dom_node_list_item(list.get(), 0);
135 g_assert_true(WEBKIT_DOM_IS_HTML_ELEMENT(node));
136 assertObjectIsDeletedWhenTestFinishes(G_OBJECT(node));
137 g_assert_true(webkit_dom_node_is_same_node(WEBKIT_DOM_NODE(div), node));
138
139 // Now remove the tag.
140 webkit_dom_node_remove_child(WEBKIT_DOM_NODE(body), node, 0);
141 list = adoptGRef(webkit_dom_node_get_child_nodes(WEBKIT_DOM_NODE(body)));
142 g_assert_true(WEBKIT_DOM_IS_NODE_LIST(list.get()));
143 assertObjectIsDeletedWhenTestFinishes(G_OBJECT(list.get()));
144 g_assert_cmpint(webkit_dom_node_list_get_length(list.get()), ==, 0);
145
146 // Test insert before. If refChild is null, insert newChild as last element of parent.
147 div = webkit_dom_document_create_element(document, "DIV", 0);
148 g_assert_true(WEBKIT_DOM_IS_HTML_ELEMENT(div));
149 assertObjectIsDeletedWhenTestFinishes(G_OBJECT(div));
150 webkit_dom_node_insert_before(WEBKIT_DOM_NODE(body), WEBKIT_DOM_NODE(div), 0, 0);
151 g_assert_true(webkit_dom_node_has_child_nodes(WEBKIT_DOM_NODE(body)));
152 list = adoptGRef(webkit_dom_node_get_child_nodes(WEBKIT_DOM_NODE(body)));
153 g_assert_true(WEBKIT_DOM_IS_NODE_LIST(list.get()));
154 assertObjectIsDeletedWhenTestFinishes(G_OBJECT(list.get()));
155 g_assert_cmpint(webkit_dom_node_list_get_length(list.get()), ==, 1);
156 node = webkit_dom_node_list_item(list.get(), 0);
157 g_assert_true(WEBKIT_DOM_IS_HTML_ELEMENT(node));
158 assertObjectIsDeletedWhenTestFinishes(G_OBJECT(node));
159 g_assert_true(webkit_dom_node_is_same_node(WEBKIT_DOM_NODE(div), node));
160
161 // Now insert a 'p' before 'div'.
162 p = webkit_dom_document_create_element(document, "P", 0);
163 g_assert_true(WEBKIT_DOM_IS_HTML_ELEMENT(p));
164 assertObjectIsDeletedWhenTestFinishes(G_OBJECT(p));
165 webkit_dom_node_insert_before(WEBKIT_DOM_NODE(body), WEBKIT_DOM_NODE(p), WEBKIT_DOM_NODE(div), 0);
166 g_assert_true(webkit_dom_node_has_child_nodes(WEBKIT_DOM_NODE(body)));
167 list = adoptGRef(webkit_dom_node_get_child_nodes(WEBKIT_DOM_NODE(body)));
168 g_assert_true(WEBKIT_DOM_IS_NODE_LIST(list.get()));
169 assertObjectIsDeletedWhenTestFinishes(G_OBJECT(list.get()));
170 g_assert_cmpint(webkit_dom_node_list_get_length(list.get()), ==, 2);
171 node = webkit_dom_node_list_item(list.get(), 0);
172 g_assert_true(WEBKIT_DOM_IS_HTML_ELEMENT(node));
173 assertObjectIsDeletedWhenTestFinishes(G_OBJECT(node));
174 g_assert_true(webkit_dom_node_is_same_node(WEBKIT_DOM_NODE(p), node));
175 node = webkit_dom_node_list_item(list.get(), 1);
176 g_assert_true(WEBKIT_DOM_IS_HTML_ELEMENT(node));
177 assertObjectIsDeletedWhenTestFinishes(G_OBJECT(node));
178 g_assert_true(webkit_dom_node_is_same_node(WEBKIT_DOM_NODE(div), node));
179
180 return true;
181 }
182
183 bool testTagNamesNodeList(WebKitWebPage* page)
184 {
185 static const char* expectedTagNames[] = { "HTML", "HEAD", "BODY", "VIDEO", "SOURCE", "VIDEO", "SOURCE", "INPUT" };
186
187 WebKitDOMDocument* document = webkit_web_page_get_dom_document(page);
188 g_assert_true(WEBKIT_DOM_IS_DOCUMENT(document));
189 assertObjectIsDeletedWhenTestFinishes(G_OBJECT(document));
190
191 GRefPtr<WebKitDOMNodeList> list = adoptGRef(webkit_dom_document_query_selector_all(document, "*", nullptr));
192 g_assert_true(WEBKIT_DOM_IS_NODE_LIST(list.get()));
193 assertObjectIsDeletedWhenTestFinishes(G_OBJECT(list.get()));
194 gulong nodeCount = webkit_dom_node_list_get_length(list.get());
195 g_assert_cmpuint(nodeCount, ==, G_N_ELEMENTS(expectedTagNames));
196 for (unsigned i = 0; i < nodeCount; i++) {
197 WebKitDOMNode* node = webkit_dom_node_list_item(list.get(), i);
198 g_assert_true(WEBKIT_DOM_IS_NODE(node));
199 assertObjectIsDeletedWhenTestFinishes(G_OBJECT(node));
200 GUniquePtr<char> tagName(webkit_dom_node_get_node_name(node));
201 g_assert_cmpstr(tagName.get(), ==, expectedTagNames[i]);
202 }
203
204 return true;
205 }
206
207 bool testTagNamesHTMLCollection(WebKitWebPage* page)
208 {
209 static const char* expectedTagNames[] = { "HTML", "HEAD", "BODY", "VIDEO", "SOURCE", "VIDEO", "SOURCE", "INPUT" };
210
211 WebKitDOMDocument* document = webkit_web_page_get_dom_document(page);
212 g_assert_true(WEBKIT_DOM_IS_DOCUMENT(document));
213 assertObjectIsDeletedWhenTestFinishes(G_OBJECT(document));
214
215 GRefPtr<WebKitDOMHTMLCollection> collection = adoptGRef(webkit_dom_document_get_elements_by_tag_name_as_html_collection(document, "*"));
216 g_assert_true(WEBKIT_DOM_IS_HTML_COLLECTION(collection.get()));
217 assertObjectIsDeletedWhenTestFinishes(G_OBJECT(collection.get()));
218 gulong nodeCount = webkit_dom_html_collection_get_length(collection.get());
219 g_assert_cmpuint(nodeCount, ==, G_N_ELEMENTS(expectedTagNames));
220 for (unsigned i = 0; i < nodeCount; i++) {
221 WebKitDOMNode* node = webkit_dom_html_collection_item(collection.get(), i);
222 g_assert_true(WEBKIT_DOM_IS_NODE(node));
223 assertObjectIsDeletedWhenTestFinishes(G_OBJECT(node));
224 GUniquePtr<char> tagName(webkit_dom_node_get_node_name(node));
225 g_assert_cmpstr(tagName.get(), ==, expectedTagNames[i]);
226 }
227
228 return true;
229 }
230
231 bool testDOMCache(WebKitWebPage* page)
232 {
233 WebKitDOMDocument* document = webkit_web_page_get_dom_document(page);
234 g_assert_true(WEBKIT_DOM_IS_DOCUMENT(document));
235 assertObjectIsDeletedWhenTestFinishes(G_OBJECT(document));
236
237 // DOM objects already in the document should be automatically handled by the cache.
238 WebKitDOMElement* div = webkit_dom_document_get_element_by_id(document, "container");
239 g_assert_true(WEBKIT_DOM_IS_HTML_ELEMENT(div));
240 assertObjectIsDeletedWhenTestFinishes(G_OBJECT(div));
241
242 // Get the same elment twice should return the same pointer.
243 g_assert_true(div == webkit_dom_document_get_element_by_id(document, "container"));
244
245 // A new DOM object created that is derived from Node should be automatically handled by the cache.
246 WebKitDOMElement* p = webkit_dom_document_create_element(document, "P", nullptr);
247 g_assert_true(WEBKIT_DOM_IS_HTML_PARAGRAPH_ELEMENT(p));
248 assertObjectIsDeletedWhenTestFinishes(G_OBJECT(p));
249
250 // A new DOM object created that isn't derived from Node should be manually handled.
251 GRefPtr<WebKitDOMNodeIterator> iter = adoptGRef(webkit_dom_document_create_node_iterator(document, WEBKIT_DOM_NODE(div), WEBKIT_DOM_NODE_FILTER_SHOW_ALL, nullptr, FALSE, nullptr));
252 g_assert_true(WEBKIT_DOM_IS_NODE_ITERATOR(iter.get()));
253 assertObjectIsDeletedWhenTestFinishes(G_OBJECT(iter.get()));
254
255 // We can also manually handle a DOM object handled by the cache.
256 GRefPtr<WebKitDOMElement> p2 = adoptGRef(webkit_dom_document_create_element(document, "P", nullptr));
257 g_assert_true(WEBKIT_DOM_IS_HTML_PARAGRAPH_ELEMENT(p2.get()));
258 assertObjectIsDeletedWhenTestFinishes(G_OBJECT(p2.get()));
259
260 // Manually handling a DOM object owned by the cache shouldn't crash when the cache has more than one reference.
261 GRefPtr<WebKitDOMElement> p3 = adoptGRef(webkit_dom_document_create_element(document, "P", nullptr));
262 g_assert_true(WEBKIT_DOM_IS_HTML_PARAGRAPH_ELEMENT(p3.get()));
263 assertObjectIsDeletedWhenTestFinishes(G_OBJECT(p3.get()));
264 webkit_dom_node_append_child(WEBKIT_DOM_NODE(div), WEBKIT_DOM_NODE(p3.get()), nullptr);
265
266 // DOM objects removed from the document are also correctly handled by the cache.
267 WebKitDOMElement* a = webkit_dom_document_create_element(document, "A", nullptr);
268 g_assert_true(WEBKIT_DOM_IS_ELEMENT(a));
269 assertObjectIsDeletedWhenTestFinishes(G_OBJECT(a));
270 webkit_dom_node_remove_child(WEBKIT_DOM_NODE(div), WEBKIT_DOM_NODE(a), nullptr);
271
272 return true;
273 }
274
275 bool runTest(const char* testName, WebKitWebPage* page) override
276 {
277 if (!strcmp(testName, "hierarchy-navigation"))
278 return testHierarchyNavigation(page);
279 if (!strcmp(testName, "insertion"))
280 return testInsertion(page);
281 if (!strcmp(testName, "tag-names-node-list"))
282 return testTagNamesNodeList(page);
283 if (!strcmp(testName, "tag-names-html-collection"))
284 return testTagNamesHTMLCollection(page);
285 if (!strcmp(testName, "dom-cache"))
286 return testDOMCache(page);
287
288 g_assert_not_reached();
289 return false;
290 }
291};
292
293static void __attribute__((constructor)) registerTests()
294{
295 REGISTER_TEST(WebKitDOMNodeTest, "WebKitDOMNode/hierarchy-navigation");
296 REGISTER_TEST(WebKitDOMNodeTest, "WebKitDOMNode/insertion");
297 REGISTER_TEST(WebKitDOMNodeTest, "WebKitDOMNode/tag-names-node-list");
298 REGISTER_TEST(WebKitDOMNodeTest, "WebKitDOMNode/tag-names-html-collection");
299 REGISTER_TEST(WebKitDOMNodeTest, "WebKitDOMNode/dom-cache");
300}
301
302G_GNUC_END_IGNORE_DEPRECATIONS;
303