blob: d1999f1a476ff469388390427d34d5f4e19c6374 [file] [log] [blame]
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef InteractiveDetector_h
#define InteractiveDetector_h
#include "base/macros.h"
#include "core/CoreExport.h"
#include "core/paint/FirstMeaningfulPaintDetector.h"
#include "platform/LongTaskDetector.h"
#include "platform/PODInterval.h"
#include "platform/Supplementable.h"
#include "platform/Timer.h"
#include "platform/heap/Handle.h"
#include "platform/wtf/Optional.h"
namespace blink {
class Document;
class WebInputEvent;
// Detects when a page reaches First Idle and Time to Interactive. See
// for detailed description and motivation of First Idle
// and Time to Interactive.
// TODO( This class currently only detects Time to
// Interactive. Implement First Idle.
class CORE_EXPORT InteractiveDetector
: public GarbageCollectedFinalized<InteractiveDetector>,
public Supplement<Document>,
public LongTaskObserver {
// This class can be easily switched out to allow better testing of
// InteractiveDetector.
class CORE_EXPORT NetworkActivityChecker {
NetworkActivityChecker(Document* document) : document_(document) {}
virtual int GetActiveConnections();
virtual ~NetworkActivityChecker() = default;
WeakPersistent<Document> document_;
static InteractiveDetector* From(Document&);
// Exposed for tests. See We must use a consistent address
// for the supplement name.
static const char* SupplementName();
virtual ~InteractiveDetector();
// Calls to CurrentTimeTicksInSeconds is expensive, so we try not to call it
// unless we really have to. If we already have the event time available, we
// pass it in as an argument.
void OnResourceLoadBegin(WTF::Optional<double> load_begin_time);
void OnResourceLoadEnd(WTF::Optional<double> load_finish_time);
void SetNavigationStartTime(double navigation_start_time);
void OnFirstMeaningfulPaintDetected(
double fmp_time,
FirstMeaningfulPaintDetector::HadUserInput user_input_before_fmp);
void OnDomContentLoadedEnd(double dcl_time);
void OnInvalidatingInputEvent(double timestamp_seconds);
void OnFirstInputDelay(double delay_seconds);
// Returns Interactive Time if already detected, or 0.0 otherwise.
double GetInteractiveTime() const;
// Returns the time when page interactive was detected. The detection time can
// be useful to make decisions about metric invalidation in scenarios like tab
// backgrounding.
double GetInteractiveDetectionTime() const;
// Returns the first time interactive detector received a significant input
// that may cause observers to discard the interactive time value.
double GetFirstInvalidatingInputTime() const;
// The duration between the hardware timestamp and being queued on the main
// thread for the first click, tap, key press, cancelable touchstart, or
// pointer down followed by a pointer up.
double GetFirstInputDelay() const;
// The timestamp of the event whose delay is reported by GetFirstInputDelay().
double GetFirstInputTimestamp() const;
// Process an input event, updating first_input_delay and
// first_input_timestamp if needed.
void HandleForFirstInputDelay(const WebInputEvent&);
virtual void Trace(Visitor*);
friend class InteractiveDetectorTest;
// Required length of main thread and network quiet window for determining
// Time to Interactive.
static constexpr double kTimeToInteractiveWindowSeconds = 5.0;
// Network is considered "quiet" if there are no more than 2 active network
// requests for this duration of time.
static constexpr int kNetworkQuietMaximumConnections = 2;
explicit InteractiveDetector(Document&, NetworkActivityChecker*);
double interactive_time_ = 0.0;
double interactive_detection_time_ = 0.0;
// Page event times that Interactive Detector depends on.
// Value of 0.0 indicate the event has not been detected yet.
struct {
double first_meaningful_paint = 0.0;
double dom_content_loaded_end = 0.0;
double nav_start = 0.0;
double first_invalidating_input = 0.0;
bool first_meaningful_paint_invalidated = false;
double first_input_delay = 0.0;
double first_input_timestamp = 0.0;
} page_event_times_;
// Stores sufficiently long quiet windows on main thread and network.
std::vector<PODInterval<double>> main_thread_quiet_windows_;
std::vector<PODInterval<double>> network_quiet_windows_;
// Start times of currently active main thread and network quiet windows.
// Values of 0.0 implies main thread or network is not quiet at the moment.
double active_main_thread_quiet_window_start_ = 0.0;
double active_network_quiet_window_start_ = 0.0;
// Adds currently active quiet main thread and network quiet windows to the
// vectors. Should be called before calling
// FindInteractiveCandidate.
void AddCurrentlyActiveQuietIntervals(double current_time);
// Undoes AddCurrentlyActiveQuietIntervals.
void RemoveCurrentlyActiveQuietIntervals();
std::unique_ptr<NetworkActivityChecker> network_activity_checker_;
int ActiveConnections();
void BeginNetworkQuietPeriod(double current_time);
void EndNetworkQuietPeriod(double current_time);
// Updates current network quietness tracking information. Opens and closes
// network quiet windows as necessary.
void UpdateNetworkQuietState(double request_count,
WTF::Optional<double> current_time);
TaskRunnerTimer<InteractiveDetector> time_to_interactive_timer_;
double time_to_interactive_timer_fire_time_ = 0.0;
void StartOrPostponeCITimer(double timer_fire_time);
void TimeToInteractiveTimerFired(TimerBase*);
void CheckTimeToInteractiveReached();
void OnTimeToInteractiveDetected();
// Finds a window of length kTimeToInteractiveWindowSeconds after lower_bound
// such that both main thread and network are quiet. Returns the end of last
// long task before that quiet window, or lower_bound, whichever is bigger -
// this is called the Interactive Candidate. Returns 0.0 if no such quiet
// window is found.
double FindInteractiveCandidate(double lower_bound);
// LongTaskObserver implementation
void OnLongTaskDetected(double start_time, double end_time) override;
// The duration between the hardware timestamp and when we received the event
// for the previous pointer down. Only non-zero if we've received a pointer
// down event, and haven't yet reported the first input delay.
double pending_pointerdown_delay_;
// The timestamp of a pending pointerdown event. Valid in the same cases as
// pending_pointerdown_delay_.
double pending_pointerdown_timestamp_;
} // namespace blink