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
  249
  250
  251
  252
  253
  254
  255
  256
  257
  258
  259
  260
  261
  262
  263
  264
  265
  266
  267
  268
  269
  270
  271
  272
  273
  274
  275
  276
  277
  278
  279
  280
  281
  282
  283
  284
  285
  286
  287
  288
  289
  290
  291
  292
  293
  294
  295
  296
  297
  298
  299
  300
  301
  302
  303
  304
  305
  306
  307
  308
  309
  310
  311
  312
  313
  314
  315
  316
  317
  318
  319
  320
  321
  322
  323
  324
  325
  326
  327
  328
  329
  330
  331
  332
  333
  334
  335
  336
  337
  338
  339
  340
  341
  342
  343
  344
  345
  346
  347
  348
  349
  350
  351
  352
  353
  354
  355
  356
  357
  358
  359
  360
  361
  362
  363
  364
  365
  366
  367
  368
  369
  370
  371
  372
  373
  374
  375
  376
  377
  378
  379
  380
  381
  382
  383
  384
  385
  386
  387
  388
  389
  390
  391
  392
  393
  394
  395
  396
  397
  398
  399
  400
  401
  402
  403
  404
  405
  406
  407
  408
  409
  410
  411
  412
  413
  414
  415
  416
  417
  418
  419
  420

content / public / renderer / render_frame_observer.h [blame]

// Copyright 2013 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_PUBLIC_RENDERER_RENDER_FRAME_OBSERVER_H_
#define CONTENT_PUBLIC_RENDERER_RENDER_FRAME_OBSERVER_H_

#include <stdint.h>

#include <optional>
#include <string>

#include "base/memory/raw_ptr.h"
#include "base/memory/read_only_shared_memory_region.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "content/common/buildflags.h"
#include "content/common/content_export.h"
#include "ipc/ipc_listener.h"
#include "ipc/ipc_sender.h"
#include "mojo/public/cpp/bindings/scoped_interface_endpoint_handle.h"
#include "mojo/public/cpp/system/message_pipe.h"
#include "services/network/public/mojom/url_response_head.mojom-forward.h"
#include "third_party/blink/public/common/loader/loading_behavior_flag.h"
#include "third_party/blink/public/common/performance/performance_timeline_constants.h"
#include "third_party/blink/public/common/responsiveness_metrics/user_interaction_latency.h"
#include "third_party/blink/public/common/subresource_load_metrics.h"
#include "third_party/blink/public/common/tokens/tokens.h"
#include "third_party/blink/public/common/use_counter/use_counter_feature.h"
#include "third_party/blink/public/mojom/devtools/console_message.mojom-shared.h"
#include "third_party/blink/public/mojom/frame/lifecycle.mojom.h"
#include "third_party/blink/public/mojom/loader/resource_load_info.mojom-shared.h"
#include "third_party/blink/public/platform/web_vector.h"
#include "third_party/blink/public/web/web_meaningful_layout.h"
#include "third_party/blink/public/web/web_navigation_type.h"
#include "ui/accessibility/ax_mode.h"
#include "ui/base/page_transition_types.h"
#include "v8/include/v8-forward.h"

class GURL;

namespace blink {
class WebDocumentLoader;
class WebElement;
class WebFormElement;
class WebString;
class WebURLRequest;
class WebWorkerFetchContext;
enum class DetachReason;
struct JavaScriptFrameworkDetectionResult;
}  // namespace blink

namespace gfx {
class Rect;
}  // namespace gfx

namespace network {
struct URLLoaderCompletionStatus;
}  // namespace network

namespace url {
class SchemeHostPort;
}  // namespace url

