Bug 1448500: Add speculative request content policy type. r=bz, r=kmag draft
authorDave Townsend <dtownsend@oxymoronical.com>
Fri, 23 Mar 2018 15:27:08 -0700
changeset 776185 d9460fdac118bc68f0db79749a16f181b580f2e7
parent 776018 c44f60c43432d468639b5fe078420e60c13fd3de
push id104824
push userdtownsend@mozilla.com
push dateMon, 02 Apr 2018 19:12:04 +0000
reviewersbz, kmag
bugs1448500
milestone61.0a1
Bug 1448500: Add speculative request content policy type. r=bz, r=kmag Adds a new TYPE_SPECULATIVE to nsIContentPolicy uses it as the type for speculative connection channels from the IO service. I believe I've added it to all the content policies in tree to make sure it behaves the same as TYPE_OTHER used to. The webextension test shows that the webextension proxy API sees speculative lookups requested through the IO service. MozReview-Commit-ID: DQ4Kq0xdUOD
dom/base/nsContentPolicyUtils.h
dom/base/nsIContentPolicy.idl
dom/cache/DBSchema.cpp
dom/chrome-webidl/ChannelWrapper.webidl
dom/fetch/InternalRequest.cpp
dom/security/nsCSPUtils.cpp
dom/security/nsContentSecurityManager.cpp
dom/security/nsMixedContentBlocker.cpp
extensions/cookie/nsPermissionManager.cpp
extensions/permissions/nsContentBlocker.cpp
netwerk/base/nsIOService.cpp
toolkit/components/extensions/test/xpcshell/test_ext_proxy_speculative.js
toolkit/components/extensions/test/xpcshell/xpcshell-common.ini
toolkit/components/extensions/webrequest/ChannelWrapper.cpp
--- a/dom/base/nsContentPolicyUtils.h
+++ b/dom/base/nsContentPolicyUtils.h
@@ -109,17 +109,16 @@ NS_CP_ContentTypeName(uint32_t contentTy
     CASE_RETURN( TYPE_MEDIA                       );
     CASE_RETURN( TYPE_WEBSOCKET                   );
     CASE_RETURN( TYPE_CSP_REPORT                  );
     CASE_RETURN( TYPE_XSLT                        );
     CASE_RETURN( TYPE_BEACON                      );
     CASE_RETURN( TYPE_FETCH                       );
     CASE_RETURN( TYPE_IMAGESET                    );
     CASE_RETURN( TYPE_WEB_MANIFEST                );
-    CASE_RETURN( TYPE_SAVEAS_DOWNLOAD             );
     CASE_RETURN( TYPE_INTERNAL_SCRIPT             );
     CASE_RETURN( TYPE_INTERNAL_WORKER             );
     CASE_RETURN( TYPE_INTERNAL_SHARED_WORKER      );
     CASE_RETURN( TYPE_INTERNAL_EMBED              );
     CASE_RETURN( TYPE_INTERNAL_OBJECT             );
     CASE_RETURN( TYPE_INTERNAL_FRAME              );
     CASE_RETURN( TYPE_INTERNAL_IFRAME             );
     CASE_RETURN( TYPE_INTERNAL_AUDIO              );
@@ -130,16 +129,18 @@ NS_CP_ContentTypeName(uint32_t contentTy
     CASE_RETURN( TYPE_INTERNAL_SERVICE_WORKER     );
     CASE_RETURN( TYPE_INTERNAL_SCRIPT_PRELOAD     );
     CASE_RETURN( TYPE_INTERNAL_IMAGE              );
     CASE_RETURN( TYPE_INTERNAL_IMAGE_PRELOAD      );
     CASE_RETURN( TYPE_INTERNAL_IMAGE_FAVICON      );
     CASE_RETURN( TYPE_INTERNAL_STYLESHEET         );
     CASE_RETURN( TYPE_INTERNAL_STYLESHEET_PRELOAD );
     CASE_RETURN( TYPE_INTERNAL_WORKER_IMPORT_SCRIPTS );
+    CASE_RETURN( TYPE_SAVEAS_DOWNLOAD             );
+    CASE_RETURN( TYPE_SPECULATIVE                 );
    default:
     return "<Unknown Type>";
   }
 }
 
 #undef CASE_RETURN
 
 /* Passes on parameters from its "caller"'s context. */
