// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "content/browser/download/download_manager_impl.h"

#include <iterator>
#include <utility>

#include "base/debug/alias.h"
#include "base/files/file_util.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/functional/callback_helpers.h"
#include "base/i18n/case_conversion.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/memory/weak_ptr.h"
#include "base/metrics/histogram_macros.h"
#include "base/observer_list.h"
#include "base/strings/string_util.h"
#include "base/strings/sys_string_conversions.h"
#include "base/supports_user_data.h"
#include "base/synchronization/lock.h"
#include "base/task/single_thread_task_runner.h"
#include "base/task/thread_pool.h"
#include "build/build_config.h"
#include "components/download/database/in_progress/download_entry.h"
#include "components/download/public/common/download_create_info.h"
#include "components/download/public/common/download_danger_type.h"
#include "components/download/public/common/download_features.h"
#include "components/download/public/common/download_file.h"
#include "components/download/public/common/download_interrupt_reasons.h"
#include "components/download/public/common/download_item_factory.h"
#include "components/download/public/common/download_item_impl.h"
#include "components/download/public/common/download_item_rename_handler.h"
#include "components/download/public/common/download_stats.h"
#include "components/download/public/common/download_target_info.h"
#include "components/download/public/common/download_task_runner.h"
#include "components/download/public/common/download_url_parameters.h"
#include "components/download/public/common/download_utils.h"
#include "components/download/public/common/input_stream.h"
#include "components/download/public/common/url_download_handler_factory.h"
#include "content/browser/bad_message.h"
#include "content/browser/blob_storage/chrome_blob_storage_context.h"
#include "content/browser/data_url_loader_factory.h"
#include "content/browser/devtools/devtools_instrumentation.h"
#include "content/browser/download/embedder_download_data.pb.h"
#include "content/browser/file_system/file_system_url_loader_factory.h"
#include "content/browser/loader/file_url_loader_factory.h"
#include "content/browser/loader/response_head_update_params.h"
#include "content/browser/renderer_host/render_frame_host_impl.h"
#include "content/browser/renderer_host/render_view_host_impl.h"
#include "content/browser/service_worker/service_worker_context_wrapper.h"
#include "content/browser/service_worker/service_worker_main_resource_handle.h"
#include "content/browser/service_worker/service_worker_main_resource_loader_interceptor.h"
#include "content/browser/site_info.h"
#include "content/browser/storage_partition_impl.h"
#include "content/browser/web_contents/web_contents_impl.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/content_browser_client.h"
#include "content/public/browser/device_service.h"
#include "content/public/browser/disallow_activation_reason.h"
#include "content/public/browser/download_item_utils.h"
#include "content/public/browser/download_manager_delegate.h"
#include "content/public/browser/download_request_utils.h"
#include "content/public/browser/global_routing_id.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/shared_cors_origin_access_list.h"
#include "content/public/browser/web_contents_delegate.h"
#include "content/public/browser/web_ui_url_loader_factory.h"
#include "content/public/common/content_client.h"
#include "content/public/common/content_features.h"
#include "content/public/common/url_constants.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/self_owned_receiver.h"
#include "net/base/elements_upload_data_stream.h"
#include "net/base/isolation_info.h"
#include "net/base/load_flags.h"
#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
#include "net/base/request_priority.h"
#include "net/base/upload_bytes_element_reader.h"
#include "services/metrics/public/cpp/ukm_source_id.h"
#include "services/network/public/cpp/cross_thread_pending_shared_url_loader_factory.h"
#include "services/network/public/cpp/features.h"
#include "services/network/public/cpp/is_potentially_trustworthy.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
#include "services/network/public/cpp/url_loader_factory_builder.h"
#include "services/network/public/cpp/url_util.h"
#include "services/network/public/cpp/wrapper_shared_url_loader_factory.h"
#include "third_party/blink/public/common/loader/referrer_utils.h"
#include "third_party/blink/public/common/loader/throttling_url_loader.h"
#include "third_party/blink/public/common/storage_key/storage_key.h"
#include "url/origin.h"

#if BUILDFLAG(IS_ANDROID)
#include "net/http/http_content_disposition.h"
#endif  // BUILDFLAG(IS_ANDROID)

namespace content {
namespace {
#if BUILDFLAG(IS_ANDROID)
// PDF MIME type.
constexpr char kPdfMimeType[] = "application/pdf";
#endif  // BUILDFLAG(IS_ANDROID)

// A SharedURLLoaderFactory that accepts CreateLoaderAndStart calls on the UI
// thread and forwards them to the IO thread, where the wrapped
// PendingSharedURLLoaderFactory is materialized. This is needed for the
// Service Worker download fallback path: Fallback() runs on the UI thread, but
// the network-service factory (ReconnectableURLLoaderFactoryForIOThread)
// requires IO-thread materialisation.
//
// Single-use: CreateLoaderAndStart() consumes the wrapped
// PendingSharedURLLoaderFactory by moving it into the IO-thread task, so a
// second call would dereference a null pending factory. A CHECK guards
// against this misuse; callers must construct a fresh instance per request.
//
// Lifetime: the materialised factory is parked in an IO-thread-resident
// IOState struct so its mojo::Remote outlives the synchronous
// CreateLoaderAndStart call (the network-service-side URLLoaderFactory may
// observe a receiver disconnect otherwise). IOState is owned by the wrapper
// via a unique_ptr with OnTaskRunnerDeleter targeting the IO sequence, so
// destruction of IOState — and release of the materialised factory's
// scoped_refptr — happens on IO regardless of which thread destroys the
// wrapper.
//
// The wrapper itself is not ref-counted across threads: SharedURLLoaderFactory
// uses single-thread base::RefCounted, so AddRef/Release must stay on the
// sequence that created the first ref. The IO-thread task captures only a
// raw IOState* (base::Unretained), valid because the wrapper's
// OnTaskRunnerDeleter posts IOState destruction to the same IO sequence and
// PostTask is FIFO — IOState deletion is ordered strictly after this task.
class DeferredIOThreadURLLoaderFactory
    : public network::SharedURLLoaderFactory {
 public:
  explicit DeferredIOThreadURLLoaderFactory(
      std::unique_ptr<network::PendingSharedURLLoaderFactory> pending)
      : pending_(std::move(pending)),
        io_state_(new IOState,
                  base::OnTaskRunnerDeleter(GetIOThreadTaskRunner({}))) {}

  DeferredIOThreadURLLoaderFactory(const DeferredIOThreadURLLoaderFactory&) =
      delete;
  DeferredIOThreadURLLoaderFactory& operator=(
      const DeferredIOThreadURLLoaderFactory&) = delete;

  // network::SharedURLLoaderFactory:
  void CreateLoaderAndStart(
      mojo::PendingReceiver<network::mojom::URLLoader> loader,
      int32_t request_id,
      uint32_t options,
      const network::ResourceRequest& request,
      mojo::PendingRemote<network::mojom::URLLoaderClient> client,
      const net::MutableNetworkTrafficAnnotationTag& traffic_annotation)
      override {
    DCHECK_CURRENTLY_ON(BrowserThread::UI);
    CHECK(pending_);
    GetIOThreadTaskRunner({})->PostTask(
        FROM_HERE,
        base::BindOnce(
            &DeferredIOThreadURLLoaderFactory::CreateLoaderAndStartOnIO,
            base::Unretained(io_state_.get()), std::move(pending_),
            std::move(loader), request_id, options, request, std::move(client),
            traffic_annotation));
  }

  void Clone(mojo::PendingReceiver<network::mojom::URLLoaderFactory>) override {
    NOTREACHED();
  }

  std::unique_ptr<network::PendingSharedURLLoaderFactory> Clone() override {
    NOTREACHED();
  }

 private:
  // IO-thread-resident state. `materialized` is default-constructed (null)
  // on the wrapper's construction thread and only assigned/read/destroyed on
  // the IO thread.
  struct IOState {
    scoped_refptr<network::SharedURLLoaderFactory> materialized;
  };

  ~DeferredIOThreadURLLoaderFactory() override = default;

  static void CreateLoaderAndStartOnIO(
      IOState* io_state,
      std::unique_ptr<network::PendingSharedURLLoaderFactory> pending,
      mojo::PendingReceiver<network::mojom::URLLoader> loader,
      int32_t request_id,
      uint32_t options,
      const network::ResourceRequest& request,
      mojo::PendingRemote<network::mojom::URLLoaderClient> client,
      const net::MutableNetworkTrafficAnnotationTag& traffic_annotation) {
    DCHECK_CURRENTLY_ON(BrowserThread::IO);
    DCHECK(!io_state->materialized);
    io_state->materialized =
        network::SharedURLLoaderFactory::Create(std::move(pending));
    io_state->materialized->CreateLoaderAndStart(
        std::move(loader), request_id, options, request, std::move(client),
        traffic_annotation);
  }

  // UI thread: set in the constructor, moved into the IO-thread task.
  std::unique_ptr<network::PendingSharedURLLoaderFactory> pending_;
  // Holds the materialised factory on the IO thread for the wrapper's
  // lifetime. Destruction is posted to the IO sequence by
  // OnTaskRunnerDeleter.
  std::unique_ptr<IOState, base::OnTaskRunnerDeleter> io_state_;
};

// A SharedURLLoaderFactory that wraps the service worker's
// SingleRequestURLLoaderFactory and extends the lifetime of the interceptor
// and handle across the download's async URLLoader invocation. The interceptor
// transitively owns the ServiceWorkerMainResourceLoader (via its
// ServiceWorkerControlleeRequestHandler and
// ServiceWorkerMainResourceLoaderWrapper); the wrapped
// SingleRequestURLLoaderFactory only holds a WeakPtr to that loader, so if the
// interceptor is dropped the factory's bound callback becomes a no-op. The
// handle owns the ServiceWorkerClient that the loader dispatches against, so
// it must outlive the loader as well.
class ServiceWorkerDownloadURLLoaderFactory
    : public network::SharedURLLoaderFactory {
 public:
  ServiceWorkerDownloadURLLoaderFactory(
      scoped_refptr<network::SharedURLLoaderFactory> wrapped,
      scoped_refptr<network::SharedURLLoaderFactory> network_fallback_factory,
      std::unique_ptr<ServiceWorkerMainResourceLoaderInterceptor> interceptor,
      std::unique_ptr<ServiceWorkerMainResourceHandle> handle)
      : wrapped_(std::move(wrapped)),
        network_fallback_factory_(std::move(network_fallback_factory)),
        interceptor_(std::move(interceptor)),
        handle_(std::move(handle)) {}

  ServiceWorkerDownloadURLLoaderFactory(
      const ServiceWorkerDownloadURLLoaderFactory&) = delete;
  ServiceWorkerDownloadURLLoaderFactory& operator=(
      const ServiceWorkerDownloadURLLoaderFactory&) = delete;

  // network::SharedURLLoaderFactory:
  void CreateLoaderAndStart(
      mojo::PendingReceiver<network::mojom::URLLoader> loader,
      int32_t request_id,
      uint32_t options,
      const network::ResourceRequest& request,
      mojo::PendingRemote<network::mojom::URLLoaderClient> client,
      const net::MutableNetworkTrafficAnnotationTag& traffic_annotation)
      override {
    wrapped_->CreateLoaderAndStart(std::move(loader), request_id, options,
                                   request, std::move(client),
                                   traffic_annotation);
  }

  void Clone(mojo::PendingReceiver<network::mojom::URLLoaderFactory> receiver)
      override {
    wrapped_->Clone(std::move(receiver));
  }

  std::unique_ptr<network::PendingSharedURLLoaderFactory> Clone() override {
    // Downloads don't need to clone across threads at this level — the outer
    // CrossThreadPendingSharedURLLoaderFactory handles that.
    NOTREACHED();
  }

 private:
  ~ServiceWorkerDownloadURLLoaderFactory() override = default;

  scoped_refptr<network::SharedURLLoaderFactory> wrapped_;
  // Keeps the FallbackCallback's returned raw pointer valid.
  scoped_refptr<network::SharedURLLoaderFactory> network_fallback_factory_;
  std::unique_ptr<ServiceWorkerMainResourceLoaderInterceptor> interceptor_;
  std::unique_ptr<ServiceWorkerMainResourceHandle> handle_;
};

void DeleteDownloadedFileOnUIThread(const base::FilePath& file_path) {
  if (!file_path.empty()) {
    download::GetDownloadTaskRunner()->PostTask(
        FROM_HERE,
        base::BindOnce(base::IgnoreResult(&download::DeleteDownloadedFile),
                       file_path));
  }
}

StoragePartitionImpl* GetStoragePartitionForConfig(
    BrowserContext* context,
    const StoragePartitionConfig& storage_partition_config) {
  return static_cast<StoragePartitionImpl*>(
      context->GetStoragePartition(storage_partition_config));
}

void OnDownloadStarted(
    download::DownloadItemImpl* download,
    download::DownloadUrlParameters::OnStartedCallback on_started) {
  if (on_started.is_null())
    return;

  if (!download || download->GetState() == download::DownloadItem::CANCELLED) {
    std::move(on_started)
        .Run(nullptr, download::DOWNLOAD_INTERRUPT_REASON_USER_CANCELED);
  } else {
    std::move(on_started)
        .Run(download, download::DOWNLOAD_INTERRUPT_REASON_NONE);
  }
}

// Creates an interrupted download and calls StartDownload. Can be called on
// any thread.
void CreateInterruptedDownload(
    std::unique_ptr<download::DownloadUrlParameters> params,
    download::DownloadInterruptReason reason,
    base::WeakPtr<DownloadManagerImpl> download_manager) {
  std::unique_ptr<download::DownloadCreateInfo> failed_created_info(
      new download::DownloadCreateInfo(
          base::Time::Now(), base::WrapUnique(new download::DownloadSaveInfo)));
  failed_created_info->url_chain.push_back(params->url());
  failed_created_info->result = reason;
  GetUIThreadTaskRunner({})->PostTask(
      FROM_HERE,
      base::BindOnce(&DownloadManagerImpl::StartDownload, download_manager,
                     std::move(failed_created_info),
                     std::make_unique<download::InputStream>(),
                     std::move(params->callback())));
}

class DownloadItemFactoryImpl : public download::DownloadItemFactory {
 public:
  DownloadItemFactoryImpl() {}
  ~DownloadItemFactoryImpl() override {}

