/* Any copyright is dedicated to the Public Domain.
 * http://creativecommons.org/publicdomain/zero/1.0/ */

"use strict";

let { DefaultBrowserNotification } = ChromeUtils.import(
  "resource:///actors/AboutNewTabParent.jsm",
  {}
);

add_task(async function notification_shown_on_first_newtab_when_not_default() {
  await test_with_mock_shellservice({ isDefault: false }, async function() {
    ok(
      !gBrowser.getNotificationBox(gBrowser.selectedBrowser)
        .currentNotification,
      "There shouldn't be a notification when the test starts"
    );
    let firstTab = await BrowserTestUtils.openNewForegroundTab({
      gBrowser,
      opening: "about:newtab",
      waitForLoad: false,
    });
    let notification = await TestUtils.waitForCondition(
      () =>
        firstTab.linkedBrowser &&
        gBrowser.getNotificationBox(firstTab.linkedBrowser) &&
        gBrowser.getNotificationBox(firstTab.linkedBrowser).currentNotification,
      "waiting for notification"
    );
    ok(notification, "A notification should be shown on the new tab page");
    is(
      notification.getAttribute("value"),
      "default-browser",
      "Notification should be default browser"
    );

    let secondTab = await BrowserTestUtils.openNewForegroundTab({
      gBrowser,
      opening: "about:newtab",
      waitForLoad: false,
    });

    ok(
      secondTab.linkedBrowser &&
        gBrowser.getNotificationBox(secondTab.linkedBrowser) &&
        !gBrowser.getNotificationBox(secondTab.linkedBrowser)
          .currentNotification,
      "A notification should not be shown on the second new tab page"
    );

    gBrowser.removeTab(firstTab);
    gBrowser.removeTab(secondTab);
  });
});

add_task(async function notification_bar_removes_itself_on_navigation() {
  await test_with_mock_shellservice({ isDefault: false }, async function() {
    let firstTab = await BrowserTestUtils.openNewForegroundTab({
      gBrowser,
      opening: "about:newtab",
      waitForLoad: false,
    });
    let notification = await TestUtils.waitForCondition(
      () =>
        firstTab.linkedBrowser &&
        gBrowser.getNotificationBox(firstTab.linkedBrowser) &&
        gBrowser.getNotificationBox(firstTab.linkedBrowser).currentNotification,
      "waiting for notification"
    );
    ok(notification, "A notification should be shown on the new tab page");
    is(
      notification.getAttribute("value"),
      "default-browser",
      "Notification should be default browser"
    );

    BrowserTestUtils.loadURI(gBrowser.selectedBrowser, "https://example.com");

    let notificationRemoved = await TestUtils.waitForCondition(
      () =>
        firstTab.linkedBrowser &&
        gBrowser.getNotificationBox(firstTab.linkedBrowser) &&
        !gBrowser.getNotificationBox(firstTab.linkedBrowser)
          .currentNotification,
      "waiting for notification to get removed"
    );
    ok(
      notificationRemoved,
      "A notification should not be shown after navigation"
    );

    gBrowser.removeTab(firstTab);
  });
});

add_task(async function notification_appears_on_first_navigation_to_homepage() {
  await test_with_mock_shellservice({ isDefault: false }, async function() {
    let tab = await BrowserTestUtils.openNewForegroundTab({
      gBrowser,
      opening: "about:robots",
    });
    ok(
      tab.linkedBrowser &&
        gBrowser.getNotificationBox(tab.linkedBrowser) &&
        !gBrowser.getNotificationBox(tab.linkedBrowser).currentNotification,
      "a notification should not be shown on about:robots"
    );
    BrowserTestUtils.loadURI(gBrowser.selectedBrowser, "about:home");

    let notification = await TestUtils.waitForCondition(
      () =>
        tab.linkedBrowser &&
        gBrowser.getNotificationBox(tab.linkedBrowser) &&
        gBrowser.getNotificationBox(tab.linkedBrowser).currentNotification,
      "waiting for notification to appear on about:home after coming from about:robots"
    );
    ok(
      notification,
      "A notification should be shown after navigation to about:home"
    );
    is(
      notification.getAttribute("value"),
      "default-browser",
      "Notification should be default browser"
    );

    gBrowser.removeTab(tab);
  });
});

add_task(async function clicking_button_on_notification_calls_setAsDefault() {
  await test_with_mock_shellservice({ isDefault: false }, async function() {
    let firstTab = await BrowserTestUtils.openNewForegroundTab({
      gBrowser,
      opening: "about:newtab",
      waitForLoad: false,
    });
    let notification = await TestUtils.waitForCondition(
      () =>
        firstTab.linkedBrowser &&
        gBrowser.getNotificationBox(firstTab.linkedBrowser) &&
        gBrowser.getNotificationBox(firstTab.linkedBrowser).currentNotification,
      "waiting for notification"
    );
    ok(notification, "A notification should be shown on the new tab page");
    is(
      notification.getAttribute("value"),
      "default-browser",
      "Notification should be default browser"
    );

    let shellService = window.getShellService();
    ok(
      !shellService.isDefaultBrowser(),
      "should not be default prior to clicking button"
    );
    let button = notification.querySelector(".notification-button");
    button.click();
    ok(
      shellService.isDefaultBrowser(),
      "should be default after clicking button"
    );

    gBrowser.removeTab(firstTab);
  });
});

