Bug 1647797 - Add GeckoView API to listen for when contentful paint status has been reset. r=geckoview-reviewers,agi,esawin

Android-components listens to the GeckoView callback onFirstContentfulPaint to
track whether a contentful paint has occured, in order to decide when to
thumbnail a tab. Currently this gets fired once per tab.

However, when the GeckoSession is paused, we clear cached resources in the
compositor. This means that when the session is resumed, the compositor does not
have the necessary information to render the page (such as painted content
buffers, or the webrender display list). Because android-components attempts to
capture a new thumbnail immediately upon resuming, it ends up capturing a blank
thumbnail.

To fix this, add a new callback onPaintStatusReset() which is invoked when the
cached resources are cleared. Android-components can listen for this to be
informed when the contentful paint is no longer visible. It can then wait until
the subsequent contentful paint occurs before capturing the thumbnail.

Differential Revision: https://phabricator.services.mozilla.com/D87341
This commit is contained in:
Jamie Nicol 2020-08-19 15:24:14 +00:00
Родитель f5c045a279
Коммит 54ed855193
8 изменённых файлов: 53 добавлений и 4 удалений

Просмотреть файл

@ -2982,6 +2982,13 @@ void BrowserChild::ClearCachedResources() {
MOZ_ASSERT(lm);
lm->ClearCachedResources();
if (nsCOMPtr<Document> document = GetTopLevelDocument()) {
nsPresContext* presContext = document->GetPresContext();
if (presContext) {
presContext->NotifyPaintStatusReset();
}
}
}
void BrowserChild::InvalidateLayers() {

Просмотреть файл

@ -2366,6 +2366,16 @@ void nsPresContext::NotifyContentfulPaint() {
}
}
void nsPresContext::NotifyPaintStatusReset() {
mHadNonBlankPaint = false;
mHadContentfulPaint = false;
#if defined(MOZ_WIDGET_ANDROID)
(new AsyncEventDispatcher(mDocument, u"MozPaintStatusReset"_ns,
CanBubble::eYes, ChromeOnlyDispatch::eYes))
->PostDOMEvent();
#endif
}
void nsPresContext::NotifyDOMContentFlushed() {
NS_ENSURE_TRUE_VOID(mPresShell);
if (IsRootContentDocument()) {

Просмотреть файл

@ -1032,6 +1032,7 @@ class nsPresContext : public nsISupports, public mozilla::SupportsWeakPtr {
bool HadContentfulPaint() const { return mHadContentfulPaint; }
void NotifyNonBlankPaint();
void NotifyContentfulPaint();
void NotifyPaintStatusReset();
void NotifyDOMContentFlushed();
bool UsesExChUnits() const { return mUsesExChUnits; }

Просмотреть файл

@ -163,6 +163,12 @@ class ContentDelegateChild extends GeckoViewActorChild {
});
break;
}
case "MozPaintStatusReset": {
this.eventDispatcher.sendRequest({
type: "GeckoView:PaintStatusReset",
});
break;
}
}
}
}

Просмотреть файл

@ -571,6 +571,7 @@ function startup() {
"MozDOMFullscreen:Exited": {},
"MozDOMFullscreen:Request": {},
MozFirstContentfulPaint: {},
MozPaintStatusReset: {},
contextmenu: { capture: true },
},
allFrames: true,

Просмотреть файл

