1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85
   86
   87
   88
   89
   90
   91
   92
   93
   94
   95
   96
   97
   98
   99
  100
  101
  102
  103
  104
  105
  106
  107
  108
  109
  110
  111
  112
  113
  114
  115
  116
  117
  118
  119
  120
  121
  122
  123
  124
  125
  126
  127
  128
  129
  130
  131
  132
  133
  134
  135
  136
  137
  138
  139
  140
  141
  142
  143
  144
  145
  146
  147
  148
  149
  150
  151
  152
  153
  154
  155
  156
  157
  158
  159
  160
  161
  162
  163
  164
  165
  166
  167
  168
  169
  170
  171
  172
  173
  174
  175
  176
  177
  178
  179
  180
  181
  182
  183
  184
  185
  186
  187
  188
  189
  190
  191
  192
  193
  194
  195
  196
  197
  198
  199
  200
  201
  202
  203
  204
  205
  206
  207
  208
  209
  210
  211
  212
  213
  214
  215

content / browser / loader / keep_alive_url_loader_service.h [blame]

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

#ifndef CONTENT_BROWSER_LOADER_KEEP_ALIVE_URL_LOADER_SERVICE_H_
#define CONTENT_BROWSER_LOADER_KEEP_ALIVE_URL_LOADER_SERVICE_H_

#include <memory>
#include <optional>

#include "base/memory/scoped_refptr.h"
#include "content/browser/attribution_reporting/attribution_suitable_context.h"
#include "content/browser/loader/keep_alive_url_loader.h"
#include "content/common/content_export.h"
#include "content/public/browser/weak_document_ptr.h"
#include "mojo/public/cpp/bindings/pending_associated_receiver.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "services/network/public/mojom/url_loader_factory.mojom.h"
#include "third_party/blink/public/common/loader/url_loader_factory_bundle.h"
#include "third_party/blink/public/mojom/loader/fetch_later.mojom.h"

namespace content {

class BrowserContext;
class PolicyContainerHost;

// A service that stores bound SharedURLLoaderFactory mojo pipes from renderers
// of the same storage partition, and the intermediate URLLoader receivers, i.e.
// KeepAliveURLLoader, they have created to load fetch keepalive requests.
//
// A fetch keepalive request is originated from a JS call to
// `fetch(..., {keepalive: true})` or `navigator.sendBeacon()`. A renderer can
// ask this service to handle such request by using a remote of
// mojom::URLLoaderFactory bound to this service by `BindFactory()`, which also
// binds RenderFrameHostImpl-specific context with every receiver.
//
// Calling the remote `CreateLoaderAndStart()` of a factory will create a
// `KeepAliveURLLoader` here in browser. The service is responsible for keeping
// these loaders in `loader_receivers_` until the corresponding request
// completes or fails.
//
// Handling keepalive requests in this service allows a request to continue even
// if a renderer unloads before completion, i.e. the request is "keepalive",
// without needing the renderer to stay extra longer than the necessary time.
//
// This service is created and stored in every `StoragePartitionImpl` instance.
// Hence, its lifetime is the same as the owner StoragePartition for a partition
// domain, which should be generally longer than any of the renderers spawned
// from the partition domain.
//
// Design Doc:
// https://docs.google.com/document/d/1ZzxMMBvpqn8VZBZKnb7Go8TWjnrGcXuLS_USwVVRUvY
class CONTENT_EXPORT KeepAliveURLLoaderService {
 public:
  // A context for the receiver of a `KeepAliveURLLoaderFactoriesBase`
  // connection between a renderer and the browser.
  //
  // A FactoryContext is created whenever `BindFactory()` or
  // `BindFetchLaterLoaderFactory()` is called by
  // RenderFrameHostImpl::CommitNavigation(). It can also be cloned by the same
  // corresponding renderer, or when new window or new child frame is created.
  //
  // See `mojo::ReceiverSetBase` for more details.
  struct CONTENT_EXPORT FactoryContext {
    FactoryContext(
        scoped_refptr<network::SharedURLLoaderFactory> factory,
        scoped_refptr<PolicyContainerHost> frame_policy_container_host);
    // Called when a factory is cloned by URLLoaderFactory::Clone().
    explicit FactoryContext(const std::unique_ptr<FactoryContext>& other);
    ~FactoryContext();
    // Not Copyable.
    FactoryContext(const FactoryContext&) = delete;
    FactoryContext& operator=(const FactoryContext&) = delete;

    // Updates `weak_document_ptr` and other document-related fields.
    void OnDidCommitNavigation(WeakDocumentPtr committed_document);

    // Updates `factory` using the given `new_factory`.
    //
    // Only called either
    // (1) when DevTools tries to intercept every URLLoaderFactory
    // (2) after network service crashes
    //
    // The default subresources loading, including non-keepalive fetch requests,
    // don't go through browser. Hence, their intercepted URLLoaderFactory are
    // updated via SubresourceLoaderUpdater::UpdateSubresourceLoaderFactories().
    // On the other hand, calling this method can update the fetch keepalive
    // factory directly in-browser.
    void UpdateFactory(
        scoped_refptr<network::SharedURLLoaderFactory> new_factory);

    // The factory to use for the requests initiated from this context.
    scoped_refptr<network::SharedURLLoaderFactory> factory;

