Bug 1382567 - boost load priority for <img> without height/width. r?hsivonen,tnikkel draft
authorShih-Chiang Chien <schien@mozilla.com>
Thu, 20 Jul 2017 16:57:24 +0800
changeset 643875 c2ec7775790229f14c1a4966f7163deab774e546
parent 641444 bb8de16ce00cb57b587a14c210ecc7505f366328
child 725419 941c1af2d09bbedcdc5439e7096f30c1b73cea4b
push id73230
push userschien@mozilla.com
push dateThu, 10 Aug 2017 06:33:18 +0000
reviewershsivonen, tnikkel
bugs1382567
milestone57.0a1
Bug 1382567 - boost load priority for <img> without height/width. r?hsivonen,tnikkel HTML5 parser will do speculative image loading while detecting <img> element. We can increase the loading priority for image that doesn't have height and width attribute specified, so that we can do size decoding as soon as possible. MozReview-Commit-ID: 6iUyDEarCKQ
docshell/base/nsContextMenuInfo.cpp
dom/base/nsContentUtils.cpp
dom/base/nsContentUtils.h
dom/base/nsDocument.cpp
dom/base/nsDocument.h
dom/base/nsIDocument.h
image/imgLoader.cpp
image/imgLoader.h
layout/generic/nsImageFrame.cpp
parser/html/nsHtml5SpeculativeLoad.cpp
parser/html/nsHtml5SpeculativeLoad.h
parser/html/nsHtml5TreeBuilderCppSupplement.h
parser/html/nsHtml5TreeOpExecutor.cpp
parser/html/nsHtml5TreeOpExecutor.h
widget/cocoa/nsMenuItemIconX.mm
--- a/docshell/base/nsContextMenuInfo.cpp
+++ b/docshell/base/nsContextMenuInfo.cpp
@@ -298,17 +298,18 @@ nsContextMenuInfo::GetBackgroundImageReq
           imgLoader* il = imgLoader::NormalLoader();
           NS_ENSURE_TRUE(il, NS_ERROR_FAILURE);
 
           return il->LoadImage(bgUri, nullptr, nullptr,
                                doc->GetReferrerPolicy(), principal, nullptr,
                                nullptr, nullptr, nullptr, nsIRequest::LOAD_NORMAL,
                                nullptr, nsIContentPolicy::TYPE_INTERNAL_IMAGE,
                                EmptyString(),
-                               /* aUseUrgentStartForChannel */ false, aRequest);
+                               /* aUseUrgentStartForChannel */ false,
+                               /* aBoostForSizeDecode */ false, aRequest);
         }
       }
 
       // bail if we encounter non-transparent background-color
       computedStyle->GetPropertyCSSValue(NS_LITERAL_STRING("background-color"),
                                          getter_AddRefs(cssValue));
       primitiveValue = do_QueryInterface(cssValue);
       if (primitiveValue) {
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -3686,17 +3686,18 @@ nsresult
 nsContentUtils::LoadImage(nsIURI* aURI, nsINode* aContext,
                           nsIDocument* aLoadingDocument,
                           nsIPrincipal* aLoadingPrincipal, nsIURI* aReferrer,
                           net::ReferrerPolicy aReferrerPolicy,
                           imgINotificationObserver* aObserver, int32_t aLoadFlags,
                           const nsAString& initiatorType,
                           imgRequestProxy** aRequest,
                           uint32_t aContentPolicyType,
-                          bool aUseUrgentStartForChannel)
+                          bool aUseUrgentStartForChannel,
+                          bool aBoostForSizeDecode)
 {
   NS_PRECONDITION(aURI, "Must have a URI");
   NS_PRECONDITION(aContext, "Must have a context");
   NS_PRECONDITION(aLoadingDocument, "Must have a document");
   NS_PRECONDITION(aLoadingPrincipal, "Must have a principal");
   NS_PRECONDITION(aRequest, "Null out param");
 
   imgLoader* imgLoader = GetImgLoaderForDocument(aLoadingDocument);
@@ -3726,16 +3727,17 @@ nsContentUtils::LoadImage(nsIURI* aURI, 
                               aObserver,            /* imgINotificationObserver */
                               aContext,             /* loading context */
                               aLoadingDocument,     /* uniquification key */
                               aLoadFlags,           /* load flags */
                               nullptr,              /* cache key */
                               aContentPolicyType,   /* content policy type */
                               initiatorType,        /* the load initiator */
                               aUseUrgentStartForChannel, /* urgent-start flag */
+                              aBoostForSizeDecode,
                               aRequest);
 }
 
 // static
 already_AddRefed<imgIContainer>
 nsContentUtils::GetImageFromContent(nsIImageLoadingContent* aContent,
                                     imgIRequest **aRequest)
 {
--- a/dom/base/nsContentUtils.h
+++ b/dom/base/nsContentUtils.h
@@ -786,17 +786,18 @@ public:
                             nsIPrincipal* aLoadingPrincipal,
                             nsIURI* aReferrer,
                             mozilla::net::ReferrerPolicy aReferrerPolicy,
                             imgINotificationObserver* aObserver,
                             int32_t aLoadFlags,
                             const nsAString& initiatorType,
                             imgRequestProxy** aRequest,
                             uint32_t aContentPolicyType = nsIContentPolicy::TYPE_INTERNAL_IMAGE,
-                            bool aUseUrgentStartForChannel = false);
+                            bool aUseUrgentStartForChannel = false,
+                            bool aBoostForSizeDecode = false);
 
   /**
    * Obtain an image loader that respects the given document/channel's privacy status.
    * Null document/channel arguments return the public image loader.
    */
   static imgLoader* GetImgLoaderForDocument(nsIDocument* aDoc);
   static imgLoader* GetImgLoaderForChannel(nsIChannel* aChannel,
                                            nsIDocument* aContext);
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -9605,17 +9605,18 @@ nsDocument::ResolvePreloadImage(nsIURI *
   // We don't clear mPreloadPictureFoundSource because subsequent <img> tags in
   // this this <picture> share the same <sources> (though this is not valid per
   // spec)
   return uri.forget();
 }
 
 void
 nsDocument::MaybePreLoadImage(nsIURI* uri, const nsAString &aCrossOriginAttr,
-                              ReferrerPolicy aReferrerPolicy)
+                              ReferrerPolicy aReferrerPolicy,
+                              bool aBoostForSizeDecode)
 {
   // Early exit if the img is already present in the img-cache
   // which indicates that the "real" load has already started and
   // that we shouldn't preload it.
   int16_t blockingStatus;
   if (nsContentUtils::IsImageInCache(uri, static_cast<nsIDocument *>(this)) ||
       !nsContentUtils::CanLoadImage(uri, static_cast<nsIDocument *>(this),
                                     this, NodePrincipal(), &blockingStatus,
@@ -9646,17 +9647,19 @@ nsDocument::MaybePreLoadImage(nsIURI* ur
                               this,
                               NodePrincipal(),
                               mDocumentURI, // uri of document used as referrer
                               aReferrerPolicy,
                               nullptr,       // no observer
                               loadFlags,
                               NS_LITERAL_STRING("img"),
                               getter_AddRefs(request),
-                              nsIContentPolicy::TYPE_INTERNAL_IMAGE_PRELOAD);
+                              nsIContentPolicy::TYPE_INTERNAL_IMAGE_PRELOAD,
+                              /* aUseUrgentStartForChannel */ false,
+                              aBoostForSizeDecode);
 
   // Pin image-reference to avoid evicting it from the img-cache before
   // the "real" load occurs. Unpinned in DispatchContentLoadedEvents and
   // unlink
   if (NS_SUCCEEDED(rv)) {
     mPreloadingImages.Put(uri, request.forget());
   }
 }
--- a/dom/base/nsDocument.h
+++ b/dom/base/nsDocument.h
@@ -822,17 +822,18 @@ public:
   virtual already_AddRefed<nsIURI>
     ResolvePreloadImage(nsIURI *aBaseURI,
                         const nsAString& aSrcAttr,
                         const nsAString& aSrcsetAttr,
                         const nsAString& aSizesAttr) override;
 
   virtual void MaybePreLoadImage(nsIURI* uri,
                                  const nsAString &aCrossOriginAttr,
-                                 ReferrerPolicy aReferrerPolicy) override;
+                                 ReferrerPolicy aReferrerPolicy,
+                                 bool aBoostForSizeDecode) override;
   virtual void ForgetImagePreload(nsIURI* aURI) override;
 
   virtual void MaybePreconnect(nsIURI* uri,
                                mozilla::CORSMode aCORSMode) override;
 
   virtual void PreloadStyle(nsIURI* uri, const nsAString& charset,
                             const nsAString& aCrossOriginAttr,
                             ReferrerPolicy aReferrerPolicy,
--- a/dom/base/nsIDocument.h
+++ b/dom/base/nsIDocument.h
@@ -2361,17 +2361,18 @@ public:
   /**
    * 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.  aCrossOriginAttr should
    * be a void string if the attr is not present.
    */
   virtual void MaybePreLoadImage(nsIURI* uri,
                                  const nsAString& aCrossOriginAttr,
-                                 ReferrerPolicyEnum aReferrerPolicy) = 0;
+                                 ReferrerPolicyEnum aReferrerPolicy,
+                                 bool aBoostForSizeDecode = false) = 0;
 
   /**
    * Called by images to forget an image preload when they start doing
    * the real load.
    */
   virtual void ForgetImagePreload(nsIURI* aURI) = 0;
 
   /**
--- a/image/imgLoader.cpp
+++ b/image/imgLoader.cpp
@@ -2071,16 +2071,17 @@ imgLoader::LoadImageXPCOM(nsIURI* aURI,
                             aObserver,
                             node,
                             doc,
                             aLoadFlags,
                             aCacheKey,
                             aContentPolicyType,
                             EmptyString(),
                             /* aUseUrgentStartForChannel */ false,