@ -748,6 +748,7 @@ package org.mozilla.geckoview {
method @UiThread default public void onFullScreen(@NonNull GeckoSession, boolean);
method @UiThread default public void onKill(@NonNull GeckoSession);
method @UiThread default public void onMetaViewportFitChange(@NonNull GeckoSession, @NonNull String);
method @UiThread default public void onPaintStatusReset(@NonNull GeckoSession);
method @UiThread @Nullable default public GeckoResult<SlowScriptResponse> onSlowScript(@NonNull GeckoSession, @NonNull String);
method @UiThread default public void onTitleChange(@NonNull GeckoSession, @Nullable String);
method @UiThread default public void onWebAppManifest(@NonNull GeckoSession, @NonNull JSONObject);

Просмотреть файл

@ -356,6 +356,7 @@ public class GeckoSession implements Parcelable {
"GeckoView:FullScreenExit",
"GeckoView:WebAppManifest",
"GeckoView:FirstContentfulPaint",
"GeckoView:PaintStatusReset",
}
) {
@Override
@ -413,6 +414,8 @@ public class GeckoSession implements Parcelable {
}
} else if ("GeckoView:FirstContentfulPaint".equals(event)) {
delegate.onFirstContentfulPaint(GeckoSession.this);
} else if ("GeckoView:PaintStatusReset".equals(event)) {
delegate.onPaintStatusReset(GeckoSession.this);
}
}
};
@ -3334,9 +3337,10 @@ public class GeckoSession implements Parcelable {
/**
* Notification that the first content paint has occurred.
* This callback is invoked for the first content paint after
* a page has been loaded. The function {@link #onFirstComposite(GeckoSession)}
* will be called once the compositor has started rendering. However, it is
* possible for the compositor to start rendering before there is any content to render.
* a page has been loaded, or after a {@link #onPaintStatusReset(GeckoSession)}
* event. The function {@link #onFirstComposite(GeckoSession)} will be called
* once the compositor has started rendering. However, it is possible for the
* compositor to start rendering before there is any content to render.
* onFirstContentfulPaint() is called once some content has been rendered. It may be nothing
* more than the page background color. It is not an indication that the whole page has
* been rendered.
@ -3345,6 +3349,21 @@ public class GeckoSession implements Parcelable {
@UiThread
default void onFirstContentfulPaint(@NonNull GeckoSession session) {}
/**
* Notification that the paint status has been reset.
*
* This callback is invoked whenever the painted content is no longer being
* displayed. This can occur in response to the session being paused.
* After this has fired the compositor may continue rendering, but may not
* render the page content. This callback can therefore be used in conjunction
* with {@link #onFirstContentfulPaint(GeckoSession)} to determine when there is
* valid content being rendered.
*
* @param session The GeckoSession that had the paint status reset event.
*/
@UiThread
default void onPaintStatusReset(@NonNull GeckoSession session) {}
/**
* This is fired when the loaded document has a valid Web App Manifest present.
*

Просмотреть файл

@ -16,9 +16,13 @@ exclude: true
## v81
- Added `cookiePurging` to [`ContentBlocking.Settings.Builder`][81.1] and `getCookiePurging` and `setCookiePurging`
to [`ContentBlocking.Settings`][81.2].
- Added [`GeckoSession.ContentDelegate.onPaintStatusReset()`][81.3] callback which notifies when valid content is no longer being rendered.
- Made [`GeckoSession.ContentDelegate.onFirstContentfulPaint()`][81.4] additionally be called for the first contentful paint following a `onPaintStatusReset()` event, rather than just the first contentful paint of the session.
[81.1]: {{javadoc_uri}}/ContentBlocking.Settings.Builder.html
[81.2]: {{javadoc_uri}}/ContentBlocking.Settings.html
[81.3]: {{javadoc_uri}}/GeckoSession.ContentDelegate.html#onPaintStatusReset-org.mozilla.geckoview.GeckoSession-
[81.4]: {{javadoc_uri}}/GeckoSession.ContentDelegate.html#onFirstContentfulPaint-org.mozilla.geckoview.GeckoSession-
## v80
- Removed `GeckoSession.hashCode` and `GeckoSession.equals` overrides in favor
@ -761,4 +765,4 @@ to allow adding gecko profiler markers.
[65.24]: {{javadoc_uri}}/CrashReporter.html#sendCrashReport-android.content.Context-android.os.Bundle-java.lang.String-
[65.25]: {{javadoc_uri}}/GeckoResult.html
[api-version]: a3245a37268efa5b1bbf787a3aca046670d36cdb
[api-version]: 9f4bf24e0795c8da7caf58487e659918ea1a56cc