blob: 9ec100cecb3664a7c7eec7dec087592f1e6d6c91 [file] [log] [blame]
// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LOADER_INTERACTIVE_DETECTOR_H_
#define THIRD_PARTY_BLINK_RENDERER_CORE_LOADER_INTERACTIVE_DETECTOR_H_
#include <optional>
#include "base/task/single_thread_task_runner.h"
#include "base/time/time.h"
#include "third_party/blink/renderer/core/core_export.h"
#include "third_party/blink/renderer/core/execution_context/execution_context_lifecycle_observer.h"
#include "third_party/blink/renderer/core/loader/long_task_detector.h"
#include "third_party/blink/renderer/core/page/page_hidden_state.h"
#include "third_party/blink/renderer/platform/heap/garbage_collected.h"
#include "third_party/blink/renderer/platform/heap/persistent.h"
#include "third_party/blink/renderer/platform/instrumentation/tracing/traced_value.h"
#include "third_party/blink/renderer/platform/supplementable.h"
#include "third_party/blink/renderer/platform/timer.h"
#include "third_party/blink/renderer/platform/wtf/pod_interval.h"
#include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
#include "third_party/blink/renderer/platform/wtf/vector.h"
namespace base {
class TickClock;
} // namespace base
namespace blink {
class Document;
class Event;
// Detects when a page reaches First Idle and Time to Interactive. See
// https://goo.gl/SYt55W for detailed description and motivation of First Idle
// and Time to Interactive.
// TODO(crbug.com/631203): This class currently only detects Time to
// Interactive. Implement First Idle.
class CORE_EXPORT InteractiveDetector
: public GarbageCollected<InteractiveDetector>,
public Supplement<Document>,
public ExecutionContextLifecycleObserver,
public LongTaskObserver {
public:
static const char kSupplementName[];
// This class can be easily switched out to allow better testing of
// InteractiveDetector.
class CORE_EXPORT NetworkActivityChecker {
public:
explicit NetworkActivityChecker(Document* document) : document_(document) {}
NetworkActivityChecker(const NetworkActivityChecker&) = delete;
NetworkActivityChecker& operator=(const NetworkActivityChecker&) = delete;
virtual int GetActiveConnections();
virtual ~NetworkActivityChecker() = default;
private:
WeakPersistent<Document> document_;
};
static InteractiveDetector* From(Document&);
// Exposed for tests. See crbug.com/810381. We must use a consistent address
// for the supplement name.
static const char* SupplementName();
explicit InteractiveDetector(Document&, NetworkActivityChecker*);
InteractiveDetector(const InteractiveDetector&) = delete;
InteractiveDetector& operator=(const InteractiveDetector&) = delete;
~InteractiveDetector() override = default;
// Calls to base::TimeTicks::Now().since_origin().InSecondsF() 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(std::optional<base::TimeTicks> load_begin_time);
void OnResourceLoadEnd(std::optional<base::TimeTicks> load_finish_time);
void SetNavigationStartTime(base::TimeTicks navigation_start_time);
void OnFirstContentfulPaint(base::TimeTicks first_contentful_paint);
void OnDomContentLoadedEnd(base::TimeTicks dcl_time);
void OnInvalidatingInputEvent(base::TimeTicks invalidation_time);
void OnPageHiddenChanged(bool is_hidden);
// 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.
std::optional<base::TimeDelta> GetFirstInputDelay() const;
WTF::Vector<std::optional<base::TimeDelta>>
GetFirstInputDelaysAfterBackForwardCacheRestore() const;
// The timestamp of the event whose delay is reported by GetFirstInputDelay().
std::optional<base::TimeTicks> GetFirstInputTimestamp() const;
// The duration between the user's first scroll and display update.
std::optional<base::TimeTicks> GetFirstScrollTimestamp() const;
// The hardware timestamp of the first scroll after a navigation.
std::optional<base::TimeDelta> GetFirstScrollDelay() const;
// Process an input event, updating first_input_delay and
// first_input_timestamp if needed. The event types we care about are
// pointerdown, pointerup, mousedown, keydown, click, mouseup. And we
// check the event types in the caller of this function.
void HandleForInputDelay(const Event&,
base::TimeTicks event_platform_timestamp,
base::TimeTicks processing_start);
// ExecutionContextLifecycleObserver
void ContextDestroyed() override;
void Trace(Visitor*) const override;
void SetTaskRunnerForTesting(
scoped_refptr<base::SingleThreadTaskRunner> task_runner_for_testing);
// The caller owns the |clock| which must outlive the InteractiveDetector.
void SetTickClockForTesting(const base::TickClock* clock);
void RecordInputEventTimingUMA(base::TimeDelta processing_time,
base::TimeDelta time_to_next_paint);
void DidObserveFirstScrollDelay(base::TimeDelta first_scroll_delay,
base::TimeTicks first_scroll_timestamp);
void OnRestoredFromBackForwardCache();
private:
friend class InteractiveDetectorTest;
const base::TickClock* clock_;
base::TimeTicks interactive_time_;
base::TimeTicks interactive_detection_time_;
// Page event times that Interactive Detector depends on.
// Null base::TimeTicks values indicate the event has not been detected yet.
struct {
base::TimeTicks first_contentful_paint;
base::TimeTicks dom_content_loaded_end;
base::TimeTicks nav_start;
// The timestamp of the first input that would invalidate a Time to
// Interactive computation. This is used when reporting Time To Interactive
// on a trace event.
base::TimeTicks first_invalidating_input;
std::optional<base::TimeDelta> first_input_delay;
std::optional<base::TimeTicks> first_input_timestamp;
std::optional<base::TimeTicks> first_scroll_timestamp;
std::optional<base::TimeDelta> frist_scroll_delay;
WTF::Vector<std::optional<base::TimeDelta>>
first_input_delays_after_back_forward_cache_restore;
} page_event_times_;
struct VisibilityChangeEvent {
base::TimeTicks timestamp;
bool was_hidden;
};
// Stores sufficiently long quiet windows on the network.
Vector<WTF::PODInterval<base::TimeTicks>> network_quiet_windows_;
// Stores long tasks in order to compute Total Blocking Time (TBT) once Time
// To Interactive (TTI) is known.
Vector<WTF::PODInterval<base::TimeTicks>> long_tasks_;
// Start time of currently active network quiet windows.
// Null base::TimeTicks values indicate network is not quiet at the moment.
base::TimeTicks active_network_quiet_window_start_;
// Adds currently active quiet network quiet window to the
// vector. Should be called before calling FindInteractiveCandidate.
void AddCurrentlyActiveNetworkQuietInterval(base::TimeTicks current_time);
// Undoes AddCurrentlyActiveNetworkQuietInterval.
void RemoveCurrentlyActiveNetworkQuietInterval();
std::unique_ptr<NetworkActivityChecker> network_activity_checker_;
int ActiveConnections();
void BeginNetworkQuietPeriod(base::TimeTicks current_time);
void EndNetworkQuietPeriod(base::TimeTicks current_time);
// Updates current network quietness tracking information. Opens and closes
// network quiet windows as necessary.
void UpdateNetworkQuietState(double request_count,
std::optional<base::TimeTicks> current_time);
HeapTaskRunnerTimer<InteractiveDetector> time_to_interactive_timer_;
base::TimeTicks time_to_interactive_timer_fire_time_;
void StartOrPostponeCITimer(base::TimeTicks timer_fire_time);
void TimeToInteractiveTimerFired(TimerBase*);
void CheckTimeToInteractiveReached();
void OnTimeToInteractiveDetected();
base::TimeDelta ComputeTotalBlockingTime();
Vector<VisibilityChangeEvent> visibility_change_events_;
bool initially_hidden_;
// Returns true if page was ever backgrounded in the range
// [event_time, base::TimeTicks::Now()].
bool PageWasBackgroundedSinceEvent(base::TimeTicks event_time);
// 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.
base::TimeTicks FindInteractiveCandidate(base::TimeTicks lower_bound,
base::TimeTicks current_time);
// LongTaskObserver implementation
void OnLongTaskDetected(base::TimeTicks start_time,
base::TimeTicks 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.
base::TimeDelta pending_pointerdown_delay_;
base::TimeDelta pending_mousedown_delay_;
// The timestamp of a pending pointerdown event. Valid in the same cases as
// pending_pointerdown_delay_.
base::TimeTicks pending_pointerdown_timestamp_;
base::TimeTicks pending_mousedown_timestamp_;
};
} // namespace blink
#endif // THIRD_PARTY_BLINK_RENDERER_CORE_LOADER_INTERACTIVE_DETECTOR_H_