Commit message: None
CVE: CVE-2017-15416
CWE: CWE-119
Vulnerability description: Heap buffer overflow in Blob API in Google Chrome prior to 63.0.3239.84 allowed a remote attacker to potentially exploit heap corruption via a crafted HTML page, aka a Blink out-of-bounds read.

Project: Chrome

std::unique_ptr<BlobDataHandle> BlobStorageContext::AddBrokenBlob(
    const std::string& uuid,
    const std::string& content_type,
    const std::string& content_disposition,
    BlobStatus reason) {
  DCHECK(!registry_.HasEntry(uuid));
  DCHECK(BlobStatusIsError(reason));
  BlobEntry* entry =
      registry_.CreateEntry(uuid, content_type, content_disposition);
  entry->set_status(reason);
  FinishBuilding(entry);
  return CreateHandle(uuid, entry);
}

Project: Chrome

std::unique_ptr<BlobDataHandle> BlobStorageContext::AddFinishedBlob(
    const BlobDataBuilder& external_builder) {
  TRACE_EVENT0("Blob", "Context::AddFinishedBlob");
  return BuildBlob(external_builder, TransportAllowedCallback());
}

Project: Chrome

std::unique_ptr<BlobDataHandle> BlobStorageContext::AddFinishedBlob(
    const BlobDataBuilder* builder) {
  DCHECK(builder);
  return AddFinishedBlob(*builder);
}

Project: Chrome

BlobStorageContext::BlobSlice::BlobSlice(const BlobEntry& source,
                                         uint64_t slice_offset,
                                         uint64_t slice_size) {
  const auto& source_items = source.items();
  const auto& offsets = source.offsets();
  DCHECK_LE(slice_offset + slice_size, source.total_size());
  size_t item_index =
      std::upper_bound(offsets.begin(), offsets.end(), slice_offset) -
      offsets.begin();
  uint64_t item_offset =
      item_index == 0 ? slice_offset : slice_offset - offsets[item_index - 1];
  size_t num_items = source_items.size();

  size_t first_item_index = item_index;

  for (uint64_t total_sliced = 0;
       item_index < num_items && total_sliced < slice_size; item_index++) {
    const scoped_refptr<BlobDataItem>& source_item =
        source_items[item_index]->item();
    uint64_t source_length = source_item->length();
    DataElement::Type type = source_item->type();
    DCHECK_NE(source_length, std::numeric_limits<uint64_t>::max());
    DCHECK_NE(source_length, 0ull);

    uint64_t read_size =
        std::min(source_length - item_offset, slice_size - total_sliced);
    total_sliced += read_size;

    bool reusing_blob_item = (read_size == source_length);
    UMA_HISTOGRAM_BOOLEAN("Storage.Blob.ReusedItem", reusing_blob_item);
    if (reusing_blob_item) {
      dest_items.push_back(source_items[item_index]);
      if (IsBytes(type)) {
        total_memory_size += source_length;
      }
      continue;
    }

    scoped_refptr<BlobDataItem> data_item;
    ShareableBlobDataItem::State state =
        ShareableBlobDataItem::POPULATED_WITHOUT_QUOTA;
    switch (type) {
      case DataElement::TYPE_BYTES_DESCRIPTION:
      case DataElement::TYPE_BYTES: {
        UMA_HISTOGRAM_COUNTS_1M("Storage.BlobItemSize.BlobSlice.Bytes",
                                read_size / 1024);
        if (item_index == first_item_index) {
          first_item_slice_offset = item_offset;
          first_source_item = source_items[item_index];
        } else {
          last_source_item = source_items[item_index];
        }
        copying_memory_size += read_size;
        total_memory_size += read_size;
        std::unique_ptr<DataElement> element(new DataElement());
        element->SetToBytesDescription(base::checked_cast<size_t>(read_size));
        data_item = new BlobDataItem(std::move(element));
        state = ShareableBlobDataItem::QUOTA_NEEDED;
        break;
      }
      case DataElement::TYPE_FILE: {
        UMA_HISTOGRAM_COUNTS_1M("Storage.BlobItemSize.BlobSlice.File",
                                read_size / 1024);
        std::unique_ptr<DataElement> element(new DataElement());
        element->SetToFilePathRange(
            source_item->path(), source_item->offset() + item_offset, read_size,
            source_item->expected_modification_time());
        data_item =
            new BlobDataItem(std::move(element), source_item->data_handle_);

        if (BlobDataBuilder::IsFutureFileItem(source_item->data_element())) {
          if (item_index == first_item_index) {
            first_item_slice_offset = item_offset;
            first_source_item = source_items[item_index];
          } else {
            last_source_item = source_items[item_index];
          }
        }
        break;
      }
      case DataElement::TYPE_FILE_FILESYSTEM: {
        UMA_HISTOGRAM_COUNTS_1M("Storage.BlobItemSize.BlobSlice.FileSystem",
                                read_size / 1024);
        std::unique_ptr<DataElement> element(new DataElement());
        element->SetToFileSystemUrlRange(
            source_item->filesystem_url(), source_item->offset() + item_offset,
            read_size, source_item->expected_modification_time());
        data_item = new BlobDataItem(std::move(element));
        break;
      }
      case DataElement::TYPE_DISK_CACHE_ENTRY: {
        UMA_HISTOGRAM_COUNTS_1M("Storage.BlobItemSize.BlobSlice.CacheEntry",
                                read_size / 1024);
        std::unique_ptr<DataElement> element(new DataElement());
        element->SetToDiskCacheEntryRange(source_item->offset() + item_offset,
                                          read_size);
        data_item =
            new BlobDataItem(std::move(element), source_item->data_handle_,
                             source_item->disk_cache_entry(),
                             source_item->disk_cache_stream_index(),
                             source_item->disk_cache_side_stream_index());
        break;
      }
      case DataElement::TYPE_BLOB:
      case DataElement::TYPE_DATA_PIPE:
      case DataElement::TYPE_UNKNOWN:
        CHECK(false) << "Illegal blob item type: " << type;
    }
    dest_items.push_back(
        new ShareableBlobDataItem(std::move(data_item), state));
    item_offset = 0;
  }
}

