Home | History | Annotate | Download | only in declarative_webrequest
      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 "extensions/browser/api/declarative_webrequest/webrequest_condition_attribute.h"
      6 
      7 #include "base/basictypes.h"
      8 #include "base/files/file_path.h"
      9 #include "base/memory/scoped_ptr.h"
     10 #include "base/message_loop/message_loop.h"
     11 #include "base/values.h"
     12 #include "content/public/browser/resource_request_info.h"
     13 #include "extensions/browser/api/declarative_webrequest/webrequest_condition.h"
     14 #include "extensions/browser/api/declarative_webrequest/webrequest_constants.h"
     15 #include "net/base/request_priority.h"
     16 #include "net/test/embedded_test_server/embedded_test_server.h"
     17 #include "net/url_request/url_request.h"
     18 #include "net/url_request/url_request_test_util.h"
     19 #include "testing/gtest/include/gtest/gtest.h"
     20 
     21 using base::DictionaryValue;
     22 using base::FundamentalValue;
     23 using base::ListValue;
     24 using base::StringValue;
     25 using base::Value;
     26 using content::ResourceType;
     27 
     28 namespace extensions {
     29 
     30 namespace keys = declarative_webrequest_constants;
     31 
     32 namespace {
     33 const char kUnknownConditionName[] = "unknownType";
     34 
     35 base::FilePath TestDataPath(base::StringPiece relative_to_src) {
     36   base::FilePath src_dir;
     37   CHECK(PathService::Get(base::DIR_SOURCE_ROOT, &src_dir));
     38   return src_dir.AppendASCII(relative_to_src);
     39 }
     40 
     41 TEST(WebRequestConditionAttributeTest, CreateConditionAttribute) {
     42   // Necessary for TestURLRequest.
     43   base::MessageLoopForIO message_loop;
     44 
     45   std::string error;
     46   scoped_refptr<const WebRequestConditionAttribute> result;
     47   base::StringValue string_value("main_frame");
     48   base::ListValue resource_types;
     49   resource_types.Append(new base::StringValue("main_frame"));
     50 
     51   // Test wrong condition name passed.
     52   error.clear();
     53   result = WebRequestConditionAttribute::Create(
     54       kUnknownConditionName, &resource_types, &error);
     55   EXPECT_FALSE(error.empty());
     56   EXPECT_FALSE(result.get());
     57 
     58   // Test wrong data type passed
     59   error.clear();
     60   result = WebRequestConditionAttribute::Create(
     61       keys::kResourceTypeKey, &string_value, &error);
     62   EXPECT_FALSE(error.empty());
     63   EXPECT_FALSE(result.get());
     64 
     65   error.clear();
     66   result = WebRequestConditionAttribute::Create(
     67       keys::kContentTypeKey, &string_value, &error);
     68   EXPECT_FALSE(error.empty());
     69   EXPECT_FALSE(result.get());
     70 
     71   // Test success
     72   error.clear();
     73   result = WebRequestConditionAttribute::Create(
     74       keys::kResourceTypeKey, &resource_types, &error);
     75   EXPECT_EQ("", error);
     76   ASSERT_TRUE(result.get());
     77   EXPECT_EQ(WebRequestConditionAttribute::CONDITION_RESOURCE_TYPE,
     78             result->GetType());
     79   EXPECT_EQ(std::string(keys::kResourceTypeKey), result->GetName());
     80 }
     81 
     82 TEST(WebRequestConditionAttributeTest, ResourceType) {
     83   // Necessary for TestURLRequest.
     84   base::MessageLoopForIO message_loop;
     85 
     86   std::string error;
     87   base::ListValue resource_types;
     88   // The 'sub_frame' value is chosen arbitrarily, so as the corresponding
     89   // content::ResourceType is not 0, the default value.
     90   resource_types.Append(new base::StringValue("sub_frame"));
     91 
     92   scoped_refptr<const WebRequestConditionAttribute> attribute =
     93       WebRequestConditionAttribute::Create(
     94           keys::kResourceTypeKey, &resource_types, &error);
     95   EXPECT_EQ("", error);
     96   ASSERT_TRUE(attribute.get());
     97   EXPECT_EQ(std::string(keys::kResourceTypeKey), attribute->GetName());
     98 
     99   net::TestURLRequestContext context;
    100   scoped_ptr<net::URLRequest> url_request_ok(context.CreateRequest(
    101       GURL("http://www.example.com"), net::DEFAULT_PRIORITY, NULL, NULL));
    102   content::ResourceRequestInfo::AllocateForTesting(
    103       url_request_ok.get(),
    104       content::RESOURCE_TYPE_SUB_FRAME,
    105       NULL,
    106       -1,
    107       -1,
    108       -1,
    109       false);
    110   EXPECT_TRUE(attribute->IsFulfilled(WebRequestData(url_request_ok.get(),
    111                                                     ON_BEFORE_REQUEST)));
    112 
    113   scoped_ptr<net::URLRequest> url_request_fail(context.CreateRequest(
    114       GURL("http://www.example.com"), net::DEFAULT_PRIORITY, NULL, NULL));
    115   content::ResourceRequestInfo::AllocateForTesting(
    116       url_request_fail.get(),
    117       content::RESOURCE_TYPE_MAIN_FRAME,
    118       NULL,
    119       -1,
    120       -1,
    121       -1,
    122       false);
    123   EXPECT_FALSE(attribute->IsFulfilled(WebRequestData(url_request_fail.get(),
    124                                                      ON_BEFORE_REQUEST)));
    125 }
    126 
    127 TEST(WebRequestConditionAttributeTest, ContentType) {
    128   // Necessary for TestURLRequest.
    129   base::MessageLoopForIO message_loop;
    130 
    131   std::string error;
    132   scoped_refptr<const WebRequestConditionAttribute> result;
    133 
    134   net::test_server::EmbeddedTestServer test_server;
    135   test_server.ServeFilesFromDirectory(TestDataPath(
    136       "chrome/test/data/extensions/api_test/webrequest/declarative"));
    137   ASSERT_TRUE(test_server.InitializeAndWaitUntilReady());
    138 
    139   net::TestURLRequestContext context;
    140   net::TestDelegate delegate;
    141   scoped_ptr<net::URLRequest> url_request(
    142       context.CreateRequest(test_server.GetURL("/headers.html"),
    143                             net::DEFAULT_PRIORITY,
    144                             &delegate,
    145                             NULL));
    146   url_request->Start();
    147   base::MessageLoop::current()->Run();
    148 
    149   base::ListValue content_types;
    150   content_types.Append(new base::StringValue("text/plain"));
    151   scoped_refptr<const WebRequestConditionAttribute> attribute_include =
    152       WebRequestConditionAttribute::Create(
    153           keys::kContentTypeKey, &content_types, &error);
    154   EXPECT_EQ("", error);
    155   ASSERT_TRUE(attribute_include.get());
    156   EXPECT_FALSE(attribute_include->IsFulfilled(
    157       WebRequestData(url_request.get(), ON_BEFORE_REQUEST,
    158                      url_request->response_headers())));
    159   EXPECT_TRUE(attribute_include->IsFulfilled(
    160       WebRequestData(url_request.get(), ON_HEADERS_RECEIVED,
    161                      url_request->response_headers())));
    162   EXPECT_EQ(std::string(keys::kContentTypeKey), attribute_include->GetName());
    163 
    164   scoped_refptr<const WebRequestConditionAttribute> attribute_exclude =
    165       WebRequestConditionAttribute::Create(
    166           keys::kExcludeContentTypeKey, &content_types, &error);
    167   EXPECT_EQ("", error);
    168   ASSERT_TRUE(attribute_exclude.get());
    169   EXPECT_FALSE(attribute_exclude->IsFulfilled(
    170       WebRequestData(url_request.get(), ON_HEADERS_RECEIVED,
    171                      url_request->response_headers())));
    172 
    173   content_types.Clear();
    174   content_types.Append(new base::StringValue("something/invalid"));
    175   scoped_refptr<const WebRequestConditionAttribute> attribute_unincluded =
    176       WebRequestConditionAttribute::Create(
    177           keys::kContentTypeKey, &content_types, &error);
    178   EXPECT_EQ("", error);
    179   ASSERT_TRUE(attribute_unincluded.get());
    180   EXPECT_FALSE(attribute_unincluded->IsFulfilled(
    181       WebRequestData(url_request.get(), ON_HEADERS_RECEIVED,
    182                      url_request->response_headers())));
    183 
    184   scoped_refptr<const WebRequestConditionAttribute> attribute_unexcluded =
    185       WebRequestConditionAttribute::Create(
    186           keys::kExcludeContentTypeKey, &content_types, &error);
    187   EXPECT_EQ("", error);
    188   ASSERT_TRUE(attribute_unexcluded.get());
    189   EXPECT_TRUE(attribute_unexcluded->IsFulfilled(
    190       WebRequestData(url_request.get(), ON_HEADERS_RECEIVED,
    191                      url_request->response_headers())));
    192   EXPECT_EQ(std::string(keys::kExcludeContentTypeKey),
    193             attribute_unexcluded->GetName());
    194 }
    195 
    196 // Testing WebRequestConditionAttributeThirdParty.
    197 TEST(WebRequestConditionAttributeTest, ThirdParty) {
    198   // Necessary for TestURLRequest.
    199   base::MessageLoopForIO message_loop;
    200 
    201   std::string error;
    202   const FundamentalValue value_true(true);
    203   // This attribute matches only third party requests.
    204   scoped_refptr<const WebRequestConditionAttribute> third_party_attribute =
    205       WebRequestConditionAttribute::Create(keys::kThirdPartyKey,
    206                                            &value_true,
    207                                            &error);
    208   ASSERT_EQ("", error);
    209   ASSERT_TRUE(third_party_attribute.get());
    210   EXPECT_EQ(std::string(keys::kThirdPartyKey),
    211             third_party_attribute->GetName());
    212   const FundamentalValue value_false(false);
    213   // This attribute matches only first party requests.
    214   scoped_refptr<const WebRequestConditionAttribute> first_party_attribute =
    215       WebRequestConditionAttribute::Create(keys::kThirdPartyKey,
    216                                            &value_false,
    217                                            &error);
    218   ASSERT_EQ("", error);
    219   ASSERT_TRUE(first_party_attribute.get());
    220   EXPECT_EQ(std::string(keys::kThirdPartyKey),
    221             first_party_attribute->GetName());
    222 
    223   const GURL url_empty;
    224   const GURL url_a("http://a.com");
    225   const GURL url_b("http://b.com");
    226   net::TestURLRequestContext context;
    227   net::TestDelegate delegate;
    228   scoped_ptr<net::URLRequest> url_request(
    229       context.CreateRequest(url_a, net::DEFAULT_PRIORITY, &delegate, NULL));
    230 
    231   for (unsigned int i = 1; i <= kLastActiveStage; i <<= 1) {
    232     if (!(kActiveStages & i))
    233       continue;
    234     const RequestStage stage = static_cast<RequestStage>(i);
    235     url_request->set_first_party_for_cookies(url_empty);
    236     EXPECT_FALSE(third_party_attribute->IsFulfilled(
    237         WebRequestData(url_request.get(), stage)));
    238     EXPECT_TRUE(first_party_attribute->IsFulfilled(
    239         WebRequestData(url_request.get(), stage)));
    240 
    241     url_request->set_first_party_for_cookies(url_b);
    242     EXPECT_TRUE(third_party_attribute->IsFulfilled(
    243         WebRequestData(url_request.get(), stage)));
    244     EXPECT_FALSE(first_party_attribute->IsFulfilled(
    245         WebRequestData(url_request.get(), stage)));
    246 
    247     url_request->set_first_party_for_cookies(url_a);
    248     EXPECT_FALSE(third_party_attribute->IsFulfilled(
    249         WebRequestData(url_request.get(), stage)));
    250     EXPECT_TRUE(first_party_attribute->IsFulfilled(
    251         WebRequestData(url_request.get(), stage)));
    252   }
    253 }
    254 
    255 // Testing WebRequestConditionAttributeStages. This iterates over all stages,
    256 // and tests a couple of "stage" attributes -- one created with an empty set of
    257 // applicable stages, one for each stage applicable for that stage, and one
    258 // applicable in all stages.
    259 TEST(WebRequestConditionAttributeTest, Stages) {
    260   // Necessary for TestURLRequest.
    261   base::MessageLoopForIO message_loop;
    262 
    263   typedef std::pair<RequestStage, const char*> StageNamePair;
    264   static const StageNamePair active_stages[] = {
    265     StageNamePair(ON_BEFORE_REQUEST, keys::kOnBeforeRequestEnum),
    266     StageNamePair(ON_BEFORE_SEND_HEADERS, keys::kOnBeforeSendHeadersEnum),
    267     StageNamePair(ON_HEADERS_RECEIVED, keys::kOnHeadersReceivedEnum),
    268     StageNamePair(ON_AUTH_REQUIRED, keys::kOnAuthRequiredEnum)
    269   };
    270 
    271   // Check that exactly all active stages are considered in this test.
    272   unsigned int covered_stages = 0;
    273   for (size_t i = 0; i < arraysize(active_stages); ++i)
    274     covered_stages |= active_stages[i].first;
    275   EXPECT_EQ(kActiveStages, covered_stages);
    276 
    277   std::string error;
    278 
    279   // Create an attribute with an empty set of applicable stages.
    280   base::ListValue empty_list;
    281   scoped_refptr<const WebRequestConditionAttribute> empty_attribute =
    282       WebRequestConditionAttribute::Create(keys::kStagesKey,
    283                                            &empty_list,
    284                                            &error);
    285   EXPECT_EQ("", error);
    286   ASSERT_TRUE(empty_attribute.get());
    287   EXPECT_EQ(std::string(keys::kStagesKey), empty_attribute->GetName());
    288 
    289   // Create an attribute with all possible applicable stages.
    290   base::ListValue all_stages;
    291   for (size_t i = 0; i < arraysize(active_stages); ++i)
    292     all_stages.AppendString(active_stages[i].second);
    293   scoped_refptr<const WebRequestConditionAttribute> attribute_with_all =
    294       WebRequestConditionAttribute::Create(keys::kStagesKey,
    295                                            &all_stages,
    296                                            &error);
    297   EXPECT_EQ("", error);
    298   ASSERT_TRUE(attribute_with_all.get());
    299   EXPECT_EQ(std::string(keys::kStagesKey), attribute_with_all->GetName());
    300 
    301   // Create one attribute for each single stage, to be applicable in that stage.
    302   std::vector<scoped_refptr<const WebRequestConditionAttribute> >
    303       one_stage_attributes;
    304 
    305   for (size_t i = 0; i < arraysize(active_stages); ++i) {
    306     base::ListValue single_stage_list;
    307     single_stage_list.AppendString(active_stages[i].second);
    308     one_stage_attributes.push_back(
    309         WebRequestConditionAttribute::Create(keys::kStagesKey,
    310                                              &single_stage_list,
    311                                              &error));
    312     EXPECT_EQ("", error);
    313     ASSERT_TRUE(one_stage_attributes.back().get() != NULL);
    314   }
    315 
    316   const GURL url_empty;
    317   net::TestURLRequestContext context;
    318   net::TestDelegate delegate;
    319   scoped_ptr<net::URLRequest> url_request(
    320       context.CreateRequest(url_empty, net::DEFAULT_PRIORITY, &delegate, NULL));
    321 
    322   for (size_t i = 0; i < arraysize(active_stages); ++i) {
    323     EXPECT_FALSE(empty_attribute->IsFulfilled(
    324         WebRequestData(url_request.get(), active_stages[i].first)));
    325 
    326     for (size_t j = 0; j < one_stage_attributes.size(); ++j) {
    327       EXPECT_EQ(i == j,
    328                 one_stage_attributes[j]->IsFulfilled(
    329                     WebRequestData(url_request.get(), active_stages[i].first)));
    330     }
    331 
    332     EXPECT_TRUE(attribute_with_all->IsFulfilled(
    333         WebRequestData(url_request.get(), active_stages[i].first)));
    334   }
    335 }
    336 
    337 namespace {
    338 
    339 // Builds a vector of vectors of string pointers from an array of strings.
    340 // |array| is in fact a sequence of arrays. The array |sizes| captures the sizes
    341 // of all parts of |array|, and |size| is the length of |sizes| itself.
    342 // Example (this is pseudo-code, not C++):
    343 // array = { "a", "b", "c", "d", "e", "f" }
    344 // sizes = { 2, 0, 4 }
    345 // size = 3
    346 // results in out == { {&"a", &"b"}, {}, {&"c", &"d", &"e", &"f"} }
    347 void GetArrayAsVector(const std::string array[],
    348                       const size_t sizes[],
    349                       const size_t size,
    350                       std::vector< std::vector<const std::string*> >* out) {
    351   out->clear();
    352   size_t next = 0;
    353   for (size_t i = 0; i < size; ++i) {
    354     out->push_back(std::vector<const std::string*>());
    355     for (size_t j = next; j < next + sizes[i]; ++j) {
    356       out->back().push_back(&(array[j]));
    357     }
    358     next += sizes[i];
    359   }
    360 }
    361 
    362 // Builds a DictionaryValue from an array of the form {name1, value1, name2,
    363 // value2, ...}. Values for the same key are grouped in a ListValue.
    364 scoped_ptr<base::DictionaryValue> GetDictionaryFromArray(
    365     const std::vector<const std::string*>& array) {
    366   const size_t length = array.size();
    367   CHECK(length % 2 == 0);
    368 
    369   scoped_ptr<base::DictionaryValue> dictionary(new base::DictionaryValue);
    370   for (size_t i = 0; i < length; i += 2) {
    371     const std::string* name = array[i];
    372     const std::string* value = array[i+1];
    373     if (dictionary->HasKey(*name)) {
    374       base::Value* entry = NULL;
    375       scoped_ptr<base::Value> entry_owned;
    376       base::ListValue* list = NULL;
    377       if (!dictionary->GetWithoutPathExpansion(*name, &entry))
    378         return scoped_ptr<base::DictionaryValue>();
    379       switch (entry->GetType()) {
    380         case base::Value::TYPE_STRING:
    381           // Replace the present string with a list.
    382           list = new base::ListValue;
    383           // Ignoring return value, we already verified the entry is there.
    384           dictionary->RemoveWithoutPathExpansion(*name, &entry_owned);
    385           list->Append(entry_owned.release());
    386           list->Append(new base::StringValue(*value));
    387           dictionary->SetWithoutPathExpansion(*name, list);
    388           break;
    389         case base::Value::TYPE_LIST:  // Just append to the list.
    390           CHECK(entry->GetAsList(&list));
    391           list->Append(new base::StringValue(*value));
    392           break;
    393         default:
    394           NOTREACHED();  // We never put other Values here.
    395           return scoped_ptr<base::DictionaryValue>();
    396       }
    397     } else {
    398       dictionary->SetString(*name, *value);
    399     }
    400   }
    401   return dictionary.Pass();
    402 }
    403 
    404 // Returns whether the response headers from |url_request| satisfy the match
    405 // criteria given in |tests|. For at least one |i| all tests from |tests[i]|
    406 // must pass. If |positive_test| is true, the dictionary is interpreted as the
    407 // containsHeaders property of a RequestMatcher, otherwise as
    408 // doesNotContainHeaders.
    409 void MatchAndCheck(const std::vector< std::vector<const std::string*> >& tests,
    410                    const std::string& key,
    411                    RequestStage stage,
    412                    net::URLRequest* url_request,
    413                    bool* result) {
    414   base::ListValue contains_headers;
    415   for (size_t i = 0; i < tests.size(); ++i) {
    416     scoped_ptr<base::DictionaryValue> temp(GetDictionaryFromArray(tests[i]));
    417     ASSERT_TRUE(temp.get());
    418     contains_headers.Append(temp.release());
    419   }
    420 
    421   std::string error;
    422   scoped_refptr<const WebRequestConditionAttribute> attribute =
    423       WebRequestConditionAttribute::Create(key, &contains_headers, &error);
    424   ASSERT_EQ("", error);
    425   ASSERT_TRUE(attribute.get());
    426   EXPECT_EQ(key, attribute->GetName());
    427 
    428   *result = attribute->IsFulfilled(WebRequestData(
    429       url_request, stage, url_request->response_headers()));
    430 }
    431 
    432 }  // namespace
    433 
    434 // Here we test WebRequestConditionAttributeRequestHeaders for matching
    435 // correctly against request headers. This test is not as extensive as
    436 // "ResponseHeaders" (below), because the header-matching code is shared
    437 // by both types of condition attributes, so it is enough to test it once.
    438 TEST(WebRequestConditionAttributeTest, RequestHeaders) {
    439   // Necessary for TestURLRequest.
    440   base::MessageLoopForIO message_loop;
    441 
    442   net::TestURLRequestContext context;
    443   net::TestDelegate delegate;
    444   scoped_ptr<net::URLRequest> url_request(
    445       context.CreateRequest(GURL("http://example.com"),  // Dummy URL.
    446                             net::DEFAULT_PRIORITY,
    447                             &delegate,
    448                             NULL));
    449   url_request->SetExtraRequestHeaderByName(
    450       "Custom-header", "custom/value", true /* overwrite */);
    451   url_request->Start();
    452   base::MessageLoop::current()->Run();
    453 
    454   std::vector<std::vector<const std::string*> > tests;
    455   bool result = false;
    456 
    457   const RequestStage stage = ON_BEFORE_SEND_HEADERS;
    458 
    459   // First set of test data -- passing conjunction.
    460   const std::string kPassingCondition[] = {
    461     keys::kNameContainsKey, "CuStOm",  // Header names are case insensitive.
    462     keys::kNameEqualsKey, "custom-header",
    463     keys::kValueSuffixKey, "alue",
    464     keys::kValuePrefixKey, "custom/value"
    465   };
    466   const size_t kPassingConditionSizes[] = { arraysize(kPassingCondition) };
    467   GetArrayAsVector(kPassingCondition, kPassingConditionSizes, 1u, &tests);
    468   // Positive filter, passing (conjunction of tests).
    469   MatchAndCheck(
    470       tests, keys::kRequestHeadersKey, stage, url_request.get(), &result);
    471   EXPECT_TRUE(result);
    472   // Negative filter, failing (conjunction of tests).
    473   MatchAndCheck(tests, keys::kExcludeRequestHeadersKey, stage,
    474                 url_request.get(), &result);
    475   EXPECT_FALSE(result);
    476 
    477   // Second set of test data -- failing disjunction.
    478   const std::string kFailCondition[] = {
    479     keys::kNameSuffixKey, "Custom",      // Test 1.
    480     keys::kNameEqualsKey, "ustom-valu",  // Test 2.
    481     keys::kValuePrefixKey, "custom ",    // Test 3.
    482     keys::kValueContainsKey, " value"    // Test 4.
    483   };
    484   const size_t kFailConditionSizes[] = { 2u, 2u, 2u, 2u };
    485   GetArrayAsVector(kFailCondition, kFailConditionSizes, 4u, &tests);
    486   // Positive filter, failing (disjunction of tests).
    487   MatchAndCheck(tests, keys::kRequestHeadersKey, stage, url_request.get(),
    488                 &result);
    489   EXPECT_FALSE(result);
    490   // Negative filter, passing (disjunction of tests).
    491   MatchAndCheck(tests, keys::kExcludeRequestHeadersKey, stage,
    492                 url_request.get(), &result);
    493   EXPECT_TRUE(result);
    494 
    495   // Third set of test data, corner case -- empty disjunction.
    496   GetArrayAsVector(NULL, NULL, 0u, &tests);
    497   // Positive filter, failing (no test to pass).
    498   MatchAndCheck(tests, keys::kRequestHeadersKey, stage, url_request.get(),
    499                 &result);
    500   EXPECT_FALSE(result);
    501   // Negative filter, passing (no test to fail).
    502   MatchAndCheck(tests, keys::kExcludeRequestHeadersKey, stage,
    503                 url_request.get(), &result);
    504   EXPECT_TRUE(result);
    505 
    506   // Fourth set of test data, corner case -- empty conjunction.
    507   const size_t kEmptyConjunctionSizes[] = { 0u };
    508   GetArrayAsVector(NULL, kEmptyConjunctionSizes, 1u, &tests);
    509   // Positive filter, passing (trivial test).
    510   MatchAndCheck(tests, keys::kRequestHeadersKey, stage, url_request.get(),
    511                 &result);
    512   EXPECT_TRUE(result);
    513   // Negative filter, failing.
    514   MatchAndCheck(tests, keys::kExcludeRequestHeadersKey, stage,
    515                 url_request.get(), &result);
    516   EXPECT_FALSE(result);
    517 }
    518 
    519 // Here we test WebRequestConditionAttributeResponseHeaders for:
    520 // 1. Correct implementation of prefix/suffix/contains/equals matching.
    521 // 2. Performing logical disjunction (||) between multiple specifications.
    522 // 3. Negating the match in case of 'doesNotContainHeaders'.
    523 TEST(WebRequestConditionAttributeTest, ResponseHeaders) {
    524   // Necessary for TestURLRequest.
    525   base::MessageLoopForIO message_loop;
    526 
    527   net::test_server::EmbeddedTestServer test_server;
    528   test_server.ServeFilesFromDirectory(TestDataPath(
    529       "chrome/test/data/extensions/api_test/webrequest/declarative"));
    530   ASSERT_TRUE(test_server.InitializeAndWaitUntilReady());
    531 
    532   net::TestURLRequestContext context;
    533   net::TestDelegate delegate;
    534   scoped_ptr<net::URLRequest> url_request(
    535       context.CreateRequest(test_server.GetURL("/headers.html"),
    536                             net::DEFAULT_PRIORITY,
    537                             &delegate,
    538                             NULL));
    539   url_request->Start();
    540   base::MessageLoop::current()->Run();
    541 
    542   // In all the tests below we assume that the server includes the headers
    543   // Custom-Header: custom/value
    544   // Custom-Header-B: valueA
    545   // Custom-Header-B: valueB
    546   // Custom-Header-C: valueC, valueD
    547   // Custom-Header-D:
    548   // in the response, but does not include "Non-existing: void".
    549 
    550   std::vector< std::vector<const std::string*> > tests;
    551   bool result;
    552 
    553   const RequestStage stage = ON_HEADERS_RECEIVED;
    554 
    555   // 1.a. -- All these tests should pass.
    556   const std::string kPassingCondition[] = {
    557     keys::kNamePrefixKey, "Custom",
    558     keys::kNameSuffixKey, "m-header",  // Header names are case insensitive.
    559     keys::kValueContainsKey, "alu",
    560     keys::kValueEqualsKey, "custom/value"
    561   };
    562   const size_t kPassingConditionSizes[] = { arraysize(kPassingCondition) };
    563   GetArrayAsVector(kPassingCondition, kPassingConditionSizes, 1u, &tests);
    564   MatchAndCheck(tests, keys::kResponseHeadersKey, stage, url_request.get(),
    565                 &result);
    566   EXPECT_TRUE(result);
    567 
    568   // 1.b. -- None of the following tests in the discjunction should pass.
    569   const std::string kFailCondition[] = {
    570     keys::kNamePrefixKey, " Custom",  // Test 1.
    571     keys::kNameContainsKey, " -",     // Test 2.
    572     keys::kValueSuffixKey, "alu",     // Test 3.
    573     keys::kValueEqualsKey, "custom"   // Test 4.
    574   };
    575   const size_t kFailConditionSizes[] = { 2u, 2u, 2u, 2u };
    576   GetArrayAsVector(kFailCondition, kFailConditionSizes, 4u, &tests);
    577   MatchAndCheck(tests, keys::kResponseHeadersKey, stage, url_request.get(),
    578                 &result);
    579   EXPECT_FALSE(result);
    580 
    581   // 1.c. -- This should fail (mixing name and value from different headers)
    582   const std::string kMixingCondition[] = {
    583     keys::kNameSuffixKey, "Header-B",
    584     keys::kValueEqualsKey, "custom/value"
    585   };
    586   const size_t kMixingConditionSizes[] = { arraysize(kMixingCondition) };
    587   GetArrayAsVector(kMixingCondition, kMixingConditionSizes, 1u, &tests);
    588   MatchAndCheck(tests, keys::kResponseHeadersKey, stage, url_request.get(),
    589                 &result);
    590   EXPECT_FALSE(result);
    591 
    592   // 1.d. -- Test handling multiple values for one header (both should pass).
    593   const std::string kMoreValues1[] = {
    594     keys::kNameEqualsKey, "Custom-header-b",
    595     keys::kValueEqualsKey, "valueA"
    596   };
    597   const size_t kMoreValues1Sizes[] = { arraysize(kMoreValues1) };
    598   GetArrayAsVector(kMoreValues1, kMoreValues1Sizes, 1u, &tests);
    599   MatchAndCheck(tests, keys::kResponseHeadersKey, stage, url_request.get(),
    600                 &result);
    601   EXPECT_TRUE(result);
    602   const std::string kMoreValues2[] = {
    603     keys::kNameEqualsKey, "Custom-header-b",
    604     keys::kValueEqualsKey, "valueB"
    605   };
    606   const size_t kMoreValues2Sizes[] = { arraysize(kMoreValues2) };
    607   GetArrayAsVector(kMoreValues2, kMoreValues2Sizes, 1u, &tests);
    608   MatchAndCheck(tests, keys::kResponseHeadersKey, stage, url_request.get(),
    609                 &result);
    610   EXPECT_TRUE(result);
    611 
    612   // 1.e. -- This should fail as conjunction but pass as disjunction.
    613   const std::string kConflict[] = {
    614     keys::kNameSuffixKey, "Header",      // True for some header.
    615     keys::kNameContainsKey, "Header-B"   // True for a different header.
    616   };
    617   // First disjunction, no conflict.
    618   const size_t kNoConflictSizes[] = { 2u, 2u };
    619   GetArrayAsVector(kConflict, kNoConflictSizes, 2u, &tests);
    620   MatchAndCheck(tests, keys::kResponseHeadersKey, stage, url_request.get(),
    621                 &result);
    622   EXPECT_TRUE(result);
    623   // Then conjunction, conflict.
    624   const size_t kConflictSizes[] = { arraysize(kConflict) };
    625   GetArrayAsVector(kConflict, kConflictSizes, 1u, &tests);
    626   MatchAndCheck(tests, keys::kResponseHeadersKey, stage, url_request.get(),
    627                 &result);
    628   EXPECT_FALSE(result);
    629 
    630   // 1.f. -- This should pass, checking for correct treatment of ',' in values.
    631   const std::string kComma[] = {
    632     keys::kNameSuffixKey, "Header-C",
    633     keys::kValueEqualsKey, "valueC, valueD"
    634   };
    635   const size_t kCommaSizes[] = { arraysize(kComma) };
    636   GetArrayAsVector(kComma, kCommaSizes, 1u, &tests);
    637   MatchAndCheck(tests, keys::kResponseHeadersKey, stage, url_request.get(),
    638                 &result);
    639   EXPECT_TRUE(result);
    640 
    641   // 1.g. -- This should pass, empty values are values as well.
    642   const std::string kEmpty[] = {
    643     keys::kNameEqualsKey, "custom-header-d",
    644     keys::kValueEqualsKey, ""
    645   };
    646   const size_t kEmptySizes[] = { arraysize(kEmpty) };
    647   GetArrayAsVector(kEmpty, kEmptySizes, 1u, &tests);
    648   MatchAndCheck(tests, keys::kResponseHeadersKey, stage, url_request.get(),
    649                 &result);
    650   EXPECT_TRUE(result);
    651 
    652   // 1.h. -- Values are case-sensitive, this should fail.
    653   const std::string kLowercase[] = {
    654     keys::kNameEqualsKey, "Custom-header-b",
    655     keys::kValuePrefixKey, "valueb",  // valueb != valueB
    656     keys::kNameEqualsKey, "Custom-header-b",
    657     keys::kValueSuffixKey, "valueb",
    658     keys::kNameEqualsKey, "Custom-header-b",
    659     keys::kValueContainsKey, "valueb",
    660     keys::kNameEqualsKey, "Custom-header-b",
    661     keys::kValueEqualsKey, "valueb"
    662   };
    663   const size_t kLowercaseSizes[] = { 4u, 4u, 4u, 4u };  // As disjunction.
    664   GetArrayAsVector(kLowercase, kLowercaseSizes, 4u, &tests);
    665   MatchAndCheck(tests, keys::kResponseHeadersKey, stage, url_request.get(),
    666                 &result);
    667   EXPECT_FALSE(result);
    668 
    669   // 1.i. -- Names are case-insensitive, this should pass.
    670   const std::string kUppercase[] = {
    671     keys::kNamePrefixKey, "CUSTOM-HEADER-B",
    672     keys::kNameSuffixKey, "CUSTOM-HEADER-B",
    673     keys::kNameEqualsKey, "CUSTOM-HEADER-B",
    674     keys::kNameContainsKey, "CUSTOM-HEADER-B"
    675   };
    676   const size_t kUppercaseSizes[] = { arraysize(kUppercase) };  // Conjunction.
    677   GetArrayAsVector(kUppercase, kUppercaseSizes, 1u, &tests);
    678   MatchAndCheck(tests, keys::kResponseHeadersKey, stage, url_request.get(),
    679                 &result);
    680   EXPECT_TRUE(result);
    681 
    682   // 2.a. -- This should pass as disjunction, because one of the tests passes.
    683   const std::string kDisjunction[] = {
    684     keys::kNamePrefixKey, "Non-existing",  // This one fails.
    685     keys::kNameSuffixKey, "Non-existing",  // This one fails.
    686     keys::kValueEqualsKey, "void",         // This one fails.
    687     keys::kValueContainsKey, "alu"         // This passes.
    688   };
    689   const size_t kDisjunctionSizes[] = { 2u, 2u, 2u, 2u };
    690   GetArrayAsVector(kDisjunction, kDisjunctionSizes, 4u, &tests);
    691   MatchAndCheck(tests, keys::kResponseHeadersKey, stage, url_request.get(),
    692                 &result);
    693   EXPECT_TRUE(result);
    694 
    695   // 3.a. -- This should pass.
    696   const std::string kNonExistent[] = {
    697     keys::kNameEqualsKey, "Non-existing",
    698     keys::kValueEqualsKey, "void"
    699   };
    700   const size_t kNonExistentSizes[] = { arraysize(kNonExistent) };
    701   GetArrayAsVector(kNonExistent, kNonExistentSizes, 1u, &tests);
    702   MatchAndCheck(tests, keys::kExcludeResponseHeadersKey, stage,
    703                 url_request.get(), &result);
    704   EXPECT_TRUE(result);
    705 
    706   // 3.b. -- This should fail.
    707   const std::string kExisting[] = {
    708     keys::kNameEqualsKey, "custom-header-b",
    709     keys::kValueEqualsKey, "valueB"
    710   };
    711   const size_t kExistingSize[] = { arraysize(kExisting) };
    712   GetArrayAsVector(kExisting, kExistingSize, 1u, &tests);
    713   MatchAndCheck(tests, keys::kExcludeResponseHeadersKey, stage,
    714                 url_request.get(), &result);
    715   EXPECT_FALSE(result);
    716 }
    717 
    718 }  // namespace
    719 }  // namespace extensions
    720