summaryrefslogtreecommitdiffstats
path: root/browser/components/downloads/test/browser/browser_downloads_panel_opens.js
blob: 499b5320daf7e23381248fa0efc3a8ddf4a2c066 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
/* Any copyright is dedicated to the Public Domain.
 * http://creativecommons.org/publicdomain/zero/1.0/ */

let { MockFilePicker } = SpecialPowers;
MockFilePicker.init(window);
registerCleanupFunction(() => MockFilePicker.cleanup());

/**
 * Check that the downloads panel opens when a download is spoofed.
 */
async function checkPanelOpens() {
  info("Waiting for panel to open.");
  let promise = promisePanelOpened();
  DownloadsCommon.getData(window)._notifyDownloadEvent("start");
  is(
    DownloadsPanel.isPanelShowing,
    true,
    "Panel state should indicate a preparation to be opened."
  );
  await promise;

  is(DownloadsPanel.panel.state, "open", "Panel should be opened.");

  DownloadsPanel.hidePanel();
}

/**
 * Start a download and check that the downloads panel opens correctly according
 * to the download parameter, openDownloadsListOnStart
 * @param {boolean} [openDownloadsListOnStart]
 *        true (default) - open downloads panel when download starts
 *        false - no downloads panel; update indicator attention state
 */
async function downloadAndCheckPanel({ openDownloadsListOnStart = true } = {}) {
  info("creating a download and setting it to in progress");
  await task_addDownloads([
    {
      state: DownloadsCommon.DOWNLOAD_DOWNLOADING,
      openDownloadsListOnStart,
    },
  ]);
  let publicList = await Downloads.getList(Downloads.PUBLIC);
  let downloads = await publicList.getAll();
  downloads[0].stopped = false;

  // Make sure we remove that download at the end of the test.
  let oldShowEventNotification = DownloadsIndicatorView.showEventNotification;
  registerCleanupFunction(async () => {
    for (let download of downloads) {
      await publicList.remove(download);
    }
    DownloadsIndicatorView.showEventNotification = oldShowEventNotification;
  });

  // Instead of the panel opening, the download notification should be shown.
  let promiseDownloadStartedNotification = new Promise(resolve => {
    DownloadsIndicatorView.showEventNotification = aType => {
      if (aType == "start") {
        resolve();
      }
    };
  });

  DownloadsCommon.getData(window)._notifyDownloadEvent("start", {
    openDownloadsListOnStart,
  });
  is(
    DownloadsPanel.isPanelShowing,
    false,
    "Panel state should indicate it is not preparing to be opened"
  );

  info("waiting for download to start");
  await promiseDownloadStartedNotification;

  DownloadsIndicatorView.showEventNotification = oldShowEventNotification;
  is(DownloadsPanel.panel.state, "closed", "Panel should be closed");
}

function clickCheckbox(checkbox) {
  // Clicking a checkbox toggles its checkedness first.
  if (checkbox.getAttribute("checked") == "true") {
    checkbox.removeAttribute("checked");
  } else {
    checkbox.setAttribute("checked", "true");
  }
  // Then it runs the command and closes the popup.
  checkbox.doCommand();
  checkbox.parentElement.hidePopup();
}

/**
 * Test that the downloads panel correctly opens or doesn't open based on
 * whether the download triggered a dialog already. If askWhereToSave is true,
 * we should get a file picker dialog. If preferredAction is alwaysAsk, we
 * should get an unknown content type dialog. If neither of those is true, we
 * should get no dialog at all, and expect the downloads panel to open.
 * @param {boolean} [expectPanelToOpen] true - fail if panel doesn't open
 *                                      false (default) - fail if it opens
 * @param {number}  [preferredAction]   Default download action:
 *                                      0 (default) - save download to disk
 *                                      1 - open UCT dialog first
 * @param {boolean} [askWhereToSave]    true - open file picker dialog
 *                                      false (default) - use download dir
 */