  download::DownloadItemImpl* CreatePersistedItem(
      download::DownloadItemImplDelegate* delegate,
      const std::string& guid,
      uint32_t download_id,
      const base::FilePath& current_path,
      const base::FilePath& target_path,
      const std::vector<GURL>& url_chain,
      const GURL& referrer_url,
      const std::string& serialized_embedder_download_data,
      const GURL& tab_url,
      const GURL& tab_refererr_url,
      const std::optional<url::Origin>& request_initiator,
      const std::string& mime_type,
      const std::string& original_mime_type,
      base::Time start_time,
      base::Time end_time,
      const std::string& etag,
      const std::string& last_modified,
      int64_t received_bytes,
      int64_t total_bytes,
      const std::string& hash,
      download::DownloadItem::DownloadState state,
      download::DownloadDangerType danger_type,
      download::DownloadInterruptReason interrupt_reason,
      bool opened,
      base::Time last_access_time,
      bool transient,
      const std::vector<download::DownloadItem::ReceivedSlice>& received_slices)
      override {
    // For history download only as history don't have auto resumption count
    // saved.
    int auto_resume_count = download::DownloadItemImpl::kMaxAutoResumeAttempts;

    return new download::DownloadItemImpl(
        delegate, guid, download_id, current_path, target_path, url_chain,
        referrer_url, serialized_embedder_download_data, tab_url,
        tab_refererr_url, request_initiator, mime_type, original_mime_type,
        start_time, end_time, etag, last_modified, received_bytes, total_bytes,
        auto_resume_count, hash, state, danger_type, interrupt_reason,
        false /* paused */, false /* allow_metered */, opened, last_access_time,
        transient, received_slices, download::kInvalidRange,
        download::kInvalidRange, nullptr /* download_entry */);
  }

  download::DownloadItemImpl* CreateActiveItem(
      download::DownloadItemImplDelegate* delegate,
      uint32_t download_id,
      const download::DownloadCreateInfo& info) override {
    return new download::DownloadItemImpl(delegate, download_id, info);
  }

