Bug 457809 and bug 457810 - Speculatively load images and stylesheets (respectively). r=mrbkap, sr=bzbarsky
authorbjarne@runitsoft.com
Mon, 30 Mar 2009 11:31:16 -0400
changeset 26719 c428086801d01ac8d8320c6d70f707f83fa8d30c
parent 26718 67ddbe030ab62e88ea40be141a58f490ce4835c1
child 26720 9ac9e1f9336008aa37cc58a261fdfec8cf901e83
push id1
push userroot
push dateTue, 26 Apr 2011 22:38:44 +0000
treeherdermozilla-beta@bfdb6e623a36 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmrbkap, bzbarsky
bugs457809, 457810
milestone1.9.2a1pre
Bug 457809 and bug 457810 - Speculatively load images and stylesheets (respectively). r=mrbkap, sr=bzbarsky
content/base/public/nsIDocument.h
content/base/src/nsDocument.cpp
parser/htmlparser/src/nsParser.cpp
--- a/content/base/public/nsIDocument.h
+++ b/content/base/public/nsIDocument.h
@@ -97,18 +97,18 @@ class nsBindingManager;
 class nsIDOMNodeList;
 class mozAutoSubtreeModified;
 struct JSObject;
 class nsFrameLoader;
 class nsIBoxObject;
 
 // IID for the nsIDocument interface
 #define NS_IDOCUMENT_IID      \
-{ 0x62579239, 0xb619, 0x4bf2, \
-  { 0x8d, 0x39, 0x0b, 0x73, 0xe8, 0x66, 0x3a, 0x85 } }
+{ 0x46003091, 0x7f99, 0x420f, \
+ { 0x95, 0xbc, 0x28, 0xd7, 0xd5, 0x01, 0x5a, 0x41 } }
 
 // Flag for AddStyleSheet().
 #define NS_STYLESHEET_FROM_CATALOG                (1 << 0)
 
 //----------------------------------------------------------------------
 
 // Document interface.  This is implemented by all document objects in
 // Gecko.
@@ -1133,16 +1133,23 @@ public:
   /**
    * Unsuppress event handling.
    * @param aFireEvents If PR_TRUE, delayed events (focus/blur) will be fired
    *                    asynchronously.
    */
   virtual void UnsuppressEventHandlingAndFireEvents(PRBool aFireEvents) = 0;
 
   PRUint32 EventHandlingSuppressed() { return mEventsSuppressed; }
+  
+  /**
+   * Called by nsParser to preload images. Can be removed and code moved
+   * to nsPreloadURIs::PreloadURIs() in file nsParser.cpp whenever the
+   * parser-module is linked with gklayout-module.
+   */
+  virtual void PreLoadImage(nsIURI* uri);
 protected:
   ~nsIDocument()
   {
     // XXX The cleanup of mNodeInfoManager (calling DropDocumentReference and
     //     releasing it) happens in the nsDocument destructor. We'd prefer to
     //     do it here but nsNodeInfoManager is a concrete class that we don't
     //     want to expose to users of the nsIDocument API outside of Gecko.
   }
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -163,16 +163,17 @@ static NS_DEFINE_CID(kDOMEventGroupCID, 
 #include "nsIContentViewer.h"
 #include "nsIXMLContentSink.h"
 #include "nsContentErrors.h"
 #include "nsIXULDocument.h"
 #include "nsIPrompt.h"
 #include "nsIPropertyBag2.h"
 
 #include "nsFrameLoader.h"
+#include "imgIRequest.h"
 
 #include "mozAutoDocUpdate.h"
 
 #ifdef MOZ_SMIL
 #include "nsSMILAnimationController.h"
 #include "imgIContainer.h"
 #include "nsSVGUtils.h"
 #endif // MOZ_SMIL
@@ -7545,8 +7546,20 @@ nsDocument::UnsuppressEventHandlingAndFi
 
   if (aFireEvents) {
     NS_DispatchToCurrentThread(new nsDelayedEventDispatcher(documents));
   } else {
     FireOrClearDelayedEvents(documents, PR_FALSE);
   }
 }
 
+void
+nsIDocument::PreLoadImage(nsIURI* uri)
+{
+  nsCOMPtr<imgIRequest> request;
+  nsContentUtils::LoadImage(uri,
+                            this,
+                            NodePrincipal(),
+                            GetDocumentURI(), // should be ok for referrer
+                            nsnull, // no observer
+                            nsIRequest::LOAD_NORMAL,
+                            getter_AddRefs(request));
+}
--- a/parser/htmlparser/src/nsParser.cpp
+++ b/parser/htmlparser/src/nsParser.cpp
@@ -66,16 +66,19 @@
 #include "nsStreamUtils.h"
 #include "nsHTMLTokenizer.h"
 #include "nsIDocument.h"
 #include "nsNetUtil.h"
 #include "nsScriptLoader.h"
 #include "nsDataHashtable.h"
 #include "nsIThreadPool.h"
 #include "nsXPCOMCIDInternal.h"
