<!DOCTYPE HTML>
<html>
<head>
  <meta charset="utf-8">
  <title>Tests for bugs 886164 and 671389</title>
  <script src="/tests/SimpleTest/SimpleTest.js"></script>
  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<p id="display"></p>
<div id="content">
</div>

<script class="testbody" type="text/javascript">

var testCases = [
  {
    // Test 1: don't load image from non-same-origin; allow loading
    // images from same-same origin
    sandboxAttribute: "allow-same-origin",
    csp: "default-src 'self'",
    file: "file_sandbox_1.html",
    results: { img1a_good: -1, img1_bad: -1 }
    // fails if scripts execute
  },
  {
    // Test 2: don't load image from non-same-origin; allow loading
    // images from same-same origin, even without allow-same-origin
    // flag
    sandboxAttribute: "",
    csp: "default-src 'self'",
    file: "file_sandbox_2.html",
    results: { img2_bad: -1, img2a_good: -1 }
    // fails if scripts execute
  },
  {
    // Test 3: disallow loading images from any host, even with
    // allow-same-origin flag set
    sandboxAttribute: "allow-same-origin",
    csp: "default-src 'none'",
    file: "file_sandbox_3.html",
    results: { img3_bad: -1, img3a_bad: -1 },
    // fails if scripts execute
  },
  {
    // Test 4: disallow loading images from any host
    sandboxAttribute: "",
    csp: "default-src 'none'",
    file: "file_sandbox_4.html",
    results: { img4_bad: -1, img4a_bad: -1 }
    // fails if scripts execute
  },
  {
    // Test 5: disallow loading images or scripts, allow inline scripts
    sandboxAttribute: "allow-scripts",
    csp: "default-src 'none'; script-src 'unsafe-inline';",
    file: "file_sandbox_5.html",
    results: { img5_bad: -1, img5a_bad: -1, script5_bad: -1, script5a_bad: -1 },
    nrOKmessages: 2 // sends 2 ok message
    // fails if scripts execute
  },
  {
    // Test 6: disallow non-same-origin images, allow inline and same origin scripts
    sandboxAttribute: "allow-same-origin allow-scripts",
    csp: "default-src 'self' 'unsafe-inline';",
    file: "file_sandbox_6.html",
    results: { img6_bad: -1, script6_bad: -1 },
    nrOKmessages: 4 // sends 4 ok message
    // fails if forms are not disallowed
  },
  {
    // Test 7: same as Test 1
    csp: "default-src 'self'; sandbox allow-same-origin",
    file: "file_sandbox_7.html",
    results: { img7a_good: -1, img7_bad: -1 }
  },
  {
    // Test 8: same as Test 2
    csp: "sandbox allow-same-origin; default-src 'self'",
    file: "file_sandbox_8.html",
    results: { img8_bad: -1, img8a_good: -1 }
  },
  {
    // Test 9: same as Test 3
    csp: "default-src 'none'; sandbox allow-same-origin",
    file: "file_sandbox_9.html",
    results: { img9_bad: -1, img9a_bad: -1 }
  },
  {
    // Test 10: same as Test 4
    csp: "default-src 'none'; sandbox allow-same-origin",
    file: "file_sandbox_10.html",
    results: { img10_bad: -1, img10a_bad: -1 }
  },
  {
    // Test 11: same as Test 5
    csp: "default-src 'none'; script-src 'unsafe-inline'; sandbox allow-scripts allow-same-origin",
    file: "file_sandbox_11.html",
    results: { img11_bad: -1, img11a_bad: -1, script11_bad: -1, script11a_bad: -1 },
    nrOKmessages: 2 // sends 2 ok message
  },
  {
    // Test 12: same as Test 6
    csp: "sandbox allow-same-origin allow-scripts; default-src 'self' 'unsafe-inline';",
    file: "file_sandbox_12.html",
    results: { img12_bad: -1, script12_bad: -1 },
    nrOKmessages: 4 // sends 4 ok message
  },
  {
    // Test 13: same as Test 5 and Test 11, but:
    // * using sandbox flag 'allow-scripts' in CSP and not as iframe attribute
    // * not using allow-same-origin in CSP (so a new NullPrincipal is created).
    csp: "default-src 'none'; script-src 'unsafe-inline'; sandbox allow-scripts",
    file: "file_sandbox_13.html",
    results: { img13_bad: -1, img13a_bad: -1, script13_bad: -1, script13a_bad: -1 },
    nrOKmessages: 2 // sends 2 ok message
  },
];

