<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=61098
-->
<head>
  <title>Test for Bug 61098</title>
  <script src="/tests/SimpleTest/SimpleTest.js"></script>
  <script src="/tests/SimpleTest/EventUtils.js"></script>
  <script src="/tests/SimpleTest/MockObjects.js"></script>
  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body onload="runtests();">
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=61098">Mozilla Bug 61098</a>
<p id="display">
</p>
<div id="content" style="display: none">
</div>
<pre id="test">
</pre>
<script class="testbody" type="text/javascript">
/** Test for Bug 61098 **/

SimpleTest.waitForExplicitFinish();

var mockPromptServiceRegisterer;

var promptState;

function registerMockPromptService()
{
  var { XPCOMUtils } = SpecialPowers.ChromeUtils.importESModule(
    "resource://gre/modules/XPCOMUtils.sys.mjs"
  );
  var Ci = SpecialPowers.Ci;

  function MockPrompt(aDOMWindow) {
    this.domWindow = SpecialPowers.unwrap(aDOMWindow);
  }

  MockPrompt.prototype = {
    QueryInterface(iid) {
      if (iid.equals(Ci.nsIPrompt)) {
        return this;
      }
      throw SpecialPowers.Cr.NS_ERROR_NO_INTERFACE;
    },

    domWindow : null,

    _toggleModalState()
    {
      // The real prompt service puts the window into a modal state
      // immediately before showing a modal prompt, and leaves the modal state
      // when the prompt is dismissed by the user. This mock prompt doesn't
      // show anything to the user, so we only need to enter and immediately
      // leave the modal state -- this is done to trigger the necessary
      // accounting for triggering the "stop showing more prompts" code for
      // abusive pages.
      var winUtils = SpecialPowers.getDOMWindowUtils(this.domWindow);
      winUtils.enterModalState();
      winUtils.leaveModalState();
    },

    alert(aDialogTitle, aText)
    {
      this._toggleModalState();
      promptState = {method: "alert",
                     parent: SpecialPowers.unwrap(this.domWindow),
                     title: aDialogTitle,
                     msg: aText
      };
    },

    alertCheck(aDialogTitle, aText, aCheckMsg, aCheckState)
    {
      this._toggleModalState();
      promptState = {method: "alertCheck",
                     parent: SpecialPowers.unwrap(this.domWindow),
                     title: aDialogTitle,
                     msg: aText,
                     checkMsg: aCheckMsg,
                     checkState: aCheckState
      };

      SpecialPowers.wrap(aCheckState).value = true;
    },

    confirm(aDialogTitle, aText)
    {
      this._toggleModalState();
      promptState = {method: "confirm",
                     parent: SpecialPowers.unwrap(this.domWindow),
                     title: aDialogTitle,
                     msg: aText
      };

      return true;
    },

    confirmCheck(aDialogTitle, aText, aCheckMsg, aCheckState)
    {
      this._toggleModalState();
      promptState = {method: "confirmCheck",
                     parent: SpecialPowers.unwrap(this.domWindow),
                     title: aDialogTitle,
                     msg: aText,
                     checkMsg: aCheckMsg,
                     checkState: aCheckState
      };

      SpecialPowers.wrap(aCheckState).value = true;

      return true;
    },

    confirmEx(aDialogTitle, aText, aButtonFlags,
                        aButton0Title, aButton1Title, aButton2Title,
                        aCheckMsg, aCheckState)
    {
      this._toggleModalState();
      promptState = {method: "confirmCheck",
                     parent: SpecialPowers.unwrap(this.domWindow),
                     title: aDialogTitle,
                     msg: aText,
                     checkMsg: aCheckMsg,
                     checkState: aCheckState
      };

      if (aCheckMsg != null)
        SpecialPowers.wrap(aCheckState).value = true;

      return 0;
    },

    prompt(aDialogTitle, aText, aValue, aCheckMsg,
                     aCheckState)
    {
      this._toggleModalState();
      promptState = {method: "prompt",
                     parent: SpecialPowers.unwrap(this.domWindow),
                     title: aDialogTitle,
                     msg: aText,
                     checkMsg: aCheckMsg,
                     checkState: aCheckState
      };

      if (aCheckMsg != null)
        SpecialPowers.wrap(aCheckState).value = true;

      return true;
    },
  };


  // Override the prompt service with our own so that we can test
  // modal dialogs

  function MockPromptService()
  {
  }

  MockPromptService.prototype = {
    QueryInterface(iid) {
      if (iid.equals(Ci.nsIPromptFactory) || iid.equals(Ci.nsIPromptService)) {
        return this;
      }
      throw SpecialPowers.Cr.NS_ERROR_NO_INTERFACE;
    },

    getPrompt(aDOMWindow, aIID)
    {
        return SpecialPowers.wrapCallbackObject(new MockPrompt(aDOMWindow));
    },

    alert(aParent, aDialogTitle, aText)
    {
      var prompt = new MockPrompt(aParent);
      return prompt.alert(aDialogTitle, aText);
    },

    alertCheck(aParent, aDialogTitle, aText, aCheckMsg, aCheckState)
    {
      var prompt = new MockPrompt(aParent);
      return prompt.alertCheck(aDialogTitle, aText, aCheckMsg, aCheckState);
    },

    confirm(aParent, aDialogTitle, aText)
    {
      var prompt = new MockPrompt(aParent);
      return prompt.confirm(aDialogTitle, aText);
    },

    confirmCheck(aParent, aDialogTitle, aText, aCheckMsg,
                           aCheckState)
    {
      var prompt = new MockPrompt(aParent);
      return prompt.confirmCheck(aDialogTitle, aText, aCheckMsg, aCheckState);
    },

    confirmEx(aParent, aDialogTitle, aText, aButtonFlags,
                        aButton0Title, aButton1Title, aButton2Title,
                        aCheckMsg, aCheckState)
    {
      var prompt = new MockPrompt(aParent);
      return prompt.confirmEx(aDialogTitle, aText, aButtonFlags,
                        aButton0Title, aButton1Title, aButton2Title,
                        aCheckMsg, aCheckState);
    },

    prompt(aParent, aDialogTitle, aText, aValue, aCheckMsg,
                     aCheckState)
    {
      var prompt = new MockPrompt(aParent);
      return prompt.prompt(aDialogTitle, aText, aValue, aCheckMsg, aCheckState);
    },

  };

  mockPromptServiceRegisterer =
    new MockObjectRegisterer("@mozilla.org/prompter;1",
                             MockPromptService);
  mockPromptServiceRegisterer.register();
};

