new file mode 100644
--- /dev/null
+++ b/docshell/test/file_signed_web_package.sjs
@@ -0,0 +1,69 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+/*global Components */
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+
+function handleRequest(request, response)
+{
+ var origin = `${request.scheme}://${request.host}:${request.port}`;
+ var pkgId = `${request.scheme}${request.host}${request.port}`;
+ response.setHeader("Content-Type", "application/package", false);
+ response.write(octetStreamData.packageHeader +
+ octetStreamData.getData(origin, pkgId));
+ return;
+}
+
+// The package content getData formats it as described at
+// http://www.w3.org/TR/web-packaging/#streamable-package-format
+var octetStreamData = {
+ packageHeader: 'manifest-signature: 11111111111111\r\n',
+
+ buildContent: function(origin, pkgId) {
+ return [
+ {
+ headers: ["Content-Location: manifest.webapp",
+ "Content-Type: application/x-web-app-manifest+json"],
+ data: `{ "package-identifier": "{${pkgId}}",` +
+ ` "moz-package-origin": "${origin}"}`,
+ type: "application/x-web-app-manifest+json"
+ },
+ {
+ headers: ["Content-Location: /index.html",
+ "Content-Type: text/html"],
+ data: "<!DOCTYPE html><html><body>OK</body></html>",
+ type: "text/html"
+ },
+ {
+ headers: ["Content-Location: /scripts/app.js",
+ "Content-Type: text/javascript"],
+ data: "module Math from '/scripts/helpers/math.js';\r\n...\r\n",
+ type: "text/javascript"
+ },
+ {
+ headers: ["Content-Location: /scripts/helpers/math.js",
+ "Content-Type: text/javascript"],
+ data: "export function sum(nums) { ... }\r\n...\r\n",
+ type: "text/javascript"
+ }
+ ];
+ },
+
+ token: "gc0pJq0M:08jU534c0p",
+ getData: function(origin, pkgId) {
+ var content = this.buildContent(origin, pkgId);
+ var str = "";
+ for (var i in content) {
+ str += "--" + this.token + "\r\n";
+ for (var j in content[i].headers) {
+ str += content[i].headers[j] + "\r\n";
+ }
+ str += "\r\n";
+ str += content[i].data + "\r\n";
+ }
+
+ str += "--" + this.token + "--";
+ return str;
+ }
+};
--- a/docshell/test/mochitest.ini
+++ b/docshell/test/mochitest.ini
@@ -29,16 +29,17 @@ support-files =
file_bug660404^headers^
file_bug662170.html
file_bug669671.sjs
file_bug680257.html
file_bug703855.html
file_bug728939.html
file_pushState_after_document_open.html
historyframes.html
+ file_signed_web_package.sjs
[test_anchor_scroll_after_document_open.html]
[test_bfcache_plus_hash.html]
skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage
[test_bug123696.html]
[test_bug369814.html]
skip-if = buildapp == 'mulet' || (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage
[test_bug384014.html]
@@ -103,8 +104,9 @@ skip-if = (buildapp == 'b2g' && toolkit
[test_framedhistoryframes.html]
skip-if = (buildapp == 'b2g' && toolkit != 'gonk') || toolkit == 'android' #Bug 931116, b2g desktop specific, initial triage, and also bug 784321
support-files = file_framedhistoryframes.html
[test_pushState_after_document_open.html]
[test_windowedhistoryframes.html]
skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage
[test_bug1121701.html]
skip-if = (buildapp == 'b2g' || buildapp == 'mulet')
+[test_bfcache_cross_process.html]
new file mode 100644
--- /dev/null
+++ b/docshell/test/test_bfcache_cross_process.html
@@ -0,0 +1,156 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Test cross process BFCache</title>
+ <script type="text/javascript" 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" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="application/javascript;version=1.7">
+
+SimpleTest.waitForExplicitFinish();
+
+SpecialPowers.pushPrefEnv(
+ { "set": [["network.http.enable-packaged-apps", true],
+ ["network.http.signed-packages.enabled", true],
+ ["network.http.signed-packages.trusted-origin", "http://test1.mochi.test:8888,http://mochi.test:8888"],
+ ["network.http.packaged-apps-developer-mode", true],
+ ["dom.ipc.tabs.disabled", false],
+ ["dom.ipc.processCount", 10],
+ ["dom.mozBrowserFramesEnabled", true]] },
+ () => SpecialPowers.pushPermissions([
+ { "type": "browser", "allow": 1, "context": document }
+ ], function() {
+ runTest();
+ }));
+
+function runInContent(iframe, script) {
+ var mm = SpecialPowers.wrap(iframe)
+ .QueryInterface(SpecialPowers.Ci.nsIFrameLoaderOwner)
+ .frameLoader
+ .messageManager;
+ mm.loadFrameScript("data:," + encodeURIComponent(script) + "",
+ false);
+};
+
+function childID(iframe) {
+ var fl = SpecialPowers.wrap(iframe)
+ .QueryInterface(SpecialPowers.Ci.nsIFrameLoaderOwner)
+ .frameLoader;
+ return fl.childID;
+}
+
+var iframeHistory = [];
+
+function checkID() {
+ var idSet = new Set();
+ iframeHistory.forEach(entry => {
+ idSet.add(entry.childID);
+ });
+ is(idSet.size, 3, "has 3 different processes");
+}
+
+// Create a mozbrowser iframe that has three session history entries.
+// Each of the entries is loaded in different process.
+function prepareIframe() {
+ return new Promise(resolve => {
+ var iframe = document.createElement("iframe");
+ iframe.setAttribute('mozbrowser', 'true');
+ iframe.setAttribute('remote', 'true');
+ iframe.setAttribute("src", "http://example.org:80");
+
+ var src;
+ iframe.addEventListener("mozbrowserloadend", function loadend(e) {
+ iframe.removeEventListener("mozbrowserloadend", loadend);
+ ok(true, "Got first mozbrowserloadend");
+ iframeHistory.push({"url": iframe.src, "childID": childID(iframe)});
+ src = "http://mochi.test:8888/tests/docshell/test/file_signed_web_package.sjs!//index.html";
+ //iframe.setAttribute("src", );
+ runInContent(iframe, 'content.location="'+src+'"');
+ iframe.addEventListener("mozbrowserloadend", function loadend(e) {
+ iframe.removeEventListener("mozbrowserloadend", loadend);
+ ok(true, "Got second mozbrowserloadend");
+ iframeHistory.push({"url": src, "childID": childID(iframe)});
+ src = "http://test1.mochi.test:8888/tests/docshell/test/file_signed_web_package.sjs!//index.html";
+ runInContent(iframe, 'content.location="'+src+'"');
+ //iframe.setAttribute("src", src);
+ iframe.addEventListener("mozbrowserloadend", function loadend(e) {
+ iframe.removeEventListener("mozbrowserloadend", loadend);
+ ok(true, "Got third mozbrowserloadend");
+ iframeHistory.push({"url": src, "childID": childID(iframe)});
+ checkID();
+ resolve(iframe);
+ });
+ });
+ });
+
+ document.body.appendChild(iframe);
+ });
+}
+
+function checkLocationChange(iframe, entry) {
+ return new Promise(resolve => {
+ iframe.addEventListener("mozbrowserlocationchange", function cb(e) {
+ iframe.removeEventListener("mozbrowserlocationchange", cb);
+ is(e.detail, entry.url, "location changed to " + entry.url);
+ is(childID(iframe), entry.childID,
+ "has correct childID " + childID(iframe));
+ resolve(iframe);
+ });
+ });
+}
+
+function checkGoBack(iframe, entry) {
+ return new Promise(resolve => {
+ iframe.getCanGoBack().then(result => {
+ ok(result, "Can go back to earlier page");
+ checkLocationChange(iframe, entry).then(resolve);
+ return iframe.goBack();
+ });
+ });
+}
+
+function checkGoForward(iframe, entry) {
+ return new Promise(resolve => {
+ iframe.getCanGoForward().then(result => {
+ ok(result, "Can go forward to later page");
+ checkLocationChange(iframe, entry).then(resolve);
+ return iframe.goForward();
+ });
+ });
+}
+
+function checkGo(iframe, offset, entry) {
+ return new Promise(resolve => {
+ checkLocationChange(iframe, entry).then(resolve);
+ runInContent(iframe, "content.history.go("+offset+")");
+ });
+}
+
+function runTest() {
+ prepareIframe().then(iframe => {
+ return checkGoBack(iframe, iframeHistory[1]);
+ }).then(iframe => {
+ return checkGoForward(iframe, iframeHistory[2]);
+ }).then(iframe => {
+ return checkGo(iframe, -1, iframeHistory[1]);
+ }).then(iframe => {
+ return checkGo(iframe, 1, iframeHistory[2]);
+ }).then(iframe => {
+ SimpleTest.finish();
+ return checkGo(iframe, -2, iframeHistory[0]);
+ }).then(iframe => {
+ return checkGo(iframe, 2, iframeHistory[2]);
+ }).then(SimpleTest.finish);
+}
+
+</script>
+</pre>
+</body>
+</html>