namespace content {

class RendererPpapiHost;
class RenderFrame;

// Base class for objects that want to filter incoming IPCs, and also get
// notified of changes to the frame.
class CONTENT_EXPORT RenderFrameObserver
#if BUILDFLAG(CONTENT_ENABLE_LEGACY_IPC)
    : public IPC::Listener,
      public IPC::Sender
#endif
{
 public:
  RenderFrameObserver(const RenderFrameObserver&) = delete;
  RenderFrameObserver& operator=(const RenderFrameObserver&) = delete;

  // A subclass can use this to delete itself. If it does not, the subclass must
  // always null-check each call to render_frame() because the RenderFrame can
  // go away at any time.
  virtual void OnDestruct() = 0;

  // Called when a Pepper plugin is created.
  virtual void DidCreatePepperPlugin(RendererPpapiHost* host) {}

  // Called when a load is explicitly stopped by the user or browser.
  virtual void OnStop() {}

  // Called when the RenderFrame visiblity is changed.
  virtual void WasHidden() {}
  virtual void WasShown() {}

  // Called when the RenderFrame's visibility status changes.
  virtual void OnFrameVisibilityChanged(
      blink::mojom::FrameVisibility render_status) {}

  // Navigation callbacks.
  //
  // Each navigation starts with a DidStartNavigation call. Then it may be
  // followed by a ReadyToCommitNavigation (if the navigation has succeeded),
  // and should always end with a DidFinishNavigation.
  // TODO(dgozman): ReadyToCommitNavigation will be removed soon.
  //
  // Unfortunately, this is currently a mess. For example, some started
  // navigations which did not commit won't receive any further notifications.
  // DidCommitProvisionalLoad will be called for same-document navigations,
  // without any other notifications. DidFailProvisionalLoad will be called
  // when committing error pages, in addition to all the methods (start, ready,
  // commit) for the error page load itself.

  // Called when the RenderFrame has started a navigation.
  // |url| is a url being navigated to. Note that final url might be different
  // due to redirects.
  // |navigation_type| is only present for renderer-initiated navigations, e.g.
  // JavaScript call, link click, form submit. User-initiated navigations from
  // the browser process (e.g. by typing a url) won't have a navigation type.
  virtual void DidStartNavigation(
      const GURL& url,
      std::optional<blink::WebNavigationType> navigation_type) {}

  // Called when a navigation has just committed and |document_loader|
  // will start loading a new document in the RenderFrame.
  // TODO(dgozman): the name does not match functionality anymore, we should
  // merge this with DidCommitProvisionalLoad, which will become
  // DidFinishNavigation.
  virtual void ReadyToCommitNavigation(
      blink::WebDocumentLoader* document_loader) {}

  // Called when a RenderFrame's page lifecycle state gets updated.
  virtual void DidSetPageLifecycleState(bool restoring_from_bfcache) {}

  // These match the Blink API notifications. These will not be called for the
  // initial empty document, since that already exists before an observer for a
  // frame has a chance to be created (before notification about the RenderFrame
  // being created occurs).
  virtual void DidCreateNewDocument() {}
  virtual void DidCreateDocumentElement() {}
  // TODO(dgozman): replace next two methods with DidFinishNavigation.
  // DidCommitProvisionalLoad is only called for new-document navigations.
  // Use DidFinishSameDocumentNavigation for same-document navigations.
  virtual void DidCommitProvisionalLoad(ui::PageTransition transition) {}
  virtual void DidFailProvisionalLoad() {}
  virtual void DidFinishLoad() {}
  virtual void DidFinishLoadForPrinting() {}
  virtual void DidDispatchDOMContentLoadedEvent() {}
  virtual void DidHandleOnloadEvents() {}
  virtual void DidCreateScriptContext(v8::Local<v8::Context> context,
                                      int32_t world_id) {}
  virtual void WillReleaseScriptContext(v8::Local<v8::Context> context,
                                        int32_t world_id) {}
  virtual void DidClearWindowObject() {}
  virtual void DidChangeScrollOffset() {}
  virtual void WillSubmitForm(const blink::WebFormElement& form) {}
  virtual void DidMatchCSS(
      const blink::WebVector<blink::WebString>& newly_matching_selectors,
      const blink::WebVector<blink::WebString>& stopped_matching_selectors) {}

  // Called when the RenderFrame creates a FencedFrame and provides the
  // RemoteFrameToken to identify the `blink::RemoteFrame` to the inner
  // RenderFrame. This is called immediately after the FencedFrame is created
  // in the browser and the `blink::RemoteFrame` initialized in this renderer.
  virtual void DidCreateFencedFrame(
      const blink::RemoteFrameToken& placeholder_token) {}

  // Called when same-document navigation finishes.
  // This is the only callback for same-document navigations,
  // DidStartNavigation and ReadyToCommitNavigation are not called.
  //
  // Same-document navigation is typically initiated by an anchor click
  // (that usually results in the page scrolling to the anchor) or a
  // history web API manipulation.
  //
  // However, it could be some rare case like user changing #hash in the url
  // bar or history restore for subframe or anything else that was classified
  // as same-document.
  virtual void DidFinishSameDocumentNavigation() {}

  // Called when this RenderFrame has been detached from the view.  Note that
  // this refers to the detachment of the RenderFrame object, not the "browsing
  // context". This means that WillDetach can fire as a result of navigating
  // within the same browsingcontext that creates a new RenderFrame (either in
  // this process or a different process), on top of being fired when the
  // browsing context is actually detached (including when the parent
  // RenderFrame is being detached). See comments for blink::DetachReason for
  // more details.
  virtual void WillDetach(blink::DetachReason detach_reason) {}

  // Called when we receive a console message from Blink for which we requested
  // extra details (like the stack trace). |message| is the error message,
  // |source| is the Blink-reported source of the error (either external or
  // internal), and |stack_trace| is the stack trace of the error in a
  // human-readable format (each frame is formatted as
  // "\n    at function_name (source:line_number:column_number)").
  virtual void DetailedConsoleMessageAdded(
      const std::u16string& message,
      const std::u16string& source,
      const std::u16string& stack_trace,
      uint32_t line_number,
      blink::mojom::ConsoleMessageLevel level) {}

  // Called when an interesting (from document lifecycle perspective),
  // compositor-driven layout had happened. This is a reasonable hook to use
  // to inspect the document and layout information, since it is in a clean
  // state and you won't accidentally force new layouts.
  // The interestingness of layouts is explained in WebMeaningfulLayout.h.
  virtual void DidMeaningfulLayout(blink::WebMeaningfulLayout layout_type) {}

  // Notifications when |PerformanceTiming| data becomes available
  virtual void DidChangePerformanceTiming() {}

  // Notifications when a user interaction latency data becomes available. A
  // user interaction can be built up from multiple input events (e.g. keydown
  // then keyup). Each of these events has an input to next frame latency. This
  // reports the timings of the max input-to-frame latency for each interaction.
  // `max_event_start` is when input was received, `max_event_end` is when
  // the next frame was presented, `max_event_queued_main_thread` is when the
  // input was queued and `max_event_commit_finish` is when the next commit
  // finished after event has been processed. See
  // https://web.dev/inp/#whats-in-an-interaction for more detailed motivation
  // and explanation.
  virtual void DidObserveUserInteraction(
      base::TimeTicks max_event_start,
      base::TimeTicks max_event_queued_main_thread,
      base::TimeTicks max_event_commit_finish,
      base::TimeTicks max_event_end,
      blink::UserInteractionType interaction_type,
      uint64_t interaction_offset) {}

  // Notification when the First Scroll Delay becomes available.
  virtual void DidObserveFirstScrollDelay(base::TimeDelta first_scroll_delay) {}

  // Notifications when a cpu timing update becomes available, when a frame
  // has performed at least 100ms of tasks.
  virtual void DidChangeCpuTiming(base::TimeDelta time) {}

  // Notification when the renderer uses a particular code path during a page
  // load. This is used for metrics collection.
  virtual void DidObserveLoadingBehavior(blink::LoadingBehaviorFlag behavior) {}

  // Notification when the renderer performed framework detection during a page
  // load. This is used for metrics collection.
  virtual void DidObserveJavaScriptFrameworks(
      const blink::JavaScriptFrameworkDetectionResult&) {}

  // Notification when the renderer uses subresources.
  // It is called when there is a subresouce load. The reported values via
  // arguments are cumulative. They are NOT a difference from the previous call.
  virtual void DidObserveSubresourceLoad(
      const blink::SubresourceLoadMetrics& subresource_load_metrics) {}

  // Notification when the renderer observes a new use counter usage during a
  // page load. This is used for UseCounter metrics.
  virtual void DidObserveNewFeatureUsage(
      const blink::UseCounterFeature& feature) {}

  // A new soft navigation was observed.
  // A soft navigation is:
  // - A same-document navigation in the top-level document.
  // - Triggered with a user gesture.
  // - Initiated with the window.history or window.navigation APIs.
  // - Accompanied with a DOM modification of the <main> element during the same
  // or a descendant task.
  virtual void DidObserveSoftNavigation(blink::SoftNavigationMetrics metrics) {}

  // Reports that visible elements in the frame shifted (bit.ly/lsm-explainer).
  // This is called once for each animation frame containing any layout shift,
  // and receives the layout shift (LS) score for that frame.  The cumulative
  // layout shift (CLS) score can be inferred by summing the LS scores.
  // |after_input_or_scroll| indicates whether the given |score| was observed
  // after an input or scroll occurred in the associated document.
  virtual void DidObserveLayoutShift(double score, bool after_input_or_scroll) {
  }

  // Reports that a resource will be requested.
  virtual void WillSendRequest(const blink::WebURLRequest& request) {}

  // Notification when the renderer a response started, completed or canceled.
  // Complete or Cancel is guaranteed to be called for a response that started.
  // |request_id| uniquely identifies the request within this render frame.
  virtual void DidStartResponse(
      const url::SchemeHostPort& final_response_url,
      int request_id,
      const network::mojom::URLResponseHead& response_head,
      network::mojom::RequestDestination request_destination,
      bool is_ad_resource) {}
  virtual void DidCompleteResponse(
      int request_id,
      const network::URLLoaderCompletionStatus& status) {}
  virtual void DidCancelResponse(int request_id) {}

  // Reports that a resource was loaded from the blink memory cache.
  // |request_id| uniquely identifies this resource within this render frame.
  // |from_archive| indicates if the resource originated from a MHTML archive.
  virtual void DidLoadResourceFromMemoryCache(const GURL& response_url,
                                              int request_id,
                                              int64_t encoded_body_length,
                                              const std::string& mime_type,
                                              bool from_archive) {}

  // Notification when the renderer observes data used during the page load.
  // This is used for page load metrics. |received_data_length| is the received
  // network bytes. |resource_id| uniquely identifies the resource within this
  // render frame.
  virtual void DidReceiveTransferSizeUpdate(int resource_id,
                                            int received_data_length) {}

  // Called when the focused element has changed to |element|.
  virtual void FocusedElementChanged(const blink::WebElement& element) {}

  // Called when accessibility is enabled or disabled.
  virtual void AccessibilityModeChanged(const ui::AXMode& mode) {}

  // Called when script in the page calls window.print().
  virtual void ScriptedPrint(bool user_initiated) {}

  // Called when a worker fetch context will be created.
  virtual void WillCreateWorkerFetchContext(blink::WebWorkerFetchContext*) {}

  // For the main frame, called when the main frame's dimensions have changed,
  // e.g. resizing a tab causes the document width to change; loading additional
  // content causes the document height to increase; explicitly changing the
  // height of the body element.
  //
  // For a subframe, called when the intersection rect between the main frame
  // and the subframe has changed, e.g. the subframe is initially added; the
  // subframe's position is updated explicitly or inherently (e.g. sticky
  // position while the page is being scrolled).
  virtual void OnMainFrameIntersectionChanged(
      const gfx::Rect& main_frame_intersection_rect) {}

  // Called when the main frame's viewport rectangle (the viewport dimensions
  // and the scroll position) changed, e.g. the user scrolled the main frame or
  // the viewport dimensions themselves changed. Only invoked on the main frame.
  virtual void OnMainFrameViewportRectangleChanged(
      const gfx::Rect& main_frame_viewport_rect) {}

  // Called when an image ad rectangle changed. An empty `image_ad_rect` is used
  // to signal the removal of the rectangle. Only invoked on the main frame.
  virtual void OnMainFrameImageAdRectangleChanged(
      int element_id,
      const gfx::Rect& image_ad_rect) {}

  // Overlay-popup-ad violates The Better Ads Standards
  // (https://www.betterads.org/standards/). This method will be called when an
  // overlay-popup-ad is detected, to let the embedder
  // (i.e. subresource_filter::ContentSubresourceFilterThrottleManager) know the
  // violation so as to apply further interventions.
  virtual void OnOverlayPopupAdDetected() {}

  // Large-sticky-ad violates The Better Ads Standards
  // (https://www.betterads.org/standards/). This method will be called when a
  // large-sticky-ad is detected, to let the embedder
  // (i.e. subresource_filter::ContentSubresourceFilterThrottleManager) know the
  // violation so as to apply further interventions.
  virtual void OnLargeStickyAdDetected() {}

  // Called to give the embedder an opportunity to bind an interface request
  // for a frame. If the request can be bound, |interface_pipe| will be taken.
  virtual void OnInterfaceRequestForFrame(
      const std::string& interface_name,
      mojo::ScopedMessagePipeHandle* interface_pipe) {}

  // Similar to above but for handling Channel-associated interface requests.
  // Returns |true| if the request is handled by the implementation (taking
  // ownership of |*handle|) and |false| otherwise (leaving |*handle|
  // unmodified).
  virtual bool OnAssociatedInterfaceRequestForFrame(
      const std::string& interface_name,
      mojo::ScopedInterfaceEndpointHandle* handle);

  // The smoothness metrics is shared over shared-memory. The interested
  // observer should invalidate |shared_memory| (by std::move()'ing it), and
  // return true. All other observers should return false (default).
  virtual bool SetUpSmoothnessReporting(
      base::ReadOnlySharedMemoryRegion& shared_memory);

#if BUILDFLAG(CONTENT_ENABLE_LEGACY_IPC)
  // IPC::Listener implementation.
  bool OnMessageReceived(const IPC::Message& message) override;

  // IPC::Sender implementation.
  bool Send(IPC::Message* message) override;
#endif

  RenderFrame* render_frame() const;

#if BUILDFLAG(CONTENT_ENABLE_LEGACY_IPC)
  int routing_id() const { return routing_id_; }
#endif

 protected:
  explicit RenderFrameObserver(RenderFrame* render_frame);
#if BUILDFLAG(CONTENT_ENABLE_LEGACY_IPC)
  ~RenderFrameObserver() override;
#else
  virtual ~RenderFrameObserver();
#endif

 private:
  friend class RenderFrameImpl;

  // This is called by the RenderFrame when it's going away so that this object
  // can null out its pointer.
  void RenderFrameGone();

  raw_ptr<RenderFrame> render_frame_;

#if BUILDFLAG(CONTENT_ENABLE_LEGACY_IPC)
  // The routing ID of the associated RenderFrame.
  int routing_id_ = MSG_ROUTING_NONE;
#endif
};

}  // namespace content

#endif  // CONTENT_PUBLIC_RENDERER_RENDER_FRAME_OBSERVER_H_