Bug 1819814 - Call MediaSession.Delegate.onFullscreen correctly when entering full screen. r=geckoview-reviewers,calu

This is a race condition of full screen event and activated event of media
controller.

Media controller will dispatch activated event when full screen button is
clicked on controller. But since this depends on full screen event, if
GeckoView's media session receives this event before controller fires activated
event, `MediaSession.Delegate.onFullscreen` won't be called because
MediaSession.isActive() is false at this time.

So I would like to retry `GeckoView:MediaSession:Fullscreen` call when
entering full screen and controller isn't active yet.

Differential Revision: https://phabricator.services.mozilla.com/D173831
This commit is contained in:
Makoto Kato 2023-03-30 14:26:13 +00:00
Родитель 6b7b637d36
Коммит f5090d66a1
4 изменённых файлов: 107 добавлений и 4 удалений

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

@ -14,6 +14,10 @@ const { XPCOMUtils } = ChromeUtils.importESModule(
const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
setTimeout: "resource://gre/modules/Timer.sys.mjs",
});
XPCOMUtils.defineLazyModuleGetters(lazy, {
MediaUtils: "resource://gre/modules/MediaUtils.jsm",
});
@ -27,12 +31,12 @@ class MediaControlDelegateChild extends GeckoViewActorChild {
switch (aEvent.type) {
case "MozDOMFullscreen:Entered":
case "MozDOMFullscreen:Exited":
this.handleFullscreenChanged();
this.handleFullscreenChanged(true);
break;
}
}
handleFullscreenChanged() {
async handleFullscreenChanged(retry) {
debug`handleFullscreenChanged`;
const element = this.document.fullscreenElement;
@ -43,11 +47,23 @@ class MediaControlDelegateChild extends GeckoViewActorChild {
debug`No fullscreen media element found.`;
}
this.eventDispatcher.sendRequest({
const activated = await this.eventDispatcher.sendRequestForResult({
type: "GeckoView:MediaSession:Fullscreen",
metadata: lazy.MediaUtils.getMetadata(mediaElement) ?? {},
enabled: !!element,
});
if (activated) {
return;
}
if (retry && element) {
// When media session is going to active, we have a race condition of
// full screen event because media session will be activated by full
// screen event.
// So we retry to call media session delegate for this situation.
lazy.setTimeout(() => {
this.handleFullscreenChanged(false);
}, 100);
}
}
}

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

@ -953,4 +953,47 @@ class MediaSessionTest : BaseSessionTest() {
mediaSession1!!.pause()
sessionRule.waitForResult(completedStep5)
}
@Test
fun fullscreenVideoWithActivated() {
// TODO: bug 1810736
assumeThat(sessionRule.env.isIsolatedProcess, equalTo(false))
sessionRule.setPrefsUntilTestEnd(
mapOf(
"media.autoplay.default" to 0,
"full-screen-api.allow-trusted-requests-only" to false
)
)
val path = VIDEO_WEBM_PATH
val session = sessionRule.createOpenSession()
val resultFullscreen = GeckoResult<Void>()
session.loadTestPath(path)
sessionRule.waitForPageStop()
session.delegateDuringNextWait(object : MediaSession.Delegate {
override fun onFullscreen(
session: GeckoSession,
mediaSession: MediaSession,
enabled: Boolean,
meta: MediaSession.ElementMetadata?
) {
assertThat(
"Fullscreen should be enabled",
enabled,
equalTo(true)
)
assertThat(
"Element metadata should exist",
meta,
notNullValue()
)
resultFullscreen.complete(null)
}
})
session.evaluateJS("document.querySelector('video').requestFullscreen()")
sessionRule.waitForResult(resultFullscreen)
}
}

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

@ -629,10 +629,18 @@ public class MediaSession {
} else if (FEATURES_EVENT.equals(event)) {
final long features = Feature.fromBundle(message.getBundle("features"));
delegate.onFeatures(mSession, mMediaSession, features);
} else if (FULLSCREEN_EVENT.equals(event) && mMediaSession.isActive()) {
} else if (FULLSCREEN_EVENT.equals(event)) {
final boolean enabled = message.getBoolean("enabled");
final ElementMetadata meta = ElementMetadata.fromBundle(message.getBundle("metadata"));
if (!mMediaSession.isActive()) {
if (DEBUG) {
Log.d(LOGTAG, "Media session is not active yet");
}
callback.sendSuccess(false);
return;
}
delegate.onFullscreen(mSession, mMediaSession, enabled, meta);
callback.sendSuccess(true);
}
}
}

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

@ -17,6 +17,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentSender;
import android.content.SharedPreferences;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.graphics.Bitmap;
@ -82,6 +83,7 @@ import org.mozilla.geckoview.GeckoSessionSettings;
import org.mozilla.geckoview.GeckoView;
import org.mozilla.geckoview.GeckoWebExecutor;
import org.mozilla.geckoview.Image;
import org.mozilla.geckoview.MediaSession;
import org.mozilla.geckoview.OrientationController;
import org.mozilla.geckoview.RuntimeTelemetry;
import org.mozilla.geckoview.SlowScriptResponse;
@ -1118,6 +1120,8 @@ public class GeckoViewActivity extends AppCompatActivity
session.setMediaDelegate(new ExampleMediaDelegate(this));
session.setMediaSessionDelegate(new ExampleMediaSessionDelegate(this));
session.setSelectionActionDelegate(new BasicSelectionActionDelegate(this));
if (sExtensionManager.extension != null) {
final WebExtension.SessionController sessionController = session.getWebExtensionController();
@ -2468,6 +2472,38 @@ public class GeckoViewActivity extends AppCompatActivity
}
}
private class ExampleMediaSessionDelegate implements MediaSession.Delegate {
private final Activity mActivity;
public ExampleMediaSessionDelegate(Activity activity) {
mActivity = activity;
}
@Override
public void onFullscreen(
@NonNull final GeckoSession session,
@NonNull final MediaSession mediaSession,
final boolean enabled,
@Nullable final MediaSession.ElementMetadata meta) {
Log.d(LOGTAG, "onFullscreen: Metadata=" + (meta != null ? meta.toString() : "null"));
if (!enabled) {
mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_USER);
return;
}
if (meta == null) {
return;
}
if (meta.width > meta.height) {
mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE);
} else {
mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT);
}
}
}
private final class ExampleTelemetryDelegate implements RuntimeTelemetry.Delegate {
@Override
public void onHistogram(final @NonNull RuntimeTelemetry.Histogram histogram) {