Bug 1268962 - Add load / error event to prefetch link. r=bz
authorSamael Wang <freesamael@gmail.com>
Tue, 09 Aug 2016 14:28:17 +0800
changeset 398693 626f8cc8b7bc7d983cfa378ffd4ad5753c142225
parent 398692 c25ff855651a568e027d7190a07890ba14cee6f5
child 398694 ff7143a7f30fe286f6b353c44b152c4d3c599b17
push id25600
push userbmo:tchiovoloni@mozilla.com
push dateTue, 09 Aug 2016 16:33:05 +0000
reviewersbz
bugs1268962
milestone51.0a1
Bug 1268962 - Add load / error event to prefetch link. r=bz
dom/base/test/file_bug1268962.sjs
dom/base/test/mochitest.ini
dom/base/test/test_bug1268962.html
dom/base/test/test_link_prefetch.html
testing/web-platform/meta/mixed-content/allowed/http-csp/same-host-https/link-prefetch-tag/top-level/keep-scheme-redirect/allowed.https.html.ini
testing/web-platform/meta/mixed-content/allowed/http-csp/same-host-https/link-prefetch-tag/top-level/no-redirect/allowed.https.html.ini
testing/web-platform/meta/mixed-content/allowed/meta-csp/same-host-https/link-prefetch-tag/top-level/no-redirect/allowed.https.html.ini
testing/web-platform/meta/mixed-content/allowed/no-opt-in/same-host-https/link-prefetch-tag/top-level/keep-scheme-redirect/allowed.https.html.ini
testing/web-platform/meta/mixed-content/allowed/no-opt-in/same-host-https/link-prefetch-tag/top-level/no-redirect/allowed.https.html.ini
testing/web-platform/meta/mixed-content/optionally-blockable/http-csp/cross-origin-http/link-prefetch-tag/top-level/keep-scheme-redirect/opt-in-blocks.https.html.ini
testing/web-platform/meta/mixed-content/optionally-blockable/http-csp/cross-origin-http/link-prefetch-tag/top-level/no-redirect/opt-in-blocks.https.html.ini
testing/web-platform/meta/mixed-content/optionally-blockable/http-csp/cross-origin-http/link-prefetch-tag/top-level/swap-scheme-redirect/opt-in-blocks.https.html.ini
testing/web-platform/meta/mixed-content/optionally-blockable/http-csp/same-host-http/link-prefetch-tag/top-level/keep-scheme-redirect/opt-in-blocks.https.html.ini
testing/web-platform/meta/mixed-content/optionally-blockable/http-csp/same-host-http/link-prefetch-tag/top-level/no-redirect/opt-in-blocks.https.html.ini
testing/web-platform/meta/mixed-content/optionally-blockable/http-csp/same-host-http/link-prefetch-tag/top-level/swap-scheme-redirect/opt-in-blocks.https.html.ini
testing/web-platform/meta/mixed-content/optionally-blockable/meta-csp/cross-origin-http/link-prefetch-tag/top-level/no-redirect/opt-in-blocks.https.html.ini
testing/web-platform/meta/mixed-content/optionally-blockable/meta-csp/same-host-http/link-prefetch-tag/top-level/no-redirect/opt-in-blocks.https.html.ini
testing/web-platform/meta/mixed-content/optionally-blockable/no-opt-in/cross-origin-http/link-prefetch-tag/top-level/keep-scheme-redirect/no-opt-in-allows.https.html.ini
testing/web-platform/meta/mixed-content/optionally-blockable/no-opt-in/cross-origin-http/link-prefetch-tag/top-level/no-redirect/no-opt-in-allows.https.html.ini
testing/web-platform/meta/mixed-content/optionally-blockable/no-opt-in/cross-origin-http/link-prefetch-tag/top-level/swap-scheme-redirect/no-opt-in-allows.https.html.ini
testing/web-platform/meta/mixed-content/optionally-blockable/no-opt-in/same-host-http/link-prefetch-tag/top-level/keep-scheme-redirect/no-opt-in-allows.https.html.ini
testing/web-platform/meta/mixed-content/optionally-blockable/no-opt-in/same-host-http/link-prefetch-tag/top-level/no-redirect/no-opt-in-allows.https.html.ini
testing/web-platform/meta/mixed-content/optionally-blockable/no-opt-in/same-host-http/link-prefetch-tag/top-level/swap-scheme-redirect/no-opt-in-allows.https.html.ini
uriloader/prefetch/nsPrefetchService.cpp
uriloader/prefetch/nsPrefetchService.h
new file mode 100644
--- /dev/null
+++ b/dom/base/test/file_bug1268962.sjs
@@ -0,0 +1,64 @@
+// Test server for bug 1268962
+'use strict';
+Components.utils.importGlobalProperties(["URLSearchParams"]);
+const HTTPStatus = new Map([
+  [100, 'Continue'],
+  [101, 'Switching Protocol'],
+  [200, 'OK'],
+  [201, 'Created'],
+  [202, 'Accepted'],
+  [203, 'Non-Authoritative Information'],
+  [204, 'No Content'],
+  [205, 'Reset Content'],
+  [206, 'Partial Content'],
+  [300, 'Multiple Choice'],
+  [301, 'Moved Permanently'],
+  [302, 'Found'],
+  [303, 'See Other'],
+  [304, 'Not Modified'],
+  [305, 'Use Proxy'],
+  [306, 'unused'],
+  [307, 'Temporary Redirect'],
+  [308, 'Permanent Redirect'],
+  [400, 'Bad Request'],
+  [401, 'Unauthorized'],
+  [402, 'Payment Required'],
+  [403, 'Forbidden'],
+  [404, 'Not Found'],
+  [405, 'Method Not Allowed'],
+  [406, 'Not Acceptable'],
+  [407, 'Proxy Authentication Required'],
+  [408, 'Request Timeout'],
+  [409, 'Conflict'],
+  [410, 'Gone'],
+  [411, 'Length Required'],
+  [412, 'Precondition Failed'],
+  [413, 'Request Entity Too Large'],
+  [414, 'Request-URI Too Long'],
+  [415, 'Unsupported Media Type'],
+  [416, 'Requested Range Not Satisfiable'],
+  [417, 'Expectation Failed'],
+  [500, 'Internal Server Error'],
+  [501, 'Not Implemented'],
+  [502, 'Bad Gateway'],
+  [503, 'Service Unavailable'],
+  [504, 'Gateway Timeout'],
+  [505, 'HTTP Version Not Supported']
+]);
+
+function handleRequest(request, response) {
+  const queryMap = new URLSearchParams(request.queryString);
+  if (queryMap.has('statusCode')) {
+    let statusCode = parseInt(queryMap.get('statusCode'));
+    let statusText = HTTPStatus.get(statusCode);
+    response.setStatusLine('1.1', statusCode, statusText);
+  }
+  if (queryMap.has('cacheControl')) {
+    let cacheControl = queryMap.get('cacheControl');
+    response.setHeader('Cache-Control', cacheControl);
+  }
+  if (queryMap.has('allowOrigin')) {
+    let allowOrigin = queryMap.get('allowOrigin');
+    response.setHeader('Access-Control-Allow-Origin', allowOrigin);
+  }
+}
--- a/dom/base/test/mochitest.ini
+++ b/dom/base/test/mochitest.ini
@@ -221,16 +221,17 @@ support-files =
   script_postmessages_fileList.js
   iframe_postMessages.html
   test_anonymousContent_style_csp.html^headers^
   file_explicit_user_agent.sjs
   referrer_change_server.sjs
   file_change_policy_redirect.html
   file_bug1198095.js
   file_bug1250148.sjs
