Bug 1348050 - Part 3: Mark channel as urgent-start for loading image. r=baku, mayhemer draft
authorTom Tung <shes050117@gmail.com>
Tue, 25 Apr 2017 09:17:38 +0800
changeset 574695 241c26c0c7daf37c0d55e13287b0cafb5b908c59
parent 574690 a96e0409608c594eae0aa39feb4f5a2d294b1ad9
child 574696 2947e1c3bdb9bc93211cfe6fb80ec354fa20e634
push id57805
push userttung@mozilla.com
push dateTue, 09 May 2017 10:10:38 +0000
reviewersbaku, mayhemer
bugs1348050
milestone55.0a1
Bug 1348050 - Part 3: Mark channel as urgent-start for loading image. r=baku, mayhemer This part is mainly to mark the channel as urgent-start if src related attributes in HTMLImageElement and HTMLInputElement is set and the channel is open due to user interaction. Unfortunately, we cannot just check the event state just after creating channel since some loading image tasks will be queue and execute in stable state. Thus, I store the event state in elements and pass it to the place where create the channel. MozReview-Commit-ID: GBdAkPfVzsn
docshell/base/nsContextMenuInfo.cpp
dom/base/nsContentUtils.cpp
dom/base/nsContentUtils.h
dom/base/nsImageLoadingContent.cpp
dom/base/nsImageLoadingContent.h
dom/html/HTMLImageElement.cpp
dom/html/HTMLInputElement.cpp
dom/svg/SVGFEImageElement.cpp
dom/svg/SVGImageElement.cpp
image/imgLoader.cpp
image/imgLoader.h
layout/generic/nsImageFrame.cpp
widget/cocoa/nsMenuItemIconX.mm
--- a/docshell/base/nsContextMenuInfo.cpp
+++ b/docshell/base/nsContextMenuInfo.cpp
@@ -297,17 +297,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(), aRequest);
+                               EmptyString(),
+                               /* aUseUrgentStartForChannel */ 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
@@ -3507,17 +3507,18 @@ nsContentUtils::IsImageInCache(nsIURI* a
 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)
+                          uint32_t aContentPolicyType,
+                          bool aUseUrgentStartForChannel)
 {
   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);