  download::DownloadItemImpl* CreateSavePageItem(
      download::DownloadItemImplDelegate* delegate,
      uint32_t download_id,
      const base::FilePath& path,
      const base::FilePath& display_name,
      const GURL& url,
      const std::string& mime_type,
      download::DownloadJob::CancelRequestCallback cancel_request_callback)
      override {
    return new download::DownloadItemImpl(delegate, download_id, path,
                                          display_name, url, mime_type,
                                          std::move(cancel_request_callback));
  }
};

std::unique_ptr<network::PendingSharedURLLoaderFactory>
CreatePendingSharedURLLoaderFactory(StoragePartitionImpl* storage_partition,
                                    RenderFrameHost* rfh) {
  network::URLLoaderFactoryBuilder factory_builder;

  if (rfh) {
    // Allow DevTools to potentially inject itself into `factory_builder`.
    devtools_instrumentation::WillCreateURLLoaderFactoryParams::ForFrame(
        static_cast<RenderFrameHostImpl*>(rfh))
        .Run(/*is_navigation=*/true,
             /*is_download=*/true, factory_builder,
             nullptr /* factory_override */, /*header_client=*/nullptr);

    // Also allow the Content embedder to inject itself if it wants to.
    GetContentClient()->browser()->WillCreateURLLoaderFactory(
        rfh->GetSiteInstance()->GetBrowserContext(), rfh,
        rfh->GetProcess()->GetDeprecatedID(),
        ContentBrowserClient::URLLoaderFactoryType::kDownload, url::Origin(),
        net::IsolationInfo(), /*navigation_id=*/std::nullopt,
        ukm::kInvalidSourceIdObj, factory_builder, /*header_client=*/nullptr,
        /*bypass_redirect_checks=*/nullptr, /*disable_secure_dns=*/nullptr,
        /*factory_override=*/nullptr,
        /*navigation_response_task_runner=*/nullptr,
        /*is_for_network_service=*/true);
  }

  if (factory_builder.RequiresFreshFactory()) {
    network::mojom::URLLoaderFactoryParamsPtr params =
        storage_partition->CreateURLLoaderFactoryParams();
    auto factory =
        std::move(factory_builder)
            .Finish<mojo::PendingRemote<network::mojom::URLLoaderFactory>>(
                storage_partition->GetNetworkContext(), std::move(params));
    return std::make_unique<network::WrapperPendingSharedURLLoaderFactory>(
        std::move(factory));
  }

  return std::make_unique<network::PendingSharedURLLoaderFactoryWithBuilder>(
      std::move(factory_builder),
      storage_partition->GetURLLoaderFactoryForBrowserProcessIOThread());
}

void RecordDownloadOpenerType(RenderFrameHost* current,
                              RenderFrameHost* opener) {
  DCHECK(current);
  DCHECK(opener);
  if (!opener->GetLastCommittedURL().SchemeIsHTTPOrHTTPS() ||
      !current->GetLastCommittedURL().SchemeIsHTTPOrHTTPS()) {
    UMA_HISTOGRAM_ENUMERATION("Download.InitiatedByWindowOpener",
                              InitiatedByWindowOpenerType::kNonHTTPOrHTTPS);
    return;
  }
  if (opener->GetLastCommittedOrigin() == current->GetLastCommittedOrigin()) {
    UMA_HISTOGRAM_ENUMERATION("Download.InitiatedByWindowOpener",
                              InitiatedByWindowOpenerType::kSameOrigin);
    return;
  }
  if (net::registry_controlled_domains::SameDomainOrHost(
          opener->GetLastCommittedOrigin(), current->GetLastCommittedOrigin(),
          net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES)) {
    UMA_HISTOGRAM_ENUMERATION("Download.InitiatedByWindowOpener",
                              InitiatedByWindowOpenerType::kSameSite);
    return;
  }
  UMA_HISTOGRAM_ENUMERATION("Download.InitiatedByWindowOpener",
                            InitiatedByWindowOpenerType::kCrossOrigin);
}

}  // namespace

DownloadManagerImpl::DownloadManagerImpl(BrowserContext* browser_context)
    : item_factory_(new DownloadItemFactoryImpl()),
      shutdown_needed_(true),
      history_db_initialized_(false),
      in_progress_cache_initialized_(false),
      browser_context_(browser_context),
      delegate_(nullptr),
      in_progress_manager_(
          browser_context_->RetrieveInProgressDownloadManager()),
      next_download_id_(download::DownloadItem::kInvalidId),
      is_history_download_id_retrieved_(false),
      should_persist_new_download_(false),
      disk_access_task_runner_(base::ThreadPool::CreateSequencedTaskRunner(
          {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
           base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN})) {
  DCHECK(browser_context);

  download::SetIOTaskRunner(GetIOThreadTaskRunner({}));

  if (!in_progress_manager_) {
    auto* proto_db_provider = browser_context->GetDefaultStoragePartition()
                                  ->GetProtoDatabaseProvider();
    in_progress_manager_ =
        std::make_unique<download::InProgressDownloadManager>(
            this, base::FilePath(), proto_db_provider,
            base::BindRepeating(&network::IsUrlPotentiallyTrustworthy),
            base::BindRepeating(&DownloadRequestUtils::IsURLSafe),
            /*wake_lock_provider_binder=*/base::NullCallback());
  } else {
    in_progress_manager_->SetDelegate(this);
    in_progress_manager_->set_download_start_observer(nullptr);
    in_progress_manager_->set_is_origin_secure_cb(
        base::BindRepeating(&network::IsUrlPotentiallyTrustworthy));
  }
}

DownloadManagerImpl::~DownloadManagerImpl() {
  DCHECK(!shutdown_needed_);
}

download::DownloadItemImpl* DownloadManagerImpl::CreateActiveItem(
    uint32_t id,
    const download::DownloadCreateInfo& info) {
  DCHECK_CURRENTLY_ON(BrowserThread::UI);

  if (downloads_by_guid_.contains(info.guid))
    return nullptr;

  download::DownloadItemImpl* download =
      item_factory_->CreateActiveItem(this, id, info);

  downloads_[id] = base::WrapUnique(download);
  downloads_by_guid_[download->GetGuid()] = download;
  GlobalRenderFrameHostId global_id(info.render_process_id,
                                    info.render_frame_id);
  DownloadItemUtils::AttachInfo(
      download, GetBrowserContext(),
      WebContentsImpl::FromRenderFrameHostID(global_id), global_id);
  if (delegate_) {
    delegate_->AttachExtraInfo(download);
#if BUILDFLAG(IS_ANDROID)
    download->set_is_from_external_app(delegate_->IsFromExternalApp(download));
#endif  // BUILDFLAG(IS_ANDROID)
  }

  return download;
}

void DownloadManagerImpl::GetNextId(GetNextIdCallback callback) {
  DCHECK_CURRENTLY_ON(BrowserThread::UI);
  if (IsNextIdInitialized()) {
    std::move(callback).Run(next_download_id_++);
    return;
  }

  id_callbacks_.emplace_back(
      std::make_unique<GetNextIdCallback>(std::move(callback)));
  // If we are first time here, call the delegate to get the next ID from
  // history db.
  if (!is_history_download_id_retrieved_ && id_callbacks_.size() == 1u) {
    if (delegate_) {
      delegate_->GetNextId(
          base::BindOnce(&DownloadManagerImpl::OnHistoryNextIdRetrieved,
                         weak_factory_.GetWeakPtr()));
    } else {
      OnHistoryNextIdRetrieved(download::DownloadItem::kInvalidId);
    }
  }
}

void DownloadManagerImpl::SetNextId(uint32_t next_id) {
  if (next_id > next_download_id_)
    next_download_id_ = next_id;
  if (!IsNextIdInitialized())
    return;

  for (auto& callback : id_callbacks_)
    std::move(*callback).Run(next_download_id_++);
  id_callbacks_.clear();
}

std::string
DownloadManagerImpl::StoragePartitionConfigToSerializedEmbedderDownloadData(
    const StoragePartitionConfig& storage_partition_config) {
  proto::EmbedderDownloadData embedder_download_data;
  proto::StoragePartitionConfig* config_proto =
      embedder_download_data.mutable_storage_partition_config();
  config_proto->set_partition_domain(
      storage_partition_config.partition_domain());
  config_proto->set_partition_name(storage_partition_config.partition_name());
  config_proto->set_in_memory(storage_partition_config.in_memory());

  switch (
      storage_partition_config.fallback_to_partition_domain_for_blob_urls()) {
    case StoragePartitionConfig::FallbackMode::kNone:
      config_proto->set_fallback_mode(
          proto::StoragePartitionConfig_FallbackMode_kNone);
      break;
    case StoragePartitionConfig::FallbackMode::kFallbackPartitionOnDisk:
      config_proto->set_fallback_mode(
          proto::StoragePartitionConfig_FallbackMode_kPartitionOnDisk);
      break;
    case StoragePartitionConfig::FallbackMode::kFallbackPartitionInMemory:
      config_proto->set_fallback_mode(
          proto::StoragePartitionConfig_FallbackMode_kPartitionInMemory);
      break;
    default:
      config_proto->set_fallback_mode(
          proto::StoragePartitionConfig_FallbackMode_kNone);
  }
  return embedder_download_data.SerializeAsString();
}

StoragePartitionConfig
DownloadManagerImpl::SerializedEmbedderDownloadDataToStoragePartitionConfig(
    const std::string& serialized_embedder_download_data) {
  proto::EmbedderDownloadData embedder_download_data;
  bool success =
      embedder_download_data.ParseFromString(serialized_embedder_download_data);
  DCHECK(success);

  StoragePartitionConfig::FallbackMode fallback_mode;
  switch (embedder_download_data.storage_partition_config().fallback_mode()) {
    case proto::StoragePartitionConfig_FallbackMode_kNone:
      fallback_mode = StoragePartitionConfig::FallbackMode::kNone;
      break;
    case proto::StoragePartitionConfig_FallbackMode_kPartitionOnDisk:
      fallback_mode =
          StoragePartitionConfig::FallbackMode::kFallbackPartitionOnDisk;
      break;
    case proto::StoragePartitionConfig_FallbackMode_kPartitionInMemory:
      fallback_mode =
          StoragePartitionConfig::FallbackMode::kFallbackPartitionInMemory;
      break;
    default:
      fallback_mode = StoragePartitionConfig::FallbackMode::kNone;
      break;
  }

  StoragePartitionConfig config;
  if (embedder_download_data.storage_partition_config()
          .partition_domain()
          .empty()) {
    config = StoragePartitionConfig::CreateDefault(browser_context_);
  } else {
    config = StoragePartitionConfig::Create(
        browser_context_,
        embedder_download_data.storage_partition_config().partition_domain(),
        embedder_download_data.storage_partition_config().partition_name(),
        embedder_download_data.storage_partition_config().in_memory());
  }
  config.set_fallback_to_partition_domain_for_blob_urls(fallback_mode);
  return config;
}

void DownloadManagerImpl::OnHistoryNextIdRetrieved(uint32_t next_id) {
  DCHECK_CURRENTLY_ON(BrowserThread::UI);
  is_history_download_id_retrieved_ = true;
  if (next_id == download::DownloadItem::kInvalidId)
    next_id++;
  else
    should_persist_new_download_ = true;
  SetNextId(next_id);
}

StoragePartitionConfig DownloadManagerImpl::GetStoragePartitionConfigForSiteUrl(
    const GURL& site_url) {
  return SiteInfo::GetStoragePartitionConfigForUrl(browser_context_, site_url);
}

void DownloadManagerImpl::DetermineDownloadTarget(
    download::DownloadItemImpl* item,
    download::DownloadTargetCallback callback) {
  if (!delegate_ || !delegate_->DetermineDownloadTarget(item, &callback)) {
    base::FilePath target_path = item->GetForcedFilePath();
    // TODO(asanka): Determine a useful path if |target_path| is empty.
    download::DownloadTargetInfo target_info;
    target_info.target_path = target_path;
    target_info.intermediate_path = target_path;

    std::move(callback).Run(std::move(target_info));
  }
}

bool DownloadManagerImpl::ShouldCompleteDownload(
    download::DownloadItemImpl* item,
    base::OnceClosure complete_callback) {
  if (!delegate_ ||
      delegate_->ShouldCompleteDownload(item, std::move(complete_callback))) {
    return true;
  }
  // Otherwise, the delegate has accepted responsibility to run the
  // callback when the download is ready for completion.
  // TODO(qinmin): When returning false, the |complete_callback| should
  // be run by this class eventually. To do so we can't pass ownership
  // to |delegate_| unconditionally.
  return false;
}

bool DownloadManagerImpl::ShouldAutomaticallyOpenFile(
    const GURL& url,
    const base::FilePath& path) {
  if (!delegate_)
    return false;

  return delegate_->ShouldAutomaticallyOpenFile(url, path);
}

bool DownloadManagerImpl::ShouldAutomaticallyOpenFileByPolicy(
    const GURL& url,
    const base::FilePath& path) {
  if (!delegate_)
    return false;

  return delegate_->ShouldAutomaticallyOpenFileByPolicy(url, path);
}

bool DownloadManagerImpl::ShouldOpenDownload(
    download::DownloadItemImpl* item,
    ShouldOpenDownloadCallback callback) {
  if (!delegate_)
    return true;

  // Relies on DownloadItemImplDelegate::ShouldOpenDownloadCallback and
  // DownloadManagerDelegate::DownloadOpenDelayedCallback "just happening"
  // to have the same type :-}.
  return delegate_->ShouldOpenDownload(item, std::move(callback));
}

void DownloadManagerImpl::SetDelegate(DownloadManagerDelegate* delegate) {
  delegate_ = delegate;
}

DownloadManagerDelegate* DownloadManagerImpl::GetDelegate() {
  return delegate_;
}

void DownloadManagerImpl::Shutdown() {
  DVLOG(20) << __func__ << "() shutdown_needed_ = " << shutdown_needed_;
  if (!shutdown_needed_)
    return;
  shutdown_needed_ = false;

  for (auto& observer : observers_)
    observer.ManagerGoingDown(this);
  // TODO(benjhayden): Consider clearing observers_.

  // If there are in-progress downloads, cancel them. This also goes for
  // dangerous downloads which will remain in history if they aren't explicitly
  // accepted or discarded. Canceling will remove the intermediate download
  // file.
  for (const auto& it : downloads_by_guid_) {
    download::DownloadItemImpl* download = it.second;
    if (download->GetState() == download::DownloadItem::IN_PROGRESS) {
      download->Cancel(false);
      if (delegate_) {
        delegate_->OnDownloadCanceledAtShutdown(download);
      }
    }
  }

  downloads_by_guid_.clear();
  downloads_.clear();

  // We'll have nothing more to report to the observers after this point.
  observers_.Clear();

  if (in_progress_manager_)
    in_progress_manager_->ShutDown();

  if (delegate_)
    delegate_->Shutdown();
  delegate_ = nullptr;
}

bool DownloadManagerImpl::InterceptDownload(
    const download::DownloadCreateInfo& info) {
  WebContents* web_contents = WebContentsImpl::FromRenderFrameHostID(
      info.render_process_id, info.render_frame_id);
  if (info.is_new_download &&
      info.result ==
          download::DOWNLOAD_INTERRUPT_REASON_SERVER_CROSS_ORIGIN_REDIRECT) {
    std::vector<GURL> url_chain(info.url_chain);
    GURL url = url_chain.back();
    if ((url.SchemeIsHTTPOrHTTPS() ||
         GetContentClient()->browser()->IsHandledURL(url)) &&
        web_contents) {
      url_chain.pop_back();
      NavigationController::LoadURLParams params(url);
      params.has_user_gesture = info.has_user_gesture;
      params.referrer = Referrer(
          info.referrer_url,
          blink::ReferrerUtils::NetToMojoReferrerPolicy(info.referrer_policy));
      params.redirect_chain = url_chain;
      params.frame_tree_node_id =
          RenderFrameHost::GetFrameTreeNodeIdForRoutingId(
              info.render_process_id, info.render_frame_id);
      params.from_download_cross_origin_redirect = true;
      params.initiator_origin = info.request_initiator;
      params.is_renderer_initiated = info.is_content_initiated;

      // Ensure the method is called from an active document.
      // If inactive documents start download, it can be a security risk.
      // Call ReceiveBadMessage to terminate such a renderer.
      // TODO(crbug.com/40201479): confirm if fenced frames are allowed to start
      // downloads.
      if (!RenderFrameHost::FromID(info.render_process_id, info.render_frame_id)
               ->IsActive()) {
        bad_message::ReceivedBadMessage(
            info.render_process_id,
            bad_message::RFH_INTERECEPT_DOWNLOAD_WHILE_INACTIVE);
        return false;
      }

      web_contents->GetController().LoadURLWithParams(params);
    }
    return true;
  }

  std::string user_agent;
  for (const auto& header : info.request_headers) {
    if (header.first == net::HttpRequestHeaders::kUserAgent) {
      user_agent = header.second;
      break;
    }
  }

  if (delegate_ &&
      delegate_->InterceptDownloadIfApplicable(
          info.url(), user_agent, info.content_disposition, info.mime_type,
          info.request_origin, info.total_bytes, info.transient,
          info.is_content_initiated, web_contents)) {
    DropDownload();
    return true;
  }
  return false;
}

base::FilePath DownloadManagerImpl::GetDefaultDownloadDirectory() {
  base::FilePath default_download_directory;

  if (delegate_ && default_download_directory.empty()) {
    base::FilePath website_save_directory;  // Unused
    delegate_->GetSaveDir(GetBrowserContext(), &website_save_directory,
                          &default_download_directory);
  }

  if (default_download_directory.empty()) {
    // |default_download_directory| can still be empty if ContentBrowserClient
    // returned an empty path for the downloads directory.
    default_download_directory =
        GetContentClient()->browser()->GetDefaultDownloadDirectory();
  }

  return default_download_directory;
}

void DownloadManagerImpl::OnDownloadsInitialized() {
  in_progress_downloads_ = in_progress_manager_->TakeInProgressDownloads();
  uint32_t max_id = download::DownloadItem::kInvalidId;
  for (auto it = in_progress_downloads_.begin();
       it != in_progress_downloads_.end();) {
    download::DownloadItemImpl* download = it->get();
    uint32_t id = download->GetId();
    if (id > max_id)
      max_id = id;

    // Clean up cancelled and non resumable interrupted downloads.
    if (ShouldClearDownloadFromDB(download->GetURL(), download->GetState(),
                                  download->GetLastReason(),
                                  download->GetStartTime())) {
      cleared_download_guids_on_startup_.insert(download->GetGuid());
      DeleteDownloadedFileOnUIThread(download->GetFullPath());
      it = in_progress_downloads_.erase(it);
      continue;
    }
    ++it;
  }
  PostInitialization(DOWNLOAD_INITIALIZATION_DEPENDENCY_IN_PROGRESS_CACHE);
  SetNextId(max_id + 1);
}

void DownloadManagerImpl::StartDownloadItem(
    std::unique_ptr<download::DownloadCreateInfo> info,
    download::DownloadUrlParameters::OnStartedCallback on_started,
    download::InProgressDownloadManager::StartDownloadItemCallback callback) {
  DCHECK_CURRENTLY_ON(BrowserThread::UI);

  download::DownloadItemImpl* download =
      static_cast<download::DownloadItemImpl*>(GetDownloadByGuid(info->guid));
  if (!info->is_new_download) {
    if (!download || download->GetState() == download::DownloadItem::CANCELLED)
      download = nullptr;
    std::move(callback).Run(std::move(info), download, base::FilePath(),
                            should_persist_new_download_);
    OnDownloadStarted(download, std::move(on_started));
  } else {
    // If the download already in system, it can only be resumed.
    if (!info->guid.empty() && download) {
      LOG(WARNING) << "A download with the same GUID already exists, the new "
                      "request is ignored.";
      return;
    }
    GetNextId(base::BindOnce(&DownloadManagerImpl::OnNewDownloadIdRetrieved,
                             weak_factory_.GetWeakPtr(), std::move(info),
                             std::move(on_started), std::move(callback)));
  }
}

void DownloadManagerImpl::OnNewDownloadIdRetrieved(
    std::unique_ptr<download::DownloadCreateInfo> info,
    download::DownloadUrlParameters::OnStartedCallback on_started,
    download::InProgressDownloadManager::StartDownloadItemCallback callback,
    uint32_t id) {
#if BUILDFLAG(IS_ANDROID)
  if (info->transient && info->allow_auto_open_after_completion &&
      delegate_->ShouldOpenPdfInline() &&
      base::EqualsCaseInsensitiveASCII(info->mime_type, kPdfMimeType)) {
    if (IsOffTheRecord()) {
      info->save_info->use_in_memory_file = true;
    } else {
      for (const auto& iter : downloads_by_guid_) {
        download::DownloadItem* item = iter.second;
        if (item->GetFileExternallyRemoved() ||
            item->GetState() != download::DownloadItem::COMPLETE) {
          continue;
        }

        if (item->GetMimeType() != kPdfMimeType ||
            item->GetUrlChain() != info->url_chain) {
          continue;
        }

        if (!item->IsTransient() || !item->AllowAutoOpenAfterCompletion()) {
          continue;
        }

        disk_access_task_runner_->PostTaskAndReplyWithResult(
            FROM_HERE,
            base::BindOnce(&base::PathExists, item->GetTargetFilePath()),
            base::BindOnce(&DownloadManagerImpl::CreateNewDownloadItemToStart,
                           weak_factory_.GetWeakPtr(), std::move(info),
                           std::move(on_started), std::move(callback), id,
                           item->GetTargetFilePath()));
        return;
      }
    }
  }
#endif  // BUILDFLAG(IS_ANDROID)
  CreateNewDownloadItemToStart(std::move(info), std::move(on_started),
                               std::move(callback), id, base::FilePath(),
                               false);
}

void DownloadManagerImpl::CreateNewDownloadItemToStart(
    std::unique_ptr<download::DownloadCreateInfo> info,
    download::DownloadUrlParameters::OnStartedCallback on_started,
    download::InProgressDownloadManager::StartDownloadItemCallback callback,
    uint32_t id,
    const base::FilePath& duplicate_download_file_path,
    bool duplicate_file_exists) {
  DCHECK_CURRENTLY_ON(BrowserThread::UI);

  download::DownloadItemImpl* download = CreateActiveItem(id, *info);
  if (delegate_ && info->save_info) {
    info->save_info->needs_obfuscation =
        delegate_->ShouldObfuscateDownload(download);
    info->save_info->total_bytes = info->total_bytes;
  }
  content::devtools_instrumentation::WillBeginDownload(info.get(), download);
  // Check if the download is a duplicate. Only GET download URL that has
  // existed are considered duplicate.
  bool is_duplicate = duplicate_file_exists && (info->method == "GET");
  if (is_duplicate) {
    bool found_same_url = false;
    // If there is another download with the same path, the download is
    // not a duplicate.
    for (auto it = downloads_.begin(); it != downloads_.end(); ++it) {
      if (it->second->GetTargetFilePath() == duplicate_download_file_path) {
        if (it->second->GetURL() != info->url()) {
          is_duplicate = false;
          break;
        } else {
          found_same_url = true;
        }
      }
    }
    is_duplicate = is_duplicate && found_same_url;
  }
  std::move(callback).Run(
      std::move(info), download,
      is_duplicate ? duplicate_download_file_path : base::FilePath(),
      should_persist_new_download_);
  if (download) {
    // For new downloads, we notify here, rather than earlier, so that
    // the download_file is bound to download and all the usual
    // setters (e.g. Cancel) work.
    for (auto& observer : observers_)
      observer.OnDownloadCreated(this, download);
    OnNewDownloadCreated(download);
  }

  OnDownloadStarted(download, std::move(on_started));
}

void DownloadManagerImpl::BindWakeLockProvider(
    mojo::PendingReceiver<device::mojom::WakeLockProvider> receiver) {
  DCHECK_CURRENTLY_ON(BrowserThread::UI);
  GetDeviceService().BindWakeLockProvider(std::move(receiver));
}

download::QuarantineConnectionCallback
DownloadManagerImpl::GetQuarantineConnectionCallback() {
  if (!delegate_)
    return base::NullCallback();

  return delegate_->GetQuarantineConnectionCallback();
}

std::unique_ptr<download::DownloadItemRenameHandler>
DownloadManagerImpl::GetRenameHandlerForDownload(
    download::DownloadItemImpl* download_item) {
  if (!delegate_) {
    return nullptr;
  }

  return delegate_->GetRenameHandlerForDownload(download_item);
}

void DownloadManagerImpl::StartDownload(
    std::unique_ptr<download::DownloadCreateInfo> info,
    std::unique_ptr<download::InputStream> stream,
    download::DownloadUrlParameters::OnStartedCallback on_started) {
  DCHECK_CURRENTLY_ON(BrowserThread::UI);
  DCHECK(info);
  in_progress_manager_->StartDownload(
      std::move(info), std::move(stream),
      download::URLLoaderFactoryProvider::GetNullPtr(), base::DoNothing(),
      std::move(on_started));
}

void DownloadManagerImpl::CheckForHistoryFilesRemoval() {
  DCHECK_CURRENTLY_ON(BrowserThread::UI);
  for (const auto& it : downloads_by_guid_) {
    CheckForFileRemoval(it.second);
  }
}

void DownloadManagerImpl::OnHistoryQueryComplete(
    base::OnceClosure load_history_downloads_cb) {
  DCHECK_CURRENTLY_ON(BrowserThread::UI);
  if (!in_progress_cache_initialized_)
    load_history_downloads_cb_ = std::move(load_history_downloads_cb);
  else
    std::move(load_history_downloads_cb).Run();
}

void DownloadManagerImpl::CheckForFileRemoval(
    download::DownloadItemImpl* download_item) {
  DCHECK_CURRENTLY_ON(BrowserThread::UI);
  if ((download_item->GetState() != download::DownloadItem::COMPLETE) ||
      download_item->GetFileExternallyRemoved()) {
    return;
  }

  // Check whether an task is already queued or running for the current download
  // and skip this check if it is the case.
  if (!pending_disk_access_query_.insert(download_item->GetGuid()).second)
    return;

  disk_access_task_runner_->PostTaskAndReplyWithResult(
      FROM_HERE,
      base::BindOnce(&base::PathExists, download_item->GetTargetFilePath()),
      base::BindOnce(&DownloadManagerImpl::OnFileExistenceChecked,
                     weak_factory_.GetWeakPtr(), download_item->GetGuid()));
}

void DownloadManagerImpl::OnFileExistenceChecked(const std::string& guid,
                                                 bool result) {
  DCHECK_CURRENTLY_ON(BrowserThread::UI);

  // Remove the pending check flag for this download to allow new requests.
  pending_disk_access_query_.erase(guid);

  if (!result) {  // File does not exist.
    auto it = downloads_by_guid_.find(guid);
    if (it != downloads_by_guid_.end())
      it->second->OnDownloadedFileRemoved();
  }
}

std::string DownloadManagerImpl::GetApplicationClientIdForFileScanning() const {
  if (delegate_)
    return delegate_->ApplicationClientIdForFileScanning();
  return std::string();
}

BrowserContext* DownloadManagerImpl::GetBrowserContext() {
  return browser_context_;
}

void DownloadManagerImpl::CreateSavePackageDownloadItem(
    const base::FilePath& main_file_path,
    const base::FilePath& main_file_display_name,
    const GURL& page_url,
    const std::string& mime_type,
    int render_process_id,
    int render_frame_id,
    download::DownloadJob::CancelRequestCallback cancel_request_callback,
    DownloadItemImplCreated item_created) {
  DCHECK_CURRENTLY_ON(BrowserThread::UI);
  GetNextId(base::BindOnce(
      &DownloadManagerImpl::CreateSavePackageDownloadItemWithId,
      weak_factory_.GetWeakPtr(), main_file_path, main_file_display_name,
      page_url, mime_type, render_process_id, render_frame_id,
      std::move(cancel_request_callback), std::move(item_created)));
}

void DownloadManagerImpl::CreateSavePackageDownloadItemWithId(
    const base::FilePath& main_file_path,
    const base::FilePath& main_file_display_name,
    const GURL& page_url,
    const std::string& mime_type,
    int render_process_id,
    int render_frame_id,
    download::DownloadJob::CancelRequestCallback cancel_request_callback,
    DownloadItemImplCreated item_created,
    uint32_t id) {
  DCHECK_CURRENTLY_ON(BrowserThread::UI);
  DCHECK_NE(download::DownloadItem::kInvalidId, id);
  DCHECK(!downloads_.contains(id));

  download::DownloadItemImpl* download_item = item_factory_->CreateSavePageItem(
      this, id, main_file_path, main_file_display_name, page_url, mime_type,
      std::move(cancel_request_callback));

  GlobalRenderFrameHostId global_id(render_process_id, render_frame_id);
  DownloadItemUtils::AttachInfo(
      download_item, GetBrowserContext(),
      WebContentsImpl::FromRenderFrameHostID(global_id), global_id);
  if (delegate_) {
    delegate_->AttachExtraInfo(download_item);
  }

  OnDownloadCreated(base::WrapUnique(download_item));
  if (!item_created.is_null())
    std::move(item_created).Run(download_item);
}

// Resume a download of a specific URL. We send the request to the
// ResourceDispatcherHost, and let it send us responses like a regular
// download.
void DownloadManagerImpl::ResumeInterruptedDownload(
    std::unique_ptr<download::DownloadUrlParameters> params,
    const std::string& serialized_embedder_download_data) {
  BeginDownloadInternal(std::move(params),
                        nullptr /* blob_url_loader_factory */, false,
                        serialized_embedder_download_data);
}

void DownloadManagerImpl::SetDownloadItemFactoryForTesting(
    std::unique_ptr<download::DownloadItemFactory> item_factory) {
  item_factory_ = std::move(item_factory);
}

void DownloadManagerImpl::SetDownloadFileFactoryForTesting(
    std::unique_ptr<download::DownloadFileFactory> file_factory) {
  in_progress_manager_->set_file_factory(std::move(file_factory));
}

download::DownloadFileFactory*
DownloadManagerImpl::GetDownloadFileFactoryForTesting() {
  return in_progress_manager_->file_factory();
}

void DownloadManagerImpl::DownloadRemoved(
    download::DownloadItemImpl* download) {
  if (!download)
    return;

  downloads_by_guid_.erase(download->GetGuid());
  downloads_.erase(download->GetId());
}

void DownloadManagerImpl::DownloadInterrupted(
    download::DownloadItemImpl* download) {
  WebContents* web_contents = DownloadItemUtils::GetWebContents(download);
  if (!web_contents) {
    download::RecordDownloadCountWithSource(
        download::INTERRUPTED_WITHOUT_WEBCONTENTS,
        download->GetDownloadSource());
  }
}

bool DownloadManagerImpl::IsOffTheRecord() const {
  return browser_context_->IsOffTheRecord();
}

void DownloadManagerImpl::ReportBytesWasted(
    download::DownloadItemImpl* download) {
  in_progress_manager_->ReportBytesWasted(download);
}

void DownloadManagerImpl::InterceptNavigation(
    std::unique_ptr<network::ResourceRequest> resource_request,
    std::vector<GURL> url_chain,
    network::mojom::URLResponseHeadPtr response_head,
    mojo::ScopedDataPipeConsumerHandle response_body,
    network::mojom::URLLoaderClientEndpointsPtr url_loader_client_endpoints,
    net::CertStatus cert_status,
    FrameTreeNodeId frame_tree_node_id,
    bool from_download_cross_origin_redirect) {
  DCHECK_CURRENTLY_ON(BrowserThread::UI);
  if (!delegate_) {
    DropDownload();
    return;
  }

  const GURL& url = resource_request->url;
  const std::string& method = resource_request->method;
  std::optional<url::Origin> request_initiator =
      resource_request->request_initiator;
  std::string mime_type = response_head->mime_type;
  ui::PageTransition transition_type =
      static_cast<ui::PageTransition>(resource_request->transition_type);

  WebContents::Getter web_contents_getter =
      base::BindRepeating(WebContents::FromFrameTreeNodeId, frame_tree_node_id);

  base::OnceCallback<void(bool /* download allowed */)>
      on_download_checks_done = base::BindOnce(
          &DownloadManagerImpl::InterceptNavigationOnChecksComplete,
          weak_factory_.GetWeakPtr(), frame_tree_node_id,
          std::move(resource_request), std::move(url_chain), cert_status,
          std::move(response_head), std::move(response_body),
          std::move(url_loader_client_endpoints));

  delegate_->CheckDownloadAllowed(
      std::move(web_contents_getter), url, method, std::move(request_initiator),
      from_download_cross_origin_redirect, false /*content_initiated*/,
      mime_type, transition_type, std::move(on_download_checks_done));
}

void DownloadManagerImpl::RemoveDownloadsByURLAndTime(
    const base::RepeatingCallback<bool(const GURL&)>& url_filter,
    base::Time remove_begin,
    base::Time remove_end,
    base::OnceClosure callback) {
  DCHECK_CURRENTLY_ON(BrowserThread::UI);

  if (!IsManagerInitialized()) {
    on_initialized_callbacks_.push_back(
        base::BindOnce(&DownloadManagerImpl::RemoveDownloadsByURLAndTime,
                       weak_factory_.GetWeakPtr(), url_filter, remove_begin,
                       remove_end, std::move(callback)));
    return;
  }

  auto it = downloads_by_guid_.begin();
  while (it != downloads_by_guid_.end()) {
    download::DownloadItemImpl* download = it->second;

    // Increment done here to protect against invalidation below.
    ++it;

    if (download->GetState() != download::DownloadItem::IN_PROGRESS &&
        url_filter.Run(download->GetURL()) &&
        download->GetStartTime() >= remove_begin &&
        (remove_end.is_null() || download->GetStartTime() < remove_end)) {
      download->Remove();
    }
  }

  if (callback) {
    std::move(callback).Run();
  }
}

bool DownloadManagerImpl::CanDownload(
    download::DownloadUrlParameters* parameters) {
  return true;
}

void DownloadManagerImpl::DownloadUrl(
    std::unique_ptr<download::DownloadUrlParameters> params) {
  DownloadUrl(std::move(params), nullptr /* blob_url_loader_factory */);
}

void DownloadManagerImpl::DownloadUrl(
    std::unique_ptr<download::DownloadUrlParameters> params,
    scoped_refptr<network::SharedURLLoaderFactory> blob_url_loader_factory) {
  if (params->post_id() >= 0) {
    // Check this here so that the traceback is more useful.
    DCHECK(params->prefer_cache());
    DCHECK_EQ("POST", params->method());
  }

  if (delegate_)
    delegate_->SanitizeDownloadParameters(params.get());

  download::RecordDownloadCountWithSource(
      download::DownloadCountTypes::DOWNLOAD_TRIGGERED_COUNT,
      params->download_source());
  BeginDownloadInternal(std::move(params), std::move(blob_url_loader_factory),
                        /*is_new_download=*/true,
                        /*serialized_embedder_download_data=*/std::string());
}

void DownloadManagerImpl::AddObserver(Observer* observer) {
  observers_.AddObserver(observer);
}

void DownloadManagerImpl::RemoveObserver(Observer* observer) {
  observers_.RemoveObserver(observer);
}

download::DownloadItem* DownloadManagerImpl::CreateDownloadItem(
    const std::string& guid,
    uint32_t id,
    const base::FilePath& current_path,
    const base::FilePath& target_path,
    const std::vector<GURL>& url_chain,
    const GURL& referrer_url,
    const StoragePartitionConfig& storage_partition_config,
    const GURL& tab_url,
    const GURL& tab_refererr_url,
    const std::optional<url::Origin>& request_initiator,
    const std::string& mime_type,
    const std::string& original_mime_type,
    base::Time start_time,
    base::Time end_time,
    const std::string& etag,
    const std::string& last_modified,
    int64_t received_bytes,
    int64_t total_bytes,
    const std::string& hash,
    download::DownloadItem::DownloadState state,
    download::DownloadDangerType danger_type,
    download::DownloadInterruptReason interrupt_reason,
    bool opened,
    base::Time last_access_time,
    bool transient,
    const std::vector<download::DownloadItem::ReceivedSlice>& received_slices) {
  // Retrieve the in-progress download if it exists. Notice that this also
  // removes it from |in_progress_downloads_|.
  auto in_progress_download = RetrieveInProgressDownload(id);

  // Return null to clear cancelled or non-resumable download.
  if (cleared_download_guids_on_startup_.contains(guid)) {
    return nullptr;
  }

  if (url_chain.empty() ||
      ShouldClearDownloadFromDB(url_chain.back(), state, interrupt_reason,
                                start_time)) {
    DeleteDownloadedFileOnUIThread(current_path);
    return nullptr;
  }
  auto item = base::WrapUnique(item_factory_->CreatePersistedItem(
      this, guid, id, current_path, target_path, url_chain, referrer_url,
      StoragePartitionConfigToSerializedEmbedderDownloadData(
          storage_partition_config),
      tab_url, tab_refererr_url, request_initiator, mime_type,
      original_mime_type, start_time, end_time, etag, last_modified,
      received_bytes, total_bytes, hash, state, danger_type, interrupt_reason,
      opened, last_access_time, transient, received_slices));
  if (in_progress_download) {
    // If a download is in both history DB and in-progress DB, we should
    // be able to remove the in-progress entry if the following 2 conditions
    // are both met:
    // 1. The download state in the history DB is a terminal state.
    // 2. The download is not currently in progress.
    // The situation could happen when browser crashes when download just
    // reaches a terminal state. If the download is already in progress, we
    // should wait for it to complete so that both DBs will be updated
    // afterwards.
    if (item->IsDone() && in_progress_download->GetState() !=
                              download::DownloadItem::IN_PROGRESS) {
      in_progress_manager_->RemoveInProgressDownload(guid);
    } else {
      // If one of the conditions are not met, use the in-progress download
      // entry.
      // TODO(qinmin): return nullptr so that the history DB will delete
      // the download.
      item = std::move(in_progress_download);
      item->SetDelegate(this);
    }
  }
#if BUILDFLAG(IS_ANDROID)
  if (target_path.IsContentUri()) {
    base::FilePath android_display_name =
        in_progress_manager_->GetDownloadDisplayName(target_path);
    if (!android_display_name.empty())
      item->SetDisplayName(android_display_name);
    else
      return nullptr;
  }
#endif
  download::DownloadItemImpl* download = item.get();
  DownloadItemUtils::AttachInfo(download, GetBrowserContext(), nullptr,
                                GlobalRenderFrameHostId());
  if (delegate_) {
    delegate_->AttachExtraInfo(download);
  }
  OnDownloadCreated(std::move(item));
  return download;
}

void DownloadManagerImpl::OnDownloadCreated(
    std::unique_ptr<download::DownloadItemImpl> download) {
  DCHECK(!downloads_.contains(download->GetId()));
  DCHECK(!downloads_by_guid_.contains(download->GetGuid()));
  download::DownloadItemImpl* item = download.get();
  downloads_[item->GetId()] = std::move(download);
  downloads_by_guid_[item->GetGuid()] = item;
  for (auto& observer : observers_)
    observer.OnDownloadCreated(this, item);
  OnNewDownloadCreated(item);
  DVLOG(20) << __func__ << "() download = " << item->DebugString(true);
}

void DownloadManagerImpl::PostInitialization(
    DownloadInitializationDependency dependency) {
  // If initialization has occurred (ie. in tests), skip post init steps.
  if (initialized_)
    return;

  switch (dependency) {
    case DOWNLOAD_INITIALIZATION_DEPENDENCY_HISTORY_DB:
      history_db_initialized_ = true;
      break;
    case DOWNLOAD_INITIALIZATION_DEPENDENCY_IN_PROGRESS_CACHE:
      in_progress_cache_initialized_ = true;
      // Post a task to load downloads from history db.
      if (load_history_downloads_cb_) {
        base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
            FROM_HERE, std::move(load_history_downloads_cb_));
      }
      break;
    case DOWNLOAD_INITIALIZATION_DEPENDENCY_NONE:
    default:
      NOTREACHED();
  }

