From bca1dd9ab34bc556b6070b021f0084f262cab206 Mon Sep 17 00:00:00 2001 From: John Schoenick Date: Fri, 2 May 2014 16:26:22 -0700 Subject: [PATCH] Bug 870021 - Part 3 - Support basic srcset in HTMLImageElement via ResponsiveSelector. r=jst --- content/html/content/src/HTMLImageElement.cpp | 153 +++++++++++++++--- content/html/content/src/HTMLImageElement.h | 15 ++ 2 files changed, 145 insertions(+), 23 deletions(-) diff --git a/content/html/content/src/HTMLImageElement.cpp b/content/html/content/src/HTMLImageElement.cpp index 1ef95559fcf..b4d41e1db7b 100644 --- a/content/html/content/src/HTMLImageElement.cpp +++ b/content/html/content/src/HTMLImageElement.cpp @@ -41,6 +41,9 @@ #include "nsLayoutUtils.h" +#include "mozilla/Preferences.h" +static const char *kPrefSrcsetEnabled = "dom.image.srcset.enabled"; + NS_IMPL_NS_NEW_HTML_ELEMENT(Image) namespace mozilla { @@ -90,6 +93,12 @@ NS_IMPL_STRING_ATTR(HTMLImageElement, Srcset, srcset) NS_IMPL_STRING_ATTR(HTMLImageElement, UseMap, usemap) NS_IMPL_INT_ATTR(HTMLImageElement, Vspace, vspace) +bool +HTMLImageElement::IsSrcsetEnabled() +{ + return Preferences::GetBool(kPrefSrcsetEnabled, false); +} + void HTMLImageElement::GetItemValueText(nsAString& aValue) { @@ -318,23 +327,37 @@ HTMLImageElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName, nsDependentAtomString(aValue->GetAtomValue())); } - if (aNameSpaceID == kNameSpaceID_None && - aName == nsGkAtoms::src && - !aValue) { - CancelImageRequests(aNotify); - } - - // 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. Skip the LoadImage call in that case. - if (aNotify && - aNameSpaceID == kNameSpaceID_None && - aName == nsGkAtoms::crossorigin) { + // Handle src/srcset/crossorigin 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. + if (aName == nsGkAtoms::src && + aNameSpaceID == kNameSpaceID_None) { + // SetAttr handles setting src in the non-responsive case, so only handle it + // for responsive mode or unsetting + if (!aValue) { + CancelImageRequests(aNotify); + } else if (mResponsiveSelector) { + mResponsiveSelector->SetDefaultSource(aValue ? aValue->GetStringValue() + : EmptyString()); + LoadSelectedImage(false, aNotify); + } + } else if (aName == nsGkAtoms::srcset && + aNameSpaceID == kNameSpaceID_None && + aNotify && + AsContent()->IsInDoc() && + IsSrcsetEnabled()) { + // We currently don't handle responsive mode until BindToTree + UpdateSourceSet(aValue->GetStringValue()); + LoadSelectedImage(false, aNotify); + } else if (aName == nsGkAtoms::crossorigin && + aNameSpaceID == kNameSpaceID_None && + aNotify) { // We want aForce == true in this LoadImage call, because we want to force // a new load of the image with the new cross origin policy. - nsAutoString uri; - GetAttr(kNameSpaceID_None, nsGkAtoms::src, uri); - LoadImage(uri, true, aNotify); + nsCOMPtr currentURI; + if (NS_SUCCEEDED(GetCurrentURI(getter_AddRefs(currentURI))) && currentURI) { + LoadImage(currentURI, true, aNotify); + } } return nsGenericHTMLElement::AfterSetAttr(aNameSpaceID, aName, @@ -412,7 +435,10 @@ HTMLImageElement::SetAttr(int32_t aNameSpaceID, nsIAtom* aName, // 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. Skip the LoadImage call in that case. - if (aNotify && + // + // If we are in responsive mode, we drop the forced reload behavior, and + // handle updates in AfterSetAttr + if (aNotify && !mResponsiveSelector && aNameSpaceID == kNameSpaceID_None && aName == nsGkAtoms::src) { @@ -455,11 +481,27 @@ HTMLImageElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent, UpdateFormOwner(); } - if (HasAttr(kNameSpaceID_None, nsGkAtoms::src)) { + bool haveSrcset = IsSrcsetEnabled() && + HasAttr(kNameSpaceID_None, nsGkAtoms::srcset); + if (haveSrcset || HasAttr(kNameSpaceID_None, nsGkAtoms::src)) { // 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 don't handle responsive changes when not bound to a tree, update them + // now if necessary + if (haveSrcset) { + nsAutoString srcset; + GetAttr(kNameSpaceID_None, nsGkAtoms::srcset, srcset); + UpdateSourceSet(srcset); + if (mResponsiveSelector) { + nsAutoString src; + GetAttr(kNameSpaceID_None, nsGkAtoms::src, src); + mResponsiveSelector->SetDefaultSource(src); + } + } + // If loading is temporarily disabled, don't even launch MaybeLoadImage. // Otherwise MaybeLoadImage may run later when someone has reenabled // loading. @@ -483,6 +525,8 @@ HTMLImageElement::UnbindFromTree(bool aDeep, bool aNullParent) } } + mResponsiveSelector = nullptr; + nsImageLoadingContent::UnbindFromTree(aDeep, aNullParent); nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent); } @@ -517,13 +561,15 @@ HTMLImageElement::UpdateFormOwner() void HTMLImageElement::MaybeLoadImage() { - // Our base URI may have changed; claim that our URI changed, and the - // nsImageLoadingContent will decide whether a new image load is warranted. + // Our base URI may have changed, or we may have had responsive parameters + // change while not bound to the tree. Re-parse src/srcset and call LoadImage, + // which is a no-op if it resolves to the same effective URI without aForce. + // Note, check LoadingEnabled() after LoadImage call. - nsAutoString uri; - if (GetAttr(kNameSpaceID_None, nsGkAtoms::src, uri) && - (NS_FAILED(LoadImage(uri, false, true)) || - !LoadingEnabled())) { + + nsresult rv = LoadSelectedImage(false, true); + + if (NS_FAILED(rv) || !LoadingEnabled()) { CancelImageRequests(true); } } @@ -704,5 +750,66 @@ HTMLImageElement::ClearForm(bool aRemoveFromForm) mForm = nullptr; } +nsresult +HTMLImageElement::LoadSelectedImage(bool aForce, bool aNotify) +{ + nsresult rv = NS_ERROR_FAILURE; + + if (mResponsiveSelector) { + nsCOMPtr url = mResponsiveSelector->GetSelectedImageURL(); + if (url) { + rv = LoadImage(url, aForce, aNotify); + } else { + CancelImageRequests(aNotify); + rv = NS_OK; + } + } else { + nsAutoString src; + if (!GetAttr(kNameSpaceID_None, nsGkAtoms::src, src)) { + CancelImageRequests(aNotify); + rv = NS_OK; + } else { + rv = LoadImage(src, aForce, aNotify); + if (NS_FAILED(rv)) { + CancelImageRequests(aNotify); + } + } + } + + return rv; +} + +void +HTMLImageElement::DestroyContent() +{ + mResponsiveSelector = nullptr; +} + +void +HTMLImageElement::UpdateSourceSet(const nsAString & aSrcset) +{ + MOZ_ASSERT(IsSrcsetEnabled()); + + bool haveSrcset = !aSrcset.IsEmpty(); + + if (haveSrcset && !mResponsiveSelector) { + mResponsiveSelector = new ResponsiveImageSelector(this); + mResponsiveSelector->SetCandidatesFromSourceSet(aSrcset); + + // src may have been set before we decided we were responsive + nsAutoString src; + if (GetAttr(kNameSpaceID_None, nsGkAtoms::src, src) && src.Length()) { + mResponsiveSelector->SetDefaultSource(src); + } + + } else if (haveSrcset) { + mResponsiveSelector->SetCandidatesFromSourceSet(aSrcset); + } else if (mResponsiveSelector) { + // Clearing srcset, don't need responsive selector anymore + mResponsiveSelector = nullptr; + } +} + } // namespace dom } // namespace mozilla + diff --git a/content/html/content/src/HTMLImageElement.h b/content/html/content/src/HTMLImageElement.h index 9c8dcd4e912..ee46b0d87d1 100644 --- a/content/html/content/src/HTMLImageElement.h +++ b/content/html/content/src/HTMLImageElement.h @@ -12,6 +12,7 @@ #include "nsIDOMHTMLImageElement.h" #include "imgRequestProxy.h" #include "Units.h" +#include "mozilla/dom/ResponsiveImageSelector.h" namespace mozilla { class EventChainPreVisitor; @@ -79,6 +80,8 @@ public: void MaybeLoadImage(); + static bool IsSrcsetEnabled(); + bool IsMap() { return GetBoolAttr(nsGkAtoms::ismap); @@ -175,7 +178,16 @@ public: void SetForm(nsIDOMHTMLFormElement* aForm); void ClearForm(bool aRemoveFromForm); + virtual void DestroyContent() MOZ_OVERRIDE; + protected: + // Resolve and load the current mResponsiveSelector (responsive mode) or src + // attr image. + nsresult LoadSelectedImage(bool aForce, bool aNotify); + + // Update/create/destroy mResponsiveSelector + void UpdateSourceSet(const nsAString & aSrcset); + CSSIntPoint GetXY(); virtual void GetItemValueText(nsAString& text) MOZ_OVERRIDE; virtual void SetItemValueText(const nsAString& text) MOZ_OVERRIDE; @@ -193,6 +205,9 @@ protected: // cooperate in maintaining. HTMLFormElement* mForm; + // Created when we're tracking responsive image state + nsRefPtr mResponsiveSelector; + private: static void MapAttributesIntoRule(const nsMappedAttributes* aAttributes, nsRuleData* aData);