// a postMessage handler that is used by sandboxed iframes without
// 'allow-same-origin' to communicate pass/fail back to this main page.
// it expects to be called with an object like:
//  { ok: true/false,
//    desc: <description of the test> which it then forwards to ok() }
window.addEventListener("message", receiveMessage);

function receiveMessage(event) {
  ok_wrapper(event.data.ok, event.data.desc);
}

var completedTests = 0;
var passedTests = 0;

var totalTests = (function() {
    var nrCSPloadTests = 0;
    for(var i = 0; i < testCases.length; i++) {
      nrCSPloadTests += Object.keys(testCases[i].results).length;
      if (testCases[i].nrOKmessages) {
        // + number of expected postMessages from iframe
        nrCSPloadTests += testCases[i].nrOKmessages;
      }
    }
    return nrCSPloadTests;
})();

function ok_wrapper(result, desc) {
  ok(result, desc);

  completedTests++;

  if (result) {
    passedTests++;
  }

  if (completedTests === totalTests) {
    window.examiner.remove();
    SimpleTest.finish();
  }
}

// Set the iframe src and sandbox attribute
function runTest(test) {
  var iframe = document.createElement('iframe');

  document.getElementById('content').appendChild(iframe);

  // set sandbox attribute
  if (test.sandboxAttribute !== undefined) {
    iframe.sandbox = test.sandboxAttribute;
  }

  // set query string
  var src = 'file_testserver.sjs';
  // path where the files are
  var path = '/tests/dom/security/test/csp/';

  src += '?file=' + escape(path+test.file);

  if (test.csp !== undefined) {
    src += '&csp=' + escape(test.csp);
  }

  iframe.src = src;
  iframe.width = iframe.height = 10;
}

// Examiner related

// This is used to watch the blocked data bounce off CSP and allowed data
// get sent out to the wire.
function examiner() {
  SpecialPowers.addObserver(this, "csp-on-violate-policy");
  SpecialPowers.addObserver(this, "specialpowers-http-notify-request");
}

examiner.prototype  = {
  observe(subject, topic, data) {
    var testpat = new RegExp("testid=([a-z0-9_]+)");

    //_good things better be allowed!
    //_bad things better be stopped!

    if (topic === "specialpowers-http-notify-request") {
      //these things were allowed by CSP
      var uri = data;
      if (!testpat.test(uri)) return;
      var testid = testpat.exec(uri)[1];

      if(/_good/.test(testid)) {
        ok_wrapper(true, uri + " is allowed by csp");
      } else {
        ok_wrapper(false, uri + " should not be allowed by csp");
      }
    }

    if(topic === "csp-on-violate-policy") {
      //these were blocked... record that they were blocked
      var asciiSpec = SpecialPowers.getPrivilegedProps(SpecialPowers.do_QueryInterface(subject, "nsIURI"), "asciiSpec");
      if (!testpat.test(asciiSpec)) return;
      var testid = testpat.exec(asciiSpec)[1];
      if(/_bad/.test(testid)) {
        ok_wrapper(true, asciiSpec + " was blocked by \"" + data + "\"");
      } else {
        ok_wrapper(false, asciiSpec + " should have been blocked by \"" + data + "\"");
      }
    }
  },

  // must eventually call this to remove the listener,
  // or mochitests might get borked.
  remove() {
    SpecialPowers.removeObserver(this, "csp-on-violate-policy");
    SpecialPowers.removeObserver(this, "specialpowers-http-notify-request");
  }
}

window.examiner = new examiner();

SimpleTest.waitForExplicitFinish();

(function() { // Run tests:
  for(var i = 0; i < testCases.length; i++) {
    runTest(testCases[i]);
  }
})();

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