  // Download manager is only initialized if both history db and in progress
  // cache are initialized.
  bool history_loaded = history_db_initialized_ || IsOffTheRecord() ||
                        !delegate_ || !delegate_->SupportsHistoryLoading();
  if (!history_loaded || !in_progress_cache_initialized_)
    return;

  for (const auto& guid : cleared_download_guids_on_startup_) {
    in_progress_manager_->RemoveInProgressDownload(guid);
  }

  if (in_progress_downloads_.empty()) {
    OnDownloadManagerInitialized();
  } else {
    GetNextId(base::BindOnce(&DownloadManagerImpl::ImportInProgressDownloads,
                             weak_factory_.GetWeakPtr()));
  }
}

void DownloadManagerImpl::ImportInProgressDownloads(uint32_t id) {
  auto download = in_progress_downloads_.begin();
  while (download != in_progress_downloads_.end()) {
    auto item = std::move(*download);
    // If the in-progress download doesn't have an ID, generate new IDs for it.
    if (item->GetId() == download::DownloadItem::kInvalidId) {
      item->SetDownloadId(id++);
      next_download_id_++;
      if (!should_persist_new_download_)
        in_progress_manager_->RemoveInProgressDownload(item->GetGuid());
    }
    item->SetDelegate(this);
    DownloadItemUtils::AttachInfo(item.get(), GetBrowserContext(), nullptr,
                                  GlobalRenderFrameHostId());
    if (delegate_) {
      delegate_->AttachExtraInfo(item.get());
    }
    download = in_progress_downloads_.erase(download);
    OnDownloadCreated(std::move(item));
  }
  OnDownloadManagerInitialized();
}

