Home | History | Annotate | Download | only in test
      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 }
    504