Bug 1511235 - Part 2: Add test. r=jya,baku, a=RyanVM
authoralwu <alwu@mozilla.com>
Thu, 20 Dec 2018 20:01:46 +0000
changeset 506425 9d3238bbfcc6
parent 506424 2616da988292
child 506426 49db147e8a5c
push id10410
push userryanvm@gmail.com
push dateSun, 30 Dec 2018 23:47:52 +0000
treeherdermozilla-beta@1d747c8ca942 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjya, baku, RyanVM
bugs1511235
milestone65.0
Bug 1511235 - Part 2: Add test. r=jya,baku, a=RyanVM Add new webidl method for testing only and a test. Differential Revision: https://phabricator.services.mozilla.com/D13805
dom/html/HTMLMediaElement.cpp
dom/html/HTMLMediaElement.h
dom/media/MediaDecoder.cpp
dom/media/MediaDecoder.h
dom/webidl/HTMLMediaElement.webidl
toolkit/content/tests/browser/browser.ini
toolkit/content/tests/browser/browser_suspend_videos_outside_viewport.js
toolkit/content/tests/browser/file_outside_viewport_videos.html
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -1625,16 +1625,20 @@ already_AddRefed<Promise> HTMLMediaEleme
 
 void HTMLMediaElement::SetVisible(bool aVisible) {
   mForcedHidden = !aVisible;
   if (mDecoder) {
     mDecoder->SetForcedHidden(!aVisible);
   }
 }
 