void DownloadManagerImpl::OnDownloadManagerInitialized() {
  OnInitialized();
  in_progress_manager_->OnAllInprogressDownloadsLoaded();
  for (auto& observer : observers_)
    observer.OnManagerInitialized();

  std::vector<base::OnceClosure> callbacks =
      std::move(on_initialized_callbacks_);
  for (auto& callback : callbacks) {
    std::move(callback).Run();
  }
}

bool DownloadManagerImpl::IsManagerInitialized() {
  return initialized_;
}

int DownloadManagerImpl::InProgressCount() {
  int count = 0;
  for (const auto& it : downloads_by_guid_) {
    if (it.second->GetState() == download::DownloadItem::IN_PROGRESS) {
      ++count;
    }
  }
  return count;
}

int DownloadManagerImpl::BlockingShutdownCount() {
  int count = 0;
  for (const auto& it : downloads_by_guid_) {
    download::DownloadItemImpl* download = it.second;
    if (download->IsTransient()) {
      continue;
    }
    if (download->GetState() == download::DownloadItem::IN_PROGRESS &&
        !download->IsDangerous() && !download->IsInsecure()) {
      ++count;
    }
  }
  return count;
}

download::DownloadItem* DownloadManagerImpl::GetDownload(uint32_t download_id) {
  auto it = downloads_.find(download_id);
  return it != downloads_.end() ? it->second.get() : nullptr;
}

