Bug 994318 - Implement caching for ShouldLoad to speed things up. r=ckerschb,grobinson
authorSid Stamm <sstamm@mozilla.com>
Tue, 20 May 2014 10:43:17 -0700
changeset 183995 a4655f5da435f4f7188517763e4be4040795f3d2
parent 183913 8da004bc9a6d478fc7e7490861ffd55ee4a594b9
child 183996 f4964171a9c08b310baffc670cb0355cb3cbcfb8
push id26810
push usercbook@mozilla.com
push dateWed, 21 May 2014 11:46:36 +0000
treeherdermozilla-central@50fb8c4db2fd [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersckerschb, grobinson
bugs994318
milestone32.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 994318 - Implement caching for ShouldLoad to speed things up. r=ckerschb,grobinson
content/base/src/nsCSPContext.cpp
content/base/src/nsCSPContext.h
--- a/content/base/src/nsCSPContext.cpp
+++ b/content/base/src/nsCSPContext.cpp
@@ -40,16 +40,57 @@ GetCspContextLog()
   if (!gCspContextPRLog)
     gCspContextPRLog = PR_NewLogModule("CSPContext");
   return gCspContextPRLog;
 }
 #endif
 
 #define CSPCONTEXTLOG(args) PR_LOG(GetCspContextLog(), 4, args)
 
+static const uint32_t CSP_CACHE_URI_CUTOFF_SIZE = 512;
+
+/**
+ * Creates a key for use in the ShouldLoad cache.
+ * Looks like: <uri>!<nsIContentPolicy::LOAD_TYPE>
+ */
+nsresult
+CreateCacheKey_Internal(nsIURI* aContentLocation,
+                        nsContentPolicyType aContentType,
+                        nsACString& outCacheKey)
+{
+  if (!aContentLocation) {
+    return NS_ERROR_FAILURE;
+  }
+
+  bool isDataScheme = false;
+  nsresult rv = aContentLocation->SchemeIs("data", &isDataScheme);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  outCacheKey.Truncate();
+  if (aContentType != nsIContentPolicy::TYPE_SCRIPT && isDataScheme) {
+    // For non-script data: URI, use ("data:", aContentType) as the cache key.
+    outCacheKey.Append(NS_LITERAL_CSTRING("data:"));
+    outCacheKey.AppendInt(aContentType);
+    return NS_OK;
+  }
+
+  nsAutoCString spec;
+  rv = aContentLocation->GetSpec(spec);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // Don't cache for a URI longer than the cutoff size.
+  if (spec.Length() <= CSP_CACHE_URI_CUTOFF_SIZE) {
+    outCacheKey.Append(spec);
+    outCacheKey.Append(NS_LITERAL_CSTRING("!"));
+    outCacheKey.AppendInt(aContentType);
+  }
+
+  return NS_OK;
+}
+
 /* =====  nsIContentSecurityPolicy impl ====== */
 
 NS_IMETHODIMP
 nsCSPContext::ShouldLoad(nsContentPolicyType aContentType,
                          nsIURI*             aContentLocation,
                          nsIURI*             aRequestOrigin,
                          nsISupports*        aRequestContext,
                          const nsACString&   aMimeTypeGuess,
@@ -69,16 +110,26 @@ nsCSPContext::ShouldLoad(nsContentPolicy
   // This ShouldLoad function is called from nsCSPService::ShouldLoad,
   // which already checked a number of things, including:
   // * aContentLocation is not null; we can consume this without further checks
   // * scheme is not a whitelisted scheme (about: chrome:, etc).
   // * CSP is enabled
   // * Content Type is not whitelisted (CSP Reports, TYPE_DOCUMENT, etc).
   // * Fast Path for Apps
 
+  nsAutoCString cacheKey;
+  rv = CreateCacheKey_Internal(aContentLocation, aContentType, cacheKey);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  bool isCached = mShouldLoadCache.Get(cacheKey, outDecision);
+  if (isCached && cacheKey.Length() > 0) {
+    // this is cached, use the cached value.
+    return NS_OK;
+  }
+
   // Default decision, CSP can revise it if there's a policy to enforce
   *outDecision = nsIContentPolicy::ACCEPT;
 
   // This may be a load or a preload. If it is a preload, the document will
   // not have been fully parsed yet, and aRequestContext will be an
   // nsIDOMHTMLDocument rather than the nsIDOMHTMLElement associated with the
   // resource. As a result, we cannot extract the element's corresponding
   // nonce attribute, and so we cannot correctly check the nonce on a preload.
@@ -132,16 +183,21 @@ nsCSPContext::ShouldLoad(nsContentPolicy
                                          violatedDirective.get());
       }
 
       // TODO: future patches fix:
       // * AsyncReportViolation, bug 994322
       // * Console error reporting, bug 994322
     }
   }