+  file_bug1268962.sjs
   mozbrowser_api_utils.js
   websocket_helpers.js
   websocket_tests.js
   !/dom/html/test/form_submit_server.sjs
   !/dom/security/test/cors/file_CrossSiteXHR_server.sjs
   !/image/test/mochitest/blue.png
   !/dom/xhr/tests/file_XHRSendData.sjs
   script_bug1238440.js
@@ -622,16 +623,17 @@ skip-if = buildapp == 'b2g'
 [test_bug1163743.html]
 [test_bug1165501.html]
 [test_bug1187157.html]
 [test_bug1198095.html]
 [test_bug1238440.html]
 [test_bug1250148.html]
 [test_bug1259588.html]
 [test_bug1263696.html]
+[test_bug1268962.html]
 [test_bug1274806.html]
 [test_bug1281963.html]
 [test_caretPositionFromPoint.html]
 [test_change_policy.html]
 skip-if = buildapp == 'b2g' #no ssl support
 [test_classList.html]
 [test_clearTimeoutIntervalNoArg.html]
 [test_constructor-assignment.html]
new file mode 100644
--- /dev/null
+++ b/dom/base/test/test_bug1268962.html
@@ -0,0 +1,80 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1268962
+-->
+<head>
+  <title>Test for Bug 1268962</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1268962">Mozilla Bug 1268962</a>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 1268962 **/
+
+function testPrefetchEvent(url, crossorigin, expectLoad) {
+  return new Promise((resolve) => {
+    var link = document.createElement("LINK");
+    link.setAttribute("rel", "prefetch");
+    link.setAttribute("href", url);
+    if (crossorigin) {
+      link.setAttribute("crossorigin", "");
+    }
+
+    link.addEventListener("load", () => {
+      ok(expectLoad, "not expecting load event for " + url);
+      link.remove();
+      resolve();
+    });
+    link.addEventListener("error", () => {
+      ok(!expectLoad, "not expecting error event for " + url);
+      link.remove();
+      resolve();
+    });
+    document.head.appendChild(link);
+  });
+}
+
+const SJS_PATH = window.location.pathname.replace(/[^/]+$/, "file_bug1268962.sjs");
+const SAME_ORIGIN = "http://mochi.test:8888" + SJS_PATH;
+const CROSS_ORIGIN = "http://example.com" + SJS_PATH;
+
+SimpleTest.waitForExplicitFinish();
+
+new Promise(resolve =>
+  SpecialPowers.pushPrefEnv({"set": [["network.prefetch-next.aggressive", true]]}, resolve))
+
+// test same origin
+.then(() => testPrefetchEvent(SAME_ORIGIN + "?statusCode=200&cacheControl=no-cache", false, false))
+.then(() => testPrefetchEvent(SAME_ORIGIN + "?statusCode=404&cacheControl=no-cache", false, false))
+.then(() => testPrefetchEvent(SAME_ORIGIN + "?statusCode=200&cacheControl=max-age%3D120", false, true))
+.then(() => testPrefetchEvent(SAME_ORIGIN + "?statusCode=404&cacheControl=max-age%3D120", false, false))
+
+// test cross origin without CORS
+.then(() => testPrefetchEvent(CROSS_ORIGIN + "?statusCode=200&cacheControl=no-cache", false, true))
+.then(() => testPrefetchEvent(CROSS_ORIGIN + "?statusCode=404&cacheControl=no-cache", false, true))
+.then(() => testPrefetchEvent(CROSS_ORIGIN + "?statusCode=200&cacheControl=max-age%3D120", false, true))
+.then(() => testPrefetchEvent(CROSS_ORIGIN + "?statusCode=404&cacheControl=max-age%3D120", false, true))
+
+// test cross origin with CORS request but no CORS response
+.then(() => testPrefetchEvent(CROSS_ORIGIN + "?statusCode=200&cacheControl=no-cache", true, true))
+.then(() => testPrefetchEvent(CROSS_ORIGIN + "?statusCode=404&cacheControl=no-cache", true, true))
+.then(() => testPrefetchEvent(CROSS_ORIGIN + "?statusCode=200&cacheControl=max-age%3D120", true, true))
+.then(() => testPrefetchEvent(CROSS_ORIGIN + "?statusCode=404&cacheControl=max-age%3D120", true, true))
+
+// test cross origin with CORS request and CORS response
+.then(() => testPrefetchEvent(CROSS_ORIGIN + "?statusCode=200&cacheControl=no-cache&allowOrigin=*", true, false))
+.then(() => testPrefetchEvent(CROSS_ORIGIN + "?statusCode=404&cacheControl=no-cache&allowOrigin=*", true, false))
+.then(() => testPrefetchEvent(CROSS_ORIGIN + "?statusCode=200&cacheControl=max-age%3D120&allowOrigin=*", true, true))
+.then(() => testPrefetchEvent(CROSS_ORIGIN + "?statusCode=404&cacheControl=max-age%3D120&allowOrigin=*", true, false))
+
+.catch((err) => ok(false, "promise rejected: " + err))
+.then(() => SimpleTest.finish());
+
+</script>
+</body>
+</html>
--- a/dom/base/test/test_link_prefetch.html
+++ b/dom/base/test/test_link_prefetch.html
@@ -155,22 +155,11 @@
          RESULT: 'full'},
       ]},
   ];
 
   </script>
   <script type="application/javascript;version=1.7" src="/tests/dom/base/test/referrer_helper.js"></script>
 </head>
 <body onload="tests.next();">