    // Upon NavigationRequest::DidCommitNavigation(), `weak_document_ptr` will
    // be set to the document that this `BindContext` is associated with. It
    // will become null whenever the document navigates away.
    WeakDocumentPtr weak_document_ptr;

    // The `PolicyContainerHost` of the document connecting to an implementation
    // of `KeepAliveURLLoaderFactoriesBase` using this context.
    //
    // This field keeps the pointed object alive such that any pending keepalive
    // redirect requests can still be verified against these same policies.
    //
    // When `this` is constructed, this field is set to the PolicyContainerHost
    // of the requesting RenderFrameHostImpl, which may be inherited from its
    // creator (See `RenderFrameHostImpl::InitializePolicyContainerHost()`):
    // when a factory is cloned due to creating new window/new child frame,
    // this field will initially inherit the same value; if the new window/new
    // child frame commits a new document after that, this field will be updated
    // by `OnDidCommitNavigation()`.
    scoped_refptr<PolicyContainerHost> policy_container_host;

    // Attribution responses might be processed from keep alive requests. For
    // them to be processed, the request must have been sent from a suitable
    // context and information from that context is needed. Upon
    // NavigationRequest::DidCommitNavigation(), if the context is suitable,
    // the `attribution_context` is created.
    std::optional<AttributionSuitableContext> attribution_context;

    // This must be the last member.
    base::WeakPtrFactory<FactoryContext> weak_ptr_factory{this};
  };

  // `browser_context` owns the StoragePartition creating the instance of this
  // service. It must not be null and surpass the lifetime of this service.
  explicit KeepAliveURLLoaderService(BrowserContext* browser_context);
  ~KeepAliveURLLoaderService();

  // Not Copyable.
  KeepAliveURLLoaderService(const KeepAliveURLLoaderService&) = delete;
  KeepAliveURLLoaderService& operator=(const KeepAliveURLLoaderService&) =
      delete;

  // Binds the pending `receiver` with this service, using
  // `subresource_proxying_factory_bundle`.
  //
  // The remote of `receiver` can be passed to another process, i.e. renderer,
  // from which to create new fetch keepalive requests.
  //
  // `policy_container_host` is the policy host of the requester frame going to
  // use the remote of `receiver` to load requests. It must not be null.
  //
  // Returns a `FactoryContext` bound to the new factory connecting with
  // `receiver`. The caller can update the context when necessary.
  base::WeakPtr<FactoryContext> BindFactory(
      mojo::PendingReceiver<network::mojom::URLLoaderFactory> receiver,
      scoped_refptr<network::SharedURLLoaderFactory>
          subresource_proxying_factory_bundle,
      scoped_refptr<PolicyContainerHost> policy_container_host);

  // Binds the pending FetchLaterLoaderFactory `receiver` with this service,
  // which uses `factory` to load FetchLater URL requests.
  // See also `BindFactory()` for other parameters. Note that the returned
  // `FactoryContext` here is different from the one of `BindFactory()`.
  base::WeakPtr<FactoryContext> BindFetchLaterLoaderFactory(
      mojo::PendingAssociatedReceiver<blink::mojom::FetchLaterLoaderFactory>
          receiver,
      scoped_refptr<network::SharedURLLoaderFactory>
          subresource_proxying_factory_bundle,
      scoped_refptr<PolicyContainerHost> policy_container_host);

  // Called when the `browser_context_` that owns this instance is shutting
  // down.
  void Shutdown();

  // For testing only:
  size_t NumLoadersForTesting() const;
  size_t NumDisconnectedLoadersForTesting() const;
  void SetLoaderObserverForTesting(
      scoped_refptr<KeepAliveURLLoader::TestObserver> observer);
  void SetURLLoaderThrottlesGetterForTesting(
      KeepAliveURLLoader::URLLoaderThrottlesGetter
          url_loader_throttles_getter_for_testing);

 private:
  template <typename Interface,
            template <typename>
            class PendingReceiverType,
            template <typename, typename>
            class ReceiverSetType>
  class KeepAliveURLLoaderFactoriesBase;
  class KeepAliveURLLoaderFactories;
  class FetchLaterLoaderFactories;

  // Handles every disconnection notification for `loader_receivers_`.
  void OnLoaderDisconnected();

  // Removes the KeepAliveURLLoader kept by this service, either from
  // `loader_receivers_` or `disconnected_loaders_`.
  void RemoveLoader(mojo::ReceiverId loader_receiver_id);

  // The browsing session that owns this instance of the service.
  const raw_ptr<BrowserContext> browser_context_;

  // Many-to-one mojo receiver of URLLoaderFactory for Fetch keepalive requests.
  std::unique_ptr<KeepAliveURLLoaderFactories> url_loader_factories_;

  // Many-to-one mojo receiver of FetchLaterLoaderFactory for FetchLater
  // keepalive requests.
  std::unique_ptr<FetchLaterLoaderFactories> fetch_later_loader_factories_;

  // For testing only:
  // Not owned.
  scoped_refptr<KeepAliveURLLoader::TestObserver> loader_test_observer_ =
      nullptr;
  KeepAliveURLLoader::URLLoaderThrottlesGetter
      url_loader_throttles_getter_for_testing_ = base::NullCallback();
};

}  // namespace content

#endif  // CONTENT_BROWSER_LOADER_KEEP_ALIVE_URL_LOADER_SERVICE_H_