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
|
// Copyright 2016 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/page_load_metrics/observers/android_page_load_metrics_observer.h"
#include <string>
#include "base/android/jni_android.h"
#include "base/android/jni_string.h"
#include "base/android/scoped_java_ref.h"
#include "base/time/time.h"
#include "chrome/browser/browser_process.h"
#include "components/page_load_metrics/browser/observers/core/largest_contentful_paint_handler.h"
#include "components/page_load_metrics/browser/page_load_metrics_util.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/navigation_handle.h"
#include "content/public/browser/web_contents.h"
#include "services/network/public/cpp/network_quality_tracker.h"
#include "url/gurl.h"
// Must come after all headers that specialize FromJniType() / ToJniType().
#include "chrome/browser/page_load_metrics/jni_headers/PageLoadMetrics_jni.h"
AndroidPageLoadMetricsObserver::AndroidPageLoadMetricsObserver() {
network_quality_tracker_ = g_browser_process->network_quality_tracker();
DCHECK(network_quality_tracker_);
}
AndroidPageLoadMetricsObserver::ObservePolicy
AndroidPageLoadMetricsObserver::OnStart(
content::NavigationHandle* navigation_handle,
const GURL& currently_committed_url,
bool started_in_foreground) {
ReportNewNavigation(navigation_handle->GetNavigationId());
return CONTINUE_OBSERVING;
}
page_load_metrics::PageLoadMetricsObserver::ObservePolicy
AndroidPageLoadMetricsObserver::OnFencedFramesStart(
content::NavigationHandle* navigation_handle,
const GURL& currently_committed_url) {
// This class uses the event OnLoadedResource, but only uses the one with
// network::mojom::RequestDestination::kDocument, which never occur in
// FencedFrames' navigation. So, we can use STOP_OBSERVING.
return STOP_OBSERVING;
}
page_load_metrics::PageLoadMetricsObserver::ObservePolicy
AndroidPageLoadMetricsObserver::OnPrerenderStart(
content::NavigationHandle* navigation_handle,
const GURL& currently_committed_url) {
ReportNewNavigation(navigation_handle->GetNavigationId());
return CONTINUE_OBSERVING;
}
void AndroidPageLoadMetricsObserver::DidActivatePrerenderedPage(
content::NavigationHandle* navigation_handle) {
// NavigationHandle for the activation contains the source of
// `activation_start` tick as NavigationStart.
ReportActivation(navigation_handle->GetNavigationId(),
navigation_handle->NavigationStart());
}
page_load_metrics::PageLoadMetricsObserver::ObservePolicy
AndroidPageLoadMetricsObserver::FlushMetricsOnAppEnterBackground(
const page_load_metrics::mojom::PageLoadTiming& timing) {
// FlushMetricsOnAppEnterBackground is invoked on Android in cases where the
// app is about to be backgrounded, as part of the Activity.onPause()
// flow. After this method is invoked, Chrome may be killed without further
// notification, so we record final metrics collected up to this point.
ReportBufferedMetrics(timing);
// We continue observing after being backgrounded, in case we are foregrounded
// again without being killed. In those cases we may still report non-buffered
// metrics such as FCP after being re-foregrounded.
return CONTINUE_OBSERVING;
}
AndroidPageLoadMetricsObserver::ObservePolicy
AndroidPageLoadMetricsObserver::OnHidden(
const page_load_metrics::mojom::PageLoadTiming& timing) {
ReportBufferedMetrics(timing);
return CONTINUE_OBSERVING;
}
void AndroidPageLoadMetricsObserver::OnComplete(
const page_load_metrics::mojom::PageLoadTiming& timing) {
// TODO(http://crbug.com/1363952): Java side WebContents insntace obtained
// via GetJavaWebContents() was already released here, and newly created wrong
// instance will be obtained in the following calls.
ReportBufferedMetrics(timing);
}
void AndroidPageLoadMetricsObserver::OnFirstContentfulPaintInPage(
const page_load_metrics::mojom::PageLoadTiming& timing) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
ReportFirstContentfulPaint(GetDelegate().GetNavigationStart(),
*timing.paint_timing->first_contentful_paint);
}
void AndroidPageLoadMetricsObserver::OnFirstInputInPage(
const page_load_metrics::mojom::PageLoadTiming& timing) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
ReportFirstInputDelay(*timing.interactive_timing->first_input_delay);
}
void AndroidPageLoadMetricsObserver::OnFirstMeaningfulPaintInMainFrameDocument(
const page_load_metrics::mojom::PageLoadTiming& timing) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
ReportFirstMeaningfulPaint(GetDelegate().GetNavigationStart(),
*timing.paint_timing->first_meaningful_paint);
}
void AndroidPageLoadMetricsObserver::OnLoadEventStart(
const page_load_metrics::mojom::PageLoadTiming& timing) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
ReportLoadEventStart(GetDelegate().GetNavigationStart(),
*timing.document_timing->load_event_start);
}
void AndroidPageLoadMetricsObserver::OnLoadedResource(
const page_load_metrics::ExtraRequestCompleteInfo&
extra_request_complete_info) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (extra_request_complete_info.request_destination ==
network::mojom::RequestDestination::kDocument) {
DCHECK(!did_dispatch_on_main_resource_);
if (did_dispatch_on_main_resource_) {
// We are defensive for the case of something strange happening and return
// in order not to post multiple times.
return;
}
did_dispatch_on_main_resource_ = true;
const net::LoadTimingInfo& timing =
*extra_request_complete_info.load_timing_info;
int64_t domain_lookup_start =
timing.connect_timing.domain_lookup_start.since_origin()
.InMilliseconds();
int64_t domain_lookup_end =
timing.connect_timing.domain_lookup_end.since_origin().InMilliseconds();
int64_t connect_start =
timing.connect_timing.connect_start.since_origin().InMilliseconds();
int64_t connect_end =
timing.connect_timing.connect_end.since_origin().InMilliseconds();
int64_t request_start =
timing.request_start.since_origin().InMilliseconds();
int64_t send_start = timing.send_start.since_origin().InMilliseconds();
int64_t send_end = timing.send_end.since_origin().InMilliseconds();
ReportLoadedMainResource(domain_lookup_start, domain_lookup_end,
connect_start, connect_end, request_start,
send_start, send_end);
}
}
void AndroidPageLoadMetricsObserver::ReportNewNavigation(
int64_t navigation_id) {
DCHECK_GE(navigation_id, 0);
navigation_id_ = navigation_id;
base::android::ScopedJavaLocalRef<jobject> java_web_contents =
GetDelegate().GetWebContents()->GetJavaWebContents();
JNIEnv* env = base::android::AttachCurrentThread();
Java_PageLoadMetrics_onNewNavigation(
env, java_web_contents, static_cast<jlong>(navigation_id_),
static_cast<jboolean>(GetDelegate().IsFirstNavigationInWebContents()),
static_cast<jboolean>(IsPrerendering()));
int64_t http_rtt = network_quality_tracker_->GetHttpRTT().InMilliseconds();
int64_t transport_rtt =
network_quality_tracker_->GetTransportRTT().InMilliseconds();
ReportNetworkQualityEstimate(
network_quality_tracker_->GetEffectiveConnectionType(), http_rtt,
transport_rtt);
}
void AndroidPageLoadMetricsObserver::ReportActivation(
int64_t activating_navigation_id,
base::TimeTicks activation_start_tick) {
DCHECK_GE(activating_navigation_id, 0);
base::android::ScopedJavaLocalRef<jobject> java_web_contents =
GetDelegate().GetWebContents()->GetJavaWebContents();
JNIEnv* env = base::android::AttachCurrentThread();
Java_PageLoadMetrics_onActivation(
env, java_web_contents, static_cast<jlong>(navigation_id_),
static_cast<jlong>(activating_navigation_id),
activation_start_tick.ToUptimeMicros());
navigation_id_ = activating_navigation_id;
}
void AndroidPageLoadMetricsObserver::ReportBufferedMetrics(
const page_load_metrics::mojom::PageLoadTiming& timing) {
// This method may be invoked multiple times. Make sure that if we already
// reported, we do not report again.
if (reported_buffered_metrics_)
return;
reported_buffered_metrics_ = true;
// Buffered metrics aren't available until after the navigation commits.
if (!GetDelegate().DidCommit())
return;
base::android::ScopedJavaLocalRef<jobject> java_web_contents =
GetDelegate().GetWebContents()->GetJavaWebContents();
JNIEnv* env = base::android::AttachCurrentThread();
int64_t navigation_start_tick =
GetDelegate().GetNavigationStart().ToUptimeMicros();
const page_load_metrics::ContentfulPaintTimingInfo& largest_contentful_paint =
GetDelegate()
.GetLargestContentfulPaintHandler()
.MergeMainFrameAndSubframes();
if (largest_contentful_paint.ContainsValidTime()) {
Java_PageLoadMetrics_onLargestContentfulPaint(
env, java_web_contents, static_cast<jlong>(navigation_id_),
static_cast<jlong>(navigation_start_tick),
static_cast<jlong>(largest_contentful_paint.Time()->InMilliseconds()),
static_cast<jlong>(largest_contentful_paint.Size()));
}
Java_PageLoadMetrics_onLayoutShiftScore(
env, java_web_contents, static_cast<jlong>(navigation_id_),
static_cast<jfloat>(GetDelegate()
.GetMainFrameRenderData()
.layout_shift_score_before_input_or_scroll),
static_cast<jfloat>(GetDelegate().GetPageRenderData().layout_shift_score),
static_cast<jboolean>(IsPrerendering()));
}
void AndroidPageLoadMetricsObserver::ReportNetworkQualityEstimate(
net::EffectiveConnectionType connection_type,
int64_t http_rtt_ms,
int64_t transport_rtt_ms) {
base::android::ScopedJavaLocalRef<jobject> java_web_contents =
GetDelegate().GetWebContents()->GetJavaWebContents();
JNIEnv* env = base::android::AttachCurrentThread();
Java_PageLoadMetrics_onNetworkQualityEstimate(
env, java_web_contents, static_cast<jlong>(navigation_id_),
static_cast<jint>(connection_type), static_cast<jlong>(http_rtt_ms),
static_cast<jlong>(transport_rtt_ms),
static_cast<jboolean>(IsPrerendering()));
}
void AndroidPageLoadMetricsObserver::ReportFirstContentfulPaint(
base::TimeTicks navigation_start_tick,
base::TimeDelta first_contentful_paint) {
base::android::ScopedJavaLocalRef<jobject> java_web_contents =
GetDelegate().GetWebContents()->GetJavaWebContents();
JNIEnv* env = base::android::AttachCurrentThread();
Java_PageLoadMetrics_onFirstContentfulPaint(
env, java_web_contents, static_cast<jlong>(navigation_id_),
navigation_start_tick.ToUptimeMicros(),
static_cast<jlong>(first_contentful_paint.InMilliseconds()));
}
void AndroidPageLoadMetricsObserver::ReportFirstMeaningfulPaint(
base::TimeTicks navigation_start_tick,
base::TimeDelta first_meaningful_paint) {
base::android::ScopedJavaLocalRef<jobject> java_web_contents =
GetDelegate().GetWebContents()->GetJavaWebContents();
JNIEnv* env = base::android::AttachCurrentThread();
Java_PageLoadMetrics_onFirstMeaningfulPaint(
env, java_web_contents, static_cast<jlong>(navigation_id_),
navigation_start_tick.ToUptimeMicros(),
static_cast<jlong>(first_meaningful_paint.InMilliseconds()));
}
void AndroidPageLoadMetricsObserver::ReportLoadEventStart(
base::TimeTicks navigation_start_tick,
base::TimeDelta load_event_start) {
base::android::ScopedJavaLocalRef<jobject> java_web_contents =
GetDelegate().GetWebContents()->GetJavaWebContents();
JNIEnv* env = base::android::AttachCurrentThread();
Java_PageLoadMetrics_onLoadEventStart(
env, java_web_contents, static_cast<jlong>(navigation_id_),
navigation_start_tick.ToUptimeMicros(),
static_cast<jlong>(load_event_start.InMilliseconds()),
static_cast<jboolean>(IsPrerendering()));
}
void AndroidPageLoadMetricsObserver::ReportLoadedMainResource(
int64_t dns_start_ms,
int64_t dns_end_ms,
int64_t connect_start_ms,
int64_t connect_end_ms,
int64_t request_start_ms,
int64_t send_start_ms,
int64_t send_end_ms) {
base::android::ScopedJavaLocalRef<jobject> java_web_contents =
GetDelegate().GetWebContents()->GetJavaWebContents();
JNIEnv* env = base::android::AttachCurrentThread();
Java_PageLoadMetrics_onLoadedMainResource(
env, java_web_contents, static_cast<jlong>(navigation_id_),
static_cast<jlong>(dns_start_ms), static_cast<jlong>(dns_end_ms),
static_cast<jlong>(connect_start_ms), static_cast<jlong>(connect_end_ms),
static_cast<jlong>(request_start_ms), static_cast<jlong>(send_start_ms),
static_cast<jlong>(send_end_ms), static_cast<jboolean>(IsPrerendering()));
}
void AndroidPageLoadMetricsObserver::ReportFirstInputDelay(
base::TimeDelta first_input_delay) {
base::android::ScopedJavaLocalRef<jobject> java_web_contents =
GetDelegate().GetWebContents()->GetJavaWebContents();
JNIEnv* env = base::android::AttachCurrentThread();
Java_PageLoadMetrics_onFirstInputDelay(
env, java_web_contents, static_cast<jlong>(navigation_id_),
static_cast<jlong>(first_input_delay.InMilliseconds()));
}
bool AndroidPageLoadMetricsObserver::IsPrerendering() {
// So that the isPrerendering argument works to realize STOP_OBSERVING on
// OnPrerenderStart().
return GetDelegate().GetPrerenderingState() !=
page_load_metrics::PrerenderingState::kNoPrerendering;
}
|