// Copyright (c) 2019 The Chromium Embedded Framework Authors. Portions
// Copyright (c) 2018 The Chromium Authors. All rights reserved. Use of this
// source code is governed by a BSD-style license that can be found in the
// LICENSE file.

#include "libcef/browser/net_service/url_loader_factory_getter.h"

#include "libcef/browser/content_browser_client.h"
#include "libcef/browser/thread_util.h"

#include "base/threading/thread_task_runner_handle.h"
#include "content/browser/devtools/devtools_instrumentation.h"
#include "content/browser/frame_host/render_frame_host_impl.h"
#include "content/public/browser/storage_partition.h"
#include "content/public/common/content_client.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
#include "services/network/public/cpp/wrapper_shared_url_loader_factory.h"

namespace net_service {

// Based on CreateDownloadURLLoaderFactoryGetter from
// content/browser/download/download_manager_impl.cc.
// static
scoped_refptr<URLLoaderFactoryGetter> URLLoaderFactoryGetter::Create(
    content::RenderFrameHost* render_frame_host,
    content::BrowserContext* browser_context) {
  CEF_REQUIRE_UIT();
  DCHECK(browser_context);

  // Call this early because newly created BrowserContexts may need to
  // initialize additional state, and that should be done on the UI thread
  // instead of potentially racing with the WillCreateURLLoaderFactory
  // implementation.
  auto loader_factory =
      content::BrowserContext::GetDefaultStoragePartition(browser_context)
          ->GetURLLoaderFactoryForBrowserProcess();

  network::mojom::URLLoaderFactoryPtrInfo proxy_factory_ptr_info;
  network::mojom::URLLoaderFactoryRequest proxy_factory_request;

  // Create an intermediate pipe that can be used to proxy the request's
  // URLLoaderFactory.
  network::mojom::URLLoaderFactoryPtrInfo maybe_proxy_factory_ptr_info;
  network::mojom::URLLoaderFactoryRequest maybe_proxy_factory_request =
      MakeRequest(&maybe_proxy_factory_ptr_info);

  bool should_proxy = false;
  int render_process_id = -1;

  if (render_frame_host) {
    render_process_id = render_frame_host->GetProcess()->GetID();

    // Allow DevTools to potentially inject itself into the proxy pipe.
    should_proxy =
        content::devtools_instrumentation::WillCreateURLLoaderFactory(
            static_cast<content::RenderFrameHostImpl*>(render_frame_host),
            false /* is_navigation */, false /* is_download */,
            &maybe_proxy_factory_request);
  }

  // Allow the Content embedder to inject itself if it wants to.
  should_proxy |= CefContentBrowserClient::Get()->WillCreateURLLoaderFactory(
      browser_context, render_frame_host, render_process_id,
      false /* is_navigation */, false /* is_download */, url::Origin(),
      &maybe_proxy_factory_request, nullptr /* header_client */,
      nullptr /* bypass_redirect_checks */);

  // If anyone above indicated that they care about proxying, pass the
  // intermediate pipe along to the URLLoaderFactoryGetter.
  if (should_proxy) {
    proxy_factory_ptr_info = std::move(maybe_proxy_factory_ptr_info);
    proxy_factory_request = std::move(maybe_proxy_factory_request);
  }

  return base::WrapRefCounted(new URLLoaderFactoryGetter(
      loader_factory->Clone(), std::move(proxy_factory_ptr_info),
      std::move(proxy_factory_request)));
}

// Based on NetworkDownloadURLLoaderFactoryGetter from
// content/browser/download/network_download_url_loader_factory_getter.cc.
scoped_refptr<network::SharedURLLoaderFactory>
URLLoaderFactoryGetter::GetURLLoaderFactory() {
  // On first call we associate with the current thread.
  if (!task_runner_) {
    task_runner_ = base::ThreadTaskRunnerHandle::Get();
  } else {
    DCHECK(task_runner_->RunsTasksInCurrentSequence());
  }

  if (lazy_factory_)
    return lazy_factory_;

  // Bind on the current thread.
  auto loader_factory =
      network::SharedURLLoaderFactory::Create(std::move(loader_factory_info_));

  if (proxy_factory_request_.is_pending()) {
    loader_factory->Clone(std::move(proxy_factory_request_));
    lazy_factory_ =
        base::MakeRefCounted<network::WrapperSharedURLLoaderFactory>(
            std::move(proxy_factory_ptr_info_));
  } else {
    lazy_factory_ = loader_factory;
  }
  return lazy_factory_;
}

URLLoaderFactoryGetter::URLLoaderFactoryGetter(
    std::unique_ptr<network::SharedURLLoaderFactoryInfo> loader_factory_info,
    network::mojom::URLLoaderFactoryPtrInfo proxy_factory_ptr_info,
    network::mojom::URLLoaderFactoryRequest proxy_factory_request)
    : loader_factory_info_(std::move(loader_factory_info)),
      proxy_factory_ptr_info_(std::move(proxy_factory_ptr_info)),
      proxy_factory_request_(std::move(proxy_factory_request)) {}

URLLoaderFactoryGetter::~URLLoaderFactoryGetter() = default;

void URLLoaderFactoryGetter::DeleteOnCorrectThread() const {
  if (task_runner_ && !task_runner_->RunsTasksInCurrentSequence()) {
    task_runner_->DeleteSoon(FROM_HERE, this);
    return;
  }
  delete this;
}

}  // namespace net_service