diff --git a/LayoutTests/platform/gtk/TestExpectations b/LayoutTests/platform/gtk/TestExpectations index ca33eced4a57e..94675bc73b756 100644 --- a/LayoutTests/platform/gtk/TestExpectations +++ b/LayoutTests/platform/gtk/TestExpectations @@ -2337,14 +2337,6 @@ fast/mediastream/mediastreamtrack-video-frameRate-clone-decreasing.html [ Crash # Trusted Types aren't implemented yet webkit.org/b/266630 imported/w3c/web-platform-tests/trusted-types/trusted-types-reporting-check-report.html [ Failure Pass ] webkit.org/b/266630 imported/w3c/web-platform-tests/trusted-types/block-string-assignment-to-attribute-via-attribute-node.html [ Failure Pass ] -webkit.org/b/266630 imported/w3c/web-platform-tests/trusted-types/Element-setAttribute.html [ Failure ] -webkit.org/b/266630 imported/w3c/web-platform-tests/trusted-types/WorkerGlobalScope-eval.html [ Failure ] -webkit.org/b/266630 imported/w3c/web-platform-tests/trusted-types/WorkerGlobalScope-importScripts.html [ Failure ] -webkit.org/b/266630 imported/w3c/web-platform-tests/trusted-types/block-string-assignment-to-Element-setAttribute.html [ Failure ] -webkit.org/b/266630 imported/w3c/web-platform-tests/trusted-types/block-string-assignment-to-HTMLElement-generic.html [ Failure ] -webkit.org/b/266630 imported/w3c/web-platform-tests/trusted-types/default-policy-report-only.html [ Failure ] -webkit.org/b/266630 imported/w3c/web-platform-tests/trusted-types/default-policy.html [ Failure ] -webkit.org/b/266630 imported/w3c/web-platform-tests/trusted-types/worker-constructor.https.html [ Failure ] # Flaky tests on Aug-2023 webkit.org/b/261024 animations/change-completed-animation-transform.html [ ImageOnlyFailure Pass ] diff --git a/Source/WebCore/dom/Element.cpp b/Source/WebCore/dom/Element.cpp index 6d66bb6b5288b..be6e98c96d46e 100644 --- a/Source/WebCore/dom/Element.cpp +++ b/Source/WebCore/dom/Element.cpp @@ -145,6 +145,7 @@ #include "StyleTreeResolver.h" #include "TextIterator.h" #include "TouchAction.h" +#include "TrustedType.h" #include "TypedElementDescendantIteratorInlines.h" #include "VisibilityAdjustment.h" #include "VoidCallback.h" @@ -2000,7 +2001,31 @@ ExceptionOr Element::toggleAttribute(const AtomString& qualifiedName, std: return true; } -ExceptionOr Element::setAttribute(const AtomString& qualifiedName, const AtomString& value) +static ExceptionOr getTrustedTypesCompliantAttributeValue(const String& attributeType, const TrustedTypeOrString& value, Element* element, String sink) +{ + auto stringValueHolder = WTF::switchOn(value, + [&](const String& str) -> ExceptionOr { + if (attributeType.isNull()) + return String(str); + return trustedTypeCompliantString(stringToTrustedType(attributeType), *(element->document().scriptExecutionContext()), str, sink); + }, + [](const RefPtr& trustedHTML) -> ExceptionOr { + return trustedHTML->toString(); + }, + [](const RefPtr& trustedScript) -> ExceptionOr { + return trustedScript->toString(); + }, + [](const RefPtr& trustedScriptURL) -> ExceptionOr { + return trustedScriptURL->toString(); + } + ); + if (stringValueHolder.hasException()) + return stringValueHolder.releaseException(); + + return stringValueHolder.releaseReturnValue(); +} + +ExceptionOr Element::setAttribute(const AtomString& qualifiedName, const TrustedTypeOrString& value) { if (!Document::isValidName(qualifiedName)) return Exception { ExceptionCode::InvalidCharacterError, makeString("Invalid qualified name: '", qualifiedName, "'") }; @@ -2009,8 +2034,18 @@ ExceptionOr Element::setAttribute(const AtomString& qualifiedName, const A auto caseAdjustedQualifiedName = shouldIgnoreAttributeCase(*this) ? qualifiedName.convertToASCIILowercase() : qualifiedName; unsigned index = elementData() ? elementData()->findAttributeIndexByName(caseAdjustedQualifiedName, false) : ElementData::attributeNotFound; auto name = index != ElementData::attributeNotFound ? attributeAt(index).name() : QualifiedName { nullAtom(), caseAdjustedQualifiedName, nullAtom() }; - setAttributeInternal(index, name, value, InSynchronizationOfLazyAttribute::No); + if (!document().scriptExecutionContext()->settingsValues().trustedTypesEnabled) + setAttributeInternal(index, name, std::get(value), InSynchronizationOfLazyAttribute::No); + else { + auto sink = nullString(); + String attributeType = getTrustedTypeForAttribute(name.localName(), getAttribute(name), ""_s, ""_s); + auto attributeValue = getTrustedTypesCompliantAttributeValue(attributeType, value, this, "Element setAttribute"_s); + if (attributeValue.hasException()) + return attributeValue.releaseException(); + + setAttributeInternal(index, name, AtomString(attributeValue.releaseReturnValue()), InSynchronizationOfLazyAttribute::No); + } return { }; } @@ -2314,7 +2349,7 @@ void Element::setElementsArrayAttribute(const QualifiedName& attributeName, std: auto newElements = copyToVectorOf>(*elements); explicitlySetAttrElementsMap().set(attributeName, WTFMove(newElements)); - + if (CheckedPtr cache = document().existingAXObjectCache()) { for (auto element : elements.value()) { // FIXME: Should this pass `element` instead of `*this`? @@ -3227,7 +3262,7 @@ static void appendAttributes(StringBuilder& builder, const Element& element) classNamesToDump = maxNumClassNames; addEllipsis = true; } - + for (size_t i = 0; i < classNamesToDump; ++i) { if (i > 0) builder.append(' '); @@ -3395,12 +3430,23 @@ ExceptionOr Element::parseAttributeName(const AtomString& namespa return parsedAttributeName; } -ExceptionOr Element::setAttributeNS(const AtomString& namespaceURI, const AtomString& qualifiedName, const AtomString& value) +ExceptionOr Element::setAttributeNS(const AtomString& namespaceURI, const AtomString& qualifiedName, const TrustedTypeOrString& value) { auto result = parseAttributeName(namespaceURI, qualifiedName); if (result.hasException()) return result.releaseException(); - setAttribute(result.releaseReturnValue(), value); + if (!document().scriptExecutionContext()->settingsValues().trustedTypesEnabled) + setAttribute(result.releaseReturnValue(), std::get(value)); + else { + String attributeType = getTrustedTypeForAttribute(qualifiedName, getAttribute(qualifiedName), ""_s, namespaceURI); + auto attributeValue = getTrustedTypesCompliantAttributeValue(attributeType, value, this, "Element setAttributeNS"_s); + + if (attributeValue.hasException()) + return attributeValue.releaseException(); + + setAttribute(result.releaseReturnValue(), AtomString(attributeValue.releaseReturnValue())); + } + return { }; } diff --git a/Source/WebCore/dom/Element.h b/Source/WebCore/dom/Element.h index cca88e29c4b48..ac44569f80c4e 100644 --- a/Source/WebCore/dom/Element.h +++ b/Source/WebCore/dom/Element.h @@ -35,6 +35,9 @@ #include "ScrollTypes.h" #include "ShadowRootMode.h" #include "SimulatedClickOptions.h" +#include "TrustedHTML.h" +#include "TrustedScript.h" +#include "TrustedScriptURL.h" #include "WebAnimationTypes.h" #include @@ -119,6 +122,7 @@ struct ShadowRootInit; using ElementName = NodeName; using ExplicitlySetAttrElementsMap = HashMap>>; +using TrustedTypeOrString = std::variant, RefPtr, RefPtr, AtomString>; // https://drafts.csswg.org/css-contain/#relevant-to-the-user enum class ContentRelevancy : uint8_t { @@ -190,9 +194,9 @@ class Element : public ContainerNode { AtomString getAttributeForBindings(const AtomString& qualifiedName, ResolveURLs = ResolveURLs::NoExcludingURLsForPrivacy) const; inline AtomString getAttributeNSForBindings(const AtomString& namespaceURI, const AtomString& localName, ResolveURLs = ResolveURLs::NoExcludingURLsForPrivacy) const; - WEBCORE_EXPORT ExceptionOr setAttribute(const AtomString& qualifiedName, const AtomString& value); + WEBCORE_EXPORT ExceptionOr setAttribute(const AtomString& qualifiedName, const TrustedTypeOrString& value); static ExceptionOr parseAttributeName(const AtomString& namespaceURI, const AtomString& qualifiedName); - WEBCORE_EXPORT ExceptionOr setAttributeNS(const AtomString& namespaceURI, const AtomString& qualifiedName, const AtomString& value); + WEBCORE_EXPORT ExceptionOr setAttributeNS(const AtomString& namespaceURI, const AtomString& qualifiedName, const TrustedTypeOrString& value); ExceptionOr toggleAttribute(const AtomString& qualifiedName, std::optional force); diff --git a/Source/WebCore/dom/Element.idl b/Source/WebCore/dom/Element.idl index d0220b77a96d7..40cc8fb070fc2 100644 --- a/Source/WebCore/dom/Element.idl +++ b/Source/WebCore/dom/Element.idl @@ -42,8 +42,8 @@ sequence getAttributeNames(); [DOMJIT=ReadDOM, ImplementedAs=getAttributeForBindings] DOMString? getAttribute([AtomString] DOMString qualifiedName); [ImplementedAs=getAttributeNSForBindings] DOMString? getAttributeNS([AtomString] DOMString? namespaceURI, [AtomString] DOMString localName); - [CEReactions=Needed] undefined setAttribute([AtomString] DOMString qualifiedName, [AtomString] DOMString value); - [CEReactions=Needed] undefined setAttributeNS([AtomString] DOMString? namespaceURI, [AtomString] DOMString qualifiedName, [AtomString] DOMString value); + [CEReactions=Needed] undefined setAttribute([AtomString] DOMString qualifiedName, (TrustedType or [AtomString] DOMString) value); + [CEReactions=Needed] undefined setAttributeNS([AtomString] DOMString? namespaceURI, [AtomString] DOMString qualifiedName, (TrustedType or [AtomString] DOMString) value); [CEReactions=Needed, ImplementedAs=removeAttributeForBindings] undefined removeAttribute([AtomString] DOMString qualifiedName); [CEReactions=Needed, ImplementedAs=removeAttributeNSForBindings] undefined removeAttributeNS([AtomString] DOMString? namespaceURI, [AtomString] DOMString localName); [CEReactions=Needed] boolean toggleAttribute([AtomString] DOMString qualifiedName, optional boolean force); @@ -106,3 +106,5 @@ Element includes NonDocumentTypeChildNode; Element includes ParentNode; Element includes Slotable; Element includes InnerHTML; + +typedef (TrustedHTML or TrustedScript or TrustedScriptURL) TrustedType; diff --git a/Source/WebCore/dom/TrustedScript.h b/Source/WebCore/dom/TrustedScript.h index ba1c1d06e2a7c..7c1c7cd784568 100644 --- a/Source/WebCore/dom/TrustedScript.h +++ b/Source/WebCore/dom/TrustedScript.h @@ -29,7 +29,7 @@ namespace WebCore { -class TrustedScript final : public ScriptWrappable, public RefCounted { +class WEBCORE_EXPORT TrustedScript final : public ScriptWrappable, public RefCounted { WTF_MAKE_ISO_ALLOCATED(TrustedScript); public: static Ref create(const String& data); diff --git a/Source/WebCore/dom/TrustedScriptURL.h b/Source/WebCore/dom/TrustedScriptURL.h index fb2cbf4eadaf7..a0deb79cf2202 100644 --- a/Source/WebCore/dom/TrustedScriptURL.h +++ b/Source/WebCore/dom/TrustedScriptURL.h @@ -31,7 +31,7 @@ namespace WebCore { -class TrustedScriptURL : public ScriptWrappable, public RefCounted { +class WEBCORE_EXPORT TrustedScriptURL : public ScriptWrappable, public RefCounted { WTF_MAKE_ISO_ALLOCATED(TrustedScriptURL); public: static Ref create(const String& data); diff --git a/Source/WebCore/dom/TrustedType.cpp b/Source/WebCore/dom/TrustedType.cpp index 6578c0cbcf344..24456ee332675 100644 --- a/Source/WebCore/dom/TrustedType.cpp +++ b/Source/WebCore/dom/TrustedType.cpp @@ -37,6 +37,7 @@ #include "TrustedTypePolicyFactory.h" #include "WindowOrWorkerGlobalScopeTrustedTypes.h" #include "WorkerGlobalScope.h" +#include "XLinkNames.h" #include #include #include @@ -84,6 +85,19 @@ ASCIILiteral trustedTypeToString(TrustedType trustedType) return { }; } +TrustedType stringToTrustedType(String str) +{ + if (str == "TrustedHTML"_s) + return TrustedType::TrustedHTML; + if (str == "TrustedScript"_s) + return TrustedType::TrustedScript; + if (str == "TrustedScriptURL"_s) + return TrustedType::TrustedScriptURL; + + ASSERT_NOT_REACHED(); + return { }; +} + ASCIILiteral trustedTypeToCallbackName(TrustedType trustedType) { switch (trustedType) { @@ -173,6 +187,30 @@ ExceptionOr trustedTypeCompliantString(TrustedType expectedType, ScriptE return stringValue; } +String getTrustedTypeForAttribute(const String& tagName, const String& attributeParameter, const String& elementNamespace, const String& attributeNamespace) +{ + auto localName = tagName.convertToASCIILowercase(); + auto attributeName = attributeParameter.convertToASCIILowercase(); + + if (attributeName.startsWith("on"_s)) + return trustedTypeToString(TrustedType::TrustedScript); + + AtomString elementNS = elementNamespace.isEmpty() ? HTMLNames::xhtmlNamespaceURI : AtomString(elementNamespace); + AtomString attributeNS = attributeNamespace.isEmpty() ? nullAtom() : AtomString(attributeNamespace); + + QualifiedName element(nullAtom(), AtomString(localName), elementNS); + QualifiedName attribute(nullAtom(), AtomString(attributeName), attributeNS); + + if (element.matches(HTMLNames::iframeTag) && attribute.matches(HTMLNames::srcdocAttr)) + return trustedTypeToString(TrustedType::TrustedHTML); + if (element.matches(HTMLNames::scriptTag) && attribute.matches(HTMLNames::srcAttr)) + return trustedTypeToString(TrustedType::TrustedScriptURL); + if (element.matches(SVGNames::scriptTag) && (attribute.matches(SVGNames::hrefAttr) || attribute.matches(XLinkNames::hrefAttr))) + return trustedTypeToString(TrustedType::TrustedScriptURL); + + return nullString(); +} + // https://w3c.github.io/trusted-types/dist/spec/#require-trusted-types-for-pre-navigation-check ExceptionOr requireTrustedTypesForPreNavigationCheckPasses(ScriptExecutionContext& scriptExecutionContext, const String& urlString) { diff --git a/Source/WebCore/dom/TrustedType.h b/Source/WebCore/dom/TrustedType.h index 5135d54dae49c..b2e44bc1bd770 100644 --- a/Source/WebCore/dom/TrustedType.h +++ b/Source/WebCore/dom/TrustedType.h @@ -45,6 +45,7 @@ enum class TrustedType : int8_t { }; ASCIILiteral trustedTypeToString(TrustedType); +TrustedType stringToTrustedType(String); ASCIILiteral trustedTypeToCallbackName(TrustedType); WEBCORE_EXPORT std::variant, Ref, Ref> processValueWithDefaultPolicy(ScriptExecutionContext&, TrustedType, const String& input, const String& sink); @@ -55,4 +56,5 @@ WEBCORE_EXPORT ExceptionOr requireTrustedTypesForPreNavigationCheckPasse ExceptionOr> processNodeOrStringAsTrustedType(Ref, RefPtr parent, std::variant, String, RefPtr>); +WEBCORE_EXPORT String getTrustedTypeForAttribute(const String& tagName, const String& attributeParameter, const String& elementNamespace, const String& attributeNamespace); } // namespace WebCore diff --git a/Source/WebCore/dom/TrustedTypePolicyFactory.cpp b/Source/WebCore/dom/TrustedTypePolicyFactory.cpp index e475f5a75595e..b0963ef55038f 100644 --- a/Source/WebCore/dom/TrustedTypePolicyFactory.cpp +++ b/Source/WebCore/dom/TrustedTypePolicyFactory.cpp @@ -108,26 +108,7 @@ Ref TrustedTypePolicyFactory::emptyScript() const String TrustedTypePolicyFactory::getAttributeType(const String& tagName, const String& attributeParameter, const String& elementNamespace, const String& attributeNamespace) const { - auto localName = tagName.convertToASCIILowercase(); - auto attributeName = attributeParameter.convertToASCIILowercase(); - - if (attributeName.startsWith("on"_s)) - return trustedTypeToString(TrustedType::TrustedScript); - - AtomString elementNS = elementNamespace.isEmpty() ? HTMLNames::xhtmlNamespaceURI : AtomString(elementNamespace); - AtomString attributeNS = attributeNamespace.isEmpty() ? nullAtom() : AtomString(attributeNamespace); - - QualifiedName element(nullAtom(), AtomString(localName), elementNS); - QualifiedName attribute(nullAtom(), AtomString(attributeName), attributeNS); - - if (element.matches(HTMLNames::iframeTag) && attribute.matches(HTMLNames::srcdocAttr)) - return trustedTypeToString(TrustedType::TrustedHTML); - if (element.matches(HTMLNames::scriptTag) && attribute.matches(HTMLNames::srcAttr)) - return trustedTypeToString(TrustedType::TrustedScriptURL); - if (element.matches(SVGNames::scriptTag) && (attribute.matches(SVGNames::hrefAttr) || attribute.matches(XLinkNames::hrefAttr))) - return trustedTypeToString(TrustedType::TrustedScriptURL); - - return nullString(); + return getTrustedTypeForAttribute(tagName, attributeParameter, elementNamespace, attributeNamespace); } String TrustedTypePolicyFactory::getPropertyType(const String& tagName, const String& property, const String& elementNamespace) const