| Index: content/browser/blob_storage/blob_dispatcher_host_unittest.cc | 
| diff --git a/content/browser/blob_storage/blob_dispatcher_host_unittest.cc b/content/browser/blob_storage/blob_dispatcher_host_unittest.cc | 
| new file mode 100644 | 
| index 0000000000000000000000000000000000000000..f55332b949165f41a20c390da22cfaa41493e045 | 
| --- /dev/null | 
| +++ b/content/browser/blob_storage/blob_dispatcher_host_unittest.cc | 
| @@ -0,0 +1,930 @@ | 
| +// Copyright 2015 The Chromium Authors. All rights reserved. | 
| +// Use of this source code is governed by a BSD-style license that can be | 
| +// found in the LICENSE file. | 
| + | 
| +#include "content/browser/blob_storage/blob_dispatcher_host.h" | 
| + | 
| +#include <vector> | 
| + | 
| +#include "base/command_line.h" | 
| +#include "base/memory/scoped_ptr.h" | 
| +#include "base/memory/shared_memory.h" | 
| +#include "base/run_loop.h" | 
| +#include "base/tuple.h" | 
| +#include "content/browser/fileapi/chrome_blob_storage_context.h" | 
| +#include "content/common/fileapi/webblob_messages.h" | 
| +#include "content/public/common/content_switches.h" | 
| +#include "content/public/test/test_browser_context.h" | 
| +#include "content/public/test/test_browser_thread_bundle.h" | 
| +#include "ipc/ipc_sender.h" | 
| +#include "ipc/ipc_test_sink.h" | 
| +#include "ipc/message_filter.h" | 
| +#include "storage/browser/blob/blob_data_builder.h" | 
| +#include "storage/browser/blob/blob_data_handle.h" | 
| +#include "storage/browser/blob/blob_storage_context.h" | 
| +#include "storage/common/blob_storage/blob_item_bytes_request.h" | 
| +#include "storage/common/blob_storage/blob_item_bytes_response.h" | 
| +#include "storage/common/data_element.h" | 
| +#include "testing/gmock/include/gmock/gmock.h" | 
| +#include "testing/gtest/include/gtest/gtest.h" | 
| + | 
| +using storage::BlobDataBuilder; | 
| +using storage::BlobDataHandle; | 
| +using storage::BlobItemBytesRequest; | 
| +using storage::BlobItemBytesResponse; | 
| +using storage::BlobStorageContext; | 
| +using storage::BlobTransportResult; | 
| +using storage::DataElement; | 
| +using storage::IPCBlobCreationCancelCode; | 
| +using RequestMemoryCallback = | 
| +    storage::BlobAsyncBuilderHost::RequestMemoryCallback; | 
| + | 
| +namespace content { | 
| +namespace { | 
| + | 
| +const char kContentType[] = "text/plain"; | 
| +const char kContentDisposition[] = "content_disposition"; | 
| +const char kData[] = "data!!"; | 
| +const size_t kDataSize = 6; | 
| + | 
| +const size_t kTestBlobStorageIPCThresholdBytes = 20; | 
| +const size_t kTestBlobStorageMaxSharedMemoryBytes = 50; | 
| +const uint64_t kTestBlobStorageMaxFileSizeBytes = 100; | 
| + | 
| +template <typename T> | 
| +void SetPointerValue(T* pointer, T value) { | 
| +  *pointer = value; | 
| +} | 
| + | 
| +class TestableBlobDispatcherHost : public BlobDispatcherHost { | 
| + public: | 
| +  TestableBlobDispatcherHost(ChromeBlobStorageContext* blob_storage_context, | 
| +                             IPC::TestSink* sink) | 
| +      : BlobDispatcherHost(blob_storage_context), sink_(sink) { | 
| +    this->SetMemoryConstantsForTesting(kTestBlobStorageIPCThresholdBytes, | 
| +                                       kTestBlobStorageMaxSharedMemoryBytes, | 
| +                                       kTestBlobStorageMaxFileSizeBytes); | 
| +  } | 
| + | 
| +  bool Send(IPC::Message* message) override { return sink_->Send(message); } | 
| + | 
| +  void ShutdownForBadMessage() override { shutdown_for_bad_message_ = true; } | 
| + | 
| +  bool shutdown_for_bad_message_ = false; | 
| +  IPC::TestSink* sink_; | 
| + | 
| + protected: | 
| +  ~TestableBlobDispatcherHost() override {} | 
| + | 
| + private: | 
| +  friend class base::RefCountedThreadSafe<TestableBlobDispatcherHost>; | 
| +}; | 
| + | 
| +}  // namespace | 
| + | 
| +class BlobDispatcherHostTest : public testing::Test { | 
| + protected: | 
| +  BlobDispatcherHostTest() | 
| +      : browser_thread_bundle_(), | 
| +        browser_context_(), | 
| +        chrome_blob_storage_context_( | 
| +            ChromeBlobStorageContext::GetFor(&browser_context_)) { | 
| +    host_ = | 
| +        new TestableBlobDispatcherHost(chrome_blob_storage_context_, &sink_); | 
| +  } | 
| +  ~BlobDispatcherHostTest() override {} | 
| + | 
| +  void SetUp() override { | 
| +    base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); | 
| +    if (!command_line->HasSwitch(switches::kDisableKillAfterBadIPC)) { | 
| +      command_line->AppendSwitch(switches::kDisableKillAfterBadIPC); | 
| +    } | 
| +    // We run the run loop to initialize the chrome blob storage context. | 
| +    base::RunLoop().RunUntilIdle(); | 
| +    context_ = chrome_blob_storage_context_->context(); | 
| +    DCHECK(context_); | 
| +  } | 
| + | 
| +  void ExpectBlobNotExist(const std::string& id) { | 
| +    EXPECT_FALSE(context_->registry().HasEntry(id)); | 
| +    EXPECT_FALSE(host_->IsInUseInHost(id)); | 
| +    EXPECT_FALSE(IsBeingBuiltInHost(id)); | 
| +  } | 
| + | 
| +  void AsyncShortcutBlobTransfer(const std::string& id) { | 
| +    sink_.ClearMessages(); | 
| +    ExpectBlobNotExist(id); | 
| +    host_->OnRegisterBlobUUID(id, std::string(kContentType), | 
| +                              std::string(kContentDisposition), | 
| +                              std::set<std::string>()); | 
| +    EXPECT_FALSE(host_->shutdown_for_bad_message_); | 
| +    DataElement element; | 
| +    element.SetToBytes(kData, kDataSize); | 
| +    std::vector<DataElement> elements = {element}; | 
| +    host_->OnStartBuildingBlob(id, elements); | 
| +    EXPECT_FALSE(host_->shutdown_for_bad_message_); | 
| +    ExpectDone(id); | 
| +    sink_.ClearMessages(); | 
| +  } | 
| + | 
| +  void AsyncBlobTransfer(const std::string& id) { | 
| +    sink_.ClearMessages(); | 
| +    ExpectBlobNotExist(id); | 
| +    host_->OnRegisterBlobUUID(id, std::string(kContentType), | 
| +                              std::string(kContentDisposition), | 
| +                              std::set<std::string>()); | 
| +    EXPECT_FALSE(host_->shutdown_for_bad_message_); | 
| +    DataElement element; | 
| +    element.SetToBytesDescription(kDataSize); | 
| +    std::vector<DataElement> elements = {element}; | 
| +    host_->OnStartBuildingBlob(id, elements); | 
| +    EXPECT_FALSE(host_->shutdown_for_bad_message_); | 
| + | 
| +    // Expect our request. | 
| +    std::vector<BlobItemBytesRequest> expected_requests = { | 
| +        BlobItemBytesRequest::CreateIPCRequest(0, 0, 0, kDataSize)}; | 
| +    ExpectRequest(id, expected_requests); | 
| +    sink_.ClearMessages(); | 
| + | 
| +    // Send results; | 
| +    BlobItemBytesResponse response(0); | 
| +    std::memcpy(response.allocate_mutable_data(kDataSize), kData, kDataSize); | 
| +    std::vector<BlobItemBytesResponse> responses = {response}; | 
| +    host_->OnMemoryItemResponse(id, responses); | 
| +    ExpectDone(id); | 
| +    sink_.ClearMessages(); | 
| +  } | 
| + | 
| +  void ExpectAndResetBadMessage() { | 
| +    EXPECT_TRUE(host_->shutdown_for_bad_message_); | 
| +    host_->shutdown_for_bad_message_ = false; | 
| +  } | 
| + | 
| +  void ExpectHandleEqualsData(BlobDataHandle* handle, | 
| +                              const std::vector<DataElement>& data) { | 
| +    scoped_ptr<storage::BlobDataSnapshot> snapshot = handle->CreateSnapshot(); | 
| +    EXPECT_FALSE(handle->IsBeingBuilt()); | 
| +    for (size_t i = 0; i < data.size(); i++) { | 
| +      const DataElement& expected = data[i]; | 
| +      const DataElement& actual = snapshot->items()[i]->data_element(); | 
| +      EXPECT_EQ(expected, actual); | 
| +    } | 
| +    EXPECT_EQ(data.size(), snapshot->items().size()); | 
| +  } | 
| + | 
| +  void ExpectRequest( | 
| +      const std::string& expected_uuid, | 
| +      const std::vector<BlobItemBytesRequest>& expected_requests) { | 
| +    EXPECT_FALSE( | 
| +        sink_.GetFirstMessageMatching(BlobStorageMsg_DoneBuildingBlob::ID)); | 
| +    EXPECT_FALSE( | 
| +        sink_.GetFirstMessageMatching(BlobStorageMsg_CancelBuildingBlob::ID)); | 
| +    const IPC::Message* message = | 
| +        sink_.GetUniqueMessageMatching(BlobStorageMsg_RequestMemoryItem::ID); | 
| +    ASSERT_TRUE(message); | 
| +    base::Tuple<std::string, std::vector<storage::BlobItemBytesRequest>, | 
| +                std::vector<base::SharedMemoryHandle>, | 
| +                std::vector<IPC::PlatformFileForTransit>> | 
| +        args; | 
| +    BlobStorageMsg_RequestMemoryItem::Read(message, &args); | 
| +    EXPECT_EQ(expected_uuid, base::get<0>(args)); | 
| +    std::vector<BlobItemBytesRequest> requests = base::get<1>(args); | 
| +    ASSERT_EQ(requests.size(), expected_requests.size()); | 
| +    for (size_t i = 0; i < expected_requests.size(); ++i) { | 
| +      EXPECT_EQ(expected_requests[i], requests[i]); | 
| +    } | 
| +  } | 
| + | 
| +  void ExpectRequestWithSharedMemoryHandles( | 
| +      const std::string& expected_uuid, | 
| +      const std::vector<BlobItemBytesRequest>& expected_requests, | 
| +      std::vector<base::SharedMemoryHandle>* shared_memory_handles) { | 
| +    EXPECT_FALSE( | 
| +        sink_.GetFirstMessageMatching(BlobStorageMsg_DoneBuildingBlob::ID)); | 
| +    EXPECT_FALSE( | 
| +        sink_.GetFirstMessageMatching(BlobStorageMsg_CancelBuildingBlob::ID)); | 
| +    const IPC::Message* message = | 
| +        sink_.GetUniqueMessageMatching(BlobStorageMsg_RequestMemoryItem::ID); | 
| +    ASSERT_TRUE(message); | 
| +    base::Tuple<std::string, std::vector<storage::BlobItemBytesRequest>, | 
| +                std::vector<base::SharedMemoryHandle>, | 
| +                std::vector<IPC::PlatformFileForTransit>> | 
| +        args; | 
| +    BlobStorageMsg_RequestMemoryItem::Read(message, &args); | 
| +    EXPECT_EQ(expected_uuid, base::get<0>(args)); | 
| +    std::vector<BlobItemBytesRequest> requests = base::get<1>(args); | 
| +    ASSERT_EQ(requests.size(), expected_requests.size()); | 
| +    for (size_t i = 0; i < expected_requests.size(); ++i) { | 
| +      EXPECT_EQ(expected_requests[i], requests[i]); | 
| +    } | 
| +    *shared_memory_handles = std::move(base::get<2>(args)); | 
| +  } | 
| + | 
| +  void ExpectCancel(const std::string& expected_uuid, | 
| +                    IPCBlobCreationCancelCode code) { | 
| +    EXPECT_FALSE( | 
| +        sink_.GetFirstMessageMatching(BlobStorageMsg_RequestMemoryItem::ID)); | 
| +    EXPECT_FALSE( | 
| +        sink_.GetFirstMessageMatching(BlobStorageMsg_DoneBuildingBlob::ID)); | 
| +    const IPC::Message* message = | 
| +        sink_.GetUniqueMessageMatching(BlobStorageMsg_CancelBuildingBlob::ID); | 
| +    ASSERT_TRUE(message); | 
| +    base::Tuple<std::string, IPCBlobCreationCancelCode> args; | 
| +    BlobStorageMsg_CancelBuildingBlob::Read(message, &args); | 
| +    EXPECT_EQ(expected_uuid, base::get<0>(args)); | 
| +    EXPECT_EQ(code, base::get<1>(args)); | 
| +  } | 
| + | 
| +  void ExpectDone(const std::string& expected_uuid) { | 
| +    EXPECT_FALSE( | 
| +        sink_.GetFirstMessageMatching(BlobStorageMsg_RequestMemoryItem::ID)); | 
| +    EXPECT_FALSE( | 
| +        sink_.GetFirstMessageMatching(BlobStorageMsg_CancelBuildingBlob::ID)); | 
| +    const IPC::Message* message = | 
| +        sink_.GetUniqueMessageMatching(BlobStorageMsg_DoneBuildingBlob::ID); | 
| +    base::Tuple<std::string> args; | 
| +    BlobStorageMsg_DoneBuildingBlob::Read(message, &args); | 
| +    EXPECT_EQ(expected_uuid, base::get<0>(args)); | 
| +  } | 
| + | 
| +  bool IsBeingBuiltInHost(const std::string& uuid) { | 
| +    return host_->async_builder_.IsBeingBuilt(uuid); | 
| +  } | 
| + | 
| +  IPC::TestSink sink_; | 
| +  TestBrowserThreadBundle browser_thread_bundle_; | 
| +  TestBrowserContext browser_context_; | 
| +  ChromeBlobStorageContext* chrome_blob_storage_context_; | 
| +  BlobStorageContext* context_ = nullptr; | 
| +  scoped_refptr<TestableBlobDispatcherHost> host_; | 
| +}; | 
| + | 
| +TEST_F(BlobDispatcherHostTest, EmptyUUIDs) { | 
| +  host_->OnRegisterBlobUUID("", "", "", std::set<std::string>()); | 
| +  ExpectAndResetBadMessage(); | 
| +  host_->OnStartBuildingBlob("", std::vector<DataElement>()); | 
| +  ExpectAndResetBadMessage(); | 
| +  host_->OnMemoryItemResponse("", std::vector<BlobItemBytesResponse>()); | 
| +  ExpectAndResetBadMessage(); | 
| +  host_->OnCancelBuildingBlob("", IPCBlobCreationCancelCode::UNKNOWN); | 
| +  ExpectAndResetBadMessage(); | 
| +} | 
| + | 
| +TEST_F(BlobDispatcherHostTest, Shortcut) { | 
| +  const std::string kId = "uuid1"; | 
| +  AsyncShortcutBlobTransfer(kId); | 
| +  EXPECT_TRUE(context_->registry().HasEntry(kId)); | 
| +  scoped_ptr<BlobDataHandle> handle = context_->GetBlobDataFromUUID(kId); | 
| +  EXPECT_TRUE(handle); | 
| + | 
| +  DataElement expected; | 
| +  expected.SetToBytes(kData, kDataSize); | 
| +  std::vector<DataElement> elements = {expected}; | 
| +  ExpectHandleEqualsData(handle.get(), elements); | 
| +} | 
| + | 
| +TEST_F(BlobDispatcherHostTest, RegularTransfer) { | 
| +  const std::string kId = "uuid1"; | 
| +  AsyncBlobTransfer(kId); | 
| +  EXPECT_TRUE(context_->registry().HasEntry(kId)); | 
| +  scoped_ptr<BlobDataHandle> handle = context_->GetBlobDataFromUUID(kId); | 
| +  EXPECT_TRUE(handle); | 
| + | 
| +  DataElement expected; | 
| +  expected.SetToBytes(kData, kDataSize); | 
| +  std::vector<DataElement> elements = {expected}; | 
| +  ExpectHandleEqualsData(handle.get(), elements); | 
| +} | 
| + | 
| +TEST_F(BlobDispatcherHostTest, SharedMemoryTransfer) { | 
| +  const std::string kId = "uuid1"; | 
| +  const size_t kLargeSize = kTestBlobStorageMaxSharedMemoryBytes * 2; | 
| +  std::vector<base::SharedMemoryHandle> shared_memory_handles; | 
| + | 
| +  ExpectBlobNotExist(kId); | 
| +  host_->OnRegisterBlobUUID(kId, std::string(kContentType), | 
| +                            std::string(kContentDisposition), | 
| +                            std::set<std::string>()); | 
| +  // Grab the handle. | 
| +  scoped_ptr<BlobDataHandle> blob_data_handle = | 
| +      context_->GetBlobDataFromUUID(kId); | 
| +  bool built = false; | 
| +  blob_data_handle->RunOnConstructionComplete( | 
| +      base::Bind(&SetPointerValue<bool>, &built)); | 
| +  EXPECT_FALSE(built); | 
| + | 
| +  EXPECT_FALSE(host_->shutdown_for_bad_message_); | 
| +  DataElement element; | 
| +  element.SetToBytesDescription(kLargeSize); | 
| +  std::vector<DataElement> elements = {element}; | 
| +  host_->OnStartBuildingBlob(kId, elements); | 
| +  EXPECT_FALSE(host_->shutdown_for_bad_message_); | 
| + | 
| +  // Expect our first request. | 
| +  std::vector<BlobItemBytesRequest> expected_requests = { | 
| +      BlobItemBytesRequest::CreateSharedMemoryRequest( | 
| +          0 /* request_number */, 0 /* renderer_item_index */, | 
| +          0 /* renderer_item_offset */, | 
| +          static_cast<uint64_t>( | 
| +              kTestBlobStorageMaxSharedMemoryBytes) /* size */, | 
| +          0 /* handle_index */, 0 /* handle_offset */)}; | 
| +  ExpectRequestWithSharedMemoryHandles(kId, expected_requests, | 
| +                                       &shared_memory_handles); | 
| +  sink_.ClearMessages(); | 
| + | 
| +  // Populate the shared memory. | 
| +  EXPECT_EQ(1u, shared_memory_handles.size()); | 
| +  EXPECT_TRUE(base::SharedMemory::IsHandleValid(shared_memory_handles[0])); | 
| +  { | 
| +    base::SharedMemory memory( | 
| +        base::SharedMemory::DuplicateHandle(shared_memory_handles[0]), false); | 
| +    memory.Map(kTestBlobStorageMaxSharedMemoryBytes); | 
| +    std::memset(memory.memory(), 'X', kTestBlobStorageMaxSharedMemoryBytes); | 
| +    memory.Close(); | 
| +  } | 
| + | 
| +  // Send the confirmation. | 
| +  std::vector<BlobItemBytesResponse> responses = {BlobItemBytesResponse(0)}; | 
| +  host_->OnMemoryItemResponse(kId, responses); | 
| + | 
| +  // Expect our second request. | 
| +  expected_requests = {BlobItemBytesRequest::CreateSharedMemoryRequest( | 
| +      1 /* request_number */, 0 /* renderer_item_index */, | 
| +      static_cast<uint64_t>( | 
| +          kTestBlobStorageMaxSharedMemoryBytes) /* renderer_item_offset */, | 
| +      static_cast<uint64_t>(kTestBlobStorageMaxSharedMemoryBytes) /* size */, | 
| +      0 /* handle_index */, 0 /* handle_offset */)}; | 
| +  ExpectRequestWithSharedMemoryHandles(kId, expected_requests, | 
| +                                       &shared_memory_handles); | 
| +  sink_.ClearMessages(); | 
| + | 
| +  // Populate the shared memory. | 
| +  EXPECT_EQ(1u, shared_memory_handles.size()); | 
| +  EXPECT_TRUE(base::SharedMemory::IsHandleValid(shared_memory_handles[0])); | 
| +  { | 
| +    base::SharedMemory memory( | 
| +        base::SharedMemory::DuplicateHandle(shared_memory_handles[0]), false); | 
| +    memory.Map(kTestBlobStorageMaxSharedMemoryBytes); | 
| +    std::memset(memory.memory(), 'Z', kTestBlobStorageMaxSharedMemoryBytes); | 
| +    memory.Close(); | 
| +  } | 
| +  // Send the confirmation. | 
| +  responses = {BlobItemBytesResponse(1)}; | 
| +  host_->OnMemoryItemResponse(kId, responses); | 
| + | 
| +  ExpectDone(kId); | 
| +  sink_.ClearMessages(); | 
| +  base::RunLoop().RunUntilIdle(); | 
| +  EXPECT_TRUE(built); | 
| +  EXPECT_TRUE(context_->registry().HasEntry(kId)); | 
| +  scoped_ptr<BlobDataHandle> handle = context_->GetBlobDataFromUUID(kId); | 
| +  EXPECT_TRUE(handle); | 
| + | 
| +  DataElement expected; | 
| +  expected.SetToAllocatedBytes(kLargeSize / 2); | 
| +  std::memset(expected.mutable_bytes(), 'X', kLargeSize / 2); | 
| +  elements = {expected}; | 
| +  std::memset(expected.mutable_bytes(), 'Z', kLargeSize / 2); | 
| +  elements.push_back(expected); | 
| +  ExpectHandleEqualsData(handle.get(), elements); | 
| +} | 
| + | 
| +TEST_F(BlobDispatcherHostTest, OnCancelBuildingBlob) { | 
| +  const std::string kId("id"); | 
| +  // We ignore blobs that are unknown, as it could have been cancelled earlier | 
| +  // and the renderer didn't know about it yet. | 
| +  host_->OnCancelBuildingBlob(kId, IPCBlobCreationCancelCode::UNKNOWN); | 
| +  EXPECT_FALSE(host_->shutdown_for_bad_message_); | 
| + | 
| +  // Start building blob. | 
| +  host_->OnRegisterBlobUUID(kId, std::string(kContentType), | 
| +                            std::string(kContentDisposition), | 
| +                            std::set<std::string>()); | 
| +  EXPECT_FALSE(host_->shutdown_for_bad_message_); | 
| +  DataElement element; | 
| +  element.SetToBytesDescription(kDataSize); | 
| +  std::vector<DataElement> elements = {element}; | 
| +  host_->OnStartBuildingBlob(kId, elements); | 
| +  // It should have requested memory here. | 
| +  EXPECT_FALSE(host_->shutdown_for_bad_message_); | 
| +  sink_.ClearMessages(); | 
| + | 
| +  // Cancel in middle of construction. | 
| +  host_->OnCancelBuildingBlob(kId, IPCBlobCreationCancelCode::UNKNOWN); | 
| +  EXPECT_TRUE(context_->registry().HasEntry(kId)); | 
| +  EXPECT_TRUE(host_->IsInUseInHost(kId)); | 
| +  EXPECT_FALSE(IsBeingBuiltInHost(kId)); | 
| +  // Check that's it's broken. | 
| +  scoped_ptr<BlobDataHandle> handle = context_->GetBlobDataFromUUID(kId); | 
| +  EXPECT_TRUE(handle->IsBroken()); | 
| +  handle.reset(); | 
| +  base::RunLoop().RunUntilIdle(); | 
| + | 
| +  // Get rid of it in the host. | 
| +  host_->OnDecrementBlobRefCount(kId); | 
| +  ExpectBlobNotExist(kId); | 
| + | 
| +  // Create blob again to verify we don't have any old construction state lying | 
| +  // around. | 
| +  AsyncBlobTransfer(kId); | 
| + | 
| +  // Check data. | 
| +  handle = context_->GetBlobDataFromUUID(kId); | 
| +  EXPECT_TRUE(handle); | 
| +  DataElement expected; | 
| +  expected.SetToBytes(kData, kDataSize); | 
| +  std::vector<DataElement> expecteds = {expected}; | 
| +  ExpectHandleEqualsData(handle.get(), expecteds); | 
| + | 
| +  // Verify we can't cancel after the fact. | 
| +  host_->OnCancelBuildingBlob(kId, IPCBlobCreationCancelCode::UNKNOWN); | 
| +  ExpectAndResetBadMessage(); | 
| +} | 
| + | 
| +TEST_F(BlobDispatcherHostTest, BlobDataWithHostDeletion) { | 
| +  // Build up a basic blob. | 
| +  const std::string kId("id"); | 
| +  AsyncShortcutBlobTransfer(kId); | 
| +  scoped_ptr<BlobDataHandle> handle = context_->GetBlobDataFromUUID(kId); | 
| +  EXPECT_TRUE(handle); | 
| + | 
| +  // Kill the host. | 
| +  host_ = nullptr; | 
| +  base::RunLoop().RunUntilIdle(); | 
| +  // Should still be there due to the handle. | 
| +  scoped_ptr<BlobDataHandle> another_handle = | 
| +      context_->GetBlobDataFromUUID(kId); | 
| +  EXPECT_TRUE(another_handle); | 
| + | 
| +  // Should disappear after dropping both handles. | 
| +  handle.reset(); | 
| +  another_handle.reset(); | 
| +  base::RunLoop().RunUntilIdle(); | 
| + | 
| +  handle = context_->GetBlobDataFromUUID(kId); | 
| +  EXPECT_FALSE(handle); | 
| +} | 
| + | 
| +TEST_F(BlobDispatcherHostTest, BlobReferenceWhileConstructing) { | 
| +  const std::string kId("id"); | 
| + | 
| +  // Start building blob. | 
| +  host_->OnRegisterBlobUUID(kId, std::string(kContentType), | 
| +                            std::string(kContentDisposition), | 
| +                            std::set<std::string>()); | 
| + | 
| +  // Grab the handle. | 
| +  scoped_ptr<BlobDataHandle> blob_data_handle = | 
| +      context_->GetBlobDataFromUUID(kId); | 
| +  EXPECT_TRUE(blob_data_handle); | 
| +  EXPECT_TRUE(blob_data_handle->IsBeingBuilt()); | 
| +  bool built = false; | 
| +  blob_data_handle->RunOnConstructionComplete( | 
| +      base::Bind(&SetPointerValue<bool>, &built)); | 
| + | 
| +  // Continue building. | 
| +  DataElement element; | 
| +  element.SetToBytesDescription(kDataSize); | 
| +  std::vector<DataElement> elements = {element}; | 
| +  host_->OnStartBuildingBlob(kId, elements); | 
| +  sink_.ClearMessages(); | 
| + | 
| +  // Send data. | 
| +  BlobItemBytesResponse response(0); | 
| +  std::memcpy(response.allocate_mutable_data(kDataSize), kData, kDataSize); | 
| +  std::vector<BlobItemBytesResponse> responses = {response}; | 
| +  sink_.ClearMessages(); | 
| +  host_->OnMemoryItemResponse(kId, responses); | 
| + | 
| +  ExpectDone(kId); | 
| +  base::RunLoop().RunUntilIdle(); | 
| +  EXPECT_TRUE(built); | 
| +} | 
| + | 
| +TEST_F(BlobDispatcherHostTest, BlobReferenceWhileShortcutConstructing) { | 
| +  const std::string kId("id"); | 
| + | 
| +  // Start building blob. | 
| +  host_->OnRegisterBlobUUID(kId, std::string(kContentType), | 
| +                            std::string(kContentDisposition), | 
| +                            std::set<std::string>()); | 
| + | 
| +  // Grab the handle. | 
| +  scoped_ptr<BlobDataHandle> blob_data_handle = | 
| +      context_->GetBlobDataFromUUID(kId); | 
| +  EXPECT_TRUE(blob_data_handle); | 
| +  EXPECT_TRUE(blob_data_handle->IsBeingBuilt()); | 
| +  bool built = false; | 
| +  blob_data_handle->RunOnConstructionComplete( | 
| +      base::Bind(&SetPointerValue<bool>, &built)); | 
| + | 
| +  // Continue building. | 
| +  DataElement element; | 
| +  element.SetToBytes(kData, kDataSize); | 
| +  std::vector<DataElement> elements = {element}; | 
| +  host_->OnStartBuildingBlob(kId, elements); | 
| +  ExpectDone(kId); | 
| +  base::RunLoop().RunUntilIdle(); | 
| +  EXPECT_TRUE(built); | 
| +} | 
| + | 
| +TEST_F(BlobDispatcherHostTest, BlobReferenceWhileConstructingCancelled) { | 
| +  const std::string kId("id"); | 
| + | 
| +  // Start building blob. | 
| +  host_->OnRegisterBlobUUID(kId, std::string(kContentType), | 
| +                            std::string(kContentDisposition), | 
| +                            std::set<std::string>()); | 
| + | 
| +  // Grab the handle. | 
| +  scoped_ptr<BlobDataHandle> blob_data_handle = | 
| +      context_->GetBlobDataFromUUID(kId); | 
| +  EXPECT_TRUE(blob_data_handle); | 
| +  EXPECT_TRUE(blob_data_handle->IsBeingBuilt()); | 
| +  bool built = true; | 
| +  blob_data_handle->RunOnConstructionComplete( | 
| +      base::Bind(&SetPointerValue<bool>, &built)); | 
| + | 
| +  // Cancel in middle of construction. | 
| +  host_->OnCancelBuildingBlob(kId, IPCBlobCreationCancelCode::UNKNOWN); | 
| +  base::RunLoop().RunUntilIdle(); | 
| +  EXPECT_TRUE(context_->registry().HasEntry(kId)); | 
| +  EXPECT_TRUE(host_->IsInUseInHost(kId)); | 
| +  EXPECT_FALSE(IsBeingBuiltInHost(kId)); | 
| +  EXPECT_TRUE(blob_data_handle->IsBroken()); | 
| +  EXPECT_FALSE(built); | 
| +  built = true; | 
| +  blob_data_handle->RunOnConstructionComplete( | 
| +      base::Bind(&SetPointerValue<bool>, &built)); | 
| +  EXPECT_FALSE(built); | 
| + | 
| +  // Remove it. | 
| +  blob_data_handle.reset(); | 
| +  base::RunLoop().RunUntilIdle(); | 
| +  EXPECT_TRUE(context_->registry().HasEntry(kId)); | 
| +  host_->OnDecrementBlobRefCount(kId); | 
| +  ExpectBlobNotExist(kId); | 
| +} | 
| + | 
| +TEST_F(BlobDispatcherHostTest, DecrementRefAfterRegister) { | 
| +  const std::string kId("id"); | 
| +  // Decrement the refcount while building (renderer blob gc'd before | 
| +  // construction was completed). | 
| +  host_->OnRegisterBlobUUID(kId, std::string(kContentType), | 
| +                            std::string(kContentDisposition), | 
| +                            std::set<std::string>()); | 
| +  EXPECT_TRUE(context_->registry().HasEntry(kId)); | 
| +  host_->OnDecrementBlobRefCount(kId); | 
| +  EXPECT_FALSE(context_->registry().HasEntry(kId)); | 
| +  EXPECT_FALSE(IsBeingBuiltInHost(kId)); | 
| +  ExpectCancel(kId, | 
| +               IPCBlobCreationCancelCode::BLOB_DEREFERENCED_WHILE_BUILDING); | 
| +  sink_.ClearMessages(); | 
| + | 
| +  // Do the same, but this time grab a handle before we decrement. | 
| +  host_->OnRegisterBlobUUID(kId, std::string(kContentType), | 
| +                            std::string(kContentDisposition), | 
| +                            std::set<std::string>()); | 
| +  scoped_ptr<BlobDataHandle> blob_data_handle = | 
| +      context_->GetBlobDataFromUUID(kId); | 
| +  host_->OnDecrementBlobRefCount(kId); | 
| +  EXPECT_TRUE(context_->registry().HasEntry(kId)); | 
| +  EXPECT_TRUE(IsBeingBuiltInHost(kId)); | 
| + | 
| +  // Finish up the blob, and verify we got the done message. | 
| +  DataElement element; | 
| +  element.SetToBytes(kData, kDataSize); | 
| +  std::vector<DataElement> elements = {element}; | 
| +  host_->OnStartBuildingBlob(kId, elements); | 
| +  EXPECT_FALSE(host_->shutdown_for_bad_message_); | 
| +  ExpectDone(kId); | 
| +  sink_.ClearMessages(); | 
| +  // Get rid of the handle, and verify it's gone. | 
| +  blob_data_handle.reset(); | 
| +  base::RunLoop().RunUntilIdle(); | 
| +  // Check that it's no longer around. | 
| +  EXPECT_FALSE(context_->registry().HasEntry(kId)); | 
| +  EXPECT_FALSE(IsBeingBuiltInHost(kId)); | 
| +} | 
| + | 
| +TEST_F(BlobDispatcherHostTest, DecrementRefAfterOnStart) { | 
| +  const std::string kId("id"); | 
| + | 
| +  // Decrement the refcount while building, after we call OnStartBuildlingBlob. | 
| +  host_->OnRegisterBlobUUID(kId, std::string(kContentType), | 
| +                            std::string(kContentDisposition), | 
| +                            std::set<std::string>()); | 
| +  EXPECT_FALSE(host_->shutdown_for_bad_message_); | 
| +  DataElement element; | 
| +  element.SetToBytesDescription(kDataSize); | 
| +  std::vector<DataElement> elements = {element}; | 
| +  host_->OnStartBuildingBlob(kId, elements); | 
| +  EXPECT_FALSE(host_->shutdown_for_bad_message_); | 
| + | 
| +  std::vector<BlobItemBytesRequest> expected_requests = { | 
| +      BlobItemBytesRequest::CreateIPCRequest(0, 0, 0, kDataSize)}; | 
| +  ExpectRequest(kId, expected_requests); | 
| +  sink_.ClearMessages(); | 
| +  EXPECT_TRUE(context_->registry().HasEntry(kId)); | 
| +  host_->OnDecrementBlobRefCount(kId); | 
| +  EXPECT_FALSE(context_->registry().HasEntry(kId)); | 
| +  EXPECT_FALSE(IsBeingBuiltInHost(kId)); | 
| +  ExpectCancel(kId, | 
| +               IPCBlobCreationCancelCode::BLOB_DEREFERENCED_WHILE_BUILDING); | 
| +  sink_.ClearMessages(); | 
| + | 
| +  // Do the same, but this time grab a handle to keep it alive. | 
| +  host_->OnRegisterBlobUUID(kId, std::string(kContentType), | 
| +                            std::string(kContentDisposition), | 
| +                            std::set<std::string>()); | 
| +  EXPECT_FALSE(host_->shutdown_for_bad_message_); | 
| +  host_->OnStartBuildingBlob(kId, elements); | 
| +  EXPECT_FALSE(host_->shutdown_for_bad_message_); | 
| +  ExpectRequest(kId, expected_requests); | 
| +  sink_.ClearMessages(); | 
| +  EXPECT_TRUE(context_->registry().HasEntry(kId)); | 
| +  // Grab the handle before decrementing. | 
| +  scoped_ptr<BlobDataHandle> blob_data_handle = | 
| +      context_->GetBlobDataFromUUID(kId); | 
| +  host_->OnDecrementBlobRefCount(kId); | 
| +  EXPECT_TRUE(context_->registry().HasEntry(kId)); | 
| +  EXPECT_TRUE(IsBeingBuiltInHost(kId)); | 
| + | 
| +  // We finish the blob, and verify that we send 'Done' back to the renderer. | 
| +  BlobItemBytesResponse response(0); | 
| +  std::memcpy(response.allocate_mutable_data(kDataSize), kData, kDataSize); | 
| +  std::vector<BlobItemBytesResponse> responses = {response}; | 
| +  host_->OnMemoryItemResponse(kId, responses); | 
| +  ExpectDone(kId); | 
| +  sink_.ClearMessages(); | 
| +  // Check that it's still around. | 
| +  EXPECT_TRUE(context_->registry().HasEntry(kId)); | 
| +  EXPECT_FALSE(IsBeingBuiltInHost(kId)); | 
| + | 
| +  // Get rid of the handle, and verify it's gone. | 
| +  blob_data_handle.reset(); | 
| +  base::RunLoop().RunUntilIdle(); | 
| +  // Check that it's no longer around. | 
| +  EXPECT_FALSE(context_->registry().HasEntry(kId)); | 
| +  EXPECT_FALSE(IsBeingBuiltInHost(kId)); | 
| +} | 
| + | 
| +TEST_F(BlobDispatcherHostTest, DecrementRefAfterOnStartWithHandle) { | 
| +  const std::string kId("id"); | 
| +  // Case 3: Decrement the refcount while building, after we call | 
| +  // OnStartBuildlingBlob, except we have another handle. | 
| +  host_->OnRegisterBlobUUID(kId, std::string(kContentType), | 
| +                            std::string(kContentDisposition), | 
| +                            std::set<std::string>()); | 
| +  EXPECT_FALSE(host_->shutdown_for_bad_message_); | 
| + | 
| +  scoped_ptr<BlobDataHandle> blob_data_handle = | 
| +      context_->GetBlobDataFromUUID(kId); | 
| +  EXPECT_TRUE(blob_data_handle->IsBeingBuilt()); | 
| +  bool built = true; | 
| +  blob_data_handle->RunOnConstructionComplete( | 
| +      base::Bind(&SetPointerValue<bool>, &built)); | 
| + | 
| +  DataElement element; | 
| +  element.SetToBytesDescription(kDataSize); | 
| +  std::vector<DataElement> elements = {element}; | 
| +  host_->OnStartBuildingBlob(kId, elements); | 
| +  EXPECT_FALSE(host_->shutdown_for_bad_message_); | 
| + | 
| +  // Check that we got the expected request. | 
| +  std::vector<BlobItemBytesRequest> expected_requests = { | 
| +      BlobItemBytesRequest::CreateIPCRequest(0, 0, 0, kDataSize)}; | 
| +  ExpectRequest(kId, expected_requests); | 
| +  sink_.ClearMessages(); | 
| +  EXPECT_TRUE(context_->registry().HasEntry(kId)); | 
| +  EXPECT_TRUE(IsBeingBuiltInHost(kId)); | 
| +  // Decrement, simulating where the ref goes out of scope in renderer. | 
| +  host_->OnDecrementBlobRefCount(kId); | 
| +  // We still have the blob as it's not done. | 
| +  EXPECT_TRUE(context_->registry().HasEntry(kId)); | 
| +  EXPECT_TRUE(blob_data_handle->IsBeingBuilt()); | 
| +  EXPECT_TRUE(IsBeingBuiltInHost(kId)); | 
| +  // Cancel to clean up. | 
| +  host_->OnCancelBuildingBlob(kId, IPCBlobCreationCancelCode::UNKNOWN); | 
| +  // Run loop to propagate the handle decrement in the host. | 
| +  base::RunLoop().RunUntilIdle(); | 
| +  // We still have the entry because of our earlier handle. | 
| +  EXPECT_TRUE(context_->registry().HasEntry(kId)); | 
| +  EXPECT_FALSE(IsBeingBuiltInHost(kId)); | 
| +  sink_.ClearMessages(); | 
| + | 
| +  // Should disappear after dropping the handle. | 
| +  EXPECT_TRUE(blob_data_handle->IsBroken()); | 
| +  blob_data_handle.reset(); | 
| +  base::RunLoop().RunUntilIdle(); | 
| +  EXPECT_FALSE(context_->registry().HasEntry(kId)); | 
| +} | 
| + | 
| +TEST_F(BlobDispatcherHostTest, HostDisconnectAfterRegisterWithHandle) { | 
| +  const std::string kId("id"); | 
| + | 
| +  // Delete host with a handle to the blob. | 
| +  host_->OnRegisterBlobUUID(kId, std::string(kContentType), | 
| +                            std::string(kContentDisposition), | 
| +                            std::set<std::string>()); | 
| + | 
| +  scoped_ptr<BlobDataHandle> blob_data_handle = | 
| +      context_->GetBlobDataFromUUID(kId); | 
| +  EXPECT_TRUE(blob_data_handle->IsBeingBuilt()); | 
| +  bool built = true; | 
| +  blob_data_handle->RunOnConstructionComplete( | 
| +      base::Bind(&SetPointerValue<bool>, &built)); | 
| +  // Get rid of host, which was doing the constructing. | 
| +  host_ = nullptr; | 
| +  EXPECT_FALSE(blob_data_handle->IsBeingBuilt()); | 
| +  base::RunLoop().RunUntilIdle(); | 
| +  EXPECT_FALSE(built); | 
| + | 
| +  // Should still be there due to the handle. | 
| +  scoped_ptr<BlobDataHandle> another_handle = | 
| +      context_->GetBlobDataFromUUID(kId); | 
| +  EXPECT_TRUE(another_handle); | 
| + | 
| +  // Should disappear after dropping both handles. | 
| +  blob_data_handle.reset(); | 
| +  another_handle.reset(); | 
| +  base::RunLoop().RunUntilIdle(); | 
| +  EXPECT_FALSE(context_->registry().HasEntry(kId)); | 
| +} | 
| + | 
| +TEST_F(BlobDispatcherHostTest, HostDisconnectAfterOnStart) { | 
| +  const std::string kId("id"); | 
| + | 
| +  // Host deleted after OnStartBuilding. | 
| +  host_->OnRegisterBlobUUID(kId, std::string(kContentType), | 
| +                            std::string(kContentDisposition), | 
| +                            std::set<std::string>()); | 
| + | 
| +  DataElement element; | 
| +  element.SetToBytesDescription(kDataSize); | 
| +  std::vector<DataElement> elements = {element}; | 
| +  host_->OnStartBuildingBlob(kId, elements); | 
| + | 
| +  std::vector<BlobItemBytesRequest> expected_requests = { | 
| +      BlobItemBytesRequest::CreateIPCRequest(0, 0, 0, kDataSize)}; | 
| +  ExpectRequest(kId, expected_requests); | 
| +  sink_.ClearMessages(); | 
| +  host_ = nullptr; | 
| +  // We need to run the message loop because of the handle in the async builder. | 
| +  base::RunLoop().RunUntilIdle(); | 
| +  EXPECT_FALSE(context_->registry().HasEntry(kId)); | 
| +} | 
| + | 
| +TEST_F(BlobDispatcherHostTest, HostDisconnectAfterOnMemoryResponse) { | 
| +  const std::string kId("id"); | 
| + | 
| +  // Host deleted after OnMemoryItemResponse. | 
| +  host_->OnRegisterBlobUUID(kId, std::string(kContentType), | 
| +                            std::string(kContentDisposition), | 
| +                            std::set<std::string>()); | 
| + | 
| +  // Create list of two items. | 
| +  DataElement element; | 
| +  element.SetToBytesDescription(kDataSize); | 
| +  std::vector<DataElement> elements = {element, element}; | 
| +  host_->OnStartBuildingBlob(kId, elements); | 
| +  std::vector<BlobItemBytesRequest> expected_requests = { | 
| +      BlobItemBytesRequest::CreateIPCRequest(0, 0, 0, kDataSize), | 
| +      BlobItemBytesRequest::CreateIPCRequest(1, 1, 0, kDataSize)}; | 
| +  ExpectRequest(kId, expected_requests); | 
| +  sink_.ClearMessages(); | 
| + | 
| +  // Send just one response so the blob isn't 'done' yet. | 
| +  BlobItemBytesResponse response(0); | 
| +  std::memcpy(response.allocate_mutable_data(kDataSize), kData, kDataSize); | 
| +  std::vector<BlobItemBytesResponse> responses = {response}; | 
| +  host_->OnMemoryItemResponse(kId, responses); | 
| +  EXPECT_EQ(0u, sink_.message_count()); | 
| + | 
| +  host_ = nullptr; | 
| +  base::RunLoop().RunUntilIdle(); | 
| +  EXPECT_FALSE(context_->registry().HasEntry(kId)); | 
| +} | 
| + | 
| +TEST_F(BlobDispatcherHostTest, CreateBlobWithBrokenReference) { | 
| +  const std::string kBrokenId("id1"); | 
| +  const std::string kReferencingId("id2"); | 
| + | 
| +  // First, let's test a circular reference. | 
| +  const std::string kCircularId("id1"); | 
| +  host_->OnRegisterBlobUUID(kCircularId, std::string(kContentType), | 
| +                            std::string(kContentDisposition), {kCircularId}); | 
| +  ExpectAndResetBadMessage(); | 
| + | 
| +  // Next, test a blob that references a broken blob. | 
| +  host_->OnRegisterBlobUUID(kBrokenId, std::string(kContentType), | 
| +                            std::string(kContentDisposition), | 
| +                            std::set<std::string>()); | 
| +  host_->OnCancelBuildingBlob(kBrokenId, IPCBlobCreationCancelCode::UNKNOWN); | 
| +  EXPECT_FALSE(host_->shutdown_for_bad_message_); | 
| +  EXPECT_TRUE(context_->GetBlobDataFromUUID(kBrokenId)->IsBroken()); | 
| + | 
| +  // Create referencing blob. We should be broken right away, but also ignore | 
| +  // the subsequent OnStart message. | 
| +  host_->OnRegisterBlobUUID(kReferencingId, std::string(kContentType), | 
| +                            std::string(kContentDisposition), {kBrokenId}); | 
| +  EXPECT_TRUE(context_->GetBlobDataFromUUID(kReferencingId)->IsBroken()); | 
| +  EXPECT_FALSE(IsBeingBuiltInHost(kReferencingId)); | 
| +  EXPECT_TRUE(context_->registry().HasEntry(kReferencingId)); | 
| +  ExpectCancel(kReferencingId, | 
| +               IPCBlobCreationCancelCode::REFERENCED_BLOB_BROKEN); | 
| +  sink_.ClearMessages(); | 
| + | 
| +  DataElement element; | 
| +  element.SetToBytesDescription(kDataSize); | 
| +  std::vector<DataElement> elements = {element}; | 
| +  element.SetToBlob(kBrokenId); | 
| +  elements.push_back(element); | 
| +  host_->OnStartBuildingBlob(kReferencingId, elements); | 
| +  EXPECT_EQ(0u, sink_.message_count()); | 
| +  base::RunLoop().RunUntilIdle(); | 
| +} | 
| + | 
| +TEST_F(BlobDispatcherHostTest, DeferenceBlobOnDifferentHost) { | 
| +  const std::string kId("id"); | 
| +  // Data elements for our transfer & checking messages. | 
| +  DataElement element; | 
| +  element.SetToBytesDescription(kDataSize); | 
| +  std::vector<DataElement> elements = {element}; | 
| +  std::vector<BlobItemBytesRequest> expected_requests = { | 
| +      BlobItemBytesRequest::CreateIPCRequest(0, 0, 0, kDataSize)}; | 
| +  BlobItemBytesResponse response(0); | 
| +  std::memcpy(response.allocate_mutable_data(kDataSize), kData, kDataSize); | 
| +  std::vector<BlobItemBytesResponse> responses = {response}; | 
| + | 
| +  scoped_refptr<TestableBlobDispatcherHost> host2( | 
| +      new TestableBlobDispatcherHost(chrome_blob_storage_context_, &sink_)); | 
| + | 
| +  // Delete host with another host having a referencing, then dereference on | 
| +  // second host. Verify we're still building it on first host, and then | 
| +  // verify that a building message from the renderer will kill it. | 
| + | 
| +  // Test OnStartBuilding after double dereference. | 
| +  host_->OnRegisterBlobUUID(kId, std::string(kContentType), | 
| +                            std::string(kContentDisposition), | 
| +                            std::set<std::string>()); | 
| +  host2->OnIncrementBlobRefCount(kId); | 
| +  host_->OnDecrementBlobRefCount(kId); | 
| +  EXPECT_FALSE(host_->IsInUseInHost(kId)); | 
| +  host2->OnDecrementBlobRefCount(kId); | 
| +  // So no more blob in the context, but we're still being built in host 1. | 
| +  EXPECT_FALSE(context_->registry().HasEntry(kId)); | 
| +  EXPECT_TRUE(host_->async_builder_.IsBeingBuilt(kId)); | 
| +  host_->OnStartBuildingBlob(kId, elements); | 
| +  EXPECT_FALSE(host_->shutdown_for_bad_message_); | 
| +  // We should be cleaned up. | 
| +  EXPECT_FALSE(host_->async_builder_.IsBeingBuilt(kId)); | 
| +  ExpectCancel(kId, | 
| +               IPCBlobCreationCancelCode::BLOB_DEREFERENCED_WHILE_BUILDING); | 
| +  sink_.ClearMessages(); | 
| + | 
| +  // Same as above, but test OnMemoryItemResponse after double dereference. | 
| +  host_->OnRegisterBlobUUID(kId, std::string(kContentType), | 
| +                            std::string(kContentDisposition), | 
| +                            std::set<std::string>()); | 
| +  host2->OnIncrementBlobRefCount(kId); | 
| +  host_->OnDecrementBlobRefCount(kId); | 
| +  EXPECT_FALSE(host_->IsInUseInHost(kId)); | 
| +  host_->OnStartBuildingBlob(kId, elements); | 
| +  ExpectRequest(kId, expected_requests); | 
| +  sink_.ClearMessages(); | 
| + | 
| +  host2->OnDecrementBlobRefCount(kId); | 
| +  // So no more blob in the context, but we're still being built in host 1. | 
| +  EXPECT_FALSE(context_->registry().HasEntry(kId)); | 
| +  EXPECT_TRUE(host_->async_builder_.IsBeingBuilt(kId)); | 
| +  host_->OnMemoryItemResponse(kId, responses); | 
| +  EXPECT_FALSE(host_->shutdown_for_bad_message_); | 
| +  // We should be cleaned up. | 
| +  EXPECT_FALSE(host_->async_builder_.IsBeingBuilt(kId)); | 
| +  ExpectCancel(kId, | 
| +               IPCBlobCreationCancelCode::BLOB_DEREFERENCED_WHILE_BUILDING); | 
| +  sink_.ClearMessages(); | 
| + | 
| +  // Same, but now for OnCancel. | 
| +  host_->OnRegisterBlobUUID(kId, std::string(kContentType), | 
| +                            std::string(kContentDisposition), | 
| +                            std::set<std::string>()); | 
| +  host2->OnIncrementBlobRefCount(kId); | 
| +  host_->OnDecrementBlobRefCount(kId); | 
| +  EXPECT_FALSE(host_->IsInUseInHost(kId)); | 
| +  host_->OnStartBuildingBlob(kId, elements); | 
| +  ExpectRequest(kId, expected_requests); | 
| +  sink_.ClearMessages(); | 
| + | 
| +  host2->OnDecrementBlobRefCount(kId); | 
| +  // So no more blob in the context, but we're still being built in host 1. | 
| +  EXPECT_FALSE(context_->registry().HasEntry(kId)); | 
| +  EXPECT_TRUE(host_->async_builder_.IsBeingBuilt(kId)); | 
| +  host_->OnCancelBuildingBlob(kId, IPCBlobCreationCancelCode::UNKNOWN); | 
| +  EXPECT_FALSE(host_->shutdown_for_bad_message_); | 
| +  // We should be cleaned up. | 
| +  EXPECT_FALSE(host_->async_builder_.IsBeingBuilt(kId)); | 
| +} | 
| + | 
| +}  // namespace content | 
|  |