+#include "nsICSSStyleSheet.h"
+#include "nsICSSLoaderObserver.h"
+#include "nsICSSLoader.h"
 
 #ifdef MOZ_VIEW_SOURCE
 #include "nsViewSourceHTML.h"
 #endif
 
 #define NS_PARSER_FLAG_PARSER_ENABLED         0x00000002
 #define NS_PARSER_FLAG_OBSERVERS_ENABLED      0x00000004
 #define NS_PARSER_FLAG_PENDING_CONTINUE_EVENT 0x00000008
@@ -216,17 +219,17 @@ public:
   }
 
   NS_DECL_ISUPPORTS
   NS_DECL_NSIRUNNABLE
 
   nsresult StartParsing(nsParser *aParser);
   void StopParsing(PRBool aFromDocWrite);
 
-  enum PrefetchType { SCRIPT, STYLESHEET, IMAGE };
+  enum PrefetchType { NONE, SCRIPT, STYLESHEET, IMAGE };
   struct PrefetchEntry {
     PrefetchType type;
     nsString uri;
     nsString charset;
     nsString elementType;
   };
 
   nsIDocument *GetDocument() {
@@ -286,16 +289,31 @@ private:
 
   // These members are only accessed on the main thread.
   nsCOMPtr<nsIDocument> mDocument;
   CParserContext *mContext;
   PreloadedType mPreloadedURIs;
   PRBool mTerminated;
 };
 
