OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2016 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 "headless/public/util/dom_tree_extractor.h" | |
6 | |
7 #include <memory> | |
8 #include "base/json/json_writer.h" | |
9 #include "base/strings/string_util.h" | |
10 #include "content/public/browser/render_widget_host_view.h" | |
11 #include "content/public/browser/web_contents.h" | |
12 #include "content/public/test/browser_test.h" | |
13 #include "headless/lib/browser/headless_web_contents_impl.h" | |
14 #include "headless/public/domains/browser.h" | |
15 #include "headless/public/domains/emulation.h" | |
16 #include "headless/public/domains/network.h" | |
17 #include "headless/public/domains/page.h" | |
18 #include "headless/public/headless_browser.h" | |
19 #include "headless/public/headless_devtools_client.h" | |
20 #include "headless/public/headless_devtools_target.h" | |
21 #include "headless/test/headless_browser_test.h" | |
22 #include "testing/gtest/include/gtest/gtest.h" | |
23 #include "url/gurl.h" | |
24 | |
25 namespace headless { | |
26 | |
27 class DomTreeExtractorBrowserTest : public HeadlessAsyncDevTooledBrowserTest, | |
28 public page::Observer { | |
29 public: | |
30 void RunDevTooledTest() override { | |
31 EXPECT_TRUE(embedded_test_server()->Start()); | |
32 devtools_client_->GetPage()->AddObserver(this); | |
33 devtools_client_->GetPage()->Enable(); | |
34 devtools_client_->GetPage()->Navigate( | |
35 embedded_test_server()->GetURL("/dom_tree_test.html").spec()); | |
36 } | |
37 | |
38 void OnLoadEventFired(const page::LoadEventFiredParams& params) override { | |
39 devtools_client_->GetPage()->RemoveObserver(this); | |
40 | |
41 extractor_.reset(new DomTreeExtractor(devtools_client_.get())); | |
42 | |
43 std::vector<std::string> css_whitelist = { | |
44 "color", "display", "font-style", "margin-left", | |
45 "margin-right", "margin-top", "margin-bottom"}; | |
46 extractor_->ExtractDomTree( | |
47 css_whitelist, | |
48 base::Bind(&DomTreeExtractorBrowserTest::OnDomTreeExtracted, | |
49 base::Unretained(this))); | |
50 } | |
51 | |
52 void OnDomTreeExtracted(DomTreeExtractor::DomTree dom_tree) { | |
53 GURL::Replacements replace_port; | |
54 replace_port.SetPortStr(""); | |
55 | |
56 std::vector<std::unique_ptr<base::DictionaryValue>> dom_nodes( | |
57 dom_tree.dom_nodes_.size()); | |
58 | |
59 // For convenience flatten the dom tree into an array. | |
60 for (size_t i = 0; i < dom_tree.dom_nodes_.size(); i++) { | |
61 dom::Node* node = const_cast<dom::Node*>(dom_tree.dom_nodes_[i]); | |
62 | |
63 dom_nodes[i].reset( | |
64 static_cast<base::DictionaryValue*>(node->Serialize().release())); | |
65 | |
66 // Convert child & content document pointers into indexes. | |
67 if (node->HasChildren()) { | |
68 std::unique_ptr<base::ListValue> children(new base::ListValue()); | |
69 for (const std::unique_ptr<dom::Node>& child : *node->GetChildren()) { | |
70 children->AppendInteger( | |
71 dom_tree.node_id_to_index_[child->GetNodeId()]); | |
72 } | |
73 dom_nodes[i]->Set("childIndices", std::move(children)); | |
74 dom_nodes[i]->Remove("children", nullptr); | |
75 } | |
76 | |
77 if (node->HasContentDocument()) { | |
78 dom_nodes[i]->SetInteger( | |
79 "contentDocumentIndex", | |
80 dom_tree | |
81 .node_id_to_index_[node->GetContentDocument()->GetNodeId()]); | |
82 dom_nodes[i]->Remove("contentDocument", nullptr); | |
83 } | |
84 | |
85 dom_nodes[i]->Remove("childNodeCount", nullptr); | |
86 | |
87 // Frame IDs are random. | |
88 if (dom_nodes[i]->HasKey("frameId")) | |
89 dom_nodes[i]->SetString("frameId", "?"); | |
90 | |
91 // Ports are random. | |
92 std::string url; | |
93 if (dom_nodes[i]->GetString("baseURL", &url)) { | |
94 dom_nodes[i]->SetString( | |
95 "baseURL", GURL(url).ReplaceComponents(replace_port).spec()); | |
96 } | |
97 | |
98 if (dom_nodes[i]->GetString("documentURL", &url)) { | |
99 dom_nodes[i]->SetString( | |
100 "documentURL", GURL(url).ReplaceComponents(replace_port).spec()); | |
101 } | |
102 } | |
103 | |
104 // Merge LayoutTreeNode data into the dictionaries. | |
105 for (const css::LayoutTreeNode* layout_node : dom_tree.layout_tree_nodes_) { | |
106 auto it = | |
107 dom_tree.node_id_to_index_.find(layout_node->GetBackendNodeId()); | |
108 ASSERT_TRUE(it != dom_tree.node_id_to_index_.end()); | |
109 | |
110 base::DictionaryValue* node_dict = dom_nodes[it->second].get(); | |
111 node_dict->Set("boundingBox", layout_node->GetBoundingBox()->Serialize()); | |
112 | |
113 if (layout_node->HasLayoutText()) | |
114 node_dict->SetString("layoutText", layout_node->GetLayoutText()); | |
115 | |
116 if (layout_node->HasStyleIndex()) | |
117 node_dict->SetInteger("styleIndex", layout_node->GetStyleIndex()); | |
118 | |
119 if (layout_node->HasInlineTextNodes()) { | |
120 std::unique_ptr<base::ListValue> inline_text_nodes( | |
121 new base::ListValue()); | |
122 for (const std::unique_ptr<css::InlineTextBox>& inline_text_box : | |
123 *layout_node->GetInlineTextNodes()) { | |
124 size_t index = inline_text_nodes->GetSize(); | |
125 inline_text_nodes->Set(index, inline_text_box->Serialize()); | |
126 } | |
127 node_dict->Set("inlineTextNodes", std::move(inline_text_nodes)); | |
128 } | |
129 } | |
130 | |
131 std::vector<std::unique_ptr<base::DictionaryValue>> computed_styles( | |
132 dom_tree.computed_styles_.size()); | |
133 | |
134 for (size_t i = 0; i < dom_tree.computed_styles_.size(); i++) { | |
135 std::unique_ptr<base::DictionaryValue> style(new base::DictionaryValue()); | |
136 for (const auto& style_proprty : | |
Eric Seckler
2016/10/20 08:43:58
nit: style_property
alex clarke (OOO till 29th)
2016/10/20 09:26:25
Done.
| |
137 *dom_tree.computed_styles_[i]->GetProperties()) { | |
138 style->SetString(style_proprty->GetName(), style_proprty->GetValue()); | |
139 } | |
140 computed_styles[i] = std::move(style); | |
141 } | |
142 | |
143 const std::vector<std::string> expected_dom_nodes = { | |
144 "{\n" | |
145 " 'baseURL': 'http://127.0.0.1/dom_tree_test.html',\n" | |
146 " 'childIndices': [ 1 ],\n" | |
147 " 'documentURL': 'http://127.0.0.1/dom_tree_test.html',\n" | |
148 " 'localName': '',\n" | |
149 " 'nodeId': 1,\n" | |
150 " 'nodeName': '#document',\n" | |
151 " 'nodeType': 9,\n" | |
152 " 'nodeValue': '',\n" | |
153 " 'xmlVersion': ''\n" | |
154 "}\n", | |
155 | |
156 "{\n" | |
157 " 'attributes': [ ],\n" | |
158 " 'childIndices': [ 2, 5 ],\n" | |
159 " 'frameId': '?',\n" | |
160 " 'localName': 'html',\n" | |
161 " 'nodeId': 2,\n" | |
162 " 'nodeName': 'HTML',\n" | |
163 " 'nodeType': 1,\n" | |
164 " 'nodeValue': ''\n" | |
165 "}\n", | |
166 | |
167 "{\n" | |
168 " 'attributes': [ ],\n" | |
169 " 'boundingBox': {\n" | |
170 " 'height': 600.0,\n" | |
171 " 'width': 800.0,\n" | |
172 " 'x': 0.0,\n" | |
173 " 'y': 0.0\n" | |
174 " },\n" | |
175 " 'childIndices': [ 3 ],\n" | |
176 " 'localName': 'head',\n" | |
177 " 'nodeId': 3,\n" | |
178 " 'nodeName': 'HEAD',\n" | |
179 " 'nodeType': 1,\n" | |
180 " 'nodeValue': '',\n" | |
181 " 'styleIndex': 0\n" | |
182 "}\n", | |
183 | |
184 "{\n" | |
185 " 'attributes': [ ],\n" | |
186 " 'boundingBox': {\n" | |
187 " 'height': 600.0,\n" | |
188 " 'width': 800.0,\n" | |
189 " 'x': 0.0,\n" | |
190 " 'y': 0.0\n" | |
191 " },\n" | |
192 " 'childIndices': [ 4 ],\n" | |
193 " 'localName': 'title',\n" | |
194 " 'nodeId': 4,\n" | |
195 " 'nodeName': 'TITLE',\n" | |
196 " 'nodeType': 1,\n" | |
197 " 'nodeValue': '',\n" | |
198 " 'styleIndex': 1\n" | |
199 "}\n", | |
200 | |
201 "{\n" | |
202 " 'boundingBox': {\n" | |
203 " 'height': 584.0,\n" | |
204 " 'width': 784.0,\n" | |
205 " 'x': 8.0,\n" | |
206 " 'y': 8.0\n" | |
207 " },\n" | |
208 " 'localName': '',\n" | |
209 " 'nodeId': 5,\n" | |
210 " 'nodeName': '#text',\n" | |
211 " 'nodeType': 3,\n" | |
212 " 'nodeValue': 'Hello world!',\n" | |
213 " 'styleIndex': 2\n" | |
214 "}\n", | |
215 | |
216 "{\n" | |
217 " 'attributes': [ ],\n" | |
218 " 'boundingBox': {\n" | |
219 " 'height': 367.0,\n" | |
220 " 'width': 784.0,\n" | |
221 " 'x': 8.0,\n" | |
222 " 'y': 8.0\n" | |
223 " },\n" | |
224 " 'childIndices': [ 6 ],\n" | |
225 " 'localName': 'body',\n" | |
226 " 'nodeId': 6,\n" | |
227 " 'nodeName': 'BODY',\n" | |
228 " 'nodeType': 1,\n" | |
229 " 'nodeValue': '',\n" | |
230 " 'styleIndex': 1\n" | |
231 "}\n", | |
232 | |
233 "{\n" | |
234 " 'attributes': [ 'id', 'id1' ],\n" | |
235 " 'boundingBox': {\n" | |
236 " 'height': 37.0,\n" | |
237 " 'width': 784.0,\n" | |
238 " 'x': 8.0,\n" | |
239 " 'y': 8.0\n" | |
240 " },\n" | |
241 " 'childIndices': [ 7, 9, 16 ],\n" | |
242 " 'localName': 'div',\n" | |
243 " 'nodeId': 7,\n" | |
244 " 'nodeName': 'DIV',\n" | |
245 " 'nodeType': 1,\n" | |
246 " 'nodeValue': '',\n" | |
247 " 'styleIndex': 3\n" | |
248 "}\n", | |
249 | |
250 "{\n" | |
251 " 'attributes': [ 'style', 'color: red' ],\n" | |
252 " 'boundingBox': {\n" | |
253 " 'height': 36.0,\n" | |
254 " 'width': 143.0,\n" | |
255 " 'x': 8.0,\n" | |
256 " 'y': 8.0\n" | |
257 " },\n" | |
258 " 'childIndices': [ 8 ],\n" | |
259 " 'inlineTextNodes': [ {\n" | |
260 " 'boundingBox': {\n" | |
261 " 'height': 36.0,\n" | |
262 " 'width': 142.171875,\n" | |
263 " 'x': 8.0,\n" | |
264 " 'y': 8.0\n" | |
265 " },\n" | |
266 " 'numCharacters': 10,\n" | |
267 " 'startCharacterIndex': 0\n" | |
268 " } ],\n" | |
269 " 'layoutText': 'Some text.',\n" | |
270 " 'localName': 'h1',\n" | |
271 " 'nodeId': 8,\n" | |
272 " 'nodeName': 'H1',\n" | |
273 " 'nodeType': 1,\n" | |
274 " 'nodeValue': '',\n" | |
275 " 'styleIndex': 3\n" | |
276 "}\n", | |
277 | |
278 "{\n" | |
279 " 'boundingBox': {\n" | |
280 " 'height': 200.0,\n" | |
281 " 'width': 400.0,\n" | |
282 " 'x': 10.0,\n" | |
283 " 'y': 68.0\n" | |
284 " },\n" | |
285 " 'localName': '',\n" | |
286 " 'nodeId': 9,\n" | |
287 " 'nodeName': '#text',\n" | |
288 " 'nodeType': 3,\n" | |
289 " 'nodeValue': 'Some text.',\n" | |
290 " 'styleIndex': 1\n" | |
291 "}\n", | |
292 | |
293 "{\n" | |
294 " 'attributes': [ 'src', '/iframe.html', 'width', '400', 'height', " | |
295 "'200' ],\n" | |
296 " 'boundingBox': {\n" | |
297 " 'height': 171.0,\n" | |
298 " 'width': 384.0,\n" | |
299 " 'x': 18.0,\n" | |
300 " 'y': 76.0\n" | |
301 " },\n" | |
302 " 'childIndices': [ ],\n" | |
303 " 'contentDocumentIndex': 10,\n" | |
304 " 'frameId': '?',\n" | |
305 " 'localName': 'iframe',\n" | |
306 " 'nodeId': 10,\n" | |
307 " 'nodeName': 'IFRAME',\n" | |
308 " 'nodeType': 1,\n" | |
309 " 'nodeValue': '',\n" | |
310 " 'styleIndex': 2\n" | |
311 "}\n", | |
312 | |
313 "{\n" | |
314 " 'baseURL': 'http://127.0.0.1/iframe.html',\n" | |
315 " 'boundingBox': {\n" | |
316 " 'height': 37.0,\n" | |
317 " 'width': 384.0,\n" | |
318 " 'x': 18.0,\n" | |
319 " 'y': 76.0\n" | |
320 " },\n" | |
321 " 'childIndices': [ 11 ],\n" | |
322 " 'documentURL': 'http://127.0.0.1/iframe.html',\n" | |
323 " 'localName': '',\n" | |
324 " 'nodeId': 11,\n" | |
325 " 'nodeName': '#document',\n" | |
326 " 'nodeType': 9,\n" | |
327 " 'nodeValue': '',\n" | |
328 " 'styleIndex': 4,\n" | |
329 " 'xmlVersion': ''\n" | |
330 "}\n", | |
331 | |
332 "{\n" | |
333 " 'attributes': [ ],\n" | |
334 " 'boundingBox': {\n" | |
335 " 'height': 36.0,\n" | |
336 " 'width': 308.0,\n" | |
337 " 'x': 8.0,\n" | |
338 " 'y': 8.0\n" | |
339 " },\n" | |
340 " 'childIndices': [ 12, 13 ],\n" | |
341 " 'frameId': '?',\n" | |
342 " 'inlineTextNodes': [ {\n" | |
343 " 'boundingBox': {\n" | |
344 " 'height': 36.0,\n" | |
345 " 'width': 307.734375,\n" | |
346 " 'x': 8.0,\n" | |
347 " 'y': 8.0\n" | |
348 " },\n" | |
349 " 'numCharacters': 22,\n" | |
350 " 'startCharacterIndex': 0\n" | |
351 " } ],\n" | |
352 " 'layoutText': 'Hello from the iframe!',\n" | |
353 " 'localName': 'html',\n" | |
354 " 'nodeId': 12,\n" | |
355 " 'nodeName': 'HTML',\n" | |
356 " 'nodeType': 1,\n" | |
357 " 'nodeValue': '',\n" | |
358 " 'styleIndex': 4\n" | |
359 "}\n", | |
360 | |
361 "{\n" | |
362 " 'attributes': [ ],\n" | |
363 " 'boundingBox': {\n" | |
364 " 'height': 205.0,\n" | |
365 " 'width': 404.0,\n" | |
366 " 'x': 8.0,\n" | |
367 " 'y': 66.0\n" | |
368 " },\n" | |
369 " 'childIndices': [ ],\n" | |
370 " 'localName': 'head',\n" | |
371 " 'nodeId': 13,\n" | |
372 " 'nodeName': 'HEAD',\n" | |
373 " 'nodeType': 1,\n" | |
374 " 'nodeValue': '',\n" | |
375 " 'styleIndex': 5\n" | |
376 "}\n", | |
377 | |
378 "{\n" | |
379 " 'attributes': [ ],\n" | |
380 " 'boundingBox': {\n" | |
381 " 'height': 0.0,\n" | |
382 " 'width': 0.0,\n" | |
383 " 'x': 0.0,\n" | |
384 " 'y': 0.0\n" | |
385 " },\n" | |
386 " 'childIndices': [ 14 ],\n" | |
387 " 'layoutText': '\\n',\n" | |
388 " 'localName': 'body',\n" | |
389 " 'nodeId': 14,\n" | |
390 " 'nodeName': 'BODY',\n" | |
391 " 'nodeType': 1,\n" | |
392 " 'nodeValue': '',\n" | |
393 " 'styleIndex': 1\n" | |
394 "}\n", | |
395 | |
396 "{\n" | |
397 " 'attributes': [ ],\n" | |
398 " 'boundingBox': {\n" | |
399 " 'height': 105.0,\n" | |
400 " 'width': 784.0,\n" | |
401 " 'x': 8.0,\n" | |
402 " 'y': 270.0\n" | |
403 " },\n" | |
404 " 'childIndices': [ 15 ],\n" | |
405 " 'localName': 'h1',\n" | |
406 " 'nodeId': 15,\n" | |
407 " 'nodeName': 'H1',\n" | |
408 " 'nodeType': 1,\n" | |
409 " 'nodeValue': '',\n" | |
410 " 'styleIndex': 1\n" | |
411 "}\n", | |
412 | |
413 "{\n" | |
414 " 'boundingBox': {\n" | |
415 " 'height': 105.0,\n" | |
416 " 'width': 784.0,\n" | |
417 " 'x': 8.0,\n" | |
418 " 'y': 270.0\n" | |
419 " },\n" | |
420 " 'localName': '',\n" | |
421 " 'nodeId': 16,\n" | |
422 " 'nodeName': '#text',\n" | |
423 " 'nodeType': 3,\n" | |
424 " 'nodeValue': 'Hello from the iframe!',\n" | |
425 " 'styleIndex': 1\n" | |
426 "}\n", | |
427 | |
428 "{\n" | |
429 " 'attributes': [ 'id', 'id2' ],\n" | |
430 " 'boundingBox': {\n" | |
431 " 'height': 105.0,\n" | |
432 " 'width': 784.0,\n" | |
433 " 'x': 8.0,\n" | |
434 " 'y': 270.0\n" | |
435 " },\n" | |
436 " 'childIndices': [ 17 ],\n" | |
437 " 'localName': 'div',\n" | |
438 " 'nodeId': 17,\n" | |
439 " 'nodeName': 'DIV',\n" | |
440 " 'nodeType': 1,\n" | |
441 " 'nodeValue': '',\n" | |
442 " 'styleIndex': 1\n" | |
443 "}\n", | |
444 | |
445 "{\n" | |
446 " 'attributes': [ 'id', 'id3' ],\n" | |
447 " 'boundingBox': {\n" | |
448 " 'height': 18.0,\n" | |
449 " 'width': 53.0,\n" | |
450 " 'x': 8.0,\n" | |
451 " 'y': 270.0\n" | |
452 " },\n" | |
453 " 'childIndices': [ 18 ],\n" | |
454 " 'localName': 'div',\n" | |
455 " 'nodeId': 18,\n" | |
456 " 'nodeName': 'DIV',\n" | |
457 " 'nodeType': 1,\n" | |
458 " 'nodeValue': '',\n" | |
459 " 'styleIndex': 6\n" | |
460 "}\n", | |
461 | |
462 "{\n" | |
463 " 'attributes': [ 'id', 'id4' ],\n" | |
464 " 'boundingBox': {\n" | |
465 " 'height': 18.0,\n" | |
466 " 'width': 53.0,\n" | |
467 " 'x': 8.0,\n" | |
468 " 'y': 270.0\n" | |
469 " },\n" | |
470 " 'childIndices': [ 19, 21, 23, 24 ],\n" | |
471 " 'inlineTextNodes': [ {\n" | |
472 " 'boundingBox': {\n" | |
473 " 'height': 17.0,\n" | |
474 " 'width': 52.421875,\n" | |
475 " 'x': 8.0,\n" | |
476 " 'y': 270.4375\n" | |
477 " },\n" | |
478 " 'numCharacters': 7,\n" | |
479 " 'startCharacterIndex': 0\n" | |
480 " } ],\n" | |
481 " 'layoutText': 'Google!',\n" | |
482 " 'localName': 'div',\n" | |
483 " 'nodeId': 19,\n" | |
484 " 'nodeName': 'DIV',\n" | |
485 " 'nodeType': 1,\n" | |
486 " 'nodeValue': '',\n" | |
487 " 'styleIndex': 6\n" | |
488 "}\n", | |
489 | |
490 "{\n" | |
491 " 'attributes': [ 'href', 'https://www.google.com' ],\n" | |
492 " 'boundingBox': {\n" | |
493 " 'height': 0.0,\n" | |
494 " 'width': 0.0,\n" | |
495 " 'x': 0.0,\n" | |
496 " 'y': 0.0\n" | |
497 " },\n" | |
498 " 'childIndices': [ 20 ],\n" | |
499 " 'layoutText': '\\n ',\n" | |
500 " 'localName': 'a',\n" | |
501 " 'nodeId': 20,\n" | |
502 " 'nodeName': 'A',\n" | |
503 " 'nodeType': 1,\n" | |
504 " 'nodeValue': '',\n" | |
505 " 'styleIndex': 1\n" | |
506 "}\n", | |
507 | |
508 "{\n" | |
509 " 'boundingBox': {\n" | |
510 " 'height': 19.0,\n" | |
511 " 'width': 784.0,\n" | |
512 " 'x': 8.0,\n" | |
513 " 'y': 304.0\n" | |
514 " },\n" | |
515 " 'localName': '',\n" | |
516 " 'nodeId': 21,\n" | |
517 " 'nodeName': '#text',\n" | |
518 " 'nodeType': 3,\n" | |
519 " 'nodeValue': 'Google!',\n" | |
520 " 'styleIndex': 7\n" | |
521 "}\n", | |
522 | |
523 "{\n" | |
524 " 'attributes': [ ],\n" | |
525 " 'boundingBox': {\n" | |
526 " 'height': 18.0,\n" | |
527 " 'width': 85.0,\n" | |
528 " 'x': 8.0,\n" | |
529 " 'y': 304.0\n" | |
530 " },\n" | |
531 " 'childIndices': [ 22 ],\n" | |
532 " 'inlineTextNodes': [ {\n" | |
533 " 'boundingBox': {\n" | |
534 " 'height': 17.0,\n" | |
535 " 'width': 84.84375,\n" | |
536 " 'x': 8.0,\n" | |
537 " 'y': 304.4375\n" | |
538 " },\n" | |
539 " 'numCharacters': 12,\n" | |
540 " 'startCharacterIndex': 0\n" | |
541 " } ],\n" | |
542 " 'layoutText': 'A paragraph!',\n" | |
543 " 'localName': 'p',\n" | |
544 " 'nodeId': 22,\n" | |
545 " 'nodeName': 'P',\n" | |
546 " 'nodeType': 1,\n" | |
547 " 'nodeValue': '',\n" | |
548 " 'styleIndex': 7\n" | |
549 "}\n", | |
550 | |
551 "{\n" | |
552 " 'boundingBox': {\n" | |
553 " 'height': 0.0,\n" | |
554 " 'width': 0.0,\n" | |
555 " 'x': 0.0,\n" | |
556 " 'y': 0.0\n" | |
557 " },\n" | |
558 " 'inlineTextNodes': [ {\n" | |
559 " 'boundingBox': {\n" | |
560 " 'height': 17.0,\n" | |
561 " 'width': 0.0,\n" | |
562 " 'x': 8.0,\n" | |
563 " 'y': 338.4375\n" | |
564 " },\n" | |
565 " 'numCharacters': 1,\n" | |
566 " 'startCharacterIndex': 0\n" | |
567 " } ],\n" | |
568 " 'layoutText': '\\n',\n" | |
569 " 'localName': '',\n" | |
570 " 'nodeId': 23,\n" | |
571 " 'nodeName': '#text',\n" | |
572 " 'nodeType': 3,\n" | |
573 " 'nodeValue': 'A paragraph!',\n" | |
574 " 'styleIndex': 5\n" | |
575 "}\n", | |
576 | |
577 "{\n" | |
578 " 'attributes': [ ],\n" | |
579 " 'boundingBox': {\n" | |
580 " 'height': 19.0,\n" | |
581 " 'width': 784.0,\n" | |
582 " 'x': 8.0,\n" | |
583 " 'y': 356.0\n" | |
584 " },\n" | |
585 " 'childIndices': [ ],\n" | |
586 " 'localName': 'br',\n" | |
587 " 'nodeId': 24,\n" | |
588 " 'nodeName': 'BR',\n" | |
589 " 'nodeType': 1,\n" | |
590 " 'nodeValue': '',\n" | |
591 " 'styleIndex': 8\n" | |
592 "}\n", | |
593 | |
594 "{\n" | |
595 " 'attributes': [ 'style', 'color: green' ],\n" | |
596 " 'boundingBox': {\n" | |
597 " 'height': 18.0,\n" | |
598 " 'width': 41.0,\n" | |
599 " 'x': 8.0,\n" | |
600 " 'y': 356.0\n" | |
601 " },\n" | |
602 " 'childIndices': [ 25, 26, 28 ],\n" | |
603 " 'inlineTextNodes': [ {\n" | |
604 " 'boundingBox': {\n" | |
605 " 'height': 17.0,\n" | |
606 " 'width': 40.4375,\n" | |
607 " 'x': 8.0,\n" | |
608 " 'y': 356.4375\n" | |
609 " },\n" | |
610 " 'numCharacters': 5,\n" | |
611 " 'startCharacterIndex': 0\n" | |
612 " } ],\n" | |
613 " 'layoutText': 'Some ',\n" | |
614 " 'localName': 'div',\n" | |
615 " 'nodeId': 25,\n" | |
616 " 'nodeName': 'DIV',\n" | |
617 " 'nodeType': 1,\n" | |
618 " 'nodeValue': '',\n" | |
619 " 'styleIndex': 8\n" | |
620 "}\n", | |
621 | |
622 "{\n" | |
623 " 'boundingBox': {\n" | |
624 " 'height': 18.0,\n" | |
625 " 'width': 37.0,\n" | |
626 " 'x': 48.0,\n" | |
627 " 'y': 356.0\n" | |
628 " },\n" | |
629 " 'localName': '',\n" | |
630 " 'nodeId': 26,\n" | |
631 " 'nodeName': '#text',\n" | |
632 " 'nodeType': 3,\n" | |
633 " 'nodeValue': 'Some ',\n" | |
634 " 'styleIndex': 9\n" | |
635 "}\n", | |
636 | |
637 "{\n" | |
638 " 'attributes': [ ],\n" | |
639 " 'boundingBox': {\n" | |
640 " 'height': 18.0,\n" | |
641 " 'width': 37.0,\n" | |
642 " 'x': 48.0,\n" | |
643 " 'y': 356.0\n" | |
644 " },\n" | |
645 " 'childIndices': [ 27 ],\n" | |
646 " 'inlineTextNodes': [ {\n" | |
647 " 'boundingBox': {\n" | |
648 " 'height': 17.0,\n" | |
649 " 'width': 35.828125,\n" | |
650 " 'x': 48.4375,\n" | |
651 " 'y': 356.4375\n" | |
652 " },\n" | |
653 " 'numCharacters': 5,\n" | |
654 " 'startCharacterIndex': 0\n" | |
655 " } ],\n" | |
656 " 'layoutText': 'green',\n" | |
657 " 'localName': 'em',\n" | |
658 " 'nodeId': 27,\n" | |
659 " 'nodeName': 'EM',\n" | |
660 " 'nodeType': 1,\n" | |
661 " 'nodeValue': '',\n" | |
662 " 'styleIndex': 9\n" | |
663 "}\n", | |
664 | |
665 "{\n" | |
666 " 'boundingBox': {\n" | |
667 " 'height': 18.0,\n" | |
668 " 'width': 41.0,\n" | |
669 " 'x': 84.0,\n" | |
670 " 'y': 356.0\n" | |
671 " },\n" | |
672 " 'inlineTextNodes': [ {\n" | |
673 " 'boundingBox': {\n" | |
674 " 'height': 17.0,\n" | |
675 " 'width': 39.984375,\n" | |
676 " 'x': 84.265625,\n" | |
677 " 'y': 356.4375\n" | |
678 " },\n" | |
679 " 'numCharacters': 8,\n" | |
680 " 'startCharacterIndex': 0\n" | |
681 " } ],\n" | |
682 " 'layoutText': ' text...',\n" | |
683 " 'localName': '',\n" | |
684 " 'nodeId': 28,\n" | |
685 " 'nodeName': '#text',\n" | |
686 " 'nodeType': 3,\n" | |
687 " 'nodeValue': 'green',\n" | |
688 " 'styleIndex': 8\n" | |
689 "}\n", | |
690 | |
691 "{\n" | |
692 " 'localName': '',\n" | |
693 " 'nodeId': 29,\n" | |
694 " 'nodeName': '#text',\n" | |
695 " 'nodeType': 3,\n" | |
696 " 'nodeValue': ' text...'\n" | |
697 "}\n"}; | |
698 | |
699 EXPECT_EQ(expected_dom_nodes.size(), dom_nodes.size()); | |
700 | |
701 for (size_t i = 0; i < dom_nodes.size(); i++) { | |
702 std::string result_json; | |
703 base::JSONWriter::WriteWithOptions( | |
704 *dom_nodes[i], base::JSONWriter::OPTIONS_PRETTY_PRINT, &result_json); | |
705 | |
706 base::ReplaceChars(result_json, "\"", "'", &result_json); | |
707 | |
708 ASSERT_LT(i, expected_dom_nodes.size()); | |
709 EXPECT_EQ(expected_dom_nodes[i], result_json) << " Node # " << i; | |
710 } | |
711 | |
712 const std::vector<std::string> expected_styles = { | |
713 "{\n" | |
714 " 'color': '',\n" | |
715 " 'display': '',\n" | |
716 " 'font-style': '',\n" | |
717 " 'margin-bottom': '',\n" | |
718 " 'margin-left': '',\n" | |
719 " 'margin-right': '',\n" | |
720 " 'margin-top': ''\n" | |
721 "}\n", | |
722 | |
723 "{\n" | |
724 " 'color': 'rgb(0, 0, 0)',\n" | |
725 " 'display': 'block',\n" | |
726 " 'font-style': 'normal',\n" | |
727 " 'margin-bottom': '0px',\n" | |
728 " 'margin-left': '0px',\n" | |
729 " 'margin-right': '0px',\n" | |
730 " 'margin-top': '0px'\n" | |
731 "}\n", | |
732 | |
733 "{\n" | |
734 " 'color': 'rgb(0, 0, 0)',\n" | |
735 " 'display': 'block',\n" | |
736 " 'font-style': 'normal',\n" | |
737 " 'margin-bottom': '8px',\n" | |
738 " 'margin-left': '8px',\n" | |
739 " 'margin-right': '8px',\n" | |
740 " 'margin-top': '8px'\n" | |
741 "}\n", | |
742 | |
743 "{\n" | |
744 " 'color': 'rgb(255, 0, 0)',\n" | |
745 " 'display': 'block',\n" | |
746 " 'font-style': 'normal',\n" | |
747 " 'margin-bottom': '21.44px',\n" | |
748 " 'margin-left': '0px',\n" | |
749 " 'margin-right': '0px',\n" | |
750 " 'margin-top': '21.44px'\n" | |
751 "}\n", | |
752 | |
753 "{\n" | |
754 " 'color': 'rgb(0, 0, 0)',\n" | |
755 " 'display': 'block',\n" | |
756 " 'font-style': 'normal',\n" | |
757 " 'margin-bottom': '21.44px',\n" | |
758 " 'margin-left': '0px',\n" | |
759 " 'margin-right': '0px',\n" | |
760 " 'margin-top': '21.44px'\n" | |
761 "}\n", | |
762 | |
763 "{\n" | |
764 " 'color': 'rgb(0, 0, 0)',\n" | |
765 " 'display': 'inline',\n" | |
766 " 'font-style': 'normal',\n" | |
767 " 'margin-bottom': '0px',\n" | |
768 " 'margin-left': '0px',\n" | |
769 " 'margin-right': '0px',\n" | |
770 " 'margin-top': '0px'\n" | |
771 "}\n", | |
772 | |
773 "{\n" | |
774 " 'color': 'rgb(0, 0, 238)',\n" | |
775 " 'display': 'inline',\n" | |
776 " 'font-style': 'normal',\n" | |
777 " 'margin-bottom': '0px',\n" | |
778 " 'margin-left': '0px',\n" | |
779 " 'margin-right': '0px',\n" | |
780 " 'margin-top': '0px'\n" | |
781 "}\n", | |
782 | |
783 "{\n" | |
784 " 'color': 'rgb(0, 0, 0)',\n" | |
785 " 'display': 'block',\n" | |
786 " 'font-style': 'normal',\n" | |
787 " 'margin-bottom': '16px',\n" | |
788 " 'margin-left': '0px',\n" | |
789 " 'margin-right': '0px',\n" | |
790 " 'margin-top': '16px'\n" | |
791 "}\n", | |
792 | |
793 "{\n" | |
794 " 'color': 'rgb(0, 128, 0)',\n" | |
795 " 'display': 'block',\n" | |
796 " 'font-style': 'normal',\n" | |
797 " 'margin-bottom': '0px',\n" | |
798 " 'margin-left': '0px',\n" | |
799 " 'margin-right': '0px',\n" | |
800 " 'margin-top': '0px'\n" | |
801 "}\n", | |
802 | |
803 "{\n" | |
804 " 'color': 'rgb(0, 128, 0)',\n" | |
805 " 'display': 'inline',\n" | |
806 " 'font-style': 'italic',\n" | |
807 " 'margin-bottom': '0px',\n" | |
808 " 'margin-left': '0px',\n" | |
809 " 'margin-right': '0px',\n" | |
810 " 'margin-top': '0px'\n" | |
811 "}\n"}; | |
812 | |
813 for (size_t i = 0; i < computed_styles.size(); i++) { | |
814 std::string result_json; | |
815 base::JSONWriter::WriteWithOptions(*computed_styles[i], | |
816 base::JSONWriter::OPTIONS_PRETTY_PRINT, | |
817 &result_json); | |
818 | |
819 base::ReplaceChars(result_json, "\"", "'", &result_json); | |
820 | |
821 ASSERT_LT(i, expected_styles.size()); | |
822 EXPECT_EQ(expected_styles[i], result_json) << " Style # " << i; | |
823 } | |
824 | |
825 FinishAsynchronousTest(); | |
826 } | |
827 | |
828 std::unique_ptr<DomTreeExtractor> extractor_; | |
829 }; | |
830 | |
831 HEADLESS_ASYNC_DEVTOOLED_TEST_F(DomTreeExtractorBrowserTest); | |
832 | |
833 } // namespace headless | |
OLD | NEW |