+bool HTMLMediaElement::IsVideoDecodingSuspended() const {
+  return mDecoder && mDecoder->IsVideoDecodingSuspended();
+}
+
 already_AddRefed<layers::Image> HTMLMediaElement::GetCurrentImage() {
   MarkAsTainted();
 
   // TODO: In bug 1345404, handle case when video decoder is already suspended.
   ImageContainer* container = GetImageContainer();
   if (!container) {
     return nullptr;
   }
--- a/dom/html/HTMLMediaElement.h
+++ b/dom/html/HTMLMediaElement.h
@@ -555,16 +555,19 @@ class HTMLMediaElement : public nsGeneri
   already_AddRefed<Promise> MozDumpDebugInfo();
 
   // For use by mochitests. Enabling pref "media.test.video-suspend"
   void SetVisible(bool aVisible);
 
   // For use by mochitests. Enabling pref "media.test.video-suspend"
   bool HasSuspendTaint() const;
 
+  // For use by mochitests.
+  bool IsVideoDecodingSuspended() const;
+
   // Synchronously, return the next video frame and mark the element unable to
   // participate in decode suspending.
   //
   // This function is synchronous for cases where decoding has been suspended
   // and JS needs a frame to use in, eg., nsLayoutUtils::SurfaceFromElement()
   // via drawImage().
   already_AddRefed<layers::Image> GetCurrentImage();
 
--- a/dom/media/MediaDecoder.cpp
+++ b/dom/media/MediaDecoder.cpp
@@ -430,19 +430,21 @@ void MediaDecoder::OnPlaybackEvent(Media
     case MediaPlaybackEvent::SeekStarted:
       SeekingStarted();
       break;
     case MediaPlaybackEvent::Invalidate:
       Invalidate();
       break;
     case MediaPlaybackEvent::EnterVideoSuspend:
       GetOwner()->DispatchAsyncEvent(NS_LITERAL_STRING("mozentervideosuspend"));
+      mIsVideoDecodingSuspended = true;
       break;
     case MediaPlaybackEvent::ExitVideoSuspend:
       GetOwner()->DispatchAsyncEvent(NS_LITERAL_STRING("mozexitvideosuspend"));
+      mIsVideoDecodingSuspended = false;
       break;
     case MediaPlaybackEvent::StartVideoSuspendTimer:
       GetOwner()->DispatchAsyncEvent(
           NS_LITERAL_STRING("mozstartvideosuspendtimer"));
       break;
     case MediaPlaybackEvent::CancelVideoSuspendTimer:
       GetOwner()->DispatchAsyncEvent(
           NS_LITERAL_STRING("mozcancelvideosuspendtimer"));
@@ -455,16 +457,20 @@ void MediaDecoder::OnPlaybackEvent(Media
       GetOwner()->DispatchAsyncEvent(
           NS_LITERAL_STRING("mozvideoonlyseekcompleted"));
       break;
     default:
       break;
   }
 }
 
+bool MediaDecoder::IsVideoDecodingSuspended() const {
+  return mIsVideoDecodingSuspended;
+}
+
 void MediaDecoder::OnPlaybackErrorEvent(const MediaResult& aError) {
   DecodeError(aError);
 }
 
 void MediaDecoder::OnDecoderDoctorEvent(DecoderDoctorEvent aEvent) {
   MOZ_ASSERT(NS_IsMainThread());
   // OnDecoderDoctorEvent is disconnected at shutdown time.
   MOZ_DIAGNOSTIC_ASSERT(!IsShutdown());
--- a/dom/media/MediaDecoder.h
+++ b/dom/media/MediaDecoder.h
@@ -307,16 +307,18 @@ class MediaDecoder : public DecoderDocto
 
   // Returns true if the decoder can't participate in suspend-video-decoder.
   bool HasSuspendTaint() const;
 
   void UpdateVideoDecodeMode();
 
   void SetIsBackgroundVideoDecodingAllowed(bool aAllowed);
 
+  bool IsVideoDecodingSuspended() const;
+
   /******
    * The following methods must only be called on the main
    * thread.
    ******/
 
   // Change to a new play state. This updates the mState variable and
   // notifies any thread blocking on this object's monitor of the
   // change. Call on the main thread only.
@@ -573,16 +575,19 @@ class MediaDecoder : public DecoderDocto
   MediaEventListener mOnPlaybackErrorEvent;
   MediaEventListener mOnDecoderDoctorEvent;
   MediaEventListener mOnMediaNotSeekable;
   MediaEventListener mOnEncrypted;
   MediaEventListener mOnWaitingForKey;
   MediaEventListener mOnDecodeWarning;
   MediaEventListener mOnNextFrameStatus;
 
+  // True if we have suspended video decoding.
+  bool mIsVideoDecodingSuspended = false;
+
  protected:
   // PlaybackRate and pitch preservation status we should start at.
   double mPlaybackRate;
 
   // True if the decoder is seeking.
   Watchable<bool> mLogicallySeeking;
 
   // Buffered range, mirrored from the reader.
--- a/dom/webidl/HTMLMediaElement.webidl
+++ b/dom/webidl/HTMLMediaElement.webidl
@@ -199,30 +199,34 @@ partial interface HTMLMediaElement {
  *     event and an "ended" event.
  */
 partial interface HTMLMediaElement {
   [Throws, Pref="media.seekToNextFrame.enabled"]
   Promise<void> seekToNextFrame();
 };
 
 /*
- * This is an API for simulating visibility changes to help debug and write
+ * These APIs are testing only, they are used to simulate visibility changes to help debug and write
  * tests about suspend-video-decoding.
  *
  * - SetVisible() is for simulating visibility changes.
  * - HasSuspendTaint() is for querying that the element's decoder cannot suspend
  *   video decoding because it has been tainted by an operation, such as
  *   drawImage().
+ * - isVideoDecodingSuspended() is used to know whether video decoding has suspended.
  */
 partial interface HTMLMediaElement {
   [Pref="media.test.video-suspend"]
   void setVisible(boolean aVisible);
 
   [Pref="media.test.video-suspend"]
   boolean hasSuspendTaint();
+
+  [ChromeOnly]
+  readonly attribute boolean isVideoDecodingSuspended;
 };
 
 /* Audio Output Devices API */
 partial interface HTMLMediaElement {
   [Pref="media.setsinkid.enabled"]
   readonly attribute DOMString sinkId;
   [Throws, Pref="media.setsinkid.enabled"]
   Promise<void> setSinkId(DOMString sinkId);
--- a/toolkit/content/tests/browser/browser.ini
+++ b/toolkit/content/tests/browser/browser.ini
@@ -98,16 +98,20 @@ uses-unsafe-cpows = true
 [browser_default_image_filename.js]
 [browser_default_image_filename_redirect.js]
 [browser_f7_caret_browsing.js]
 [browser_findbar.js]
 [browser_findbar_disabled_manual.js]
 [browser_isSynthetic.js]
 [browser_keyevents_during_autoscrolling.js]
 [browser_label_textlink.js]
+[browser_suspend_videos_outside_viewport.js]
+support-files =
+  file_outside_viewport_videos.html
+  gizmo.mp4
 [browser_mediaPlayback.js]
 tags = audiochannel
 [browser_mediaPlayback_mute.js]
 tags = audiochannel
 [browser_mediaPlayback_suspended.js]
 tags = audiochannel
 [browser_mediaPlayback_suspended_multipleAudio.js]
 tags = audiochannel
new file mode 100644
--- /dev/null
+++ b/toolkit/content/tests/browser/browser_suspend_videos_outside_viewport.js
@@ -0,0 +1,33 @@
+/**
+ * This test is used to ensure we suspend video decoding if video is not in the
+ * viewport.
+ */
+"use strict";
+
+const PAGE = "https://example.com/browser/toolkit/content/tests/browser/file_outside_viewport_videos.html";
+
+async function test_suspend_video_decoding() {
+  let videos = content.document.getElementsByTagName("video");
+  for (let video of videos) {
+    info(`- start video on the ${video.id} side and outside the viewport -`);
+    await video.play();
+    ok(true, `video started playing`);
+    ok(video.isVideoDecodingSuspended, `video decoding is suspended`);
+  }
+}
+
+add_task(async function setup_test_preference() {
+  await SpecialPowers.pushPrefEnv({"set": [
+    ["media.suspend-bkgnd-video.enabled", true],
+    ["media.suspend-bkgnd-video.delay-ms", 0],
+  ]});
+});
+
+add_task(async function start_test() {
+  await BrowserTestUtils.withNewTab({
+    gBrowser,
+    url: PAGE,
+  }, async browser => {
+    await ContentTask.spawn(browser, null, test_suspend_video_decoding);
+  });
+});
new file mode 100644
--- /dev/null
+++ b/toolkit/content/tests/browser/file_outside_viewport_videos.html
@@ -0,0 +1,41 @@
+<html>
+<head>
+  <title>outside viewport videos</title>
+<style>
+/**
+ * These CSS would move elements to the far left/right/top/bottom where user
+ * can not see elements in the viewport if user doesn't scroll the page.
+ */
+.outside-left {
+  position: absolute;
+  left: -1000%;
+}
+.outside-right {
+  position: absolute;
+  right: -1000%;
+}
+.outside-top {
+  position: absolute;
+  top: -1000%;
+}
+.outside-bottom {
+  position: absolute;
+  bottom: -1000%;
+}
+</style>
+</head>
+<body>
+	<div class="outside-left">
+		<video id="left" src="gizmo.mp4">
+	</div>
+	<div class="outside-right">
+		<video id="right" src="gizmo.mp4">
+	</div>
+	<div class="outside-top">
+		<video id="top" src="gizmo.mp4">
+	</div>
+	<div class="outside-bottom">
+		<video id="bottom" src="gizmo.mp4">
+	</div>
+</body>
+</html>