+/**
+ * Used if we need to pass an nsICSSLoaderObserver as parameter,
+ * but don't really need its services
+ */
+class nsDummyCSSLoaderObserver : public nsICSSLoaderObserver {
+public:
+  NS_IMETHOD
+  StyleSheetLoaded(nsICSSStyleSheet* aSheet, PRBool aWasAlternate, nsresult aStatus) {
+      return NS_OK;
+  }
+  NS_DECL_ISUPPORTS
+};
+
+NS_IMPL_ISUPPORTS1(nsDummyCSSLoaderObserver, nsICSSLoaderObserver)
+
 class nsPreloadURIs : public nsIRunnable {
 public:
   nsPreloadURIs(nsAutoTArray<nsSpeculativeScriptThread::PrefetchEntry, 5> &aURIs,
                 nsSpeculativeScriptThread *aScriptThread)
     : mURIs(aURIs),
       mScriptThread(aScriptThread) {
   }
 
@@ -338,20 +356,16 @@ nsPreloadURIs::PreloadURIs(const nsAutoT
   // the URIs using the document's base URI at the potential cost of being
   // wrong and having to re-load a given relative URI later.
   nsIURI *base = doc->GetBaseURI();
   const nsCString &charset = doc->GetDocumentCharacterSet();
   nsSpeculativeScriptThread::PreloadedType &alreadyPreloaded =
     aScriptThread->GetPreloadedURIs();
   for (PRUint32 i = 0, e = aURIs.Length(); i < e; ++i) {
     const nsSpeculativeScriptThread::PrefetchEntry &pe = aURIs[i];
-    if (pe.type != nsSpeculativeScriptThread::SCRIPT) {
-      continue;
-    }
-
     nsCOMPtr<nsIURI> uri;
     nsresult rv = NS_NewURI(getter_AddRefs(uri), pe.uri, charset.get(), base);
     if (NS_FAILED(rv)) {
       NS_WARNING("Failed to create a URI");
       continue;
     }
 
     nsCAutoString spec;
@@ -359,17 +373,28 @@ nsPreloadURIs::PreloadURIs(const nsAutoT
     PRBool answer;
     if (alreadyPreloaded.Get(spec, &answer)) {
       // Already preloaded. Don't preload again.
       continue;
     }
 
     alreadyPreloaded.Put(spec, PR_TRUE);
 
-    doc->ScriptLoader()->PreloadURI(uri, pe.charset, pe.elementType);
+    switch (pe.type) {
+      case nsSpeculativeScriptThread::SCRIPT:
+        doc->ScriptLoader()->PreloadURI(uri, pe.charset, pe.elementType);
+        break; 
+      case nsSpeculativeScriptThread::IMAGE:
+        doc->PreLoadImage(uri);
+        break;
+      case nsSpeculativeScriptThread::STYLESHEET:
+        nsCOMPtr<nsICSSLoaderObserver> obs = new nsDummyCSSLoaderObserver();
+        doc->CSSLoader()->LoadSheet(uri, doc->NodePrincipal(), obs);
+        break;
+    }
   }
 }
 
 NS_IMPL_THREADSAFE_ISUPPORTS1(nsSpeculativeScriptThread, nsIRunnable)
 
 NS_IMETHODIMP
 nsSpeculativeScriptThread::Run()
 {
@@ -548,94 +573,83 @@ nsSpeculativeScriptThread::ProcessToken(
     case eToken_start: {
         CStartToken *start = static_cast<CStartToken *>(aToken);
         nsHTMLTag tag = static_cast<nsHTMLTag>(start->GetTypeID());
         PRInt16 attrs = start->GetAttributeCount();
         PRInt16 i = 0;
         nsAutoString src;
         nsAutoString elementType;
         nsAutoString charset;
-        PrefetchType ptype = SCRIPT;
+        nsAutoString href;
+        nsAutoString rel;
+        PrefetchType ptype = NONE;
 
         switch (tag) {
-#if 0 // TODO Support stylesheet and image preloading.
-          case eHTMLTag_link: {
-            // If this is a <link rel=stylesheet> find the src.
-            PRBool isRelStylesheet = PR_FALSE;
-            for (; i < attrs; ++i) {
+          case eHTMLTag_link:
+              ptype = STYLESHEET;
+              break;
+
+          case eHTMLTag_img:
+              ptype = IMAGE;
+              break;
+
+          case eHTMLTag_script:
+              ptype = SCRIPT;
+              break;
+
+          default:
+              break;
+        }
+
+        // We currently handle the following element/attribute combos :
+        //     <link rel="stylesheet" href= charset= type>
+        //     <img src= >
+        //     <script src= charset= type=>
+        if (ptype != NONE) {
+      
+            // 1. loop over all attributes to extract relevant info
+            for (; i < attrs ; ++i) {
               CAttributeToken *attr = static_cast<CAttributeToken *>(mTokenizer->PopToken());
               NS_ASSERTION(attr->GetTokenType() == eToken_attribute, "Weird token");
-
+    
               if (attr->GetKey().EqualsLiteral("rel")) {
-                if (!attr->GetValue().EqualsLiteral("stylesheet")) {
-                  IF_FREE(attr, &mTokenAllocator);
-                  break;
-                }
-                isRelStylesheet = PR_TRUE;
+                rel.Assign(attr->GetValue());
+              } else if (attr->GetKey().EqualsLiteral("href")) {
+                href.Assign(attr->GetValue());
               } else if (attr->GetKey().EqualsLiteral("src")) {
                 src.Assign(attr->GetValue());
-                if (isRelStylesheet) {
-                  IF_FREE(attr, &mTokenAllocator);
-                  break;
-                }
-              }
-
-              IF_FREE(attr, &mTokenAllocator);
-            }
-
-            if (isRelStylesheet && !src.IsEmpty()) {
-              AddToPrefetchList(src, STYLESHEET);
-            }
-            break;
-          }
-
-          case eHTMLTag_style:
-            ptype = STYLESHEET;
-            /* FALL THROUGH */
-          case eHTMLTag_img:
-            if (tag == eHTMLTag_img)
-              ptype = IMAGE;
-            /* FALL THROUGH */
-#endif
-          case eHTMLTag_script:
-            if (tag == eHTMLTag_script)
-              ptype = SCRIPT;
-
-            for (; i < attrs; ++i) {
-              CAttributeToken *attr = static_cast<CAttributeToken *>(mTokenizer->PopToken());
-              NS_ASSERTION(attr->GetTokenType() == eToken_attribute, "Weird token");
-
-              if (attr->GetKey().EqualsLiteral("src")) {
-                src.Assign(attr->GetValue());
               } else if (attr->GetKey().EqualsLiteral("charset")) {
                 charset.Assign(attr->GetValue());
               } else if (attr->GetKey().EqualsLiteral("type")) {
                 elementType.Assign(attr->GetValue());
               }
+
               IF_FREE(attr, &mTokenAllocator);
             }
 
+            // 2. ensure we have the right kind if it's a link-element
+            if (ptype == STYLESHEET) {
+              if (rel.EqualsLiteral("stylesheet")) {
+                src = href; // src is the important variable below
+              } else {
+                src.Truncate(); // clear src if wrong kind of link
+              }
+            }
+
+            // 3. add to list if we have a valid src
             if (!src.IsEmpty()) {
               AddToPrefetchList(src, charset, elementType, ptype);
             }
-            break;
-
-          default:
-            break;
+        } else {
+            // Irrelevant tag, but pop and free all its attributes in any case
+            for (; i < attrs ; ++i) {
+              CToken *attr = mTokenizer->PopToken();
+              IF_FREE(attr, &mTokenAllocator);
+            }
         }
-
-        for (; i < attrs; ++i) {
-          CToken *attr = mTokenizer->PopToken();
-          if (!attr) {
-            break;
-          }
-          NS_ASSERTION(attr->GetTokenType() == eToken_attribute, "Weird token");
-          IF_FREE(attr, &mTokenAllocator);
-        }
-
         break;
       }
 
     default:
       break;
   }
 
   IF_FREE(aToken, &mTokenAllocator);