Project: Chrome

BlobStorageContext::BlobStorageContext()
    : memory_controller_(base::FilePath(), scoped_refptr<base::TaskRunner>()),
      ptr_factory_(this) {}

Project: Chrome

std::unique_ptr<BlobDataHandle> BlobStorageContext::BuildBlob(
    const BlobDataBuilder& content,
    const TransportAllowedCallback& transport_allowed_callback) {
  DCHECK(!registry_.HasEntry(content.uuid_));

  BlobEntry* entry = registry_.CreateEntry(
      content.uuid(), content.content_type_, content.content_disposition_);

  return BuildBlobInternal(entry, content, transport_allowed_callback);
}

Project: Chrome

std::unique_ptr<BlobDataHandle> BlobStorageContext::BuildBlobInternal(
    BlobEntry* entry,
    const BlobDataBuilder& content,
    const TransportAllowedCallback& transport_allowed_callback) {
  BlobFlattener flattener(content, entry, &registry_);

  DCHECK(!flattener.contains_unpopulated_transport_items ||
         transport_allowed_callback)
      << "If we have pending unpopulated content then a callback is required";

  DCHECK(flattener.total_size == 0 ||
         flattener.total_size == entry->total_size());
  entry->set_size(flattener.total_size);
  entry->set_status(flattener.status);
  std::unique_ptr<BlobDataHandle> handle = CreateHandle(content.uuid_, entry);

  UMA_HISTOGRAM_COUNTS_1M("Storage.Blob.ItemCount", entry->items().size());
  UMA_HISTOGRAM_COUNTS_1M("Storage.Blob.TotalSize",
                          flattener.total_memory_size / 1024);

  uint64_t total_memory_needed =
      flattener.copy_quota_needed +
      (flattener.transport_quota_type == TransportQuotaType::MEMORY
           ? flattener.transport_quota_needed
           : 0);
  UMA_HISTOGRAM_COUNTS_1M("Storage.Blob.TotalUnsharedSize",
                          total_memory_needed / 1024);

  size_t num_building_dependent_blobs = 0;
  std::vector<std::unique_ptr<BlobDataHandle>> dependent_blobs;
  for (const std::pair<std::string, BlobEntry*>& pending_blob :
       flattener.dependent_blobs) {
    dependent_blobs.push_back(
        CreateHandle(pending_blob.first, pending_blob.second));
    if (BlobStatusIsPending(pending_blob.second->status())) {
      pending_blob.second->building_state_->build_completion_callbacks
          .push_back(base::Bind(&BlobStorageContext::OnDependentBlobFinished,
                                ptr_factory_.GetWeakPtr(), content.uuid_));
      num_building_dependent_blobs++;
    }
  }

  auto previous_building_state = std::move(entry->building_state_);
  entry->set_building_state(base::MakeUnique<BlobEntry::BuildingState>(
      !flattener.pending_transport_items.empty(), transport_allowed_callback,
      num_building_dependent_blobs));
  BlobEntry::BuildingState* building_state = entry->building_state_.get();
  std::swap(building_state->copies, flattener.copies);
  std::swap(building_state->dependent_blobs, dependent_blobs);
  std::swap(building_state->transport_items, flattener.transport_items);
  if (previous_building_state) {
    DCHECK(!previous_building_state->transport_items_present);
    DCHECK(!previous_building_state->transport_allowed_callback);
    DCHECK(previous_building_state->transport_items.empty());
    DCHECK(previous_building_state->dependent_blobs.empty());
    DCHECK(previous_building_state->copies.empty());
    std::swap(building_state->build_completion_callbacks,
              previous_building_state->build_completion_callbacks);
    auto runner = base::ThreadTaskRunnerHandle::Get();
    for (const auto& callback :
         previous_building_state->build_started_callbacks)
      runner->PostTask(FROM_HERE, base::BindOnce(callback, entry->status()));
  }

  if (BlobStatusIsError(flattener.status)) {
    CancelBuildingBlobInternal(entry, flattener.status);
    return handle;
  }

  if (!memory_controller_.CanReserveQuota(flattener.copy_quota_needed +
                                          flattener.transport_quota_needed)) {
    CancelBuildingBlobInternal(entry, BlobStatus::ERR_OUT_OF_MEMORY);
    return handle;
  }

  if (flattener.copy_quota_needed > 0) {
    base::WeakPtr<QuotaAllocationTask> pending_request =
        memory_controller_.ReserveMemoryQuota(
            std::move(flattener.pending_copy_items),
            base::Bind(&BlobStorageContext::OnEnoughSpaceForCopies,
                       ptr_factory_.GetWeakPtr(), content.uuid_));
    if (entry->building_state_)
      entry->building_state_->copy_quota_request = std::move(pending_request);
  }

  if (flattener.transport_quota_needed > 0) {
    base::WeakPtr<QuotaAllocationTask> pending_request;

    switch (flattener.transport_quota_type) {
      case TransportQuotaType::MEMORY: {
        std::vector<BlobMemoryController::FileCreationInfo> empty_files;
        pending_request = memory_controller_.ReserveMemoryQuota(
            std::move(flattener.pending_transport_items),
            base::Bind(&BlobStorageContext::OnEnoughSpaceForTransport,
                       ptr_factory_.GetWeakPtr(), content.uuid_,
                       base::Passed(&empty_files)));
        break;
      }
      case TransportQuotaType::FILE:
        pending_request = memory_controller_.ReserveFileQuota(
            std::move(flattener.pending_transport_items),
            base::Bind(&BlobStorageContext::OnEnoughSpaceForTransport,
                       ptr_factory_.GetWeakPtr(), content.uuid_));
        break;
    }

    if (entry->building_state_) {
      entry->building_state_->transport_quota_request =
          std::move(pending_request);
    }
  }

  if (entry->CanFinishBuilding())
    FinishBuilding(entry);

  return handle;
}