-  <script type="application/javascript;version=1.7">
-    /**
-     * Listen for notifications that pretching finishes.
-     * XXX Bug 1268962 - Fire load/error events on <link rel="prefetch">
-     * Because there's no onload/onerror event fired, we catch prefetch-load-completed
-     * to test
-     * Simply remove this after bug 1268962 is fixed
-     */
-    netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
-    SpecialPowers.Services.obs.addObserver(function() { tests.next(); }, "prefetch-load-completed", false);
-  </script>
   <iframe id="testframe"></iframe>
 </body>
 </html>
--- a/testing/web-platform/meta/mixed-content/allowed/http-csp/same-host-https/link-prefetch-tag/top-level/keep-scheme-redirect/allowed.https.html.ini
+++ b/testing/web-platform/meta/mixed-content/allowed/http-csp/same-host-https/link-prefetch-tag/top-level/keep-scheme-redirect/allowed.https.html.ini
@@ -1,6 +1,5 @@
 [allowed.https.html]
   type: testharness
-  expected: TIMEOUT
   [opt_in_method: http-csp\n                                 origin: same-host-https\n                                 source_scheme: https\n                                 context_nesting: top-level\n                                 redirection: keep-scheme-redirect\n                                 subresource: link-prefetch-tag\n                                 expectation: allowed]
-    expected: NOTRUN
-
+    expected: FAIL
+    bug: the test case uses "no-cache" HTTP header. send an error until we have conclusion at https://github.com/w3c/resource-hints/issues/62
--- a/testing/web-platform/meta/mixed-content/allowed/http-csp/same-host-https/link-prefetch-tag/top-level/no-redirect/allowed.https.html.ini
+++ b/testing/web-platform/meta/mixed-content/allowed/http-csp/same-host-https/link-prefetch-tag/top-level/no-redirect/allowed.https.html.ini
@@ -1,6 +1,5 @@
 [allowed.https.html]
   type: testharness
-  expected: TIMEOUT
   [opt_in_method: http-csp\n                                 origin: same-host-https\n                                 source_scheme: https\n                                 context_nesting: top-level\n                                 redirection: no-redirect\n                                 subresource: link-prefetch-tag\n                                 expectation: allowed]