--- a/dom/base/nsIContentPolicy.idl
+++ b/dom/base/nsIContentPolicy.idl
@@ -176,21 +176,16 @@ interface nsIContentPolicy : nsISupports
   const nsContentPolicyType TYPE_IMAGESET = 21;
 
   /**
    * Indicates a web manifest.
    */
   const nsContentPolicyType TYPE_WEB_MANIFEST = 22;
 
   /**
-   * Indicates an save-as link download from the front-end code.
-   */
-  const nsContentPolicyType TYPE_SAVEAS_DOWNLOAD = 43;
-
-  /**
    * Indicates an internal constant for scripts loaded through script
    * elements.
    *
    * This will be mapped to TYPE_SCRIPT before being passed to content policy
    * implementations.
    */
   const nsContentPolicyType TYPE_INTERNAL_SCRIPT = 23;
 
@@ -336,21 +331,32 @@ interface nsIContentPolicy : nsISupports
   /**
    * Indicates an importScripts() inside a worker script.
    *
    * This will be mapped to TYPE_SCRIPT before being passed to content policy
    * implementations.
    */
   const nsContentPolicyType TYPE_INTERNAL_WORKER_IMPORT_SCRIPTS = 42;
 
+  /**
+   * Indicates an save-as link download from the front-end code.
+   */
+  const nsContentPolicyType TYPE_SAVEAS_DOWNLOAD = 43;
+
+  /**
+   * Indicates a speculative connection.
+   */
+  const nsContentPolicyType TYPE_SPECULATIVE = 44;
+
   /* When adding new content types, please update nsContentBlocker,
-   * NS_CP_ContentTypeName, nsCSPContext, all nsIContentPolicy
-   * implementations, the static_assert in dom/cache/DBSchema.cpp,
-   * ChannelWrapper.webidl, ChannelWrapper.cpp, and other things that
-   * are not listed here that are related to nsIContentPolicy. */
+   * NS_CP_ContentTypeName, nsCSPContext, CSP_ContentTypeToDirective,
+   * DoContentSecurityChecks, all nsIContentPolicy implementations, the
+   * static_assert in dom/cache/DBSchema.cpp, ChannelWrapper.webidl,
+   * ChannelWrapper.cpp, nsPermissionManager.cpp, and other things that are not
+   * listed here that are related to nsIContentPolicy. */
 
   //////////////////////////////////////////////////////////////////////
 
   /**
    * Returned from shouldLoad or shouldProcess if the load or process request
    * is rejected based on details of the request.
    */
   const short REJECT_REQUEST = -1;