Project: Chrome

std::unique_ptr<BlobDataHandle> BlobStorageContext::BuildPreregisteredBlob(
    const BlobDataBuilder& content,
    const TransportAllowedCallback& transport_allowed_callback) {
  BlobEntry* entry = registry_.GetEntry(content.uuid());
  DCHECK(entry);
  DCHECK_EQ(BlobStatus::PENDING_CONSTRUCTION, entry->status());
  entry->set_size(0);

  return BuildBlobInternal(entry, content, transport_allowed_callback);
}

Project: Chrome

void BlobStorageContext::CancelBuildingBlob(const std::string& uuid,
                                            BlobStatus reason) {
  CancelBuildingBlobInternal(registry_.GetEntry(uuid), reason);
}

Project: Chrome

void BlobStorageContext::CancelBuildingBlobInternal(BlobEntry* entry,
                                                    BlobStatus reason) {
  DCHECK(entry);
  DCHECK(BlobStatusIsError(reason));
  TransportAllowedCallback transport_allowed_callback;
  if (entry->building_state_ &&
      entry->building_state_->transport_allowed_callback) {
    transport_allowed_callback =
        entry->building_state_->transport_allowed_callback;
    entry->building_state_->transport_allowed_callback.Reset();
  }
  if (entry->building_state_ &&
      entry->status() == BlobStatus::PENDING_CONSTRUCTION) {
    auto runner = base::ThreadTaskRunnerHandle::Get();
    for (const auto& callback : entry->building_state_->build_started_callbacks)
      runner->PostTask(FROM_HERE, base::BindOnce(callback, reason));
  }
  ClearAndFreeMemory(entry);
  entry->set_status(reason);
  if (transport_allowed_callback) {
    transport_allowed_callback.Run(
        reason, std::vector<BlobMemoryController::FileCreationInfo>());
  }
  FinishBuilding(entry);
}