+  // Done looping, cache any relevant result
+  if (cacheKey.Length() > 0 && !isPreload) {
+    mShouldLoadCache.Put(cacheKey, *outDecision);
+  }
+
 #ifdef PR_LOGGING
   {
   nsAutoCString spec;
   aContentLocation->GetSpec(spec);
   CSPCONTEXTLOG(("nsCSPContext::ShouldLoad, decision: %s, aContentLocation: %s", *outDecision ? "load" : "deny", spec.get()));
   }
 #endif
   return NS_OK;
@@ -178,16 +234,17 @@ nsCSPContext::nsCSPContext()
 }
 
 nsCSPContext::~nsCSPContext()
 {
   CSPCONTEXTLOG(("nsCSPContext::~nsCSPContext"));
   for (uint32_t i = 0; i < mPolicies.Length(); i++) {
     delete mPolicies[i];
   }
+  mShouldLoadCache.Clear();
 }
 
 NS_IMETHODIMP
 nsCSPContext::GetIsInitialized(bool *outIsInitialized)
 {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
@@ -210,16 +267,18 @@ nsCSPContext::GetPolicyCount(uint32_t *o
 
 NS_IMETHODIMP
 nsCSPContext::RemovePolicy(uint32_t aIndex)
 {
   if (aIndex >= mPolicies.Length()) {
     return NS_ERROR_ILLEGAL_VALUE;
   }
   mPolicies.RemoveElementAt(aIndex);
+  // reset cache since effective policy changes
+  mShouldLoadCache.Clear();
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsCSPContext::AppendPolicy(const nsAString& aPolicyString,
                            nsIURI* aSelfURI,
                            bool aReportOnly,
                            bool aSpecCompliant)
@@ -232,16 +291,18 @@ nsCSPContext::AppendPolicy(const nsAStri
     NS_WARNING("aSelfURI should be a nullptr in AppendPolicy and removed in bug 991474");
   }
 
   // Use the mSelfURI from setRequestContext, see bug 991474
   NS_ASSERTION(mSelfURI, "mSelfURI required for AppendPolicy, but not set");
   nsCSPPolicy* policy = nsCSPParser::parseContentSecurityPolicy(aPolicyString, mSelfURI, aReportOnly, 0);
   if (policy) {
     mPolicies.AppendElement(policy);
+    // reset cache since effective policy changes
+    mShouldLoadCache.Clear();
   }
   return NS_OK;
 }
 
 // aNonceOrContent either holds the nonce-value or otherwise the content
 // of the element to be hashed.
 NS_IMETHODIMP
 nsCSPContext::getAllowsInternal(nsContentPolicyType aContentType,
--- a/content/base/src/nsCSPContext.h
+++ b/content/base/src/nsCSPContext.h
@@ -2,16 +2,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef nsCSPContext_h___
 #define nsCSPContext_h___
 
 #include "nsCSPUtils.h"
+#include "nsDataHashtable.h"
 #include "nsIChannel.h"
 #include "nsIClassInfo.h"
 #include "nsIContentSecurityPolicy.h"
 #include "nsISerializable.h"
 #include "nsXPCOM.h"
 
 #define NS_CSPCONTEXT_CONTRACTID "@mozilla.org/cspcontext;1"
  // 09d9ed1a-e5d4-4004-bfe0-27ceb923d9ac
@@ -31,13 +32,14 @@ class nsCSPContext : public nsIContentSe
 
   private:
     NS_IMETHODIMP getAllowsInternal(nsContentPolicyType aContentType,
                                     enum CSPKeyword aKeyword,
                                     const nsAString& aNonceOrContent,
                                     bool* outShouldReportViolations,
                                     bool* outIsAllowed) const;
 
-    nsTArray<nsCSPPolicy*> mPolicies;
-    nsCOMPtr<nsIURI>       mSelfURI;
+    nsTArray<nsCSPPolicy*>                     mPolicies;
+    nsCOMPtr<nsIURI>                           mSelfURI;
+    nsDataHashtable<nsCStringHashKey, int16_t> mShouldLoadCache;
 };
 
 #endif /* nsCSPContext_h___ */