Chromium Code Reviews| Index: chrome/browser/extensions/api/web_request/web_request_api_unittest.cc |
| diff --git a/chrome/browser/extensions/api/web_request/web_request_api_unittest.cc b/chrome/browser/extensions/api/web_request/web_request_api_unittest.cc |
| index 175926a0d0dd79c8cb2013664dfacc0c002da574..49033b750de2c2373e1bc9a81fc0f00e36d564dc 100644 |
| --- a/chrome/browser/extensions/api/web_request/web_request_api_unittest.cc |
| +++ b/chrome/browser/extensions/api/web_request/web_request_api_unittest.cc |
| @@ -8,11 +8,13 @@ |
| #include "base/bind.h" |
| #include "base/callback.h" |
| #include "base/file_util.h" |
| +#include "base/json/json_reader.h" |
| #include "base/json/json_string_value_serializer.h" |
| #include "base/memory/weak_ptr.h" |
| #include "base/message_loop.h" |
| #include "base/path_service.h" |
| #include "base/stl_util.h" |
| +#include "base/string_piece.h" |
| #include "base/utf_string_conversions.h" |
| #include "chrome/browser/content_settings/cookie_settings.h" |
| #include "chrome/browser/extensions/api/web_request/web_request_api.h" |
| @@ -37,6 +39,7 @@ |
| namespace helpers = extension_web_request_api_helpers; |
| namespace keys = extension_web_request_api_constants; |
| +using chrome::VersionInfo; |
| using helpers::CalculateOnAuthRequiredDelta; |
| using helpers::CalculateOnBeforeRequestDelta; |
| using helpers::CalculateOnBeforeSendHeadersDelta; |
| @@ -73,6 +76,72 @@ bool Contains(const Collection& collection, const Key& key) { |
| collection.end(); |
| } |
| +// Parses |json_string| as a Value. Returns NULL on failure. The caller owns |
| +// the returned object. |
| +const Value* GetValueFromJson(const base::StringPiece& json_string) { |
| + const Value* parsed = base::JSONReader::Read(json_string); |
| + return parsed; |
| +} |
| + |
| +// Parses |json_string| as a DictionaryValue. Returns NULL on failure. |
| +scoped_ptr<const DictionaryValue> GetDictionaryFromJson( |
| + const base::StringPiece& json_string) { |
| + const Value* value = GetValueFromJson(json_string); |
| + if (value == NULL) |
| + return scoped_ptr<const DictionaryValue>(); |
| + // Now we own the object pointed to by |value|. |
| + const DictionaryValue* dictionary_raw = NULL; |
| + const bool success = value->GetAsDictionary(&dictionary_raw); |
| + scoped_ptr<const DictionaryValue> dictionary(dictionary_raw); |
| + if (success) |
| + return dictionary.Pass(); |
| + else |
| + return scoped_ptr<const DictionaryValue>(); |
| +} |
| + |
| +// Parses the JSON data attached to the |message| and tries to return it. |
| +// Returns NULL on failure. |
| +scoped_ptr<const DictionaryValue> GetPartOfMessageArguments( |
| + IPC::Message* message) { |
| + bool success = true; |
| + EXPECT_TRUE(success = (message->type() == ExtensionMsg_MessageInvoke::ID)); |
| + if (!success) |
| + return scoped_ptr<const DictionaryValue>(); |
|
Matt Perry
2012/08/02 10:11:03
Sadly this makes the code a bit hard to read. What
vabr (Chromium)
2012/08/05 18:54:46
Good idea. I actually changed the signature of Get
|
| + ExtensionMsg_MessageInvoke::Param param; |
| + Value* temp_value = NULL; |
| + EXPECT_TRUE(success = (ExtensionMsg_MessageInvoke::Read(message, ¶m))); |
| + if (!success) |
| + return scoped_ptr<const DictionaryValue>(); |
| + EXPECT_TRUE(success = (param.c.GetSize() >= 2)); |
| + if (!success) |
| + return scoped_ptr<const DictionaryValue>(); |
| + EXPECT_TRUE(success = (param.c.Get(1, &temp_value))); |
| + if (!success) |
| + return scoped_ptr<const DictionaryValue>(); |
| + std::string args; |
| + EXPECT_TRUE(success = (temp_value->GetAsString(&args))); |
| + if (!success) |
| + return scoped_ptr<const DictionaryValue>(); |
| + const Value* value = GetValueFromJson(args); |
| + EXPECT_TRUE(success = (value != NULL)); |
| + if (!success) |
| + return scoped_ptr<const DictionaryValue>(); |
| + const ListValue* list = NULL; |
| + EXPECT_TRUE(success = (value->GetAsList(&list))); |
| + if (!success) |
| + return scoped_ptr<const DictionaryValue>(); |
| + scoped_ptr<const ListValue> list_scoped(list); |
| + EXPECT_TRUE(success = (list->GetSize() == 1)); |
| + if (!success) |
| + return scoped_ptr<const DictionaryValue>(); |
| + // TODO(vabr): Add const once ListValue getters get corrected. |
| + DictionaryValue* dictionary = NULL; |
| + EXPECT_TRUE(success = (list->GetDictionary(0, &dictionary))); |
| + if (!success) |
| + return scoped_ptr<const DictionaryValue>(); |
| + return scoped_ptr<const DictionaryValue>(dictionary->DeepCopy()); |
| +} |
| + |
| } // namespace |
| // A mock event router that responds to events with a pre-arranged queue of |
| @@ -134,6 +203,11 @@ class ExtensionWebRequestTest : public testing::Test { |
| context_->Init(); |
| } |
| + // Fires a URLRequest with the specified |content_type|. Method will be "POST" |
| + // and the data will be |bytes|. |
| + void FireURLRequestWithPostData(const char* content_type, |
| + const char* bytes, size_t bytes_length); |
| + |
| MessageLoopForIO message_loop_; |
| content::TestBrowserThread ui_thread_; |
| content::TestBrowserThread io_thread_; |
| @@ -399,7 +473,169 @@ TEST_F(ExtensionWebRequestTest, SimulateChancelWhileBlocked) { |
| ExtensionWebRequestEventRouter::GetInstance()->RemoveEventListener( |
| &profile_, extension_id, kEventName + "/1"); |
| ExtensionWebRequestEventRouter::GetInstance()->RemoveEventListener( |
| - &profile_, extension_id, kEventName2 + "/1"); |
| + &profile_, extension_id, kEventName2 + "/1"); |
| +} |
| + |
| +namespace { |
| + |
| +// Based on the string with extraInfoSpec |values|, create the numerical |
| +// representation. Returns true on success, otherwise false. |
| +bool GenerateInfoSpec(const std::string& values, int* result) { |
| + // Create a ListValue of strings. |
| + std::vector<std::string> split_values; |
| + scoped_ptr<base::ListValue> list_value(new base::ListValue()); |
| + size_t num_values = Tokenize(values, ",", &split_values); |
| + for (size_t i = 0; i < num_values ; ++i) |
| + list_value->Append(new base::StringValue(split_values[i])); |
| + return ExtensionWebRequestEventRouter::ExtraInfoSpec::InitFromValue( |
| + *list_value, result); |
| +} |
| + |
| +} // namespace |
| + |
| +void ExtensionWebRequestTest::FireURLRequestWithPostData( |
| + const char* content_type, |
| + const char* bytes, size_t bytes_length) { |
| + // The request URL can be arbitrary but must have an HTTP or HTTPS scheme. |
| + GURL request_url("http://www.example.com"); |
| + net::URLRequest request(request_url, &delegate_, context_.get()); |
| + request.set_method("POST"); |
| + request.SetExtraRequestHeaderByName( |
| + net::HttpRequestHeaders::kContentType, content_type, true); |
| + request.AppendBytesToUpload(bytes, bytes_length); |
| + ipc_sender_.PushTask(base::Bind(&base::DoNothing)); |
| + request.Start(); |
| +} |
| + |
| +TEST_F(ExtensionWebRequestTest, AccessPostData) { |
| + // We verify that POST data are accessible to OnBeforeRequest listeners. |
| + // Three testing steps are repeated twice: |
| + // 1. Register an extension requesting ExtraInfoSpec::POST_DATA and file a |
| + // URLRequest with a multipart-encoded form. See it getting parsed. |
| + // 2. Do the same, but without requesting ExtraInfoSpec::POST_DATA. Nothing |
| + // should be parsed. |
| + // 3. With ExtraInfoSpec::POST_DATA, fire a URLRequest with a chunked |
| + // upload. Get the error message. |
| + // Each of these steps is done once as if the channel was DEV or CANARY, |
| + // and once as if it was BETA or STABLE. It is checked that POST data access |
| + // is not available on BETA/STABLE, but that it works on DEV/CANARY. |
| +#define kBoundary "THIS_IS_A_BOUNDARY" |
| +#define kBlock "--" kBoundary "\r\n" \ |
| + "Content-Disposition: form-data; name=\"text\"\r\n" \ |
| + "\r\n" \ |
| + "test text\r\n" \ |
| + "--" kBoundary "--" |
| + // POST data input. |
| + const char kMultipartBytes[] = kBlock; |
| + // Expected POST data output. |
| + const std::string kPostDataPath(keys::kPostDataKey); |
| + const std::string kFormDataPath(kPostDataPath + "." + keys::kPostDataFormKey); |
| + const std::string kErrorPath(kPostDataPath + "." + keys::kPostDataErrorKey); |
| + const std::string* kPath[] = {&kFormDataPath, &kPostDataPath, &kErrorPath}; |
| + // form |
| + const char kFormDataString[] = "{\"text\":[\"test text\"]}"; |
| + scoped_ptr<const DictionaryValue> form_data( |
| + GetDictionaryFromJson(kFormDataString)); |
| + ASSERT_TRUE(form_data.get() != NULL); |
| + // error |
| + const StringValue error("chunked_encoding"); |
| + // Summary. |
| + const Value* kExpected[] = {form_data.get(), NULL, &error, NULL, NULL, NULL}; |
| + // Header. |
| + const char kMultipart[] = "multipart/form-data; boundary=" kBoundary; |
| +#undef kBlock |
| +#undef kBoundary |
| + |
| + // Set up a dummy extension name. |
| + ExtensionWebRequestEventRouter::RequestFilter filter; |
| + std::string extension_id("1"); |
| + const std::string string_spec_post("blocking,postData"); |
| + const std::string string_spec_no_post("blocking"); |
| + int extra_info_spec_no_post = 0; |
| + int extra_info_spec_post = 0; |
| + |
| + for (int pass = 0; pass < 2; ++pass) { |
| + SetChannelForTestingWebRequest( |
| + pass > 0 ? VersionInfo::CHANNEL_BETA : VersionInfo::CHANNEL_CANARY); |
| + |
| + // Part 1. |
| + // Subscribe to OnBeforeRequest with POST data requirement. |
| + ASSERT_TRUE(GenerateInfoSpec(string_spec_post, &extra_info_spec_post)); |
| + const std::string kEventName(keys::kOnBeforeRequest); |
| + base::WeakPtrFactory<TestIPCSender> ipc_sender_factory(&ipc_sender_); |
| + ExtensionWebRequestEventRouter::GetInstance()->AddEventListener( |
| + &profile_, extension_id, extension_id, kEventName, kEventName + "/1", |
| + filter, extra_info_spec_post, ipc_sender_factory.GetWeakPtr()); |
| + |
| + FireURLRequestWithPostData( |
| + kMultipart, kMultipartBytes, strlen(kMultipartBytes)); |
| + |
| + MessageLoop::current()->RunAllPending(); |
| + |
| + SetChannelForTestingWebRequest( |
| + pass > 0 ? VersionInfo::CHANNEL_STABLE : VersionInfo::CHANNEL_DEV); |
| + |
| + // Part 2. |
| + // Now remove the requirement of POST data. |
| + ASSERT_TRUE( |
| + GenerateInfoSpec(string_spec_no_post, &extra_info_spec_no_post)); |
| + ExtensionWebRequestEventRouter::GetInstance()->RemoveEventListener( |
| + &profile_, extension_id, kEventName + "/1"); |
| + ExtensionWebRequestEventRouter::GetInstance()->AddEventListener( |
| + &profile_, extension_id, extension_id, kEventName, kEventName + "/1", |
| + filter, extra_info_spec_no_post, ipc_sender_factory.GetWeakPtr()); |
| + |
| + FireURLRequestWithPostData( |
| + kMultipart, kMultipartBytes, strlen(kMultipartBytes)); |
| + |
| + // Part 3. |
| + // Get back the requirement of POST data. |
| + ASSERT_TRUE(GenerateInfoSpec(string_spec_post, &extra_info_spec_post)); |
| + ExtensionWebRequestEventRouter::GetInstance()->RemoveEventListener( |
| + &profile_, extension_id, kEventName + "/1"); |
| + ExtensionWebRequestEventRouter::GetInstance()->AddEventListener( |
| + &profile_, extension_id, extension_id, kEventName, kEventName + "/1", |
| + filter, extra_info_spec_post, ipc_sender_factory.GetWeakPtr()); |
| + { |
| + GURL request_url("http://www.example.com"); |
| + net::URLRequest request(request_url, &delegate_, context_.get()); |
| + request.set_method("POST"); |
| + request.EnableChunkedUpload(); |
| + const char kDummyBytes[] = "dummy"; |
| + request.AppendChunkToUpload(kDummyBytes, strlen(kDummyBytes), true); |
| + net::HttpRequestHeaders headers(request.extra_request_headers()); |
| + headers.SetHeader(net::HttpRequestHeaders::kTransferEncoding, "chunked"); |
| + request.SetExtraRequestHeaders(headers); |
| + ipc_sender_.PushTask(base::Bind(&base::DoNothing)); |
| + request.Start(); |
| + } |
| + |
| + MessageLoop::current()->RunAllPending(); |
| + |
| + // Clean-up. |
| + ExtensionWebRequestEventRouter::GetInstance()->RemoveEventListener( |
| + &profile_, extension_id, kEventName + "/1"); |
| + } |
| + |
| + ResetChannelForTestingWebRequest(); |
| + |
| + IPC::Message* message = NULL; |
| + TestIPCSender::SentMessages::const_iterator i = ipc_sender_.sent_begin(); |
| + for (size_t test = 0; test < arraysize(kExpected); ++test) { |
| + EXPECT_NE(i, ipc_sender_.sent_end()); |
| + message = (i++)->get(); |
| + scoped_ptr<const DictionaryValue> |
| + details(GetPartOfMessageArguments(message).Pass()); |
| + ASSERT_TRUE(details.get() != NULL); |
| + const Value* result = NULL; |
| + EXPECT_EQ( |
| + kExpected[test] != NULL, details->Get(*(kPath[test % 3]), &result)); |
| + if (kExpected[test] != NULL) { |
| + EXPECT_TRUE(kExpected[test]->Equals(result)); |
| + } |
| + } |
| + |
| + EXPECT_EQ(i, ipc_sender_.sent_end()); |
| } |
| struct HeaderModificationTest_Header { |
| @@ -629,16 +865,8 @@ namespace { |
| void TestInitFromValue(const std::string& values, bool expected_return_code, |
| int expected_extra_info_spec) { |
| - // Create a ListValue of strings. |
| - std::vector<std::string> split_values; |
| - scoped_ptr<base::ListValue> list_value(new base::ListValue()); |
| - size_t num_values = Tokenize(values, ",", &split_values); |
| - for (size_t i = 0; i < num_values ; ++i) |
| - list_value->Append(new base::StringValue(split_values[i])); |
| int actual_info_spec; |
| - bool actual_return_code = |
| - ExtensionWebRequestEventRouter::ExtraInfoSpec::InitFromValue( |
| - *list_value, &actual_info_spec); |
| + bool actual_return_code = GenerateInfoSpec(values, &actual_info_spec); |
| EXPECT_EQ(expected_return_code, actual_return_code); |
| if (expected_return_code) |
| EXPECT_EQ(expected_extra_info_spec, actual_info_spec); |
| @@ -665,6 +893,17 @@ TEST_F(ExtensionWebRequestTest, InitFromValue) { |
| "asyncBlocking", |
| true, |
| ExtensionWebRequestEventRouter::ExtraInfoSpec::ASYNC_BLOCKING); |
| + SetChannelForTestingWebRequest(VersionInfo::CHANNEL_BETA); |
| + TestInitFromValue( |
| + "postData", |
| + true, |
| + 0); |
| + SetChannelForTestingWebRequest(VersionInfo::CHANNEL_DEV); |
| + TestInitFromValue( |
| + "postData", |
| + true, |
| + ExtensionWebRequestEventRouter::ExtraInfoSpec::POST_DATA); |
| + ResetChannelForTestingWebRequest(); |
| // Multiple valid values are bitwise-or'ed. |
| TestInitFromValue( |
| @@ -1488,4 +1727,3 @@ TEST(ExtensionWebRequestHelpersTest, TestMergeOnAuthRequiredResponses) { |
| EXPECT_TRUE(ContainsKey(conflicting_extensions, "extid2")); |
| EXPECT_EQ(3u, capturing_net_log.GetSize()); |
| } |
| - |