async function testDownloadsPanelAfterDialog({
  expectPanelToOpen = false,
  preferredAction,
  askWhereToSave = false,
} = {}) {
  const { saveToDisk, alwaysAsk } = Ci.nsIHandlerInfo;
  if (![saveToDisk, alwaysAsk].includes(preferredAction)) {
    preferredAction = saveToDisk;
  }
  const openUCT = preferredAction === alwaysAsk;
  const TEST_PATH = getRootDirectory(gTestPath).replace(
    "chrome://mochitests/content",
    "https://example.com"
  );
  const MimeSvc = Cc["@mozilla.org/mime;1"].getService(Ci.nsIMIMEService);
  const HandlerSvc = Cc["@mozilla.org/uriloader/handler-service;1"].getService(
    Ci.nsIHandlerService
  );
  let publicList = await Downloads.getList(Downloads.PUBLIC);

  for (let download of await publicList.getAll()) {
    await publicList.remove(download);
  }

  // We need to test the changes from bug 1739348, where the helper app service
  // sets a flag based on whether a file picker dialog was opened, and this flag
  // determines whether the downloads panel will be opened as the download
  // starts. We need to actually hit "Save" for the download to start, but we
  // can't interact with the real file picker dialog. So this temporarily
  // replaces it with a barebones component that plugs into the helper app
  // service and tells it to start saving the file to the default path.
  if (askWhereToSave) {
    MockFilePicker.returnValue = MockFilePicker.returnOK;
    MockFilePicker.showCallback = function (fp) {
      // Get the default location from the helper app service.
      let testFile = MockFilePicker.displayDirectory.clone();
      testFile.append(fp.defaultString);
      info("File picker download path: " + testFile.path);
      MockFilePicker.setFiles([testFile]);
      MockFilePicker.filterIndex = 0; // kSaveAsType_Complete
      MockFilePicker.showCallback = null;
      // Confirm that saving should proceed. The helper app service uses this
      // value to determine whether to invoke launcher.saveDestinationAvailable
      return MockFilePicker.returnOK;
    };
  }

  await SpecialPowers.pushPrefEnv({
    set: [
      ["browser.download.useDownloadDir", !askWhereToSave],
      ["browser.download.always_ask_before_handling_new_types", openUCT],
      ["security.dialog_enable_delay", 0],
    ],
  });

  // Configure the handler for the file according to parameters.
  let mimeInfo = MimeSvc.getFromTypeAndExtension("text/plain", "txt");
  let existed = HandlerSvc.exists(mimeInfo);
  mimeInfo.alwaysAskBeforeHandling = openUCT;
  mimeInfo.preferredAction = preferredAction;
  HandlerSvc.store(mimeInfo);
  registerCleanupFunction(async () => {
    // Reset the handler to its original state.
    if (existed) {
      HandlerSvc.store(mimeInfo);
    } else {
      HandlerSvc.remove(mimeInfo);
    }
    await publicList.removeFinished();
    BrowserTestUtils.removeTab(loadingTab);
  });

  let dialogWindowPromise = BrowserTestUtils.domWindowOpenedAndLoaded();
  let downloadFinishedPromise = new Promise(resolve => {
    publicList.addView({
      onDownloadChanged(download) {
        info("Download changed!");
        if (download.succeeded || download.error) {
          info("Download succeeded or failed.");
          publicList.removeView(this);
          resolve(download);
        }
      },
    });
  });
  let panelOpenedPromise = expectPanelToOpen ? promisePanelOpened() : null;

  // Open the tab that will trigger the download.
  let loadingTab = await BrowserTestUtils.openNewForegroundTab({
    gBrowser,
    opening: TEST_PATH + "foo.txt",
    waitForLoad: false,
    waitForStateStop: true,
  });

  // Wait for a UCT dialog if the handler was set up to open one.
  if (openUCT) {
    let dialogWindow = await dialogWindowPromise;
    is(
      dialogWindow.location.href,
      "chrome://mozapps/content/downloads/unknownContentType.xhtml",
      "Should have seen the unknown content dialogWindow."
    );
    let doc = dialogWindow.document;
    let dialog = doc.getElementById("unknownContentType");
    let radio = doc.getElementById("save");
    let button = dialog.getButton("accept");

    await TestUtils.waitForCondition(
      () => !button.disabled,
      "Waiting for the UCT dialog's Accept button to be enabled."
    );
    ok(!radio.hidden, "The Save option should be visible");
    // Make sure we aren't opening the file.
    radio.click();
    ok(radio.selected, "The Save option should be selected");
    button.disabled = false;
    dialog.acceptDialog();
  }

  info("Waiting for download to finish.");
  let download = await downloadFinishedPromise;
  ok(!download.error, "There should be no error.");
  is(
    DownloadsPanel.isPanelShowing,
    expectPanelToOpen,
    `Panel should${expectPanelToOpen ? " " : " not "}be showing.`
  );
  if (DownloadsPanel.isPanelShowing) {
    await panelOpenedPromise;
    let hiddenPromise = BrowserTestUtils.waitForPopupEvent(
      DownloadsPanel.panel,
      "hidden"
    );
    DownloadsPanel.hidePanel();
    await hiddenPromise;
  }
  if (download?.target.exists) {
    try {
      info("Removing test file: " + download.target.path);
      if (Services.appinfo.OS === "WINNT") {
        await IOUtils.setPermissions(download.target.path, 0o600);
      }
      await IOUtils.remove(download.target.path);
    } catch (ex) {
      /* ignore */
    }
  }
  for (let dl of await publicList.getAll()) {
    await publicList.remove(dl);
  }
  BrowserTestUtils.removeTab(loadingTab);
}

