Bug 1240357 - Support crossOrigin attribute on image and feImage elements r=emilio

Differential Revision: https://phabricator.services.mozilla.com/D174737
This commit is contained in:
Robert Longson 2023-04-11 12:37:21 +00:00
parent cfb6408c17
commit 12236086e3
9 changed files with 152 additions and 20 deletions

View File

@ -101,6 +101,19 @@ void SVGFEImageElement::AsyncEventRunning(AsyncEventDispatcher* aEvent) {
//----------------------------------------------------------------------
// nsIContent methods:
bool SVGFEImageElement::ParseAttribute(int32_t aNamespaceID, nsAtom* aAttribute,
const nsAString& aValue,
nsIPrincipal* aMaybeScriptedPrincipal,
nsAttrValue& aResult) {
if (aNamespaceID == kNameSpaceID_None &&
aAttribute == nsGkAtoms::crossorigin) {
ParseCORSValue(aValue, aResult);
return true;
}
return SVGFEImageElementBase::ParseAttribute(
aNamespaceID, aAttribute, aValue, aMaybeScriptedPrincipal, aResult);
}
nsresult SVGFEImageElement::AfterSetAttr(int32_t aNamespaceID, nsAtom* aName,
const nsAttrValue* aValue,
const nsAttrValue* aOldValue,
@ -115,6 +128,12 @@ nsresult SVGFEImageElement::AfterSetAttr(int32_t aNamespaceID, nsAtom* aName,
} else {
CancelImageRequests(aNotify);
}
} else if (aNamespaceID == kNameSpaceID_None &&
aName == nsGkAtoms::crossorigin) {
if (aNotify && GetCORSMode() != AttrValueToCORSMode(aOldValue) &&
ShouldLoadImage()) {
ForceReload(aNotify, IgnoreErrors());
}
}
return SVGFEImageElementBase::AfterSetAttr(
@ -177,6 +196,13 @@ already_AddRefed<DOMSVGAnimatedString> SVGFEImageElement::Href() {
: mStringAttributes[XLINK_HREF].ToDOMAnimatedString(this);
}
//----------------------------------------------------------------------
// nsImageLoadingContent methods:
CORSMode SVGFEImageElement::GetCORSMode() {
return AttrValueToCORSMode(GetParsedAttr(nsGkAtoms::crossorigin));
}
//----------------------------------------------------------------------
// nsIDOMSVGFEImageElement methods

View File

@ -52,12 +52,20 @@ class SVGFEImageElement final : public SVGFEImageElementBase,
SVGAnimatedString& GetResultImageName() override {
return mStringAttributes[RESULT];
}
// nsImageLoadingContent
CORSMode GetCORSMode() override;
bool OutputIsTainted(const nsTArray<bool>& aInputsAreTainted,
nsIPrincipal* aReferencePrincipal) override;
// nsIContent
nsresult Clone(dom::NodeInfo*, nsINode** aResult) const override;
bool ParseAttribute(int32_t aNamespaceID, nsAtom* aAttribute,
const nsAString& aValue,
nsIPrincipal* aMaybeScriptedPrincipal,
nsAttrValue& aResult) override;
nsresult AfterSetAttr(int32_t aNamespaceID, nsAtom* aName,
const nsAttrValue* aValue, const nsAttrValue* aOldValue,
nsIPrincipal* aSubjectPrincipal, bool aNotify) override;
@ -76,6 +84,15 @@ class SVGFEImageElement final : public SVGFEImageElementBase,
// WebIDL
already_AddRefed<DOMSVGAnimatedString> Href();
already_AddRefed<DOMSVGAnimatedPreserveAspectRatio> PreserveAspectRatio();
void GetCrossOrigin(nsAString& aCrossOrigin) {
// Null for both missing and invalid defaults is ok, since we
// always parse to an enum value, so we don't need an invalid
// default, and we _want_ the missing default to be null.
GetEnumAttr(nsGkAtoms::crossorigin, nullptr, aCrossOrigin);
}
void SetCrossOrigin(const nsAString& aCrossOrigin, ErrorResult& aError) {
SetOrRemoveNullableStringAttr(nsGkAtoms::crossorigin, aCrossOrigin, aError);
}
private:
nsresult LoadSVGImage(bool aForce, bool aNotify);

View File

@ -174,6 +174,13 @@ void SVGImageElement::AsyncEventRunning(AsyncEventDispatcher* aEvent) {
nsImageLoadingContent::AsyncEventRunning(aEvent);
}
//----------------------------------------------------------------------
// nsImageLoadingContent methods:
CORSMode SVGImageElement::GetCORSMode() {
return AttrValueToCORSMode(GetParsedAttr(nsGkAtoms::crossorigin));
}
//----------------------------------------------------------------------
// nsIContent methods:
@ -182,6 +189,10 @@ bool SVGImageElement::ParseAttribute(int32_t aNamespaceID, nsAtom* aAttribute,
nsIPrincipal* aMaybeScriptedPrincipal,
nsAttrValue& aResult) {
if (aNamespaceID == kNameSpaceID_None) {
if (aAttribute == nsGkAtoms::crossorigin) {
ParseCORSValue(aValue, aResult);
return true;
}
if (aAttribute == nsGkAtoms::decoding) {
return aResult.ParseEnumValue(aValue, kDecodingTable, false,
kDecodingTableDefault);
@ -206,13 +217,20 @@ nsresult SVGImageElement::AfterSetAttr(int32_t aNamespaceID, nsAtom* aName,
} else {
CancelImageRequests(aNotify);
}
} else if (aName == nsGkAtoms::decoding &&
aNamespaceID == kNameSpaceID_None) {
// Request sync or async image decoding.
SetSyncDecodingHint(
aValue && static_cast<ImageDecodingType>(aValue->GetEnumValue()) ==
ImageDecodingType::Sync);
} else if (aNamespaceID == kNameSpaceID_None) {
if (aName == nsGkAtoms::decoding) {
// Request sync or async image decoding.
SetSyncDecodingHint(
aValue && static_cast<ImageDecodingType>(aValue->GetEnumValue()) ==
ImageDecodingType::Sync);
} else if (aName == nsGkAtoms::crossorigin) {
if (aNotify && GetCORSMode() != AttrValueToCORSMode(aOldValue) &&
ShouldLoadImage()) {
ForceReload(aNotify, IgnoreErrors());
}
}
}
return SVGImageElementBase::AfterSetAttr(
aNamespaceID, aName, aValue, aOldValue, aSubjectPrincipal, aNotify);
}

View File

@ -46,6 +46,9 @@ class SVGImageElement final : public SVGImageElementBase,
// EventTarget
void AsyncEventRunning(AsyncEventDispatcher* aEvent) override;
// nsImageLoadingContent interface
CORSMode GetCORSMode() override;
// nsIContent interface
bool ParseAttribute(int32_t aNamespaceID, nsAtom* aAttribute,
const nsAString& aValue,
@ -92,6 +95,15 @@ class SVGImageElement final : public SVGImageElementBase,
already_AddRefed<DOMSVGAnimatedLength> Height();
already_AddRefed<DOMSVGAnimatedPreserveAspectRatio> PreserveAspectRatio();
already_AddRefed<DOMSVGAnimatedString> Href();
void GetCrossOrigin(nsAString& aCrossOrigin) {
// Null for both missing and invalid defaults is ok, since we
// always parse to an enum value, so we don't need an invalid
// default, and we _want_ the missing default to be null.
GetEnumAttr(nsGkAtoms::crossorigin, nullptr, aCrossOrigin);
}
void SetCrossOrigin(const nsAString& aCrossOrigin, ErrorResult& aError) {
SetOrRemoveNullableStringAttr(nsGkAtoms::crossorigin, aCrossOrigin, aError);
}
void SetDecoding(const nsAString& aDecoding, ErrorResult& aError) {
SetAttr(nsGkAtoms::decoding, aDecoding, aError);

View File

@ -14,6 +14,8 @@
interface SVGFEImageElement : SVGElement {
[Constant]
readonly attribute SVGAnimatedPreserveAspectRatio preserveAspectRatio;
[SetterThrows]
attribute DOMString? crossOrigin;
};
SVGFEImageElement includes SVGFilterPrimitiveStandardAttributes;

View File

@ -22,6 +22,8 @@ interface SVGImageElement : SVGGraphicsElement {
readonly attribute SVGAnimatedLength height;
[Constant]
readonly attribute SVGAnimatedPreserveAspectRatio preserveAspectRatio;
[SetterThrows]
attribute DOMString? crossOrigin;
[CEReactions, SetterThrows]
attribute DOMString decoding;
[NewObject]

View File

@ -1,14 +1,6 @@
[svg-image.https.sub.html]
expected:
if (os == "android") and fission: [OK, TIMEOUT]
[sec-fetch-mode attributes: crossorigin]
expected: FAIL
[sec-fetch-mode attributes: crossorigin=anonymous]
expected: FAIL
[sec-fetch-mode attributes: crossorigin=use-credentials]
expected: FAIL
[sec-fetch-dest no attributes]
expected: FAIL

View File

@ -230,12 +230,6 @@
[SVGElement interface: objects.textPath must inherit property "correspondingUseElement" with the proper type]
expected: FAIL
[SVGImageElement interface: attribute crossOrigin]
expected: FAIL
[SVGImageElement interface: objects.image must inherit property "crossOrigin" with the proper type]
expected: FAIL
[SVGElement interface: objects.image must inherit property "correspondingElement" with the proper type]
expected: FAIL

View File

@ -0,0 +1,69 @@
<!DOCTYPE HTML>
<html>
<title>Test Crossorigin</title>
<link rel="help" href="https://www.w3.org/TR/SVG/embedded.html#ImageElementCrossoriginAttribute">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script class="testbody" type="text/javascript">
function draw_and_read_image(image, should_throw) {
let c = document.createElement('canvas');
document.body.appendChild(c);
let ctx = c.getContext('2d');
ctx.drawImage(image, 0, 0);
function get_image_data() {
ctx.getImageData(0, 0, 4, 4);
}
if (should_throw) {
assert_throws_dom('SecurityError', get_image_data);
} else {
get_image_data();
}
document.body.removeChild(c);
}
async_test(t => {
let image = document.createElementNS("http://www.w3.org/2000/svg", "image");
image.setAttribute("href", "/images/green.png");
image.crossOrigin = "anonymous";
image.onload = t.step_func_done(() => {
draw_and_read_image(image, false);
});
image.onerror = t.unreached_func();
}, "Can get pixels of canvas with same origin image drawn");
async_test(t => {
let image = document.createElementNS("http://www.w3.org/2000/svg", "image");
image.setAttribute("href", "http://{{hosts[][www]}}:{{ports[http][0]}}/images/green.png?pipe=header(Access-Control-Allow-Origin,*)");
image.crossOrigin = "anonymous";
image.onload = t.step_func_done(() => {
draw_and_read_image(image, false);
});
image.onerror = t.unreached_func();
}, "Can get pixels of canvas with CORS enabled cross origin image drawn");
async_test(t => {
let image = document.createElementNS("http://www.w3.org/2000/svg", "image");
image.setAttribute("href", "http://{{hosts[][www]}}:{{ports[http][0]}}/images/green.png?pipe=header(Access-Control-Allow-Origin,*)");
image.onload = t.step_func_done(() => {
draw_and_read_image(image, true);
});
image.onerror = t.unreached_func();
}, "Can't get pixels of canvas with CORS enabled cross origin image drawn from non-CORS element");
async_test(t => {
let image = document.createElementNS("http://www.w3.org/2000/svg", "image");
image.setAttribute("href", "http://{{hosts[][www]}}:{{ports[http][0]}}/images/green.png");
image.onload = t.step_func_done(() => {
draw_and_read_image(image, true);
});
image.onerror = t.unreached_func();
}, "Can't get pixels of canvas with non-CORS enabled cross origin image drawn");
</script>
</html>