Project: Chrome

void BlobStorageContext::ClearAndFreeMemory(BlobEntry* entry) {
  if (entry->building_state_)
    entry->building_state_->CancelRequests();
  entry->ClearItems();
  entry->ClearOffsets();
  entry->set_size(0);
}

Project: Chrome

std::unique_ptr<BlobDataSnapshot> BlobStorageContext::CreateSnapshot(
    const std::string& uuid) {
  std::unique_ptr<BlobDataSnapshot> result;
  BlobEntry* entry = registry_.GetEntry(uuid);
  if (entry->status() != BlobStatus::DONE)
    return result;

  std::unique_ptr<BlobDataSnapshot> snapshot(new BlobDataSnapshot(
      uuid, entry->content_type(), entry->content_disposition()));
  snapshot->items_.reserve(entry->items().size());
  for (const auto& shareable_item : entry->items()) {
    snapshot->items_.push_back(shareable_item->item());
  }
  memory_controller_.NotifyMemoryItemsUsed(entry->items());
  return snapshot;
}

Project: Chrome

void BlobStorageContext::DecrementBlobRefCount(const std::string& uuid) {
  BlobEntry* entry = registry_.GetEntry(uuid);
  DCHECK(entry);
  DCHECK_GT(entry->refcount(), 0u);
  entry->DecrementRefCount();
  if (entry->refcount() == 0) {
    DVLOG(1) << "BlobStorageContext::DecrementBlobRefCount(" << uuid
             << "): Deleting blob.";
    ClearAndFreeMemory(entry);
    registry_.DeleteEntry(uuid);
  }
}

Project: Chrome

void BlobStorageContext::FinishBuilding(BlobEntry* entry) {
  DCHECK(entry);

  BlobStatus status = entry->status_;
  DCHECK_NE(BlobStatus::DONE, status);

  bool error = BlobStatusIsError(status);
  UMA_HISTOGRAM_BOOLEAN("Storage.Blob.Broken", error);
  if (error) {
    UMA_HISTOGRAM_ENUMERATION("Storage.Blob.BrokenReason",
                              static_cast<int>(status),
                              (static_cast<int>(BlobStatus::LAST_ERROR) + 1));
  }

  if (BlobStatusIsPending(entry->status_)) {
    for (const ItemCopyEntry& copy : entry->building_state_->copies) {
      size_t dest_size = static_cast<size_t>(copy.dest_item->item()->length());
      DataElement::Type dest_type = copy.dest_item->item()->type();
      switch (copy.source_item->item()->type()) {
        case DataElement::TYPE_BYTES: {
          DCHECK_EQ(dest_type, DataElement::TYPE_BYTES_DESCRIPTION);
          const char* src_data =
              copy.source_item->item()->bytes() + copy.source_item_offset;
          copy.dest_item->item()->item_->SetToBytes(src_data, dest_size);
          break;
        }
        case DataElement::TYPE_FILE: {
          if (dest_type == DataElement::TYPE_BYTES_DESCRIPTION)
            copy.dest_item->set_memory_allocation(nullptr);

          const DataElement& source_element =
              copy.source_item->item()->data_element();
          std::unique_ptr<DataElement> new_element(new DataElement());
          new_element->SetToFilePathRange(
              source_element.path(),
              source_element.offset() + copy.source_item_offset, dest_size,
              source_element.expected_modification_time());
          scoped_refptr<BlobDataItem> new_item(new BlobDataItem(
              std::move(new_element), copy.source_item->item()->data_handle_));
          copy.dest_item->set_item(std::move(new_item));
          break;
        }
        case DataElement::TYPE_UNKNOWN:
        case DataElement::TYPE_BLOB:
        case DataElement::TYPE_BYTES_DESCRIPTION:
        case DataElement::TYPE_FILE_FILESYSTEM:
        case DataElement::TYPE_DISK_CACHE_ENTRY:
        case DataElement::TYPE_DATA_PIPE:
          NOTREACHED();
          break;
      }
      copy.dest_item->set_state(ShareableBlobDataItem::POPULATED_WITH_QUOTA);
    }

    entry->set_status(BlobStatus::DONE);
  }

  std::vector<BlobStatusCallback> callbacks;
  if (entry->building_state_.get()) {
    std::swap(callbacks, entry->building_state_->build_completion_callbacks);
    entry->building_state_.reset();
  }

  memory_controller_.NotifyMemoryItemsUsed(entry->items());

  auto runner = base::ThreadTaskRunnerHandle::Get();
  for (const auto& callback : callbacks)
    runner->PostTask(FROM_HERE, base::Bind(callback, entry->status()));

  for (const auto& shareable_item : entry->items()) {
    DCHECK_NE(DataElement::TYPE_BYTES_DESCRIPTION,
              shareable_item->item()->type());
    DCHECK(shareable_item->IsPopulated()) << shareable_item->state();
  }
}

