Bug 512566. Load font data url's synchronously, netwerk/style changes. r=bz
authorJohn Daggett <jdaggett@mozilla.com>
Fri, 16 Mar 2012 12:31:01 +0900
changeset 92877 2c023d5f75846ba126c4872af5c203f81c35d1c2
parent 92876 f06bc45e46ae007004f424fa9094c238a0a19dfd
child 92878 d0caad830e14d049d9cef4abadd0500dbe03595a
push id886
push userlsblakk@mozilla.com
push dateMon, 04 Jun 2012 19:57:52 +0000
treeherdermozilla-beta@bbd8d5efd6d1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbz
bugs512566
milestone14.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 512566. Load font data url's synchronously, netwerk/style changes. r=bz
layout/style/nsFontFaceLoader.cpp
layout/style/nsFontFaceLoader.h
netwerk/base/public/nsIProtocolHandler.idl
netwerk/protocol/data/nsDataHandler.cpp
--- a/layout/style/nsFontFaceLoader.cpp
+++ b/layout/style/nsFontFaceLoader.cpp
@@ -268,17 +268,17 @@ nsFontFaceLoader::Cancel()
 }
 
 nsresult
 nsFontFaceLoader::CheckLoadAllowed(nsIPrincipal* aSourcePrincipal,
                                    nsIURI* aTargetURI,
                                    nsISupports* aContext)
 {
   nsresult rv;
-  
+
   if (!aSourcePrincipal)
     return NS_OK;
 
   // check with the security manager
   nsIScriptSecurityManager *secMan = nsContentUtils::GetSecurityManager();
   rv = secMan->CheckLoadURIWithPrincipal(aSourcePrincipal, aTargetURI,
                                         nsIScriptSecurityManager::STANDARD);
   if (NS_FAILED(rv)) {
@@ -298,17 +298,17 @@ nsFontFaceLoader::CheckLoadAllowed(nsIPr
                                  nsContentUtils::GetSecurityManager());
 
   if (NS_FAILED(rv) || NS_CP_REJECTED(shouldLoad)) {
     return NS_ERROR_CONTENT_BLOCKED;
   }
 
   return NS_OK;
 }
-  
+
 nsUserFontSet::nsUserFontSet(nsPresContext *aContext)
   : mPresContext(aContext)
 {
   NS_ASSERTION(mPresContext, "null context passed to nsUserFontSet");
   mLoaders.Init();
 }
 
 nsUserFontSet::~nsUserFontSet()
@@ -331,50 +331,29 @@ nsUserFontSet::Destroy()
 }
 
 void
 nsUserFontSet::RemoveLoader(nsFontFaceLoader *aLoader)
 {
   mLoaders.RemoveEntry(aLoader);
 }
 