download::DownloadItem* DownloadManagerImpl::GetDownloadByGuid(
    const std::string& guid) {
  if (!in_progress_downloads_.empty()) {
    for (const auto& it : in_progress_downloads_) {
      if (it->GetGuid() == guid)
        return it.get();
    }
  }
  auto it = downloads_by_guid_.find(guid);
  return it != downloads_by_guid_.end() ? it->second : nullptr;
}

void DownloadManagerImpl::GetAllDownloads(
    download::SimpleDownloadManager::DownloadVector* downloads) {
  for (const auto& it : downloads_by_guid_) {
    downloads->push_back(it.second);
  }
}

void DownloadManagerImpl::GetUninitializedActiveDownloadsIfAny(
    download::SimpleDownloadManager::DownloadVector* downloads) {
  for (const auto& it : in_progress_downloads_)
    downloads->push_back(it.get());
}

void DownloadManagerImpl::OpenDownload(download::DownloadItemImpl* download) {
  if (delegate_)
    delegate_->OpenDownload(download);
}

void DownloadManagerImpl::ShowDownloadInShell(
    download::DownloadItemImpl* download) {
  if (delegate_)
    delegate_->ShowDownloadInShell(download);
}

void DownloadManagerImpl::DropDownload() {
  download::RecordDownloadCount(download::DOWNLOAD_DROPPED_COUNT);
  for (auto& observer : observers_)
    observer.OnDownloadDropped(this);
}

