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

#include "content/browser/frame_host/render_frame_message_filter.h"

#include <memory>
#include <string>
#include <utility>
#include <vector>

#include "base/command_line.h"
#include "base/debug/alias.h"
#include "base/macros.h"
#include "base/strings/string_util.h"
#include "base/syslog_logging.h"
#include "base/unguessable_token.h"
#include "build/build_config.h"
#include "components/download/public/common/download_url_parameters.h"
#include "content/browser/bad_message.h"
#include "content/browser/blob_storage/chrome_blob_storage_context.h"
#include "content/browser/child_process_security_policy_impl.h"
#include "content/browser/frame_host/render_frame_host_impl.h"
#include "content/browser/gpu/gpu_data_manager_impl.h"
#include "content/browser/renderer_host/render_widget_helper.h"
#include "content/browser/resource_context_impl.h"
#include "content/browser/storage_partition_impl.h"
#include "content/common/content_constants_internal.h"
#include "content/common/frame_messages.h"
#include "content/common/frame_owner_properties.h"
#include "content/common/view_messages.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/download_manager.h"
#include "content/public/browser/storage_partition.h"
#include "content/public/common/content_constants.h"
#include "gpu/GLES2/gl2extchromium.h"
#include "mojo/public/cpp/bindings/callback_helpers.h"
#include "mojo/public/cpp/system/message_pipe.h"
#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
#include "net/cookies/canonical_cookie.h"
#include "net/cookies/cookie_options.h"
#include "net/cookies/cookie_store.h"
#include "net/traffic_annotation/network_traffic_annotation.h"
#include "net/url_request/url_request_context.h"
#include "net/url_request/url_request_context_getter.h"
#include "ppapi/buildflags/buildflags.h"
#include "services/network/public/cpp/features.h"
#include "services/service_manager/public/mojom/interface_provider.mojom.h"
#include "storage/browser/blob/blob_storage_context.h"
#include "third_party/blink/public/common/frame/frame_policy.h"
#include "url/gurl.h"
#include "url/origin.h"

#if !defined(OS_MACOSX)
#include "third_party/khronos/GLES2/gl2.h"
#include "third_party/khronos/GLES2/gl2ext.h"
#endif

#if BUILDFLAG(ENABLE_PLUGINS)
#include "content/browser/plugin_service_impl.h"
#include "content/browser/ppapi_plugin_process_host.h"
#include "content/public/browser/plugin_service_filter.h"
#endif

