tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

BrowserCaptureMediaStreamTrack-restrictTo.https.html (7947B)


      1 <!doctype html>
      2 <html>
      3 
      4 <head>
      5  <title>BrowserCaptureMediaStreamTrack restrictTo()</title>
      6  <link rel="help" href="https://screen-share.github.io/element-capture/">
      7 </head>
      8 
      9 <body>
     10  <p class="instructions">
     11    When prompted, accept to give permission to use your audio, video devices.
     12  </p>
     13  <h1 class="instructions">Description</h1>
     14  <p class="instructions">
     15    This test checks that restricting BrowserCaptureMediaStreamTrack works as
     16    expected.
     17  </p>
     18 
     19  <style>
     20    div {
     21      height: 100px;
     22    }
     23    .stacking {
     24      opacity: 0.9;
     25    }
     26    #container {
     27      columns:4;
     28      column-fill:auto;
     29    }
     30    .fragmentize {
     31      height: 50px;
     32    }
     33    #target {
     34      background: linear-gradient(red, blue);
     35    }
     36  </style>
     37 
     38 
     39  <div id='container'>
     40    <div id='target'></div>
     41  </div>
     42  <video id="video"
     43         style="border: 2px blue dotted; width: 250px; height: 250px;"
     44         autoplay playsinline muted></video>
     45 
     46  <script src=/resources/testharness.js></script>
     47  <script src=/resources/testharnessreport.js></script>
     48  <script src=/resources/testdriver.js></script>
     49  <script src=/resources/testdriver-vendor.js></script>
     50 
     51  <script>
     52    "use strict";
     53 
     54    // For more information, see:
     55    // https://screen-share.github.io/element-capture/#elements-eligible-for-restriction
     56    const EligibilityRequirement = {
     57      StackingContext: "StackingContext",
     58      OnlyOneBoxFragment: "OnlyOneBoxFragment",
     59      FlattenedIn3D: "FlattenedIn3D",
     60    };
     61 
     62    // The target div.
     63    const div = document.getElementById('target');
     64 
     65    // Returns a promise that, if successful, will resolve to a media stream.
     66    async function getDisplayMedia() {
     67      return test_driver.bless('getDisplayMedia', () =>
     68        navigator.mediaDevices.getDisplayMedia({
     69          video: { displaySurface: "browser" },
     70          selfBrowserSurface: "include",
     71        }));
     72    }
     73 
     74    // Returns a promise that will resolve successfully if at least one frame is
     75    // read before the timeout.
     76    function assertFrameRead(t, state, message) {
     77      const last_frame_count = state.frame_count;
     78      return t.step_wait(() => state.frame_count > last_frame_count,
     79        message, 5000, 10);
     80    }
     81 
     82    // Returns a promise that will resolve successfully if there are no frames
     83    // produced for an entire second after being called.
     84    function assertStopsProducingFrames(t, state, message) {
     85      let last_frame_count = state.frame_count;
     86 
     87      return t.step_timeout(() => {
     88        assert_equals(state.frame_count, last_frame_count);
     89      }, 1000);
     90    }
     91 
     92    function makeDivEligible() {
     93      // Must always have a stacking context to be eligible.
     94      div.classList.add("stacking");
     95      div.parentElement.classList.remove("fragmented");
     96      div.style.transform = "";
     97    }
     98 
     99    function makeDivIneligible(state) {
    100      switch(state.eligibilityParam) {
    101        case EligibilityRequirement.StackingContext:
    102          div.classList.remove("stacking");
    103          break;
    104 
    105        case EligibilityRequirement.OnlyOneBoxFragment:
    106          div.parentElement.classList.add("fragmented");
    107          break;
    108 
    109        case EligibilityRequirement.FlattenedIn3D:
    110          div.style.transform = "rotateY(90deg)";
    111          break;
    112      }
    113    }
    114 
    115    // Restore element state after each test.
    116    function cleanupDiv() {
    117      div.classList.remove("stacking");
    118      div.parentElement.classList.remove("fragmented");
    119      div.style.transform = "";
    120    }
    121 
    122    function startAnimation(t, state) {
    123      let count = 0;
    124      function animate() {
    125        if (!state.running) {
    126          return;
    127        }
    128        count += 1;
    129        div.innerText = count;
    130        window.requestAnimationFrame(animate);
    131      }
    132      window.requestAnimationFrame(animate);
    133 
    134      // Stop animation as part of cleanup.
    135      t.add_cleanup(() => { state.running = false; });
    136    }
    137 
    138    // Updates the state.frame_count value whenever a new frame is received on
    139    // the passed in media stream track.
    140    async function readFromTrack(state, track) {
    141      while (state.running) {
    142        const reader = new MediaStreamTrackProcessor(track).readable.getReader();
    143        while (true) {
    144          const frameOrDone = await reader.read();
    145          if (frameOrDone.done) {
    146            break;
    147          }
    148          frameOrDone.value.close();
    149          state.frame_count += 1;
    150        }
    151      }
    152    }
    153 
    154    // Parameterized test method. Note that this returns a Promise that will be
    155    // resolved to represent success of the entire promise test.
    156    async function runTest(t, eligibilityParam) {
    157      let state = {
    158        eligibilityParam: eligibilityParam,
    159        frame_count: 0,
    160        running: true,
    161        reading: false,
    162      };
    163      startAnimation(t, state);
    164 
    165      let videoTrack = undefined;
    166      return getDisplayMedia().then(stream => {
    167        t.add_cleanup(() => {
    168          stream.getTracks().forEach(track => track.stop());
    169        });
    170        assert_true(!!stream, "should have resolved to a stream.");
    171        assert_true(stream.active, "stream should be active.");
    172        assert_equals(stream.getVideoTracks().length, 1);
    173 
    174        [videoTrack] = stream.getVideoTracks();
    175        assert_true(videoTrack instanceof MediaStreamTrack,
    176          "track should be either MediaStreamTrack or a subclass thereof.");
    177        assert_equals(videoTrack.readyState, "live", "track should be live.");
    178 
    179        // Consume the stream in a video element.
    180        const video = document.querySelector('video');
    181        video.srcObject = stream;
    182 
    183        // Remove the video source, so that the stream object can be released.
    184        t.add_cleanup(() => {video.srcObject = null});
    185 
    186        // Keep track of the number of frames used.
    187        const readPromise = readFromTrack(state, videoTrack);
    188        t.add_cleanup(() => readPromise);
    189 
    190        return assertFrameRead(t, state, "Track should produce frames.");
    191      }).then(() => {
    192        assert_true(!!RestrictionTarget, "RestrictionTarget exposed.");
    193        assert_true(!!RestrictionTarget.fromElement,
    194          "RestrictionTarget.fromElement exposed.");
    195 
    196        return RestrictionTarget.fromElement(div);
    197      }).then(restrictionTarget => {
    198        assert_true(!!videoTrack.restrictTo, "restrictTo exposed.");
    199        assert_true(typeof videoTrack.restrictTo === 'function',
    200          "restrictTo is a function.");
    201 
    202        return videoTrack.restrictTo(restrictionTarget);
    203      }).then(() => {
    204        // By default, elements are not eligible for restriction due to not being
    205        // placed in their own stacking context.
    206        return assertStopsProducingFrames(t, state,
    207          "No new frames after restriction.");
    208      });
    209 
    210    // TODO(crbug.com/333770107): once the issue with the
    211    // --disable-threaded-compositing flag is resolved on Chrome's check in bots
    212    // the rest of this test should be enabled.
    213    //   ).then(() => {
    214    //     // Should be unpaused now that the element is eligible.
    215    //     makeDivEligible();
    216 
    217    //     // Make sure the element state is restored to default between tests.
    218    //     t.add_cleanup(() => { cleanupDiv(); });
    219 
    220    //     // Restart the reader now that the stream is producing frames again.
    221    //     return assertFrameRead(t, state,
    222    //       "Received at least one frame after becoming eligible.");
    223    //   }).then(() => {
    224 
    225    //     // Should pause if it becomes ineligible again.
    226    //     makeDivIneligible(state);
    227    //     return assertStopsProducingFrames(t, state,
    228    //       "No new frames after becoming ineligible again.");
    229    //   });
    230    }
    231 
    232    // Test parameterizations.
    233    [
    234      EligibilityRequirement.StackingContext,
    235      EligibilityRequirement.OnlyOneBoxFragment,
    236      EligibilityRequirement.FlattenedIn3D,
    237    ].forEach(param =>
    238      promise_test(t => runTest(t, param),
    239        `Tests that restricting MediaStreamTrack objects works as expected (${param}).`
    240    ));
    241 
    242  </script>
    243 </body>
    244 
    245 </html>