1 // Copyright (c) 2010 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.
6 #include "base/command_line.h"
7 #include "base/compiler_specific.h"
8 #include "base/containers/hash_tables.h"
9 #include "base/files/file_path.h"
10 #include "base/files/file_util.h"
11 #include "base/strings/string_util.h"
12 #include "base/strings/utf_string_conversions.h"
13 #include "content/public/common/content_switches.h"
14 #include "content/public/renderer/render_view.h"
15 #include "content/public/renderer/render_view_observer.h"
16 #include "content/public/test/content_browser_test.h"
17 #include "content/public/test/content_browser_test_utils.h"
18 #include "content/public/test/routing_id_mangling_disabler.h"
19 #include "content/public/test/test_utils.h"
20 #include "content/renderer/savable_resources.h"
21 #include "content/shell/browser/shell.h"
22 #include "net/base/filename_util.h"
23 #include "net/url_request/url_request_context.h"
24 #include "third_party/WebKit/public/platform/WebCString.h"
25 #include "third_party/WebKit/public/platform/WebData.h"
26 #include "third_party/WebKit/public/platform/WebString.h"
27 #include "third_party/WebKit/public/platform/WebURL.h"
28 #include "third_party/WebKit/public/platform/WebVector.h"
29 #include "third_party/WebKit/public/web/WebDocument.h"
30 #include "third_party/WebKit/public/web/WebElement.h"
31 #include "third_party/WebKit/public/web/WebElementCollection.h"
32 #include "third_party/WebKit/public/web/WebLocalFrame.h"
33 #include "third_party/WebKit/public/web/WebNode.h"
34 #include "third_party/WebKit/public/web/WebNodeList.h"
35 #include "third_party/WebKit/public/web/WebPageSerializer.h"
36 #include "third_party/WebKit/public/web/WebPageSerializerClient.h"
37 #include "third_party/WebKit/public/web/WebView.h"
39 using blink::WebCString
;
41 using blink::WebDocument
;
42 using blink::WebElement
;
43 using blink::WebElementCollection
;
44 using blink::WebFrame
;
45 using blink::WebLocalFrame
;
47 using blink::WebNodeList
;
48 using blink::WebPageSerializer
;
49 using blink::WebPageSerializerClient
;
50 using blink::WebString
;
53 using blink::WebVector
;
57 // The first RenderFrame is routing ID 1, and the first RenderView is 2.
58 const int kRenderViewRoutingId
= 2;
64 // Iterate recursively over sub-frames to find one with with a given url.
65 WebFrame
* FindSubFrameByURL(WebView
* web_view
, const GURL
& url
) {
66 if (!web_view
->mainFrame())
69 std::vector
<WebFrame
*> stack
;
70 stack
.push_back(web_view
->mainFrame());
72 while (!stack
.empty()) {
73 WebFrame
* current_frame
= stack
.back();
75 if (GURL(current_frame
->document().url()) == url
)
77 WebElementCollection all
= current_frame
->document().all();
78 for (WebElement element
= all
.firstItem();
79 !element
.isNull(); element
= all
.nextItem()) {
80 // Check frame tag and iframe tag
81 if (!element
.hasHTMLTagName("frame") && !element
.hasHTMLTagName("iframe"))
83 WebFrame
* sub_frame
= WebLocalFrame::fromFrameOwnerElement(element
);
85 stack
.push_back(sub_frame
);
91 // Helper function that test whether the first node in the doc is a doc type
93 bool HasDocType(const WebDocument
& doc
) {
94 WebNode node
= doc
.firstChild();
97 return node
.nodeType() == WebNode::DocumentTypeNode
;
100 // Helper function for checking whether input node is META tag. Return true
101 // means it is META element, otherwise return false. The parameter charset_info
102 // return actual charset info if the META tag has charset declaration.
103 bool IsMetaElement(const WebNode
& node
, std::string
& charset_info
) {
104 if (!node
.isElementNode())
106 const WebElement meta
= node
.toConst
<WebElement
>();
107 if (!meta
.hasHTMLTagName("meta"))
109 charset_info
.erase(0, charset_info
.length());
110 // Check the META charset declaration.
111 WebString httpEquiv
= meta
.getAttribute("http-equiv");
112 if (LowerCaseEqualsASCII(httpEquiv
, "content-type")) {
113 std::string content
= meta
.getAttribute("content").utf8();
114 int pos
= content
.find("charset", 0);
116 // Add a dummy charset declaration to charset_info, which indicates this
117 // META tag has charset declaration although we do not get correct value
119 charset_info
.append("has-charset-declaration");
120 int remaining_length
= content
.length() - pos
- 7;
121 if (!remaining_length
)
123 int start_pos
= pos
+ 7;
125 while (remaining_length
--)
126 if (content
[start_pos
++] == L
'=')
128 // Skip beginning space.
129 while (remaining_length
) {
130 if (content
[start_pos
] > 0x0020)
135 if (!remaining_length
)
137 int end_pos
= start_pos
;
138 // Now we find out the start point of charset info. Search the end point.
139 while (remaining_length
--) {
140 if (content
[end_pos
] <= 0x0020 || content
[end_pos
] == L
';')
144 // Get actual charset info.
145 charset_info
= content
.substr(start_pos
, end_pos
- start_pos
);
152 class LoadObserver
: public RenderViewObserver
{
154 LoadObserver(RenderView
* render_view
, const base::Closure
& quit_closure
)
155 : RenderViewObserver(render_view
),
156 quit_closure_(quit_closure
) {}
158 void DidFinishLoad(blink::WebLocalFrame
* frame
) override
{
159 if (frame
== render_view()->GetWebView()->mainFrame())
164 base::Closure quit_closure_
;
167 class DomSerializerTests
: public ContentBrowserTest
,
168 public WebPageSerializerClient
{
171 : serialized_(false),
172 local_directory_name_(FILE_PATH_LITERAL("./dummy_files/")) {}
174 void SetUpCommandLine(base::CommandLine
* command_line
) override
{
175 command_line
->AppendSwitch(switches::kSingleProcess
);
177 // Don't want to try to create a GPU process.
178 command_line
->AppendSwitch(switches::kDisableGpu
);
182 // DomSerializerDelegate.
183 virtual void didSerializeDataForFrame(const WebURL
& frame_web_url
,
184 const WebCString
& data
,
185 PageSerializationStatus status
) {
187 GURL
frame_url(frame_web_url
);
188 // If the all frames are finished saving, check all finish status
189 if (status
== WebPageSerializerClient::AllFramesAreFinished
) {
190 SerializationFinishStatusMap::iterator it
=
191 serialization_finish_status_
.begin();
192 for (; it
!= serialization_finish_status_
.end(); ++it
)
193 ASSERT_TRUE(it
->second
);
198 // Check finish status of current frame.
199 SerializationFinishStatusMap::iterator it
=
200 serialization_finish_status_
.find(frame_url
.spec());
201 // New frame, set initial status as false.
202 if (it
== serialization_finish_status_
.end())
203 serialization_finish_status_
[frame_url
.spec()] = false;
205 it
= serialization_finish_status_
.find(frame_url
.spec());
206 ASSERT_TRUE(it
!= serialization_finish_status_
.end());
207 // In process frame, finish status should be false.
208 ASSERT_FALSE(it
->second
);
210 // Add data to corresponding frame's content.
211 serialized_frame_map_
[frame_url
.spec()] += data
.data();
213 // Current frame is completed saving, change the finish status.
214 if (status
== WebPageSerializerClient::CurrentFrameIsFinished
)
218 bool HasSerializedFrame(const GURL
& frame_url
) {
219 return serialized_frame_map_
.find(frame_url
.spec()) !=
220 serialized_frame_map_
.end();
223 const std::string
& GetSerializedContentForFrame(
224 const GURL
& frame_url
) {
225 return serialized_frame_map_
[frame_url
.spec()];
228 RenderView
* GetRenderView() {
229 // We could have the test on the UI thread get the WebContent's routing ID,
230 // but we know this will be the first RV so skip that and just hardcode it.
231 return RenderView::FromRoutingID(kRenderViewRoutingId
);
234 WebView
* GetWebView() {
235 return GetRenderView()->GetWebView();
238 WebFrame
* GetMainFrame() {
239 return GetWebView()->mainFrame();
242 // Load web page according to input content and relative URLs within
244 void LoadContents(const std::string
& contents
,
245 const GURL
& base_url
,
246 const WebString encoding_info
) {
247 scoped_refptr
<MessageLoopRunner
> runner
= new MessageLoopRunner
;
248 LoadObserver
observer(GetRenderView(), runner
->QuitClosure());
250 // If input encoding is empty, use UTF-8 as default encoding.
251 if (encoding_info
.isEmpty()) {
252 GetMainFrame()->loadHTMLString(contents
, base_url
);
254 WebData
data(contents
.data(), contents
.length());
256 // Do not use WebFrame.LoadHTMLString because it assumes that input
257 // html contents use UTF-8 encoding.
258 // TODO(darin): This should use WebFrame::loadData.
259 WebFrame
* web_frame
= GetMainFrame();
261 ASSERT_TRUE(web_frame
!= NULL
);
263 web_frame
->loadData(data
, "text/html", encoding_info
, base_url
);
269 // Serialize page DOM according to specific page URL. The parameter
270 // recursive_serialization indicates whether we will serialize all
272 void SerializeDomForURL(const GURL
& page_url
,
273 bool recursive_serialization
) {
274 // Find corresponding WebFrame according to page_url.
275 WebFrame
* web_frame
= FindSubFrameByURL(GetWebView(), page_url
);
276 ASSERT_TRUE(web_frame
!= NULL
);
277 WebVector
<WebURL
> links
;
278 links
.assign(&page_url
, 1);
279 WebString file_path
=
280 base::FilePath(FILE_PATH_LITERAL("c:\\dummy.htm")).AsUTF16Unsafe();
281 WebVector
<WebString
> local_paths
;
282 local_paths
.assign(&file_path
, 1);
283 // Start serializing DOM.
284 bool result
= WebPageSerializer::serialize(web_frame
->toWebLocalFrame(),
285 recursive_serialization
,
286 static_cast<WebPageSerializerClient
*>(this),
289 local_directory_name_
.AsUTF16Unsafe());
291 ASSERT_TRUE(serialized_
);
294 void SerializeHTMLDOMWithDocTypeOnRenderer(const GURL
& file_url
) {
295 // Make sure original contents have document type.
296 WebFrame
* web_frame
= FindSubFrameByURL(GetWebView(), file_url
);
297 ASSERT_TRUE(web_frame
!= NULL
);
298 WebDocument doc
= web_frame
->document();
299 ASSERT_TRUE(HasDocType(doc
));
301 SerializeDomForURL(file_url
, false);
302 // Load the serialized contents.
303 ASSERT_TRUE(HasSerializedFrame(file_url
));
304 const std::string
& serialized_contents
=
305 GetSerializedContentForFrame(file_url
);
306 LoadContents(serialized_contents
, file_url
,
307 web_frame
->document().encoding());
308 // Make sure serialized contents still have document type.
309 web_frame
= GetMainFrame();
310 doc
= web_frame
->document();
311 ASSERT_TRUE(HasDocType(doc
));
314 void SerializeHTMLDOMWithoutDocTypeOnRenderer(const GURL
& file_url
) {
315 // Make sure original contents do not have document type.
316 WebFrame
* web_frame
= FindSubFrameByURL(GetWebView(), file_url
);
317 ASSERT_TRUE(web_frame
!= NULL
);
318 WebDocument doc
= web_frame
->document();
319 ASSERT_TRUE(!HasDocType(doc
));
321 SerializeDomForURL(file_url
, false);
322 // Load the serialized contents.
323 ASSERT_TRUE(HasSerializedFrame(file_url
));
324 const std::string
& serialized_contents
=
325 GetSerializedContentForFrame(file_url
);
326 LoadContents(serialized_contents
, file_url
,
327 web_frame
->document().encoding());
328 // Make sure serialized contents do not have document type.
329 web_frame
= GetMainFrame();
330 doc
= web_frame
->document();
331 ASSERT_TRUE(!HasDocType(doc
));
334 void SerializeXMLDocWithBuiltInEntitiesOnRenderer(
335 const GURL
& xml_file_url
, const std::string
& original_contents
) {
337 SerializeDomForURL(xml_file_url
, false);
338 // Compare the serialized contents with original contents.
339 ASSERT_TRUE(HasSerializedFrame(xml_file_url
));
340 const std::string
& serialized_contents
=
341 GetSerializedContentForFrame(xml_file_url
);
342 ASSERT_EQ(original_contents
, serialized_contents
);
345 void SerializeHTMLDOMWithAddingMOTWOnRenderer(
346 const GURL
& file_url
, const std::string
& original_contents
) {
347 // Make sure original contents does not have MOTW;
348 std::string motw_declaration
=
349 WebPageSerializer::generateMarkOfTheWebDeclaration(file_url
).utf8();
350 ASSERT_FALSE(motw_declaration
.empty());
351 // The encoding of original contents is ISO-8859-1, so we convert the MOTW
352 // declaration to ASCII and search whether original contents has it or not.
353 ASSERT_TRUE(std::string::npos
== original_contents
.find(motw_declaration
));
356 SerializeDomForURL(file_url
, false);
357 // Make sure the serialized contents have MOTW ;
358 ASSERT_TRUE(HasSerializedFrame(file_url
));
359 const std::string
& serialized_contents
=
360 GetSerializedContentForFrame(file_url
);
361 ASSERT_FALSE(std::string::npos
==
362 serialized_contents
.find(motw_declaration
));
365 void SerializeHTMLDOMWithNoMetaCharsetInOriginalDocOnRenderer(
366 const GURL
& file_url
) {
367 // Make sure there is no META charset declaration in original document.
368 WebFrame
* web_frame
= FindSubFrameByURL(GetWebView(), file_url
);
369 ASSERT_TRUE(web_frame
!= NULL
);
370 WebDocument doc
= web_frame
->document();
371 ASSERT_TRUE(doc
.isHTMLDocument());
372 WebElement head_element
= doc
.head();
373 ASSERT_TRUE(!head_element
.isNull());
374 // Go through all children of HEAD element.
375 for (WebNode child
= head_element
.firstChild(); !child
.isNull();
376 child
= child
.nextSibling()) {
377 std::string charset_info
;
378 if (IsMetaElement(child
, charset_info
))
379 ASSERT_TRUE(charset_info
.empty());
382 SerializeDomForURL(file_url
, false);
384 // Load the serialized contents.
385 ASSERT_TRUE(HasSerializedFrame(file_url
));
386 const std::string
& serialized_contents
=
387 GetSerializedContentForFrame(file_url
);
388 LoadContents(serialized_contents
, file_url
,
389 web_frame
->document().encoding());
390 // Make sure the first child of HEAD element is META which has charset
391 // declaration in serialized contents.
392 web_frame
= GetMainFrame();
393 ASSERT_TRUE(web_frame
!= NULL
);
394 doc
= web_frame
->document();
395 ASSERT_TRUE(doc
.isHTMLDocument());
396 head_element
= doc
.head();
397 ASSERT_TRUE(!head_element
.isNull());
398 WebNode meta_node
= head_element
.firstChild();
399 ASSERT_TRUE(!meta_node
.isNull());
400 // Get meta charset info.
401 std::string charset_info2
;
402 ASSERT_TRUE(IsMetaElement(meta_node
, charset_info2
));
403 ASSERT_TRUE(!charset_info2
.empty());
404 ASSERT_EQ(charset_info2
,
405 std::string(web_frame
->document().encoding().utf8()));
407 // Make sure no more additional META tags which have charset declaration.
408 for (WebNode child
= meta_node
.nextSibling(); !child
.isNull();
409 child
= child
.nextSibling()) {
410 std::string charset_info
;
411 if (IsMetaElement(child
, charset_info
))
412 ASSERT_TRUE(charset_info
.empty());
416 void SerializeHTMLDOMWithMultipleMetaCharsetInOriginalDocOnRenderer(
417 const GURL
& file_url
) {
418 // Make sure there are multiple META charset declarations in original
420 WebFrame
* web_frame
= FindSubFrameByURL(GetWebView(), file_url
);
421 ASSERT_TRUE(web_frame
!= NULL
);
422 WebDocument doc
= web_frame
->document();
423 ASSERT_TRUE(doc
.isHTMLDocument());
424 WebElement head_ele
= doc
.head();
425 ASSERT_TRUE(!head_ele
.isNull());
426 // Go through all children of HEAD element.
427 int charset_declaration_count
= 0;
428 for (WebNode child
= head_ele
.firstChild(); !child
.isNull();
429 child
= child
.nextSibling()) {
430 std::string charset_info
;
431 if (IsMetaElement(child
, charset_info
) && !charset_info
.empty())
432 charset_declaration_count
++;
434 // The original doc has more than META tags which have charset declaration.
435 ASSERT_TRUE(charset_declaration_count
> 1);
438 SerializeDomForURL(file_url
, false);
440 // Load the serialized contents.
441 ASSERT_TRUE(HasSerializedFrame(file_url
));
442 const std::string
& serialized_contents
=
443 GetSerializedContentForFrame(file_url
);
444 LoadContents(serialized_contents
, file_url
,
445 web_frame
->document().encoding());
446 // Make sure only first child of HEAD element is META which has charset
447 // declaration in serialized contents.
448 web_frame
= GetMainFrame();
449 ASSERT_TRUE(web_frame
!= NULL
);
450 doc
= web_frame
->document();
451 ASSERT_TRUE(doc
.isHTMLDocument());
452 head_ele
= doc
.head();
453 ASSERT_TRUE(!head_ele
.isNull());
454 WebNode meta_node
= head_ele
.firstChild();
455 ASSERT_TRUE(!meta_node
.isNull());
456 // Get meta charset info.
457 std::string charset_info2
;
458 ASSERT_TRUE(IsMetaElement(meta_node
, charset_info2
));
459 ASSERT_TRUE(!charset_info2
.empty());
460 ASSERT_EQ(charset_info2
,
461 std::string(web_frame
->document().encoding().utf8()));
463 // Make sure no more additional META tags which have charset declaration.
464 for (WebNode child
= meta_node
.nextSibling(); !child
.isNull();
465 child
= child
.nextSibling()) {
466 std::string charset_info
;
467 if (IsMetaElement(child
, charset_info
))
468 ASSERT_TRUE(charset_info
.empty());
472 void SerializeHTMLDOMWithEntitiesInTextOnRenderer() {
473 base::FilePath page_file_path
= GetTestFilePath(
474 "dom_serializer", "dom_serializer/htmlentities_in_text.htm");
475 // Get file URL. The URL is dummy URL to identify the following loading
476 // actions. The test content is in constant:original_contents.
477 GURL file_url
= net::FilePathToFileURL(page_file_path
);
478 ASSERT_TRUE(file_url
.SchemeIsFile());
480 static const char* const original_contents
=
481 "<html><body>&<>\"\'</body></html>";
482 // Load the test contents.
483 LoadContents(original_contents
, file_url
, WebString());
485 // Get BODY's text content in DOM.
486 WebFrame
* web_frame
= FindSubFrameByURL(GetWebView(), file_url
);
487 ASSERT_TRUE(web_frame
!= NULL
);
488 WebDocument doc
= web_frame
->document();
489 ASSERT_TRUE(doc
.isHTMLDocument());
490 WebElement body_ele
= doc
.body();
491 ASSERT_TRUE(!body_ele
.isNull());
492 WebNode text_node
= body_ele
.firstChild();
493 ASSERT_TRUE(text_node
.isTextNode());
494 ASSERT_TRUE(std::string(text_node
.createMarkup().utf8()) ==
495 "&<>\"\'");
497 SerializeDomForURL(file_url
, false);
498 // Compare the serialized contents with original contents.
499 ASSERT_TRUE(HasSerializedFrame(file_url
));
500 const std::string
& serialized_contents
=
501 GetSerializedContentForFrame(file_url
);
502 // Compare the serialized contents with original contents to make sure
504 // Because we add MOTW when serializing DOM, so before comparison, we also
505 // need to add MOTW to original_contents.
506 std::string original_str
=
507 WebPageSerializer::generateMarkOfTheWebDeclaration(file_url
).utf8();
508 original_str
+= original_contents
;
509 // Since WebCore now inserts a new HEAD element if there is no HEAD element
510 // when creating BODY element. (Please see
511 // HTMLParser::bodyCreateErrorCheck.) We need to append the HEAD content and
512 // corresponding META content if we find WebCore-generated HEAD element.
513 if (!doc
.head().isNull()) {
514 WebString encoding
= web_frame
->document().encoding();
515 std::string
htmlTag("<html>");
516 std::string::size_type pos
= original_str
.find(htmlTag
);
517 ASSERT_NE(std::string::npos
, pos
);
518 pos
+= htmlTag
.length();
519 std::string
head_part("<head>");
521 WebPageSerializer::generateMetaCharsetDeclaration(encoding
).utf8();
522 head_part
+= "</head>";
523 original_str
.insert(pos
, head_part
);
525 ASSERT_EQ(original_str
, serialized_contents
);
528 void SerializeHTMLDOMWithEntitiesInAttributeValueOnRenderer() {
529 base::FilePath page_file_path
= GetTestFilePath(
530 "dom_serializer", "dom_serializer/htmlentities_in_attribute_value.htm");
531 // Get file URL. The URL is dummy URL to identify the following loading
532 // actions. The test content is in constant:original_contents.
533 GURL file_url
= net::FilePathToFileURL(page_file_path
);
534 ASSERT_TRUE(file_url
.SchemeIsFile());
536 static const char* const original_contents
=
537 "<html><body title=\"&<>"'\"></body></html>";
538 // Load the test contents.
539 LoadContents(original_contents
, file_url
, WebString());
540 // Get value of BODY's title attribute in DOM.
541 WebFrame
* web_frame
= FindSubFrameByURL(GetWebView(), file_url
);
542 ASSERT_TRUE(web_frame
!= NULL
);
543 WebDocument doc
= web_frame
->document();
544 ASSERT_TRUE(doc
.isHTMLDocument());
545 WebElement body_ele
= doc
.body();
546 ASSERT_TRUE(!body_ele
.isNull());
547 WebString value
= body_ele
.getAttribute("title");
548 ASSERT_TRUE(std::string(value
.utf8()) == "&<>\"\'");
550 SerializeDomForURL(file_url
, false);
551 // Compare the serialized contents with original contents.
552 ASSERT_TRUE(HasSerializedFrame(file_url
));
553 const std::string
& serialized_contents
=
554 GetSerializedContentForFrame(file_url
);
555 // Compare the serialized contents with original contents to make sure
557 std::string original_str
=
558 WebPageSerializer::generateMarkOfTheWebDeclaration(file_url
).utf8();
559 original_str
+= original_contents
;
561 WebString encoding
= web_frame
->document().encoding();
562 std::string
htmlTag("<html>");
563 std::string::size_type pos
= original_str
.find(htmlTag
);
564 ASSERT_NE(std::string::npos
, pos
);
565 pos
+= htmlTag
.length();
566 std::string
head_part("<head>");
568 WebPageSerializer::generateMetaCharsetDeclaration(encoding
).utf8();
569 head_part
+= "</head>";
570 original_str
.insert(pos
, head_part
);
572 ASSERT_EQ(original_str
, serialized_contents
);
575 void SerializeHTMLDOMWithNonStandardEntitiesOnRenderer(const GURL
& file_url
) {
576 // Get value of BODY's title attribute in DOM.
577 WebFrame
* web_frame
= FindSubFrameByURL(GetWebView(), file_url
);
578 WebDocument doc
= web_frame
->document();
579 ASSERT_TRUE(doc
.isHTMLDocument());
580 WebElement body_element
= doc
.body();
581 // Unescaped string for "%⊅¹'".
582 static const wchar_t parsed_value
[] = {
583 '%', 0x2285, 0x00b9, '\'', 0
585 WebString value
= body_element
.getAttribute("title");
586 ASSERT_TRUE(base::UTF16ToWide(value
) == parsed_value
);
587 ASSERT_TRUE(base::UTF16ToWide(body_element
.innerText()) == parsed_value
);
590 SerializeDomForURL(file_url
, false);
591 // Check the serialized string.
592 ASSERT_TRUE(HasSerializedFrame(file_url
));
593 const std::string
& serialized_contents
=
594 GetSerializedContentForFrame(file_url
);
595 // Confirm that the serialized string has no non-standard HTML entities.
596 ASSERT_EQ(std::string::npos
, serialized_contents
.find("%"));
597 ASSERT_EQ(std::string::npos
, serialized_contents
.find("⊅"));
598 ASSERT_EQ(std::string::npos
, serialized_contents
.find("¹"));
599 ASSERT_EQ(std::string::npos
, serialized_contents
.find("'"));
602 void SerializeHTMLDOMWithBaseTagOnRenderer(const GURL
& file_url
,
603 const GURL
& path_dir_url
) {
604 // There are total 2 available base tags in this test file.
605 const int kTotalBaseTagCountInTestFile
= 2;
607 // Since for this test, we assume there is no savable sub-resource links for
608 // this test file, also all links are relative URLs in this test file, so we
609 // need to check those relative URLs and make sure document has BASE tag.
610 WebFrame
* web_frame
= FindSubFrameByURL(GetWebView(), file_url
);
611 ASSERT_TRUE(web_frame
!= NULL
);
612 WebDocument doc
= web_frame
->document();
613 ASSERT_TRUE(doc
.isHTMLDocument());
614 // Go through all descent nodes.
615 WebElementCollection all
= doc
.all();
616 int original_base_tag_count
= 0;
617 for (WebElement element
= all
.firstItem(); !element
.isNull();
618 element
= all
.nextItem()) {
619 if (element
.hasHTMLTagName("base")) {
620 original_base_tag_count
++;
623 WebString value
= GetSubResourceLinkFromElement(element
);
624 if (value
.isNull() && element
.hasHTMLTagName("a")) {
625 value
= element
.getAttribute("href");
629 // Each link is relative link.
630 if (!value
.isNull()) {
631 GURL
link(value
.utf8());
632 ASSERT_TRUE(link
.scheme().empty());
636 ASSERT_EQ(original_base_tag_count
, kTotalBaseTagCountInTestFile
);
637 // Make sure in original document, the base URL is not equal with the
639 GURL
original_base_url(doc
.baseURL());
640 ASSERT_NE(original_base_url
, path_dir_url
);
643 SerializeDomForURL(file_url
, false);
645 // Load the serialized contents.
646 ASSERT_TRUE(HasSerializedFrame(file_url
));
647 const std::string
& serialized_contents
=
648 GetSerializedContentForFrame(file_url
);
649 LoadContents(serialized_contents
, file_url
,
650 web_frame
->document().encoding());
652 // Make sure all links are absolute URLs and doc there are some number of
653 // BASE tags in serialized HTML data. Each of those BASE tags have same base
654 // URL which is as same as URL of current test file.
655 web_frame
= GetMainFrame();
656 ASSERT_TRUE(web_frame
!= NULL
);
657 doc
= web_frame
->document();
658 ASSERT_TRUE(doc
.isHTMLDocument());
659 // Go through all descent nodes.
661 int new_base_tag_count
= 0;
662 for (WebNode node
= all
.firstItem(); !node
.isNull();
663 node
= all
.nextItem()) {
664 if (!node
.isElementNode())
666 WebElement element
= node
.to
<WebElement
>();
667 if (element
.hasHTMLTagName("base")) {
668 new_base_tag_count
++;
671 WebString value
= GetSubResourceLinkFromElement(element
);
672 if (value
.isNull() && element
.hasHTMLTagName("a")) {
673 value
= element
.getAttribute("href");
677 // Each link is absolute link.
678 if (!value
.isNull()) {
679 GURL
link(std::string(value
.utf8()));
680 ASSERT_FALSE(link
.scheme().empty());
684 // We have one more added BASE tag which is generated by JavaScript.
685 ASSERT_EQ(new_base_tag_count
, original_base_tag_count
+ 1);
686 // Make sure in new document, the base URL is equal with the |path_dir_url|.
687 GURL
new_base_url(doc
.baseURL());
688 ASSERT_EQ(new_base_url
, path_dir_url
);
691 void SerializeHTMLDOMWithEmptyHeadOnRenderer() {
692 base::FilePath page_file_path
= GetTestFilePath(
693 "dom_serializer", "empty_head.htm");
694 GURL file_url
= net::FilePathToFileURL(page_file_path
);
695 ASSERT_TRUE(file_url
.SchemeIsFile());
697 // Load the test html content.
698 static const char* const empty_head_contents
=
699 "<html><head></head><body>hello world</body></html>";
700 LoadContents(empty_head_contents
, file_url
, WebString());
702 // Make sure the head tag is empty.
703 WebFrame
* web_frame
= GetMainFrame();
704 ASSERT_TRUE(web_frame
!= NULL
);
705 WebDocument doc
= web_frame
->document();
706 ASSERT_TRUE(doc
.isHTMLDocument());
707 WebElement head_element
= doc
.head();
708 ASSERT_TRUE(!head_element
.isNull());
709 ASSERT_TRUE(!head_element
.hasChildNodes());
710 ASSERT_TRUE(head_element
.childNodes().length() == 0);
713 SerializeDomForURL(file_url
, false);
714 // Make sure the serialized contents have META ;
715 ASSERT_TRUE(HasSerializedFrame(file_url
));
716 const std::string
& serialized_contents
=
717 GetSerializedContentForFrame(file_url
);
719 // Reload serialized contents and make sure there is only one META tag.
720 LoadContents(serialized_contents
, file_url
,
721 web_frame
->document().encoding());
722 web_frame
= GetMainFrame();
723 ASSERT_TRUE(web_frame
!= NULL
);
724 doc
= web_frame
->document();
725 ASSERT_TRUE(doc
.isHTMLDocument());
726 head_element
= doc
.head();
727 ASSERT_TRUE(!head_element
.isNull());
728 ASSERT_TRUE(head_element
.hasChildNodes());
729 ASSERT_TRUE(head_element
.childNodes().length() == 1);
730 WebNode meta_node
= head_element
.firstChild();
731 ASSERT_TRUE(!meta_node
.isNull());
732 // Get meta charset info.
733 std::string charset_info
;
734 ASSERT_TRUE(IsMetaElement(meta_node
, charset_info
));
735 ASSERT_TRUE(!charset_info
.empty());
736 ASSERT_EQ(charset_info
,
737 std::string(web_frame
->document().encoding().utf8()));
739 // Check the body's first node is text node and its contents are
741 WebElement body_element
= doc
.body();
742 ASSERT_TRUE(!body_element
.isNull());
743 WebNode text_node
= body_element
.firstChild();
744 ASSERT_TRUE(text_node
.isTextNode());
745 WebString text_node_contents
= text_node
.nodeValue();
746 ASSERT_TRUE(std::string(text_node_contents
.utf8()) == "hello world");
749 void SerializeDocumentWithDownloadedIFrameOnRenderer(const GURL
& file_url
) {
750 // Do a recursive serialization. We pass if we don't crash.
751 SerializeDomForURL(file_url
, true);
754 void SubResourceForElementsInNonHTMLNamespaceOnRenderer(
755 const GURL
& file_url
) {
756 WebFrame
* web_frame
= FindSubFrameByURL(GetWebView(), file_url
);
757 ASSERT_TRUE(web_frame
!= NULL
);
758 WebDocument doc
= web_frame
->document();
759 WebNode lastNodeInBody
= doc
.body().lastChild();
760 ASSERT_EQ(WebNode::ElementNode
, lastNodeInBody
.nodeType());
761 WebString uri
= GetSubResourceLinkFromElement(
762 lastNodeInBody
.to
<WebElement
>());
763 EXPECT_TRUE(uri
.isNull());
767 // Map frame_url to corresponding serialized_content.
768 typedef base::hash_map
<std::string
, std::string
> SerializedFrameContentMap
;
769 SerializedFrameContentMap serialized_frame_map_
;
770 // Map frame_url to corresponding status of serialization finish.
771 typedef base::hash_map
<std::string
, bool> SerializationFinishStatusMap
;
772 SerializationFinishStatusMap serialization_finish_status_
;
773 // Flag indicates whether the process of serializing DOM is finished or not.
775 // The local_directory_name_ is dummy relative path of directory which
776 // contain all saved auxiliary files included all sub frames and resources.
777 const base::FilePath local_directory_name_
;
779 content::RoutingIDManglingDisabler mangling_disabler_
;
782 // If original contents have document type, the serialized contents also have
784 IN_PROC_BROWSER_TEST_F(DomSerializerTests
, SerializeHTMLDOMWithDocType
) {
785 base::FilePath page_file_path
=
786 GetTestFilePath("dom_serializer", "youtube_1.htm");
787 GURL file_url
= net::FilePathToFileURL(page_file_path
);
788 ASSERT_TRUE(file_url
.SchemeIsFile());
789 // Load the test file.
790 NavigateToURL(shell(), file_url
);
792 PostTaskToInProcessRendererAndWait(
793 base::Bind(&DomSerializerTests::SerializeHTMLDOMWithDocTypeOnRenderer
,
794 base::Unretained(this), file_url
));
797 // If original contents do not have document type, the serialized contents
798 // also do not have document type.
799 IN_PROC_BROWSER_TEST_F(DomSerializerTests
, SerializeHTMLDOMWithoutDocType
) {
800 base::FilePath page_file_path
=
801 GetTestFilePath("dom_serializer", "youtube_2.htm");
802 GURL file_url
= net::FilePathToFileURL(page_file_path
);
803 ASSERT_TRUE(file_url
.SchemeIsFile());
804 // Load the test file.
805 NavigateToURL(shell(), file_url
);
807 PostTaskToInProcessRendererAndWait(
809 &DomSerializerTests::SerializeHTMLDOMWithoutDocTypeOnRenderer
,
810 base::Unretained(this), file_url
));
813 // Serialize XML document which has all 5 built-in entities. After
814 // finishing serialization, the serialized contents should be same
815 // with original XML document.
817 // TODO(tiger@opera.com): Disabled in preparation of page serializer merge --
818 // XML headers are handled differently in the merged serializer.
819 // Bug: http://crbug.com/328354
820 IN_PROC_BROWSER_TEST_F(DomSerializerTests
,
821 DISABLED_SerializeXMLDocWithBuiltInEntities
) {
822 base::FilePath page_file_path
=
823 GetTestFilePath("dom_serializer", "note.html");
824 base::FilePath xml_file_path
= GetTestFilePath("dom_serializer", "note.xml");
825 // Read original contents for later comparison.
826 std::string original_contents
;
827 ASSERT_TRUE(base::ReadFileToString(xml_file_path
, &original_contents
));
829 GURL file_url
= net::FilePathToFileURL(page_file_path
);
830 GURL xml_file_url
= net::FilePathToFileURL(xml_file_path
);
831 ASSERT_TRUE(file_url
.SchemeIsFile());
832 // Load the test file.
833 NavigateToURL(shell(), file_url
);
835 PostTaskToInProcessRendererAndWait(
837 &DomSerializerTests::SerializeXMLDocWithBuiltInEntitiesOnRenderer
,
838 base::Unretained(this), xml_file_url
, original_contents
));
841 // When serializing DOM, we add MOTW declaration before html tag.
842 IN_PROC_BROWSER_TEST_F(DomSerializerTests
, SerializeHTMLDOMWithAddingMOTW
) {
843 base::FilePath page_file_path
=
844 GetTestFilePath("dom_serializer", "youtube_2.htm");
845 // Read original contents for later comparison .
846 std::string original_contents
;
847 ASSERT_TRUE(base::ReadFileToString(page_file_path
, &original_contents
));
849 GURL file_url
= net::FilePathToFileURL(page_file_path
);
850 ASSERT_TRUE(file_url
.SchemeIsFile());
852 // Load the test file.
853 NavigateToURL(shell(), file_url
);
855 PostTaskToInProcessRendererAndWait(
857 &DomSerializerTests::SerializeHTMLDOMWithAddingMOTWOnRenderer
,
858 base::Unretained(this), file_url
, original_contents
));
861 // When serializing DOM, we will add the META which have correct charset
862 // declaration as first child of HEAD element for resolving WebKit bug:
863 // http://bugs.webkit.org/show_bug.cgi?id=16621 even the original document
864 // does not have META charset declaration.
865 IN_PROC_BROWSER_TEST_F(DomSerializerTests
,
866 SerializeHTMLDOMWithNoMetaCharsetInOriginalDoc
) {
867 base::FilePath page_file_path
=
868 GetTestFilePath("dom_serializer", "youtube_1.htm");
870 GURL file_url
= net::FilePathToFileURL(page_file_path
);
871 ASSERT_TRUE(file_url
.SchemeIsFile());
872 // Load the test file.
873 NavigateToURL(shell(), file_url
);
875 PostTaskToInProcessRendererAndWait(
877 &DomSerializerTests::
878 SerializeHTMLDOMWithNoMetaCharsetInOriginalDocOnRenderer
,
879 base::Unretained(this), file_url
));
882 // When serializing DOM, if the original document has multiple META charset
883 // declaration, we will add the META which have correct charset declaration
884 // as first child of HEAD element and remove all original META charset
886 IN_PROC_BROWSER_TEST_F(DomSerializerTests
,
887 SerializeHTMLDOMWithMultipleMetaCharsetInOriginalDoc
) {
888 base::FilePath page_file_path
=
889 GetTestFilePath("dom_serializer", "youtube_2.htm");
891 GURL file_url
= net::FilePathToFileURL(page_file_path
);
892 ASSERT_TRUE(file_url
.SchemeIsFile());
893 // Load the test file.
894 NavigateToURL(shell(), file_url
);
896 PostTaskToInProcessRendererAndWait(
898 &DomSerializerTests::
899 SerializeHTMLDOMWithMultipleMetaCharsetInOriginalDocOnRenderer
,
900 base::Unretained(this), file_url
));
903 // Test situation of html entities in text when serializing HTML DOM.
904 IN_PROC_BROWSER_TEST_F(DomSerializerTests
, SerializeHTMLDOMWithEntitiesInText
) {
905 // Need to spin up the renderer and also navigate to a file url so that the
906 // renderer code doesn't attempt a fork when it sees a load to file scheme
907 // from non-file scheme.
908 NavigateToURL(shell(), GetTestUrl(".", "simple_page.html"));
910 PostTaskToInProcessRendererAndWait(
912 &DomSerializerTests::SerializeHTMLDOMWithEntitiesInTextOnRenderer
,
913 base::Unretained(this)));
916 // Test situation of html entities in attribute value when serializing
918 // This test started to fail at WebKit r65388. See http://crbug.com/52279.
920 // TODO(tiger@opera.com): Disabled in preparation of page serializer merge --
921 // Some attributes are handled differently in the merged serializer.
922 // Bug: http://crbug.com/328354
923 IN_PROC_BROWSER_TEST_F(DomSerializerTests
,
924 DISABLED_SerializeHTMLDOMWithEntitiesInAttributeValue
) {
925 // Need to spin up the renderer and also navigate to a file url so that the
926 // renderer code doesn't attempt a fork when it sees a load to file scheme
927 // from non-file scheme.
928 NavigateToURL(shell(), GetTestUrl(".", "simple_page.html"));
930 PostTaskToInProcessRendererAndWait(
932 &DomSerializerTests::
933 SerializeHTMLDOMWithEntitiesInAttributeValueOnRenderer
,
934 base::Unretained(this)));
937 // Test situation of non-standard HTML entities when serializing HTML DOM.
938 // This test started to fail at WebKit r65351. See http://crbug.com/52279.
939 IN_PROC_BROWSER_TEST_F(DomSerializerTests
,
940 SerializeHTMLDOMWithNonStandardEntities
) {
941 // Make a test file URL and load it.
942 base::FilePath page_file_path
= GetTestFilePath(
943 "dom_serializer", "nonstandard_htmlentities.htm");
944 GURL file_url
= net::FilePathToFileURL(page_file_path
);
945 NavigateToURL(shell(), file_url
);
947 PostTaskToInProcessRendererAndWait(
949 &DomSerializerTests::
950 SerializeHTMLDOMWithNonStandardEntitiesOnRenderer
,
951 base::Unretained(this), file_url
));
954 // Test situation of BASE tag in original document when serializing HTML DOM.
955 // When serializing, we should comment the BASE tag, append a new BASE tag.
956 // rewrite all the savable URLs to relative local path, and change other URLs
959 // TODO(tiger@opera.com): Disabled in preparation of page serializer merge --
960 // Base tags are handled a bit different in merged version.
961 // Bug: http://crbug.com/328354
962 IN_PROC_BROWSER_TEST_F(DomSerializerTests
,
963 DISABLED_SerializeHTMLDOMWithBaseTag
) {
964 base::FilePath page_file_path
= GetTestFilePath(
965 "dom_serializer", "html_doc_has_base_tag.htm");
967 // Get page dir URL which is base URL of this file.
968 base::FilePath dir_name
= page_file_path
.DirName();
969 dir_name
= dir_name
.Append(
970 base::FilePath::StringType(base::FilePath::kSeparators
[0], 1));
971 GURL path_dir_url
= net::FilePathToFileURL(dir_name
);
974 GURL file_url
= net::FilePathToFileURL(page_file_path
);
975 ASSERT_TRUE(file_url
.SchemeIsFile());
976 // Load the test file.
977 NavigateToURL(shell(), file_url
);
979 PostTaskToInProcessRendererAndWait(
981 &DomSerializerTests::SerializeHTMLDOMWithBaseTagOnRenderer
,
982 base::Unretained(this), file_url
, path_dir_url
));
985 // Serializing page which has an empty HEAD tag.
986 IN_PROC_BROWSER_TEST_F(DomSerializerTests
, SerializeHTMLDOMWithEmptyHead
) {
987 // Need to spin up the renderer and also navigate to a file url so that the
988 // renderer code doesn't attempt a fork when it sees a load to file scheme
989 // from non-file scheme.
990 NavigateToURL(shell(), GetTestUrl(".", "simple_page.html"));
992 PostTaskToInProcessRendererAndWait(
993 base::Bind(&DomSerializerTests::SerializeHTMLDOMWithEmptyHeadOnRenderer
,
994 base::Unretained(this)));
997 // Test that we don't crash when the page contains an iframe that
998 // was handled as a download (http://crbug.com/42212).
999 IN_PROC_BROWSER_TEST_F(DomSerializerTests
,
1000 SerializeDocumentWithDownloadedIFrame
) {
1001 base::FilePath page_file_path
= GetTestFilePath(
1002 "dom_serializer", "iframe-src-is-exe.htm");
1003 GURL file_url
= net::FilePathToFileURL(page_file_path
);
1004 ASSERT_TRUE(file_url
.SchemeIsFile());
1005 // Load the test file.
1006 NavigateToURL(shell(), file_url
);
1008 PostTaskToInProcessRendererAndWait(
1010 &DomSerializerTests::
1011 SerializeDocumentWithDownloadedIFrameOnRenderer
,
1012 base::Unretained(this), file_url
));
1015 IN_PROC_BROWSER_TEST_F(DomSerializerTests
,
1016 SubResourceForElementsInNonHTMLNamespace
) {
1017 base::FilePath page_file_path
= GetTestFilePath(
1018 "dom_serializer", "non_html_namespace.htm");
1019 GURL file_url
= net::FilePathToFileURL(page_file_path
);
1020 NavigateToURL(shell(), file_url
);
1022 PostTaskToInProcessRendererAndWait(
1024 &DomSerializerTests::
1025 SubResourceForElementsInNonHTMLNamespaceOnRenderer
,
1026 base::Unretained(this), file_url
));
1029 } // namespace content