-nsresult 
+nsresult
 nsUserFontSet::StartLoad(gfxProxyFontEntry *aProxy,
                          const gfxFontFaceSrc *aFontFaceSrc)
 {
   nsresult rv;
-  
-  // check same-site origin
+  nsCOMPtr<nsIPrincipal> principal;
+
+  rv = CheckFontLoad(aProxy, aFontFaceSrc, principal);
+  NS_ENSURE_SUCCESS(rv, rv);
+
   nsIPresShell *ps = mPresContext->PresShell();
   if (!ps)
     return NS_ERROR_FAILURE;
-    
-  NS_ASSERTION(aFontFaceSrc && !aFontFaceSrc->mIsLocal, 
-               "bad font face url passed to fontloader");
-  NS_ASSERTION(aFontFaceSrc->mURI, "null font uri");
-  if (!aFontFaceSrc->mURI)
-    return NS_ERROR_FAILURE;
-
-  // use document principal, original principal if flag set
-  // this enables user stylesheets to load font files via
-  // @font-face rules
-  nsCOMPtr<nsIPrincipal> principal = ps->GetDocument()->NodePrincipal();
-
-  NS_ASSERTION(aFontFaceSrc->mOriginPrincipal, 
-               "null origin principal in @font-face rule");
-  if (aFontFaceSrc->mUseOriginPrincipal) {
-    principal = do_QueryInterface(aFontFaceSrc->mOriginPrincipal);
-  }
-  
-  rv = nsFontFaceLoader::CheckLoadAllowed(principal, aFontFaceSrc->mURI, 
-                                          ps->GetDocument());
-  if (NS_FAILED(rv)) {
-    LogMessage(aProxy, "download not allowed", nsIScriptError::errorFlag, rv);
-    return rv;
-  }
 
   nsCOMPtr<nsIStreamLoader> streamLoader;
   nsCOMPtr<nsILoadGroup> loadGroup(ps->GetDocument()->GetDocumentLoadGroup());
 
   nsCOMPtr<nsIChannel> channel;
   // get Content Security Policy from principal to pass into channel
   nsCOMPtr<nsIChannelPolicy> channelPolicy;
   nsCOMPtr<nsIContentSecurityPolicy> csp;
@@ -403,37 +382,37 @@ nsUserFontSet::StartLoad(gfxProxyFontEnt
 
 #ifdef PR_LOGGING
   if (LOG_ENABLED()) {
     nsCAutoString fontURI, referrerURI;
     aFontFaceSrc->mURI->GetSpec(fontURI);
     if (aFontFaceSrc->mReferrer)
       aFontFaceSrc->mReferrer->GetSpec(referrerURI);
     LOG(("fontdownloader (%p) download start - font uri: (%s) "
-         "referrer uri: (%s)\n", 
+         "referrer uri: (%s)\n",
          fontLoader.get(), fontURI.get(), referrerURI.get()));
   }
-#endif  
+#endif
 
   nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
   if (httpChannel)
     httpChannel->SetReferrer(aFontFaceSrc->mReferrer);
   rv = NS_NewStreamLoader(getter_AddRefs(streamLoader), fontLoader);
   NS_ENSURE_SUCCESS(rv, rv);
-  
+
   bool inherits = false;
   rv = NS_URIChainHasFlags(aFontFaceSrc->mURI,
                            nsIProtocolHandler::URI_INHERITS_SECURITY_CONTEXT,
                            &inherits);
   if (NS_SUCCEEDED(rv) && inherits) {
     // allow data, javascript, etc URI's
     rv = channel->AsyncOpen(streamLoader, nsnull);
   } else {
     nsCOMPtr<nsIStreamListener> listener =
-      new nsCORSListenerProxy(streamLoader, principal, channel, 
+      new nsCORSListenerProxy(streamLoader, principal, channel,
                               false, &rv);
     if (NS_FAILED(rv)) {
       fontLoader->DropChannel();  // explicitly need to break ref cycle
     }
     NS_ENSURE_TRUE(listener, NS_ERROR_OUT_OF_MEMORY);
     NS_ENSURE_SUCCESS(rv, rv);
 
     rv = channel->AsyncOpen(listener, nsnull);
@@ -826,8 +805,127 @@ nsUserFontSet::LogMessage(gfxProxyFontEn
                                      "CSS Loader", // category (make separate?)
                                      innerWindowID);
   if (NS_SUCCEEDED(rv)) {
     console->LogMessage(scriptError);
   }
 
   return NS_OK;
 }
+
+nsresult
+nsUserFontSet::CheckFontLoad(gfxProxyFontEntry *aFontToLoad,
+                             const gfxFontFaceSrc *aFontFaceSrc,
+                             nsCOMPtr<nsIPrincipal>& aPrincipal)
+{
+  nsresult rv;
+
+  // check same-site origin
+  nsIPresShell *ps = mPresContext->PresShell();
+  if (!ps)
+    return NS_ERROR_FAILURE;
+
+  NS_ASSERTION(aFontFaceSrc && !aFontFaceSrc->mIsLocal,
+               "bad font face url passed to fontloader");
+  NS_ASSERTION(aFontFaceSrc->mURI, "null font uri");
+  if (!aFontFaceSrc->mURI)
+    return NS_ERROR_FAILURE;
+
+  // use document principal, original principal if flag set
+  // this enables user stylesheets to load font files via
+  // @font-face rules
+  aPrincipal = ps->GetDocument()->NodePrincipal();
+
+  NS_ASSERTION(aFontFaceSrc->mOriginPrincipal,
+               "null origin principal in @font-face rule");
+  if (aFontFaceSrc->mUseOriginPrincipal) {
+    aPrincipal = do_QueryInterface(aFontFaceSrc->mOriginPrincipal);
+  }
+
+  rv = nsFontFaceLoader::CheckLoadAllowed(aPrincipal, aFontFaceSrc->mURI,
+                                          ps->GetDocument());
+  if (NS_FAILED(rv)) {
+    LogMessage(aFontToLoad, "download not allowed", nsIScriptError::errorFlag, rv);
+  }
+
+  return rv;
+}
+
+nsresult
+nsUserFontSet::SyncLoadFontData(gfxProxyFontEntry *aFontToLoad,
+                                const gfxFontFaceSrc *aFontFaceSrc,
+                                PRUint8* &aBuffer,
+                                PRUint32 &aBufferLength)
+{
+  nsresult rv;
+  nsCOMPtr<nsIPrincipal> principal;
+
+  rv = CheckFontLoad(aFontToLoad, aFontFaceSrc, principal);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr<nsIChannel> channel;
+  // get Content Security Policy from principal to pass into channel
+  nsCOMPtr<nsIChannelPolicy> channelPolicy;
+  nsCOMPtr<nsIContentSecurityPolicy> csp;
+  rv = principal->GetCsp(getter_AddRefs(csp));
+  NS_ENSURE_SUCCESS(rv, rv);
+  if (csp) {
+    channelPolicy = do_CreateInstance("@mozilla.org/nschannelpolicy;1");
+    channelPolicy->SetContentSecurityPolicy(csp);
+    channelPolicy->SetLoadType(nsIContentPolicy::TYPE_FONT);
+  }
+  rv = NS_NewChannel(getter_AddRefs(channel),
+                     aFontFaceSrc->mURI,
+                     nsnull,
+                     nsnull,
+                     nsnull,
+                     nsIRequest::LOAD_NORMAL,
+                     channelPolicy);
+
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // blocking stream is OK for data URIs
+  nsCOMPtr<nsIInputStream> stream;
+  rv = channel->Open(getter_AddRefs(stream));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = stream->Available(&aBufferLength);
+  NS_ENSURE_SUCCESS(rv, rv);
+  if (aBufferLength == 0) {
+    return NS_ERROR_FAILURE;
+  }
+
+  // read all the decoded data
+  aBuffer = static_cast<PRUint8*> (NS_Alloc(sizeof(PRUint8) * aBufferLength));
+  if (!aBuffer) {
+    aBufferLength = 0;
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+
+  PRUint32 numRead, totalRead = 0;
+  while (NS_SUCCEEDED(rv =
+           stream->Read(reinterpret_cast<char*>(aBuffer + totalRead),
+                        aBufferLength - totalRead, &numRead)) &&
+         numRead != 0)
+  {
+    totalRead += numRead;
+    if (totalRead > aBufferLength) {
+      rv = NS_ERROR_FAILURE;
+      break;
+    }
+  }
+
+  // make sure there's a mime type
+  if (NS_SUCCEEDED(rv)) {
+    nsCAutoString mimeType;
+    rv = channel->GetContentType(mimeType);
+    aBufferLength = totalRead;
+  }
+
+  if (NS_FAILED(rv)) {
+    NS_Free(aBuffer);
+    aBuffer = nsnull;
+    aBufferLength = 0;
+    return rv;
+  }
+
+  return NS_OK;
+}
--- a/layout/style/nsFontFaceLoader.h
+++ b/layout/style/nsFontFaceLoader.h
@@ -67,17 +67,17 @@ public:
   nsUserFontSet(nsPresContext *aContext);
   ~nsUserFontSet();
 
   // Called when this font set is no longer associated with a presentation.
   void Destroy();
 
   // starts loading process, creating and initializing a nsFontFaceLoader obj
   // returns whether load process successfully started or not
-  nsresult StartLoad(gfxProxyFontEntry *aFontToLoad, 
+  nsresult StartLoad(gfxProxyFontEntry *aFontToLoad,
                      const gfxFontFaceSrc *aFontFaceSrc);
 
   // Called by nsFontFaceLoader when the loader has completed normally.
   // It's removed from the mLoaders set.
   void RemoveLoader(nsFontFaceLoader *aLoader);
 
   bool UpdateRules(const nsTArray<nsFontFaceRuleContainer>& aRules);
 
@@ -102,16 +102,25 @@ protected:
                   nsTArray<FontFaceRuleRecord>& oldRules,
                   bool& aFontSetModified);
 
   virtual nsresult LogMessage(gfxProxyFontEntry *aProxy,
                               const char *aMessage,
                               PRUint32 aFlags = nsIScriptError::errorFlag,
                               nsresult aStatus = 0);
 
+  nsresult CheckFontLoad(gfxProxyFontEntry *aFontToLoad,
+                         const gfxFontFaceSrc *aFontFaceSrc,
+                         nsCOMPtr<nsIPrincipal>& aPrincipal);
+
+  virtual nsresult SyncLoadFontData(gfxProxyFontEntry *aFontToLoad,
+                                    const gfxFontFaceSrc *aFontFaceSrc,
+                                    PRUint8* &aBuffer,
+                                    PRUint32 &aBufferLength);
+
   nsPresContext *mPresContext;  // weak reference
 
   // Set of all loaders pointing to us. These are not strong pointers,
   // but that's OK because nsFontFaceLoader always calls RemoveLoader on
   // us before it dies (unless we die first).
   nsTHashtable< nsPtrHashKey<nsFontFaceLoader> > mLoaders;
 
   nsTArray<FontFaceRuleRecord>   mRules;
--- a/netwerk/base/public/nsIProtocolHandler.idl
+++ b/netwerk/base/public/nsIProtocolHandler.idl
@@ -237,16 +237,22 @@ interface nsIProtocolHandler : nsISuppor
     /**
      * URIs for this protocol execute script when they are opened.
      */
     const unsigned long URI_OPENING_EXECUTES_SCRIPT = (1<<13);
 
     // Note that 1 << 14 is used above
 
     /**
+     * Channels for this protocol don't need to spin the event loop to handle
+     * Open() and reads on the resulting stream.
+     */
+    const unsigned long URI_SYNC_LOAD_DOESNT_SPIN_EVENT_LOOP = (1<<15);
+
+    /**
      * This protocol handler can be proxied via a proxy (socks or http)
      * (e.g., irc, smtp, http, etc.).  If the protocol supports transparent
      * proxying, the handler should implement nsIProxiedProtocolHandler.
      *
      * If it supports only HTTP proxying, then it need not support
      * nsIProxiedProtocolHandler, but should instead set the ALLOWS_PROXY_HTTP
      * flag (see below).
      *
--- a/netwerk/protocol/data/nsDataHandler.cpp
+++ b/netwerk/protocol/data/nsDataHandler.cpp
@@ -86,17 +86,18 @@ nsDataHandler::GetDefaultPort(PRInt32 *r
     // no ports for data protocol
     *result = -1;
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDataHandler::GetProtocolFlags(PRUint32 *result) {
     *result = URI_NORELATIVE | URI_NOAUTH | URI_INHERITS_SECURITY_CONTEXT |
-        URI_LOADABLE_BY_ANYONE | URI_NON_PERSISTABLE | URI_IS_LOCAL_RESOURCE;
+        URI_LOADABLE_BY_ANYONE | URI_NON_PERSISTABLE | URI_IS_LOCAL_RESOURCE |
+        URI_SYNC_LOAD_DOESNT_SPIN_EVENT_LOOP;
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDataHandler::NewURI(const nsACString &aSpec,
                       const char *aCharset, // ignore charset info
                       nsIURI *aBaseURI,
                       nsIURI **result) {