Project: Chrome

std::unique_ptr<BlobDataHandle> BlobStorageContext::GetBlobDataFromPublicURL(
    const GURL& url) {
  std::string uuid;
  BlobEntry* entry = registry_.GetEntryFromURL(url, &uuid);
  if (!entry)
    return nullptr;
  return CreateHandle(uuid, entry);
}

Project: Chrome

std::unique_ptr<BlobDataHandle> BlobStorageContext::GetBlobDataFromUUID(
    const std::string& uuid) {
  BlobEntry* entry = registry_.GetEntry(uuid);
  if (!entry)
    return nullptr;
  return CreateHandle(uuid, entry);
}

Project: Chrome

BlobStatus BlobStorageContext::GetBlobStatus(const std::string& uuid) const {
  const BlobEntry* entry = registry_.GetEntry(uuid);
  if (!entry)
    return BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS;
  return entry->status();
}

Project: Chrome

void BlobStorageContext::IncrementBlobRefCount(const std::string& uuid) {
  BlobEntry* entry = registry_.GetEntry(uuid);
  DCHECK(entry);
  entry->IncrementRefCount();
}

Project: Chrome

bool IsBytes(DataElement::Type type) {
  return type == DataElement::TYPE_BYTES ||
         type == DataElement::TYPE_BYTES_DESCRIPTION;
}

Project: Chrome

void BlobStorageContext::NotifyTransportComplete(const std::string& uuid) {
  BlobEntry* entry = registry_.GetEntry(uuid);
  CHECK(entry) << "There is no blob entry with uuid " << uuid;
  DCHECK(BlobStatusIsPending(entry->status()));
  NotifyTransportCompleteInternal(entry);
}

Project: Chrome

void BlobStorageContext::OnDependentBlobFinished(
    const std::string& owning_blob_uuid,
    BlobStatus status) {
  BlobEntry* entry = registry_.GetEntry(owning_blob_uuid);
  if (!entry || !entry->building_state_)
    return;

  if (BlobStatusIsError(status)) {
    DCHECK_NE(BlobStatus::ERR_BLOB_DEREFERENCED_WHILE_BUILDING, status)
        << "Referenced blob should never be dereferenced while we "
        << "are depending on it, as our system holds a handle.";
    CancelBuildingBlobInternal(entry, BlobStatus::ERR_REFERENCED_BLOB_BROKEN);
    return;
  }
  DCHECK_GT(entry->building_state_->num_building_dependent_blobs, 0u);
  --entry->building_state_->num_building_dependent_blobs;

  if (entry->CanFinishBuilding())
    FinishBuilding(entry);
}

Project: Chrome

void BlobStorageContext::OnEnoughSpaceForCopies(const std::string& uuid,
                                                bool success) {
  if (!success) {
    CancelBuildingBlob(uuid, BlobStatus::ERR_OUT_OF_MEMORY);
    return;
  }
  BlobEntry* entry = registry_.GetEntry(uuid);
  if (!entry)
    return;

  if (entry->CanFinishBuilding())
    FinishBuilding(entry);
}

