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 308826 626f8cc8b7bc7d983cfa378ffd4ad5753c142225
parent 308825 c25ff855651a568e027d7190a07890ba14cee6f5
child 308827 ff7143a7f30fe286f6b353c44b152c4d3c599b17
push id30550
push usercbook@mozilla.com
push dateWed, 10 Aug 2016 13:55:02 +0000
treeherdermozilla-central@c12bb83ad278 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbz
bugs1268962
milestone51.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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__