Add NavigationCallback.onFirstContentfulPaint variant that matches up to CCT's version for easier comparison.
Bug: 1152600 Change-Id: I3025464676ecb2cc649e445701cdc5797e4d068d Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2559285 Reviewed-by: Scott Violet <sky@chromium.org> Commit-Queue: John Abd-El-Malek <jam@chromium.org> Cr-Commit-Position: refs/heads/master@{#833811}
This commit is contained in:

committed by
Chromium LUCI CQ

parent
0b198d00f7
commit
3883710078
weblayer
BUILD.gnnavigation_controller_impl.ccnavigation_controller_impl.hpage_load_metrics_initialize.ccpage_load_metrics_observer_impl.ccpage_load_metrics_observer_impl.hukm_page_load_metrics_observer.h
browser
android
javatests
src
org
chromium
weblayer
java
org
chromium
weblayer_private
public
@ -225,6 +225,8 @@ source_set("weblayer_lib_base") {
|
||||
"browser/no_state_prefetch/prerender_utils.h",
|
||||
"browser/page_load_metrics_initialize.cc",
|
||||
"browser/page_load_metrics_initialize.h",
|
||||
"browser/page_load_metrics_observer_impl.cc",
|
||||
"browser/page_load_metrics_observer_impl.h",
|
||||
"browser/page_specific_content_settings_delegate.cc",
|
||||
"browser/page_specific_content_settings_delegate.h",
|
||||
"browser/password_manager_driver_factory.cc",
|
||||
@ -269,8 +271,6 @@ source_set("weblayer_lib_base") {
|
||||
"browser/translate_client_impl.h",
|
||||
"browser/translate_ranker_factory.cc",
|
||||
"browser/translate_ranker_factory.h",
|
||||
"browser/ukm_page_load_metrics_observer.cc",
|
||||
"browser/ukm_page_load_metrics_observer.h",
|
||||
"browser/url_bar/autocomplete_scheme_classifier_impl.cc",
|
||||
"browser/url_bar/autocomplete_scheme_classifier_impl.h",
|
||||
"browser/url_bar/page_info_delegate_impl.cc",
|
||||
|
@ -15,6 +15,7 @@ import static org.chromium.content_public.browser.test.util.TestThreadUtils.runO
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.SystemClock;
|
||||
import android.support.test.InstrumentationRegistry;
|
||||
import android.webkit.WebResourceResponse;
|
||||
|
||||
@ -193,6 +194,25 @@ public class NavigationTest {
|
||||
}
|
||||
}
|
||||
|
||||
public static class FirstContentfulPaintCallbackHelper extends CallbackHelper {
|
||||
private long mNavigationStartMillis;
|
||||
private long mFirstContentfulPaintMs;
|
||||
|
||||
public void notifyCalled(long navigationStartMillis, long firstContentfulPaintMs) {
|
||||
mNavigationStartMillis = navigationStartMillis;
|
||||
mFirstContentfulPaintMs = firstContentfulPaintMs;
|
||||
notifyCalled();
|
||||
}
|
||||
|
||||
public long getNavigationStartMillis() {
|
||||
return mNavigationStartMillis;
|
||||
}
|
||||
|
||||
public long getFirstContentfulPaintMs() {
|
||||
return mFirstContentfulPaintMs;
|
||||
}
|
||||
}
|
||||
|
||||
public NavigationCallbackHelper onStartedCallback = new NavigationCallbackHelper();
|
||||
public NavigationCallbackHelper onRedirectedCallback = new NavigationCallbackHelper();
|
||||
public NavigationCallbackHelper onReadyToCommitCallback = new NavigationCallbackHelper();
|
||||
@ -203,6 +223,8 @@ public class NavigationTest {
|
||||
public NavigationCallbackValueRecorder loadProgressChangedCallback =
|
||||
new NavigationCallbackValueRecorder();
|
||||
public CallbackHelper onFirstContentfulPaintCallback = new CallbackHelper();
|
||||
public FirstContentfulPaintCallbackHelper onFirstContentfulPaint2Callback =
|
||||
new FirstContentfulPaintCallbackHelper();
|
||||
public UriCallbackHelper onOldPageNoLongerRenderedCallback = new UriCallbackHelper();
|
||||
|
||||
@Override
|
||||
@ -235,6 +257,13 @@ public class NavigationTest {
|
||||
onFirstContentfulPaintCallback.notifyCalled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFirstContentfulPaint(
|
||||
long navigationStartMillis, long firstContentfulPaintMs) {
|
||||
onFirstContentfulPaint2Callback.notifyCalled(
|
||||
navigationStartMillis, firstContentfulPaintMs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onOldPageNoLongerRendered(Uri newNavigationUri) {
|
||||
onOldPageNoLongerRenderedCallback.notifyCalled(newNavigationUri);
|
||||
@ -1209,4 +1238,29 @@ public class NavigationTest {
|
||||
mCallback.onFailedCallback.assertCalledWith(
|
||||
curFailedCount, STREAM_URL, LoadError.CONNECTIVITY_ERROR);
|
||||
}
|
||||
|
||||
@MinWebLayerVersion(89)
|
||||
@Test
|
||||
@SmallTest
|
||||
public void testOnFirstContentfulPaintTiming() throws Exception {
|
||||
long activityStartTimeMs = SystemClock.uptimeMillis();
|
||||
|
||||
TestWebServer testServer = TestWebServer.start();
|
||||
InstrumentationActivity activity = mActivityTestRule.launchShellWithUrl(null);
|
||||
setNavigationCallback(activity);
|
||||
String url = testServer.setResponse("/ok.html", "<html>ok</html>", null);
|
||||
|
||||
int count = mCallback.onFirstContentfulPaint2Callback.getCallCount();
|
||||
mActivityTestRule.navigateAndWait(url);
|
||||
mCallback.onFirstContentfulPaint2Callback.waitForCallback(count);
|
||||
|
||||
long navigationStart = mCallback.onFirstContentfulPaint2Callback.getNavigationStartMillis();
|
||||
long current = SystemClock.uptimeMillis();
|
||||
Assert.assertTrue(navigationStart <= current);
|
||||
Assert.assertTrue(navigationStart >= activityStartTimeMs);
|
||||
|
||||
long firstContentfulPaint =
|
||||
mCallback.onFirstContentfulPaint2Callback.getFirstContentfulPaintMs();
|
||||
Assert.assertTrue(firstContentfulPaint <= (current - navigationStart));
|
||||
}
|
||||
}
|
||||
|
@ -5,8 +5,10 @@
|
||||
package org.chromium.weblayer_private;
|
||||
|
||||
import android.os.RemoteException;
|
||||
import android.os.SystemClock;
|
||||
import android.webkit.WebResourceResponse;
|
||||
|
||||
import org.chromium.base.TimeUtilsJni;
|
||||
import org.chromium.base.annotations.CalledByNative;
|
||||
import org.chromium.base.annotations.JNINamespace;
|
||||
import org.chromium.base.annotations.NativeMethods;
|
||||
@ -28,6 +30,10 @@ public final class NavigationControllerImpl extends INavigationController.Stub {
|
||||
private long mNativeNavigationController;
|
||||
private INavigationControllerClient mNavigationControllerClient;
|
||||
|
||||
// Conversion between native TimeTicks and SystemClock.uptimeMillis().
|
||||
private long mNativeTickOffsetUs;
|
||||
private boolean mNativeTickOffsetUsComputed;
|
||||
|
||||
public NavigationControllerImpl(TabImpl tab, INavigationControllerClient client) {
|
||||
mTab = tab;
|
||||
mNavigationControllerClient = client;
|
||||
@ -220,6 +226,27 @@ public final class NavigationControllerImpl extends INavigationController.Stub {
|
||||
mNavigationControllerClient.onFirstContentfulPaint();
|
||||
}
|
||||
|
||||
@CalledByNative
|
||||
private void onFirstContentfulPaint2(
|
||||
long navigationStartTick, long firstContentfulPaintDurationMs) throws RemoteException {
|
||||
if (WebLayerFactoryImpl.getClientMajorVersion() < 89) return;
|
||||
|
||||
// See logic in CustomTabsConnection.java that this was based on.
|
||||
if (!mNativeTickOffsetUsComputed) {
|
||||
// Compute offset from time ticks to uptimeMillis.
|
||||
mNativeTickOffsetUsComputed = true;
|
||||
long nativeNowUs = TimeUtilsJni.get().getTimeTicksNowUs();
|
||||
long javaNowUs = SystemClock.uptimeMillis() * 1000;
|
||||
mNativeTickOffsetUs = nativeNowUs - javaNowUs;
|
||||
}
|
||||
// SystemClock.uptimeMillis() is used here as it (as of June 2017) uses the same system call
|
||||
// as all the native side of Chrome, that is clock_gettime(CLOCK_MONOTONIC). Meaning that
|
||||
// the offset relative to navigationStart is to be compared with a
|
||||
// SystemClock.uptimeMillis() value.
|
||||
mNavigationControllerClient.onFirstContentfulPaint2(
|
||||
(navigationStartTick - mNativeTickOffsetUs) / 1000, firstContentfulPaintDurationMs);
|
||||
}
|
||||
|
||||
@CalledByNative
|
||||
private void onOldPageNoLongerRendered(String uri) throws RemoteException {
|
||||
if (WebLayerFactoryImpl.getClientMajorVersion() < 85) return;
|
||||
|
@ -33,4 +33,7 @@ interface INavigationControllerClient {
|
||||
|
||||
// Added in M85.
|
||||
void onOldPageNoLongerRendered(in String uri) = 9;
|
||||
|
||||
// Added in M89.
|
||||
void onFirstContentfulPaint2(long navigationStartMs, long firstContentfulPaintDurationMs) = 10;
|
||||
}
|
||||
|
@ -154,6 +154,23 @@ NavigationImpl* NavigationControllerImpl::GetNavigationImplFromId(
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void NavigationControllerImpl::OnFirstContentfulPaint(
|
||||
const base::TimeTicks& navigation_start,
|
||||
const base::TimeDelta& first_contentful_paint) {
|
||||
#if defined(OS_ANDROID)
|
||||
TRACE_EVENT0("weblayer",
|
||||
"Java_NavigationControllerImpl_onFirstContentfulPaint2");
|
||||
int64_t first_contentful_paint_ms = first_contentful_paint.InMilliseconds();
|
||||
Java_NavigationControllerImpl_onFirstContentfulPaint2(
|
||||
AttachCurrentThread(), java_controller_,
|
||||
(navigation_start - base::TimeTicks()).InMicroseconds(),
|
||||
first_contentful_paint_ms);
|
||||
#endif
|
||||
|
||||
for (auto& observer : observers_)
|
||||
observer.OnFirstContentfulPaint(navigation_start, first_contentful_paint);
|
||||
}
|
||||
|
||||
#if defined(OS_ANDROID)
|
||||
void NavigationControllerImpl::SetNavigationControllerImpl(
|
||||
JNIEnv* env,
|
||||
|
@ -48,6 +48,13 @@ class NavigationControllerImpl : public NavigationController,
|
||||
// Returns the NavigationImpl for |navigation_id|, or null if there isn't one.
|
||||
NavigationImpl* GetNavigationImplFromId(int64_t navigation_id);
|
||||
|
||||
// Called when the first contentful paint page load metric is available.
|
||||
// |navigation_start| is the navigation start time.
|
||||
// |first_contentful_paint_ms| is the duration to first contentful paint from
|
||||
// navigation start.
|
||||
void OnFirstContentfulPaint(const base::TimeTicks& navigation_start,
|
||||
const base::TimeDelta& first_contentful_paint);
|
||||
|
||||
#if defined(OS_ANDROID)
|
||||
void SetNavigationControllerImpl(
|
||||
JNIEnv* env,
|
||||
|
@ -10,7 +10,7 @@
|
||||
#include "components/page_load_metrics/browser/page_load_metrics_observer.h"
|
||||
#include "components/page_load_metrics/browser/page_load_tracker.h"
|
||||
#include "weblayer/browser/no_state_prefetch/prerender_utils.h"
|
||||
#include "weblayer/browser/ukm_page_load_metrics_observer.h"
|
||||
#include "weblayer/browser/page_load_metrics_observer_impl.h"
|
||||
|
||||
namespace weblayer {
|
||||
|
||||
@ -39,10 +39,7 @@ class PageLoadMetricsEmbedder
|
||||
// page_load_metrics::PageLoadMetricsEmbedderBase:
|
||||
void RegisterEmbedderObservers(
|
||||
page_load_metrics::PageLoadTracker* tracker) override {
|
||||
std::unique_ptr<page_load_metrics::PageLoadMetricsObserver> ukm_observer =
|
||||
UkmPageLoadMetricsObserver::CreateIfNeeded();
|
||||
if (ukm_observer)
|
||||
tracker->AddObserver(std::move(ukm_observer));
|
||||
tracker->AddObserver(std::make_unique<PageLoadMetricsObserverImpl>());
|
||||
|
||||
if (g_callback_for_testing)
|
||||
(*g_callback_for_testing).Run(tracker);
|
||||
|
@ -2,31 +2,31 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "weblayer/browser/ukm_page_load_metrics_observer.h"
|
||||
#include "weblayer/browser/page_load_metrics_observer_impl.h"
|
||||
|
||||
#include "base/time/time.h"
|
||||
#include "build/build_config.h"
|
||||
#include "components/no_state_prefetch/browser/prerender_manager.h"
|
||||
#include "components/no_state_prefetch/browser/prerender_util.h"
|
||||
#include "content/public/browser/navigation_handle.h"
|
||||
#include "content/public/browser/web_contents.h"
|
||||
#include "services/metrics/public/cpp/ukm_recorder.h"
|
||||
#include "weblayer/browser/navigation_controller_impl.h"
|
||||
#include "weblayer/browser/no_state_prefetch/prerender_manager_factory.h"
|
||||
#include "weblayer/browser/tab_impl.h"
|
||||
|
||||
namespace weblayer {
|
||||
|
||||
// static
|
||||
std::unique_ptr<page_load_metrics::PageLoadMetricsObserver>
|
||||
UkmPageLoadMetricsObserver::CreateIfNeeded() {
|
||||
if (!ukm::UkmRecorder::Get()) {
|
||||
return nullptr;
|
||||
}
|
||||
return std::make_unique<UkmPageLoadMetricsObserver>();
|
||||
}
|
||||
|
||||
UkmPageLoadMetricsObserver::ObservePolicy UkmPageLoadMetricsObserver::OnCommit(
|
||||
PageLoadMetricsObserverImpl::ObservePolicy
|
||||
PageLoadMetricsObserverImpl::OnCommit(
|
||||
content::NavigationHandle* navigation_handle,
|
||||
ukm::SourceId source_id) {
|
||||
#if defined(OS_ANDROID)
|
||||
if (!ukm::UkmRecorder::Get())
|
||||
return CONTINUE_OBSERVING;
|
||||
|
||||
// If URL-Keyed-Metrics (UKM) is enabled in the system, this is used to
|
||||
// populate it with top-level page-load metrics.
|
||||
prerender::PrerenderManager* const prerender_manager =
|
||||
PrerenderManagerFactory::GetForBrowserContext(
|
||||
navigation_handle->GetWebContents()->GetBrowserContext());
|
||||
@ -38,4 +38,17 @@ UkmPageLoadMetricsObserver::ObservePolicy UkmPageLoadMetricsObserver::OnCommit(
|
||||
return CONTINUE_OBSERVING;
|
||||
}
|
||||
|
||||
void PageLoadMetricsObserverImpl::OnFirstContentfulPaintInPage(
|
||||
const page_load_metrics::mojom::PageLoadTiming& timing) {
|
||||
auto* tab = TabImpl::FromWebContents(GetDelegate().GetWebContents());
|
||||
if (!tab)
|
||||
return;
|
||||
|
||||
auto* nav_controller =
|
||||
static_cast<NavigationControllerImpl*>(tab->GetNavigationController());
|
||||
nav_controller->OnFirstContentfulPaint(
|
||||
GetDelegate().GetNavigationStart(),
|
||||
*timing.paint_timing->first_contentful_paint);
|
||||
}
|
||||
|
||||
} // namespace weblayer
|
27
weblayer/browser/page_load_metrics_observer_impl.h
Normal file
27
weblayer/browser/page_load_metrics_observer_impl.h
Normal file
@ -0,0 +1,27 @@
|
||||
// Copyright 2020 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 WEBLAYER_BROWSER_PAGE_LOAD_METRICS_OBSERVER_IMPL_H_
|
||||
#define WEBLAYER_BROWSER_PAGE_LOAD_METRICS_OBSERVER_IMPL_H_
|
||||
|
||||
#include "components/page_load_metrics/browser/page_load_metrics_observer.h"
|
||||
|
||||
namespace weblayer {
|
||||
|
||||
class PageLoadMetricsObserverImpl
|
||||
: public page_load_metrics::PageLoadMetricsObserver {
|
||||
public:
|
||||
PageLoadMetricsObserverImpl() = default;
|
||||
~PageLoadMetricsObserverImpl() override = default;
|
||||
|
||||
// page_load_metrics::PageLoadMetricsObserver implementation:
|
||||
ObservePolicy OnCommit(content::NavigationHandle* navigation_handle,
|
||||
ukm::SourceId source_id) override;
|
||||
void OnFirstContentfulPaintInPage(
|
||||
const page_load_metrics::mojom::PageLoadTiming& timing) override;
|
||||
};
|
||||
|
||||
} // namespace weblayer
|
||||
|
||||
#endif // WEBLAYER_BROWSER_PAGE_LOAD_METRICS_OBSERVER_IMPL_H_
|
@ -1,35 +0,0 @@
|
||||
// Copyright 2020 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 WEBLAYER_BROWSER_UKM_PAGE_LOAD_METRICS_OBSERVER_H_
|
||||
#define WEBLAYER_BROWSER_UKM_PAGE_LOAD_METRICS_OBSERVER_H_
|
||||
|
||||
#include "components/page_load_metrics/browser/page_load_metrics_observer.h"
|
||||
|
||||
namespace content {
|
||||
class NavigationHandle;
|
||||
}
|
||||
|
||||
namespace weblayer {
|
||||
|
||||
// If URL-Keyed-Metrics (UKM) is enabled in the system, this is used to
|
||||
// populate it with top-level page-load metrics.
|
||||
class UkmPageLoadMetricsObserver
|
||||
: public page_load_metrics::PageLoadMetricsObserver {
|
||||
public:
|
||||
// Returns a UkmPageLoadMetricsObserver, or nullptr if it is not needed.
|
||||
static std::unique_ptr<page_load_metrics::PageLoadMetricsObserver>
|
||||
CreateIfNeeded();
|
||||
|
||||
UkmPageLoadMetricsObserver() = default;
|
||||
~UkmPageLoadMetricsObserver() override = default;
|
||||
|
||||
// page_load_metrics::PageLoadMetricsObserver implementation:
|
||||
ObservePolicy OnCommit(content::NavigationHandle* navigation_handle,
|
||||
ukm::SourceId source_id) override;
|
||||
};
|
||||
|
||||
} // namespace weblayer
|
||||
|
||||
#endif // WEBLAYER_BROWSER_UKM_PAGE_LOAD_METRICS_OBSERVER_H_
|
@ -115,6 +115,19 @@ public abstract class NavigationCallback {
|
||||
*/
|
||||
public void onFirstContentfulPaint() {}
|
||||
|
||||
/**
|
||||
* Similar to onFirstContentfulPaint but contains timing information from the renderer process
|
||||
* to better align with the Navigation Timing API.
|
||||
*
|
||||
* @param navigationStartMs the absolute navigation start time in milliseconds since boot,
|
||||
* not counting time spent in deep sleep. This comes from SystemClock.uptimeMillis().
|
||||
* @param firstContentfulPaintDurationMs the number of milliseconds to first contentful paint
|
||||
* from navigation start.
|
||||
* @since 89
|
||||
*/
|
||||
public void onFirstContentfulPaint(
|
||||
long navigationStartMs, long firstContentfulPaintDurationMs) {}
|
||||
|
||||
/**
|
||||
* Called after each navigation to indicate that the old page is no longer
|
||||
* being rendered. Note this is not ordered with respect to onFirstContentfulPaint.
|
||||
|
@ -378,6 +378,15 @@ public class NavigationController {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFirstContentfulPaint2(
|
||||
long navigationStartMs, long firstContentfulPaintDurationMs) {
|
||||
StrictModeWorkaround.apply();
|
||||
for (NavigationCallback callback : mCallbacks) {
|
||||
callback.onFirstContentfulPaint(navigationStartMs, firstContentfulPaintDurationMs);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onOldPageNoLongerRendered(String uri) {
|
||||
StrictModeWorkaround.apply();
|
||||
|
@ -7,6 +7,11 @@
|
||||
|
||||
class GURL;
|
||||
|
||||
namespace base {
|
||||
class TimeDelta;
|
||||
class TimeTicks;
|
||||
} // namespace base
|
||||
|
||||
namespace weblayer {
|
||||
class Navigation;
|
||||
|
||||
@ -98,6 +103,15 @@ class NavigationObserver {
|
||||
// first paint after a non-empty layout has finished.
|
||||
virtual void OnFirstContentfulPaint() {}
|
||||
|
||||
// Similar to OnFirstContentfulPaint but contains timing information from the
|
||||
// renderer process to better align with the Navigation Timing API.
|
||||
// |navigation_start| is the navigation start time.
|
||||
// |first_contentful_paint| is the duration to first contentful paint from
|
||||
// navigation start.
|
||||
virtual void OnFirstContentfulPaint(
|
||||
const base::TimeTicks& navigation_start,
|
||||
const base::TimeDelta& first_contentful_paint) {}
|
||||
|
||||
// Called after each navigation to indicate that the old page is no longer
|
||||
// being rendered. Note this is not ordered with respect to
|
||||
// OnFirstContentfulPaint.
|
||||
|
Reference in New Issue
Block a user