-    expected: NOTRUN
-
+    expected: FAIL
+    bug: the test case uses "no-cache" HTTP header. send an error until we have conclusion at https://github.com/w3c/resource-hints/issues/62
--- a/testing/web-platform/meta/mixed-content/allowed/meta-csp/same-host-https/link-prefetch-tag/top-level/no-redirect/allowed.https.html.ini
+++ b/testing/web-platform/meta/mixed-content/allowed/meta-csp/same-host-https/link-prefetch-tag/top-level/no-redirect/allowed.https.html.ini
@@ -1,6 +1,5 @@
 [allowed.https.html]
   type: testharness
-  expected: TIMEOUT
   [opt_in_method: meta-csp\n                                 origin: same-host-https\n                                 source_scheme: https\n                                 context_nesting: top-level\n                                 redirection: no-redirect\n                                 subresource: link-prefetch-tag\n                                 expectation: allowed]
-    expected: NOTRUN
-
+    expected: FAIL
+    bug: the test case uses "no-cache" HTTP header. send an error until we have conclusion at https://github.com/w3c/resource-hints/issues/62
--- a/testing/web-platform/meta/mixed-content/allowed/no-opt-in/same-host-https/link-prefetch-tag/top-level/keep-scheme-redirect/allowed.https.html.ini
+++ b/testing/web-platform/meta/mixed-content/allowed/no-opt-in/same-host-https/link-prefetch-tag/top-level/keep-scheme-redirect/allowed.https.html.ini
@@ -1,6 +1,5 @@
 [allowed.https.html]
   type: testharness
-  expected: TIMEOUT
   [opt_in_method: no-opt-in\n                                 origin: same-host-https\n                                 source_scheme: https\n                                 context_nesting: top-level\n                                 redirection: keep-scheme-redirect\n                                 subresource: link-prefetch-tag\n                                 expectation: allowed]
-    expected: NOTRUN
-
+    expected: FAIL
+    bug: the test case uses "no-cache" HTTP header. send an error until we have conclusion at https://github.com/w3c/resource-hints/issues/62
--- a/testing/web-platform/meta/mixed-content/allowed/no-opt-in/same-host-https/link-prefetch-tag/top-level/no-redirect/allowed.https.html.ini
+++ b/testing/web-platform/meta/mixed-content/allowed/no-opt-in/same-host-https/link-prefetch-tag/top-level/no-redirect/allowed.https.html.ini
@@ -1,6 +1,5 @@
 [allowed.https.html]
   type: testharness
-  expected: TIMEOUT
   [opt_in_method: no-opt-in\n                                 origin: same-host-https\n                                 source_scheme: https\n                                 context_nesting: top-level\n                                 redirection: no-redirect\n                                 subresource: link-prefetch-tag\n                                 expectation: allowed]