/**
 * Make sure the downloads panel opens automatically with a new download.
 */
add_task(async function test_downloads_panel_opens() {
  await SpecialPowers.pushPrefEnv({
    set: [
      ["browser.download.always_ask_before_handling_new_types", false],
      ["browser.download.alwaysOpenPanel", true],
    ],
  });
  await checkPanelOpens();
});

add_task(async function test_customizemode_doesnt_wreck_things() {
  await SpecialPowers.pushPrefEnv({
    set: [
      ["browser.download.always_ask_before_handling_new_types", false],
      ["browser.download.alwaysOpenPanel", true],
    ],
  });

  // Enter customize mode:
  let customizationReadyPromise = BrowserTestUtils.waitForEvent(
    gNavToolbox,
    "customizationready"
  );
  gCustomizeMode.enter();
  await customizationReadyPromise;

  info("Try to open the panel (will not work, in customize mode)");
  let promise = promisePanelOpened();
  DownloadsCommon.getData(window)._notifyDownloadEvent("start");
  await TestUtils.waitForCondition(
    () => DownloadsPanel._state == DownloadsPanel.kStateHidden,
    "Should try to show but stop short and hide the panel"
  );
  is(
    DownloadsPanel._state,
    DownloadsPanel.kStateHidden,
    "Should not start opening the panel."
  );

  let afterCustomizationPromise = BrowserTestUtils.waitForEvent(
    gNavToolbox,
    "aftercustomization"
  );
  gCustomizeMode.exit();
  await afterCustomizationPromise;

  // Avoid a failure on Linux where the window isn't active for some reason,
  // which prevents the window's downloads panel from opening.
  if (Services.focus.activeWindow != window) {
    info("Main window is not active, trying to focus.");
    await SimpleTest.promiseFocus(window);
    is(Services.focus.activeWindow, window, "Main window should be active.");
  }
  DownloadsCommon.getData(window)._notifyDownloadEvent("start");
  await TestUtils.waitForCondition(
    () => DownloadsPanel.isPanelShowing,
    "Panel state should indicate a preparation to be opened"
  );
  await promise;

  is(DownloadsPanel.panel.state, "open", "Panel should be opened");

  DownloadsPanel.hidePanel();
});

/**
 * Make sure the downloads panel _does not_ open automatically if we set the
 * pref telling it not to do that.
 */
add_task(async function test_downloads_panel_opening_pref() {
  await SpecialPowers.pushPrefEnv({
    set: [
      ["browser.download.always_ask_before_handling_new_types", false],
      ["browser.download.alwaysOpenPanel", false],
    ],
  });
  registerCleanupFunction(async () => {
    await SpecialPowers.popPrefEnv();
  });
  await downloadAndCheckPanel();
  await SpecialPowers.popPrefEnv();
});

/**
 * Make sure the downloads panel _does not_ open automatically if we pass the
 * parameter telling it not to do that to the download constructor.
 */
add_task(async function test_downloads_openDownloadsListOnStart_param() {
  await SpecialPowers.pushPrefEnv({
    set: [
      ["browser.download.always_ask_before_handling_new_types", false],
      ["browser.download.alwaysOpenPanel", true],
    ],
  });
  registerCleanupFunction(async () => {
    await SpecialPowers.popPrefEnv();
  });
  await downloadAndCheckPanel({ openDownloadsListOnStart: false });
  await SpecialPowers.popPrefEnv();
});

/**
 * Make sure the downloads panel _does not_ open automatically when an
 * extension calls the browser.downloads.download API method while it is
 * not handling user input, but that we do open it automatically when
 * the same WebExtensions API is called while handling user input
 * (See Bug 1759231)
 */
