Bug 1513733 - part3 : add test. r=baku,karlt
authorAlastor Wu <alwu@mozilla.com>
Fri, 11 Jan 2019 18:45:31 +0000
changeset 453533 22ce92cbdf64
parent 453532 c34e287f2f7c
child 453534 8699d4c48838
push id35360
push usernbeleuzu@mozilla.com
push dateSat, 12 Jan 2019 09:39:47 +0000
treeherdermozilla-central@cb35977ae7a4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbaku, karlt
bugs1513733
milestone66.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 1513733 - part3 : add test. r=baku,karlt Add test and mochitest-only function in document. Differential Revision: https://phabricator.services.mozilla.com/D14913
dom/base/Document.cpp
dom/base/Document.h
dom/media/test/mochitest.ini
dom/media/test/test_autoplay_policy_web_audio_mediaElementAudioSourceNode.html
dom/webidl/Document.webidl
--- a/dom/base/Document.cpp
+++ b/dom/base/Document.cpp
@@ -11686,16 +11686,26 @@ void Document::MaybeNotifyAutoplayBlocke
   // This event is used to notify front-end side that we've blocked autoplay,
   // so front-end side should show blocking icon as well.
   RefPtr<AsyncEventDispatcher> asyncDispatcher = new AsyncEventDispatcher(
       topLevelDoc, NS_LITERAL_STRING("GloballyAutoplayBlocked"),
       CanBubble::eYes, ChromeOnlyDispatch::eYes);
   asyncDispatcher->PostDOMEvent();
 }
 
+void Document::ClearUserGestureActivation() {
+  Document* doc = this;
+  while (doc) {
+    MOZ_LOG(gUserInteractionPRLog, LogLevel::Debug,
+          ("Reset user activation flag for document %p.", this));
+    doc->mUserGestureActivated = false;
+    doc = doc->GetSameTypeParentDocument();
+  }
+}
+
 void Document::SetDocTreeHadAudibleMedia() {
   Document* topLevelDoc = GetTopLevelContentDocument();
   if (!topLevelDoc) {
     return;
   }
 
   topLevelDoc->mDocTreeHadAudibleMedia = true;
 }
--- a/dom/base/Document.h
+++ b/dom/base/Document.h
@@ -3290,16 +3290,19 @@ class Document : public nsINode,
   // This should be called when this document receives events which are likely
   // to be user interaction with the document, rather than the byproduct of
   // interaction with the browser (i.e. a keypress to scroll the view port,
   // keyboard shortcuts, etc). This is used to decide whether we should
   // permit autoplay audible media. This also gesture activates all other
   // content documents in this tab.
   void NotifyUserGestureActivation();
 
+  // This function is used for mochitest only.
+  void ClearUserGestureActivation();
+
   // Return true if NotifyUserGestureActivation() has been called on any
   // document in the document tree.
   bool HasBeenUserGestureActivated();
 
   // This document is a WebExtension page, it might be a background page, a
   // popup, a visible tab, a visible iframe ...e.t.c.
   bool IsExtensionPage() const;
 
--- a/dom/media/test/mochitest.ini
+++ b/dom/media/test/mochitest.ini
@@ -719,16 +719,18 @@ skip-if = android_version >= '23' # bug 
 [test_autoplay_policy_key_blacklist.html]
 skip-if = android_version >= '23' || (verify && debug && (os == 'win')) # bug 1424903
 [test_autoplay_policy_unmute_pauses.html]
 skip-if = android_version >= '23' # bug 1424903
 [test_autoplay_policy_play_before_loadedmetadata.html]
 skip-if = android_version >= '23' # bug 1424903
 [test_autoplay_policy_permission.html]
 skip-if = android_version >= '23' # bug 1424903
+[test_autoplay_policy_web_audio_mediaElementAudioSourceNode.html]
+skip-if = android_version >= '23' # bug 1424903
 [test_buffered.html]
 skip-if = android_version == '22' # bug 1308388, android(bug 1232305)
 [test_bug448534.html]
 [test_bug463162.xhtml]
 [test_bug465498.html]
 skip-if = toolkit == 'android' # android(bug 1232305)
 [test_bug495145.html]
 skip-if = toolkit == 'android' # android(bug 1232305)
