<?xml version="1.0"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->

<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
        width="800" height="800" orient="vertical" onload="initRemoteFrameScript();">
  <script>

    function dumpClientRect(r) {
      dump(r.left + "," + r.top + "," + r.right + "," +
           r.bottom + "," + r.width + "," + r.height + "\n");
    }

    function handleMozAfterPaint(e) {
      return;
      dump(e.type + "\n")
      var rects = e.clientRects;
      var i;
      dump("\tclientRects:\n");
      for (i = 0; i &lt; rects.length; ++i) {
        var r = rects.item(i);
        dump("\t\t");
        dumpClientRect(rects.item(i));
      }

      dump("\tboundingClientRect\n\t\t");
      dumpClientRect(e.boundingClientRect);

      var paintRequests = e.paintRequests;
      dump("\tpaintRequests\n");
      for (i = 0; i &lt; paintRequests.length; ++i) {
        var pr = paintRequests.item(i);
        dump("\t\t");
        dumpClientRect(pr.clientRect);
        if (pr.reason)
          dump("\t\t" + pr.reason + "\n");
      }
    }

    function handleMozScrolledAreaChanged(e) {
      return;
      dump(e.type + "\n");
      dump("\t" + e.x + "," + e.y + "," + e.width + "," + e.height + "\n");
    }

    function restart() {
      var y = document.getElementById('page');
      var p = y.parentNode;
      p.removeChild(y);
      p.appendChild(y);

      var fl = y.QueryInterface(Components.interfaces.nsIFrameLoaderOwner).frameLoader;
      fl.activateFrameEvent("MozAfterPaint", true);
      fl.activateFrameEvent("MozScrolledAreaChanged", true);
      y.addEventListener("MozAfterPaint", handleMozAfterPaint, true);
      y.addEventListener("MozScrolledAreaChanged", handleMozScrolledAreaChanged, true);
    }
    
    function loadURL(url) {
      document.getElementById('page').setAttribute('src', url);
    }

    function randomClick() {
       // First focus the remote frame, then dispatch click. This way remote frame gets focus before
       // mouse event.
       document.getElementById('page').focus();
       var frameLoader = document.getElementById('page').QueryInterface(Components.interfaces.nsIFrameLoaderOwner).frameLoader;
       var x = parseInt(Math.random() * 100);
       var y = parseInt(Math.random() * 100);
       frameLoader.sendCrossProcessMouseEvent("mousedown", x, y, 0, 1, 0, false);
       frameLoader.sendCrossProcessMouseEvent("mouseup", x, y, 0, 1, 0, false);
     }

    function keyPress() {
       // First focus the remote frame, then dispatch click. This way remote frame gets focus before
       // mouse event.
       document.getElementById('page').focus();
       var frameLoader = document.getElementById('page').QueryInterface(Components.interfaces.nsIFrameLoaderOwner).frameLoader;

       var keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_A;
       frameLoader.sendCrossProcessKeyEvent("keydown", keyCode, 0, 0);
       frameLoader.sendCrossProcessKeyEvent("keypress", keyCode, 0, 0);
       frameLoader.sendCrossProcessKeyEvent("keyup", keyCode, 0, 0);
     }

    function openWindow() {
      window.open('chrome://global/content/test-ipc.xul', '_blank', 'chrome,resizable,width=800,height=800');
    }
    
    function closeWindow() {
      window.close();
    }

    function initRemoteFrameScript() {
      // 1. Test that loading a script works, and that accessing process level mm and
      //     global mm works.
      var ppm = Components.classes["@mozilla.org/parentprocessmessagemanager;1"]
                          .getService(Components.interfaces.nsIMessageBroadcaster);
      var gm = Components.classes["@mozilla.org/globalmessagemanager;1"]
                         .getService(Components.interfaces.nsIMessageBroadcaster);
      var cpm = Components.classes["@mozilla.org/childprocessmessagemanager;1"]
                            .getService(Components.interfaces.nsISyncMessageSender);

      if (ppm.childCount != 2) {
        alert("Should have two child processes!");
      }
      var childprocessmm = ppm.getChildAt(1); // 0 is the in-process child process mm
      
      childprocessmm.addMessageListener("ppm-sync",
        function(m) {
          if (m.target != childprocessmm) alert("Wrong target!");
          document.getElementById("messageLog").value += "[SYNC1 PPM]"; 
        }
      );
      
      ppm.addMessageListener("ppm-sync",
        function(m) {
          // Check that global process message manager gets the per-process mm as target.
          if (m.target != childprocessmm) alert("Wrong target!");
          document.getElementById("messageLog").value += "[SYNC2 PPM]"; 
        }
      );
      childprocessmm.addMessageListener("ppm-async",
        function(m) {
          if (m.target != childprocessmm) alert("Wrong target!");
          document.getElementById("messageLog").value += "[ASYNC1 PPM]"; 
        }
      );
      ppm.addMessageListener("ppm-async",
        function(m) {
          // Check that global process message manager gets the per-process mm as target.
          if (m.target != childprocessmm) alert("Wrong target!");
          document.getElementById("messageLog").value += "[ASYNC2 PPM]"; 
        }
      );
      messageManager.loadFrameScript("chrome://global/content/remote-test-ipc.js", true);
      ppm.sendAsyncMessage("cpm-async");

      // 2. Test that adding message listener works, and that receiving a sync message works.
      messageManager.addMessageListener("linkclick",
        function(m) {
          // This checks that json sending works in sync messages. 
          document.getElementById("messageLog").value = m.name + ": " + m.json.href;
          return { message: "linkclick-received" };
        });

      // 3. Test that returning multiple json results works.
      messageManager.addMessageListener("linkclick",
        function(m) {
          return { message: "linkclick-received" };
        });

      // 4. Test that receiving an async message works.
      //    Test also that sending async message to content works.
      //    Test also that { receiveMessage: function(m) {} } works.
      messageManager.addMessageListener("linkclick-reply-object",
        { foobarObjectVar: true,
          receiveMessage: function(m) {
            var s = (m.json.message == "linkclick-received") &amp;&amp;
                    (this.foobarObjectVar) ? "PASS" : "FAIL";
            messageManager.broadcastAsyncMessage("chrome-message", { ok : s } );
          }
        }
        );

      // 5. Final test to check that everything went ok.
      messageManager.addMessageListener("chrome-message-reply",
        function(m) {
          // Check that 'this' and .target values are handled correctly
          if (m.target == document.getElementById("page") &amp;&amp;
              this == messageManager) {
            // Check that the message properties are enumerable.
            var hasName = false;
            var hasSync = false;
            var hasJSON = false;
            for (i in m) {
              if (i == "name") {
                hasName = true;
              } else if (i == "sync") {
                hasSync = true;
              } else if (i == "json") {
                hasJSON = true;
              }
            }
            if (hasName &amp;&amp; hasSync &amp;&amp; hasJSON) {
              document.getElementById("messageLog").value += ", " + m.json.ok;
            }
          }
        });
    }

    var speedTestStartTime = 0;
    var speedTestCount = 0;
    function messageSpeed() {
      speedTestCount = 0;
      messageManager.addMessageListener("speed-test", speedHandler);
      messageManager.broadcastAsyncMessage("speed-test-start");
    }

    function speedHandler() {
      if (!speedTestCount) {
        speedTestStartTime = new Date().getTime();
      }
      if (++speedTestCount == 1000) {
        setTimeout("alert('" + speedTestCount + " in "  + (new Date().getTime() - speedTestStartTime) +  "ms')", 0);
        return { message: "done" };
      }
      return { message: "continue" };
    }

    var addRemoveTestCount = 0;

    function echoListener() {
      if (++addRemoveTestCount == 1) {
        alert("Expected echo message");
        messageManager.removeMessageListener("async-echo", echoListener);
        messageManager.broadcastAsyncMessage("async-echo");
        return;
      }
      alert("Unexpected echo message");
    }

    function listenerAddRemove() {
      addRemoveTestCount = 0;
      messageManager.addMessageListener("async-echo", echoListener);
      messageManager.broadcastAsyncMessage("async-echo");
    }

    var MozAfterPaintCount = 0;
    function enableMozAfterPaint() {
      messageManager.addMessageListener("MozAfterPaint",
        function(m) {
          document.getElementById("messageLog").value = m.name + "[" + (++MozAfterPaintCount) + "]";
        });
      messageManager.loadFrameScript("data:,addEventListener('MozAfterPaint', function(e) { sendAsyncMessage('MozAfterPaint'); },true);", false);
    }

   var Ci = Components.interfaces;
   var Cu = Components.utils;
   Cu.import("resource://gre/modules/XPCOMUtils.jsm");

    function DialogProvider() { }

    DialogProvider.prototype = {
      QueryInterface: XPCOMUtils.generateQI([Ci.nsIBrowserDOMWindow, Ci.nsIDialogCreator, Ci.nsISupports]),
      openURI: function (aURI, aOpener, aWhere, aContext) {
        return null;
      },
      isTabContentWindow: function (aWindow) {
        return false;
      },
      openDialog: function(aType, aName, aFeatures, aArguments, aFrameElement) {
        alert(aType + ", " + aName + ", " + aFeatures + ", " + aArguments + ", " + aFrameElement);
      }
    }

    function defaultDialogs(useDefault) {
       window.QueryInterface(Ci.nsIDOMChromeWindow).browserDOMWindow = useDefault ? null : new DialogProvider();
    }

    defaultDialogs(false);

    window.addEventListener("MozDelayedModalDialog",
                            function(e) { alert(e.type + ", " + e.target); },
                            true);

  </script>

  <toolbar id="controls">
    <toolbarbutton label="Back"/>
    <toolbarbutton label="Forward"/>
    <textbox onchange="loadURL(this.value)" flex="1" id="URL"/>
  </toolbar>
  <toolbar>
    <toolbarbutton onclick="restart()" label="Recover"/>
    <toolbarbutton onclick="randomClick()" label="random click"/>
    <toolbarbutton onclick="keyPress()" label="key press"/>
    <toolbarbutton onclick="messageSpeed()" label="test message handling speed"/>
    <toolbarbutton onclick="listenerAddRemove()" label="test listener add/remove"/>
    <toolbarbutton onclick="enableMozAfterPaint()" label="MozAfterPaint"/>
    <toolbarbutton onclick="openWindow()" label="open new window"/>
    <toolbarbutton onclick="closeWindow()" label="close this window"/>
  </toolbar>
  <toolbar><label value="Load a script (URL) to content process:"/>
    <textbox flex="1" id="script"/><button
      label="send" oncommand="document.getElementById('page')
                                      .QueryInterface(Components.interfaces.nsIFrameLoaderOwner)
                                      .frameLoader.messageManager
                                      .loadFrameScript(this.previousSibling.value, false);"/>
  </toolbar>
  <toolbar>
    <label value="Eval script in chrome context"/>
    <textbox flex="1"/><button label="run" oncommand="eval(this.previousSibling.value);"/>
  </toolbar>
  <toolbar>
    <checkbox label="allow dialogs from remote content"
               oncommand="document.getElementById('page').QueryInterface(Components.interfaces.nsIFrameLoaderOwner).frameLoader.delayRemoteDialogs = this.checked;"/>
  </toolbar>

  <browser type="content" src="http://www.google.com/" flex="1" id="page" remote="true"/>
  <label id="messageLog" value="" crop="center"/>
</window>
