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

content / browser / preloading / preloading_attempt_impl.h [blame]

// 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_PRELOADING_PRELOADING_ATTEMPT_IMPL_H_
#define CONTENT_BROWSER_PRELOADING_PRELOADING_ATTEMPT_IMPL_H_

#include <optional>
#include <vector>

#include "base/timer/elapsed_timer.h"
#include "content/public/browser/preloading.h"
#include "content/public/browser/preloading_data.h"
#include "services/metrics/public/cpp/ukm_source_id.h"
#include "third_party/blink/public/mojom/speculation_rules/speculation_rules.mojom-shared.h"
#include "url/gurl.h"

namespace content {

namespace test {
class PreloadingAttemptAccessor;
}

class CONTENT_EXPORT PreloadingAttemptImpl : public PreloadingAttempt {
 public:
  ~PreloadingAttemptImpl() override;

  // PreloadingAttempt implementation:
  void SetEligibility(PreloadingEligibility eligibility) override;
  void SetHoldbackStatus(PreloadingHoldbackStatus holdback_status) override;
  bool ShouldHoldback() override;
  void SetTriggeringOutcome(
      PreloadingTriggeringOutcome triggering_outcome) override;
  void SetFailureReason(PreloadingFailureReason reason) override;
  base::WeakPtr<PreloadingAttempt> GetWeakPtr() override;

  // Records UKMs for both Preloading_Attempt and
  // Preloading_Attempt_PreviousPrimaryPage. Metrics for both these are same.
  // Only difference is that the Preloading_Attempt_PreviousPrimaryPage UKM is
  // associated with the WebContents primary page that triggered the preloading
  // attempt. This is done to easily analyze the impact of the preloading
  // attempt on the primary visible page. Here `navigated_page` represent the
  // ukm::SourceId of the navigated page. If the navigation doesn't happen this
  // could be invalid. This must be called after the page load ends and we know
  // if the attempt was accurate.
  void RecordPreloadingAttemptMetrics(ukm::SourceId navigated_page);

  void SetNoVarySearchMatchPredicate(
      PreloadingURLMatchCallback no_vary_search_match_predicate);

  // Sets `is_accurate_triggering_` to true if `navigated_url` matches the
  // predicate URL logic. It also records `time_to_next_navigation_`.
  void SetIsAccurateTriggering(const GURL& navigated_url);

  bool IsAccurateTriggering() const { return is_accurate_triggering_; }

  PreloadingAttemptImpl(
      const PreloadingPredictor& creating_predictor,
      const PreloadingPredictor& enacting_predictor,
      PreloadingType preloading_type,
      ukm::SourceId triggered_primary_page_source_id,
      PreloadingURLMatchCallback url_match_predicate,
      std::optional<PreloadingType> planned_max_preloading_type,
      uint32_t sampling_seed);

  std::vector<PreloadingPredictor> GetPredictors() const;

  PreloadingType preloading_type() const { return preloading_type_; }
  PreloadingType planned_max_preloading_type() const {
    return planned_max_preloading_type_;
  }

  void SetSpeculationEagerness(blink::mojom::SpeculationEagerness eagerness);

  // Describes what type of checks we had to do to identify if the attempt's
  // URL is or is not under a Service Worker.
  enum class ServiceWorkerRegisteredCheck {
    // These values are persisted to logs. Entries should not be renumbered and
    // numeric values should never be reused.

    // The origin doesn't have any Service Workers registered.
    kOriginOnly = 0,
    // The origin has at least one Service Worker registered and we had to
    // perform a path test to identify if the attempt's URL is under a
    // registered Service Worker.
    kPath = 1,
    kMaxValue = kPath
  };
  static constexpr double kServiceWorkerRegisteredCheckDurationBucketSpacing =
      1.15;
  void SetServiceWorkerRegisteredCheck(ServiceWorkerRegisteredCheck check);
  void SetServiceWorkerRegisteredCheckDuration(base::TimeDelta duration);