namespace content {

namespace {

void CreateChildFrameOnUI(
    int process_id,
    int parent_routing_id,
    blink::WebTreeScopeType scope,
    const std::string& frame_name,
    const std::string& frame_unique_name,
    bool is_created_by_script,
    const base::UnguessableToken& devtools_frame_token,
    const blink::FramePolicy& frame_policy,
    const FrameOwnerProperties& frame_owner_properties,
    int new_routing_id,
    mojo::ScopedMessagePipeHandle interface_provider_request_handle) {
  DCHECK_CURRENTLY_ON(BrowserThread::UI);
  RenderFrameHostImpl* render_frame_host =
      RenderFrameHostImpl::FromID(process_id, parent_routing_id);
  // Handles the RenderFrameHost being deleted on the UI thread while
  // processing a subframe creation message.
  if (render_frame_host) {
    render_frame_host->OnCreateChildFrame(
        new_routing_id,
        service_manager::mojom::InterfaceProviderRequest(
            std::move(interface_provider_request_handle)),
        scope, frame_name, frame_unique_name, is_created_by_script,
        devtools_frame_token, frame_policy, frame_owner_properties);
  }
}

// |blob_data_handle| is only here for the legacy code path. With network
// service enabled |blob_url_token| should be provided and will be used instead
// to download the correct blob.
void DownloadUrlOnUIThread(
    std::unique_ptr<download::DownloadUrlParameters> parameters,
    std::unique_ptr<storage::BlobDataHandle> blob_data_handle,
    blink::mojom::BlobURLTokenPtrInfo blob_url_token) {
  DCHECK_CURRENTLY_ON(BrowserThread::UI);

  RenderProcessHost* render_process_host =
      RenderProcessHost::FromID(parameters->render_process_host_id());
  if (!render_process_host)
    return;

  BrowserContext* browser_context = render_process_host->GetBrowserContext();

  scoped_refptr<network::SharedURLLoaderFactory> blob_url_loader_factory;
  if (blob_url_token) {
    blob_url_loader_factory =
        ChromeBlobStorageContext::URLLoaderFactoryForToken(
            browser_context,
            blink::mojom::BlobURLTokenPtr(std::move(blob_url_token)));
  }

  DownloadManager* download_manager =
      BrowserContext::GetDownloadManager(browser_context);
  parameters->set_download_source(download::DownloadSource::FROM_RENDERER);
  download_manager->DownloadUrl(std::move(parameters),
                                std::move(blob_data_handle),
                                std::move(blob_url_loader_factory));
}

// With network service disabled the downloads code wouldn't know what to do
// with a BlobURLToken, so this method is used to convert from a token to a
// BlobDataHandle to be passed on to the rest of the downloads system.
void DownloadBlobURLFromToken(
    std::unique_ptr<download::DownloadUrlParameters> params,
    blink::mojom::BlobURLTokenPtr,
    const base::WeakPtr<storage::BlobStorageContext>& context,
    const base::UnguessableToken& token) {
  std::unique_ptr<storage::BlobDataHandle> blob_handle;
  GURL blob_url;
  if (context) {
    std::string uuid;
    if (context->registry().GetTokenMapping(token, &blob_url, &uuid) &&
        blob_url == params->url()) {
      blob_handle = context->GetBlobDataFromUUID(uuid);
    }
  }
  BrowserThread::PostTask(
      BrowserThread::UI, FROM_HERE,
      base::BindOnce(&DownloadUrlOnUIThread, std::move(params),
                     std::move(blob_handle), nullptr));
}

// Common functionality for converting a sync renderer message to a callback
// function in the browser. Derive from this, create it on the heap when
// issuing your callback. When done, write your reply parameters into
// reply_msg(), and then call SendReplyAndDeleteThis().
class RenderMessageCompletionCallback {
 public:
  RenderMessageCompletionCallback(RenderFrameMessageFilter* filter,
                                  IPC::Message* reply_msg)
      : filter_(filter),
        reply_msg_(reply_msg) {
  }

  virtual ~RenderMessageCompletionCallback() {
    if (reply_msg_) {
      // If the owner of this class failed to call SendReplyAndDeleteThis(),
      // send an error reply to prevent the renderer from being hung.
      reply_msg_->set_reply_error();
      filter_->Send(reply_msg_);
    }
  }

  RenderFrameMessageFilter* filter() { return filter_.get(); }
  IPC::Message* reply_msg() { return reply_msg_; }

  void SendReplyAndDeleteThis() {
    filter_->Send(reply_msg_);
    reply_msg_ = nullptr;
    delete this;
  }

