Bug 1522324 - Update GeckoView API to match current Gecko autoplay implementation r=geckoview-reviewers,esawin
☠☠ backed out by 44c507a00940 ☠ ☠
authorRandall Barker <rbarker@mozilla.com>
Tue, 12 Feb 2019 20:25:52 +0000
changeset 458803 f145dfc61991
parent 458802 b51c6d4af327
child 458804 34f33ae5913e
push id35548
push useropoprus@mozilla.com
push dateWed, 13 Feb 2019 09:48:26 +0000
treeherdermozilla-central@93e37c529818 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgeckoview-reviewers, esawin
bugs1522324
milestone67.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1522324 - Update GeckoView API to match current Gecko autoplay implementation r=geckoview-reviewers,esawin Differential Revision: https://phabricator.services.mozilla.com/D19209
mobile/android/geckoview/api.txt
mobile/android/geckoview/src/androidTest/assets/www/autoplay.html
mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/BaseSessionTest.kt
mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/MediaElementTest.kt
mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoRuntimeSettings.java
mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoSession.java
mobile/android/geckoview/src/main/java/org/mozilla/geckoview/doc-files/CHANGELOG.md
mobile/android/geckoview_example/src/main/java/org/mozilla/geckoview_example/GeckoViewActivity.java
mobile/android/geckoview_example/src/main/res/values/strings.xml
--- a/mobile/android/geckoview/api.txt
+++ b/mobile/android/geckoview/api.txt
@@ -202,42 +202,47 @@ package org.mozilla.geckoview {
   }
 
   public static interface GeckoRuntime.Delegate {
     method @android.support.annotation.UiThread public void onShutdown();
   }
 
   @android.support.annotation.AnyThread public final class GeckoRuntimeSettings extends org.mozilla.geckoview.RuntimeSettings {
     method @android.support.annotation.NonNull public java.lang.String[] getArguments();
+    method public int getAutoplayDefault();
     method public boolean getConsoleOutputEnabled();
     method @android.support.annotation.NonNull public org.mozilla.geckoview.ContentBlocking.Settings getContentBlocking();
     method @android.support.annotation.Nullable public java.lang.Class<?> getCrashHandler();
     method @android.support.annotation.Nullable public java.lang.Float getDisplayDensityOverride();
     method @android.support.annotation.Nullable public java.lang.Integer getDisplayDpiOverride();
     method @android.support.annotation.NonNull public android.os.Bundle getExtras();
     method public boolean getJavaScriptEnabled();
     method @android.support.annotation.Nullable public java.lang.String[] getLocales();
     method public boolean getPauseForDebuggerEnabled();
     method public boolean getRemoteDebuggingEnabled();
     method @android.support.annotation.Nullable public org.mozilla.geckoview.GeckoRuntime getRuntime();
     method @android.support.annotation.Nullable public android.graphics.Rect getScreenSizeOverride();
     method public boolean getUseContentProcessHint();
     method public boolean getUseMaxScreenDepth();
     method public boolean getWebFontsEnabled();
+    method @android.support.annotation.NonNull public org.mozilla.geckoview.GeckoRuntimeSettings setAutoplayDefault(int);
     method @android.support.annotation.NonNull public org.mozilla.geckoview.GeckoRuntimeSettings setConsoleOutputEnabled(boolean);
     method @android.support.annotation.NonNull public org.mozilla.geckoview.GeckoRuntimeSettings setJavaScriptEnabled(boolean);
     method public void setLocales(@android.support.annotation.Nullable java.lang.String[]);
     method @android.support.annotation.NonNull public org.mozilla.geckoview.GeckoRuntimeSettings setRemoteDebuggingEnabled(boolean);
     method @android.support.annotation.NonNull public org.mozilla.geckoview.GeckoRuntimeSettings setWebFontsEnabled(boolean);
+    field public static final int AUTOPLAY_DEFAULT_ALLOWED = 0;
+    field public static final int AUTOPLAY_DEFAULT_BLOCKED = 1;
     field public static final android.os.Parcelable.Creator<org.mozilla.geckoview.GeckoRuntimeSettings> CREATOR;
   }
 
   @android.support.annotation.AnyThread public static final class GeckoRuntimeSettings.Builder extends org.mozilla.geckoview.RuntimeSettings.Builder<Settings extends org.mozilla.geckoview.RuntimeSettings> {
     ctor public Builder();
     method @android.support.annotation.NonNull public org.mozilla.geckoview.GeckoRuntimeSettings.Builder arguments(@android.support.annotation.NonNull java.lang.String[]);
+    method @android.support.annotation.NonNull public org.mozilla.geckoview.GeckoRuntimeSettings.Builder autoplayDefault(int);
     method @android.support.annotation.NonNull public org.mozilla.geckoview.GeckoRuntimeSettings.Builder consoleOutput(boolean);
     method @android.support.annotation.NonNull public org.mozilla.geckoview.GeckoRuntimeSettings.Builder contentBlocking(@android.support.annotation.NonNull org.mozilla.geckoview.ContentBlocking.Settings);
     method @android.support.annotation.NonNull public org.mozilla.geckoview.GeckoRuntimeSettings.Builder crashHandler(java.lang.Class<?>);
     method @android.support.annotation.NonNull public org.mozilla.geckoview.GeckoRuntimeSettings.Builder displayDensityOverride(float);
     method @android.support.annotation.NonNull public org.mozilla.geckoview.GeckoRuntimeSettings.Builder displayDpiOverride(int);
     method @android.support.annotation.NonNull public org.mozilla.geckoview.GeckoRuntimeSettings.Builder extras(@android.support.annotation.NonNull android.os.Bundle);
     method @android.support.annotation.NonNull public org.mozilla.geckoview.GeckoRuntimeSettings.Builder javaScriptEnabled(boolean);
     method @android.support.annotation.NonNull public org.mozilla.geckoview.GeckoRuntimeSettings.Builder locales(java.lang.String[]);
@@ -411,17 +416,16 @@ package org.mozilla.geckoview {
 
   public static interface GeckoSession.NavigationDelegate.TargetWindow implements java.lang.annotation.Annotation {
   }
 
   public static interface GeckoSession.PermissionDelegate {
     method @android.support.annotation.UiThread public void onAndroidPermissionsRequest(@android.support.annotation.NonNull org.mozilla.geckoview.GeckoSession, @android.support.annotation.Nullable java.lang.String[], @android.support.annotation.NonNull org.mozilla.geckoview.GeckoSession.PermissionDelegate.Callback);
     method @android.support.annotation.UiThread public void onContentPermissionRequest(@android.support.annotation.NonNull org.mozilla.geckoview.GeckoSession, @android.support.annotation.Nullable java.lang.String, int, @android.support.annotation.NonNull org.mozilla.geckoview.GeckoSession.PermissionDelegate.Callback);
     method @android.support.annotation.UiThread public void onMediaPermissionRequest(@android.support.annotation.NonNull org.mozilla.geckoview.GeckoSession, @android.support.annotation.NonNull java.lang.String, @android.support.annotation.Nullable org.mozilla.geckoview.GeckoSession.PermissionDelegate.MediaSource[], @android.support.annotation.Nullable org.mozilla.geckoview.GeckoSession.PermissionDelegate.MediaSource[], @android.support.annotation.NonNull org.mozilla.geckoview.GeckoSession.PermissionDelegate.MediaCallback);
-    field public static final int PERMISSION_AUTOPLAY_MEDIA = 2;
     field public static final int PERMISSION_DESKTOP_NOTIFICATION = 1;
     field public static final int PERMISSION_GEOLOCATION = 0;
   }
 
   public static interface GeckoSession.PermissionDelegate.Callback {
     method @android.support.annotation.UiThread public void grant();
     method @android.support.annotation.UiThread public void reject();
   }
new file mode 100644
--- /dev/null
+++ b/mobile/android/geckoview/src/androidTest/assets/www/autoplay.html
@@ -0,0 +1,8 @@
+<html>
+    <head><title>WEBM Video</title></head>
+    <body>
+        <video preload autoplay>
+            <source src="videos/gizmo.webm"></source>
+        </video>
+    </body>
+</html>
--- a/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/BaseSessionTest.kt
+++ b/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/BaseSessionTest.kt
@@ -46,16 +46,17 @@ open class BaseSessionTest(noErrorCollec
         const val VIDEO_MP4_PATH = "/assets/www/mp4.html"
         const val VIDEO_WEBM_PATH = "/assets/www/webm.html"
         const val VIDEO_BAD_PATH = "/assets/www/badVideoPath.html"
         const val UNKNOWN_HOST_URI = "http://www.test.invalid/"
         const val FULLSCREEN_PATH = "/assets/www/fullscreen.html"
         const val VIEWPORT_PATH = "/assets/www/viewport.html"
         const val IFRAME_REDIRECT_LOCAL = "/assets/www/iframe_redirect_local.html"
         const val IFRAME_REDIRECT_AUTOMATION = "/assets/www/iframe_redirect_automation.html"
+        const val AUTOPLAY_PATH = "/assets/www/autoplay.html"
     }
 
     @get:Rule val sessionRule = GeckoSessionTestRule()
 
     @get:Rule val errors = ErrorCollector()
 
     val mainSession get() = sessionRule.session
 
--- a/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/MediaElementTest.kt
+++ b/mobile/android/geckoview/src/androidTest/java/org/mozilla/geckoview/test/MediaElementTest.kt
@@ -13,16 +13,18 @@ import org.mozilla.geckoview.test.util.C
 
 import android.support.test.filters.MediumTest
 import android.support.test.runner.AndroidJUnit4
 import org.hamcrest.Matchers.*
 import org.junit.Assume.assumeThat
 import org.junit.Ignore
 import org.junit.Test
 import org.junit.runner.RunWith
+import org.mozilla.geckoview.GeckoRuntimeSettings
+import org.mozilla.geckoview.test.rule.GeckoSessionTestRule
 
 @RunWith(AndroidJUnit4::class)
 @TimeoutMillis(45000)
 @MediumTest
 class MediaElementTest : BaseSessionTest() {
 
     interface MediaElementDelegate : MediaElement.Delegate {
         override fun onPlaybackStateChange(mediaElement: MediaElement, mediaState: Int) {}
@@ -31,37 +33,44 @@ class MediaElementTest : BaseSessionTest
         override fun onLoadProgress(mediaElement: MediaElement, progressInfo: MediaElement.LoadProgressInfo) {}
         override fun onVolumeChange(mediaElement: MediaElement, volume: Double, muted: Boolean) {}
         override fun onTimeChange(mediaElement: MediaElement, time: Double) {}
         override fun onPlaybackRateChange(mediaElement: MediaElement, rate: Double) {}
         override fun onFullscreenChange(mediaElement: MediaElement, fullscreen: Boolean) {}
         override fun onError(mediaElement: MediaElement, errorCode: Int) {}
     }
 
-    private fun setupPrefsAndDelegates(path: String) {
+    private fun setupPrefs() {
+
         sessionRule.setPrefsUntilTestEnd(mapOf(
-                "media.autoplay.enabled.user-gestures-needed" to false,
                 "media.autoplay.default" to 0,
                 "full-screen-api.allow-trusted-requests-only" to false))
 
+    }
+
+    private fun setupDelegate(path: String) {
         sessionRule.session.loadTestPath(path)
         sessionRule.waitUntilCalled(object : Callbacks.MediaDelegate {
             @AssertCalled
             override fun onMediaAdd(session: GeckoSession, element: MediaElement) {
                 sessionRule.addExternalDelegateUntilTestEnd(
                         MediaElementDelegate::class,
                         element::setDelegate,
                         { element.delegate = null },
                         object : MediaElementDelegate {})
             }
         })
     }
 
-    private fun waitUntilVideoReady(path: String, waitState: Int = MediaElement.MEDIA_READY_STATE_HAVE_ENOUGH_DATA): MediaElement {
-        setupPrefsAndDelegates(path)
+    private fun setupPrefsAndDelegates(path: String) {
+        setupPrefs()
+        setupDelegate(path)
+    }
+
+    private fun waitUntilState(waitState: Int = MediaElement.MEDIA_READY_STATE_HAVE_ENOUGH_DATA): MediaElement {
         var ready = false
         var result: MediaElement? = null
         while (!ready) {
             sessionRule.waitUntilCalled(object : MediaElementDelegate {
                 @AssertCalled
                 override fun onReadyStateChange(mediaElement: MediaElement, readyState: Int) {
                     if (readyState == waitState) {
                         ready = true
@@ -71,16 +80,26 @@ class MediaElementTest : BaseSessionTest
             })
         }
         if (result == null) {
             throw IllegalStateException("No MediaElement Found")
         }
         return result!!
     }
 
+    private fun waitUntilVideoReady(path: String, waitState: Int = MediaElement.MEDIA_READY_STATE_HAVE_ENOUGH_DATA): MediaElement {
+        setupPrefsAndDelegates(path)
+        return waitUntilState(waitState)
+    }
+
+    private fun waitUntilVideoReadyNoPrefs(path: String, waitState: Int = MediaElement.MEDIA_READY_STATE_HAVE_ENOUGH_DATA): MediaElement {
+        setupDelegate(path)
+        return waitUntilState(waitState)
+    }
+
     private fun waitForPlaybackStateChange(waitState: Int, lambda: (element: MediaElement, state: Int) -> Unit = { _: MediaElement, _: Int -> }) {
         var waiting = true
         while (waiting) {
             sessionRule.waitUntilCalled(object : MediaElementDelegate {
                 @AssertCalled
                 override fun onPlaybackStateChange(mediaElement: MediaElement, mediaState: Int) {
                     if (mediaState == waitState) {
                         waiting = false
@@ -404,9 +423,81 @@ class MediaElementTest : BaseSessionTest
         sessionRule.waitForPageStop()
         sessionRule.waitUntilCalled(object : MediaElementDelegate {
             @AssertCalled
             override fun onError(mediaElement: MediaElement, errorCode: Int) {
                 assertThat("Got media error", errorCode, equalTo(MediaElement.MEDIA_ERROR_NETWORK_NO_SOURCE))
             }
         })
     }
+
+    @WithDevToolsAPI
+    @Test fun autoplay() {
+        sessionRule.runtime.settings.autoplayDefault = GeckoRuntimeSettings.AUTOPLAY_DEFAULT_ALLOWED
+
+        waitUntilVideoReadyNoPrefs(AUTOPLAY_PATH)
+        waitForPlaybackStateChange(MediaElement.MEDIA_STATE_PLAY)
+        waitForPlaybackStateChange(MediaElement.MEDIA_STATE_PLAYING)
+
+        // Restore default runtime settings
+        sessionRule.runtime.settings.autoplayDefault = GeckoRuntimeSettings.AUTOPLAY_DEFAULT_BLOCKED
+    }
+
+    @WithDevToolsAPI
+    @Test fun autoplayBlocked() {
+        sessionRule.runtime.settings.autoplayDefault = GeckoRuntimeSettings.AUTOPLAY_DEFAULT_BLOCKED
+
+        val media = waitUntilVideoReadyNoPrefs(AUTOPLAY_PATH)
+        val promise = sessionRule.evaluateJS(mainSession, "$('video').play()").asJSPromise()
+        var exceptionCaught = false
+        try {
+            val result = promise.value as Boolean
+            assertThat("Promise should not resolve", result, equalTo(false))
+        } catch (e: GeckoSessionTestRule.RejectedPromiseException) {
+            exceptionCaught = true;
+        }
+
+        assertThat("video.play() failed with exception", exceptionCaught, equalTo(true))
+        media.play()
+        /*
+        // Fails due to bug 1524092
+        waitForPlaybackStateChange(MediaElement.MEDIA_STATE_PLAY)
+        waitForPlaybackStateChange(MediaElement.MEDIA_STATE_PLAYING)
+        */
+    }
+
+    @WithDevToolsAPI
+    @Test fun autoplayNoUserInteractionRequired() {
+        sessionRule.runtime.settings.autoplayDefault = GeckoRuntimeSettings.AUTOPLAY_DEFAULT_ALLOWED
+
+        val media = waitUntilVideoReadyNoPrefs(VIDEO_WEBM_PATH)
+        sessionRule.evaluateJS(mainSession, "$('video').play()")
+        waitForPlaybackStateChange(MediaElement.MEDIA_STATE_PLAY)
+        waitForPlaybackStateChange(MediaElement.MEDIA_STATE_PLAYING)
+        media.pause()
+
+        media.play()
+        waitForPlaybackStateChange(MediaElement.MEDIA_STATE_PLAY)
+        waitForPlaybackStateChange(MediaElement.MEDIA_STATE_PLAYING)
+
+        // Restore default runtime settings
+        sessionRule.runtime.settings.autoplayDefault = GeckoRuntimeSettings.AUTOPLAY_DEFAULT_BLOCKED
+    }
+
+    @WithDevToolsAPI
+    @Test fun autoplayUserInteractionRequired() {
+        sessionRule.runtime.settings.autoplayDefault = GeckoRuntimeSettings.AUTOPLAY_DEFAULT_ALLOWED
+
+        waitUntilVideoReadyNoPrefs(VIDEO_WEBM_PATH)
+        val promise = sessionRule.evaluateJS(mainSession, "$('video').play()").asJSPromise()
+        var exceptionCaught = false
+        try {
+            promise.value
+        } catch (e: GeckoSessionTestRule.RejectedPromiseException) {
+            exceptionCaught = true;
+        }
+
+        assertThat("video.play() did not fail", exceptionCaught, equalTo(false))
+
+        // Restore default runtime settings
+        sessionRule.runtime.settings.autoplayDefault = GeckoRuntimeSettings.AUTOPLAY_DEFAULT_BLOCKED
+    }
 }
--- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoRuntimeSettings.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoRuntimeSettings.java
@@ -238,16 +238,27 @@ public final class GeckoRuntimeSettings 
             return this;
         }
 
         public @NonNull Builder contentBlocking(
                 final @NonNull ContentBlocking.Settings cb) {
             getSettings().mContentBlocking = cb;
             return this;
         }
+
+        /**
+         * Sets video autoplay mode.
+         * May be either {@link GeckoRuntimeSettings#AUTOPLAY_DEFAULT_ALLOWED} or {@link GeckoRuntimeSettings#AUTOPLAY_DEFAULT_BLOCKED}
+         * @param autoplay Allows or blocks video autoplay.
+         * @return This Builder instance.
+         */
+        public @NonNull Builder autoplayDefault(@AutoplayDefault int autoplay) {
+            getSettings().mAutoplayDefault.set(autoplay);
+            return this;
+        }
     }
 
     private GeckoRuntime mRuntime;
     /* package */ boolean mUseContentProcess;
     /* package */ String[] mArgs;
     /* package */ Bundle mExtras;
 
     /* package */ ContentBlocking.Settings mContentBlocking;
@@ -259,16 +270,18 @@ public final class GeckoRuntimeSettings 
     /* package */ final Pref<Boolean> mJavaScript = new Pref<Boolean>(
         "javascript.enabled", true);
     /* package */ final Pref<Boolean> mRemoteDebugging = new Pref<Boolean>(
         "devtools.debugger.remote-enabled", false);
     /* package */ final Pref<Integer> mWebFonts = new Pref<Integer>(
         "browser.display.use_document_fonts", 1);
     /* package */ final Pref<Boolean> mConsoleOutput = new Pref<Boolean>(
         "geckoview.console.enabled", false);
+    /* package */ final Pref<Integer> mAutoplayDefault = new Pref<Integer>(
+        "media.autoplay.default", AUTOPLAY_DEFAULT_BLOCKED);
 
     /* package */ boolean mDebugPause;
     /* package */ boolean mUseMaxScreenDepth;
     /* package */ float mDisplayDensityOverride = -1.0f;
     /* package */ int mDisplayDpiOverride;
     /* package */ int mScreenWidthOverride;
     /* package */ int mScreenHeightOverride;
     /* package */ Class<? extends Service> mCrashHandler;
@@ -520,16 +533,51 @@ public final class GeckoRuntimeSettings 
      * Get whether or not web console messages are sent to logcat.
      *
      * @return True if console output is enabled.
      */
     public boolean getConsoleOutputEnabled() {
         return mConsoleOutput.get();
     }
 
+    // Sync values with dom/media/nsIAutoplay.idl.
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({ AUTOPLAY_DEFAULT_ALLOWED, AUTOPLAY_DEFAULT_BLOCKED })
+    /* package */ @interface AutoplayDefault {}
+
+    /**
+     * Autoplay video is allowed.
+     */
+    public static final int AUTOPLAY_DEFAULT_ALLOWED = 0;
+
+    /**
+     * Autoplay video is blocked.
+     */
+    public static final int AUTOPLAY_DEFAULT_BLOCKED = 1;
+
+    /**
+     * Sets video autoplay mode.
+     * May be either {@link GeckoRuntimeSettings#AUTOPLAY_DEFAULT_ALLOWED} or {@link GeckoRuntimeSettings#AUTOPLAY_DEFAULT_BLOCKED}
+     * @param autoplay Allows or blocks video autoplay.
+     * @return This GeckoRuntimeSettings instance.
+     */
+    public @NonNull GeckoRuntimeSettings setAutoplayDefault(@AutoplayDefault int autoplay) {
+        mAutoplayDefault.commit(autoplay);
+        return this;
+    }
+
+    /**
+     * Gets the current video autoplay mode.
+     * @return The current video autoplay mode. Will be either {@link GeckoRuntimeSettings#AUTOPLAY_DEFAULT_ALLOWED}
+     * or {@link GeckoRuntimeSettings#AUTOPLAY_DEFAULT_BLOCKED}
+     */
+    public @AutoplayDefault int getAutoplayDefault() {
+        return mAutoplayDefault.get();
+    }
+
     @Override // Parcelable
     public void writeToParcel(Parcel out, int flags) {
         super.writeToParcel(out, flags);
 
         ParcelableUtils.writeBoolean(out, mUseContentProcess);
         out.writeStringArray(mArgs);
         mExtras.writeToParcel(out, flags);
         ParcelableUtils.writeBoolean(out, mDebugPause);
--- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoSession.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoSession.java
@@ -647,18 +647,16 @@ public class GeckoSession implements Par
                             new PermissionCallback("android", callback));
                 } else if ("GeckoView:ContentPermission".equals(event)) {
                     final String typeString = message.getString("perm");
                     final int type;
                     if ("geolocation".equals(typeString)) {
                         type = PermissionDelegate.PERMISSION_GEOLOCATION;
                     } else if ("desktop-notification".equals(typeString)) {
                         type = PermissionDelegate.PERMISSION_DESKTOP_NOTIFICATION;
-                    } else if ("autoplay-media".equals(typeString)) {
-                        type = PermissionDelegate.PERMISSION_AUTOPLAY_MEDIA;
                     } else {
                         throw new IllegalArgumentException("Unknown permission request: " + typeString);
                     }
                     delegate.onContentPermissionRequest(
                             GeckoSession.this, message.getString("uri"),
                             type, new PermissionCallback(typeString, callback));
                 } else if ("GeckoView:MediaPermission".equals(event)) {
                     GeckoBundle[] videoBundles = message.getBundleArray("video");
@@ -3806,37 +3804,32 @@ public class GeckoSession implements Par
      * two requests are generated: one request for the Android app permission through
      * requestAppPermissions, which is typically handled by a system permission dialog;
      * and another request for the content permission (e.g. through
      * requestContentPermission), which is typically handled by an app-specific
      * permission dialog.
      **/
     public interface PermissionDelegate {
         @Retention(RetentionPolicy.SOURCE)
-        @IntDef({PERMISSION_GEOLOCATION, PERMISSION_DESKTOP_NOTIFICATION, PERMISSION_AUTOPLAY_MEDIA})
+        @IntDef({PERMISSION_GEOLOCATION, PERMISSION_DESKTOP_NOTIFICATION})
         /* package */ @interface Permission {}
 
         /**
          * Permission for using the geolocation API.
          * See: https://developer.mozilla.org/en-US/docs/Web/API/Geolocation
          */
         public static final int PERMISSION_GEOLOCATION = 0;
 
         /**
          * Permission for using the notifications API.
          * See: https://developer.mozilla.org/en-US/docs/Web/API/notification
          */
         public static final int PERMISSION_DESKTOP_NOTIFICATION = 1;
 
         /**
-         * Permission for allowing auto-playing media.
-         */
-        public static final int PERMISSION_AUTOPLAY_MEDIA = 2;
-
-        /**
          * Callback interface for notifying the result of a permission request.
          */
         interface Callback {
             /**
              * Called by the implementation after permissions are granted; the
              * implementation must call either grant() or reject() for every request.
              */
             @UiThread
@@ -3869,17 +3862,16 @@ public class GeckoSession implements Par
         /**
          * Request content permission.
          *
          * @param session GeckoSession instance requesting the permission.
          * @param uri The URI of the content requesting the permission.
          * @param type The type of the requested permission; possible values are,
          *             PERMISSION_GEOLOCATION
          *             PERMISSION_DESKTOP_NOTIFICATION
-         *             PERMISSION_AUTOPLAY_MEDIA
          * @param callback Callback interface.
          */
         @UiThread
         void onContentPermissionRequest(@NonNull GeckoSession session, @Nullable String uri,
                                         @Permission int type, @NonNull Callback callback);
 
         class MediaSource {
             @Retention(RetentionPolicy.SOURCE)
--- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/doc-files/CHANGELOG.md
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/doc-files/CHANGELOG.md
@@ -4,16 +4,20 @@ title: API Changelog
 description: GeckoView API Changelog.
 nav_exclude: true
 exclude: true
 ---
 
 <h1> GeckoView API Changelog. </h1>
 
 ## v67
+- Updated video autoplay API to reflect changes in Gecko. Instead of being a per-video
+  permission in the PermissionDelegate, it is a runtime setting that either allows or
+  blocks autoplay videos.
+
 - Change `ContentBlocking.AT_ALL` and `ContentBlocking.SB_ALL` values to mirror
   the actual constants they encompass.
 
 - Added nested `ContentBlocking` runtime settings.
 
 - Added `RuntimeSettings` base class to support nested settings.
 
 - Added `baseUri` to [`ContentDelegate.ContextElement`][65.21] and changed
@@ -123,9 +127,9 @@ exclude: true
 [65.23]: ../GeckoSession.FinderResult.html
 
 - Update [`CrashReporter#sendCrashReport`][65.24] to return the crash ID as a
   [`GeckoResult<String>`][65.25].
 
 [65.24]: ../CrashReporter.html#sendCrashReport-android.content.Context-android.os.Bundle-java.lang.String-
 [65.25]: ../GeckoResult.html
 
-[api-version]: 1a31e792d4802cb18219b7ac5281a63a89a1df75
+[api-version]: a42a6f4481dd690ac46f14d8e692785bb00e8b04
--- a/mobile/android/geckoview_example/src/main/java/org/mozilla/geckoview_example/GeckoViewActivity.java
+++ b/mobile/android/geckoview_example/src/main/java/org/mozilla/geckoview_example/GeckoViewActivity.java
@@ -614,18 +614,16 @@ public class GeckoViewActivity extends A
         @Override
         public void onContentPermissionRequest(final GeckoSession session, final String uri,
                                              final int type, final Callback callback) {
             final int resId;
             if (PERMISSION_GEOLOCATION == type) {
                 resId = R.string.request_geolocation;
             } else if (PERMISSION_DESKTOP_NOTIFICATION == type) {
                 resId = R.string.request_notification;
-            } else if (PERMISSION_AUTOPLAY_MEDIA == type) {
-                resId = R.string.request_autoplay;
             } else {
                 Log.w(LOGTAG, "Unknown permission: " + type);
                 callback.reject();
                 return;
             }
 
             final String title = getString(resId, Uri.parse(uri).getAuthority());
             final BasicGeckoViewPrompt prompt = (BasicGeckoViewPrompt)
--- a/mobile/android/geckoview_example/src/main/res/values/strings.xml
+++ b/mobile/android/geckoview_example/src/main/res/values/strings.xml
@@ -1,16 +1,15 @@
 <resources>
     <string name="app_name">GeckoView Example</string>
     <string name="activity_label">GeckoView Example</string>
     <string name="location_hint">Enter URL or search keywords...</string>
     <string name="username">Username</string>
     <string name="password">Password</string>
     <string name="clear_field">Clear</string>
-    <string name="request_autoplay">Allow media to play on "%1$s"?</string>
     <string name="request_geolocation">Share location with "%1$s"?</string>
     <string name="request_notification">Allow notifications for "%1$s"?</string>
     <string name="request_video">Share video with "%1$s"</string>
     <string name="request_audio">Share audio with "%1$s"</string>
     <string name="request_media">Share video and audio with "%1$s"</string>
     <string name="media_back_camera">Back camera</string>
     <string name="media_front_camera">Front camera</string>
     <string name="media_microphone">Microphone</string>