new file mode 100644
--- /dev/null
+++ b/dom/media/test/test_autoplay_policy_web_audio_mediaElementAudioSourceNode.html
@@ -0,0 +1,104 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Autoplay policy test : use media element as source for web audio</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+  <script type="text/javascript" src="manifest.js"></script>
+</head>
+<body>
+<script>
+
+/**
+ * This test is used to ensure blocked AudioContext would be resumed when the
+ * source media element of MediaElementAudioSouceNode which has been created and
+ * connected to destinationnode starts.
+ */
+
+SimpleTest.waitForExplicitFinish();
+
+(async function testResumeAudioContextWhenMediaElementSourceStarted() {
+  await setupTestPreferences();
+
+  info(`- create audio context -`);
+  createAudioContext();
+
+  info(`- AudioContext is not allowed to start in beginning -`);
+  await audioContextShouldBeBlocked();
+
+  info(`- create a source for web audio and start the source -`);
+  await useMediaElementAsSourceAndPlayMediaElement();
+
+  info(`- AudioContext should be allowed to start after MediaElementAudioSourceNode started -`);
+  await audioContextShouldBeAllowedToStart();
+
+  endTest();
+})();
+
+/**
+ * Test utility functions
+ */
+
+function setupTestPreferences() {
+  return SpecialPowers.pushPrefEnv({"set": [
+    ["media.autoplay.default", SpecialPowers.Ci.nsIAutoplay.BLOCKED],
+    ["media.autoplay.enabled.user-gestures-needed", true],
+    ["media.autoplay.block-webaudio", true],
+    ["media.autoplay.block-event.enabled", true],
+  ]});
+}
+
+function createAudioContext() {
+  window.ac = new AudioContext();
+
+  ac.allowedToStart = new Promise(resolve => {
+    ac.addEventListener("statechange", function() {
+      if (ac.state === "running") {
+        resolve();
+      }
+    }, {once: true});
+  });
+
+  ac.notAllowedToStart = new Promise(resolve => {
+    ac.addEventListener("blocked", async function() {
+      resolve();
+    }, {once: true});
+  });
+}
+
+async function audioContextShouldBeBlocked() {
+  await ac.notAllowedToStart;
+  is(ac.state, "suspended", `AudioContext is blocked.`);
+}
+
+async function useMediaElementAsSourceAndPlayMediaElement() {
+  let video = document.createElement('video');
+  video.src = "gizmo.mp4";
+
+  let source = ac.createMediaElementSource(video);
+  source.connect(ac.destination);
+  // simulate user gesture in order to start video.
+  SpecialPowers.wrap(document).notifyUserGestureActivation();
+  await playVideo(video);
+}
+
+async function playVideo(video) {
+  video.play();
+  await once(video, "play");
+  ok(true, `video started.`);
+  removeNodeAndSource(video);
+}
+
+async function audioContextShouldBeAllowedToStart() {
+  await ac.allowedToStart;
+  is(ac.state, "running", `AudioContext is allowed to start.`);
+}
+
+function endTest() {
+  // reset the activation flag in order not to interfere following test in the
+  // verify mode which would run the test using same document couple times.
+  SpecialPowers.wrap(document).clearUserGestureActivation();
+  SimpleTest.finish();
+}
+
+</script>
--- a/dom/webidl/Document.webidl
+++ b/dom/webidl/Document.webidl
@@ -498,16 +498,19 @@ partial interface Document {
   [ChromeOnly] readonly attribute boolean userHasInteracted;
 };
 
 // Extension to give chrome JS the ability to simulate activate the docuement
 // by user gesture.
 partial interface Document {
   [ChromeOnly]
   void notifyUserGestureActivation();
+  // For testing only.
+  [ChromeOnly]
+  void clearUserGestureActivation();
 };
 
 // Extension to give chrome JS the ability to set an event handler which is
 // called with certain events that happened while events were suppressed in the
 // document or one of its subdocuments.
 partial interface Document {
   [ChromeOnly]
   void setSuppressedEventListener(EventListener? aListener);