File: pending_beacon_host.h

package info (click to toggle)
chromium 120.0.6099.224-1~deb11u1
  • links: PTS, VCS
  • area: main
  • in suites: bullseye
  • size: 6,112,112 kB
  • sloc: cpp: 32,907,025; ansic: 8,148,123; javascript: 3,679,536; python: 2,031,248; asm: 959,718; java: 804,675; xml: 617,256; sh: 111,417; objc: 100,835; perl: 88,443; cs: 53,032; makefile: 29,579; fortran: 24,137; php: 21,162; tcl: 21,147; sql: 20,809; ruby: 17,735; pascal: 12,864; yacc: 8,045; lisp: 3,388; lex: 1,323; ada: 727; awk: 329; jsp: 267; csh: 117; exp: 43; sed: 37
file content (248 lines) | stat: -rw-r--r-- 10,405 bytes parent folder | download
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
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
// Copyright 2022 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_RENDERER_HOST_PENDING_BEACON_HOST_H_
#define CONTENT_BROWSER_RENDERER_HOST_PENDING_BEACON_HOST_H_

#include <vector>

#include "base/memory/raw_ptr.h"
#include "content/common/content_export.h"
#include "content/public/browser/document_user_data.h"
#include "content/public/browser/render_process_host_observer.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "third_party/blink/public/mojom/frame/pending_beacon.mojom.h"

namespace network {
class DataElement;
}  // namespace network

namespace content {

class Beacon;
class PendingBeaconService;

// `PendingBeaconHost` holds and manages a single blink::Document's pending
// beacons in the browser process.
//
// PendingBeaconHost is created once per document and bound to a RenderFrameHost
// by `PendingBeaconHost::CreateForCurrentDocument()` called from
// `RenderFrameHostImpl::GetPendingBeaconHost()`.
//
// PendingBeaconHost's lifetime is roughly the same as a single document (except
// when crashing). See `DocumentUserData` for more details about lifetime.
//
// PendingBeaconHost creates a new Beacon when `CreateBeacon()` is called
// remotely from a document in renderer.
//
// PendingBeaconHost is responsible for preparing beacons and forwards them to
// `PendingBeaconService` for sending. A "beacon-sending" operation can be
// initiated from either the renderer process, or from the browser process:
//
// 1. From renderer. The `SendBeacon()` method handles beacon-sending requests
//    initiated from renderer. It can be called in one of the following
//    scenarios:
//    A. When JavaScript executes `PendingBeacon.sendNow()`, which connects to
//       receiver `Beacon::SendNow()`.
//    B. When the associated document enters `hidden` state, and the renderer's
//       `PendingBeaconDispatcher` schedules and dispatches the request
//       according to individual PendingBeacon's backgroundTimeout property.
//    C. When the individual PendingBeacon's timer of timeout property expires.
//
// 2. From browser. PendingBeaconHost can trigger the sending of beacons by
//    itself:
//    A. When the associated document is discarded or deleted, PendingBeaconHost
//       sends out all queued beacons from its destructor.
//    B. When the associated document's renderer process crashes,
//       RenderFrameHost becomes non-live and its DocumentUserData won't be
//       cleared immediately, i.e. `DeleteForCurrentDocument()` and destructor
//       won't be called, so queued beacons won't be sent there.
//       Instead, PendingBeaconHost sends them out on being notified by
//       `RenderProcessExited()`.
//    C. When the associated document enters `pagehide` state, i.e. the user has
//       navigated away from the document, PendingBeaconHost sends out all
//       queued beacons.
class CONTENT_EXPORT PendingBeaconHost
    : public blink::mojom::PendingBeaconHost,
      public DocumentUserData<PendingBeaconHost>,
      public RenderProcessHostObserver {
 public:
  ~PendingBeaconHost() override;
  PendingBeaconHost(const PendingBeaconHost&) = delete;
  PendingBeaconHost& operator=(const PendingBeaconHost&) = delete;

  // Please keep in sync with "PendingBeaconHostAction" in
  // tools/metrics/histograms/enums.xml. These values should not be renumbered.
  enum class Action {
    kNone = 0,
    // Creates a pending beacon. Initiated by renderer.
    kCreate = 1,
    // About to send a pending beacon. Initiated by renderer.
    kSend = 2,
    // Deletes a pending beacon. Initiated by renderer.
    kDelete = 3,
    // Network service is sending a pending beacon.
    kNetworkSend = 4,
    // Network service receives response for a pending beacon.
    // Note that the number of this action is not guaranteed to match the one of
    // `kNetworkSend`, as RFH may not be alive when a response is sent back.
    kNetworkComplete = 5,

    kMaxValue = kNetworkComplete,
  };
  // Please keep in sync with "PendingBeaconHostBatchAction" in
  // tools/metrics/histograms/enums.xml. These values should not be renumbered.
  enum class BatchAction {
    kNone = 0,
    // The browser process sends all beacons in this host's dtor.
    kSendAllOnHostDestroy = 1,
    // The browser process sends all beacons when a navigation happens.
    kSendAllOnNavigation = 2,
    // The browser process sends all beacons when this renderer process exits.
    kSendAllOnProcessExit = 3,

    kMaxValue = kSendAllOnProcessExit,
  };

  // Creates a new browser-side `Beacon` instance and stores it in this host.
  void CreateBeacon(mojo::PendingReceiver<blink::mojom::PendingBeacon> receiver,
                    const GURL& url,
                    blink::mojom::BeaconMethod method) override;

  // Deletes the `beacon` if exists.
  void DeleteBeacon(Beacon* beacon);
  // Sends out the `beacon` if exists.
  //
  // This method handles beacon-sending requests from the renderer. See class
  // doc for more details.
  void SendBeacon(Beacon* beacon);

  void SetReceiver(
      mojo::PendingReceiver<blink::mojom::PendingBeaconHost> receiver);

  // Forces sending out all `beacons_` on navigating away (pagehide).
  //
  // Whether or not the page is put into BackForwardCache is not relevant.
  //
  // "Unlike `SendBeacon()` which is triggered by the renderer, this method is
  // called only by the browser process itself.
  //
  // https://github.com/WICG/pending-beacon/issues/30
  void SendAllOnNavigation();

  // `RenderProcessHostObserver` implementation.
  // Sends out all queued beacons when renderer process crashes or exits.
  // See `PendingBeaconHost` 2-B for more details.
  void RenderProcessExited(RenderProcessHost*,
                           const ChildProcessTerminationInfo&) override;
  void RenderProcessHostDestroyed(RenderProcessHost*) override;

 private:
  friend class DocumentUserData<PendingBeaconHost>;

  explicit PendingBeaconHost(
      RenderFrameHost* rfh,
      scoped_refptr<network::SharedURLLoaderFactory> shared_url_factory,
      PendingBeaconService* service);

  // Sends out all stored `beacons_` and clear the references.
  void SendAll(const BatchAction& action);
  // Encapsulates how `beacons` are sent.
  void Send(const std::vector<std::unique_ptr<Beacon>>& beacons);

  // Stores all the browser-side instances of `Beacon`.
  std::vector<std::unique_ptr<Beacon>> beacons_;

  mojo::Receiver<blink::mojom::PendingBeaconHost> receiver_;

  // A SharedURLLoaderFactory owned by the owning document's StoragePartition,
  // to be used to send the document's beacons.
  scoped_refptr<network::SharedURLLoaderFactory> shared_url_factory_;

  // Service is a singleton object that has a lifetime longer than any
  // instance of PendingBeaconHost is expected to, so this pointer should be
  // safe.
  raw_ptr<PendingBeaconService> service_;

  DOCUMENT_USER_DATA_KEY_DECL();
};

// `Beacon` is the browser-side representation of a PendingBeacon.
// It is created and stored in a `PendingBeaconHost`. Hence, their lifetime is
// until they are sent, which happens in one of the following scenarios:
//   - When the PendingBeaconHost is destroyed.
//   - When the beacon's `SendNow()` method is called.
class Beacon : public blink::mojom::PendingBeacon {
 public:
  // Browser-side pending beacon constructor. Parameters correspond to the
  // renderer-side PendingBeacon class.
  // API explainer can be found at:
  // https://github.com/WICG/pending-beacon/blob/main/README.md
  Beacon(const GURL& url,
         blink::mojom::BeaconMethod method,
         PendingBeaconHost* beacon_host,
         mojo::PendingReceiver<blink::mojom::PendingBeacon> receiver);
  ~Beacon() override;