@@ -3546,16 +3547,17 @@ nsContentUtils::LoadImage(nsIURI* aURI, 
                               loadGroup,            /* loadgroup */
                               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 */
                               aRequest);
 }
 
 // static
 already_AddRefed<imgIContainer>
 nsContentUtils::GetImageFromContent(nsIImageLoadingContent* aContent,
                                     imgIRequest **aRequest)
 {
--- a/dom/base/nsContentUtils.h
+++ b/dom/base/nsContentUtils.h
@@ -764,29 +764,32 @@ public:
    * @param aLoadingPrincipal the principal doing the load
    * @param aReferrer the referrer URI
    * @param aReferrerPolicy the referrer-sending policy to use on channel
    *         creation
    * @param aObserver the observer for the image load
    * @param aLoadFlags the load flags to use.  See nsIRequest
    * @param [aContentPolicyType=nsIContentPolicy::TYPE_INTERNAL_IMAGE] (Optional)
    *        The CP content type to use
+   * @param aUseUrgentStartForChannel,(Optional) a flag to mark on channel if it
+   *        is triggered by user input events.
    * @return the imgIRequest for the image load
    */
   static nsresult LoadImage(nsIURI* aURI,
                             nsINode* aContext,
                             nsIDocument* aLoadingDocument,
                             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);
+                            uint32_t aContentPolicyType = nsIContentPolicy::TYPE_INTERNAL_IMAGE,
+                            bool aUseUrgentStartForChannel = 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/nsImageLoadingContent.cpp
+++ b/dom/base/nsImageLoadingContent.cpp
@@ -39,16 +39,17 @@
 #include "nsLayoutUtils.h"
 #include "nsIContentPolicy.h"
 #include "nsSVGEffects.h"
 
 #include "gfxPrefs.h"
 
 #include "mozAutoDocUpdate.h"
 #include "mozilla/AsyncEventDispatcher.h"
+#include "mozilla/EventStateManager.h"
 #include "mozilla/EventStates.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/ImageTracker.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/Preferences.h"
 
 #ifdef LoadImage
 // Undefine LoadImage to prevent naming conflict with Windows.
@@ -87,16 +88,17 @@ nsImageLoadingContent::nsImageLoadingCon
     mLoadingEnabled(true),
     mIsImageStateForced(false),
     mLoading(false),
     // mBroken starts out true, since an image without a URI is broken....
     mBroken(true),
     mUserDisabled(false),
     mSuppressed(false),
     mNewRequestsWillNeedAnimationReset(false),
+    mUseUrgentStartForChannel(false),
     mStateChangerDepth(0),
     mCurrentRequestRegistered(false),
     mPendingRequestRegistered(false)
 {
   if (!nsContentUtils::GetImgLoaderForChannel(nullptr, nullptr)) {
     mLoadingEnabled = false;
   }
 
@@ -879,17 +881,22 @@ nsImageLoadingContent::LoadImage(nsIURI*
                                           thisNode,
                                           aDocument,
                                           aDocument->NodePrincipal(),
                                           aDocument->GetDocumentURI(),
                                           referrerPolicy,
                                           this, loadFlags,
                                           content->LocalName(),
                                           getter_AddRefs(req),
-                                          policyType);
+                                          policyType,
+                                          mUseUrgentStartForChannel);
+
+  // Reset the flag to avoid loading from XPCOM or somewhere again else without
+  // initiated by user interaction.
+  mUseUrgentStartForChannel = false;
 
   // Tell the document to forget about the image preload, if any, for
   // this URI, now that we might have another imgRequestProxy for it.
   // That way if we get canceled later the image load won't continue.
   aDocument->ForgetImagePreload(aNewURI);
 
   if (NS_SUCCEEDED(rv)) {
     TrackImage(req);
--- a/dom/base/nsImageLoadingContent.h
+++ b/dom/base/nsImageLoadingContent.h
@@ -450,16 +450,23 @@ protected:
    * that originate from setting .src, we mark them for needing their animation
    * reset when they are ready. mNewRequestsWillNeedAnimationReset is set to
    * true while preparing such requests (as a hack around needing to change an
    * interface), and the other two booleans store which of the current
    * and pending requests are of the sort that need their animation restarted.
    */
   bool mNewRequestsWillNeedAnimationReset : 1;
 
+  /**
+   * Flag to indicate whether the channel should be mark as urgent-start.
+   * It should be set in *Element and passed to nsContentUtils::LoadImage.
+   * True if we want to set nsIClassOfService::UrgentStart to the channel to
+   * get the response ASAP for better user responsiveness.
+   */
+  bool mUseUrgentStartForChannel;
 private:
   /* The number of nested AutoStateChangers currently tracking our state. */
   uint8_t mStateChangerDepth;
 
   // Flags to indicate whether each of the current and pending requests are
   // registered with the refresh driver.
   bool mCurrentRequestRegistered;
   bool mPendingRequestRegistered;
--- a/dom/html/HTMLImageElement.cpp
+++ b/dom/html/HTMLImageElement.cpp
@@ -74,43 +74,50 @@ namespace mozilla {
 namespace dom {
 
 // Calls LoadSelectedImage on host element unless it has been superseded or
 // canceled -- this is the synchronous section of "update the image data".
 // https://html.spec.whatwg.org/multipage/embedded-content.html#update-the-image-data
 class ImageLoadTask : public Runnable
 {
 public:
-  ImageLoadTask(HTMLImageElement *aElement, bool aAlwaysLoad)
+  ImageLoadTask(HTMLImageElement *aElement, bool aAlwaysLoad,
+                bool aUseUrgentStartForChannel)
     : mElement(aElement)
     , mAlwaysLoad(aAlwaysLoad)
+    , mUseUrgentStartForChannel(aUseUrgentStartForChannel)
   {
     mDocument = aElement->OwnerDoc();
     mDocument->BlockOnload();
   }
 
   NS_IMETHOD Run() override
   {
     if (mElement->mPendingImageLoadTask == this) {
       mElement->mPendingImageLoadTask = nullptr;
+      mElement->mUseUrgentStartForChannel = mUseUrgentStartForChannel;
       mElement->LoadSelectedImage(true, true, mAlwaysLoad);
     }
     mDocument->UnblockOnload(false);
     return NS_OK;
   }
 
   bool AlwaysLoad() {
     return mAlwaysLoad;
   }
 
 private:
   ~ImageLoadTask() {}
   RefPtr<HTMLImageElement> mElement;
   nsCOMPtr<nsIDocument> mDocument;
   bool mAlwaysLoad;
+
+  // True if we want to set nsIClassOfService::UrgentStart to the channel to
+  // get the response ASAP for better user responsiveness.
+  bool mUseUrgentStartForChannel;
 };
 
 HTMLImageElement::HTMLImageElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo)
   : nsGenericHTMLElement(aNodeInfo)
   , mForm(nullptr)
   , mInDocResponsiveContent(false)
   , mCurrentDensity(1.0)
 {
@@ -405,33 +412,45 @@ HTMLImageElement::AfterSetAttr(int32_t a
   // parser or some such place; we'll get bound after all the attributes have
   // been set, so we'll do the image load from BindToTree.
 
   nsAttrValueOrString attrVal(aValue);
 
   if (aName == nsGkAtoms::src &&
       aNameSpaceID == kNameSpaceID_None &&
       !aValue) {
+    // Mark channel as urgent-start before load image if the image load is
+    // initaiated by a user interaction.
+    mUseUrgentStartForChannel = EventStateManager::IsHandlingUserInput();
+
     // SetAttr handles setting src since it needs to catch img.src =
     // img.src, so we only need to handle the unset case
     if (InResponsiveMode()) {
       if (mResponsiveSelector &&
           mResponsiveSelector->Content() == this) {
         mResponsiveSelector->SetDefaultSource(NullString());
       }
       QueueImageLoadTask(true);
     } else {
       // Bug 1076583 - We still behave synchronously in the non-responsive case
       CancelImageRequests(aNotify);
     }
   } else if (aName == nsGkAtoms::srcset &&
              aNameSpaceID == kNameSpaceID_None) {
+    // Mark channel as urgent-start before load image if the image load is
+    // initaiated by a user interaction.
+    mUseUrgentStartForChannel = EventStateManager::IsHandlingUserInput();
+
     PictureSourceSrcsetChanged(this, attrVal.String(), aNotify);
   } else if (aName == nsGkAtoms::sizes &&
              aNameSpaceID == kNameSpaceID_None) {
+    // Mark channel as urgent-start before load image if the image load is
+    // initaiated by a user interaction.
+    mUseUrgentStartForChannel = EventStateManager::IsHandlingUserInput();
+
     PictureSourceSizesChanged(this, attrVal.String(), aNotify);
   }
 
   return nsGenericHTMLElement::AfterSetAttr(aNameSpaceID, aName,
                                             aValue, aNotify);
 }
 
 nsresult
@@ -500,16 +519,20 @@ HTMLImageElement::SetAttr(int32_t aNameS
   // If we are in responsive mode, we drop the forced reload behavior,
   // but still trigger a image load task for img.src = img.src per
   // spec.
   //
   // Both cases handle unsetting src in AfterSetAttr
   if (aNameSpaceID == kNameSpaceID_None &&
       aName == nsGkAtoms::src) {
 
+    // Mark channel as urgent-start before load image if the image load is
+    // initaiated by a user interaction.
+    mUseUrgentStartForChannel = EventStateManager::IsHandlingUserInput();
+
     if (InResponsiveMode()) {
       if (mResponsiveSelector &&
           mResponsiveSelector->Content() == this) {
         mResponsiveSelector->SetDefaultSource(aValue);
       }
       QueueImageLoadTask(true);
     } else if (aNotify) {
       // If aNotify is false, we are coming from the parser or some such place;
@@ -557,16 +580,20 @@ HTMLImageElement::SetAttr(int32_t aNameS
 
   nsresult rv = nsGenericHTMLElement::SetAttr(aNameSpaceID, aName, aPrefix,
                                               aValue, aNotify);
 
   // Because we load image synchronously in non-responsive-mode, we need to do
   // reload after the attribute has been set if the reload is triggerred by
   // cross origin changing.
   if (forceReload) {
+    // Mark channel as urgent-start before load image if the image load is
+    // initaiated by a user interaction.
+    mUseUrgentStartForChannel = EventStateManager::IsHandlingUserInput();
+
     if (InResponsiveMode()) {
       // per spec, full selection runs when this changes, even though
       // it doesn't directly affect the source selection
       QueueImageLoadTask(true);
     } else {
       // Bug 1076583 - We still use the older synchronous algorithm in
       // non-responsive mode. Force a new load of the image with the
       // new cross origin policy
@@ -595,28 +622,36 @@ HTMLImageElement::BindToTree(nsIDocument
   }
 
   if (HaveSrcsetOrInPicture()) {
     if (aDocument && !mInDocResponsiveContent) {
       aDocument->AddResponsiveContent(this);
       mInDocResponsiveContent = true;
     }
 
+    // Mark channel as urgent-start before load image if the image load is
+    // initaiated by a user interaction.
+    mUseUrgentStartForChannel = EventStateManager::IsHandlingUserInput();
+
     // Run selection algorithm when an img element is inserted into a document
     // in order to react to changes in the environment. See note of
     // https://html.spec.whatwg.org/multipage/embedded-content.html#img-environment-changes
     QueueImageLoadTask(false);
   } else if (!InResponsiveMode() &&
              HasAttr(kNameSpaceID_None, nsGkAtoms::src)) {
     // We skip loading when our attributes were set from parser land,
     // so trigger a aForce=false load now to check if things changed.
     // This isn't necessary for responsive mode, since creating the
     // image load task is asynchronous we don't need to take special
     // care to avoid doing so when being filled by the parser.
 
+    // Mark channel as urgent-start before load image if the image load is
+    // initaiated by a user interaction.
+    mUseUrgentStartForChannel = EventStateManager::IsHandlingUserInput();
+
     // FIXME: Bug 660963 it would be nice if we could just have
     // ClearBrokenState update our state and do it fast...
     ClearBrokenState();
     RemoveStatesSilently(NS_EVENT_STATE_BROKEN);
 
     // We still act synchronously for the non-responsive case (Bug
     // 1076583), but still need to delay if it is unsafe to run
     // script.
@@ -825,16 +860,20 @@ HTMLImageElement::CopyInnerTo(Element* a
 
   if (!destIsStatic) {
     // In SetAttr (called from nsGenericHTMLElement::CopyInnerTo), dest skipped
     // doing the image load because we passed in false for aNotify.  But we
     // really do want it to do the load, so set it up to happen once the cloning
     // reaches a stable state.
     if (!dest->InResponsiveMode() &&
         dest->HasAttr(kNameSpaceID_None, nsGkAtoms::src)) {
+      // Mark channel as urgent-start before load image if the image load is
+      // initaiated by a user interaction.
+      mUseUrgentStartForChannel = EventStateManager::IsHandlingUserInput();
+
       nsContentUtils::AddScriptRunner(
         NewRunnableMethod(dest, &HTMLImageElement::MaybeLoadImage));
     }
   }
 
   return NS_OK;
 }
 
@@ -908,17 +947,19 @@ HTMLImageElement::QueueImageLoadTask(boo
   }
 
   // Ensure that we don't overwrite a previous load request that requires
   // a complete load to occur.
   bool alwaysLoad = aAlwaysLoad;
   if (mPendingImageLoadTask) {
     alwaysLoad = alwaysLoad || mPendingImageLoadTask->AlwaysLoad();
   }
-  RefPtr<ImageLoadTask> task = new ImageLoadTask(this, alwaysLoad);
+  RefPtr<ImageLoadTask> task = new ImageLoadTask(this,
+                                                 alwaysLoad,
+                                                 mUseUrgentStartForChannel);
   // The task checks this to determine if it was the last
   // queued event, and so earlier tasks are implicitly canceled.
   mPendingImageLoadTask = task;
   nsContentUtils::RunInStableState(task.forget());
 }
 
 bool
 HTMLImageElement::HaveSrcsetOrInPicture()
--- a/dom/html/HTMLInputElement.cpp
+++ b/dom/html/HTMLInputElement.cpp
@@ -1356,16 +1356,20 @@ HTMLInputElement::BeforeSetAttr(int32_t 
     if ((aName == nsGkAtoms::name ||
          (aName == nsGkAtoms::type && !mForm)) &&
         mType == NS_FORM_INPUT_RADIO &&
         (mForm || mDoneCreating)) {
       WillRemoveFromRadioGroup();
     } else if (aNotify && aName == nsGkAtoms::src &&
                mType == NS_FORM_INPUT_IMAGE) {
       if (aValue) {
+        // Mark channel as urgent-start before load image if the image load is
+        // initaiated by a user interaction.
+        mUseUrgentStartForChannel = EventStateManager::IsHandlingUserInput();
+
         LoadImage(aValue->String(), true, aNotify, eImageLoadType_Normal);
       } else {
         // Null value means the attr got unset; drop the image
         CancelImageRequests(aNotify);
       }
     } else if (aNotify && aName == nsGkAtoms::disabled) {
       mDisabledChanged = true;
     } else if (aName == nsGkAtoms::dir &&
@@ -5096,16 +5100,20 @@ HTMLInputElement::BindToTree(nsIDocument
 
   nsImageLoadingContent::BindToTree(aDocument, aParent, aBindingParent,
                                     aCompileEventHandlers);
 
   if (mType == NS_FORM_INPUT_IMAGE) {
     // Our base URI may have changed; claim that our URI changed, and the
     // nsImageLoadingContent will decide whether a new image load is warranted.
     if (HasAttr(kNameSpaceID_None, nsGkAtoms::src)) {
+      // Mark channel as urgent-start before load image if the image load is
+      // initaiated by a user interaction.
+      mUseUrgentStartForChannel = EventStateManager::IsHandlingUserInput();
+
       // FIXME: Bug 660963 it would be nice if we could just have
       // ClearBrokenState update our state and do it fast...
       ClearBrokenState();
       RemoveStatesSilently(NS_EVENT_STATE_BROKEN);
       nsContentUtils::AddScriptRunner(
         NewRunnableMethod(this, &HTMLInputElement::MaybeLoadImage));
     }
   }
@@ -5287,16 +5295,20 @@ HTMLInputElement::HandleTypeChange(uint8
     // We're no longer an image input.  Cancel our image requests, if we have
     // any.
     CancelImageRequests(aNotify);
   } else if (aNotify && mType == NS_FORM_INPUT_IMAGE) {
     // We just got switched to be an image input; we should see
     // whether we have an image to load;
     nsAutoString src;
     if (GetAttr(kNameSpaceID_None, nsGkAtoms::src, src)) {
+      // Mark channel as urgent-start before load image if the image load is
+      // initaiated by a user interaction.
+      mUseUrgentStartForChannel = EventStateManager::IsHandlingUserInput();
+
       LoadImage(src, false, aNotify, eImageLoadType_Normal);
     }
   }
 
   if (mType == NS_FORM_INPUT_PASSWORD && IsInComposedDoc()) {
     AsyncEventDispatcher* dispatcher =
       new AsyncEventDispatcher(this,
                                NS_LITERAL_STRING("DOMInputPasswordAdded"),
--- a/dom/svg/SVGFEImageElement.cpp
+++ b/dom/svg/SVGFEImageElement.cpp
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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/. */
 
 #include "mozilla/dom/SVGFEImageElement.h"
 
+#include "mozilla/EventStateManager.h"
 #include "mozilla/EventStates.h"
 #include "mozilla/dom/SVGFEImageElementBinding.h"
 #include "mozilla/dom/SVGFilterElement.h"
 #include "mozilla/gfx/2D.h"
 #include "mozilla/RefPtr.h"
 #include "nsContentUtils.h"
 #include "nsLayoutUtils.h"
 #include "nsSVGUtils.h"
@@ -86,16 +87,19 @@ SVGFEImageElement::LoadSVGImage(bool aFo
   if (NS_SUCCEEDED(StringToURI(href, doc, getter_AddRefs(hrefAsURI)))) {
     bool isEqual;
     if (NS_SUCCEEDED(hrefAsURI->Equals(baseURI, &isEqual)) && isEqual) {
       // Image URI matches our URI exactly! Bail out.
       return NS_OK;
     }
   }
 
+  // Mark channel as urgent-start before load image if the image load is
+  // initaiated by a user interaction.
+  mUseUrgentStartForChannel = EventStateManager::IsHandlingUserInput();
   return LoadImage(href, aForce, aNotify, eImageLoadType_Normal);
 }
 
 //----------------------------------------------------------------------
 // EventTarget methods:
 
 void
 SVGFEImageElement::AsyncEventRunning(AsyncEventDispatcher* aEvent)
--- a/dom/svg/SVGImageElement.cpp
+++ b/dom/svg/SVGImageElement.cpp
@@ -1,15 +1,16 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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/. */
 
 #include "mozilla/ArrayUtils.h"
+#include "mozilla/EventStateManager.h"
 #include "mozilla/EventStates.h"
 
 #include "mozilla/dom/SVGImageElement.h"
 #include "mozilla/gfx/2D.h"
 #include "nsCOMPtr.h"
 #include "nsIURI.h"
 #include "nsNetUtil.h"
 #include "imgINotificationObserver.h"
@@ -128,16 +129,20 @@ SVGImageElement::LoadSVGImage(bool aForc
   } else {
     mStringAttributes[XLINK_HREF].GetAnimValue(href, this);
   }
   href.Trim(" \t\n\r");
 
   if (baseURI && !href.IsEmpty())
     NS_MakeAbsoluteURI(href, href, baseURI);
 
+  // Mark channel as urgent-start before load image if the image load is
+  // initaiated by a user interaction.
+  mUseUrgentStartForChannel = EventStateManager::IsHandlingUserInput();
+
   return LoadImage(href, aForce, aNotify, eImageLoadType_Normal);
 }
 
 //----------------------------------------------------------------------
 // EventTarget methods:
 
 void
 SVGImageElement::AsyncEventRunning(AsyncEventDispatcher* aEvent)
--- a/image/imgLoader.cpp
+++ b/image/imgLoader.cpp
@@ -25,16 +25,17 @@
 #include "nsContentUtils.h"
 #include "nsNetUtil.h"
 #include "nsNetCID.h"
 #include "nsIProtocolHandler.h"
 #include "nsMimeTypes.h"
 #include "nsStreamUtils.h"
 #include "nsIHttpChannel.h"
 #include "nsICacheInfoChannel.h"
+#include "nsIClassOfService.h"
 #include "nsIInterfaceRequestor.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsIProgressEventSink.h"
 #include "nsIChannelEventSink.h"
 #include "nsIAsyncVerifyRedirectCallback.h"
 #include "nsIFileURL.h"
 #include "nsIFile.h"
 #include "nsCRT.h"
@@ -2084,16 +2085,17 @@ imgLoader::LoadImageXPCOM(nsIURI* aURI,
                             aLoadGroup,
                             aObserver,
                             node,
                             doc,
                             aLoadFlags,
                             aCacheKey,
                             aContentPolicyType,
                             EmptyString(),
+                            /* aUseUrgentStartForChannel */ false,
                             &proxy);
     *_retval = proxy;
     return rv;
 }
 
 nsresult
 imgLoader::LoadImage(nsIURI* aURI,
                      nsIURI* aInitialDocumentURI,
@@ -2103,16 +2105,17 @@ imgLoader::LoadImage(nsIURI* aURI,
                      nsILoadGroup* aLoadGroup,
                      imgINotificationObserver* aObserver,
                      nsINode *aContext,
                      nsIDocument* aLoadingDocument,
                      nsLoadFlags aLoadFlags,
                      nsISupports* aCacheKey,
                      nsContentPolicyType aContentPolicyType,
                      const nsAString& initiatorType,
+                     bool aUseUrgentStartForChannel,
                      imgRequestProxy** _retval)
 {
   MOZ_RELEASE_ASSERT(NS_IsMainThread());
 
   VerifyCacheSizes();
 
   NS_ASSERTION(aURI, "imgLoader::LoadImage -- NULL URI pointer");
 
@@ -2272,16 +2275,21 @@ imgLoader::LoadImage(nsIURI* aURI,
     NewRequestAndEntry(forcePrincipalCheck, this, key,
                        getter_AddRefs(request),
                        getter_AddRefs(entry));
 
     MOZ_LOG(gImgLog, LogLevel::Debug,
            ("[this=%p] imgLoader::LoadImage -- Created new imgRequest"
             " [request=%p]\n", this, request.get()));
 
+    nsCOMPtr<nsIClassOfService> cos(do_QueryInterface(newChannel));
+    if (cos && aUseUrgentStartForChannel) {
+      cos->AddClassFlags(nsIClassOfService::UrgentStart);
+    }
+
     nsCOMPtr<nsILoadGroup> channelLoadGroup;
     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;
     }
--- a/image/imgLoader.h
+++ b/image/imgLoader.h
@@ -302,16 +302,17 @@ public:
                                   nsILoadGroup* aLoadGroup,
                                   imgINotificationObserver* aObserver,
                                   nsINode* aContext,
                                   nsIDocument* aLoadingDocument,
                                   nsLoadFlags aLoadFlags,
                                   nsISupports* aCacheKey,
                                   nsContentPolicyType aContentPolicyType,
                                   const nsAString& initiatorType,
+                                  bool aUseUrgentStartForChannel,
                                   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
@@ -2225,16 +2225,17 @@ nsImageFrame::LoadIcon(const nsAString& 
                        loadGroup,
                        gIconLoad,
                        nullptr,      /* No context */
                        nullptr,      /* Not associated with any particular document */
                        loadFlags,
                        nullptr,
                        contentPolicyType,
                        EmptyString(),
+                       false,        /* aUseUrgentStartForChannel */
                        aRequest);
 }
 
 void
 nsImageFrame::GetDocumentCharacterSet(nsACString& aCharset) const
 {
   if (mContent) {
     NS_ASSERTION(mContent->GetComposedDoc(),
--- a/widget/cocoa/nsMenuItemIconX.mm
+++ b/widget/cocoa/nsMenuItemIconX.mm
@@ -315,16 +315,17 @@ nsMenuItemIconX::LoadIcon(nsIURI* aIconU
       [mNativeMenuItem setImage:sPlaceholderIconImage];
   }
 
   nsresult rv = loader->LoadImage(aIconURI, nullptr, nullptr,
                                   mozilla::net::RP_Unset,
                                   mLoadingPrincipal, loadGroup, this,
                                   mContent, document, nsIRequest::LOAD_NORMAL, nullptr,
                                   mContentType, EmptyString(),
+                                  /* aUseUrgentStartForChannel */ false,
                                   getter_AddRefs(mIconRequest));
   if (NS_FAILED(rv)) return rv;
 
   return NS_OK;
 
   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
 }