+                            /* aBoostForSizeDecode */ false,
                             &proxy);
     *_retval = proxy;
     return rv;
 }
 
 nsresult
 imgLoader::LoadImage(nsIURI* aURI,
                      nsIURI* aInitialDocumentURI,
@@ -2091,16 +2092,17 @@ imgLoader::LoadImage(nsIURI* aURI,
                      imgINotificationObserver* aObserver,
                      nsINode *aContext,
                      nsIDocument* aLoadingDocument,
                      nsLoadFlags aLoadFlags,
                      nsISupports* aCacheKey,
                      nsContentPolicyType aContentPolicyType,
                      const nsAString& initiatorType,
                      bool aUseUrgentStartForChannel,
+                     bool aBoostForSizeDecode,
                      imgRequestProxy** _retval)
 {
   VerifyCacheSizes();
 
   NS_ASSERTION(aURI, "imgLoader::LoadImage -- NULL URI pointer");
 
   if (!aURI) {
     return NS_ERROR_NULL_POINTER;
@@ -2249,16 +2251,20 @@ imgLoader::LoadImage(nsIURI* aURI,
     newChannel->GetLoadGroup(getter_AddRefs(channelLoadGroup));
     rv = request->Init(aURI, aURI, /* aHadInsecureRedirect = */ false,
                        channelLoadGroup, newChannel, entry, aLoadingDocument,
                        aLoadingPrincipal, corsmode, aReferrerPolicy);
     if (NS_FAILED(rv)) {
       return NS_ERROR_FAILURE;
     }
 
+    if (aBoostForSizeDecode) {
+      request->BoostPriority(imgIRequest::CATEGORY_SIZE_QUERY);
+    }
+
     // Add the initiator type for this image load
     nsCOMPtr<nsITimedChannel> timedChannel = do_QueryInterface(newChannel);
     if (timedChannel) {
       timedChannel->SetInitiatorType(initiatorType);
     }
 
     // create the proxy listener
     nsCOMPtr<nsIStreamListener> listener = new ProxyListener(request.get());
--- a/image/imgLoader.h
+++ b/image/imgLoader.h
@@ -294,16 +294,17 @@ public:
                                   imgINotificationObserver* aObserver,
                                   nsINode* aContext,
                                   nsIDocument* aLoadingDocument,
                                   nsLoadFlags aLoadFlags,
                                   nsISupports* aCacheKey,
                                   nsContentPolicyType aContentPolicyType,
                                   const nsAString& initiatorType,
                                   bool aUseUrgentStartForChannel,
+                                  bool aBoostForSizeDecode,
                                   imgRequestProxy** _retval);
 
   MOZ_MUST_USE nsresult
   LoadImageWithChannel(nsIChannel* channel,
                        imgINotificationObserver* aObserver,
                        nsISupports* aCX,
                        nsIStreamListener** listener,
                        imgRequestProxy** _retval);
--- a/layout/generic/nsImageFrame.cpp
+++ b/layout/generic/nsImageFrame.cpp
@@ -2278,16 +2278,17 @@ nsImageFrame::LoadIcon(const nsAString& 
                        gIconLoad,
                        nullptr,      /* No context */
                        nullptr,      /* Not associated with any particular document */
                        loadFlags,
                        nullptr,
                        contentPolicyType,
                        EmptyString(),
                        false,        /* aUseUrgentStartForChannel */
+                       false,        /* aBoostForSizeDecode */
                        aRequest);
 }
 
 void
 nsImageFrame::GetDocumentCharacterSet(nsACString& aCharset) const
 {
   if (mContent) {
     NS_ASSERTION(mContent->GetComposedDoc(),
--- a/parser/html/nsHtml5SpeculativeLoad.cpp
+++ b/parser/html/nsHtml5SpeculativeLoad.cpp
@@ -29,17 +29,18 @@ nsHtml5SpeculativeLoad::Perform(nsHtml5T
       break;
     case eSpeculativeLoadCSP:
       aExecutor->AddSpeculationCSP(mMetaCSP);
       break;
     case eSpeculativeLoadMetaReferrer:
       aExecutor->SetSpeculationReferrerPolicy(mReferrerPolicy);
       break;
     case eSpeculativeLoadImage:
-      aExecutor->PreloadImage(mUrl, mCrossOrigin, mSrcset, mSizes, mReferrerPolicy);
+      aExecutor->PreloadImage(mUrl, mCrossOrigin, mSrcset, mSizes, mReferrerPolicy,
+                              mShouldBoostPriority);
       break;
     case eSpeculativeLoadOpenPicture:
       aExecutor->PreloadOpenPicture();
       break;
     case eSpeculativeLoadEndPicture:
       aExecutor->PreloadEndPicture();
       break;
     case eSpeculativeLoadPictureSource:
--- a/parser/html/nsHtml5SpeculativeLoad.h
+++ b/parser/html/nsHtml5SpeculativeLoad.h
@@ -66,31 +66,33 @@ class nsHtml5SpeculativeLoad {
         nsContentUtils::TrimWhitespace<nsContentUtils::IsHTMLWhitespace>(
           referrerPolicy));
     }
 
     inline void InitImage(nsHtml5String aUrl,
                           nsHtml5String aCrossOrigin,
                           nsHtml5String aReferrerPolicy,
                           nsHtml5String aSrcset,
-                          nsHtml5String aSizes)
+                          nsHtml5String aSizes,
+                          bool aShouldBoostPriority)
     {
       NS_PRECONDITION(mOpCode == eSpeculativeLoadUninitialized,
                       "Trying to reinitialize a speculative load!");
       mOpCode = eSpeculativeLoadImage;
       aUrl.ToString(mUrl);
       aCrossOrigin.ToString(mCrossOrigin);
       nsString
         referrerPolicy; // Not Auto, because using it to hold nsStringBuffer*
       aReferrerPolicy.ToString(referrerPolicy);
       mReferrerPolicy.Assign(
         nsContentUtils::TrimWhitespace<nsContentUtils::IsHTMLWhitespace>(
           referrerPolicy));
       aSrcset.ToString(mSrcset);
       aSizes.ToString(mSizes);
+      mShouldBoostPriority = aShouldBoostPriority;
     }
 
     // <picture> elements have multiple <source> nodes followed by an <img>,
     // where we use the first valid source, which may be the img. Because we
     // can't determine validity at this point without parsing CSS and getting
     // main thread state, we push preload operations for picture pushed and
     // popped, so that the target of the preload ops can determine what picture
     // and nesting level each source/img from the main preloading code exists
@@ -265,11 +267,16 @@ class nsHtml5SpeculativeLoad {
      */
     nsString mMedia;
     /**
      * If mOpCode is eSpeculativeLoadScript[FromHead], this is the value of the
      * "integrity" attribute.  If the attribute is not set, this will be a void
      * string.
      */
     nsString mIntegrity;
+    /**
+     * If mOpCode is eSpeculativeLoadImage, this is the value of whether the
+     * image loading priority should be increased.
+     */
+    bool mShouldBoostPriority= false;
 };
 
 #endif // nsHtml5SpeculativeLoad_h
--- a/parser/html/nsHtml5TreeBuilderCppSupplement.h
+++ b/parser/html/nsHtml5TreeBuilderCppSupplement.h
@@ -128,18 +128,25 @@ nsHtml5TreeBuilder::createElement(int32_
           nsHtml5String srcset =
             aAttributes->getValue(nsHtml5AttributeName::ATTR_SRCSET);
           nsHtml5String crossOrigin =
             aAttributes->getValue(nsHtml5AttributeName::ATTR_CROSSORIGIN);
           nsHtml5String referrerPolicy =
             aAttributes->getValue(nsHtml5AttributeName::ATTR_REFERRERPOLICY);
           nsHtml5String sizes =
             aAttributes->getValue(nsHtml5AttributeName::ATTR_SIZES);
+
+          // Increase the image loading priority to provide size information
+          // as soon as possible.
+          bool hasHeightWidth =
+            aAttributes->contains(nsHtml5AttributeName::ATTR_HEIGHT) ||
+            aAttributes->contains(nsHtml5AttributeName::ATTR_WIDTH);
+
           mSpeculativeLoadQueue.AppendElement()->InitImage(
-            url, crossOrigin, referrerPolicy, srcset, sizes);
+            url, crossOrigin, referrerPolicy, srcset, sizes, !hasHeightWidth);
         } else if (nsGkAtoms::source == aName) {
           nsHtml5String srcset =
             aAttributes->getValue(nsHtml5AttributeName::ATTR_SRCSET);
           // Sources without srcset cannot be selected. The source could also be
           // for a media element, but in that context doesn't use srcset.  See
           // comments in nsHtml5SpeculativeLoad.h about <picture> preloading
           if (srcset) {
             nsHtml5String sizes =
@@ -208,17 +215,17 @@ nsHtml5TreeBuilder::createElement(int32_
               }
             }
           }
         } else if (nsGkAtoms::video == aName) {
           nsHtml5String url =
             aAttributes->getValue(nsHtml5AttributeName::ATTR_POSTER);
           if (url) {
             mSpeculativeLoadQueue.AppendElement()->InitImage(
-              url, nullptr, nullptr, nullptr, nullptr);
+              url, nullptr, nullptr, nullptr, nullptr, false);
           }
         } else if (nsGkAtoms::style == aName) {
           nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
           NS_ASSERTION(treeOp, "Tree op allocation failed.");
           treeOp->Init(eTreeOpSetStyleLineNumber, content, tokenizer->getLineNumber());
         } else if (nsGkAtoms::html == aName) {
           nsHtml5String url =
             aAttributes->getValue(nsHtml5AttributeName::ATTR_MANIFEST);
@@ -252,17 +259,17 @@ nsHtml5TreeBuilder::createElement(int32_
         }
         break;
       case kNameSpaceID_SVG:
         if (nsGkAtoms::image == aName) {
           nsHtml5String url =
             aAttributes->getValue(nsHtml5AttributeName::ATTR_XLINK_HREF);
           if (url) {
             mSpeculativeLoadQueue.AppendElement()->InitImage(
-              url, nullptr, nullptr, nullptr, nullptr);
+              url, nullptr, nullptr, nullptr, nullptr, false);
           }
         } else if (nsGkAtoms::script == aName) {
           nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
           NS_ASSERTION(treeOp, "Tree op allocation failed.");
           treeOp->Init(eTreeOpSetScriptLineNumberAndFreeze, content, tokenizer->getLineNumber());
 
           nsHtml5String url =
             aAttributes->getValue(nsHtml5AttributeName::ATTR_XLINK_HREF);
--- a/parser/html/nsHtml5TreeOpExecutor.cpp
+++ b/parser/html/nsHtml5TreeOpExecutor.cpp
@@ -972,31 +972,33 @@ nsHtml5TreeOpExecutor::PreloadStyle(cons
                           mSpeculationReferrerPolicy, aIntegrity);
 }
 
 void
 nsHtml5TreeOpExecutor::PreloadImage(const nsAString& aURL,
                                     const nsAString& aCrossOrigin,
                                     const nsAString& aSrcset,
                                     const nsAString& aSizes,
-                                    const nsAString& aImageReferrerPolicy)
+                                    const nsAString& aImageReferrerPolicy,
+                                    const bool& aShouldBoostPriority)
 {
   nsCOMPtr<nsIURI> baseURI = BaseURIForPreload();
   nsCOMPtr<nsIURI> uri = mDocument->ResolvePreloadImage(baseURI, aURL, aSrcset,
                                                         aSizes);
   if (uri && ShouldPreloadURI(uri)) {
     // use document wide referrer policy
     mozilla::net::ReferrerPolicy referrerPolicy = mSpeculationReferrerPolicy;
     mozilla::net::ReferrerPolicy imageReferrerPolicy =
       mozilla::net::AttributeReferrerPolicyFromString(aImageReferrerPolicy);
     if (imageReferrerPolicy != mozilla::net::RP_Unset) {
       referrerPolicy = imageReferrerPolicy;
     }
 
-    mDocument->MaybePreLoadImage(uri, aCrossOrigin, referrerPolicy);
+    mDocument->MaybePreLoadImage(uri, aCrossOrigin, referrerPolicy,
+                                 /* boostForSizeQuery */ aShouldBoostPriority);
   }
 }
 
 // These calls inform the document of picture state and seen sources, such that
 // it can use them to inform ResolvePreLoadImage as necessary
 void
 nsHtml5TreeOpExecutor::PreloadPictureSource(const nsAString& aSrcset,
                                             const nsAString& aSizes,
--- a/parser/html/nsHtml5TreeOpExecutor.h
+++ b/parser/html/nsHtml5TreeOpExecutor.h
@@ -257,17 +257,18 @@ class nsHtml5TreeOpExecutor final : publ
     void PreloadStyle(const nsAString& aURL, const nsAString& aCharset,
                       const nsAString& aCrossOrigin,
                       const nsAString& aIntegrity);
 
     void PreloadImage(const nsAString& aURL,
                       const nsAString& aCrossOrigin,
                       const nsAString& aSrcset,
                       const nsAString& aSizes,
-                      const nsAString& aImageReferrerPolicy);
+                      const nsAString& aImageReferrerPolicy,
+                      const bool& aShouldBoostPriority);
 
     void PreloadOpenPicture();
 
     void PreloadEndPicture();
 
     void PreloadPictureSource(const nsAString& aSrcset,
                               const nsAString& aSizes,
                               const nsAString& aType,
--- a/widget/cocoa/nsMenuItemIconX.mm
+++ b/widget/cocoa/nsMenuItemIconX.mm
@@ -315,16 +315,17 @@ nsMenuItemIconX::LoadIcon(nsIURI* aIconU
   }
 
   nsresult rv = loader->LoadImage(aIconURI, nullptr, nullptr,
                                   mozilla::net::RP_Unset,
                                   mLoadingPrincipal, loadGroup, this,
                                   mContent, document, nsIRequest::LOAD_NORMAL, nullptr,
                                   mContentType, EmptyString(),
                                   /* aUseUrgentStartForChannel */ false,
+                                  /* aBoostForSizeDecode */ false,
                                   getter_AddRefs(mIconRequest));
   if (NS_FAILED(rv)) return rv;
 
   return NS_OK;
 
   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
 }