--- a/dom/cache/DBSchema.cpp
+++ b/dom/cache/DBSchema.cpp
@@ -306,17 +306,16 @@ static_assert(nsIContentPolicy::TYPE_INV
               nsIContentPolicy::TYPE_MEDIA == 15 &&
               nsIContentPolicy::TYPE_WEBSOCKET == 16 &&
               nsIContentPolicy::TYPE_CSP_REPORT == 17 &&
               nsIContentPolicy::TYPE_XSLT == 18 &&
               nsIContentPolicy::TYPE_BEACON == 19 &&
               nsIContentPolicy::TYPE_FETCH == 20 &&
               nsIContentPolicy::TYPE_IMAGESET == 21 &&
               nsIContentPolicy::TYPE_WEB_MANIFEST == 22 &&
-              nsIContentPolicy::TYPE_SAVEAS_DOWNLOAD == 43 &&
               nsIContentPolicy::TYPE_INTERNAL_SCRIPT == 23 &&
               nsIContentPolicy::TYPE_INTERNAL_WORKER == 24 &&
               nsIContentPolicy::TYPE_INTERNAL_SHARED_WORKER == 25 &&
               nsIContentPolicy::TYPE_INTERNAL_EMBED == 26 &&
               nsIContentPolicy::TYPE_INTERNAL_OBJECT == 27 &&
               nsIContentPolicy::TYPE_INTERNAL_FRAME == 28 &&
               nsIContentPolicy::TYPE_INTERNAL_IFRAME == 29 &&
               nsIContentPolicy::TYPE_INTERNAL_AUDIO == 30 &&
@@ -326,17 +325,19 @@ static_assert(nsIContentPolicy::TYPE_INV
               nsIContentPolicy::TYPE_INTERNAL_EVENTSOURCE == 34 &&
               nsIContentPolicy::TYPE_INTERNAL_SERVICE_WORKER == 35 &&
               nsIContentPolicy::TYPE_INTERNAL_SCRIPT_PRELOAD == 36 &&
               nsIContentPolicy::TYPE_INTERNAL_IMAGE == 37 &&
               nsIContentPolicy::TYPE_INTERNAL_IMAGE_PRELOAD == 38 &&
               nsIContentPolicy::TYPE_INTERNAL_STYLESHEET == 39 &&
               nsIContentPolicy::TYPE_INTERNAL_STYLESHEET_PRELOAD == 40 &&
               nsIContentPolicy::TYPE_INTERNAL_IMAGE_FAVICON == 41 &&
-              nsIContentPolicy::TYPE_INTERNAL_WORKER_IMPORT_SCRIPTS == 42,
+              nsIContentPolicy::TYPE_INTERNAL_WORKER_IMPORT_SCRIPTS == 42 &&
+              nsIContentPolicy::TYPE_SAVEAS_DOWNLOAD == 43 &&
+              nsIContentPolicy::TYPE_SPECULATIVE == 44,
               "nsContentPolicyType values are as expected");
 
 namespace {
 
 typedef int32_t EntryId;
 
 struct IdCount
 {
--- a/dom/chrome-webidl/ChannelWrapper.webidl
+++ b/dom/chrome-webidl/ChannelWrapper.webidl
@@ -28,16 +28,17 @@ enum MozContentPolicyType {
   "beacon",
   "xml_dtd",
   "font",
   "media",
   "websocket",
   "csp_report",
   "imageset",
   "web_manifest",
+  "speculative",
   "other"
 };
 
 /**
  * A thin wrapper around nsIChannel and nsIHttpChannel that allows JS
  * callers to access them without XPConnect overhead.
  */
 [ChromeOnly, Exposed=System]
--- a/dom/fetch/InternalRequest.cpp
+++ b/dom/fetch/InternalRequest.cpp
@@ -328,16 +328,19 @@ InternalRequest::MapContentPolicyTypeToR
     context = RequestContext::Imageset;
     break;
   case nsIContentPolicy::TYPE_WEB_MANIFEST:
     context = RequestContext::Manifest;
     break;
   case nsIContentPolicy::TYPE_SAVEAS_DOWNLOAD:
     context = RequestContext::Internal;
     break;
+  case nsIContentPolicy::TYPE_SPECULATIVE:
+    context = RequestContext::Internal;
+    break;
   default:
     MOZ_ASSERT(false, "Unhandled nsContentPolicyType value");
     break;
   }
   return context;
 }
 
 // static
--- a/dom/security/nsCSPUtils.cpp
+++ b/dom/security/nsCSPUtils.cpp
@@ -248,16 +248,17 @@ CSP_ContentTypeToDirective(nsContentPoli
 
     case nsIContentPolicy::TYPE_OBJECT:
     case nsIContentPolicy::TYPE_OBJECT_SUBREQUEST:
       return nsIContentSecurityPolicy::OBJECT_SRC_DIRECTIVE;
 
     case nsIContentPolicy::TYPE_XBL:
     case nsIContentPolicy::TYPE_DTD:
     case nsIContentPolicy::TYPE_OTHER:
+    case nsIContentPolicy::TYPE_SPECULATIVE:
       return nsIContentSecurityPolicy::DEFAULT_SRC_DIRECTIVE;
 
     // csp shold not block top level loads, e.g. in case
     // of a redirect.
     case nsIContentPolicy::TYPE_DOCUMENT:
     // CSP can not block csp reports
     case nsIContentPolicy::TYPE_CSP_REPORT:
       return nsIContentSecurityPolicy::NO_DIRECTIVE;
--- a/dom/security/nsContentSecurityManager.cpp
+++ b/dom/security/nsContentSecurityManager.cpp
@@ -511,16 +511,21 @@ DoContentSecurityChecks(nsIChannel* aCha
       break;
     }
 
     case nsIContentPolicy::TYPE_SAVEAS_DOWNLOAD: {
       mimeTypeGuess = EmptyCString();
       break;
     }
 
+    case nsIContentPolicy::TYPE_SPECULATIVE: {
+      mimeTypeGuess = EmptyCString();
+      break;
+    }
+
     default:
       // nsIContentPolicy::TYPE_INVALID
       MOZ_ASSERT(false, "can not perform security check without a valid contentType");
   }
 
   int16_t shouldLoad = nsIContentPolicy::ACCEPT;
   rv = NS_CheckContentLoadPolicy(uri,
                                  aLoadInfo,
--- a/dom/security/nsMixedContentBlocker.cpp
+++ b/dom/security/nsMixedContentBlocker.cpp
@@ -560,16 +560,17 @@ nsMixedContentBlocker::ShouldLoad(bool a
     case TYPE_STYLESHEET:
     case TYPE_SUBDOCUMENT:
     case TYPE_PING:
     case TYPE_WEB_MANIFEST:
     case TYPE_XBL:
     case TYPE_XMLHTTPREQUEST:
     case TYPE_XSLT:
     case TYPE_OTHER:
+    case TYPE_SPECULATIVE:
       break;
 
 
     // This content policy works as a whitelist.
     default:
       MOZ_ASSERT(false, "Mixed content of unknown type");
   }
 
--- a/extensions/cookie/nsPermissionManager.cpp
+++ b/extensions/cookie/nsPermissionManager.cpp
@@ -123,16 +123,17 @@ static const char* kPreloadPermissions[]
   "media",
   "websocket",
   "csp_report",
   "xslt",
   "beacon",
   "fetch",
   "image",
   "manifest",
+  "speculative",
 
   // This permission is preloaded to support properly blocking service worker
   // interception when a user has disabled storage for a specific site.  Once
   // service worker interception moves to the parent process this should be
   // removed.  See bug 1428130.
   "cookie"
 };
 
--- a/extensions/permissions/nsContentBlocker.cpp
+++ b/extensions/permissions/nsContentBlocker.cpp
@@ -61,16 +61,18 @@ static const char *kTypeString[] = {
                                     "", // TYPE_INTERNAL_SERVICE_WORKER
                                     "", // TYPE_INTERNAL_SCRIPT_PRELOAD
                                     "", // TYPE_INTERNAL_IMAGE
                                     "", // TYPE_INTERNAL_IMAGE_PRELOAD
                                     "", // TYPE_INTERNAL_STYLESHEET
                                     "", // TYPE_INTERNAL_STYLESHEET_PRELOAD
                                     "", // TYPE_INTERNAL_IMAGE_FAVICON
                                     "", // TYPE_INTERNAL_WORKERS_IMPORT_SCRIPTS
+                                    "saveas_download",
+                                    "speculative",
 };
 
 #define NUMBER_OF_TYPES MOZ_ARRAY_LENGTH(kTypeString)
 uint8_t nsContentBlocker::mBehaviorPref[NUMBER_OF_TYPES];
 
 NS_IMPL_ISUPPORTS(nsContentBlocker,
                   nsIContentPolicy,
                   nsIObserver,
--- a/netwerk/base/nsIOService.cpp
+++ b/netwerk/base/nsIOService.cpp
@@ -1881,17 +1881,17 @@ nsIOService::SpeculativeConnectInternal(
     // channel we create underneath - hence it's safe to use
     // the systemPrincipal as the loadingPrincipal for this channel.
     nsCOMPtr<nsIChannel> channel;
     rv = NewChannelFromURI2(aURI,
                             nullptr, // aLoadingNode,
                             loadingPrincipal,
                             nullptr, //aTriggeringPrincipal,
                             nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
-                            nsIContentPolicy::TYPE_OTHER,
+                            nsIContentPolicy::TYPE_SPECULATIVE,
                             getter_AddRefs(channel));
     NS_ENSURE_SUCCESS(rv, rv);
 
     if (aAnonymous) {
         nsLoadFlags loadFlags = 0;
         channel->GetLoadFlags(&loadFlags);
         loadFlags |= nsIRequest::LOAD_ANONYMOUS;
         channel->SetLoadFlags(loadFlags);
new file mode 100644
--- /dev/null
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_proxy_speculative.js
@@ -0,0 +1,38 @@
+"use strict";
+
+ChromeUtils.import("resource://gre/modules/ExtensionUtils.jsm");
+
+const proxy = createHttpServer();
+
+add_task(async function test_speculative_connect() {
+  function background() {
+    // Handle the proxy request.
+    browser.proxy.onRequest.addListener(details => {
+      browser.test.log(`onRequest ${JSON.stringify(details)}`);
+      browser.test.assertEq(details.type, "speculative", "Should have seen a speculative proxy request.");
+      return [{type: "direct"}];
+    }, {urls: ["<all_urls>"]}, ["requestHeaders"]);
+  }
+
+  let handlingExt = ExtensionTestUtils.loadExtension({
+    manifest: {
+      permissions: [
+        "proxy",
+        "<all_urls>",
+      ],
+    },
+    background: `(${background})()`,
+  });
+
+  Services.prefs.setBoolPref("network.http.debug-observations", true);
+
+  await handlingExt.startup();
+
+  let notificationPromise = ExtensionUtils.promiseObserved("speculative-connect-request");
+
+  let uri = Services.io.newURI(`http://${proxy.identity.primaryHost}:${proxy.identity.primaryPort}`);
+  Services.io.speculativeConnect2(uri, Services.scriptSecurityManager.getSystemPrincipal(), null);
+  await notificationPromise;
+
+  await handlingExt.unload();
+});
--- a/toolkit/components/extensions/test/xpcshell/xpcshell-common.ini
+++ b/toolkit/components/extensions/test/xpcshell/xpcshell-common.ini
@@ -62,16 +62,17 @@ skip-if = (os == "win" && !debug) #Bug 1
 skip-if = true # This test no longer tests what it is meant to test.
 [test_ext_permission_xhr.js]
 [test_ext_privacy.js]
 [test_ext_privacy_disable.js]
 [test_ext_privacy_update.js]
 [test_ext_proxy_auth.js]
 [test_ext_proxy_onauthrequired.js]
 [test_ext_proxy_socks.js]
+[test_ext_proxy_speculative.js]
 [test_ext_redirects.js]
 [test_ext_runtime_connect_no_receiver.js]
 [test_ext_runtime_getBrowserInfo.js]
 [test_ext_runtime_getPlatformInfo.js]
 [test_ext_runtime_id.js]
 [test_ext_runtime_onInstalled_and_onStartup.js]
 skip-if = true # bug 1315829
 [test_ext_runtime_sendMessage.js]
--- a/toolkit/components/extensions/webrequest/ChannelWrapper.cpp
+++ b/toolkit/components/extensions/webrequest/ChannelWrapper.cpp
@@ -717,16 +717,18 @@ GetContentPolicyType(uint32_t aType)
   case nsIContentPolicy::TYPE_WEBSOCKET:
     return MozContentPolicyType::Websocket;
   case nsIContentPolicy::TYPE_CSP_REPORT:
     return MozContentPolicyType::Csp_report;
   case nsIContentPolicy::TYPE_IMAGESET:
     return MozContentPolicyType::Imageset;
   case nsIContentPolicy::TYPE_WEB_MANIFEST:
     return MozContentPolicyType::Web_manifest;
+  case nsIContentPolicy::TYPE_SPECULATIVE:
+    return MozContentPolicyType::Speculative;
   default:
     return MozContentPolicyType::Other;
   }
 }
 
 MozContentPolicyType
 ChannelWrapper::Type() const
 {