- add sources.
[platform/framework/web/crosswalk.git] / src / chrome_frame / test / html_util_unittests.cc
1 // Copyright (c) 2012 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 <windows.h>
6 #include <atlsecurity.h>
7 #include <shellapi.h>
8 #include <string>
9 #include <vector>
10
11 #include "base/basictypes.h"
12 #include "base/file_util.h"
13 #include "base/memory/ref_counted.h"
14 #include "base/memory/scoped_handle.h"
15 #include "base/message_loop/message_loop.h"
16 #include "base/path_service.h"
17 #include "base/strings/utf_string_conversions.h"
18 #include "net/base/net_util.h"
19
20 #include "chrome/browser/automation/url_request_automation_job.h"
21 #include "chrome/common/chrome_version_info.h"
22 #include "chrome_frame/chrome_frame_automation.h"
23 #include "chrome_frame/chrome_frame_delegate.h"
24 #include "chrome_frame/html_utils.h"
25 #include "testing/gtest/include/gtest/gtest.h"
26 #include "webkit/common/user_agent/user_agent_util.h"
27
28 const char kChromeFrameUserAgent[] = "chromeframe";
29
30 class HtmlUtilUnittest : public testing::Test {
31  protected:
32   // Constructor
33   HtmlUtilUnittest() {}
34
35   // Returns the test path given a test case.
36   virtual bool GetTestPath(const std::string& test_case, base::FilePath* path) {
37     if (!path) {
38       NOTREACHED();
39       return false;
40     }
41
42     base::FilePath test_path;
43     if (!PathService::Get(base::DIR_SOURCE_ROOT, &test_path)) {
44       NOTREACHED();
45       return false;
46     }
47
48     test_path = test_path.AppendASCII("chrome_frame");
49     test_path = test_path.AppendASCII("test");
50     test_path = test_path.AppendASCII("html_util_test_data");
51     test_path = test_path.AppendASCII(test_case);
52
53     *path = test_path;
54     return true;
55   }
56
57   virtual bool GetTestData(const std::string& test_case, std::wstring* data) {
58     if (!data) {
59       NOTREACHED();
60       return false;
61     }
62
63     base::FilePath path;
64     if (!GetTestPath(test_case, &path)) {
65       NOTREACHED();
66       return false;
67     }
68
69     std::string raw_data;
70     base::ReadFileToString(path, &raw_data);
71
72     // Convert to wide using the "best effort" assurance described in
73     // string_util.h
74     data->assign(UTF8ToWide(raw_data));
75     return true;
76   }
77 };
78
79 TEST_F(HtmlUtilUnittest, BasicTest) {
80   std::wstring test_data;
81   GetTestData("basic_test.html", &test_data);
82
83   HTMLScanner scanner(test_data.c_str());
84
85   // Grab the meta tag from the document and ensure that we get exactly one.
86   HTMLScanner::StringRangeList tag_list;
87   scanner.GetTagsByName(L"meta", &tag_list, L"body");
88   ASSERT_EQ(1, tag_list.size());
89
90   // Pull out the http-equiv attribute and check its value:
91   HTMLScanner::StringRange attribute_value;
92   EXPECT_TRUE(tag_list[0].GetTagAttribute(L"http-equiv", &attribute_value));
93   EXPECT_TRUE(attribute_value.Equals(L"X-UA-Compatible"));
94
95   // Pull out the content attribute and check its value:
96   EXPECT_TRUE(tag_list[0].GetTagAttribute(L"content", &attribute_value));
97   EXPECT_TRUE(attribute_value.Equals(L"chrome=1"));
98 }
99
100 TEST_F(HtmlUtilUnittest, QuotesTest) {
101   std::wstring test_data;
102   GetTestData("quotes_test.html", &test_data);
103
104   HTMLScanner scanner(test_data.c_str());
105
106   // Grab the meta tag from the document and ensure that we get exactly one.
107   HTMLScanner::StringRangeList tag_list;
108   scanner.GetTagsByName(L"meta", &tag_list, L"body");
109   ASSERT_EQ(1, tag_list.size());
110
111   // Pull out the http-equiv attribute and check its value:
112   HTMLScanner::StringRange attribute_value;
113   EXPECT_TRUE(tag_list[0].GetTagAttribute(L"http-equiv", &attribute_value));
114   EXPECT_TRUE(attribute_value.Equals(L"X-UA-Compatible"));
115
116   // Pull out the content attribute and check its value:
117   EXPECT_TRUE(tag_list[0].GetTagAttribute(L"content", &attribute_value));
118   EXPECT_TRUE(attribute_value.Equals(L"chrome=1"));
119 }
120
121 TEST_F(HtmlUtilUnittest, DegenerateCasesTest) {
122   std::wstring test_data;
123   GetTestData("degenerate_cases_test.html", &test_data);
124
125   HTMLScanner scanner(test_data.c_str());
126
127   // Scan for meta tags in the document. We expect not to pick up the one
128   // that appears to be there since it is technically inside a quote block.
129   HTMLScanner::StringRangeList tag_list;
130   scanner.GetTagsByName(L"meta", &tag_list, L"body");
131   EXPECT_TRUE(tag_list.empty());
132 }
133
134 TEST_F(HtmlUtilUnittest, MultipleTagsTest) {
135   std::wstring test_data;
136   GetTestData("multiple_tags.html", &test_data);
137
138   HTMLScanner scanner(test_data.c_str());
139
140   // Grab the meta tag from the document and ensure that we get exactly three.
141   HTMLScanner::StringRangeList tag_list;
142   scanner.GetTagsByName(L"meta", &tag_list, L"body");
143   EXPECT_EQ(7, tag_list.size());
144
145   // Pull out the content attribute for each tag and check its value:
146   HTMLScanner::StringRange attribute_value;
147   HTMLScanner::StringRangeList::const_iterator tag_list_iter(
148       tag_list.begin());
149   int valid_tag_count = 0;
150   for (; tag_list_iter != tag_list.end(); tag_list_iter++) {
151     HTMLScanner::StringRange attribute_value;
152     if (tag_list_iter->GetTagAttribute(L"http-equiv", &attribute_value) &&
153         attribute_value.Equals(L"X-UA-Compatible")) {
154       EXPECT_TRUE(tag_list_iter->GetTagAttribute(L"content", &attribute_value));
155       EXPECT_TRUE(attribute_value.Equals(L"chrome=1"));
156       valid_tag_count++;
157     }
158   }
159   EXPECT_EQ(3, valid_tag_count);
160 }
161
162 TEST_F(HtmlUtilUnittest, ShortDegenerateTest1) {
163   std::wstring test_data(
164       L"<foo><META http-equiv=X-UA-Compatible content='chrome=1'");
165
166   HTMLScanner scanner(test_data.c_str());
167
168   // Scan for meta tags in the document. We expect not to pick up the one
169   // that is there since it is not properly closed.
170   HTMLScanner::StringRangeList tag_list;
171   scanner.GetTagsByName(L"meta", &tag_list, L"body");
172   EXPECT_TRUE(tag_list.empty());
173 }
174
175 TEST_F(HtmlUtilUnittest, ShortDegenerateTest2) {
176   std::wstring test_data(
177     L"<foo <META http-equiv=X-UA-Compatible content='chrome=1'/>");
178
179   HTMLScanner scanner(test_data.c_str());
180
181   // Scan for meta tags in the document. We expect not to pick up the one
182   // that appears to be there since it is inside a non-closed tag.
183   HTMLScanner::StringRangeList tag_list;
184   scanner.GetTagsByName(L"meta", &tag_list, L"body");
185   EXPECT_TRUE(tag_list.empty());
186 }
187
188 TEST_F(HtmlUtilUnittest, QuoteInsideHTMLCommentTest) {
189   std::wstring test_data(
190     L"<!-- comment' --><META http-equiv=X-UA-Compatible content='chrome=1'/>");
191
192   HTMLScanner scanner(test_data.c_str());
193
194   // Grab the meta tag from the document and ensure that we get exactly one.
195   HTMLScanner::StringRangeList tag_list;
196   scanner.GetTagsByName(L"meta", &tag_list, L"body");
197   ASSERT_EQ(1, tag_list.size());
198
199   // Pull out the http-equiv attribute and check its value:
200   HTMLScanner::StringRange attribute_value;
201   EXPECT_TRUE(tag_list[0].GetTagAttribute(L"http-equiv", &attribute_value));
202   EXPECT_TRUE(attribute_value.Equals(L"X-UA-Compatible"));
203
204   // Pull out the content attribute and check its value:
205   EXPECT_TRUE(tag_list[0].GetTagAttribute(L"content", &attribute_value));
206   EXPECT_TRUE(attribute_value.Equals(L"chrome=1"));
207 }
208
209 TEST_F(HtmlUtilUnittest, CloseTagInsideHTMLCommentTest) {
210   std::wstring test_data(
211     L"<!-- comment> <META http-equiv=X-UA-Compatible content='chrome=1'/>-->");
212
213   HTMLScanner scanner(test_data.c_str());
214
215   // Ensure that the the meta tag is NOT detected.
216   HTMLScanner::StringRangeList tag_list;
217   scanner.GetTagsByName(L"meta", &tag_list, L"body");
218   ASSERT_TRUE(tag_list.empty());
219 }
220
221 TEST_F(HtmlUtilUnittest, IEConditionalCommentTest) {
222   std::wstring test_data(
223       L"<!--[if lte IE 8]><META http-equiv=X-UA-Compatible content='chrome=1'/>"
224       L"<![endif]-->");
225
226   HTMLScanner scanner(test_data.c_str());
227
228   // Ensure that the the meta tag IS detected.
229   HTMLScanner::StringRangeList tag_list;
230   scanner.GetTagsByName(L"meta", &tag_list, L"body");
231   ASSERT_EQ(1, tag_list.size());
232 }
233
234 TEST_F(HtmlUtilUnittest, IEConditionalCommentWithNestedCommentTest) {
235   std::wstring test_data(
236       L"<!--[if IE]><!--<META http-equiv=X-UA-Compatible content='chrome=1'/>"
237       L"--><![endif]-->");
238
239   HTMLScanner scanner(test_data.c_str());
240
241   // Ensure that the the meta tag IS NOT detected.
242   HTMLScanner::StringRangeList tag_list;
243   scanner.GetTagsByName(L"meta", &tag_list, L"body");
244   ASSERT_TRUE(tag_list.empty());
245 }
246
247 TEST_F(HtmlUtilUnittest, IEConditionalCommentWithMultipleNestedTagsTest) {
248   std::wstring test_data(
249       L"<!--[if lte IE 8]>        <META http-equiv=X-UA-Compatible "
250       L"content='chrome=1'/><foo bar></foo><foo baz/><![endif]-->"
251       L"<boo hoo><boo hah>");
252
253   HTMLScanner scanner(test_data.c_str());
254
255   // Ensure that the the meta tag IS detected.
256   HTMLScanner::StringRangeList meta_tag_list;
257   scanner.GetTagsByName(L"meta", &meta_tag_list, L"body");
258   ASSERT_EQ(1, meta_tag_list.size());
259
260   // Ensure that the foo tags are also detected.
261   HTMLScanner::StringRangeList foo_tag_list;
262   scanner.GetTagsByName(L"foo", &foo_tag_list, L"body");
263   ASSERT_EQ(2, foo_tag_list.size());
264
265   // Ensure that the boo tags are also detected.
266   HTMLScanner::StringRangeList boo_tag_list;
267   scanner.GetTagsByName(L"boo", &boo_tag_list, L"body");
268   ASSERT_EQ(2, boo_tag_list.size());
269 }
270
271 TEST_F(HtmlUtilUnittest, IEConditionalCommentWithAlternateEndingTest) {
272   std::wstring test_data(
273       L"<!--[if lte IE 8]>        <META http-equiv=X-UA-Compatible "
274       L"content='chrome=1'/><foo bar></foo><foo baz/><![endif]>"
275       L"<boo hoo><!--><boo hah>");
276
277   HTMLScanner scanner(test_data.c_str());
278
279   // Ensure that the the meta tag IS detected.
280   HTMLScanner::StringRangeList meta_tag_list;
281   scanner.GetTagsByName(L"meta", &meta_tag_list, L"body");
282   ASSERT_EQ(1, meta_tag_list.size());
283
284   // Ensure that the foo tags are also detected.
285   HTMLScanner::StringRangeList foo_tag_list;
286   scanner.GetTagsByName(L"foo", &foo_tag_list, L"body");
287   ASSERT_EQ(2, foo_tag_list.size());
288
289   // Ensure that the boo tags are also detected.
290   HTMLScanner::StringRangeList boo_tag_list;
291   scanner.GetTagsByName(L"boo", &boo_tag_list, L"body");
292   ASSERT_EQ(2, boo_tag_list.size());
293 }
294
295 TEST_F(HtmlUtilUnittest, IEConditionalCommentNonTerminatedTest) {
296   // This test shouldn't detect any tags up until the end of the conditional
297   // comment tag.
298   std::wstring test_data(
299       L"<!--[if lte IE 8>        <META http-equiv=X-UA-Compatible "
300       L"content='chrome=1'/><foo bar></foo><foo baz/><![endif]>"
301       L"<boo hoo><!--><boo hah>");
302
303   HTMLScanner scanner(test_data.c_str());
304
305   // Ensure that the the meta tag IS NOT detected.
306   HTMLScanner::StringRangeList meta_tag_list;
307   scanner.GetTagsByName(L"meta", &meta_tag_list, L"body");
308   ASSERT_TRUE(meta_tag_list.empty());
309
310   // Ensure that the foo tags are NOT detected.
311   HTMLScanner::StringRangeList foo_tag_list;
312   scanner.GetTagsByName(L"foo", &foo_tag_list, L"body");
313   ASSERT_TRUE(foo_tag_list.empty());
314
315   // Ensure that the boo tags are detected.
316   HTMLScanner::StringRangeList boo_tag_list;
317   scanner.GetTagsByName(L"boo", &boo_tag_list, L"body");
318   ASSERT_EQ(2, boo_tag_list.size());
319 }
320
321 struct UserAgentTestCase {
322   std::string input_;
323   std::string expected_;
324 } user_agent_test_cases[] = {
325   {
326     "", ""
327   }, {
328     "Mozilla/4.7 [en] (WinNT; U)",
329     "Mozilla/4.7 [en] (WinNT; U; chromeframe/0.0.0.0)"
330   }, {
331     "Mozilla/4.0 (compatible; MSIE 5.01; Windows NT)",
332     "Mozilla/4.0 (compatible; MSIE 5.01; Windows NT; chromeframe/0.0.0.0)"
333   }, {
334     "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0; T312461; "
335         ".NET CLR 1.1.4322)",
336     "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0; T312461; "
337         ".NET CLR 1.1.4322; chromeframe/0.0.0.0)"
338   }, {
339     "Mozilla/4.0 (compatible; MSIE 5.0; Windows NT 4.0) Opera 5.11 [en]",
340     "Mozilla/4.0 (compatible; MSIE 5.0; Windows NT 4.0; chromeframe/0.0.0.0) "
341         "Opera 5.11 [en]"
342   }, {
343     "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)",
344     "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0; "
345         "chromeframe/0.0.0.0)"
346   }, {
347     "Mozilla/5.0 (Windows; U; Windows NT 5.0; en-US; rv:1.0.2) "
348         "Gecko/20030208 Netscape/7.02",
349     "Mozilla/5.0 (Windows; U; Windows NT 5.0; en-US; rv:1.0.2; "
350         "chromeframe/0.0.0.0) Gecko/20030208 Netscape/7.02"
351   }, {
352     "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.6) Gecko/20040612 "
353         "Firefox/0.8",
354     "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.6; chromeframe/0.0.0.0) "
355         "Gecko/20040612 Firefox/0.8"
356   }, {
357     "Mozilla/5.0 (compatible; Konqueror/3.2; Linux) (KHTML, like Gecko)",
358     "Mozilla/5.0 (compatible; Konqueror/3.2; Linux; chromeframe/0.0.0.0) "
359         "(KHTML, like Gecko)"
360   }, {
361     "Lynx/2.8.4rel.1 libwww-FM/2.14 SSL-MM/1.4.1 OpenSSL/0.9.6h",
362     "Lynx/2.8.4rel.1 libwww-FM/2.14 SSL-MM/1.4.1 "
363         "OpenSSL/0.9.6h chromeframe/0.0.0.0",
364   }, {
365     "Mozilla/5.0 (X11; U; Linux i686 (x86_64); en-US; rv:1.7.10) "
366         "Gecko/20050716 Firefox/1.0.6",
367     "Mozilla/5.0 (X11; U; Linux i686 (x86_64; chromeframe/0.0.0.0); en-US; "
368         "rv:1.7.10) Gecko/20050716 Firefox/1.0.6"
369   }, {
370     "Invalid/1.1 ((((((",
371     "Invalid/1.1 (((((( chromeframe/0.0.0.0",
372   }, {
373     "Invalid/1.1 ()))))",
374     "Invalid/1.1 ( chromeframe/0.0.0.0)))))",
375   }, {
376     "Strange/1.1 ()",
377     "Strange/1.1 ( chromeframe/0.0.0.0)",
378   }
379 };
380
381 TEST_F(HtmlUtilUnittest, AddChromeFrameToUserAgentValue) {
382   for (int i = 0; i < arraysize(user_agent_test_cases); ++i) {
383     std::string new_ua(
384         http_utils::AddChromeFrameToUserAgentValue(
385             user_agent_test_cases[i].input_));
386     EXPECT_EQ(user_agent_test_cases[i].expected_, new_ua);
387   }
388
389   // Now do the same test again, but test that we don't add the chromeframe
390   // tag if we've already added it.
391   for (int i = 0; i < arraysize(user_agent_test_cases); ++i) {
392     std::string ua(user_agent_test_cases[i].expected_);
393     std::string new_ua(http_utils::AddChromeFrameToUserAgentValue(ua));
394     EXPECT_EQ(user_agent_test_cases[i].expected_, new_ua);
395   }
396 }
397
398 TEST_F(HtmlUtilUnittest, RemoveChromeFrameFromUserAgentValue) {
399   for (int i = 0; i < arraysize(user_agent_test_cases); ++i) {
400     std::string new_ua(
401         http_utils::RemoveChromeFrameFromUserAgentValue(
402             user_agent_test_cases[i].expected_));
403     EXPECT_EQ(user_agent_test_cases[i].input_, new_ua);
404   }
405
406   // Also test that we don't modify the UA if chromeframe is not present.
407   for (int i = 0; i < arraysize(user_agent_test_cases); ++i) {
408     std::string ua(user_agent_test_cases[i].input_);
409     std::string new_ua(http_utils::RemoveChromeFrameFromUserAgentValue(ua));
410     EXPECT_EQ(user_agent_test_cases[i].input_, new_ua);
411   }
412 }
413
414 TEST_F(HtmlUtilUnittest, GetDefaultUserAgentHeaderWithCFTag) {
415   std::string ua(http_utils::GetDefaultUserAgentHeaderWithCFTag());
416   EXPECT_NE(0u, ua.length());
417   EXPECT_NE(std::string::npos, ua.find("Mozilla"));
418   EXPECT_NE(std::string::npos, ua.find(kChromeFrameUserAgent));
419 }
420
421 TEST_F(HtmlUtilUnittest, GetChromeUserAgent) {
422   // This code is duplicated from chrome_content_client.cc to avoid
423   // introducing a link-time dependency on chrome_common.
424   chrome::VersionInfo version_info;
425   std::string product("Chrome/");
426   product += version_info.is_valid() ? version_info.Version() : "0.0.0.0";
427   std::string chrome_ua(webkit_glue::BuildUserAgentFromProduct(product));
428
429   const char* ua = http_utils::GetChromeUserAgent();
430   EXPECT_EQ(ua, chrome_ua);
431 }
432
433 TEST_F(HtmlUtilUnittest, GetDefaultUserAgent) {
434   std::string ua(http_utils::GetDefaultUserAgent());
435   EXPECT_NE(0u, ua.length());
436   EXPECT_NE(std::string::npos, ua.find("Mozilla"));
437 }
438
439 TEST_F(HtmlUtilUnittest, GetChromeFrameUserAgent) {
440   const char* call1 = http_utils::GetChromeFrameUserAgent();
441   const char* call2 = http_utils::GetChromeFrameUserAgent();
442   // Expect static buffer since caller does no cleanup.
443   EXPECT_EQ(call1, call2);
444   std::string ua(call1);
445   EXPECT_EQ("chromeframe/0.0.0.0", ua);
446 }
447
448 TEST(HttpUtils, HasFrameBustingHeader) {
449   // Simple negative cases.
450   EXPECT_FALSE(http_utils::HasFrameBustingHeader(""));
451   EXPECT_FALSE(http_utils::HasFrameBustingHeader("Content-Type: text/plain"));
452   EXPECT_FALSE(http_utils::HasFrameBustingHeader("X-Frame-Optionss: ALLOWALL"));
453   // Explicit negative cases, test that we ignore case.
454   EXPECT_FALSE(http_utils::HasFrameBustingHeader("X-Frame-Options: ALLOWALL"));
455   EXPECT_FALSE(http_utils::HasFrameBustingHeader("X-Frame-Options: allowall"));
456   EXPECT_FALSE(http_utils::HasFrameBustingHeader("X-Frame-Options: ALLowalL"));
457   // Added space, ensure stripped out
458   EXPECT_FALSE(http_utils::HasFrameBustingHeader(
459     "X-Frame-Options: ALLOWALL "));
460   // Added space with linefeed, ensure still stripped out
461   EXPECT_FALSE(http_utils::HasFrameBustingHeader(
462     "X-Frame-Options: ALLOWALL \r\n"));
463   // Multiple identical headers, all of them allowing framing.
464   EXPECT_FALSE(http_utils::HasFrameBustingHeader(
465     "X-Frame-Options: ALLOWALL\r\n"
466     "X-Frame-Options: ALLOWALL\r\n"
467     "X-Frame-Options: ALLOWALL"));
468   // Interleave with other headers.
469   EXPECT_FALSE(http_utils::HasFrameBustingHeader(
470     "Content-Type: text/plain\r\n"
471     "X-Frame-Options: ALLOWALL\r\n"
472     "Content-Length: 42"));
473
474   // Simple positive cases.
475   EXPECT_TRUE(http_utils::HasFrameBustingHeader("X-Frame-Options: deny"));
476   EXPECT_TRUE(http_utils::HasFrameBustingHeader(
477     "X-Frame-Options: SAMEorigin"));
478
479   // Verify that we pick up case changes in the header name too:
480   EXPECT_TRUE(http_utils::HasFrameBustingHeader("X-FRAME-OPTIONS: deny"));
481   EXPECT_TRUE(http_utils::HasFrameBustingHeader("x-frame-options: deny"));
482   EXPECT_TRUE(http_utils::HasFrameBustingHeader("X-frame-optionS: deny"));
483   EXPECT_TRUE(http_utils::HasFrameBustingHeader("X-Frame-optionS: deny"));
484
485   // Allowall entries do not override the denying entries, are
486   // order-independent, and the deny entries can interleave with
487   // other headers.
488   EXPECT_TRUE(http_utils::HasFrameBustingHeader(
489     "Content-Length: 42\r\n"
490     "X-Frame-Options: ALLOWall\r\n"
491     "X-Frame-Options: deny\r\n"));
492   EXPECT_TRUE(http_utils::HasFrameBustingHeader(
493     "X-Frame-Options: ALLOWall\r\n"
494     "Content-Length: 42\r\n"
495     "X-Frame-Options: SAMEORIGIN\r\n"));
496   EXPECT_TRUE(http_utils::HasFrameBustingHeader(
497     "X-Frame-Options: deny\r\n"
498     "X-Frame-Options: ALLOWall\r\n"
499     "Content-Length: 42\r\n"));
500   EXPECT_TRUE(http_utils::HasFrameBustingHeader(
501     "X-Frame-Options: SAMEORIGIN\r\n"
502     "X-Frame-Options: ALLOWall\r\n"));
503 }