-    expected: NOTRUN
-
+    expected: FAIL
+    bug: the test case uses "no-cache" HTTP header. send an error until we have conclusion at https://github.com/w3c/resource-hints/issues/62
deleted file mode 100644
--- a/testing/web-platform/meta/mixed-content/optionally-blockable/http-csp/cross-origin-http/link-prefetch-tag/top-level/keep-scheme-redirect/opt-in-blocks.https.html.ini
+++ /dev/null
@@ -1,6 +0,0 @@
-[opt-in-blocks.https.html]
-  type: testharness
-  expected: TIMEOUT
-  [opt_in_method: http-csp\n                                 origin: cross-origin-http\n                                 source_scheme: https\n                                 context_nesting: top-level\n                                 redirection: keep-scheme-redirect\n                                 subresource: link-prefetch-tag\n                                 expectation: blocked]
-    expected: NOTRUN
-
deleted file mode 100644
--- a/testing/web-platform/meta/mixed-content/optionally-blockable/http-csp/cross-origin-http/link-prefetch-tag/top-level/no-redirect/opt-in-blocks.https.html.ini
+++ /dev/null
@@ -1,6 +0,0 @@
-[opt-in-blocks.https.html]
-  type: testharness
-  expected: TIMEOUT
-  [opt_in_method: http-csp\n                                 origin: cross-origin-http\n                                 source_scheme: https\n                                 context_nesting: top-level\n                                 redirection: no-redirect\n                                 subresource: link-prefetch-tag\n                                 expectation: blocked]
-    expected: NOTRUN
-
deleted file mode 100644
--- a/testing/web-platform/meta/mixed-content/optionally-blockable/http-csp/cross-origin-http/link-prefetch-tag/top-level/swap-scheme-redirect/opt-in-blocks.https.html.ini
+++ /dev/null
@@ -1,6 +0,0 @@
-[opt-in-blocks.https.html]
-  type: testharness
-  expected: TIMEOUT
-  [opt_in_method: http-csp\n                                 origin: cross-origin-http\n                                 source_scheme: https\n                                 context_nesting: top-level\n                                 redirection: swap-scheme-redirect\n                                 subresource: link-prefetch-tag\n                                 expectation: blocked]
-    expected: NOTRUN
-
deleted file mode 100644
--- a/testing/web-platform/meta/mixed-content/optionally-blockable/http-csp/same-host-http/link-prefetch-tag/top-level/keep-scheme-redirect/opt-in-blocks.https.html.ini
+++ /dev/null
@@ -1,6 +0,0 @@
-[opt-in-blocks.https.html]
-  type: testharness
-  expected: TIMEOUT
-  [opt_in_method: http-csp\n                                 origin: same-host-http\n                                 source_scheme: https\n                                 context_nesting: top-level\n                                 redirection: keep-scheme-redirect\n                                 subresource: link-prefetch-tag\n                                 expectation: blocked]
-    expected: NOTRUN
-
deleted file mode 100644
--- a/testing/web-platform/meta/mixed-content/optionally-blockable/http-csp/same-host-http/link-prefetch-tag/top-level/no-redirect/opt-in-blocks.https.html.ini
+++ /dev/null
@@ -1,6 +0,0 @@
-[opt-in-blocks.https.html]
-  type: testharness
-  expected: TIMEOUT
-  [opt_in_method: http-csp\n                                 origin: same-host-http\n                                 source_scheme: https\n                                 context_nesting: top-level\n                                 redirection: no-redirect\n                                 subresource: link-prefetch-tag\n                                 expectation: blocked]
-    expected: NOTRUN
-
deleted file mode 100644
--- a/testing/web-platform/meta/mixed-content/optionally-blockable/http-csp/same-host-http/link-prefetch-tag/top-level/swap-scheme-redirect/opt-in-blocks.https.html.ini
+++ /dev/null
@@ -1,6 +0,0 @@
-[opt-in-blocks.https.html]
-  type: testharness
-  expected: TIMEOUT
-  [opt_in_method: http-csp\n                                 origin: same-host-http\n                                 source_scheme: https\n                                 context_nesting: top-level\n                                 redirection: swap-scheme-redirect\n                                 subresource: link-prefetch-tag\n                                 expectation: blocked]
-    expected: NOTRUN
-
deleted file mode 100644
--- a/testing/web-platform/meta/mixed-content/optionally-blockable/meta-csp/cross-origin-http/link-prefetch-tag/top-level/no-redirect/opt-in-blocks.https.html.ini
+++ /dev/null
@@ -1,6 +0,0 @@
-[opt-in-blocks.https.html]
-  type: testharness
-  expected: TIMEOUT
-  [opt_in_method: meta-csp\n                                 origin: cross-origin-http\n                                 source_scheme: https\n                                 context_nesting: top-level\n                                 redirection: no-redirect\n                                 subresource: link-prefetch-tag\n                                 expectation: blocked]
-    expected: NOTRUN
-
deleted file mode 100644
--- a/testing/web-platform/meta/mixed-content/optionally-blockable/meta-csp/same-host-http/link-prefetch-tag/top-level/no-redirect/opt-in-blocks.https.html.ini
+++ /dev/null
@@ -1,6 +0,0 @@
-[opt-in-blocks.https.html]
-  type: testharness
-  expected: TIMEOUT
-  [opt_in_method: meta-csp\n                                 origin: same-host-http\n                                 source_scheme: https\n                                 context_nesting: top-level\n                                 redirection: no-redirect\n                                 subresource: link-prefetch-tag\n                                 expectation: blocked]
-    expected: NOTRUN
-
--- a/testing/web-platform/meta/mixed-content/optionally-blockable/no-opt-in/cross-origin-http/link-prefetch-tag/top-level/keep-scheme-redirect/no-opt-in-allows.https.html.ini
+++ b/testing/web-platform/meta/mixed-content/optionally-blockable/no-opt-in/cross-origin-http/link-prefetch-tag/top-level/keep-scheme-redirect/no-opt-in-allows.https.html.ini
@@ -1,6 +1,5 @@
 [no-opt-in-allows.https.html]
   type: testharness
-  expected: TIMEOUT
   [opt_in_method: no-opt-in\n                                 origin: cross-origin-http\n                                 source_scheme: https\n                                 context_nesting: top-level\n                                 redirection: keep-scheme-redirect\n                                 subresource: link-prefetch-tag\n                                 expectation: allowed]
-    expected: NOTRUN
-
+    expected: FAIL
+    bug: haven't implement prefetch link as an optionally blockable item
--- a/testing/web-platform/meta/mixed-content/optionally-blockable/no-opt-in/cross-origin-http/link-prefetch-tag/top-level/no-redirect/no-opt-in-allows.https.html.ini
+++ b/testing/web-platform/meta/mixed-content/optionally-blockable/no-opt-in/cross-origin-http/link-prefetch-tag/top-level/no-redirect/no-opt-in-allows.https.html.ini
@@ -1,6 +1,5 @@
 [no-opt-in-allows.https.html]
   type: testharness
-  expected: TIMEOUT
   [opt_in_method: no-opt-in\n                                 origin: cross-origin-http\n                                 source_scheme: https\n                                 context_nesting: top-level\n                                 redirection: no-redirect\n                                 subresource: link-prefetch-tag\n                                 expectation: allowed]
-    expected: NOTRUN
-
+    expected: FAIL
+    bug: haven't implement prefetch link as an optionally blockable item
--- a/testing/web-platform/meta/mixed-content/optionally-blockable/no-opt-in/cross-origin-http/link-prefetch-tag/top-level/swap-scheme-redirect/no-opt-in-allows.https.html.ini
+++ b/testing/web-platform/meta/mixed-content/optionally-blockable/no-opt-in/cross-origin-http/link-prefetch-tag/top-level/swap-scheme-redirect/no-opt-in-allows.https.html.ini
@@ -1,6 +1,5 @@
 [no-opt-in-allows.https.html]
   type: testharness
