tor-browser

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

browser_devices_get_user_media_screen.js (30942B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 // The rejection "The fetching process for the media resource was aborted by the
      6 // user agent at the user's request." is left unhandled in some cases. This bug
      7 // should be fixed, but for the moment this file allows a class of rejections.
      8 //
      9 // NOTE: Allowing a whole class of rejections should be avoided. Normally you
     10 //       should use "expectUncaughtRejection" to flag individual failures.
     11 const { PromiseTestUtils } = ChromeUtils.importESModule(
     12  "resource://testing-common/PromiseTestUtils.sys.mjs"
     13 );
     14 PromiseTestUtils.allowMatchingRejectionsGlobally(/aborted by the user agent/);
     15 
     16 const permissionError =
     17  "error: NotAllowedError: The request is not allowed " +
     18  "by the user agent or the platform in the current context.";
     19 
     20 const notFoundError = "error: NotFoundError: The object can not be found here.";
     21 
     22 const isHeadless = Services.env.get("MOZ_HEADLESS");
     23 
     24 function verifyTabSharingPopup(expectedItems) {
     25  let event = new MouseEvent("popupshowing");
     26  let sharingMenu = document.getElementById("tabSharingMenuPopup");
     27  sharingMenu.dispatchEvent(event);
     28 
     29  is(
     30    sharingMenu.children.length,
     31    expectedItems.length,
     32    "correct number of items on tab sharing menu"
     33  );
     34  for (let i = 0; i < expectedItems.length; i++) {
     35    is(
     36      JSON.parse(sharingMenu.children[i].getAttribute("data-l10n-args"))
     37        .itemList,
     38      expectedItems[i],
     39      "label of item " + i + " + was correct"
     40    );
     41  }
     42 
     43  sharingMenu.dispatchEvent(new MouseEvent("popuphiding"));
     44 }
     45 
     46 var gTests = [
     47  {
     48    desc: "getUserMedia window/screen picking screen",
     49    run: async function checkWindowOrScreen() {
     50      let observerPromise = expectObserverCalled("getUserMedia:request");
     51      let promise = promisePopupNotificationShown("webRTC-shareDevices");
     52      await promiseRequestDevice(false, true, null, "screen");
     53      await promise;
     54      await observerPromise;
     55 
     56      is(
     57        PopupNotifications.getNotification("webRTC-shareDevices").anchorID,
     58        "webRTC-shareScreen-notification-icon",
     59        "anchored to device icon"
     60      );
     61      checkDeviceSelectors(["screen"]);
     62      let notification = PopupNotifications.panel.firstElementChild;
     63 
     64      let menulist = document.getElementById("webRTC-selectWindow-menulist");
     65      let count = menulist.itemCount;
     66      Assert.greaterOrEqual(
     67        count,
     68        4,
     69        "There should be the 'Select Window or Screen' item, a separator and at least one window and one screen"
     70      );
     71 
     72      let noWindowOrScreenItem = menulist.getItemAtIndex(0);
     73      ok(
     74        noWindowOrScreenItem.hasAttribute("selected"),
     75        "the 'Select Window or Screen' item is selected"
     76      );
     77      is(
     78        menulist.selectedItem,
     79        noWindowOrScreenItem,
     80        "'Select Window or Screen' is the selected item"
     81      );
     82      is(menulist.value, "-1", "no window or screen is selected by default");
     83      ok(
     84        noWindowOrScreenItem.disabled,
     85        "'Select Window or Screen' item is disabled"
     86      );
     87      ok(notification.button.disabled, "Allow button is disabled");
     88      ok(
     89        notification.hasAttribute("invalidselection"),
     90        "Notification is marked as invalid"
     91      );
     92 
     93      let separator = menulist.getItemAtIndex(1);
     94      is(
     95        separator.localName,
     96        "menuseparator",
     97        "the second item is a separator"
     98      );
     99 
    100      ok(
    101        document.getElementById("webRTC-all-windows-shared").hidden,
    102        "the 'all windows will be shared' warning should be hidden while there's no selection"
    103      );
    104      ok(
    105        document.getElementById("webRTC-preview-section").hidden,
    106        "the preview area is hidden"
    107      );
    108 
    109      let scaryScreenIndex;
    110      for (let i = 2; i < count; ++i) {
    111        let item = menulist.getItemAtIndex(i);
    112        is(
    113          parseInt(item.getAttribute("value")),
    114          i - 2,
    115          "the window/screen item has the correct index"
    116        );
    117        let type = item.getAttribute("devicetype");
    118        ok(
    119          ["window", "screen"].includes(type),
    120          "the devicetype attribute is set correctly"
    121        );
    122        if (type == "screen") {
    123          ok(item.scary, "the screen item is marked as scary");
    124          scaryScreenIndex = i;
    125        }
    126      }
    127      Assert.equal(
    128        typeof scaryScreenIndex,
    129        "number",
    130        "there's at least one scary screen, as as all screens are"
    131      );
    132 
    133      // Select a screen, a preview with a scary warning should appear.
    134      menulist.getItemAtIndex(scaryScreenIndex).doCommand();
    135      ok(
    136        !document.getElementById("webRTC-all-windows-shared").hidden,
    137        "the 'all windows will be shared' warning should now be visible"
    138      );
    139      await TestUtils.waitForCondition(
    140        () => !document.getElementById("webRTC-preview-section").hidden,
    141        "preview unhide",
    142        100,
    143        100
    144      );
    145      ok(
    146        !document.getElementById("webRTC-preview-section").hidden,
    147        "the preview area is visible"
    148      );
    149      ok(
    150        !document.getElementById("webRTC-previewWarningBox").hidden,
    151        "the scary warning is visible"
    152      );
    153      ok(!notification.button.disabled, "Allow button is enabled");
    154 
    155      // Select the 'Select Window or Screen' item again, the preview should be hidden.
    156      menulist.getItemAtIndex(0).doCommand();
    157      ok(
    158        document.getElementById("webRTC-all-windows-shared").hidden,
    159        "the 'all windows will be shared' warning should now be hidden"
    160      );
    161      ok(
    162        document.getElementById("webRTC-preview-section").hidden,
    163        "the preview area is hidden"
    164      );
    165 
    166      // Select the scary screen again so that we can have a stream.
    167      menulist.getItemAtIndex(scaryScreenIndex).doCommand();
    168 
    169      let indicator = promiseIndicatorWindow();
    170      let observerPromise1 = expectObserverCalled(
    171        "getUserMedia:response:allow"
    172      );
    173      let observerPromise2 = expectObserverCalled("recording-device-events");
    174      await promiseMessage("ok", () => {
    175        PopupNotifications.panel.firstElementChild.button.click();
    176      });
    177      await observerPromise1;
    178      await observerPromise2;
    179      Assert.deepEqual(
    180        await getMediaCaptureState(),
    181        { screen: "Screen" },
    182        "expected screen to be shared"
    183      );
    184 
    185      await indicator;
    186      await checkSharingUI({ screen: "Screen" });
    187      verifyTabSharingPopup(["screen"]);
    188 
    189      // we always show prompt for screen sharing.
    190      promise = promisePopupNotificationShown("webRTC-shareDevices");
    191      observerPromise = expectObserverCalled("getUserMedia:request");
    192      await promiseRequestDevice(false, true, null, "screen");
    193      await promise;
    194      await observerPromise;
    195 
    196      is(
    197        PopupNotifications.getNotification("webRTC-shareDevices").anchorID,
    198        "webRTC-shareScreen-notification-icon",
    199        "anchored to device icon"
    200      );
    201      checkDeviceSelectors(["screen"]);
    202 
    203      observerPromise = expectObserverCalled("getUserMedia:response:deny");
    204      await promiseMessage(permissionError, () => {
    205        activateSecondaryAction(kActionDeny);
    206      });
    207 
    208      await observerPromise;
    209      SitePermissions.removeFromPrincipal(
    210        null,
    211        "screen",
    212        gBrowser.selectedBrowser
    213      );
    214      await closeStream();
    215    },
    216  },
    217 
    218  {
    219    desc: "getUserMedia window/screen picking window",
    220    run: async function checkWindowOrScreen() {
    221      let observerPromise = expectObserverCalled("getUserMedia:request");
    222      let promise = promisePopupNotificationShown("webRTC-shareDevices");
    223      await promiseRequestDevice(false, true, null, "window");
    224      await promise;
    225      await observerPromise;
    226 
    227      is(
    228        PopupNotifications.getNotification("webRTC-shareDevices").anchorID,
    229        "webRTC-shareScreen-notification-icon",
    230        "anchored to device icon"
    231      );
    232      checkDeviceSelectors(["screen"]);
    233      let notification = PopupNotifications.panel.firstElementChild;
    234 
    235      let menulist = document.getElementById("webRTC-selectWindow-menulist");
    236      let count = menulist.itemCount;
    237      Assert.greaterOrEqual(
    238        count,
    239        4,
    240        "There should be the 'Select Window or Screen' item, a separator and at least one window and one screen"
    241      );
    242 
    243      let noWindowOrScreenItem = menulist.getItemAtIndex(0);
    244      ok(
    245        noWindowOrScreenItem.hasAttribute("selected"),
    246        "the 'Select Window or Screen' item is selected"
    247      );
    248      is(
    249        menulist.selectedItem,
    250        noWindowOrScreenItem,
    251        "'Select Window or Screen' is the selected item"
    252      );
    253      is(menulist.value, "-1", "no window or screen is selected by default");
    254      ok(
    255        noWindowOrScreenItem.disabled,
    256        "'Select Window or Screen' item is disabled"
    257      );
    258      ok(notification.button.disabled, "Allow button is disabled");
    259      ok(
    260        notification.hasAttribute("invalidselection"),
    261        "Notification is marked as invalid"
    262      );
    263 
    264      let separator = menulist.getItemAtIndex(1);
    265      is(
    266        separator.localName,
    267        "menuseparator",
    268        "the second item is a separator"
    269      );
    270 
    271      ok(
    272        document.getElementById("webRTC-all-windows-shared").hidden,
    273        "the 'all windows will be shared' warning should be hidden while there's no selection"
    274      );
    275      ok(
    276        document.getElementById("webRTC-preview-section").hidden,
    277        "the preview area is hidden"
    278      );
    279 
    280      let scaryWindowIndexes = [],
    281        nonScaryWindowIndex,
    282        scaryScreenIndex;
    283      for (let i = 2; i < count; ++i) {
    284        let item = menulist.getItemAtIndex(i);
    285        is(
    286          parseInt(item.getAttribute("value")),
    287          i - 2,
    288          "the window/screen item has the correct index"
    289        );
    290        let type = item.getAttribute("devicetype");
    291        ok(
    292          ["window", "screen"].includes(type),
    293          "the devicetype attribute is set correctly"
    294        );
    295        if (type == "screen") {
    296          ok(item.scary, "the screen item is marked as scary");
    297          scaryScreenIndex = i;
    298        } else if (item.scary) {
    299          scaryWindowIndexes.push(i);
    300        } else {
    301          nonScaryWindowIndex = i;
    302        }
    303      }
    304      if (isHeadless) {
    305        is(
    306          scaryWindowIndexes.length,
    307          0,
    308          "there are no scary Firefox windows in headless mode"
    309        );
    310      } else {
    311        ok(
    312          scaryWindowIndexes.length,
    313          "there's at least one scary window, as Firefox is running"
    314        );
    315      }
    316      Assert.equal(
    317        typeof scaryScreenIndex,
    318        "number",
    319        "there's at least one scary screen, as all screens are"
    320      );
    321 
    322      if (!isHeadless) {
    323        // Select one scary window, a preview with a scary warning should appear.
    324        let scaryWindowIndex;
    325        for (scaryWindowIndex of scaryWindowIndexes) {
    326          menulist.getItemAtIndex(scaryWindowIndex).doCommand();
    327          ok(
    328            document.getElementById("webRTC-all-windows-shared").hidden,
    329            "the 'all windows will be shared' warning should still be hidden"
    330          );
    331          try {
    332            await TestUtils.waitForCondition(
    333              () => !document.getElementById("webRTC-preview-section").hidden,
    334              "",
    335              100,
    336              100
    337            );
    338            break;
    339          } catch (e) {
    340            // A "scary window" is Firefox. Multiple Firefox windows have been
    341            // observed to come and go during try runs, so we won't know which one
    342            // is ours. To avoid intermittents, we ignore preview failing due to
    343            // these going away on us, provided it succeeds on one of them.
    344          }
    345        }
    346        ok(
    347          !document.getElementById("webRTC-preview-section").hidden,
    348          "the preview area is visible"
    349        );
    350        ok(
    351          !document.getElementById("webRTC-previewWarningBox").hidden,
    352          "the scary warning is visible"
    353        );
    354        // Select the 'Select Window' item again, the preview should be hidden.
    355        menulist.getItemAtIndex(0).doCommand();
    356        ok(
    357          document.getElementById("webRTC-preview-section").hidden,
    358          "the preview area is hidden"
    359        );
    360 
    361        // Select the first window again so that we can have a stream.
    362        menulist.getItemAtIndex(scaryWindowIndex).doCommand();
    363      }
    364 
    365      let sharingNonScaryWindow = typeof nonScaryWindowIndex == "number";
    366 
    367      // If we have a non-scary window, select it and verify the warning isn't displayed.
    368      // A non-scary window may not always exist on test machines.
    369      if (sharingNonScaryWindow) {
    370        menulist.getItemAtIndex(nonScaryWindowIndex).doCommand();
    371        ok(
    372          document.getElementById("webRTC-all-windows-shared").hidden,
    373          "the 'all windows will be shared' warning should still be hidden"
    374        );
    375        await TestUtils.waitForCondition(
    376          () => !document.getElementById("webRTC-preview-section").hidden,
    377          "preview unhide",
    378          100,
    379          100
    380        );
    381        ok(
    382          !document.getElementById("webRTC-preview-section").hidden,
    383          "the preview area is visible"
    384        );
    385        ok(
    386          document.getElementById("webRTC-previewWarningBox").hidden,
    387          "the scary warning is hidden"
    388        );
    389      } else {
    390        info("no non-scary window available on this test machine");
    391      }
    392 
    393      let indicator = promiseIndicatorWindow();
    394      let observerPromise1 = expectObserverCalled(
    395        "getUserMedia:response:allow"
    396      );
    397      let observerPromise2 = expectObserverCalled("recording-device-events");
    398      await promiseMessage("ok", () => {
    399        PopupNotifications.panel.firstElementChild.button.click();
    400      });
    401      await observerPromise1;
    402      await observerPromise2;
    403      Assert.deepEqual(
    404        await getMediaCaptureState(),
    405        { window: true },
    406        "expected screen to be shared"
    407      );
    408 
    409      await indicator;
    410      if (sharingNonScaryWindow) {
    411        await checkSharingUI({ screen: "Window" });
    412      } else {
    413        await checkSharingUI({ screen: "Window", browserwindow: true });
    414      }
    415 
    416      verifyTabSharingPopup(["window"]);
    417 
    418      await closeStream();
    419    },
    420  },
    421 
    422  {
    423    desc: "getUserMedia audio + window/screen",
    424    run: async function checkAudioVideo() {
    425      if (AppConstants.platform == "macosx") {
    426        todo(
    427          false,
    428          "Bug 1323481 - On Mac on treeherder, but not locally, requesting microphone + screen never makes the permission prompt appear, and so causes the test to timeout"
    429        );
    430        return;
    431      }
    432 
    433      let observerPromise = expectObserverCalled("getUserMedia:request");
    434      let promise = promisePopupNotificationShown("webRTC-shareDevices");
    435      await promiseRequestDevice(true, true, null, "window");
    436      await promise;
    437      await observerPromise;
    438 
    439      is(
    440        PopupNotifications.getNotification("webRTC-shareDevices").anchorID,
    441        "webRTC-shareScreen-notification-icon",
    442        "anchored to device icon"
    443      );
    444      checkDeviceSelectors(["microphone", "screen"]);
    445 
    446      let menulist = document.getElementById("webRTC-selectWindow-menulist");
    447      let count = menulist.itemCount;
    448      Assert.greaterOrEqual(
    449        count,
    450        4,
    451        "There should be the 'Select Window or Screen' item, a separator and at least one window and one screen"
    452      );
    453 
    454      // Select a screen, a preview with a scary warning should appear.
    455      menulist.getItemAtIndex(count - 1).doCommand();
    456      ok(
    457        !document.getElementById("webRTC-all-windows-shared").hidden,
    458        "the 'all windows will be shared' warning should now be visible"
    459      );
    460      await TestUtils.waitForCondition(
    461        () => !document.getElementById("webRTC-preview-section").hidden,
    462        "preview unhide",
    463        100,
    464        100
    465      );
    466      ok(
    467        !document.getElementById("webRTC-preview-section").hidden,
    468        "the preview area is visible"
    469      );
    470      ok(
    471        !document.getElementById("webRTC-previewWarningBox").hidden,
    472        "the scary warning is visible"
    473      );
    474 
    475      let indicator = promiseIndicatorWindow();
    476      let observerPromise1 = expectObserverCalled(
    477        "getUserMedia:response:allow"
    478      );
    479      let observerPromise2 = expectObserverCalled("recording-device-events");
    480      await promiseMessage("ok", () => {
    481        PopupNotifications.panel.firstElementChild.button.click();
    482      });
    483      await observerPromise1;
    484      await observerPromise2;
    485      Assert.deepEqual(
    486        await getMediaCaptureState(),
    487        { audio: true, screen: "Screen" },
    488        "expected screen and microphone to be shared"
    489      );
    490 
    491      await indicator;
    492      await checkSharingUI({ audio: true, screen: "Screen" });
    493 
    494      verifyTabSharingPopup(["microphone and screen"]);
    495 
    496      await closeStream();
    497    },
    498  },
    499 
    500  {
    501    desc: 'getUserMedia screen, user clicks "Don\'t Allow"',
    502    run: async function checkDontShare() {
    503      let observerPromise = expectObserverCalled("getUserMedia:request");
    504      let promise = promisePopupNotificationShown("webRTC-shareDevices");
    505      await promiseRequestDevice(false, true, null, "screen");
    506      await promise;
    507      await observerPromise;
    508      checkDeviceSelectors(["screen"]);
    509 
    510      let observerPromise1 = expectObserverCalled("getUserMedia:response:deny");
    511      let observerPromise2 = expectObserverCalled("recording-window-ended");
    512      await promiseMessage(permissionError, () => {
    513        activateSecondaryAction(kActionDeny);
    514      });
    515 
    516      await observerPromise1;
    517      await observerPromise2;
    518      await checkNotSharing();
    519      SitePermissions.removeFromPrincipal(
    520        null,
    521        "screen",
    522        gBrowser.selectedBrowser
    523      );
    524      SitePermissions.removeFromPrincipal(
    525        null,
    526        "camera",
    527        gBrowser.selectedBrowser
    528      );
    529    },
    530  },
    531 
    532  {
    533    desc: "getUserMedia audio + window/screen: stop sharing",
    534    run: async function checkStopSharing() {
    535      if (AppConstants.platform == "macosx") {
    536        todo(
    537          false,
    538          "Bug 1323481 - On Mac on treeherder, but not locally, requesting microphone + screen never makes the permission prompt appear, and so causes the test to timeout"
    539        );
    540        return;
    541      }
    542 
    543      async function share(deviceTypes) {
    544        let promise = promisePopupNotificationShown("webRTC-shareDevices");
    545        let observerPromise = expectObserverCalled("getUserMedia:request");
    546        await promiseRequestDevice(
    547          /* audio */ deviceTypes.includes("microphone"),
    548          /* video */ deviceTypes.some(t => t == "screen" || t == "camera"),
    549          null,
    550          deviceTypes.includes("screen") && "window"
    551        );
    552        await promise;
    553        await observerPromise;
    554        checkDeviceSelectors(deviceTypes);
    555        if (screen) {
    556          let menulist = document.getElementById(
    557            "webRTC-selectWindow-menulist"
    558          );
    559          menulist.getItemAtIndex(menulist.itemCount - 1).doCommand();
    560        }
    561        let observerPromise1 = expectObserverCalled(
    562          "getUserMedia:response:allow"
    563        );
    564        let observerPromise2 = expectObserverCalled("recording-device-events");
    565        await promiseMessage("ok", () => {
    566          PopupNotifications.panel.firstElementChild.button.click();
    567        });
    568        await observerPromise1;
    569        await observerPromise2;
    570      }
    571 
    572      async function check(expected = {}, expectedSharingLabel) {
    573        let shared = Object.keys(expected).join(" and ");
    574        if (shared) {
    575          Assert.deepEqual(
    576            await getMediaCaptureState(),
    577            expected,
    578            "expected " + shared + " to be shared"
    579          );
    580          await checkSharingUI(expected);
    581          verifyTabSharingPopup([expectedSharingLabel]);
    582        } else {
    583          await checkNotSharing();
    584          verifyTabSharingPopup([""]);
    585        }
    586      }
    587 
    588      info("Share screen and microphone");
    589      let indicator = promiseIndicatorWindow();
    590      await share(["microphone", "screen"]);
    591      await indicator;
    592      await check({ audio: true, screen: "Screen" }, "microphone and screen");
    593 
    594      info("Share camera");
    595      await share(["camera"]);
    596      await check(
    597        { video: true, audio: true, screen: "Screen" },
    598        "microphone, screen, and camera"
    599      );
    600 
    601      info("Stop the screen share, mic+cam should continue");
    602      await stopSharing("screen", true);
    603      await check({ video: true, audio: true }, "microphone and camera");
    604 
    605      info("Stop the camera, everything should stop.");
    606      await stopSharing("camera");
    607 
    608      info("Now, share only the screen...");
    609      indicator = promiseIndicatorWindow();
    610      await share(["screen"]);
    611      await indicator;
    612      await check({ screen: "Screen" }, "screen");
    613 
    614      info("... and add camera and microphone in a second request.");
    615      await share(["microphone", "camera"]);
    616      await check(
    617        { video: true, audio: true, screen: "Screen" },
    618        "screen, microphone, and camera"
    619      );
    620 
    621      info("Stop the camera, this should stop everything.");
    622      await stopSharing("camera");
    623    },
    624  },
    625 
    626  {
    627    desc: "getUserMedia window/screen: reloading the page removes all gUM UI",
    628    run: async function checkReloading() {
    629      let observerPromise = expectObserverCalled("getUserMedia:request");
    630      let promise = promisePopupNotificationShown("webRTC-shareDevices");
    631      await promiseRequestDevice(false, true, null, "screen");
    632      await promise;
    633      await observerPromise;
    634      checkDeviceSelectors(["screen"]);
    635      let menulist = document.getElementById("webRTC-selectWindow-menulist");
    636      menulist.getItemAtIndex(menulist.itemCount - 1).doCommand();
    637 
    638      let indicator = promiseIndicatorWindow();
    639      let observerPromise1 = expectObserverCalled(
    640        "getUserMedia:response:allow"
    641      );
    642      let observerPromise2 = expectObserverCalled("recording-device-events");
    643      await promiseMessage("ok", () => {
    644        PopupNotifications.panel.firstElementChild.button.click();
    645      });
    646      await observerPromise1;
    647      await observerPromise2;
    648      Assert.deepEqual(
    649        await getMediaCaptureState(),
    650        { screen: "Screen" },
    651        "expected screen to be shared"
    652      );
    653 
    654      await indicator;
    655      await checkSharingUI({ screen: "Screen" });
    656      verifyTabSharingPopup(["screen"]);
    657 
    658      await reloadAndAssertClosedStreams();
    659    },
    660  },
    661 
    662  {
    663    desc: "Only persistent block is possible for screen sharing",
    664    run: async function checkPersistentPermissions() {
    665      // This test doesn't apply when the notification silencing
    666      // feature is enabled, since the "Remember this decision"
    667      // checkbox doesn't exist.
    668      if (ALLOW_SILENCING_NOTIFICATIONS) {
    669        return;
    670      }
    671 
    672      let browser = gBrowser.selectedBrowser;
    673      let devicePerms = SitePermissions.getForPrincipal(
    674        browser.contentPrincipal,
    675        "screen",
    676        browser
    677      );
    678      is(
    679        devicePerms.state,
    680        SitePermissions.UNKNOWN,
    681        "starting without screen persistent permissions"
    682      );
    683 
    684      let observerPromise = expectObserverCalled("getUserMedia:request");
    685      let promise = promisePopupNotificationShown("webRTC-shareDevices");
    686      await promiseRequestDevice(false, true, null, "screen");
    687      await promise;
    688      await observerPromise;
    689      checkDeviceSelectors(["screen"]);
    690      document
    691        .getElementById("webRTC-selectWindow-menulist")
    692        .getItemAtIndex(2)
    693        .doCommand();
    694 
    695      // Ensure that checking the 'Remember this decision' checkbox disables
    696      // 'Allow'.
    697      let notification = PopupNotifications.panel.firstElementChild;
    698      ok(
    699        notification.hasAttribute("warninghidden"),
    700        "warning message is hidden"
    701      );
    702      let checkbox = notification.checkbox;
    703      ok(!!checkbox, "checkbox is present");
    704      ok(!checkbox.checked, "checkbox is not checked");
    705      checkbox.click();
    706      ok(checkbox.checked, "checkbox now checked");
    707      ok(notification.button.disabled, "Allow button is disabled");
    708      ok(
    709        !notification.hasAttribute("warninghidden"),
    710        "warning message is shown"
    711      );
    712 
    713      // Click "Don't Allow" to save a persistent block permission.
    714      let observerPromise1 = expectObserverCalled("getUserMedia:response:deny");
    715      let observerPromise2 = expectObserverCalled("recording-window-ended");
    716      await promiseMessage(permissionError, () => {
    717        activateSecondaryAction(kActionDeny);
    718      });
    719      await observerPromise1;
    720      await observerPromise2;
    721      await checkNotSharing();
    722 
    723      let permission = SitePermissions.getForPrincipal(
    724        browser.contentPrincipal,
    725        "screen",
    726        browser
    727      );
    728      is(permission.state, SitePermissions.BLOCK, "screen sharing is blocked");
    729      is(
    730        permission.scope,
    731        SitePermissions.SCOPE_PERSISTENT,
    732        "screen sharing is persistently blocked"
    733      );
    734 
    735      // Request screensharing again, expect an immediate failure.
    736      await Promise.all([
    737        expectObserverCalled("getUserMedia:request"),
    738        expectObserverCalled("getUserMedia:response:deny"),
    739        expectObserverCalled("recording-window-ended"),
    740        promiseMessage(permissionError),
    741        promiseRequestDevice(false, true, null, "screen"),
    742      ]);
    743 
    744      // Now set the permission to allow and expect a prompt.
    745      SitePermissions.setForPrincipal(
    746        browser.contentPrincipal,
    747        "screen",
    748        SitePermissions.ALLOW
    749      );
    750 
    751      // Request devices and expect a prompt despite the saved 'Allow' permission.
    752      observerPromise = expectObserverCalled("getUserMedia:request");
    753      promise = promisePopupNotificationShown("webRTC-shareDevices");
    754      await promiseRequestDevice(false, true, null, "screen");
    755      await promise;
    756      await observerPromise;
    757 
    758      // The 'remember' checkbox shouldn't be checked anymore.
    759      notification = PopupNotifications.panel.firstElementChild;
    760      ok(
    761        notification.hasAttribute("warninghidden"),
    762        "warning message is hidden"
    763      );
    764      checkbox = notification.checkbox;
    765      ok(!!checkbox, "checkbox is present");
    766      ok(!checkbox.checked, "checkbox is not checked");
    767 
    768      // Deny the request to cleanup...
    769      observerPromise1 = expectObserverCalled("getUserMedia:response:deny");
    770      observerPromise2 = expectObserverCalled("recording-window-ended");
    771      await promiseMessage(permissionError, () => {
    772        activateSecondaryAction(kActionDeny);
    773      });
    774      await observerPromise1;
    775      await observerPromise2;
    776      SitePermissions.removeFromPrincipal(
    777        browser.contentPrincipal,
    778        "screen",
    779        browser
    780      );
    781    },
    782  },
    783 
    784  {
    785    desc: "Switching between menu options maintains correct main action state while window sharing",
    786    skipObserverVerification: true,
    787    run: async function checkDoorhangerState() {
    788      await enableObserverVerification();
    789 
    790      let win = await BrowserTestUtils.openNewBrowserWindow();
    791      await BrowserTestUtils.openNewForegroundTab(win.gBrowser, "about:newtab");
    792      BrowserWindowTracker.orderedWindows[1].focus();
    793 
    794      let observerPromise = expectObserverCalled("getUserMedia:request");
    795      let promise = promisePopupNotificationShown("webRTC-shareDevices");
    796      await promiseRequestDevice(false, true, null, "window");
    797      await promise;
    798      await observerPromise;
    799 
    800      let menulist = document.getElementById("webRTC-selectWindow-menulist");
    801      let notification = PopupNotifications.panel.firstElementChild;
    802      let checkbox = notification.checkbox;
    803 
    804      menulist.getItemAtIndex(2).doCommand();
    805      checkbox.click();
    806      ok(checkbox.checked, "checkbox now checked");
    807 
    808      if (ALLOW_SILENCING_NOTIFICATIONS) {
    809        // When the notification silencing feature is enabled, the checkbox
    810        // controls that feature, and its state should not disable the
    811        // "Allow" button.
    812        ok(!notification.button.disabled, "Allow button is not disabled");
    813      } else {
    814        ok(notification.button.disabled, "Allow button is disabled");
    815        ok(
    816          !notification.hasAttribute("warninghidden"),
    817          "warning message is shown"
    818        );
    819      }
    820 
    821      menulist.getItemAtIndex(3).doCommand();
    822      ok(checkbox.checked, "checkbox still checked");
    823      if (ALLOW_SILENCING_NOTIFICATIONS) {
    824        // When the notification silencing feature is enabled, the checkbox
    825        // controls that feature, and its state should not disable the
    826        // "Allow" button.
    827        ok(!notification.button.disabled, "Allow button remains not disabled");
    828      } else {
    829        ok(notification.button.disabled, "Allow button remains disabled");
    830        ok(
    831          !notification.hasAttribute("warninghidden"),
    832          "warning message is still shown"
    833        );
    834      }
    835 
    836      await disableObserverVerification();
    837 
    838      observerPromise = expectObserverCalled("recording-window-ended");
    839 
    840      gBrowser.removeCurrentTab();
    841      win.close();
    842 
    843      await observerPromise;
    844 
    845      await openNewTestTab();
    846    },
    847  },
    848  {
    849    desc: "Switching between tabs does not bleed state into other prompts",
    850    skipObserverVerification: true,
    851    run: async function checkSwitchingTabs() {
    852      // Open a new window in the background to have a choice in the menulist.
    853      let win = await BrowserTestUtils.openNewBrowserWindow();
    854      await BrowserTestUtils.openNewForegroundTab(win.gBrowser, "about:newtab");
    855      await enableObserverVerification();
    856      BrowserWindowTracker.orderedWindows[1].focus();
    857 
    858      let observerPromise = expectObserverCalled("getUserMedia:request");
    859      let promise = promisePopupNotificationShown("webRTC-shareDevices");
    860      await promiseRequestDevice(false, true, null, "window");
    861      await promise;
    862      await observerPromise;
    863 
    864      let notification = PopupNotifications.panel.firstElementChild;
    865      ok(notification.button.disabled, "Allow button is disabled");
    866      await disableObserverVerification();
    867 
    868      await openNewTestTab("get_user_media_in_xorigin_frame.html");
    869      await enableObserverVerification();
    870 
    871      observerPromise = expectObserverCalled("getUserMedia:request");
    872      promise = promisePopupNotificationShown("webRTC-shareDevices");
    873      await promiseRequestDevice(true, true, "frame1");
    874      await promise;
    875      await observerPromise;
    876 
    877      notification = PopupNotifications.panel.firstElementChild;
    878      ok(!notification.button.disabled, "Allow button is not disabled");
    879 
    880      await disableObserverVerification();
    881 
    882      gBrowser.removeCurrentTab();
    883      gBrowser.removeCurrentTab();
    884      win.close();
    885 
    886      await openNewTestTab();
    887    },
    888  },
    889 ];
    890 
    891 add_task(async function test() {
    892  await runTests(gTests);
    893 });