 private:
  scoped_refptr<RenderFrameMessageFilter> filter_;
  IPC::Message* reply_msg_;
};

}  // namespace

#if BUILDFLAG(ENABLE_PLUGINS)

class RenderFrameMessageFilter::OpenChannelToPpapiBrokerCallback
    : public PpapiPluginProcessHost::BrokerClient {
 public:
  OpenChannelToPpapiBrokerCallback(RenderFrameMessageFilter* filter,
                                   int routing_id)
      : filter_(filter),
        routing_id_(routing_id) {
  }

  ~OpenChannelToPpapiBrokerCallback() override {}

  void GetPpapiChannelInfo(base::ProcessHandle* renderer_handle,
                           int* renderer_id) override {
    // base::kNullProcessHandle indicates that the channel will be used by the
    // browser itself. Make sure we never output that value here.
    CHECK_NE(base::kNullProcessHandle, filter_->PeerHandle());
    *renderer_handle = filter_->PeerHandle();
    *renderer_id = filter_->render_process_id_;
  }

  void OnPpapiChannelOpened(const IPC::ChannelHandle& channel_handle,
                            base::ProcessId plugin_pid,
                            int /* plugin_child_id */) override {
    filter_->Send(new ViewMsg_PpapiBrokerChannelCreated(routing_id_,
                                                        plugin_pid,
                                                        channel_handle));
    delete this;
  }

  bool Incognito() override { return filter_->incognito_; }

 private:
  scoped_refptr<RenderFrameMessageFilter> filter_;
  int routing_id_;
};

class RenderFrameMessageFilter::OpenChannelToPpapiPluginCallback
    : public RenderMessageCompletionCallback,
      public PpapiPluginProcessHost::PluginClient {
 public:
  OpenChannelToPpapiPluginCallback(RenderFrameMessageFilter* filter,
                                   IPC::Message* reply_msg)
      : RenderMessageCompletionCallback(filter, reply_msg) {}

  void GetPpapiChannelInfo(base::ProcessHandle* renderer_handle,
                           int* renderer_id) override {
    // base::kNullProcessHandle indicates that the channel will be used by the
    // browser itself. Make sure we never output that value here.
    CHECK_NE(base::kNullProcessHandle, filter()->PeerHandle());
    *renderer_handle = filter()->PeerHandle();
    *renderer_id = filter()->render_process_id_;
  }

  void OnPpapiChannelOpened(const IPC::ChannelHandle& channel_handle,
                            base::ProcessId plugin_pid,
                            int plugin_child_id) override {
    FrameHostMsg_OpenChannelToPepperPlugin::WriteReplyParams(
        reply_msg(), channel_handle, plugin_pid, plugin_child_id);
    SendReplyAndDeleteThis();
  }

  bool Incognito() override { return filter()->incognito_; }
};

#endif  // ENABLE_PLUGINS

RenderFrameMessageFilter::RenderFrameMessageFilter(
    int render_process_id,
    PluginServiceImpl* plugin_service,
    BrowserContext* browser_context,
    StoragePartition* storage_partition,
    RenderWidgetHelper* render_widget_helper)
    : BrowserMessageFilter(FrameMsgStart),
      BrowserAssociatedInterface<mojom::RenderFrameMessageFilter>(this, this),
#if BUILDFLAG(ENABLE_PLUGINS)
      plugin_service_(plugin_service),
      profile_data_directory_(storage_partition->GetPath()),
#endif  // ENABLE_PLUGINS
      request_context_(storage_partition->GetURLRequestContext()),
      resource_context_(browser_context->GetResourceContext()),
      render_widget_helper_(render_widget_helper),
      incognito_(browser_context->IsOffTheRecord()),
      render_process_id_(render_process_id) {
  network::mojom::CookieManagerPtr cookie_manager;
  storage_partition->GetNetworkContext()->GetCookieManager(
      mojo::MakeRequest(&cookie_manager));

  // The PostTask below could finish before the constructor returns which would
  // lead to this object being destructed prematurely.
  AddRef();
  base::ThreadTaskRunnerHandle::Get()->ReleaseSoon(FROM_HERE, this);

  BrowserThread::PostTask(
      BrowserThread::IO, FROM_HERE,
      base::BindOnce(&RenderFrameMessageFilter::InitializeOnIO, this,
                     cookie_manager.PassInterface()));
}

RenderFrameMessageFilter::~RenderFrameMessageFilter() {
  // This function should be called on the IO thread.
  DCHECK_CURRENTLY_ON(BrowserThread::IO);
}

void RenderFrameMessageFilter::InitializeOnIO(
    network::mojom::CookieManagerPtrInfo cookie_manager) {
  cookie_manager_.Bind(std::move(cookie_manager));
}

bool RenderFrameMessageFilter::OnMessageReceived(const IPC::Message& message) {
  bool handled = true;
  IPC_BEGIN_MESSAGE_MAP(RenderFrameMessageFilter, message)
    IPC_MESSAGE_HANDLER(FrameHostMsg_CreateChildFrame, OnCreateChildFrame)
    IPC_MESSAGE_HANDLER(FrameHostMsg_CookiesEnabled, OnCookiesEnabled)
    IPC_MESSAGE_HANDLER(FrameHostMsg_DownloadUrl, OnDownloadUrl)
    IPC_MESSAGE_HANDLER(FrameHostMsg_SaveImageFromDataURL,
                        OnSaveImageFromDataURL)
    IPC_MESSAGE_HANDLER(FrameHostMsg_Are3DAPIsBlocked, OnAre3DAPIsBlocked)
    IPC_MESSAGE_HANDLER_GENERIC(FrameHostMsg_RenderProcessGone,
                                OnRenderProcessGone())
#if BUILDFLAG(ENABLE_PLUGINS)
    IPC_MESSAGE_HANDLER(FrameHostMsg_GetPluginInfo, OnGetPluginInfo)
    IPC_MESSAGE_HANDLER_DELAY_REPLY(FrameHostMsg_OpenChannelToPepperPlugin,
                                    OnOpenChannelToPepperPlugin)
    IPC_MESSAGE_HANDLER(FrameHostMsg_DidCreateOutOfProcessPepperInstance,
                        OnDidCreateOutOfProcessPepperInstance)
    IPC_MESSAGE_HANDLER(FrameHostMsg_DidDeleteOutOfProcessPepperInstance,
                        OnDidDeleteOutOfProcessPepperInstance)
    IPC_MESSAGE_HANDLER(FrameHostMsg_OpenChannelToPpapiBroker,
                        OnOpenChannelToPpapiBroker)
    IPC_MESSAGE_HANDLER(FrameHostMsg_PluginInstanceThrottleStateChange,
                        OnPluginInstanceThrottleStateChange)
#endif  // ENABLE_PLUGINS
    IPC_MESSAGE_UNHANDLED(handled = false)
  IPC_END_MESSAGE_MAP()

  return handled;
}

void RenderFrameMessageFilter::OnDestruct() const {
  BrowserThread::DeleteOnIOThread::Destruct(this);
}

void RenderFrameMessageFilter::DownloadUrl(
    int render_view_id,
    int render_frame_id,
    const GURL& url,
    const Referrer& referrer,
    const url::Origin& initiator,
    const base::string16& suggested_name,
    const bool use_prompt,
    const bool follow_cross_origin_redirects,
    blink::mojom::BlobURLTokenPtrInfo blob_url_token) const {
  if (!resource_context_)
    return;

  net::NetworkTrafficAnnotationTag traffic_annotation =
      net::DefineNetworkTrafficAnnotation("renderer_initiated_download", R"(
        semantics {
          sender: "Download from Renderer"
          description:
            "The frame has either navigated to a URL that was determined to be "
            "a download via one of the renderer's classification mechanisms, "
            "or WebView has requested a <canvas> or <img> element at a "
            "specific location be to downloaded."
          trigger:
            "The user navigated to a destination that was categorized as a "
            "download, or WebView triggered saving a <canvas> or <img> tag."
          data: "Only the URL we are attempting to download."
          destination: WEBSITE
        }
        policy {
          cookies_allowed: YES
          cookies_store: "user"
          setting: "This feature cannot be disabled by settings."
          chrome_policy {
            DownloadRestrictions {
              DownloadRestrictions: 3
            }
          }
        })");
  std::unique_ptr<download::DownloadUrlParameters> parameters(
      new download::DownloadUrlParameters(
          url, render_process_id_, render_view_id, render_frame_id,
          request_context_.get(), traffic_annotation));
  parameters->set_content_initiated(true);
  parameters->set_suggested_name(suggested_name);
  parameters->set_prompt(use_prompt);
  parameters->set_follow_cross_origin_redirects(follow_cross_origin_redirects);
  parameters->set_referrer(referrer.url);
  parameters->set_referrer_policy(
      Referrer::ReferrerPolicyForUrlRequest(referrer.policy));
  parameters->set_initiator(initiator);

  // If network service is enabled we should always have a |blob_url_token|,
  // which will be used to download the correct blob. But in the legacy
  // non-network service code path we still need to look up the BlobDataHandle
  // for the URL here, to make sure the correct blob ends up getting downloaded.
  std::unique_ptr<storage::BlobDataHandle> blob_data_handle;
  if (url.SchemeIsBlob()) {
    ChromeBlobStorageContext* blob_context =
        GetChromeBlobStorageContextForResourceContext(resource_context_);

    // With network service disabled the downloads code wouldn't know what to do
    // with the BlobURLToken (or the resulting URLLoaderFactory). So for that
    // case convert the token to a BlobDataHandle before passing it of to the
    // rest of the downloads system.
    if (blob_url_token &&
        !base::FeatureList::IsEnabled(network::features::kNetworkService)) {
      blink::mojom::BlobURLTokenPtr blob_url_token_ptr(
          std::move(blob_url_token));
      auto* raw_token = blob_url_token_ptr.get();
      raw_token->GetToken(mojo::WrapCallbackWithDefaultInvokeIfNotRun(
          base::BindOnce(&DownloadBlobURLFromToken, std::move(parameters),
                         std::move(blob_url_token_ptr),
                         blob_context->context()->AsWeakPtr()),
          base::UnguessableToken()));
      return;
    }

    blob_data_handle = blob_context->context()->GetBlobDataFromPublicURL(url);
    // Don't care if the above fails. We are going to let the download go
    // through and allow it to be interrupted so that the embedder can deal.
  }

  BrowserThread::PostTask(
      BrowserThread::UI, FROM_HERE,
      base::BindOnce(&DownloadUrlOnUIThread, std::move(parameters),
                     std::move(blob_data_handle), std::move(blob_url_token)));
}

void RenderFrameMessageFilter::OnCreateChildFrame(
    const FrameHostMsg_CreateChildFrame_Params& params,
    int* new_routing_id,
    mojo::MessagePipeHandle* new_interface_provider,
    base::UnguessableToken* devtools_frame_token) {
  *new_routing_id = render_widget_helper_->GetNextRoutingID();

  service_manager::mojom::InterfaceProviderPtr interface_provider;
  auto interface_provider_request(mojo::MakeRequest(&interface_provider));
  *new_interface_provider =
      interface_provider.PassInterface().PassHandle().release();

  *devtools_frame_token = base::UnguessableToken::Create();

  BrowserThread::PostTask(
      BrowserThread::UI, FROM_HERE,
      base::BindOnce(&CreateChildFrameOnUI, render_process_id_,
                     params.parent_routing_id, params.scope, params.frame_name,
                     params.frame_unique_name, params.is_created_by_script,
                     *devtools_frame_token, params.frame_policy,
                     params.frame_owner_properties, *new_routing_id,
                     interface_provider_request.PassMessagePipe()));
}

void RenderFrameMessageFilter::OnCookiesEnabled(int render_frame_id,
                                                const GURL& url,
                                                const GURL& site_for_cookies,
                                                bool* cookies_enabled) {
  // TODO(ananta): If this render frame is associated with an automation
  // channel, aka ChromeFrame then we need to retrieve cookie settings from the
  // external host.
  *cookies_enabled = GetContentClient()->browser()->AllowGetCookie(
      url, site_for_cookies, net::CookieList(), resource_context_,
      render_process_id_, render_frame_id);
}

void RenderFrameMessageFilter::CheckPolicyForCookies(
    int render_frame_id,
    const GURL& url,
    const GURL& site_for_cookies,
    GetCookiesCallback callback,
    const net::CookieList& cookie_list) {
  net::URLRequestContext* context = GetRequestContextForURL(url);
  // Check the policy for get cookies, and pass cookie_list to the
  // TabSpecificContentSetting for logging purpose.
  if (context && GetContentClient()->browser()->AllowGetCookie(
                     url, site_for_cookies, cookie_list, resource_context_,
                     render_process_id_, render_frame_id)) {
    std::move(callback).Run(net::CanonicalCookie::BuildCookieLine(cookie_list));
  } else {
    std::move(callback).Run(std::string());
  }
}

void RenderFrameMessageFilter::OnDownloadUrl(
    const FrameHostMsg_DownloadUrl_Params& params) {
  mojo::ScopedMessagePipeHandle blob_url_token_handle(params.blob_url_token);
  blink::mojom::BlobURLTokenPtrInfo blob_url_token(
      std::move(blob_url_token_handle), blink::mojom::BlobURLToken::Version_);
  if (blob_url_token && !params.url.SchemeIsBlob()) {
    bad_message::ReceivedBadMessage(
        this, bad_message::RFMF_BLOB_URL_TOKEN_FOR_NON_BLOB_URL);
    return;
  }

  DownloadUrl(params.render_view_id, params.render_frame_id, params.url,
              params.referrer, params.initiator_origin, params.suggested_name,
              false, params.follow_cross_origin_redirects,
              std::move(blob_url_token));
}

void RenderFrameMessageFilter::OnSaveImageFromDataURL(
    int render_view_id,
    int render_frame_id,
    const std::string& url_str) {
  // Please refer to RenderFrameImpl::saveImageFromDataURL().
  if (url_str.length() >= kMaxLengthOfDataURLString)
    return;

  GURL data_url(url_str);
  if (!data_url.is_valid() || !data_url.SchemeIs(url::kDataScheme))
    return;

  DownloadUrl(render_view_id, render_frame_id, data_url, Referrer(),
              url::Origin(), base::string16(), true, true, nullptr);
}

void RenderFrameMessageFilter::OnAre3DAPIsBlocked(int render_frame_id,
                                                  const GURL& top_origin_url,
                                                  ThreeDAPIType requester,
                                                  bool* blocked) {
  *blocked = GpuDataManagerImpl::GetInstance()->Are3DAPIsBlocked(
      top_origin_url, render_process_id_, render_frame_id, requester);
}

void RenderFrameMessageFilter::OnRenderProcessGone() {
  // FrameHostMessage_RenderProcessGone is a synthetic IPC message used by
  // RenderProcessHostImpl to clean things up after a crash (it's injected
  // downstream of this filter). Allowing it to proceed would enable a renderer
  // to fake its own death; instead, actually kill the renderer.
  bad_message::ReceivedBadMessage(
      this, bad_message::RFMF_RENDERER_FAKED_ITS_OWN_DEATH);
}

void RenderFrameMessageFilter::SetCookie(int32_t render_frame_id,
                                         const GURL& url,
                                         const GURL& site_for_cookies,
                                         const std::string& cookie_line,
                                         SetCookieCallback callback) {
  std::move(callback).Run();
  ChildProcessSecurityPolicyImpl* policy =
      ChildProcessSecurityPolicyImpl::GetInstance();
  if (!policy->CanAccessDataForOrigin(render_process_id_, url)) {
    bad_message::BadMessageReason reason =
        bad_message::RFMF_SET_COOKIE_BAD_ORIGIN;
    SYSLOG(WARNING) << "Killing renderer: illegal cookie write. Reason: "
                    << reason;
    bad_message::ReceivedBadMessage(this, reason);
    return;
  }

  net::CookieOptions options;
  std::unique_ptr<net::CanonicalCookie> cookie = net::CanonicalCookie::Create(
      url, cookie_line, base::Time::Now(), options);
  if (!cookie)
    return;

  if (!GetContentClient()->browser()->AllowSetCookie(
          url, site_for_cookies, *cookie, resource_context_, render_process_id_,
          render_frame_id))
    return;

  net::URLRequestContext* context = GetRequestContextForURL(url);
  // If the embedder overrides the URLRequestContext then always use it, even if
  // the network service is enabled, instead of the CookieManager associated
  // this process' StoragePartition.
  if (base::FeatureList::IsEnabled(network::features::kNetworkService) &&
      context == request_context_->GetURLRequestContext()) {
    cookie_manager_->SetCanonicalCookie(*cookie, url.SchemeIsCryptographic(),
                                        !options.exclude_httponly(),
                                        net::CookieStore::SetCookiesCallback());
    return;
  }

  // Pass a null callback since we don't care about when the 'set' completes.
  context->cookie_store()->SetCanonicalCookieAsync(
      std::move(cookie), url.SchemeIsCryptographic(),
      !options.exclude_httponly(), net::CookieStore::SetCookiesCallback());
}

void RenderFrameMessageFilter::GetCookies(int render_frame_id,
                                          const GURL& url,
                                          const GURL& site_for_cookies,
                                          GetCookiesCallback callback) {
  ChildProcessSecurityPolicyImpl* policy =
      ChildProcessSecurityPolicyImpl::GetInstance();
  if (!policy->CanAccessDataForOrigin(render_process_id_, url)) {
    bad_message::BadMessageReason reason =
        bad_message::RFMF_GET_COOKIES_BAD_ORIGIN;
    SYSLOG(WARNING) << "Killing renderer: illegal cookie read. Reason: "
                    << reason;
    bad_message::ReceivedBadMessage(this, reason);
    std::move(callback).Run(std::string());
    return;
  }

  net::CookieOptions options;
  if (net::registry_controlled_domains::SameDomainOrHost(
          url, site_for_cookies,
          net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES)) {
    // TODO(mkwst): This check ought to further distinguish between frames
    // initiated in a strict or lax same-site context.
    options.set_same_site_cookie_mode(
        net::CookieOptions::SameSiteCookieMode::INCLUDE_STRICT_AND_LAX);
  } else {
    options.set_same_site_cookie_mode(
        net::CookieOptions::SameSiteCookieMode::DO_NOT_INCLUDE);
  }

  net::URLRequestContext* context = GetRequestContextForURL(url);
  // If the embedder overrides the URLRequestContext then always use it, even if
  // the network service is enabled, instead of the CookieManager associated
  // this process' StoragePartition.
  if (base::FeatureList::IsEnabled(network::features::kNetworkService) &&
      context == request_context_->GetURLRequestContext()) {
    // TODO(jam): modify GetRequestContextForURL to work with network service.
    // Merge this with code path below for non-network service.
    cookie_manager_->GetCookieList(
        url, options,
        base::BindOnce(&RenderFrameMessageFilter::CheckPolicyForCookies, this,
                       render_frame_id, url, site_for_cookies,
                       std::move(callback)));
    return;
  }

  // If we crash here, figure out what URL the renderer was requesting.
  // http://crbug.com/99242
  DEBUG_ALIAS_FOR_GURL(url_buf, url);

  context->cookie_store()->GetCookieListWithOptionsAsync(
      url, options,
      base::BindOnce(&RenderFrameMessageFilter::CheckPolicyForCookies, this,
                     render_frame_id, url, site_for_cookies,
                     std::move(callback)));
}

#if BUILDFLAG(ENABLE_PLUGINS)

void RenderFrameMessageFilter::OnGetPluginInfo(
    int render_frame_id,
    const GURL& url,
    const url::Origin& main_frame_origin,
    const std::string& mime_type,
    bool* found,
    WebPluginInfo* info,
    std::string* actual_mime_type) {
  bool allow_wildcard = true;
  *found = plugin_service_->GetPluginInfo(
      render_process_id_, render_frame_id, resource_context_, url,
      main_frame_origin, mime_type, allow_wildcard, nullptr, info,
      actual_mime_type);
}

void RenderFrameMessageFilter::OnOpenChannelToPepperPlugin(
    const base::FilePath& path,
    const base::Optional<url::Origin>& origin_lock,
    IPC::Message* reply_msg) {
  plugin_service_->OpenChannelToPpapiPlugin(
      render_process_id_, path, profile_data_directory_, origin_lock,
      new OpenChannelToPpapiPluginCallback(this, reply_msg));
}

void RenderFrameMessageFilter::OnDidCreateOutOfProcessPepperInstance(
    int plugin_child_id,
    int32_t pp_instance,
    PepperRendererInstanceData instance_data,
    bool is_external) {
  // It's important that we supply the render process ID ourselves based on the
  // channel the message arrived on. We use the
  //   PP_Instance -> (process id, frame id)
  // mapping to decide how to handle messages received from the (untrusted)
  // plugin, so an exploited renderer must not be able to insert fake mappings
  // that may allow it access to other render processes.
  DCHECK_EQ(0, instance_data.render_process_id);
  instance_data.render_process_id = render_process_id_;
  if (is_external) {
    // We provide the BrowserPpapiHost to the embedder, so it's safe to cast.
    BrowserPpapiHostImpl* host = static_cast<BrowserPpapiHostImpl*>(
        GetContentClient()->browser()->GetExternalBrowserPpapiHost(
            plugin_child_id));
    if (host)
      host->AddInstance(pp_instance, instance_data);
  } else {
    PpapiPluginProcessHost::DidCreateOutOfProcessInstance(
        plugin_child_id, pp_instance, instance_data);
  }
}

void RenderFrameMessageFilter::OnDidDeleteOutOfProcessPepperInstance(
    int plugin_child_id,
    int32_t pp_instance,
    bool is_external) {
  if (is_external) {
    // We provide the BrowserPpapiHost to the embedder, so it's safe to cast.
    BrowserPpapiHostImpl* host = static_cast<BrowserPpapiHostImpl*>(
        GetContentClient()->browser()->GetExternalBrowserPpapiHost(
            plugin_child_id));
    if (host)
      host->DeleteInstance(pp_instance);
  } else {
    PpapiPluginProcessHost::DidDeleteOutOfProcessInstance(
        plugin_child_id, pp_instance);
  }
}

void RenderFrameMessageFilter::OnOpenChannelToPpapiBroker(
    int routing_id,
    const base::FilePath& path) {
  plugin_service_->OpenChannelToPpapiBroker(
      render_process_id_, routing_id, path,
      new OpenChannelToPpapiBrokerCallback(this, routing_id));
}

void RenderFrameMessageFilter::OnPluginInstanceThrottleStateChange(
    int plugin_child_id,
    int32_t pp_instance,
    bool is_throttled) {
  // Feature is only implemented for non-external Plugins.
  PpapiPluginProcessHost::OnPluginInstanceThrottleStateChange(
      plugin_child_id, pp_instance, is_throttled);
}

#endif  // ENABLE_PLUGINS

net::URLRequestContext* RenderFrameMessageFilter::GetRequestContextForURL(
    const GURL& url) {
  DCHECK_CURRENTLY_ON(BrowserThread::IO);

  net::URLRequestContext* context =
      GetContentClient()->browser()->OverrideRequestContextForURL(
          url, resource_context_);
  if (!context)
    context = request_context_->GetURLRequestContext();

  return context;
}

}  // namespace content