-  expected: TIMEOUT
   [opt_in_method: no-opt-in\n                                 origin: cross-origin-http\n                                 source_scheme: https\n                                 context_nesting: top-level\n                                 redirection: swap-scheme-redirect\n                                 subresource: link-prefetch-tag\n                                 expectation: allowed]
-    expected: NOTRUN
-
+    expected: FAIL
+    bug: haven't implement prefetch link as an optionally blockable item
--- a/testing/web-platform/meta/mixed-content/optionally-blockable/no-opt-in/same-host-http/link-prefetch-tag/top-level/keep-scheme-redirect/no-opt-in-allows.https.html.ini
+++ b/testing/web-platform/meta/mixed-content/optionally-blockable/no-opt-in/same-host-http/link-prefetch-tag/top-level/keep-scheme-redirect/no-opt-in-allows.https.html.ini
@@ -1,6 +1,5 @@
 [no-opt-in-allows.https.html]
   type: testharness
-  expected: TIMEOUT
   [opt_in_method: no-opt-in\n                                 origin: same-host-http\n                                 source_scheme: https\n                                 context_nesting: top-level\n                                 redirection: keep-scheme-redirect\n                                 subresource: link-prefetch-tag\n                                 expectation: allowed]
-    expected: NOTRUN
-
+    expected: FAIL
+    bug: haven't implement prefetch link as an optionally blockable item
--- a/testing/web-platform/meta/mixed-content/optionally-blockable/no-opt-in/same-host-http/link-prefetch-tag/top-level/no-redirect/no-opt-in-allows.https.html.ini
+++ b/testing/web-platform/meta/mixed-content/optionally-blockable/no-opt-in/same-host-http/link-prefetch-tag/top-level/no-redirect/no-opt-in-allows.https.html.ini
@@ -1,6 +1,5 @@
 [no-opt-in-allows.https.html]
   type: testharness
-  expected: TIMEOUT
   [opt_in_method: no-opt-in\n                                 origin: same-host-http\n                                 source_scheme: https\n                                 context_nesting: top-level\n                                 redirection: no-redirect\n                                 subresource: link-prefetch-tag\n                                 expectation: allowed]
-    expected: NOTRUN
-
+    expected: FAIL
+    bug: haven't implement prefetch link as an optionally blockable item
--- a/testing/web-platform/meta/mixed-content/optionally-blockable/no-opt-in/same-host-http/link-prefetch-tag/top-level/swap-scheme-redirect/no-opt-in-allows.https.html.ini
+++ b/testing/web-platform/meta/mixed-content/optionally-blockable/no-opt-in/same-host-http/link-prefetch-tag/top-level/swap-scheme-redirect/no-opt-in-allows.https.html.ini
@@ -1,6 +1,5 @@
 [no-opt-in-allows.https.html]
   type: testharness
-  expected: TIMEOUT
   [opt_in_method: no-opt-in\n                                 origin: same-host-http\n                                 source_scheme: https\n                                 context_nesting: top-level\n                                 redirection: swap-scheme-redirect\n                                 subresource: link-prefetch-tag\n                                 expectation: allowed]