 private:
  friend class test::PreloadingAttemptAccessor;

  void RecordPreloadingAttemptUMA();

  // Reason why the preloading attempt failed, this is similar to specific
  // preloading logging reason. Zero as a failure reason signifies no reason is
  // specified. This value is casted from preloading specific enum to int64_t
  // instead of having an enum declaration for each case.
  PreloadingFailureReason failure_reason_ =
      PreloadingFailureReason::kUnspecified;

  // Specifies the eligibility status for this PreloadingAttempt.
  PreloadingEligibility eligibility_ = PreloadingEligibility::kUnspecified;

  PreloadingHoldbackStatus holdback_status_ =
      PreloadingHoldbackStatus::kUnspecified;

  // Specifies the triggering outcome for this PreloadingAttempt.
  PreloadingTriggeringOutcome triggering_outcome_ =
      PreloadingTriggeringOutcome::kUnspecified;

  // Preloading predictor of this PreloadingAttempt.
  const PreloadingPredictor creating_predictor_;
  const PreloadingPredictor enacting_predictor_;

  // PreloadingType this attempt is associated with.
  const PreloadingType preloading_type_;

  // Holds the ukm::SourceId of the triggered primary page of this preloading
  // attempt.
  const ukm::SourceId triggered_primary_page_source_id_;

  // Triggers can specify their own predicate for judging whether two URLs are
  // considered as pointing to the same destination.
  const PreloadingURLMatchCallback url_match_predicate_;

  // Set when a predicted page provides No-Vary-Search header.
  PreloadingURLMatchCallback no_vary_search_match_predicate_;

  // Max PreloadingType that this attempt can be upgraded to in the future.
  const PreloadingType planned_max_preloading_type_;

  // Set to true if this PreloadingAttempt was used for the next navigation.
  bool is_accurate_triggering_ = false;

  // Records when the preloading attempt began, for computing times.
  const base::ElapsedTimer elapsed_timer_;

  // The time between the creation of the attempt and the start of the next
  // navigation, whether accurate or not. The latency is reported as standard
  // buckets, of 1.15 spacing.
  std::optional<base::TimeDelta> time_to_next_navigation_;

  // Represents the duration between the attempt creation and its
  // `triggering_outcome_` becoming `kReady`. The latency is reported as
  // standard buckets, of 1.15 spacing.
  std::optional<base::TimeDelta> ready_time_;

  // The random seed used to determine if a preloading attempt should be sampled
  // in UKM logs. We use a different random seed for each session (that is the
  // source of randomness for sampling) and then hash that seed with the UKM
  // source ID so that all attempts for a given source ID use the same random
  // value to determine sampling. This allows all PreloadingAttempt for a given
  // (preloading_type, predictor) in a page load to be sampled in or out
  // together.
  uint32_t sampling_seed_;

  // Eagerness of this preloading attempt (specified by a speculation rule).
  // This is only set for attempts that are triggered by speculation rules.
  std::optional<blink::mojom::SpeculationEagerness> eagerness_ = std::nullopt;

  // Describes the type of check we did for to find out if the attempt's URL
  // is under a Service Worker's path. The simplest check is: does the URL's
  // origin have any registered service workers or not, the more complicated
  // check is: given the URL's origin has service workers registered, is the
  // URL under one of these Service Workers.
  // This is only set for prefetch attempts that are triggered by speculation
  // rules.
  std::optional<ServiceWorkerRegisteredCheck> service_worker_registered_check_ =
      std::nullopt;
  std::optional<base::TimeDelta> service_worker_registered_check_duration_ =
      std::nullopt;

  base::WeakPtrFactory<PreloadingAttemptImpl> weak_factory_{this};
};

// Used when DCHECK_STATE_TRANSITION triggers.
CONTENT_EXPORT std::ostream& operator<<(std::ostream& o,
                                        const PreloadingTriggeringOutcome& s);

}  // namespace content

#endif  // CONTENT_BROWSER_PRELOADING_PRELOADING_ATTEMPT_IMPL_H_