Issue #2736 - Part 5: Re-work img <srcset> attribute.

Use subject principal as triggering principal in <img> "srcset" attribute.
This commit is contained in:
Moonchild 2025-04-28 19:15:02 +02:00 • committed by roytam1
commit 2bdb9f3d85
5 changed files with 69 additions and 16 deletions

View file

@ -115,7 +115,8 @@ ResponsiveImageSelector::~ResponsiveImageSelector()
// http://www.whatwg.org/specs/web-apps/current-work/#processing-the-image-candidates // http://www.whatwg.org/specs/web-apps/current-work/#processing-the-image-candidates
bool bool
ResponsiveImageSelector::SetCandidatesFromSourceSet(const nsAString & aSrcSet) ResponsiveImageSelector::SetCandidatesFromSourceSet(const nsAString & aSrcSet,
nsIPrincipal* aTriggeringPrincipal)
{ {
ClearSelectedCandidate(); ClearSelectedCandidate();
@ -167,6 +168,8 @@ ResponsiveImageSelector::SetCandidatesFromSourceSet(const nsAString & aSrcSet)
ResponsiveImageCandidate candidate; ResponsiveImageCandidate candidate;
if (candidate.ConsumeDescriptors(iter, end)) { if (candidate.ConsumeDescriptors(iter, end)) {
candidate.SetURLSpec(urlStr); candidate.SetURLSpec(urlStr);
candidate.SetTriggeringPrincipal(nsContentUtils::GetAttrTriggeringPrincipal(
Content(), urlStr, aTriggeringPrincipal));
AppendCandidateIfUnique(candidate); AppendCandidateIfUnique(candidate);
} }
} }
@ -207,7 +210,8 @@ ResponsiveImageSelector::Document()
} }
void void
ResponsiveImageSelector::SetDefaultSource(const nsAString& aURLString) ResponsiveImageSelector::SetDefaultSource(const nsAString& aURLString,
nsIPrincipal* aPrincipal)
{ {
ClearSelectedCandidate(); ClearSelectedCandidate();
@ -219,6 +223,7 @@ ResponsiveImageSelector::SetDefaultSource(const nsAString& aURLString)
} }
mDefaultSourceURL = aURLString; mDefaultSourceURL = aURLString;
mDefaultSourceTriggeringPrincipal = aPrincipal;
// Add new default to end of list // Add new default to end of list
MaybeAppendDefaultCandidate(); MaybeAppendDefaultCandidate();
@ -291,8 +296,9 @@ ResponsiveImageSelector::MaybeAppendDefaultCandidate()
ResponsiveImageCandidate defaultCandidate; ResponsiveImageCandidate defaultCandidate;
defaultCandidate.SetParameterDefault(); defaultCandidate.SetParameterDefault();
defaultCandidate.SetURLSpec(mDefaultSourceURL); defaultCandidate.SetURLSpec(mDefaultSourceURL);
defaultCandidate.SetTriggeringPrincipal(mDefaultSourceTriggeringPrincipal);
// We don't use MaybeAppend since we want to keep this even if it can never // We don't use MaybeAppend since we want to keep this even if it can never
// match, as it may if the source set changes. // match, because as it may match if the source set changes dynamically.
mCandidates.AppendElement(defaultCandidate); mCandidates.AppendElement(defaultCandidate);
} }
@ -329,6 +335,17 @@ ResponsiveImageSelector::GetSelectedImageDensity()
return mCandidates[bestIndex].Density(this); return mCandidates[bestIndex].Density(this);
} }
nsIPrincipal*
ResponsiveImageSelector::GetSelectedImageTriggeringPrincipal()
{
int bestIndex = GetSelectedCandidateIndex();
if (bestIndex < 0) {
return nullptr;
}
return mCandidates[bestIndex].TriggeringPrincipal();
}
bool bool
ResponsiveImageSelector::SelectImage(bool aReselect) ResponsiveImageSelector::SelectImage(bool aReselect)
{ {
@ -457,8 +474,10 @@ ResponsiveImageCandidate::ResponsiveImageCandidate()
} }
ResponsiveImageCandidate::ResponsiveImageCandidate(const nsAString& aURLString, ResponsiveImageCandidate::ResponsiveImageCandidate(const nsAString& aURLString,
double aDensity) double aDensity,
nsIPrincipal* aTriggeringPrincipal)
: mURLString(aURLString) : mURLString(aURLString)
, mTriggeringPrincipal(aTriggeringPrincipal)
{ {
mType = eCandidateType_Density; mType = eCandidateType_Density;
mValue.mDensity = aDensity; mValue.mDensity = aDensity;
@ -471,6 +490,12 @@ ResponsiveImageCandidate::SetURLSpec(const nsAString& aURLString)
mURLString = aURLString; mURLString = aURLString;
} }
void
ResponsiveImageCandidate::SetTriggeringPrincipal(nsIPrincipal* aPrincipal)
{
mTriggeringPrincipal = aPrincipal;
}
void void
ResponsiveImageCandidate::SetParameterAsComputedWidth(int32_t aWidth) ResponsiveImageCandidate::SetParameterAsComputedWidth(int32_t aWidth)
{ {
@ -717,6 +742,12 @@ ResponsiveImageCandidate::URLString() const
return mURLString; return mURLString;
} }
nsIPrincipal*
ResponsiveImageCandidate::TriggeringPrincipal() const
{
return mTriggeringPrincipal;
}
double double
ResponsiveImageCandidate::Density(ResponsiveImageSelector *aSelector) const ResponsiveImageCandidate::Density(ResponsiveImageSelector *aSelector) const
{ {

View file

@ -44,14 +44,16 @@ public:
// Given a srcset string, parse and replace current candidates (does not // Given a srcset string, parse and replace current candidates (does not
// replace default source) // replace default source)
bool SetCandidatesFromSourceSet(const nsAString & aSrcSet); bool SetCandidatesFromSourceSet(const nsAString & aSrcSet,
nsIPrincipal* aTriggeringPrincipal = nullptr);
// Fill the source sizes from a valid sizes descriptor. Returns false if // Fill the source sizes from a valid sizes descriptor. Returns false if
// descriptor is invalid. // descriptor is invalid.
bool SetSizesFromDescriptor(const nsAString & aSizesDescriptor); bool SetSizesFromDescriptor(const nsAString & aSizesDescriptor);
// Set the default source, treated as the least-precedence 1.0 density source. // Set the default source, treated as the least-precedence 1.0 density source.
void SetDefaultSource(const nsAString& aURLString); void SetDefaultSource(const nsAString& aURLString,
nsIPrincipal* aPrincipal = nullptr);
uint32_t NumCandidates(bool aIncludeDefault = true); uint32_t NumCandidates(bool aIncludeDefault = true);
@ -69,6 +71,7 @@ public:
// Returns false if there is no selected image // Returns false if there is no selected image
bool GetSelectedImageURLSpec(nsAString& aResult); bool GetSelectedImageURLSpec(nsAString& aResult);
double GetSelectedImageDensity(); double GetSelectedImageDensity();
nsIPrincipal* GetSelectedImageTriggeringPrincipal();
// Runs image selection now if necessary. If an image has already // Runs image selection now if necessary. If an image has already
// been choosen, takes no action unless aReselect is true. // been choosen, takes no action unless aReselect is true.
@ -107,6 +110,7 @@ private:
nsCOMPtr<nsINode> mOwnerNode; nsCOMPtr<nsINode> mOwnerNode;
// The cached URL for default candidate. // The cached URL for default candidate.
nsString mDefaultSourceURL; nsString mDefaultSourceURL;
nsCOMPtr<nsIPrincipal> mDefaultSourceTriggeringPrincipal;
// If this array contains an eCandidateType_Default, it should be the last // If this array contains an eCandidateType_Default, it should be the last
// element, such that the Setters can preserve/replace it respectively. // element, such that the Setters can preserve/replace it respectively.
nsTArray<ResponsiveImageCandidate> mCandidates; nsTArray<ResponsiveImageCandidate> mCandidates;
@ -122,9 +126,11 @@ private:
class ResponsiveImageCandidate { class ResponsiveImageCandidate {
public: public:
ResponsiveImageCandidate(); ResponsiveImageCandidate();
ResponsiveImageCandidate(const nsAString& aURLString, double aDensity); ResponsiveImageCandidate(const nsAString& aURLString, double aDensity,
nsIPrincipal* aTriggeringPrincipal = nullptr);
void SetURLSpec(const nsAString& aURLString); void SetURLSpec(const nsAString& aURLString);
void SetTriggeringPrincipal(nsIPrincipal* aPrincipal);
// Set this as a default-candidate. This behaves the same as density 1.0, but // Set this as a default-candidate. This behaves the same as density 1.0, but
// has a differing type such that it can be replaced by subsequent // has a differing type such that it can be replaced by subsequent
// SetDefaultSource calls. // SetDefaultSource calls.
@ -147,6 +153,7 @@ public:
bool HasSameParameter(const ResponsiveImageCandidate & aOther) const; bool HasSameParameter(const ResponsiveImageCandidate & aOther) const;
const nsAString& URLString() const; const nsAString& URLString() const;
nsIPrincipal* TriggeringPrincipal() const;
// Compute and return the density relative to a selector. // Compute and return the density relative to a selector.
double Density(ResponsiveImageSelector *aSelector) const; double Density(ResponsiveImageSelector *aSelector) const;
@ -171,6 +178,7 @@ public:
private: private:
nsString mURLString; nsString mURLString;
nsCOMPtr<nsIPrincipal> mTriggeringPrincipal;
eCandidateType mType; eCandidateType mType;
union { union {
double mDensity; double mDensity;

View file

@ -430,6 +430,7 @@ HTMLImageElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
} }
} else if (aName == nsGkAtoms::srcset && } else if (aName == nsGkAtoms::srcset &&
aNameSpaceID == kNameSpaceID_None) { aNameSpaceID == kNameSpaceID_None) {
mSrcsetTriggeringPrincipal = aMaybeScriptedPrincipal;
PictureSourceSrcsetChanged(this, attrVal.String(), aNotify); PictureSourceSrcsetChanged(this, attrVal.String(), aNotify);
} else if (aName == nsGkAtoms::sizes && } else if (aName == nsGkAtoms::sizes &&
aNameSpaceID == kNameSpaceID_None) { aNameSpaceID == kNameSpaceID_None) {
@ -481,7 +482,7 @@ HTMLImageElement::AfterMaybeChangeAttr(int32_t aNamespaceID, nsIAtom* aName,
if (InResponsiveMode()) { if (InResponsiveMode()) {
if (mResponsiveSelector && if (mResponsiveSelector &&
mResponsiveSelector->Content() == this) { mResponsiveSelector->Content() == this) {
mResponsiveSelector->SetDefaultSource(aValue.String()); mResponsiveSelector->SetDefaultSource(aValue.String(), mSrcTriggeringPrincipal);
} }
QueueImageLoadTask(true); QueueImageLoadTask(true);
} else if (aNotify && OwnerDoc()->IsCurrentActiveDocument()) { } else if (aNotify && OwnerDoc()->IsCurrentActiveDocument()) {
@ -1000,13 +1001,14 @@ HTMLImageElement::LoadSelectedImage(bool aForce, bool aNotify, bool aAlwaysLoad)
double currentDensity = 1.0; // default to 1.0 for the src attribute case double currentDensity = 1.0; // default to 1.0 for the src attribute case
if (mResponsiveSelector) { if (mResponsiveSelector) {
nsCOMPtr<nsIURI> url = mResponsiveSelector->GetSelectedImageURL(); nsCOMPtr<nsIURI> url = mResponsiveSelector->GetSelectedImageURL();
nsCOMPtr<nsIPrincipal> triggeringPrincipal = mResponsiveSelector->GetSelectedImageTriggeringPrincipal();
selectedSource = url; selectedSource = url;
currentDensity = mResponsiveSelector->GetSelectedImageDensity(); currentDensity = mResponsiveSelector->GetSelectedImageDensity();
if (!aAlwaysLoad && SelectedSourceMatchesLast(selectedSource, currentDensity)) { if (!aAlwaysLoad && SelectedSourceMatchesLast(selectedSource, currentDensity)) {
return NS_OK; return NS_OK;
} }
if (url) { if (url) {
rv = LoadImage(url, aForce, aNotify, eImageLoadType_Imageset); rv = LoadImage(url, aForce, aNotify, eImageLoadType_Imageset, triggeringPrincipal);
} }
} else { } else {
nsAutoString src; nsAutoString src;
@ -1055,7 +1057,11 @@ HTMLImageElement::PictureSourceSrcsetChanged(nsIContent *aSourceNode,
if (aSourceNode == currentSrc) { if (aSourceNode == currentSrc) {
// We're currently using this node as our responsive selector // We're currently using this node as our responsive selector
// source. // source.
mResponsiveSelector->SetCandidatesFromSourceSet(aNewValue); nsCOMPtr<nsIPrincipal> principal;
if (aSourceNode == this) {
principal = mSrcsetTriggeringPrincipal;
}
mResponsiveSelector->SetCandidatesFromSourceSet(aNewValue, principal);
} }
if (!mInDocResponsiveContent && IsInComposedDoc()) { if (!mInDocResponsiveContent && IsInComposedDoc()) {
@ -1240,6 +1246,8 @@ HTMLImageElement::SourceElementMatches(nsIContent* aSourceNode)
bool bool
HTMLImageElement::TryCreateResponsiveSelector(nsIContent *aSourceNode) HTMLImageElement::TryCreateResponsiveSelector(nsIContent *aSourceNode)
{ {
nsCOMPtr<nsIPrincipal> principal;
// Skip if this is not a <source> with matching media query // Skip if this is not a <source> with matching media query
bool isSourceTag = aSourceNode->IsHTMLElement(nsGkAtoms::source); bool isSourceTag = aSourceNode->IsHTMLElement(nsGkAtoms::source);
if (isSourceTag) { if (isSourceTag) {
@ -1249,6 +1257,7 @@ HTMLImageElement::TryCreateResponsiveSelector(nsIContent *aSourceNode)
} else if (aSourceNode->IsHTMLElement(nsGkAtoms::img)) { } else if (aSourceNode->IsHTMLElement(nsGkAtoms::img)) {
// Otherwise this is the <img> tag itself // Otherwise this is the <img> tag itself
MOZ_ASSERT(aSourceNode == this); MOZ_ASSERT(aSourceNode == this);
principal = mSrcsetTriggeringPrincipal;
} }
// Skip if has no srcset or an empty srcset // Skip if has no srcset or an empty srcset
@ -1264,8 +1273,8 @@ HTMLImageElement::TryCreateResponsiveSelector(nsIContent *aSourceNode)
// Try to parse // Try to parse
RefPtr<ResponsiveImageSelector> sel = new ResponsiveImageSelector(aSourceNode); RefPtr<ResponsiveImageSelector> sel = new ResponsiveImageSelector(aSourceNode);
if (!sel->SetCandidatesFromSourceSet(srcset)) { if (!sel->SetCandidatesFromSourceSet(srcset, principal)) {
// No possible candidates, don't need to bother parsing sizes // No possible candidates; no need to bother parsing sizes
return false; return false;
} }
@ -1278,7 +1287,7 @@ HTMLImageElement::TryCreateResponsiveSelector(nsIContent *aSourceNode)
MOZ_ASSERT(aSourceNode == this); MOZ_ASSERT(aSourceNode == this);
nsAutoString src; nsAutoString src;
if (GetAttr(kNameSpaceID_None, nsGkAtoms::src, src) && !src.IsEmpty()) { if (GetAttr(kNameSpaceID_None, nsGkAtoms::src, src) && !src.IsEmpty()) {
sel->SetDefaultSource(src); sel->SetDefaultSource(src, mSrcTriggeringPrincipal);
} }
} }

View file

@ -145,9 +145,13 @@ public:
{ {
SetHTMLAttr(nsGkAtoms::src, aSrc, aTriggeringPrincipal, aError); SetHTMLAttr(nsGkAtoms::src, aSrc, aTriggeringPrincipal, aError);
} }
void SetSrcset(const nsAString& aSrcset, ErrorResult& aError) void GetSrcset(nsAString& aSrcset, nsIPrincipal&)
{ {
SetHTMLAttr(nsGkAtoms::srcset, aSrcset, aError); GetHTMLAttr(nsGkAtoms::srcset, aSrcset);
}
void SetSrcset(const nsAString& aSrcset, nsIPrincipal& aTriggeringPrincipal, ErrorResult& aError)
{
SetHTMLAttr(nsGkAtoms::srcset, aSrcset, aTriggeringPrincipal, aError);
} }
void GetCrossOrigin(nsAString& aResult) void GetCrossOrigin(nsAString& aResult)
{ {
@ -387,6 +391,7 @@ private:
bool mInDocResponsiveContent; bool mInDocResponsiveContent;
RefPtr<ImageLoadTask> mPendingImageLoadTask; RefPtr<ImageLoadTask> mPendingImageLoadTask;
nsCOMPtr<nsIPrincipal> mSrcTriggeringPrincipal; nsCOMPtr<nsIPrincipal> mSrcTriggeringPrincipal;
nsCOMPtr<nsIPrincipal> mSrcsetTriggeringPrincipal;
// Last URL that was attempted to load by this element. // Last URL that was attempted to load by this element.
nsCOMPtr<nsIURI> mLastSelectedSource; nsCOMPtr<nsIURI> mLastSelectedSource;

View file

@ -23,7 +23,7 @@ interface HTMLImageElement : HTMLElement {
attribute DOMString alt; attribute DOMString alt;
[CEReactions, NeedsSubjectPrincipal, SetterThrows] [CEReactions, NeedsSubjectPrincipal, SetterThrows]
attribute DOMString src; attribute DOMString src;
[CEReactions, SetterThrows] [CEReactions, NeedsSubjectPrincipal, SetterThrows]
attribute DOMString srcset; attribute DOMString srcset;
[CEReactions, SetterThrows] [CEReactions, SetterThrows]
attribute DOMString? crossOrigin; attribute DOMString? crossOrigin;