-    expected: NOTRUN
-
+    expected: FAIL
+    bug: haven't implement prefetch link as an optionally blockable item
--- a/uriloader/prefetch/nsPrefetchService.cpp
+++ b/uriloader/prefetch/nsPrefetchService.cpp
@@ -49,16 +49,17 @@ static LazyLogModule gPrefetchLog("nsPre
 #undef LOG
 #define LOG(args) MOZ_LOG(gPrefetchLog, mozilla::LogLevel::Debug, args)
 
 #undef LOG_ENABLED
 #define LOG_ENABLED() MOZ_LOG_TEST(gPrefetchLog, mozilla::LogLevel::Debug)
 
 #define PREFETCH_PREF "network.prefetch-next"
 #define PARALLELISM_PREF "network.prefetch-next.parallelism"
+#define AGGRESSIVE_PREF "network.prefetch-next.aggressive"
 
 //-----------------------------------------------------------------------------
 // helpers
 //-----------------------------------------------------------------------------
 
 static inline uint32_t
 PRTimeToSeconds(PRTime t_usec)
 {
@@ -76,16 +77,17 @@ nsPrefetchNode::nsPrefetchNode(nsPrefetc
                                nsIURI *aURI,
                                nsIURI *aReferrerURI,
                                nsIDOMNode *aSource)
     : mURI(aURI)
     , mReferrerURI(aReferrerURI)
     , mService(aService)
     , mChannel(nullptr)
     , mBytesRead(0)
+    , mShouldFireLoadEvent(false)
 {
     nsCOMPtr<nsIWeakReference> source = do_GetWeakReference(aSource);
     mSources.AppendElement(source);
 }
 
 nsresult
 nsPrefetchNode::OpenChannel()
 {
@@ -182,25 +184,46 @@ NS_IMPL_ISUPPORTS(nsPrefetchNode,
 //-----------------------------------------------------------------------------
 
 NS_IMETHODIMP
 nsPrefetchNode::OnStartRequest(nsIRequest *aRequest,
                                nsISupports *aContext)
 {
     nsresult rv;
 
+    nsCOMPtr<nsIHttpChannel> httpChannel =
+        do_QueryInterface(aRequest, &rv);
+    if (NS_FAILED(rv)) return rv;
+
+    // if the load is cross origin without CORS, or the CORS access is rejected,
+    // always fire load event to avoid leaking site information.
+    nsCOMPtr<nsILoadInfo> loadInfo = mChannel->GetLoadInfo();
+    mShouldFireLoadEvent = loadInfo->GetTainting() == LoadTainting::Opaque ||
+                           (loadInfo->GetTainting() == LoadTainting::CORS &&
+                            (NS_FAILED(mChannel->GetStatus(&rv)) ||
+                             NS_FAILED(rv)));
+
+    // no need to prefetch http error page
+    bool requestSucceeded;
+    if (NS_FAILED(httpChannel->GetRequestSucceeded(&requestSucceeded)) ||
+        !requestSucceeded) {
+      return NS_BINDING_ABORTED;
+    }
+
     nsCOMPtr<nsICacheInfoChannel> cacheInfoChannel =
         do_QueryInterface(aRequest, &rv);
     if (NS_FAILED(rv)) return rv;
  
     // no need to prefetch a document that is already in the cache
     bool fromCache;
     if (NS_SUCCEEDED(cacheInfoChannel->IsFromCache(&fromCache)) &&
         fromCache) {
         LOG(("document is already in the cache; canceling prefetch\n"));
+        // although it's canceled we still want to fire load event
+        mShouldFireLoadEvent = true;
         return NS_BINDING_ABORTED;
     }
 
     //
     // no need to prefetch a document that must be requested fresh each
     // and every time.
     //
     uint32_t expTime;
@@ -240,16 +263,17 @@ nsPrefetchNode::OnStopRequest(nsIRequest
     if (mBytesRead == 0 && aStatus == NS_OK && mChannel) {
         // we didn't need to read (because LOAD_ONLY_IF_MODIFIED was
         // specified), but the object should report loadedSize as if it
         // did.
         mChannel->GetContentLength(&mBytesRead);
     }
 
     mService->NotifyLoadCompleted(this);
+    mService->DispatchEvent(this, mShouldFireLoadEvent || NS_SUCCEEDED(aStatus));
     mService->ProcessNextURI(this);
     return NS_OK;
 }
 
 //-----------------------------------------------------------------------------
 // nsPrefetchNode::nsIInterfaceRequestor
 //-----------------------------------------------------------------------------
 
@@ -331,23 +355,25 @@ nsPrefetchNode::OnRedirectResult(bool pr
 // nsPrefetchService <public>
 //-----------------------------------------------------------------------------
 
 nsPrefetchService::nsPrefetchService()
     : mMaxParallelism(6)
     , mStopCount(0)
     , mHaveProcessed(false)
     , mDisabled(true)
+    , mAggressive(false)
 {
 }
 
 nsPrefetchService::~nsPrefetchService()
 {
     Preferences::RemoveObserver(this, PREFETCH_PREF);
     Preferences::RemoveObserver(this, PARALLELISM_PREF);
+    Preferences::RemoveObserver(this, AGGRESSIVE_PREF);
     // cannot reach destructor if prefetch in progress (listener owns reference
     // to this service)
     EmptyQueue();
 }
 
 nsresult
 nsPrefetchService::Init()
 {
@@ -358,16 +384,19 @@ nsPrefetchService::Init()
     Preferences::AddWeakObserver(this, PREFETCH_PREF);
 
     mMaxParallelism = Preferences::GetInt(PARALLELISM_PREF, mMaxParallelism);
     if (mMaxParallelism < 1) {
         mMaxParallelism = 1;
     }
     Preferences::AddWeakObserver(this, PARALLELISM_PREF);
 
+    mAggressive = Preferences::GetBool(AGGRESSIVE_PREF, false);
+    Preferences::AddWeakObserver(this, AGGRESSIVE_PREF);
+
     // Observe xpcom-shutdown event
     nsCOMPtr<nsIObserverService> observerService =
       mozilla::services::GetObserverService();
     if (!observerService)
       return NS_ERROR_FAILURE;
 
     rv = observerService->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, true);
     NS_ENSURE_SUCCESS(rv, rv);
@@ -403,21 +432,24 @@ nsPrefetchService::ProcessNextURI(nsPref
 
         if (LOG_ENABLED()) {
             nsAutoCString spec;
             node->mURI->GetSpec(spec);
             LOG(("ProcessNextURI [%s]\n", spec.get()));
         }
 
         //
-        // if opening the channel fails, then just skip to the next uri
+        // if opening the channel fails (e.g. security check returns an error),
+        // send an error event and then just skip to the next uri
         //
         rv = node->OpenChannel();
         if (NS_SUCCEEDED(rv)) {
             mCurrentNodes.AppendElement(node);
+        } else {
+          DispatchEvent(node, false);
         }
     }
     while (NS_FAILED(rv));
 }
 
 void
 nsPrefetchService::NotifyLoadRequested(nsPrefetchNode *node)
 {
@@ -437,16 +469,33 @@ nsPrefetchService::NotifyLoadCompleted(n
       mozilla::services::GetObserverService();
     if (!observerService)
       return;
 
     observerService->NotifyObservers(static_cast<nsIStreamListener*>(node),
                                      "prefetch-load-completed", nullptr);
 }
 
+void
+nsPrefetchService::DispatchEvent(nsPrefetchNode *node, bool aSuccess)
+{
+    for (uint32_t i = 0; i < node->mSources.Length(); i++) {
+      nsCOMPtr<nsINode> domNode = do_QueryReferent(node->mSources.ElementAt(i));
+      if (domNode && domNode->IsInComposedDoc()) {
+        nsContentUtils::DispatchTrustedEvent(domNode->OwnerDoc(),
+                                             domNode,
+                                             aSuccess ?
+                                              NS_LITERAL_STRING("load") :
+                                              NS_LITERAL_STRING("error"),
+                                             /* aCanBubble = */ false,
+                                             /* aCancelable = */ false);
+      }
+    }
+}
+
 //-----------------------------------------------------------------------------
 // nsPrefetchService <private>
 //-----------------------------------------------------------------------------
 
 void
 nsPrefetchService::AddProgressListener()
 {
     // Register as an observer for the document loader  
@@ -770,16 +819,21 @@ nsPrefetchService::OnProgressChange(nsIW
 }
 
 NS_IMETHODIMP 
 nsPrefetchService::OnStateChange(nsIWebProgress* aWebProgress, 
                                  nsIRequest *aRequest, 
                                  uint32_t progressStateFlags, 
                                  nsresult aStatus)
 {
+    if (mAggressive) {
+        LOG(("Document load state is ignored in aggressive mode"));
+        return NS_OK;
+    }
+
     if (progressStateFlags & STATE_IS_DOCUMENT) {
         if (progressStateFlags & STATE_STOP)
             StartPrefetching();
         else if (progressStateFlags & STATE_START)
             StopPrefetching();
     }
             
     return NS_OK;
@@ -857,15 +911,22 @@ nsPrefetchService::Observe(nsISupports  
             }
             // If our parallelism has increased, go ahead and kick off enough
             // prefetches to fill up our allowance. If we're now over our
             // allowance, we'll just silently let some of them finish to get
             // back below our limit.
             while (!mQueue.empty() && mCurrentNodes.Length() < static_cast<uint32_t>(mMaxParallelism)) {
                 ProcessNextURI(nullptr);
             }
+        } else if (!strcmp(pref, AGGRESSIVE_PREF)) {
+          mAggressive = Preferences::GetBool(AGGRESSIVE_PREF, false);
+          // in aggressive mode, clear stop count and start prefetching immediately
+          if (mAggressive) {
+            mStopCount = 0;
+            StartPrefetching();
+          }
         }
     }
 
     return NS_OK;
 }
 
 // vim: ts=4 sw=4 expandtab
--- a/uriloader/prefetch/nsPrefetchService.h
+++ b/uriloader/prefetch/nsPrefetchService.h
@@ -40,16 +40,17 @@ public:
 
     nsPrefetchService();
 
     nsresult Init();
     void     ProcessNextURI(nsPrefetchNode *aFinished);
 
     void NotifyLoadRequested(nsPrefetchNode *node);
     void NotifyLoadCompleted(nsPrefetchNode *node);
+    void DispatchEvent(nsPrefetchNode *node, bool aSuccess);
 
 private:
     ~nsPrefetchService();
 
     nsresult Prefetch(nsIURI *aURI,
                       nsIURI *aReferrerURI,
                       nsIDOMNode *aSource,
                       bool aExplicit);
@@ -65,16 +66,22 @@ private:
 
     std::deque<RefPtr<nsPrefetchNode>> mQueue;
     nsTArray<RefPtr<nsPrefetchNode>>   mCurrentNodes;
     int32_t                            mMaxParallelism;
     int32_t                            mStopCount;
     // true if pending document loads have ever reached zero.
     int32_t                            mHaveProcessed;
     bool                               mDisabled;
+
+    // In usual case prefetch does not start until all normal loads are done.
+    // Aggressive mode ignores normal loads and just start prefetch ASAP.
+    // It's mainly for testing purpose and discoraged for normal use;
+    // see https://bugzilla.mozilla.org/show_bug.cgi?id=1281415 for details.
+    bool                               mAggressive;
 };
 
 //-----------------------------------------------------------------------------
 // nsPrefetchNode
 //-----------------------------------------------------------------------------
 
 class nsPrefetchNode final : public nsIStreamListener
                            , public nsIInterfaceRequestor
@@ -103,11 +110,12 @@ public:
 
 private:
     ~nsPrefetchNode() {}
 
     RefPtr<nsPrefetchService>   mService;
     nsCOMPtr<nsIChannel>        mChannel;
     nsCOMPtr<nsIChannel>        mRedirectChannel;
     int64_t                     mBytesRead;
+    bool                        mShouldFireLoadEvent;
 };
 
 #endif // !nsPrefetchService_h__