  // `blink::mojom::PendingBeacon` overrides (used by the renderer):
  // Deletes this beacon from its containing PendingBeaconHost.
  void Deactivate() override;
  // Sets request data for the pending beacon.
  //
  // It is only allowed when this beacon's `BeaconMethod` is kPost.
  // `request_body` must
  //    - Contain only single data element. Complex body is not allowed.
  //    - Contain NO `kChunkedDataPipe` data element.
  //    The above restrictions come from how PendingBeaconService handles
  //    requests.
  void SetRequestData(scoped_refptr<network::ResourceRequestBody> request_body,
                      const std::string& content_type) override;
  // Sets request url for the pending beacon.
  //
  // The spec only allows GET beacons to update its own URL. So `BeaconMethod`
  // must be kGet when calling this.
  void SetRequestURL(const GURL& url) override;
  // Sends the beacon immediately, and deletes it from its containing
  // PendingBeaconHost.
  void SendNow() override;

  // Creates a request based on the beacon's url and data.
  // * If `method_` is GET, the request url is constructed from `url_`.
  // * If `method_` is POST, the request url is from `url_`, and the request
  //   content is from `request_body_`, `content_type_`, and `request_element_`.
  const std::unique_ptr<network::ResourceRequest> GenerateResourceRequest()
      const;

  const std::string& content_type() const {
    DCHECK(method_ != blink::mojom::BeaconMethod::kGet ||
           content_type_.empty());
    return content_type_;
  }

 private:
  mojo::Receiver<blink::mojom::PendingBeacon> receiver_;

  // Points to the PendingBeaconHost that owns the instance of this beacon.
  // raw_ptr is safe here as the `beacon_host_`'s lifetime will always be longer
  // than the individual beacons it owns.
  raw_ptr<PendingBeaconHost> beacon_host_;
  // The request URL this beacon will be sent to.
  GURL url_;
  // The request method that will be used to send this beacon.
  const blink::mojom::BeaconMethod method_;

  // The request content type for POST beacon. If `method_` is GET, this field
  // should not be used.
  std::string content_type_;
  // The beacon data represented as a data element. If `method_` is GET, this
  // field should not be used.
  absl::optional<network::DataElement> request_element_;
};

}  // namespace content

#endif  // CONTENT_BROWSER_RENDERER_HOST_PENDING_BEACON_HOST_H_