var expectedState;

function runtests()
{
  SpecialPowers.pushPrefEnv({'set': [["dom.successive_dialog_time_limit", 3]]},
                            runtestsInner);
}

async function runtestsInner()
{
  registerMockPromptService();

  // Test that alert() works normally and then gets blocked on the
  // second call.
  w = window.open();
  w.alert("alert message 1");
  is (promptState.method, "alert", "Wrong prompt method called");
  is (promptState.parent, w, "Wrong alert parent");
  is (promptState.msg, "alert message 1", "Wrong alert message");
  promptState = void(0);

  w.alert("alert message 2");
  is (promptState.method, "alertCheck", "Wrong prompt method called");
  is (promptState.parent, w, "Wrong alert parent");
  is (promptState.msg, "alert message 2", "Wrong alert message");
  promptState = void(0);

  try {
    w.alert("alert message 3");
  } catch(e) {
    is(e.name, "NS_ERROR_NOT_AVAILABLE", "Wrong exception");
  }

  is (promptState, void(0), "Wrong prompt state after blocked alert()");

  w.close();

  // Then check that alert() gets blocked with a new window, because
  // dialogs are disabled/enabled on basis of browsing context groups.
  w = window.open();

  try {
    w.alert("alert message 4");
  } catch(e) {
    is(e.name, "NS_ERROR_NOT_AVAILABLE", "Wrong exception");
  }

  is (promptState, void(0), "Wrong prompt state after blocked alert()");

  // Reset the state and enable the dialogs again.
  SpecialPowers.DOMWindowUtils.enableDialogs();
  SpecialPowers.DOMWindowUtils.resetDialogAbuseState();

  // Test that confirm() works normally and then gets blocked on the
  // second call.
  w.confirm("confirm message 1");
  is (promptState.method, "confirm", "Wrong prompt method called");
  is (promptState.parent, w, "Wrong confirm parent");
  is (promptState.msg, "confirm message 1", "Wrong confirm message");
  promptState = void(0);

  w.confirm("confirm message 2");
  is (promptState.method, "confirmCheck", "Wrong prompt method called");
  is (promptState.parent, w, "Wrong confirm parent");
  is (promptState.msg, "confirm message 2", "Wrong confirm message");
  promptState = void(0);

  try {
    w.confirm("confirm message 3");
  } catch(e) {
    is(e.name, "NS_ERROR_NOT_AVAILABLE", "Wrong exception");
  }

  is (promptState, void(0), "Wrong prompt state after blocked confirm()");

  w.close();

  // Then check that confirm() gets blocked with a new window, because
  // dialogs are disabled/enabled on basis of browsing context groups.
  w = window.open();

  try {
    w.confirm("confirm message 4");
  } catch(e) {
    is(e.name, "NS_ERROR_NOT_AVAILABLE", "Wrong exception");
  }

  is (promptState, void(0), "Wrong prompt state after blocked prompt()");

  // Reset the state and enable the dialogs again.
  SpecialPowers.DOMWindowUtils.enableDialogs();
  SpecialPowers.DOMWindowUtils.resetDialogAbuseState();

  // Test that prompt() works normally and then gets blocked on the
  // second call.
  w.prompt("prompt message 1");
  is (promptState.method, "prompt", "Wrong prompt method called");
  is (promptState.parent, w, "Wrong prompt parent");
  is (promptState.msg, "prompt message 1", "Wrong prompt message");
  is (promptState.checkMsg, null, "Wrong dialog value");
  promptState = void(0);

  w.prompt("prompt message 2");
  is (promptState.method, "prompt", "Wrong prompt method called");
  is (promptState.parent, w, "Wrong prompt parent");
  is (promptState.msg, "prompt message 2", "Wrong prompt message");
  is (promptState.checkMsg, "Prevent this page from creating additional dialogs", "Wrong dialog value");
  promptState = void(0);

  try {
    w.prompt("prompt message 3");
  } catch(e) {
    is(e.name, "NS_ERROR_NOT_AVAILABLE", "Wrong exception");
  }

  is (promptState, void(0), "Wrong prompt state after blocked prompt()");

  w.close();

  // Then check that prompt() gets blocked with a new window, because
  // dialogs are disabled/enabled on basis of browsing context groups.
  w = window.open();

  try {
    w.prompt("prompt message 4");
  } catch(e) {
    is(e.name, "NS_ERROR_NOT_AVAILABLE", "Wrong exception");
  }

  is (promptState, void(0), "Wrong prompt state after blocked prompt()");

  w.close();

  // Reset the state and enable the dialogs again.
  SpecialPowers.DOMWindowUtils.enableDialogs();
  SpecialPowers.DOMWindowUtils.resetDialogAbuseState();

  mockPromptServiceRegisterer.unregister();

  SimpleTest.finish();
}

</script>
</body>
</html>