Project: Chrome

void BlobStorageContext::OnEnoughSpaceForTransport(
    const std::string& uuid,
    std::vector<BlobMemoryController::FileCreationInfo> files,
    bool success) {
  if (!success) {
    CancelBuildingBlob(uuid, BlobStatus::ERR_OUT_OF_MEMORY);
    return;
  }
  BlobEntry* entry = registry_.GetEntry(uuid);
  if (!entry || !entry->building_state_.get())
    return;
  BlobEntry::BuildingState& building_state = *entry->building_state_;
  DCHECK(!building_state.transport_quota_request);
  DCHECK(building_state.transport_items_present);

  entry->set_status(BlobStatus::PENDING_TRANSPORT);
  RequestTransport(entry, std::move(files));

  if (entry->CanFinishBuilding())
    FinishBuilding(entry);
}

Project: Chrome

void RecordBlobItemSizeStats(const DataElement& input_element) {
  uint64_t length = input_element.length();

  switch (input_element.type()) {
    case DataElement::TYPE_BYTES:
    case DataElement::TYPE_BYTES_DESCRIPTION:
      UMA_HISTOGRAM_COUNTS_1M("Storage.BlobItemSize.Bytes", length / 1024);
      break;
    case DataElement::TYPE_BLOB:
      UMA_HISTOGRAM_COUNTS_1M("Storage.BlobItemSize.Blob",
                              (length - input_element.offset()) / 1024);
      break;
    case DataElement::TYPE_FILE: {
      bool full_file = (length == std::numeric_limits<uint64_t>::max());
      UMA_HISTOGRAM_BOOLEAN("Storage.BlobItemSize.File.Unknown", full_file);
      if (!full_file) {
        UMA_HISTOGRAM_COUNTS_1M("Storage.BlobItemSize.File",
                                (length - input_element.offset()) / 1024);
      }
      break;
    }
    case DataElement::TYPE_FILE_FILESYSTEM: {
      bool full_file = (length == std::numeric_limits<uint64_t>::max());
      UMA_HISTOGRAM_BOOLEAN("Storage.BlobItemSize.FileSystem.Unknown",
                            full_file);
      if (!full_file) {
        UMA_HISTOGRAM_COUNTS_1M("Storage.BlobItemSize.FileSystem",
                                (length - input_element.offset()) / 1024);
      }
      break;
    }
    case DataElement::TYPE_DISK_CACHE_ENTRY:
      UMA_HISTOGRAM_COUNTS_1M("Storage.BlobItemSize.CacheEntry",
                              (length - input_element.offset()) / 1024);
      break;
    case DataElement::TYPE_DATA_PIPE:
    case DataElement::TYPE_UNKNOWN:
      NOTREACHED();
      break;
  }
}

Project: Chrome

bool BlobStorageContext::RegisterPublicBlobURL(const GURL& blob_url,
                                               const std::string& uuid) {
  if (!registry_.CreateUrlMapping(blob_url, uuid))
    return false;
  IncrementBlobRefCount(uuid);
  return true;
}

Project: Chrome

void BlobStorageContext::RequestTransport(
    BlobEntry* entry,
    std::vector<BlobMemoryController::FileCreationInfo> files) {
  BlobEntry::BuildingState* building_state = entry->building_state_.get();
  if (building_state->transport_allowed_callback) {
    base::ResetAndReturn(&building_state->transport_allowed_callback)
        .Run(BlobStatus::PENDING_TRANSPORT, std::move(files));
    return;
  }
  DCHECK(files.empty());
  NotifyTransportCompleteInternal(entry);
}

Project: Chrome

void BlobStorageContext::RevokePublicBlobURL(const GURL& blob_url) {
  std::string uuid;
  if (!registry_.DeleteURLMapping(blob_url, &uuid))
    return;
  DecrementBlobRefCount(uuid);
}

Project: Chrome

void BlobStorageContext::RunOnConstructionBegin(
    const std::string& uuid,
    const BlobStatusCallback& done) {
  BlobEntry* entry = registry_.GetEntry(uuid);
  DCHECK(entry);
  if (entry->status() == BlobStatus::PENDING_CONSTRUCTION) {
    entry->building_state_->build_started_callbacks.push_back(done);
    return;
  }
  done.Run(entry->status());
}

