OLD | NEW |
(Empty) | |
| 1 // Copyright 2015 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 "content/browser/blob_storage/blob_dispatcher_host.h" |
| 6 |
| 7 #include <vector> |
| 8 |
| 9 #include "base/command_line.h" |
| 10 #include "base/memory/scoped_ptr.h" |
| 11 #include "base/run_loop.h" |
| 12 #include "base/tuple.h" |
| 13 #include "content/browser/fileapi/chrome_blob_storage_context.h" |
| 14 #include "content/common/fileapi/webblob_messages.h" |
| 15 #include "content/public/common/content_switches.h" |
| 16 #include "content/public/test/test_browser_context.h" |
| 17 #include "content/public/test/test_browser_thread_bundle.h" |
| 18 #include "ipc/ipc_sender.h" |
| 19 #include "ipc/ipc_test_sink.h" |
| 20 #include "ipc/message_filter.h" |
| 21 #include "storage/browser/blob/blob_data_builder.h" |
| 22 #include "storage/browser/blob/blob_data_handle.h" |
| 23 #include "storage/browser/blob/blob_storage_context.h" |
| 24 #include "storage/common/blob_storage/blob_item_bytes_request.h" |
| 25 #include "storage/common/blob_storage/blob_item_bytes_response.h" |
| 26 #include "storage/common/data_element.h" |
| 27 #include "testing/gmock/include/gmock/gmock.h" |
| 28 #include "testing/gtest/include/gtest/gtest.h" |
| 29 |
| 30 using storage::BlobDataBuilder; |
| 31 using storage::BlobDataHandle; |
| 32 using storage::BlobItemBytesRequest; |
| 33 using storage::BlobItemBytesResponse; |
| 34 using storage::BlobStorageContext; |
| 35 using storage::BlobTransportResult; |
| 36 using storage::DataElement; |
| 37 using RequestMemoryCallback = |
| 38 storage::BlobAsyncBuilderHost::RequestMemoryCallback; |
| 39 |
| 40 namespace content { |
| 41 namespace { |
| 42 |
| 43 const char kContentType[] = "text/plain"; |
| 44 const char kContentDisposition[] = "content_disposition"; |
| 45 const char kData[] = "data!!"; |
| 46 const size_t kDataSize = 6; |
| 47 |
| 48 template <typename T> |
| 49 void SetPointerValue(T* pointer, T value) { |
| 50 *pointer = value; |
| 51 } |
| 52 |
| 53 class TestableBlobDispatcherHost : public BlobDispatcherHost { |
| 54 public: |
| 55 TestableBlobDispatcherHost(ChromeBlobStorageContext* blob_storage_context, |
| 56 IPC::TestSink* sink) |
| 57 : BlobDispatcherHost(blob_storage_context), sink_(sink) {} |
| 58 |
| 59 bool Send(IPC::Message* message) override { return sink_->Send(message); } |
| 60 |
| 61 void ShutdownForBadMessage() override { shutdown_for_bad_message_ = true; } |
| 62 |
| 63 bool shutdown_for_bad_message_ = false; |
| 64 IPC::TestSink* sink_; |
| 65 |
| 66 protected: |
| 67 ~TestableBlobDispatcherHost() override {} |
| 68 |
| 69 private: |
| 70 friend class base::RefCountedThreadSafe<TestableBlobDispatcherHost>; |
| 71 }; |
| 72 |
| 73 } // namespace |
| 74 |
| 75 class BlobDispatcherHostTest : public testing::Test { |
| 76 protected: |
| 77 BlobDispatcherHostTest() |
| 78 : browser_thread_bundle_(), |
| 79 browser_context_(), |
| 80 chrome_blob_storage_context_( |
| 81 ChromeBlobStorageContext::GetFor(&browser_context_)) { |
| 82 host_ = |
| 83 new TestableBlobDispatcherHost(chrome_blob_storage_context_, &sink_); |
| 84 } |
| 85 ~BlobDispatcherHostTest() override {} |
| 86 |
| 87 void SetUp() override { |
| 88 base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); |
| 89 if (!command_line->HasSwitch(switches::kDisableKillAfterBadIPC)) { |
| 90 command_line->AppendSwitch(switches::kDisableKillAfterBadIPC); |
| 91 } |
| 92 // We run the run loop to initialize the chrome blob storage context. |
| 93 base::RunLoop().RunUntilIdle(); |
| 94 context_ = chrome_blob_storage_context_->context(); |
| 95 DCHECK(context_); |
| 96 } |
| 97 |
| 98 void ExpectBlobNotExist(const std::string& id) { |
| 99 EXPECT_FALSE(context_->registry().HasEntry(id)); |
| 100 EXPECT_FALSE(host_->IsInUseInHost(id)); |
| 101 EXPECT_FALSE(host_->IsBeingBuiltInHost(id)); |
| 102 } |
| 103 |
| 104 void AsyncShortcutBlobTransfer(const std::string& id) { |
| 105 sink_.ClearMessages(); |
| 106 ExpectBlobNotExist(id); |
| 107 host_->OnRegisterBlobUUID(id, std::string(kContentType), |
| 108 std::string(kContentDisposition)); |
| 109 EXPECT_FALSE(host_->shutdown_for_bad_message_); |
| 110 DataElement element; |
| 111 element.SetToBytes(kData, kDataSize); |
| 112 std::vector<DataElement> elements = {element}; |
| 113 host_->OnStartBuildingBlob(id, elements); |
| 114 EXPECT_FALSE(host_->shutdown_for_bad_message_); |
| 115 ExpectDone(id); |
| 116 sink_.ClearMessages(); |
| 117 } |
| 118 |
| 119 void AsyncBlobTransfer(const std::string& id) { |
| 120 sink_.ClearMessages(); |
| 121 ExpectBlobNotExist(id); |
| 122 host_->OnRegisterBlobUUID(id, std::string(kContentType), |
| 123 std::string(kContentDisposition)); |
| 124 EXPECT_FALSE(host_->shutdown_for_bad_message_); |
| 125 DataElement element; |
| 126 element.SetToBytesDescription(kDataSize); |
| 127 std::vector<DataElement> elements = {element}; |
| 128 host_->OnStartBuildingBlob(id, elements); |
| 129 EXPECT_FALSE(host_->shutdown_for_bad_message_); |
| 130 |
| 131 // Expect our request. |
| 132 std::vector<BlobItemBytesRequest> expected_requests = { |
| 133 BlobItemBytesRequest::CreateIPCRequest(0, 0, 0, kDataSize)}; |
| 134 ExpectRequest(id, expected_requests); |
| 135 sink_.ClearMessages(); |
| 136 |
| 137 // Send results; |
| 138 BlobItemBytesResponse response(0); |
| 139 std::memcpy(response.allocate_mutable_data(kDataSize), kData, kDataSize); |
| 140 std::vector<BlobItemBytesResponse> responses = {response}; |
| 141 host_->OnMemoryItemResponse(id, responses); |
| 142 ExpectDone(id); |
| 143 sink_.ClearMessages(); |
| 144 } |
| 145 |
| 146 void ExpectAndResetBadMessage() { |
| 147 EXPECT_TRUE(host_->shutdown_for_bad_message_); |
| 148 host_->shutdown_for_bad_message_ = false; |
| 149 } |
| 150 |
| 151 void ExpectHandleEqualsData(BlobDataHandle* handle, |
| 152 const std::vector<DataElement>& data) { |
| 153 scoped_ptr<storage::BlobDataSnapshot> snapshot = handle->CreateSnapshot(); |
| 154 EXPECT_FALSE(handle->IsBeingBuilt()); |
| 155 for (size_t i = 0; i < data.size(); i++) { |
| 156 const DataElement& expected = data[i]; |
| 157 const DataElement& actual = snapshot->items()[i]->data_element(); |
| 158 EXPECT_EQ(expected, actual); |
| 159 } |
| 160 EXPECT_EQ(data.size(), snapshot->items().size()); |
| 161 } |
| 162 |
| 163 void ExpectRequest( |
| 164 const std::string& expected_uuid, |
| 165 const std::vector<BlobItemBytesRequest>& expected_requests) { |
| 166 EXPECT_FALSE( |
| 167 sink_.GetFirstMessageMatching(BlobStorageMsg_DoneBuildingBlob::ID)); |
| 168 EXPECT_FALSE( |
| 169 sink_.GetFirstMessageMatching(BlobStorageMsg_CancelBuildingBlob::ID)); |
| 170 const IPC::Message* message = |
| 171 sink_.GetUniqueMessageMatching(BlobStorageMsg_RequestMemoryItem::ID); |
| 172 ASSERT_TRUE(message); |
| 173 base::Tuple<std::string, std::vector<storage::BlobItemBytesRequest>, |
| 174 std::vector<base::SharedMemoryHandle>, |
| 175 std::vector<IPC::PlatformFileForTransit>> |
| 176 args; |
| 177 BlobStorageMsg_RequestMemoryItem::Read(message, &args); |
| 178 EXPECT_EQ(expected_uuid, base::get<0>(args)); |
| 179 std::vector<BlobItemBytesRequest> requests = base::get<1>(args); |
| 180 ASSERT_EQ(requests.size(), expected_requests.size()); |
| 181 for (size_t i = 0; i < expected_requests.size(); ++i) { |
| 182 EXPECT_EQ(expected_requests[i], requests[i]); |
| 183 } |
| 184 } |
| 185 |
| 186 void ExpectCancel(const std::string& expected_uuid, |
| 187 storage::IPCBlobCreationCancelCode code) { |
| 188 EXPECT_FALSE( |
| 189 sink_.GetFirstMessageMatching(BlobStorageMsg_RequestMemoryItem::ID)); |
| 190 EXPECT_FALSE( |
| 191 sink_.GetFirstMessageMatching(BlobStorageMsg_DoneBuildingBlob::ID)); |
| 192 const IPC::Message* message = |
| 193 sink_.GetUniqueMessageMatching(BlobStorageMsg_CancelBuildingBlob::ID); |
| 194 ASSERT_TRUE(message); |
| 195 base::Tuple<std::string, storage::IPCBlobCreationCancelCode> args; |
| 196 BlobStorageMsg_CancelBuildingBlob::Read(message, &args); |
| 197 EXPECT_EQ(expected_uuid, base::get<0>(args)); |
| 198 EXPECT_EQ(code, base::get<1>(args)); |
| 199 } |
| 200 |
| 201 void ExpectDone(const std::string& expected_uuid) { |
| 202 EXPECT_FALSE( |
| 203 sink_.GetFirstMessageMatching(BlobStorageMsg_RequestMemoryItem::ID)); |
| 204 EXPECT_FALSE( |
| 205 sink_.GetFirstMessageMatching(BlobStorageMsg_CancelBuildingBlob::ID)); |
| 206 const IPC::Message* message = |
| 207 sink_.GetUniqueMessageMatching(BlobStorageMsg_DoneBuildingBlob::ID); |
| 208 base::Tuple<std::string> args; |
| 209 BlobStorageMsg_DoneBuildingBlob::Read(message, &args); |
| 210 EXPECT_EQ(expected_uuid, base::get<0>(args)); |
| 211 } |
| 212 |
| 213 IPC::TestSink sink_; |
| 214 TestBrowserThreadBundle browser_thread_bundle_; |
| 215 TestBrowserContext browser_context_; |
| 216 ChromeBlobStorageContext* chrome_blob_storage_context_; |
| 217 BlobStorageContext* context_ = nullptr; |
| 218 scoped_refptr<TestableBlobDispatcherHost> host_; |
| 219 }; |
| 220 |
| 221 TEST_F(BlobDispatcherHostTest, EmptyUUIDs) { |
| 222 host_->OnRegisterBlobUUID("", "", ""); |
| 223 ExpectAndResetBadMessage(); |
| 224 host_->OnStartBuildingBlob("", std::vector<DataElement>()); |
| 225 ExpectAndResetBadMessage(); |
| 226 host_->OnMemoryItemResponse("", std::vector<BlobItemBytesResponse>()); |
| 227 ExpectAndResetBadMessage(); |
| 228 host_->OnCancelBuildingBlob("", storage::IPCBlobCreationCancelCode::UNKNOWN); |
| 229 ExpectAndResetBadMessage(); |
| 230 } |
| 231 |
| 232 TEST_F(BlobDispatcherHostTest, Shortcut) { |
| 233 const std::string kId = "uuid1"; |
| 234 AsyncShortcutBlobTransfer(kId); |
| 235 EXPECT_TRUE(context_->registry().HasEntry(kId)); |
| 236 scoped_ptr<BlobDataHandle> handle = context_->GetBlobDataFromUUID(kId); |
| 237 EXPECT_TRUE(handle); |
| 238 |
| 239 DataElement expected; |
| 240 expected.SetToBytes(kData, kDataSize); |
| 241 std::vector<DataElement> elements = {expected}; |
| 242 ExpectHandleEqualsData(handle.get(), elements); |
| 243 } |
| 244 |
| 245 TEST_F(BlobDispatcherHostTest, RegularTransfer) { |
| 246 const std::string kId = "uuid1"; |
| 247 AsyncBlobTransfer(kId); |
| 248 EXPECT_TRUE(context_->registry().HasEntry(kId)); |
| 249 scoped_ptr<BlobDataHandle> handle = context_->GetBlobDataFromUUID(kId); |
| 250 EXPECT_TRUE(handle); |
| 251 |
| 252 DataElement expected; |
| 253 expected.SetToBytes(kData, kDataSize); |
| 254 std::vector<DataElement> elements = {expected}; |
| 255 ExpectHandleEqualsData(handle.get(), elements); |
| 256 } |
| 257 |
| 258 TEST_F(BlobDispatcherHostTest, OnCancelBuildingBlob) { |
| 259 const std::string kId("id"); |
| 260 host_->OnCancelBuildingBlob(kId, storage::IPCBlobCreationCancelCode::UNKNOWN); |
| 261 ExpectAndResetBadMessage(); |
| 262 |
| 263 // Start building blob. |
| 264 host_->OnRegisterBlobUUID(kId, std::string(kContentType), |
| 265 std::string(kContentDisposition)); |
| 266 EXPECT_FALSE(host_->shutdown_for_bad_message_); |
| 267 DataElement element; |
| 268 element.SetToBytesDescription(kDataSize); |
| 269 std::vector<DataElement> elements = {element}; |
| 270 host_->OnStartBuildingBlob(kId, elements); |
| 271 // It should have requested memory here. |
| 272 EXPECT_FALSE(host_->shutdown_for_bad_message_); |
| 273 sink_.ClearMessages(); |
| 274 |
| 275 // Cancel in middle of construction. |
| 276 host_->OnCancelBuildingBlob(kId, storage::IPCBlobCreationCancelCode::UNKNOWN); |
| 277 EXPECT_TRUE(context_->registry().HasEntry(kId)); |
| 278 EXPECT_TRUE(host_->IsInUseInHost(kId)); |
| 279 EXPECT_FALSE(host_->IsBeingBuiltInHost(kId)); |
| 280 // Check that's it's broken. |
| 281 scoped_ptr<BlobDataHandle> handle = context_->GetBlobDataFromUUID(kId); |
| 282 EXPECT_TRUE(handle->IsBroken()); |
| 283 handle.reset(); |
| 284 base::RunLoop().RunUntilIdle(); |
| 285 |
| 286 // Get rid of it in the host. |
| 287 host_->OnDecrementBlobRefCount(kId); |
| 288 ExpectBlobNotExist(kId); |
| 289 |
| 290 // Create blob again to verify we don't have any old construction state lying |
| 291 // around. |
| 292 AsyncBlobTransfer(kId); |
| 293 |
| 294 // Check data. |
| 295 handle = context_->GetBlobDataFromUUID(kId); |
| 296 EXPECT_TRUE(handle); |
| 297 DataElement expected; |
| 298 expected.SetToBytes(kData, kDataSize); |
| 299 std::vector<DataElement> expecteds = {expected}; |
| 300 ExpectHandleEqualsData(handle.get(), expecteds); |
| 301 |
| 302 // Verify we can't cancel after the fact. |
| 303 host_->OnCancelBuildingBlob(kId, storage::IPCBlobCreationCancelCode::UNKNOWN); |
| 304 ExpectAndResetBadMessage(); |
| 305 } |
| 306 |
| 307 TEST_F(BlobDispatcherHostTest, BlobDataWithHostDeletion) { |
| 308 // Build up a basic blob. |
| 309 const std::string kId("id"); |
| 310 AsyncShortcutBlobTransfer(kId); |
| 311 scoped_ptr<BlobDataHandle> handle = context_->GetBlobDataFromUUID(kId); |
| 312 EXPECT_TRUE(handle); |
| 313 |
| 314 // Kill the host. |
| 315 host_ = nullptr; |
| 316 base::RunLoop().RunUntilIdle(); |
| 317 // Should still be there due to the handle. |
| 318 scoped_ptr<BlobDataHandle> another_handle = |
| 319 context_->GetBlobDataFromUUID(kId); |
| 320 EXPECT_TRUE(another_handle); |
| 321 |
| 322 // Should disappear after dropping both handles. |
| 323 handle.reset(); |
| 324 another_handle.reset(); |
| 325 base::RunLoop().RunUntilIdle(); |
| 326 |
| 327 handle = context_->GetBlobDataFromUUID(kId); |
| 328 EXPECT_FALSE(handle); |
| 329 } |
| 330 |
| 331 TEST_F(BlobDispatcherHostTest, BlobReferenceWhileConstructing) { |
| 332 const std::string kId("id"); |
| 333 |
| 334 // Start building blob. |
| 335 host_->OnRegisterBlobUUID(kId, std::string(kContentType), |
| 336 std::string(kContentDisposition)); |
| 337 EXPECT_FALSE(host_->shutdown_for_bad_message_); |
| 338 |
| 339 // Grab the handle. |
| 340 scoped_ptr<BlobDataHandle> blob_data_handle = |
| 341 context_->GetBlobDataFromUUID(kId); |
| 342 EXPECT_TRUE(blob_data_handle); |
| 343 EXPECT_TRUE(blob_data_handle->IsBeingBuilt()); |
| 344 bool built = false; |
| 345 blob_data_handle->RunOnConstructionComplete( |
| 346 base::Bind(&SetPointerValue<bool>, &built)); |
| 347 |
| 348 // Continue building. |
| 349 DataElement element; |
| 350 element.SetToBytesDescription(kDataSize); |
| 351 std::vector<DataElement> elements = {element}; |
| 352 host_->OnStartBuildingBlob(kId, elements); |
| 353 EXPECT_FALSE(host_->shutdown_for_bad_message_); |
| 354 sink_.ClearMessages(); |
| 355 |
| 356 // Send data. |
| 357 BlobItemBytesResponse response(0); |
| 358 std::memcpy(response.allocate_mutable_data(kDataSize), kData, kDataSize); |
| 359 std::vector<BlobItemBytesResponse> responses = {response}; |
| 360 sink_.ClearMessages(); |
| 361 host_->OnMemoryItemResponse(kId, responses); |
| 362 |
| 363 ExpectDone(kId); |
| 364 base::RunLoop().RunUntilIdle(); |
| 365 EXPECT_TRUE(built); |
| 366 } |
| 367 |
| 368 TEST_F(BlobDispatcherHostTest, BlobReferenceWhileShortcutConstructing) { |
| 369 const std::string kId("id"); |
| 370 |
| 371 // Start building blob. |
| 372 host_->OnRegisterBlobUUID(kId, std::string(kContentType), |
| 373 std::string(kContentDisposition)); |
| 374 EXPECT_FALSE(host_->shutdown_for_bad_message_); |
| 375 |
| 376 // Grab the handle. |
| 377 scoped_ptr<BlobDataHandle> blob_data_handle = |
| 378 context_->GetBlobDataFromUUID(kId); |
| 379 EXPECT_TRUE(blob_data_handle); |
| 380 EXPECT_TRUE(blob_data_handle->IsBeingBuilt()); |
| 381 bool built = false; |
| 382 blob_data_handle->RunOnConstructionComplete( |
| 383 base::Bind(&SetPointerValue<bool>, &built)); |
| 384 |
| 385 // Continue building. |
| 386 DataElement element; |
| 387 element.SetToBytes(kData, kDataSize); |
| 388 std::vector<DataElement> elements = {element}; |
| 389 host_->OnStartBuildingBlob(kId, elements); |
| 390 EXPECT_FALSE(host_->shutdown_for_bad_message_); |
| 391 ExpectDone(kId); |
| 392 base::RunLoop().RunUntilIdle(); |
| 393 EXPECT_TRUE(built); |
| 394 } |
| 395 |
| 396 TEST_F(BlobDispatcherHostTest, BlobReferenceWhileConstructingCancelled) { |
| 397 const std::string kId("id"); |
| 398 |
| 399 // Start building blob. |
| 400 host_->OnRegisterBlobUUID(kId, std::string(kContentType), |
| 401 std::string(kContentDisposition)); |
| 402 EXPECT_FALSE(host_->shutdown_for_bad_message_); |
| 403 |
| 404 // Grab the handle. |
| 405 scoped_ptr<BlobDataHandle> blob_data_handle = |
| 406 context_->GetBlobDataFromUUID(kId); |
| 407 EXPECT_TRUE(blob_data_handle); |
| 408 EXPECT_TRUE(blob_data_handle->IsBeingBuilt()); |
| 409 bool built = true; |
| 410 blob_data_handle->RunOnConstructionComplete( |
| 411 base::Bind(&SetPointerValue<bool>, &built)); |
| 412 |
| 413 // Cancel in middle of construction. |
| 414 host_->OnCancelBuildingBlob(kId, storage::IPCBlobCreationCancelCode::UNKNOWN); |
| 415 base::RunLoop().RunUntilIdle(); |
| 416 EXPECT_TRUE(context_->registry().HasEntry(kId)); |
| 417 EXPECT_FALSE(host_->shutdown_for_bad_message_); |
| 418 EXPECT_TRUE(host_->IsInUseInHost(kId)); |
| 419 EXPECT_FALSE(host_->IsBeingBuiltInHost(kId)); |
| 420 EXPECT_TRUE(blob_data_handle->IsBroken()); |
| 421 EXPECT_FALSE(built); |
| 422 built = true; |
| 423 blob_data_handle->RunOnConstructionComplete( |
| 424 base::Bind(&SetPointerValue<bool>, &built)); |
| 425 EXPECT_FALSE(built); |
| 426 |
| 427 // Remove it. |
| 428 blob_data_handle.reset(); |
| 429 base::RunLoop().RunUntilIdle(); |
| 430 EXPECT_TRUE(context_->registry().HasEntry(kId)); |
| 431 host_->OnDecrementBlobRefCount(kId); |
| 432 ExpectBlobNotExist(kId); |
| 433 } |
| 434 |
| 435 TEST_F(BlobDispatcherHostTest, BlobDestructionEdgeCases) { |
| 436 const std::string kId("id"); |
| 437 |
| 438 // Start building blob. |
| 439 host_->OnRegisterBlobUUID(kId, std::string(kContentType), |
| 440 std::string(kContentDisposition)); |
| 441 EXPECT_FALSE(host_->shutdown_for_bad_message_); |
| 442 |
| 443 // Grab the handle. |
| 444 scoped_ptr<BlobDataHandle> blob_data_handle = |
| 445 context_->GetBlobDataFromUUID(kId); |
| 446 EXPECT_TRUE(blob_data_handle->IsBeingBuilt()); |
| 447 bool built = true; |
| 448 blob_data_handle->RunOnConstructionComplete( |
| 449 base::Bind(&SetPointerValue<bool>, &built)); |
| 450 |
| 451 // Get rid of host, which was doing the constructing. |
| 452 host_ = nullptr; |
| 453 |
| 454 EXPECT_FALSE(blob_data_handle->IsBeingBuilt()); |
| 455 base::RunLoop().RunUntilIdle(); |
| 456 |
| 457 EXPECT_FALSE(built); |
| 458 |
| 459 // Should still be there due to the handle. |
| 460 scoped_ptr<BlobDataHandle> another_handle = |
| 461 context_->GetBlobDataFromUUID(kId); |
| 462 EXPECT_TRUE(another_handle); |
| 463 |
| 464 // Should disappear after dropping both handles. |
| 465 blob_data_handle.reset(); |
| 466 another_handle.reset(); |
| 467 base::RunLoop().RunUntilIdle(); |
| 468 |
| 469 EXPECT_FALSE(context_->registry().HasEntry(kId)); |
| 470 } |
| 471 |
| 472 } // namespace contet |
OLD | NEW |