add_task(async function test_downloads_panel_on_webext_download_api() {
  await SpecialPowers.pushPrefEnv({
    set: [
      ["browser.download.always_ask_before_handling_new_types", false],
      ["browser.download.alwaysOpenPanel", true],
    ],
  });
  registerCleanupFunction(async () => {
    await SpecialPowers.popPrefEnv();
  });

  const extension = ExtensionTestUtils.loadExtension({
    manifest: {
      permissions: ["downloads"],
    },
    background() {
      async function startDownload(downloadOptions) {
        /* globals browser */
        const downloadId = await browser.downloads.download(downloadOptions);
        const downloadDone = new Promise(resolve => {
          browser.downloads.onChanged.addListener(function listener(delta) {
            browser.test.log(`downloads.onChanged = ${JSON.stringify(delta)}`);
            if (
              delta.id == downloadId &&
              delta.state?.current !== "in_progress"
            ) {
              browser.downloads.onChanged.removeListener(listener);
              resolve();
            }
          });
        });

        browser.test.sendMessage("start-download:done");
        await downloadDone;
        await browser.downloads.removeFile(downloadId);
        browser.test.sendMessage("removed-download-file");
      }

      browser.test.onMessage.addListener(
        (msg, { withHandlingUserInput, downloadOptions }) => {
          if (msg !== "start-download") {
            browser.test.fail(`Got unexpected test message: ${msg}`);
            return;
          }

          if (withHandlingUserInput) {
            browser.test.withHandlingUserInput(() =>
              startDownload(downloadOptions)
            );
          } else {
            startDownload(downloadOptions);
          }
        }
      );
    },
  });

  await extension.startup();

  startServer();

  async function testExtensionDownloadCall({ withHandlingUserInput }) {
    mustInterruptResponses();
    let rnd = Math.random();
    let url = httpUrl(`interruptible.txt?q=${rnd}`);

    extension.sendMessage("start-download", {
      withHandlingUserInput,
      downloadOptions: { url },
    });
    await extension.awaitMessage("start-download:done");

    let publicList = await Downloads.getList(Downloads.PUBLIC);
    let downloads = await publicList.getAll();

    let download = downloads.find(d => d.source.url === url);
    is(download.source.url, url, "download has the expected url");
    is(
      download.openDownloadsListOnStart,
      withHandlingUserInput,
      `download panel should ${withHandlingUserInput ? "open" : "stay closed"}`
    );

    continueResponses();
    await extension.awaitMessage("removed-download-file");
  }

  info(
    "Test extension downloads.download API method call without handling user input"
  );
  await testExtensionDownloadCall({ withHandlingUserInput: true });

  info(
    "Test extension downloads.download API method call while handling user input"
  );
  await testExtensionDownloadCall({ withHandlingUserInput: false });

  await extension.unload();
  await SpecialPowers.popPrefEnv();
});

/**
 * Make sure the downloads panel opens automatically with new download, only if
 * no other downloads are in progress.
 */
add_task(async function test_downloads_panel_remains_closed() {
  await SpecialPowers.pushPrefEnv({
    set: [["browser.download.always_ask_before_handling_new_types", false]],
  });
  await task_addDownloads([
    { state: DownloadsCommon.DOWNLOAD_DOWNLOADING },
    { state: DownloadsCommon.DOWNLOAD_DOWNLOADING },
  ]);

  let publicList = await Downloads.getList(Downloads.PUBLIC);
  let downloads = await publicList.getAll();

  info("setting 2 downloads to be in progress");
  downloads[0].stopped = false;
  downloads[1].stopped = false;

  let oldShowEventNotification = DownloadsIndicatorView.showEventNotification;

  registerCleanupFunction(async () => {
    // Remove all downloads created during the test.
    for (let download of downloads) {
      await publicList.remove(download);
    }
    DownloadsIndicatorView.showEventNotification = oldShowEventNotification;
  });

  let promiseDownloadStartedNotification = new Promise(resolve => {
    // Instead of downloads panel opening, download notification should be shown.
    DownloadsIndicatorView.showEventNotification = aType => {
      if (aType == "start") {
        DownloadsIndicatorView.showEventNotification = oldShowEventNotification;
        resolve();
      }
    };
  });

  DownloadsCommon.getData(window)._notifyDownloadEvent("start");

  is(
    DownloadsPanel.isPanelShowing,
    false,
    "Panel state should NOT indicate a preparation to be opened"
  );

  await promiseDownloadStartedNotification;

  is(DownloadsPanel.panel.state, "closed", "Panel should be closed");

  for (let download of downloads) {
    await publicList.remove(download);
  }
  is((await publicList.getAll()).length, 0, "Should have no downloads left.");
});

/**
 * Make sure the downloads panel doesn't open if the window isn't in the
 * foreground.
 */