Project: Chrome

void BlobStorageContext::RunOnConstructionComplete(
    const std::string& uuid,
    const BlobStatusCallback& done) {
  BlobEntry* entry = registry_.GetEntry(uuid);
  DCHECK(entry);
  if (BlobStatusIsPending(entry->status())) {
    entry->building_state_->build_completion_callbacks.push_back(done);
    return;
  }
  done.Run(entry->status());
}

Project: Chrome

BlobStorageContext::BlobFlattener::~BlobFlattener() {}

Project: Chrome

BlobStorageContext::BlobSlice::~BlobSlice() {}

Project: Chrome

BlobStorageContext::~BlobStorageContext() {}

Project: Chrome

size_t AppendDataInBuilder(BlobDataBuilder* builder,
                           size_t index,
                           disk_cache::Entry* cache_entry) {
  size_t size = 0;
  if (index % 2 != 0) {
    builder->AppendFutureData(5u);
    size += 5u;
    if (index % 3 == 1) {
      builder->AppendData("abcdefghij", 4u);
      size += 4u;
    }
    if (index % 3 == 0) {
      builder->AppendFutureData(1u);
      size += 1u;
    }
  } else if (index % 3 == 0) {
    builder->AppendFutureFile(0lu, 3lu, 0);
    size += 3u;
  }
  if (index % 5 != 0) {
    builder->AppendFile(
        base::FilePath::FromUTF8Unsafe(base::SizeTToString(index)), 0ul, 20ul,
        base::Time::Max());
    size += 20u;
  }
  if (index % 3 != 0) {
    scoped_refptr<BlobDataBuilder::DataHandle> disk_cache_data_handle =
        new EmptyDataHandle();
    builder->AppendDiskCacheEntry(disk_cache_data_handle, cache_entry,
                                  kTestDiskCacheStreamIndex);
    size += strlen(kTestDiskCacheData);
  }
  return size;
}

Project: Chrome

  BlobStorageContextTest() {}

Project: Chrome

disk_cache::ScopedEntryPtr CreateDiskCacheEntry(disk_cache::Backend* cache,
                                                const char* key,
                                                const std::string& data) {
  disk_cache::Entry* temp_entry = nullptr;
  net::TestCompletionCallback callback;
  int rv = cache->CreateEntry(key, &temp_entry, callback.callback());
  if (callback.GetResult(rv) != net::OK)
    return nullptr;
  disk_cache::ScopedEntryPtr entry(temp_entry);

  scoped_refptr<net::StringIOBuffer> iobuffer = new net::StringIOBuffer(data);
  rv = entry->WriteData(kTestDiskCacheStreamIndex, 0, iobuffer.get(),
                        iobuffer->size(), callback.callback(), false);
  EXPECT_EQ(static_cast<int>(data.size()), callback.GetResult(rv));
  return entry;
}

Project: Chrome

std::unique_ptr<disk_cache::Backend> CreateInMemoryDiskCache() {
  std::unique_ptr<disk_cache::Backend> cache;
  net::TestCompletionCallback callback;
  int rv = disk_cache::CreateCacheBackend(
      net::MEMORY_CACHE, net::CACHE_BACKEND_DEFAULT, base::FilePath(), 0, false,
      nullptr, &cache, callback.callback());
  EXPECT_EQ(net::OK, callback.GetResult(rv));

  return cache;
}

Project: Chrome

  void DecrementRefCount(const std::string& uuid) {
    context_->DecrementBlobRefCount(uuid);
  }

Project: Chrome

bool DoesBuilderHaveFutureData(size_t index) {
  return index < kTotalRawBlobs && (index % 2 != 0 || index % 3 == 0);
}

Project: Chrome

void IncrementNumber(size_t* number, BlobStatus status) {
  EXPECT_EQ(BlobStatus::DONE, status);
  *number = *number + 1;
}

Project: Chrome

void SaveBlobStatus(BlobStatus* status_ptr, BlobStatus status) {
  *status_ptr = status;
}

Project: Chrome

  std::unique_ptr<BlobDataHandle> SetupBasicBlob(const std::string& id) {
    BlobDataBuilder builder(id);
    builder.AppendData("1", 1);
    builder.set_content_type("text/plain");
    return context_->AddFinishedBlob(builder);
  }