void DownloadManagerImpl::InterceptNavigationOnChecksComplete(
    FrameTreeNodeId frame_tree_node_id,
    std::unique_ptr<network::ResourceRequest> resource_request,
    std::vector<GURL> url_chain,
    net::CertStatus cert_status,
    network::mojom::URLResponseHeadPtr response_head,
    mojo::ScopedDataPipeConsumerHandle response_body,
    network::mojom::URLLoaderClientEndpointsPtr url_loader_client_endpoints,
    bool is_download_allowed) {
  if (!is_download_allowed) {
    DropDownload();
    return;
  }

  int render_process_id = -1;
  int render_frame_id = -1;
  GURL site_url, tab_url, tab_referrer_url;
  RenderFrameHost* render_frame_host = nullptr;
  auto storage_partition_config =
      StoragePartitionConfig::CreateDefault(browser_context_);
  auto* ftn = FrameTreeNode::GloballyFindByID(frame_tree_node_id);
  if (ftn) {
    render_frame_host = ftn->current_frame_host();
    if (render_frame_host) {
      render_process_id = render_frame_host->GetProcess()->GetDeprecatedID();
      render_frame_id = render_frame_host->GetRoutingID();
      storage_partition_config = render_frame_host->GetSiteInstance()
                                     ->GetSecurityPrincipal()
                                     .GetStoragePartitionConfig();
    }
    auto* web_contents = WebContentsImpl::FromFrameTreeNode(ftn);
    DCHECK(web_contents);
    NavigationEntry* entry = web_contents->GetController().GetVisibleEntry();
    if (entry) {
      tab_url = entry->GetURL();
      tab_referrer_url = entry->GetReferrer().url;
    }
    RenderFrameHost* opener = web_contents->GetOpener();
    if (opener) {
      RecordDownloadOpenerType(render_frame_host, opener);
    }
  }

  bool is_transient = false;
#if BUILDFLAG(IS_ANDROID)
  if (!download::IsContentDispositionAttachmentInHead(*response_head)) {
    is_transient = delegate_->ShouldOpenPdfInline() &&
                   base::EqualsCaseInsensitiveASCII(response_head->mime_type,
                                                    kPdfMimeType);
  }
#endif  // BUILDFLAG(IS_ANDROID)

  StoragePartitionImpl* storage_partition =
      GetStoragePartitionForConfig(browser_context_, storage_partition_config);
  in_progress_manager_->InterceptDownloadFromNavigation(
      std::move(resource_request), render_process_id, render_frame_id,
      StoragePartitionConfigToSerializedEmbedderDownloadData(
          storage_partition_config),
      tab_url, tab_referrer_url, std::move(url_chain), std::move(cert_status),
      std::move(response_head), std::move(response_body),
      std::move(url_loader_client_endpoints),
      CreatePendingSharedURLLoaderFactory(storage_partition, render_frame_host),
      is_transient);
}

void DownloadManagerImpl::BeginResourceDownloadOnChecksComplete(
    std::unique_ptr<download::DownloadUrlParameters> params,
    scoped_refptr<network::SharedURLLoaderFactory> blob_url_loader_factory,
    bool is_new_download,
    const StoragePartitionConfig& storage_partition_config,
    bool is_download_allowed) {
  if (!is_download_allowed) {
    DropDownload();
    return;
  }

  GURL tab_url, tab_referrer_url;
  auto* rfh = RenderFrameHost::FromID(params->render_process_host_id(),
                                      params->render_frame_host_routing_id());
  if (rfh) {
    auto* web_contents = WebContents::FromRenderFrameHost(rfh);
    NavigationEntry* entry = web_contents->GetController().GetVisibleEntry();
    if (entry) {
      tab_url = entry->GetURL();
      tab_referrer_url = entry->GetReferrer().url;
    }
  }

  DCHECK_EQ(params->url().SchemeIsBlob(), bool{blob_url_loader_factory});
  std::unique_ptr<network::PendingSharedURLLoaderFactory>
      pending_url_loader_factory;
  if (params->url_loader_factory()) {
    pending_url_loader_factory = params->take_url_loader_factory();
  } else if (blob_url_loader_factory) {
    DCHECK(params->url().SchemeIsBlob());
    pending_url_loader_factory = blob_url_loader_factory->Clone();
  } else if (params->url().SchemeIsFile()) {
    pending_url_loader_factory =
        std::make_unique<network::WrapperPendingSharedURLLoaderFactory>(
            FileURLLoaderFactory::Create(
                browser_context_->GetPath(),
                browser_context_->GetSharedCorsOriginAccessList(),
                // USER_VISIBLE because download should progress
                // even when there is high priority work to do.
                base::TaskPriority::USER_VISIBLE));
  } else if (rfh && params->url().SchemeIs(content::kChromeUIScheme)) {
    pending_url_loader_factory =
        std::make_unique<network::WrapperPendingSharedURLLoaderFactory>(
            CreateWebUIURLLoaderFactory(rfh, params->url().GetScheme(),
                                        base::flat_set<std::string>()));
  } else if (rfh && params->url().SchemeIsFileSystem()) {
    StoragePartitionImpl* storage_partition = GetStoragePartitionForConfig(
        browser_context_, storage_partition_config);
    pending_url_loader_factory =
        std::make_unique<network::WrapperPendingSharedURLLoaderFactory>(
            CreateFileSystemURLLoaderFactory(
                rfh->GetProcess()->GetDeprecatedID(), rfh->GetFrameTreeNodeId(),
                storage_partition->GetFileSystemContext(),
                storage_partition->GetPartitionDomain(),
                static_cast<RenderFrameHostImpl*>(rfh)->GetStorageKey()));
  } else if (params->url().SchemeIs(url::kDataScheme)) {
    pending_url_loader_factory =
        std::make_unique<network::WrapperPendingSharedURLLoaderFactory>(
            DataURLLoaderFactory::CreateForOneSpecificUrl(params->url()));
  } else if (rfh && !network::IsURLHandledByNetworkService(params->url())) {
    ContentBrowserClient::NonNetworkURLLoaderFactoryMap
        non_network_url_loader_factories;
    GetContentClient()
        ->browser()
        ->RegisterNonNetworkSubresourceURLLoaderFactories(
            params->render_process_host_id(),
            params->render_frame_host_routing_id(), params->initiator(),
            &non_network_url_loader_factories);
    auto it = non_network_url_loader_factories.find(params->url().GetScheme());
    if (it != non_network_url_loader_factories.end()) {
      pending_url_loader_factory =
          std::make_unique<network::WrapperPendingSharedURLLoaderFactory>(
              std::move(it->second));
    } else {
      DLOG(ERROR) << "No URLLoaderFactory found to download " << params->url();
      return;
    }
  } else {
    // Resumes of network-fetched downloads opt out via
    // skip_service_worker_interception() so Range/If-Range continuation
    // isn't intercepted by a SW that would return an unrelated full body.
    if (!params->skip_service_worker_interception() &&
        base::FeatureList::IsEnabled(
            features::kServiceWorkerInterceptDownloads) &&
        params->url().SchemeIsHTTPOrHTTPS()) {
      StoragePartitionImpl* storage_partition = GetStoragePartitionForConfig(
          browser_context_, storage_partition_config);
      auto* sw_context = static_cast<ServiceWorkerContextWrapper*>(
          storage_partition->GetServiceWorkerContext());
      if (sw_context && sw_context->context()) {
        std::unique_ptr<network::ResourceRequest> resource_request =
            download::CreateResourceRequest(params.get());
        // Use kEmpty destination per Fetch spec for explicit downloads. The
        // service worker's FetchEvent will have request.destination === "".
        resource_request->destination =
            network::mojom::RequestDestination::kEmpty;

        auto handle = std::make_unique<ServiceWorkerMainResourceHandle>(
            sw_context, base::DoNothing(),
            /*fetch_event_client_id=*/std::string());

        auto interceptor =
            ServiceWorkerMainResourceLoaderInterceptor::CreateForDownload(
                *resource_request, handle->AsWeakPtr());

        if (interceptor) {
          // Fallback factory bridged to the IO thread; see
          // DeferredIOThreadURLLoaderFactory.
          scoped_refptr<network::SharedURLLoaderFactory> fallback_factory =
              base::MakeRefCounted<DeferredIOThreadURLLoaderFactory>(
                  CreatePendingSharedURLLoaderFactory(storage_partition, rfh));
          // Explicit copy: C++ does not order argument evaluation, so we
          // cannot mix copy and std::move of the same scoped_refptr in one
          // call.
          scoped_refptr<network::SharedURLLoaderFactory>
              fallback_callback_factory = fallback_factory;

          auto* interceptor_ptr = interceptor.get();
          interceptor_ptr->MaybeCreateLoader(
              *resource_request, browser_context_,
              base::BindOnce(
                  [](base::WeakPtr<DownloadManagerImpl> manager,
                     std::unique_ptr<download::DownloadUrlParameters> params,
                     bool is_new_download,
                     StoragePartitionConfig storage_partition_config,
                     GURL tab_url, GURL tab_referrer_url,
                     std::unique_ptr<ServiceWorkerMainResourceLoaderInterceptor>
                         interceptor,
                     std::unique_ptr<ServiceWorkerMainResourceHandle> handle,
                     scoped_refptr<network::SharedURLLoaderFactory>
                         fallback_factory,
                     std::optional<NavigationLoaderInterceptor::Result>
                         result) {
                    if (!manager) {
                      return;
                    }
                    scoped_refptr<network::SharedURLLoaderFactory> sw_factory;
                    if (result.has_value() && result->single_request_factory) {
                      // Wrap the SW factory so the interceptor and handle
                      // outlive the async URLLoader invocation. See
                      // ServiceWorkerDownloadURLLoaderFactory's class-level
                      // comment for the ownership chain. Owns the network
                      // factory referenced by the FallbackCallback.
                      sw_factory = base::MakeRefCounted<
                          ServiceWorkerDownloadURLLoaderFactory>(
                          std::move(result->single_request_factory),
                          std::move(fallback_factory), std::move(interceptor),
                          std::move(handle));
                    }
                    manager
                        ->ContinueResourceDownloadAfterServiceWorkerIntercept(
                            std::move(params), is_new_download,
                            storage_partition_config, tab_url, tab_referrer_url,
                            std::move(sw_factory));
                  },
                  weak_factory_.GetWeakPtr(), std::move(params),
                  is_new_download, storage_partition_config, tab_url,
                  tab_referrer_url, std::move(interceptor), std::move(handle),
                  std::move(fallback_factory)),
              base::BindOnce(
                  [](scoped_refptr<network::SharedURLLoaderFactory> factory,
                     ResponseHeadUpdateParams)
                      -> network::mojom::URLLoaderFactory* {
                    return factory.get();
                  },
                  std::move(fallback_callback_factory)));
          return;
        }
      }
    }

    StoragePartitionImpl* storage_partition = GetStoragePartitionForConfig(
        browser_context_, storage_partition_config);
    pending_url_loader_factory =
        CreatePendingSharedURLLoaderFactory(storage_partition, rfh);
  }

  in_progress_manager_->BeginDownload(
      std::move(params), std::move(pending_url_loader_factory), is_new_download,
      StoragePartitionConfigToSerializedEmbedderDownloadData(
          storage_partition_config),
      tab_url, tab_referrer_url);
}

