Bug 1370705 - Move attribute change effects from HTMLImageElement::BeforeMaybeChangeAttr to HTMLImageElement::AfterMaybeChangeAttr draft
authorKirk Steuber <ksteuber@mozilla.com>
Fri, 09 Jun 2017 09:46:54 -0700
changeset 592724 4f5140e9c4a85cfc652850a243f8637e939f5d76
parent 591816 2d20b365eee19434657f6b365d310e8b70904d2b
child 632927 64b743bce50f51c2d0626229ae1952c9ff565fb8
push id63500
push userksteuber@mozilla.com
push dateMon, 12 Jun 2017 19:22:03 +0000
bugs1370705
milestone55.0a1
Bug 1370705 - Move attribute change effects from HTMLImageElement::BeforeMaybeChangeAttr to HTMLImageElement::AfterMaybeChangeAttr It logically makes more sense for these effects to happen after the attribute has actually been changed and moving them allows us to get rid of the member variable HTMLImageElement::mForceReload. MozReview-Commit-ID: IJBF3AHVb0U
dom/base/Element.cpp
dom/base/Element.h
dom/html/HTMLImageElement.cpp
dom/html/HTMLImageElement.h
--- a/dom/base/Element.cpp
+++ b/dom/base/Element.cpp
@@ -4025,19 +4025,26 @@ Element::FontSizeInflation()
   return 1.0;
 }
 
 net::ReferrerPolicy
 Element::GetReferrerPolicyAsEnum()
 {
   if (IsHTMLElement()) {
     const nsAttrValue* referrerValue = GetParsedAttr(nsGkAtoms::referrerpolicy);
-    if (referrerValue && referrerValue->Type() == nsAttrValue::eEnum) {
-      return net::ReferrerPolicy(referrerValue->GetEnumValue());
-    }
+    return ReferrerPolicyFromAttr(referrerValue);
+  }
+  return net::RP_Unset;
+}
+
+net::ReferrerPolicy
+Element::ReferrerPolicyFromAttr(const nsAttrValue* aValue)
+{
+  if (aValue && aValue->Type() == nsAttrValue::eEnum) {
+    return net::ReferrerPolicy(aValue->GetEnumValue());
   }
   return net::RP_Unset;
 }
 
 already_AddRefed<nsDOMStringMap>
 Element::Dataset()
 {
   nsDOMSlots *slots = DOMSlots();
--- a/dom/base/Element.h
+++ b/dom/base/Element.h
@@ -1332,16 +1332,17 @@ public:
    *
    * @note The font size inflation ratio that is returned is actually the
    *       font size inflation data for the element's _primary frame_, not the
    *       element itself, but for most purposes, this should be sufficient.
    */
   float FontSizeInflation();
 
   net::ReferrerPolicy GetReferrerPolicyAsEnum();
+  net::ReferrerPolicy ReferrerPolicyFromAttr(const nsAttrValue* aValue);
 
   /*
    * Helpers for .dataset.  This is implemented on Element, though only some
    * sorts of elements expose it to JS as a .dataset property
    */
   // Getter, to be called from bindings.
   already_AddRefed<nsDOMStringMap> Dataset();
   // Callback for destructor of dataset to ensure to null out our weak pointer
--- a/dom/html/HTMLImageElement.cpp
+++ b/dom/html/HTMLImageElement.cpp
@@ -113,17 +113,16 @@ private:
   // 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)
-  , mForceReload(false)
   , mInDocResponsiveContent(false)
   , mCurrentDensity(1.0)
 {
   // We start out broken
   AddStatesSilently(NS_EVENT_STATE_BROKEN);
 }
 
 HTMLImageElement::~HTMLImageElement()
@@ -373,20 +372,16 @@ HTMLImageElement::GetAttributeMappingFun
   return &MapAttributesIntoRule;
 }
 
 nsresult
 HTMLImageElement::BeforeSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
                                 const nsAttrValueOrString* aValue,
                                 bool aNotify)
 {
-  if (aValue) {
-    BeforeMaybeChangeAttr(aNameSpaceID, aName, *aValue, aNotify);
-  }
-
   if (aNameSpaceID == kNameSpaceID_None && mForm &&
       (aName == nsGkAtoms::name || aName == nsGkAtoms::id)) {
     // remove the image from the hashtable as needed
     nsAutoString tmp;
     GetAttr(kNameSpaceID_None, aName, tmp);
 
     if (!tmp.IsEmpty()) {
       mForm->RemoveImageElementFromTable(this, tmp);
@@ -397,36 +392,37 @@ HTMLImageElement::BeforeSetAttr(int32_t 
                                              aValue, aNotify);
 }
 
 nsresult
 HTMLImageElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
                                const nsAttrValue* aValue,
                                const nsAttrValue* aOldValue, bool aNotify)
 {
+  nsAttrValueOrString attrVal(aValue);
+
   if (aValue) {
-    AfterMaybeChangeAttr(aNameSpaceID, aName, aNotify);
+    AfterMaybeChangeAttr(aNameSpaceID, aName, attrVal, aOldValue, true,
+                         aNotify);
   }
 
   if (aNameSpaceID == kNameSpaceID_None && mForm &&
       (aName == nsGkAtoms::name || aName == nsGkAtoms::id) &&
       aValue && !aValue->IsEmptyString()) {
     // add the image to the hashtable as needed
     MOZ_ASSERT(aValue->Type() == nsAttrValue::eAtom,
                "Expected atom value for name/id");
     mForm->AddImageElementToTable(this,
       nsDependentAtomString(aValue->GetAtomValue()));
   }
 
   // Handle src/srcset updates. If aNotify is false, we are coming from the
   // 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 =
@@ -461,41 +457,39 @@ HTMLImageElement::AfterSetAttr(int32_t a
                                             aValue, aOldValue, aNotify);
 }
 
 nsresult
 HTMLImageElement::OnAttrSetButNotChanged(int32_t aNamespaceID, nsIAtom* aName,
                                          const nsAttrValueOrString& aValue,
                                          bool aNotify)
 {
-  BeforeMaybeChangeAttr(aNamespaceID, aName, aValue, aNotify);
-  AfterMaybeChangeAttr(aNamespaceID, aName, aNotify);
+  AfterMaybeChangeAttr(aNamespaceID, aName, aValue, nullptr, false, aNotify);
 
   return nsGenericHTMLElement::OnAttrSetButNotChanged(aNamespaceID, aName,
                                                       aValue, aNotify);
 }
 
 void
-HTMLImageElement::BeforeMaybeChangeAttr(int32_t aNamespaceID, nsIAtom* aName,
-                                        const nsAttrValueOrString& aValue,
-                                        bool aNotify)
+HTMLImageElement::AfterMaybeChangeAttr(int32_t aNamespaceID, nsIAtom* aName,
+                                       const nsAttrValueOrString& aValue,
+                                       const nsAttrValue* aOldValue,
+                                       bool aValueMaybeChanged, bool aNotify)
 {
+  bool forceReload = false;
   // We need to force our image to reload.  This must be done here, not in
   // AfterSetAttr or BeforeSetAttr, because we want to do it even if the attr is
   // being set to its existing value, which is normally optimized away as a
   // no-op.
   //
   // 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
-  //
-  // Much of this should probably happen in AfterMaybeChangeAttr.
-  // See Bug 1370705
   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()) {
@@ -510,78 +504,71 @@ HTMLImageElement::BeforeMaybeChangeAttr(
       // sync image load from BindToTree. Skip the LoadImage call in that case.
 
       // Note that this sync behavior is partially removed from the spec, bug 1076583
 
       // A hack to get animations to reset. See bug 594771.
       mNewRequestsWillNeedAnimationReset = true;
 
       // Force image loading here, so that we'll try to load the image from
-      // network if it's set to be not cacheable...  If we change things so that
-      // the state gets in Element's attr-setting happen around this
-      // LoadImage call, we could start passing false instead of aNotify
-      // here.
+      // network if it's set to be not cacheable.
+      // Potentially, false could be passed here rather than aNotify since
+      // UpdateState will be called by SetAttrAndNotify, but there are two
+      // obstacles to this: 1) LoadImage will end up calling
+      // UpdateState(aNotify), and we do not want it to call UpdateState(false)
+      // when aNotify is true, and 2) When this function is called by
+      // OnAttrSetButNotChanged, SetAttrAndNotify will not subsequently call
+      // UpdateState.
       LoadImage(aValue.String(), true, aNotify, eImageLoadType_Normal);
 
       mNewRequestsWillNeedAnimationReset = false;
     }
   } else if (aNamespaceID == kNameSpaceID_None &&
              aName == nsGkAtoms::crossorigin &&
              aNotify) {
-    nsAttrValue attrValue;
-    ParseCORSValue(aValue.String(), attrValue);
-    if (GetCORSMode() != AttrValueToCORSMode(&attrValue)) {
+    if (aValueMaybeChanged && GetCORSMode() != AttrValueToCORSMode(aOldValue)) {
       // Force a new load of the image with the new cross origin policy.
-      mForceReload = true;
+      forceReload = true;
     }
   } else if (aName == nsGkAtoms::referrerpolicy &&
       aNamespaceID == kNameSpaceID_None &&
       aNotify) {
-    ReferrerPolicy referrerPolicy = AttributeReferrerPolicyFromString(aValue.String());
+    ReferrerPolicy referrerPolicy = GetImageReferrerPolicy();
     if (!InResponsiveMode() &&
         referrerPolicy != RP_Unset &&
-        referrerPolicy != GetImageReferrerPolicy()) {
+        aValueMaybeChanged &&
+        referrerPolicy != ReferrerPolicyFromAttr(aOldValue)) {
       // XXX: Bug 1076583 - We still use the older synchronous algorithm
       // Because referrerPolicy is not treated as relevant mutations, setting
       // the attribute will neither trigger a reload nor update the referrer
       // policy of the loading channel (whether it has previously completed or
       // not). Force a new load of the image with the new referrerpolicy.
-      mForceReload = true;
+      forceReload = true;
     }
   }
 
-  return;
-}
-
-void
-HTMLImageElement::AfterMaybeChangeAttr(int32_t aNamespaceID, nsIAtom* aName,
-                                       bool 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 (mForceReload) {
-    mForceReload = false;
+  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 if (OwnerDoc()->IsCurrentActiveDocument()) {
       // 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
       ForceReload(aNotify);
     }
   }
-
-  return;
 }
 
 nsresult
 HTMLImageElement::GetEventTargetParent(EventChainPreVisitor& aVisitor)
 {
   // We handle image element with attribute ismap in its corresponding frame
   // element. Set mMultipleActionsPrevented here to prevent the click event
   // trigger the behaviors in Element::PostHandleEventForLinks
--- a/dom/html/HTMLImageElement.h
+++ b/dom/html/HTMLImageElement.h
@@ -356,44 +356,35 @@ protected:
   RefPtr<ResponsiveImageSelector> mResponsiveSelector;
 
 private:
   bool SourceElementMatches(nsIContent* aSourceNode);
 
   static void MapAttributesIntoRule(const nsMappedAttributes* aAttributes,
                                     GenericSpecifiedValues* aGenericData);
   /**
-   * This function is called by BeforeSetAttr and OnAttrSetButNotChanged.
+   * This function is called by AfterSetAttr and OnAttrSetButNotChanged.
    * It will not be called if the value is being unset.
    *
    * @param aNamespaceID the namespace of the attr being set
    * @param aName the localname of the attribute being set
    * @param aValue the value it's being set to represented as either a string or
    *        a parsed nsAttrValue.
-   * @param aNotify Whether we plan to notify document observers.
-   */
-  void BeforeMaybeChangeAttr(int32_t aNamespaceID, nsIAtom* aName,
-                             const nsAttrValueOrString& aValue,
-                             bool aNotify);
-  /**
-   * This function is called by AfterSetAttr and OnAttrSetButNotChanged.
-   * It will not be called if the value is being unset.
-   *
-   * @param aNamespaceID the namespace of the attr being set
-   * @param aName the localname of the attribute being set
+   * @param aOldValue the value previously set. Will be null if no value was
+   *        previously set. This value should only be used when
+   *        aValueMaybeChanged is true; when aValueMaybeChanged is false,
+   *        aOldValue should be considered unreliable.
+   * @param aValueMaybeChanged will be false when this function is called from
+   *        OnAttrSetButNotChanged to indicate that the value was not changed.
    * @param aNotify Whether we plan to notify document observers.
    */
   void AfterMaybeChangeAttr(int32_t aNamespaceID, nsIAtom* aName,
-                            bool aNotify);
-  /**
-   * Used by BeforeMaybeChangeAttr and AfterMaybeChangeAttr to keep track of
-   * whether a reload needs to be forced after an attribute change that is
-   * currently in progress.
-   */
-  bool mForceReload;
+                            const nsAttrValueOrString& aValue,
+                            const nsAttrValue* aOldValue,
+                            bool aValueMaybeChanged, bool aNotify);
 
   bool mInDocResponsiveContent;
   RefPtr<ImageLoadTask> mPendingImageLoadTask;
 
   // Last URL that was attempted to load by this element.
   nsCOMPtr<nsIURI> mLastSelectedSource;
   // Last pixel density that was selected.
   double mCurrentDensity;