add_task(async function test_downloads_panel_inactive_window() {
  await SpecialPowers.pushPrefEnv({
    set: [["browser.download.always_ask_before_handling_new_types", false]],
  });

  let oldShowEventNotification = DownloadsIndicatorView.showEventNotification;

  registerCleanupFunction(async () => {
    DownloadsIndicatorView.showEventNotification = oldShowEventNotification;
  });

  let promiseDownloadStartedNotification = new Promise(resolve => {
    // Instead of downloads panel opening, download notification should be shown.
    DownloadsIndicatorView.showEventNotification = aType => {
      if (aType == "start") {
        DownloadsIndicatorView.showEventNotification = oldShowEventNotification;
        resolve();
      }
    };
  });

  let testRunnerWindow = Array.from(Services.wm.getEnumerator("")).find(
    someWin => someWin != window
  );

  await SimpleTest.promiseFocus(testRunnerWindow);

  DownloadsCommon.getData(window)._notifyDownloadEvent("start");

  is(
    DownloadsPanel.isPanelShowing,
    false,
    "Panel state should NOT indicate a preparation to be opened"
  );

  await promiseDownloadStartedNotification;
  await SimpleTest.promiseFocus(window);

  is(DownloadsPanel.panel.state, "closed", "Panel should be closed");

  testRunnerWindow = null;
});

/**
 * When right-clicking the downloads toolbar button, there should be a menuitem
 * for toggling alwaysOpenPanel. Check that it works correctly.
 */
add_task(async function test_alwaysOpenPanel_menuitem() {
  const alwaysOpenPanelPref = "browser.download.alwaysOpenPanel";
  let checkbox = document.getElementById(
    "toolbar-context-always-open-downloads-panel"
  );
  let button = document.getElementById("downloads-button");

  Services.prefs.clearUserPref(alwaysOpenPanelPref);
  await SpecialPowers.pushPrefEnv({
    set: [["browser.download.autohideButton", false]],
  });
  registerCleanupFunction(async () => {
    await SpecialPowers.popPrefEnv();
    Services.prefs.clearUserPref(alwaysOpenPanelPref);
  });

  is(button.hidden, false, "Downloads button should not be hidden.");

  info("Check context menu for downloads button.");
  await openContextMenu(button);
  is(checkbox.hidden, false, "Always Open checkbox is visible.");
  is(checkbox.getAttribute("checked"), "true", "Always Open is enabled.");

  info("Disable Always Open via context menu.");
  clickCheckbox(checkbox);
  is(
    Services.prefs.getBoolPref(alwaysOpenPanelPref),
    false,
    "Always Open pref has been set to false."
  );

  await downloadAndCheckPanel();

  await openContextMenu(button);
  is(checkbox.hidden, false, "Always Open checkbox is visible.");
  isnot(checkbox.getAttribute("checked"), "true", "Always Open is disabled.");

  info("Enable Always Open via context menu");
  clickCheckbox(checkbox);
  is(
    Services.prefs.getBoolPref(alwaysOpenPanelPref),
    true,
    "Pref has been set to true"
  );

  await checkPanelOpens();
});

/**
 * Verify that the downloads panel opens if the download did not open a file
 * picker or UCT dialog
 */
add_task(async function test_downloads_panel_after_no_dialogs() {
  await testDownloadsPanelAfterDialog({ expectPanelToOpen: true });
  ok(true, "Downloads panel opened because no dialogs were opened.");
});

/**
 * Verify that the downloads panel doesn't open if the download opened an
 * unknown content type dialog (e.g. action = always ask)
 */
add_task(async function test_downloads_panel_after_UCT_dialog() {
  await testDownloadsPanelAfterDialog({
    expectPanelToOpen: false,
    preferredAction: Ci.nsIHandlerInfo.alwaysAsk,
  });
  ok(true, "Downloads panel suppressed after UCT dialog.");
});

/**
 * Verify that the downloads panel doesn't open if the download opened a file
 * picker dialog (e.g. useDownloadDir = false)
 */
add_task(async function test_downloads_panel_after_file_picker_dialog() {
  await testDownloadsPanelAfterDialog({
    expectPanelToOpen: false,
    preferredAction: Ci.nsIHandlerInfo.saveToDisk,
    askWhereToSave: true,
  });
  ok(true, "Downloads panel suppressed after file picker dialog.");
});

/**
 * Verify that the downloads panel doesn't open if the download opened both
 * dialogs (e.g. default action = always ask AND useDownloadDir = false)
 */
add_task(async function test_downloads_panel_after_both_dialogs() {
  await testDownloadsPanelAfterDialog({
    expectPanelToOpen: false,
    preferredAction: Ci.nsIHandlerInfo.alwaysAsk,
    askWhereToSave: true,
  });
  ok(true, "Downloads panel suppressed after UCT and file picker dialogs.");
});