Commit 4522fbe2 authored by John Abd-El-Malek's avatar John Abd-El-Malek Committed by Chromium LUCI CQ

Add NavigationCallback.LargestContentfulPaint callback to WebLayer.

Bug: 1156663
Change-Id: I0ca8c2a2882c627780f94dae09b0e19a65bd04a9
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2591708Reviewed-by: default avatarScott Violet <sky@chromium.org>
Commit-Queue: John Abd-El-Malek <jam@chromium.org>
Cr-Commit-Position: refs/heads/master@{#837121}
parent 51844ff9
......@@ -213,6 +213,25 @@ public class NavigationTest {
}
}
public static class LargestContentfulPaintCallbackHelper extends CallbackHelper {
private long mNavigationStartMillis;
private long mLargestContentfulPaintMs;
public void notifyCalled(long navigationStartMillis, long largestContentfulPaintMs) {
mNavigationStartMillis = navigationStartMillis;
mLargestContentfulPaintMs = largestContentfulPaintMs;
notifyCalled();
}
public long getNavigationStartMillis() {
return mNavigationStartMillis;
}
public long getLargestContentfulPaintMs() {
return mLargestContentfulPaintMs;
}
}
public NavigationCallbackHelper onStartedCallback = new NavigationCallbackHelper();
public NavigationCallbackHelper onRedirectedCallback = new NavigationCallbackHelper();
public NavigationCallbackHelper onReadyToCommitCallback = new NavigationCallbackHelper();
......@@ -225,6 +244,8 @@ public class NavigationTest {
public CallbackHelper onFirstContentfulPaintCallback = new CallbackHelper();
public FirstContentfulPaintCallbackHelper onFirstContentfulPaint2Callback =
new FirstContentfulPaintCallbackHelper();
public LargestContentfulPaintCallbackHelper onLargestContentfulPaintCallback =
new LargestContentfulPaintCallbackHelper();
public UriCallbackHelper onOldPageNoLongerRenderedCallback = new UriCallbackHelper();
@Override
......@@ -264,6 +285,13 @@ public class NavigationTest {
navigationStartMillis, firstContentfulPaintMs);
}
@Override
public void onLargestContentfulPaint(
long navigationStartMillis, long largestContentfulPaintMs) {
onLargestContentfulPaintCallback.notifyCalled(
navigationStartMillis, largestContentfulPaintMs);
}
@Override
public void onOldPageNoLongerRendered(Uri newNavigationUri) {
onOldPageNoLongerRenderedCallback.notifyCalled(newNavigationUri);
......@@ -1263,4 +1291,34 @@ public class NavigationTest {
mCallback.onFirstContentfulPaint2Callback.getFirstContentfulPaintMs();
Assert.assertTrue(firstContentfulPaint <= (current - navigationStart));
}
@MinWebLayerVersion(89)
@Test
@SmallTest
public void testOnLargestContentfulPaintTiming() 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.onLargestContentfulPaintCallback.getCallCount();
mActivityTestRule.navigateAndWait(url);
// Navigate to a new page, as metrics like LCP are only reported at the end of the page load
// lifetime.
mActivityTestRule.navigateAndWait("about:blank");
mCallback.onLargestContentfulPaintCallback.waitForCallback(count);
long navigationStart =
mCallback.onLargestContentfulPaintCallback.getNavigationStartMillis();
long current = SystemClock.uptimeMillis();
Assert.assertTrue(navigationStart <= current);
Assert.assertTrue(navigationStart >= activityStartTimeMs);
long largestContentfulPaint =
mCallback.onLargestContentfulPaintCallback.getLargestContentfulPaintMs();
Assert.assertTrue(largestContentfulPaint <= (current - navigationStart));
}
}
......@@ -231,6 +231,28 @@ public final class NavigationControllerImpl extends INavigationController.Stub {
long navigationStartTick, long firstContentfulPaintDurationMs) throws RemoteException {
if (WebLayerFactoryImpl.getClientMajorVersion() < 88) return;
mNavigationControllerClient.onFirstContentfulPaint2(
(navigationStartTick - getNativeTickOffsetUs()) / 1000,
firstContentfulPaintDurationMs);
}
@CalledByNative
private void onLargestContentfulPaint(long navigationStartTick,
long largestContentfulPaintDurationMs) throws RemoteException {
if (WebLayerFactoryImpl.getClientMajorVersion() < 89) return;
mNavigationControllerClient.onLargestContentfulPaint(
(navigationStartTick - getNativeTickOffsetUs()) / 1000,
largestContentfulPaintDurationMs);
}
@CalledByNative
private void onOldPageNoLongerRendered(String uri) throws RemoteException {
if (WebLayerFactoryImpl.getClientMajorVersion() < 85) return;
mNavigationControllerClient.onOldPageNoLongerRendered(uri);
}
private long getNativeTickOffsetUs() {
// See logic in CustomTabsConnection.java that this was based on.
if (!mNativeTickOffsetUsComputed) {
// Compute offset from time ticks to uptimeMillis.
......@@ -239,18 +261,7 @@ public final class NavigationControllerImpl extends INavigationController.Stub {
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;
mNavigationControllerClient.onOldPageNoLongerRendered(uri);
return mNativeTickOffsetUs;
}
private static final class NavigateParamsImpl extends INavigateParams.Stub {
......
......@@ -36,4 +36,7 @@ interface INavigationControllerClient {
// Added in M88.
void onFirstContentfulPaint2(long navigationStartMs, long firstContentfulPaintDurationMs) = 10;
// Added in M89.
void onLargestContentfulPaint(long navigationStartMs, long largestContentfulPaintDurationMs) = 11;
}
......@@ -171,6 +171,25 @@ void NavigationControllerImpl::OnFirstContentfulPaint(
observer.OnFirstContentfulPaint(navigation_start, first_contentful_paint);
}
void NavigationControllerImpl::OnLargestContentfulPaint(
const base::TimeTicks& navigation_start,
const base::TimeDelta& largest_contentful_paint) {
#if defined(OS_ANDROID)
TRACE_EVENT0("weblayer",
"Java_NavigationControllerImpl_onLargestContentfulPaint2");
int64_t largest_contentful_paint_ms =
largest_contentful_paint.InMilliseconds();
Java_NavigationControllerImpl_onLargestContentfulPaint(
AttachCurrentThread(), java_controller_,
(navigation_start - base::TimeTicks()).InMicroseconds(),
largest_contentful_paint_ms);
#endif
for (auto& observer : observers_)
observer.OnLargestContentfulPaint(navigation_start,
largest_contentful_paint);
}
#if defined(OS_ANDROID)
void NavigationControllerImpl::SetNavigationControllerImpl(
JNIEnv* env,
......
......@@ -55,6 +55,14 @@ class NavigationControllerImpl : public NavigationController,
void OnFirstContentfulPaint(const base::TimeTicks& navigation_start,
const base::TimeDelta& first_contentful_paint);
// Called when the largest contentful paint page load metric is available.
// |navigation_start| is the navigation start time.
// |largest_contentful_paint| is the duration to largest contentful paint from
// navigation start.
void OnLargestContentfulPaint(
const base::TimeTicks& navigation_start,
const base::TimeDelta& largest_contentful_paint);
#if defined(OS_ANDROID)
void SetNavigationControllerImpl(
JNIEnv* env,
......
......@@ -8,6 +8,7 @@
#include "build/build_config.h"
#include "components/no_state_prefetch/browser/prerender_manager.h"
#include "components/no_state_prefetch/browser/prerender_util.h"
#include "components/page_load_metrics/browser/observers/core/largest_contentful_paint_handler.h"
#include "content/public/browser/navigation_handle.h"
#include "content/public/browser/web_contents.h"
#include "services/metrics/public/cpp/ukm_recorder.h"
......@@ -38,6 +39,33 @@ PageLoadMetricsObserverImpl::OnCommit(
return CONTINUE_OBSERVING;
}
page_load_metrics::PageLoadMetricsObserver::ObservePolicy
PageLoadMetricsObserverImpl::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, WebLayer 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;
}
PageLoadMetricsObserverImpl::ObservePolicy
PageLoadMetricsObserverImpl::OnHidden(
const page_load_metrics::mojom::PageLoadTiming& timing) {
ReportBufferedMetrics(timing);
return CONTINUE_OBSERVING;
}
void PageLoadMetricsObserverImpl::OnComplete(
const page_load_metrics::mojom::PageLoadTiming& timing) {
ReportBufferedMetrics(timing);
}
void PageLoadMetricsObserverImpl::OnFirstContentfulPaintInPage(
const page_load_metrics::mojom::PageLoadTiming& timing) {
auto* tab = TabImpl::FromWebContents(GetDelegate().GetWebContents());
......@@ -51,4 +79,33 @@ void PageLoadMetricsObserverImpl::OnFirstContentfulPaintInPage(
*timing.paint_timing->first_contentful_paint);
}
void PageLoadMetricsObserverImpl::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;
auto* tab = TabImpl::FromWebContents(GetDelegate().GetWebContents());
if (!tab)
return;
const page_load_metrics::ContentfulPaintTimingInfo& largest_contentful_paint =
GetDelegate()
.GetLargestContentfulPaintHandler()
.MergeMainFrameAndSubframes();
if (!largest_contentful_paint.ContainsValidTime())
return;
auto* nav_controller =
static_cast<NavigationControllerImpl*>(tab->GetNavigationController());
nav_controller->OnLargestContentfulPaint(GetDelegate().GetNavigationStart(),
*largest_contentful_paint.Time());
}
} // namespace weblayer
......@@ -16,10 +16,22 @@ class PageLoadMetricsObserverImpl
~PageLoadMetricsObserverImpl() override = default;
// page_load_metrics::PageLoadMetricsObserver implementation:
ObservePolicy FlushMetricsOnAppEnterBackground(
const page_load_metrics::mojom::PageLoadTiming& timing) override;
ObservePolicy OnHidden(
const page_load_metrics::mojom::PageLoadTiming& timing) override;
void OnComplete(
const page_load_metrics::mojom::PageLoadTiming& timing) override;
ObservePolicy OnCommit(content::NavigationHandle* navigation_handle,
ukm::SourceId source_id) override;
void OnFirstContentfulPaintInPage(
const page_load_metrics::mojom::PageLoadTiming& timing) override;
void ReportBufferedMetrics(
const page_load_metrics::mojom::PageLoadTiming& timing);
private:
bool reported_buffered_metrics_ = false;
};
} // namespace weblayer
......
......@@ -128,6 +128,19 @@ public abstract class NavigationCallback {
public void onFirstContentfulPaint(
long navigationStartMs, long firstContentfulPaintDurationMs) {}
/**
* This is fired when the largest contentful paint metric is available.
*
* @param navigationStartMs the absolute navigation start time in milliseconds since boot,
* not counting time spent in deep sleep. This comes from SystemClock.uptimeMillis().
* @param largestContentfulPaintDurationMs the number of milliseconds to largest contentful
* paint
* from navigation start.
* @since 89
*/
public void onLargestContentfulPaint(
long navigationStartMs, long largestContentfulPaintDurationMs) {}
/**
* Called after each navigation to indicate that the old page is no longer
* being rendered. Note this is not ordered with respect to onFirstContentfulPaint.
......
......@@ -387,6 +387,16 @@ public class NavigationController {
}
}
@Override
public void onLargestContentfulPaint(
long navigationStartMs, long largestContentfulPaintDurationMs) {
StrictModeWorkaround.apply();
for (NavigationCallback callback : mCallbacks) {
callback.onLargestContentfulPaint(
navigationStartMs, largestContentfulPaintDurationMs);
}
}
@Override
public void onOldPageNoLongerRendered(String uri) {
StrictModeWorkaround.apply();
......
......@@ -112,6 +112,14 @@ class NavigationObserver {
const base::TimeTicks& navigation_start,
const base::TimeDelta& first_contentful_paint) {}
// This is fired when the largest contentful paint page load metric is
// available. |navigation_start| is the navigation start time.
// |largest_contentful_paint| is the duration to largest contentful paint from
// navigation start.
virtual void OnLargestContentfulPaint(
const base::TimeTicks& navigation_start,
const base::TimeDelta& largest_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.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment