<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
                 type="text/css"?>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=549682
-->
<window title="Mozilla Bug 549682"
  xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
  onload="run()">
  <label value="Mozilla Bug 549682"/>
  <!-- test code goes here -->
  <script type="application/javascript"><![CDATA[
  var Cc = Components.classes;
  var Ci = Components.interfaces;
  var Cr = Components.results;
  var Cu = Components.utils;

  var didRunAsync = false;
  var didRunLocal = false;

  var global = Cc["@mozilla.org/globalmessagemanager;1"]
                 .getService(Components.interfaces.nsIMessageBroadcaster);
  var ppm = Cc["@mozilla.org/parentprocessmessagemanager;1"]
              .getService(Components.interfaces.nsIMessageBroadcaster);
  var cpm = Cc["@mozilla.org/childprocessmessagemanager;1"]
              .getService(Components.interfaces.nsISyncMessageSender);

  Cu.import("resource://gre/modules/XPCOMUtils.jsm");

  function ok(cond, msg) {
    opener.wrappedJSObject.ok(cond, msg);
  }

  function is(actual, expected, msg) {
    opener.wrappedJSObject.is(actual, expected, msg);
  }

  var asyncPPML = false;
  function ppmASL(m) {
    asyncPPML = true;
  }
  var syncPPML = false;
  function ppmSL(m) {
    syncPPML = true;
  }
  ppm.addMessageListener("processmessageAsync", ppmASL);
  ppm.addMessageListener("processmessageSync", ppmSL);

  cpm.sendAsyncMessage("processmessageAsync", "");
  cpm.sendSyncMessage("processmessageSync", "");

  var asyncCPML = false;
  function cpmASL(m) {
    asyncCPML = true;
  }
  cpm.addMessageListener("childprocessmessage", cpmASL);
  ppm.broadcastAsyncMessage("childprocessmessage", "");
  
  function checkPMMMessages() {
    ok(asyncPPML, "should have handled async message");
    ok(syncPPML, "should have handled sync message");
    ok(asyncCPML, "should have handled async message");
    ppm.removeMessageListener("processmessageAsync", ppmASL);
    ppm.removeMessageListener("processmessageSync", ppmSL);
    cpm.removeMessageListener("childprocessmessage", cpmASL);
  }

  var globalListenerCallCount = 0;
  function globalListener(m) {
    ++globalListenerCallCount;
    if (m.name == "sync") {
      global.removeMessageListener("async", globalListener);
      global.removeMessageListener("sync", globalListener);
      global.removeMessageListener("global-sync", globalListener);
      // Note, the result depends on what other windows are open.
      ok(globalListenerCallCount >= 4,
         "Global listener should have been called at least 4 times!");
      ok(didRunLocal, "Local message received.");
    }
  }

  function asyncL(m) {
    didRunAsync = true;
    is(m.name, "async", "Wrong message!");
    is(m.json.data, 1234, "Wrong data!");
  }

  function syncL(m) {
    is(m.name, "sync", "Wrong message!");
    is(m.json.data, 1234, "Wrong data!");
    ok(didRunAsync, "Should have run async!");
  }

  function localL(m) {
    is(m.name, "lasync", "Wrong message!");
    is(m.json.data, 2345, "Wrong data!");
    didRunLocal = true;
  }

  var weakMessageReceived = false;
  var weakListener = {
    QueryInterface: XPCOMUtils.generateQI([Ci.nsIMessageListener,
                                           Ci.nsISupportsWeakReference]),

    receiveMessage: function(msg) {
      if (weakMessageReceived) {
        ok(false, 'Weak listener fired twice.');
        return;
      }

      ok(true, 'Weak listener fired once.');
      weakMessageReceived = true;
      document.getElementById('ifr').messageManager
              .removeWeakMessageListener('weak', weakListener);
    }
  };

  var weakListener2 = {
    QueryInterface: XPCOMUtils.generateQI([Ci.nsIMessageListener,
                                           Ci.nsISupportsWeakReference]),

    receiveMessage: function(msg) {
      ok(false, 'Should not have received a message.');
    }
  };

  function weakDoneListener() {
    ok(weakMessageReceived, 'Got "weak" message.');
    finish();
  }

  function finish() {
    opener.setTimeout("done()", 0);
    var i = document.getElementById("ifr");
    i.parentNode.removeChild(i); // This is a crash test!
    window.close();
  }

  function loadScript() {
    // Async should be processed first!
    messageManager.loadFrameScript("data:,\
      sendAsyncMessage('async', { data: 1234 });\
      sendSyncMessage('sync', { data: 1234 });\
      sendAsyncMessage('weak', {});\
      sendAsyncMessage('weak', {});\
      sendAsyncMessage('weakdone', {});", false);
  }

  function run() {
    var localmm = document.getElementById('ifr').messageManager;

    var wn = document.getElementById('ifr').contentWindow
      .getInterface(Components.interfaces.nsIWebNavigation);
    ok(wn, "Should have webnavigation");
    var cfmm = wn.getInterface(Components.interfaces.nsIContentFrameMessageManager);
    ok(cfmm, "Should have content messageManager");

    var didGetSyncMessage = false;
    function syncContinueTestFn() {
      didGetSyncMessage = true;
    }
    localmm.addMessageListener("syncContinueTest", syncContinueTestFn);
    cfmm.sendSyncMessage("syncContinueTest", {});
    localmm.removeMessageListener("syncContinueTest", syncContinueTestFn);
    ok(didGetSyncMessage, "Should have got sync message!");
    
    localmm.addMessageListener("lasync", localL);
    localmm.loadFrameScript("data:,sendAsyncMessage('lasync', { data: 2345 })", false);

    messageManager.addMessageListener("async", asyncL);
    messageManager.addMessageListener("sync", syncL);
    global.addMessageListener("async", globalListener);
    global.addMessageListener("sync", globalListener);
    global.addMessageListener("global-sync", globalListener);
    global.loadFrameScript("data:,sendSyncMessage('global-sync', { data: 1234 });", true);
    var toBeRemovedScript = "data:,sendAsyncMessage('toberemoved', { data: 2345 })";
    var c = 0;
    messageManager.addMessageListener("toberemoved", function() {
      ++c;
      is(c, 1, "Should be called only once!");
    });
    // This loads the script in the existing <browser>
    messageManager.loadFrameScript(toBeRemovedScript, true);
    // But it won't be loaded in the dynamically created <browser>
    messageManager.removeDelayedFrameScript(toBeRemovedScript);

    var oldValue = globalListenerCallCount;
    var b = document.createElement("browser");
    b.setAttribute("type", "content");
    document.documentElement.appendChild(b);
    is(globalListenerCallCount, oldValue + 1,
                              "Wrong message count");

    localmm.addWeakMessageListener('weak', weakListener);
    localmm.addMessageListener('weakdone', weakDoneListener);

    // Add weakListener2 as a weak message listener, then force weakListener2
    // to be gc'ed.  weakListener2 shouldn't be run.
    var weakRef = Cu.getWeakReference(weakListener2);
    localmm.addWeakMessageListener('weak', weakListener2);
    weakListener2 = null;

    // Force a gc/cc in a loop until weakRef's referent has gone away.
    function waitForWeakRefToDie() {
      if (weakRef.get()) {
        var mgr = Cc["@mozilla.org/memory-reporter-manager;1"]
                    .getService(Ci.nsIMemoryReporterManager);
        mgr.minimizeMemoryUsage(waitForWeakRefToDie);

        // Print a message so that if the test hangs in a minimizeMemoryUsage
        // loop, we'll be able to see it in the log.
        ok(true, "waitForWeakRefToDie spinning...");
        return;
      }

      setTimeout(checkPMMMessages, 0);
      setTimeout(loadScript, 0);
    }

    waitForWeakRefToDie();
  }

  ]]></script>
  <browser type="content" src="about:blank" id="ifr"/>
</window>