void DownloadManagerImpl::ContinueResourceDownloadAfterServiceWorkerIntercept(
    std::unique_ptr<download::DownloadUrlParameters> params,
    bool is_new_download,
    const StoragePartitionConfig& storage_partition_config,
    const GURL& tab_url,
    const GURL& tab_referrer_url,
    scoped_refptr<network::SharedURLLoaderFactory> sw_factory) {
  std::unique_ptr<network::PendingSharedURLLoaderFactory>
      pending_url_loader_factory;

  if (sw_factory) {
    // The service worker handled the request — wrap its factory in a
    // CrossThreadPendingSharedURLLoaderFactory so it can be used on the IO
    // thread where the download pipeline runs, while the SW URLLoader stays
    // on the UI thread.
    pending_url_loader_factory =
        std::make_unique<network::CrossThreadPendingSharedURLLoaderFactory>(
            std::move(sw_factory));
  } else {
    // No service worker interception — fall back to the network factory.
    StoragePartitionImpl* storage_partition = GetStoragePartitionForConfig(
        browser_context_, storage_partition_config);
    auto* rfh = RenderFrameHost::FromID(params->render_process_host_id(),
                                        params->render_frame_host_routing_id());
    pending_url_loader_factory =
        CreatePendingSharedURLLoaderFactory(storage_partition, rfh);
  }

  in_progress_manager_->BeginDownload(
      std::move(params), std::move(pending_url_loader_factory), is_new_download,
      StoragePartitionConfigToSerializedEmbedderDownloadData(
          storage_partition_config),
      tab_url, tab_referrer_url);
}

void DownloadManagerImpl::BeginDownloadInternal(
    std::unique_ptr<download::DownloadUrlParameters> params,
    scoped_refptr<network::SharedURLLoaderFactory> blob_url_loader_factory,
    bool is_new_download,
    const std::string& serialized_embedder_download_data) {
  // Check if the renderer is permitted to request the requested URL.
  if (params->render_process_host_id() >= 0 &&
      !DownloadRequestUtils::IsURLSafe(params->render_process_host_id(),
                                       params->url())) {
    CreateInterruptedDownload(
        std::move(params),
        download::DOWNLOAD_INTERRUPT_REASON_NETWORK_INVALID_REQUEST,
        weak_factory_.GetWeakPtr());
    return;
  }

  auto* rfh = RenderFrameHost::FromID(params->render_process_host_id(),
                                      params->render_frame_host_routing_id());

  StoragePartitionConfig storage_partition_config;
  if (rfh && serialized_embedder_download_data.empty()) {
    storage_partition_config = rfh->GetSiteInstance()
                                   ->GetSecurityPrincipal()
                                   .GetStoragePartitionConfig();
  } else {
    storage_partition_config =
        SerializedEmbedderDownloadDataToStoragePartitionConfig(
            serialized_embedder_download_data);
  }

  // Ideally everywhere a blob: URL is downloaded a URLLoaderFactory for that
  // blob URL is also passed, but since that isn't always the case, create
  // a new factory if we don't have one already.
  if (!blob_url_loader_factory && params->url().SchemeIsBlob()) {
    blob_url_loader_factory = ChromeBlobStorageContext::URLLoaderFactoryForUrl(
        GetStoragePartitionForConfig(browser_context_,
                                     storage_partition_config),
        params->url());
  }

  bool content_initiated = params->content_initiated();
  if (rfh && content_initiated) {
    // Cancel downloads from non-active documents (e.g prerendered, bfcached) or
    // fenced frames.
    if (rfh->IsInactiveAndDisallowActivation(
            DisallowActivationReasonId::kBeginDownload) ||
        rfh->IsNestedWithinFencedFrame()) {
      DropDownload();
      return;
    }

    // Push the throttle on the web-initiated downloads before allowing them to
    // proceed.
    if (delegate_) {
      WebContents::Getter web_contents_getter = base::BindRepeating(
          WebContents::FromFrameTreeNodeId, rfh->GetFrameTreeNodeId());
      const GURL& url = params->url();
      const std::string& method = params->method();
      std::optional<url::Origin> initiator = params->initiator();
      base::OnceCallback<void(bool /* download allowed */)>
          on_can_download_checks_done = base::BindOnce(
              &DownloadManagerImpl::BeginResourceDownloadOnChecksComplete,
              weak_factory_.GetWeakPtr(), std::move(params),
              std::move(blob_url_loader_factory), is_new_download,
              storage_partition_config);
      delegate_->CheckDownloadAllowed(
          std::move(web_contents_getter), url, method, std::move(initiator),
          false /* from_download_cross_origin_redirect */, content_initiated,
          /* mime_type= */ std::string(),
          /* page_transition= */ std::nullopt,
          std::move(on_can_download_checks_done));
      return;
    }
  }

  BeginResourceDownloadOnChecksComplete(
      std::move(params), std::move(blob_url_loader_factory), is_new_download,
      storage_partition_config, rfh ? !content_initiated : true);
}

bool DownloadManagerImpl::IsNextIdInitialized() const {
  return is_history_download_id_retrieved_ && in_progress_cache_initialized_;
}

bool DownloadManagerImpl::ShouldClearDownloadFromDB(
    const GURL& url,
    download::DownloadItem::DownloadState state,
    download::DownloadInterruptReason reason,
    const base::Time& start_time) {
  // Use system time to determine if the download is expired. Manually setting
  // the system time can affect this.
  bool expired = base::Time::Now() - start_time >=
                 download::GetExpiredDownloadDeleteTime();
  if (state == download::DownloadItem::CANCELLED && expired)
    return true;

  if (reason != download::DOWNLOAD_INTERRUPT_REASON_NONE &&
      state == download::DownloadItem::INTERRUPTED && expired) {
    return true;
  }

  return false;
}

std::unique_ptr<download::DownloadItemImpl>
DownloadManagerImpl::RetrieveInProgressDownload(uint32_t id) {
  // In case the history DB has some invalid IDs, skip them.
  if (id == download::DownloadItem::kInvalidId)
    return nullptr;

  for (auto it = in_progress_downloads_.begin();
       it != in_progress_downloads_.end(); ++it) {
    if ((*it)->GetId() == id) {
      auto download = std::move(*it);
      in_progress_downloads_.erase(it);
      return download;
    }
  }

  return nullptr;
}

}  // namespace content