add_task(async function notification_not_displayed_on_private_window() {
  let privateWin = await BrowserTestUtils.openNewBrowserWindow({
    private: true,
  });
  await test_with_mock_shellservice(
    { win: privateWin, isDefault: false },
    async function() {
      await BrowserTestUtils.openNewForegroundTab({
        gBrowser: privateWin.gBrowser,
        opening: "about:newtab",
        waitForLoad: false,
      });
      ok(
        !privateWin.gBrowser.getNotificationBox(
          privateWin.gBrowser.selectedBrowser
        ).currentNotification,
        "There shouldn't be a notification in the private window"
      );
      await BrowserTestUtils.closeWindow(privateWin);
    }
  );
});

add_task(async function notification_displayed_on_perm_private_window() {
  SpecialPowers.pushPrefEnv({
    set: [["browser.privatebrowsing.autostart", true]],
  });
  let privateWin = await BrowserTestUtils.openNewBrowserWindow({
    private: true,
  });
  await test_with_mock_shellservice(
    { win: privateWin, isDefault: false },
    async function() {
      let tab = await BrowserTestUtils.openNewForegroundTab({
        gBrowser: privateWin.gBrowser,
        opening: "about:newtab",
        waitForLoad: false,
      });
      ok(
        PrivateBrowsingUtils.isBrowserPrivate(
          privateWin.gBrowser.selectedBrowser
        ),
        "Browser should be private"
      );
      let notification = await TestUtils.waitForCondition(
        () =>
          tab.linkedBrowser &&
          gBrowser.getNotificationBox(tab.linkedBrowser) &&
          gBrowser.getNotificationBox(tab.linkedBrowser).currentNotification,
        "waiting for notification"
      );
      ok(notification, "A notification should be shown on the new tab page");
      is(
        notification.getAttribute("value"),
        "default-browser",
        "Notification should be default browser"
      );
      await BrowserTestUtils.closeWindow(privateWin);
    }
  );
});

add_task(async function clicking_dismiss_disables_default_browser_checking() {
  await test_with_mock_shellservice({ isDefault: false }, async function() {
    let firstTab = await BrowserTestUtils.openNewForegroundTab({
      gBrowser,
      opening: "about:newtab",
      waitForLoad: false,
    });
    let notification = await TestUtils.waitForCondition(
      () =>
        firstTab.linkedBrowser &&
        gBrowser.getNotificationBox(firstTab.linkedBrowser) &&
        gBrowser.getNotificationBox(firstTab.linkedBrowser).currentNotification,
      "waiting for notification"
    );
    ok(notification, "A notification should be shown on the new tab page");
    is(
      notification.getAttribute("value"),
      "default-browser",
      "Notification should be default browser"
    );

    let closeButton = notification.querySelector(".close-icon");
    closeButton.click();
    ok(
      !Services.prefs.getBoolPref("browser.shell.checkDefaultBrowser"),
      "checkDefaultBrowser bar pref should be false after dismissing notification"
    );

    gBrowser.removeTab(firstTab);
  });
});

add_task(async function notification_not_shown_on_first_newtab_when_default() {
  await test_with_mock_shellservice({ isDefault: true }, async function() {
    ok(
      !gBrowser.getNotificationBox(gBrowser.selectedBrowser)
        .currentNotification,
      "There shouldn't be a notification when the test starts"
    );
    let firstTab = await BrowserTestUtils.openNewForegroundTab({
      gBrowser,
      opening: "about:newtab",
      waitForLoad: false,
    });
    ok(
      firstTab.linkedBrowser &&
        gBrowser.getNotificationBox(firstTab.linkedBrowser) &&
        !gBrowser.getNotificationBox(firstTab.linkedBrowser)
          .currentNotification,
      "No notification on first tab when browser is default"
    );

    gBrowser.removeTab(firstTab);
  });
});

add_task(async function modal_notification_shown_when_bar_disabled() {
  await test_with_mock_shellservice({ useModal: true }, async function() {
    let modalOpenPromise = BrowserTestUtils.promiseAlertDialogOpen("cancel");

    // This method is called during startup. Call it now so we don't have to test startup.
    let { BrowserGlue } = ChromeUtils.import(
      "resource:///modules/BrowserGlue.jsm",
      {}
    );
    BrowserGlue.prototype._maybeShowDefaultBrowserPrompt();

    await modalOpenPromise;
  });
});

async function test_with_mock_shellservice(options, testFn) {
  let win = options.win || window;
  let oldShellService = win.getShellService;
  let mockShellService = {
    _isDefault: !!options.isDefault,
    canSetDesktopBackground() {},
    isDefaultBrowserOptOut() {
      return false;
    },
    get shouldCheckDefaultBrowser() {
      return true;
    },
    isDefaultBrowser() {
      return this._isDefault;
    },
    setAsDefault() {
      this.setDefaultBrowser();
    },
    setDefaultBrowser() {
      this._isDefault = true;
    },
  };
  win.getShellService = function() {
    return mockShellService;
  };
  let prefs = {
    set: [
      ["browser.shell.checkDefaultBrowser", true],
      ["browser.defaultbrowser.notificationbar", !options.useModal],
      ["browser.defaultbrowser.notificationbar.checkcount", 0],
    ],
  };
  if (options.useModal) {
    prefs.set.push(["browser.shell.skipDefaultBrowserCheckOnFirstRun", false]);
  }
  await SpecialPowers.pushPrefEnv(prefs);

  // Reset the state so the notification can be shown multiple times in one session
  DefaultBrowserNotification.reset();

  await testFn();

  win.getShellService = oldShellService;
}
