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

content / public / browser / navigation_throttle.h [blame]

// Copyright 2015 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_BROWSER_NAVIGATION_THROTTLE_H_
#define CONTENT_PUBLIC_BROWSER_NAVIGATION_THROTTLE_H_

#include <optional>

#include "base/functional/callback.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/safety_checks.h"
#include "content/common/content_export.h"
#include "net/base/net_errors.h"

namespace content {
class NavigationHandle;

// A NavigationThrottle tracks and allows interaction with a navigation on the
// UI thread. NavigationThrottles may not be run for some kinds of navigations
// (e.g. same-document navigations, about:blank, activations into the primary
// frame tree like prerendering and back-forward cache, etc.). Content-internal
// code that just wishes to defer a commit, including activations to the
// primary frame tree, should instead use a CommitDeferringCondition.
class CONTENT_EXPORT NavigationThrottle {
  // Do not remove this macro!
  // The macro is maintained by the memory safety team.
  ADVANCED_MEMORY_SAFETY_CHECKS();

 public:
  // Represents what a NavigationThrottle can decide to do to a navigation. Note
  // that this enum is implicitly convertable to ThrottleCheckResult.
  enum ThrottleAction {
    FIRST = 0,

    // The action proceeds. This can either mean the navigation continues (e.g.
    // for WillStartRequest) or that the navigation fails (e.g. for
    // WillFailRequest).
    PROCEED = FIRST,

    // Defers the navigation until the NavigationThrottle calls
    // NavigationHandle::Resume or NavigationHandle::CancelDeferredRequest. If
    // the NavigationHandle is destroyed while the navigation is deferred, the
    // navigation will be canceled in the network stack.
    // Note: since this slows page load it should be avoided unless there's no
    // other option. An example necessary case would be locked down users where
    // a server check needs to be done before starting the navigation. For other
    // cases, please consider alternatives like sending data to the renderer
    // asynchronously, showing interstitials later when possible etc. It's good
    // practice to add histograms to know how long the delay takes.
    DEFER,

    // Cancels the navigation.
    CANCEL,

    // Cancels the navigation and makes the requester of the navigation act
    // like the request was never made.
    CANCEL_AND_IGNORE,

    // Blocks a navigation due to rules asserted before the request is made.
    // This can only be returned from WillStartRequest or WillRedirectRequest.
    // This will result in a default net_error code of
    // net::ERR_BLOCKED_BY_CLIENT being loaded in the frame that is navigated.
    BLOCK_REQUEST,

    // Blocks a navigation taking place in a subframe, and collapses the frame
    // owner element in the parent document (i.e. removes it from the layout).
    // This can only be returned from WillStartRequest or WillRedirectRequest.
    BLOCK_REQUEST_AND_COLLAPSE,

    // Blocks a navigation due to rules asserted by a response (for instance,
    // embedding restrictions like 'X-Frame-Options'). This result will only
    // be returned from WillProcessResponse.
    BLOCK_RESPONSE,

    LAST = BLOCK_RESPONSE,
  };

  // ThrottleCheckResult, the return value for NavigationThrottle decision
  // methods, is a ThrottleAction value with an attached net::Error and an
  // optional attached error page HTML string.
  //
  // ThrottleCheckResult is implicitly convertible from ThrottleAction, allowing
  // the following examples to work:
  //
  //   ThrottleCheckResult WillStartRequest() override {
  //      // Uses default error for PROCEED (net::OK).
  //      return PROCEED;
  //   }
  //
  //   ThrottleCheckResult WillStartRequest() override {
  //      // Uses default error for BLOCK_REQUEST (net::ERR_BLOCKED_BY_CLIENT).
  //      return BLOCK_REQUEST;
  //   }
  //
  //   ThrottleCheckResult WillStartRequest() override {
  //      // Identical to previous example (net::ERR_BLOCKED_BY_CLIENT)
  //      return {BLOCK_REQUEST};
  //   }
  //
  //   ThrottleCheckResult WillStartRequest() override {
  //      // Uses a custom error code of ERR_FILE_NOT_FOUND.
  //      return {BLOCK_REQUEST, net::ERR_FILE_NOT_FOUND};
  //   }
  //
  //   ThrottleCheckResult WillStartRequest() override {
  //      // Uses a custom error code of ERR_FILE_NOT_FOUND and an error page
  //      string.
  //      return {BLOCK_REQUEST,
  //              net::ERR_FILE_NOT_FOUND,
  //              std::string("<html><body>Could not find.</body></html>")};
  //   }
  class CONTENT_EXPORT ThrottleCheckResult {
   public:
    // Construct with just a ThrottleAction, using the default net::Error for
    // that action.
    ThrottleCheckResult(ThrottleAction action);

    // Construct with an action and error.
    ThrottleCheckResult(ThrottleAction action, net::Error net_error_code);

    // Construct with an action, error, and error page HTML.
    ThrottleCheckResult(ThrottleAction action,
                        net::Error net_error_code,
                        std::optional<std::string> error_page_content);

    ThrottleCheckResult(const ThrottleCheckResult& other);

    ~ThrottleCheckResult();

    ThrottleAction action() const { return action_; }
    net::Error net_error_code() const { return net_error_code_; }
    const std::optional<std::string>& error_page_content() {
      return error_page_content_;
    }

   private:
    ThrottleAction action_;
    net::Error net_error_code_;
    std::optional<std::string> error_page_content_;
  };

  NavigationThrottle(NavigationHandle* navigation_handle);
  virtual ~NavigationThrottle();

