Commit 57dbb758 authored by ckitagawa's avatar ckitagawa Committed by Commit Bot

[Paint Preview] First Meaningful Paint Swap Trigger

This is an alternative/complimentary change to
http://crrev.com/c/2346905

Using First Meaningful Paint has two benefits.

1. It waits for key content to paint.
2. It doesn't wait for total loading to stop just down to a couple
   network requests. This is good in that we stop showing the preview
   earlier (which is desirable if the page is actually painted sooner).

I think both this and LoadingProgress are feasible. This one is more
promising in the sense that it takes a guess as to when the painting
is mostly done whereas the PageLoadProgress variant is mostly about
network requests being done.

One option might be to combine the two and take the later of the
signals? With these changes it might be worth making the removal
delay 0 ms or at least smaller.

Bug: 1110360
Change-Id: I6c4400166d0bfef57160424de2238bcbe238949a
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2346863Reviewed-by: default avatarMehran Mahmoudi <mahmoudi@chromium.org>
Commit-Queue: Calder Kitagawa <ckitagawa@chromium.org>
Cr-Commit-Position: refs/heads/master@{#796953}
parent c9a0131e
...@@ -7,12 +7,14 @@ package org.chromium.chrome.browser.paint_preview; ...@@ -7,12 +7,14 @@ package org.chromium.chrome.browser.paint_preview;
import org.chromium.chrome.browser.ChromeActivity; import org.chromium.chrome.browser.ChromeActivity;
import org.chromium.chrome.browser.flags.CachedFeatureFlags; import org.chromium.chrome.browser.flags.CachedFeatureFlags;
import org.chromium.chrome.browser.flags.ChromeFeatureList; import org.chromium.chrome.browser.flags.ChromeFeatureList;
import org.chromium.chrome.browser.metrics.PageLoadMetrics;
import org.chromium.chrome.browser.multiwindow.MultiWindowUtils; import org.chromium.chrome.browser.multiwindow.MultiWindowUtils;
import org.chromium.chrome.browser.paint_preview.services.PaintPreviewTabServiceFactory; import org.chromium.chrome.browser.paint_preview.services.PaintPreviewTabServiceFactory;
import org.chromium.chrome.browser.tab.Tab; import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.browser.tabmodel.EmptyTabModelSelectorObserver; import org.chromium.chrome.browser.tabmodel.EmptyTabModelSelectorObserver;
import org.chromium.chrome.browser.tabmodel.TabModelSelector; import org.chromium.chrome.browser.tabmodel.TabModelSelector;
import org.chromium.chrome.browser.util.ChromeAccessibilityUtil; import org.chromium.chrome.browser.util.ChromeAccessibilityUtil;
import org.chromium.content_public.browser.WebContents;
/** /**
* Handles initialization of the Paint Preview tab observers. * Handles initialization of the Paint Preview tab observers.
...@@ -69,6 +71,25 @@ public class PaintPreviewHelper { ...@@ -69,6 +71,25 @@ public class PaintPreviewHelper {
} }
sHasAttemptedToShowOnRestore = true; sHasAttemptedToShowOnRestore = true;
return TabbedPaintPreviewPlayer.get(tab).maybeShow(onShown, onDismissed);
TabbedPaintPreviewPlayer player = TabbedPaintPreviewPlayer.get(tab);
PageLoadMetrics.Observer observer = new PageLoadMetrics.Observer() {
@Override
public void onFirstMeaningfulPaint(WebContents webContents, long navigationId,
long navigationStartTick, long firstMeaningfulPaintMs) {
player.onFirstMeaningfulPaint(webContents);
}
};
if (!player.maybeShow(onShown, () -> {
onDismissed.run();
PageLoadMetrics.removeObserver(observer);
})) {
return false;
}
PageLoadMetrics.addObserver(observer);
return true;
} }
} }
...@@ -27,6 +27,7 @@ import org.chromium.components.browser_ui.styles.ChromeColors; ...@@ -27,6 +27,7 @@ import org.chromium.components.browser_ui.styles.ChromeColors;
import org.chromium.components.paintpreview.player.PlayerManager; import org.chromium.components.paintpreview.player.PlayerManager;
import org.chromium.content_public.browser.LoadUrlParams; import org.chromium.content_public.browser.LoadUrlParams;
import org.chromium.content_public.browser.NavigationHandle; import org.chromium.content_public.browser.NavigationHandle;
import org.chromium.content_public.browser.WebContents;
import org.chromium.url.GURL; import org.chromium.url.GURL;
/** /**
...@@ -53,7 +54,7 @@ public class TabbedPaintPreviewPlayer implements TabViewProvider, UserData { ...@@ -53,7 +54,7 @@ public class TabbedPaintPreviewPlayer implements TabViewProvider, UserData {
TabbedPaintPreviewPlayer.class; TabbedPaintPreviewPlayer.class;
private static final int SNACKBAR_DURATION_MS = 8 * 1000; private static final int SNACKBAR_DURATION_MS = 8 * 1000;
private static final int DEFAULT_INITIAL_REMOVE_DELAY_MS = 400; private static final int DEFAULT_INITIAL_REMOVE_DELAY_MS = 0;
private static final String INITIAL_REMOVE_DELAY_PARAM = "initial_remove_delay_ms"; private static final String INITIAL_REMOVE_DELAY_PARAM = "initial_remove_delay_ms";
private Tab mTab; private Tab mTab;
...@@ -62,7 +63,7 @@ public class TabbedPaintPreviewPlayer implements TabViewProvider, UserData { ...@@ -62,7 +63,7 @@ public class TabbedPaintPreviewPlayer implements TabViewProvider, UserData {
private Runnable mOnDismissed; private Runnable mOnDismissed;
private Boolean mInitializing; private Boolean mInitializing;
private boolean mHasUserInteraction; private boolean mHasUserInteraction;
private EmptyTabObserver mTabObserver; private TabbedPaintPreviewObserver mObserver;
private long mLastShownSnackBarTime; private long mLastShownSnackBarTime;
private boolean mDidStartRestore; private boolean mDidStartRestore;
private long mShownTime; private long mShownTime;
...@@ -75,70 +76,71 @@ public class TabbedPaintPreviewPlayer implements TabViewProvider, UserData { ...@@ -75,70 +76,71 @@ public class TabbedPaintPreviewPlayer implements TabViewProvider, UserData {
return tab.getUserDataHost().getUserData(USER_DATA_KEY); return tab.getUserDataHost().getUserData(USER_DATA_KEY);
} }
class TabbedPaintPreviewObserver extends EmptyTabObserver {
public void onFirstMeaningfulPaint() {
if (!isShowingAndNeedsBadge()) return;
long delayMs = ChromeFeatureList.getFieldTrialParamByFeatureAsInt(
ChromeFeatureList.PAINT_PREVIEW_SHOW_ON_STARTUP, INITIAL_REMOVE_DELAY_PARAM,
DEFAULT_INITIAL_REMOVE_DELAY_MS);
// Delay removing paint preview after didFirstVisuallyNonEmptyPaint and no user
// interaction by |delayMs|. This is to account for 'heavy' pages that take a while
// to finish painting and avoid having flickers when switching from paint preview
// to the live page.
new Handler().postDelayed(() -> {
if (!isShowingAndNeedsBadge()) return;
if (!mHasUserInteraction) {
removePaintPreview(ExitCause.TAB_FINISHED_LOADING);
return;
}
showSnackbar();
}, delayMs);
}
@Override
public void onRestoreStarted(Tab tab) {
mDidStartRestore = true;
}
@Override
public void onDidStartNavigation(Tab tab, NavigationHandle navigationHandle) {
if (mPlayerManager == null || !isShowingAndNeedsBadge()) return;
// Ignore navigations from subframes. We should only remove the paint preview
// player when the user navigates to a new page.
if (!navigationHandle.isInMainFrame()) return;
// If we haven't started to restore, this is the navigation call to start the
// restoration. We shouldn't remove the paint preview player.
if (!mDidStartRestore) return;
removePaintPreview(ExitCause.NAVIGATION_STARTED);
}
}
private TabbedPaintPreviewPlayer(Tab tab) { private TabbedPaintPreviewPlayer(Tab tab) {
mTab = tab; mTab = tab;
mPaintPreviewTabService = PaintPreviewTabServiceFactory.getServiceInstance(); mPaintPreviewTabService = PaintPreviewTabServiceFactory.getServiceInstance();
mTabObserver = new EmptyTabObserver() {
private boolean mFirstPaintHappened;
private boolean mPageLoadFinished;
@Override
public void didFirstVisuallyNonEmptyPaint(Tab tab) {
mFirstPaintHappened = true;
maybeRemovePaintPreview();
}
@Override
public void onPageLoadFinished(Tab tab, String url) {
mPageLoadFinished = true;
maybeRemovePaintPreview();
}
private void maybeRemovePaintPreview() {
if (!isShowingAndNeedsBadge() || !mFirstPaintHappened || !mPageLoadFinished) return;
mFirstPaintHappened = false;
mPageLoadFinished = false;
long delayMs = ChromeFeatureList.getFieldTrialParamByFeatureAsInt(
ChromeFeatureList.PAINT_PREVIEW_SHOW_ON_STARTUP, INITIAL_REMOVE_DELAY_PARAM,
DEFAULT_INITIAL_REMOVE_DELAY_MS);
// Delay removing paint preview after didFirstVisuallyNonEmptyPaint and no user
// interaction by |delayMs|. This is to account for 'heavy' pages that take a while
// to finish painting and avoid having flickers when switching from paint preview
// to the live page.
new Handler().postDelayed(() -> {
if (!isShowingAndNeedsBadge()) return;
if (!mHasUserInteraction) {
removePaintPreview(ExitCause.TAB_FINISHED_LOADING);
return;
}
showSnackbar();
}, delayMs);
}
@Override mObserver = new TabbedPaintPreviewObserver();
public void onRestoreStarted(Tab tab) { mTab.addObserver(mObserver);
mDidStartRestore = true; }
}
@Override
public void onDidStartNavigation(Tab tab, NavigationHandle navigationHandle) {
if (mPlayerManager == null || !isShowingAndNeedsBadge()) return;
// Ignore navigations from subframes. We should only remove the paint preview /**
// player when the user navigates to a new page. * Triggered via {@link PageLoadMetrics.Observer} when First Meaningful Paint happens.
if (!navigationHandle.isInMainFrame()) return; * @param webContents the webContents that triggered the event.
* @return Whether the event was handled for the provided webContents.
*/
public void onFirstMeaningfulPaint(WebContents webContents) {
// If there is no observer or tab this will never handle the event so it should be
// treated as a success.
if (mObserver == null || mTab == null) return;
// If we haven't started to restore, this is the navigation call to start the if (mTab.getWebContents() != webContents) return;
// restoration. We shouldn't remove the paint preview player.
if (!mDidStartRestore) return;
removePaintPreview(ExitCause.NAVIGATION_STARTED); mObserver.onFirstMeaningfulPaint();
}
};
mTab.addObserver(mTabObserver);
} }
/** /**
...@@ -254,7 +256,7 @@ public class TabbedPaintPreviewPlayer implements TabViewProvider, UserData { ...@@ -254,7 +256,7 @@ public class TabbedPaintPreviewPlayer implements TabViewProvider, UserData {
@Override @Override
public void destroy() { public void destroy() {
removePaintPreview(ExitCause.TAB_DESTROYED); removePaintPreview(ExitCause.TAB_DESTROYED);
mTab.removeObserver(mTabObserver); mTab.removeObserver(mObserver);
mTab = null; mTab = null;
} }
} }
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