| 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..8a2bbb34daf5495d68334d5915bb1bdeb680da05 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"
|
| @@ -73,6 +75,69 @@ 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. The
|
| +// caller owns the returned object.
|
| +const DictionaryValue* GetDictionaryFromJson(
|
| + const base::StringPiece& json_string) {
|
| + const Value* value = GetValueFromJson(json_string);
|
| + if (value == NULL)
|
| + return NULL;
|
| + // Now we own the object pointed to by |value|.
|
| + const DictionaryValue* dictionary = NULL;
|
| + if (value->GetAsDictionary(&dictionary)) {
|
| + // We pass the ownership of the object |value|==|dictionary| to the caller.
|
| + return dictionary;
|
| + } else {
|
| + // The object at |value| is no longer needed.
|
| + delete value;
|
| + return NULL;
|
| + }
|
| +}
|
| +
|
| +// 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) {
|
| + CHECK(message->type() == ExtensionMsg_MessageInvoke::ID);
|
| + ExtensionMsg_MessageInvoke::Param param;
|
| + Value* temp_value = NULL;
|
| + CHECK(ExtensionMsg_MessageInvoke::Read(message, ¶m));
|
| + CHECK(param.c.GetSize() >= 2);
|
| + CHECK(param.c.Get(1, &temp_value));
|
| + std::string args;
|
| + CHECK(temp_value->GetAsString(&args));
|
| + const Value* value = GetValueFromJson(args);
|
| + if (value == NULL) {
|
| + DLOG(INFO) << "Failed to parse JSON in the message arguments.";
|
| + return scoped_ptr<const DictionaryValue>();
|
| + }
|
| + const ListValue* list = NULL;
|
| + if (!value->GetAsList(&list)) {
|
| + DLOG(INFO) << "Parsed message argument is not a ListValue.";
|
| + return scoped_ptr<const DictionaryValue>();
|
| + }
|
| + scoped_ptr<const ListValue> list_scoped(list); // To ensure clean-up.
|
| + if (list->GetSize() != 1) {
|
| + DLOG(INFO) << "The Listvalue in message arguments has not size 1.";
|
| + return scoped_ptr<const DictionaryValue>();
|
| + }
|
| + // TODO(vabr): Add const once ListValue getters get corrected.
|
| + DictionaryValue* dictionary = NULL;
|
| + if (!list->GetDictionary(0, &dictionary)) {
|
| + DLOG(INFO) << "The only Value in the list in message arguments"
|
| + "is not a DictionaryiValue.";
|
| + 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 +199,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 +469,135 @@ 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");
|
| +}
|
| +
|
| +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:
|
| + // 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.
|
| +#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::kFormDataKey);
|
| + const std::string kErrorPath(kPostDataPath + "." + keys::kPostDataErrorKey);
|
| + const std::string* kPath[] = {&kFormDataPath, &kPostDataPath, &kErrorPath};
|
| + // formData
|
| + 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};
|
| + // 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");
|
| + int extra_info_spec_post =
|
| + ExtensionWebRequestEventRouter::ExtraInfoSpec::BLOCKING |
|
| + ExtensionWebRequestEventRouter::ExtraInfoSpec::POST_DATA;
|
| + int extra_info_spec_no_post =
|
| + ExtensionWebRequestEventRouter::ExtraInfoSpec::BLOCKING;
|
| +
|
| + // Part 1.
|
| + // Subscribe to OnBeforeRequest with POST data requirement.
|
| + 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();
|
| +
|
| + // Part 2.
|
| + // Now remove the requirement of POST data.
|
| + 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.
|
| + 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();
|
| +
|
| + 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]), &result));
|
| + if (kExpected[test] != NULL) {
|
| + EXPECT_TRUE(kExpected[test]->Equals(result));
|
| + }
|
| + }
|
| +
|
| + EXPECT_EQ(i, ipc_sender_.sent_end());
|
| +
|
| + // Clean-up.
|
| + ExtensionWebRequestEventRouter::GetInstance()->RemoveEventListener(
|
| + &profile_, extension_id, kEventName + "/1");
|
| }
|
|
|
| struct HeaderModificationTest_Header {
|
| @@ -665,6 +863,10 @@ TEST_F(ExtensionWebRequestTest, InitFromValue) {
|
| "asyncBlocking",
|
| true,
|
| ExtensionWebRequestEventRouter::ExtraInfoSpec::ASYNC_BLOCKING);
|
| + TestInitFromValue(
|
| + "postData",
|
| + true,
|
| + ExtensionWebRequestEventRouter::ExtraInfoSpec::POST_DATA);
|
|
|
| // Multiple valid values are bitwise-or'ed.
|
| TestInitFromValue(
|
| @@ -1488,4 +1690,3 @@ TEST(ExtensionWebRequestHelpersTest, TestMergeOnAuthRequiredResponses) {
|
| EXPECT_TRUE(ContainsKey(conflicting_extensions, "extid2"));
|
| EXPECT_EQ(3u, capturing_net_log.GetSize());
|
| }
|
| -
|
|
|