  // Called when a network request is about to be made for this navigation.
  //
  // The implementer is responsible for ensuring that the WebContents this
  // throttle is associated with remain alive during the duration of this
  // method. Failing to do so will result in use-after-free bugs. Should the
  // implementer need to destroy the WebContents, it should return CANCEL,
  // CANCEL_AND_IGNORE or DEFER and perform the destruction asynchronously.
  virtual ThrottleCheckResult WillStartRequest();

  // Called when a server redirect is received by the navigation.
  //
  // The implementer is responsible for ensuring that the WebContents this
  // throttle is associated with remain alive during the duration of this
  // method. Failing to do so will result in use-after-free bugs. Should the
  // implementer need to destroy the WebContents, it should return CANCEL,
  // CANCEL_AND_IGNORE or DEFER and perform the destruction asynchronously.
  virtual ThrottleCheckResult WillRedirectRequest();

  // Called when a request will fail.
  //
  // The implementer is responsible for ensuring that the WebContents this
  // throttle is associated with remain alive during the duration of this
  // method. Failing to do so will result in use-after-free bugs. Should the
  // implementer need to destroy the WebContents, it should return CANCEL,
  // CANCEL_AND_IGNORE or DEFER and perform the destruction asynchronously.
  virtual ThrottleCheckResult WillFailRequest();

  // Called when a response's metadata is available.
  //
  // For HTTP(S) responses, headers will be available.
  // The implementer is responsible for ensuring that the WebContents this
  // throttle is associated with remain alive during the duration of this
  // method. Failing to do so will result in use-after-free bugs. Should the
  // implementer need to destroy the WebContents, it should return CANCEL,
  // CANCEL_AND_IGNORE, or BLOCK_RESPONSE and perform the destruction
  // asynchronously.
  virtual ThrottleCheckResult WillProcessResponse();

  // Called when a navigation is about to immediately commit because there's no
  // need for a url loader. This includes browser-initiated same-document
  // navigations, same-document history navigations, about:blank, about:srcdoc,
  // any other empty document scheme, and MHTML subframes.
  // Renderer-initiated non-history same-document navigations do NOT go through
  // this path, because they are handled synchronously in the renderer and the
  // browser process is only notified after the fact.
  // BFCache and prerender activation also do NOT go through this path, because
  // they are considered already loaded when they are activated.
  // In order to get this event, a NavigationThrottle must register itself with
  // RegisterNavigationThrottlesForCommitWithoutUrlLoader().
  // This event is mutually exclusive with WillStartRequest,
  // WillRedirectRequest, and WillProcessResponse. Only WillFailRequest can
  // be called after WillCommitWithoutUrlLoader.
  // Only PROCEED, DEFER, and CANCEL_AND_IGNORE results are supported at this
  // time.
  virtual ThrottleCheckResult WillCommitWithoutUrlLoader();

  // Returns the name of the throttle for logging purposes. It must not return
  // nullptr.
  virtual const char* GetNameForLogging() = 0;

  // The NavigationHandle that is tracking the information related to this
  // navigation.
  NavigationHandle* navigation_handle() const { return navigation_handle_; }

  // Overrides the default Resume method and replaces it by |callback|. This
  // should only be used in tests.
  void set_resume_callback_for_testing(const base::RepeatingClosure& callback) {
    resume_callback_ = callback;
  }

  // Overrides the default CancelDeferredNavigation method and replaces it by
  // |callback|. This should only be used in tests.
  void set_cancel_deferred_navigation_callback_for_testing(
      const base::RepeatingCallback<void(ThrottleCheckResult)> callback) {
    cancel_deferred_navigation_callback_ = callback;
  }

 protected:
  // Resumes a navigation that was previously deferred by this
  // NavigationThrottle.
  // Note: this may lead to the deletion of the NavigationHandle and its
  // associated NavigationThrottles, including this one.
  virtual void Resume();

  // Cancels a navigation that was previously deferred by this
  // NavigationThrottle. |result|'s action should be equal to either:
  //  - NavigationThrottle::CANCEL,
  //  - NavigationThrottle::CANCEL_AND_IGNORE, or
  //  - NavigationThrottle::BLOCK_REQUEST_AND_COLLAPSE.
  // Note: this may lead to the deletion of the NavigationHandle and its
  // associated NavigationThrottles, including this one.
  virtual void CancelDeferredNavigation(ThrottleCheckResult result);

 private:
  const raw_ptr<NavigationHandle> navigation_handle_;

  // Used in tests.
  base::RepeatingClosure resume_callback_;
  base::RepeatingCallback<void(ThrottleCheckResult)>
      cancel_deferred_navigation_callback_;
};

#if defined(UNIT_TEST)
// Test-only operator== to enable assertions like:
//   EXPECT_EQ(NavigationThrottle::PROCEED, throttle->WillProcessResponse())
inline bool operator==(NavigationThrottle::ThrottleAction lhs,
                       const NavigationThrottle::ThrottleCheckResult& rhs) {
  return lhs == rhs.action();
}
// Test-only operator!= to enable assertions like:
//   EXPECT_NE(NavigationThrottle::PROCEED, throttle->WillProcessResponse())
inline bool operator!=(NavigationThrottle::ThrottleAction lhs,
                       const NavigationThrottle::ThrottleCheckResult& rhs) {
  return lhs != rhs.action();
}
#endif

}  // namespace content

#endif  // CONTENT_PUBLIC_BROWSER_NAVIGATION_THROTTLE_H_