Skip to content

Commit

Permalink
Implement trusted types integrations with DOM setAttribute APIs.
Browse files Browse the repository at this point in the history
https://bugs.webkit.org/show_bug.cgi?id=270436.

Reviewed by NOBODY (OOPS!).

Implement the spec updates at whatwg/dom#1247.
It also remove the some expectations in GTK as the results should be
in line with the general expectation file.

* LayoutTests/platform/gtk/TestExpectations:
* Source/WebCore/dom/Element.cpp:
(WebCore::getTrustedTypesCompliantAttributeValue):
(WebCore::Element::setAttribute):
(WebCore::Element::setElementsArrayAttribute):
(WebCore::appendAttributes):
(WebCore::Element::setAttributeNS):
* Source/WebCore/dom/Element.h:
* Source/WebCore/dom/Element.idl:
* Source/WebCore/dom/TrustedType.cpp:
(WebCore::stringToTrustedType):
(WebCore::getAttributeTrustedType):
* Source/WebCore/dom/TrustedType.h:
* Source/WebCore/dom/TrustedTypePolicyFactory.cpp:
(WebCore::TrustedTypePolicyFactory::getAttributeType const):
  • Loading branch information
ziransun committed Apr 18, 2024
1 parent 2459e14 commit 54ca5d3
Show file tree
Hide file tree
Showing 9 changed files with 105 additions and 40 deletions.
8 changes: 0 additions & 8 deletions LayoutTests/platform/gtk/TestExpectations
Original file line number Diff line number Diff line change
Expand Up @@ -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 ]
Expand Down
58 changes: 52 additions & 6 deletions Source/WebCore/dom/Element.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -2000,7 +2001,31 @@ ExceptionOr<bool> Element::toggleAttribute(const AtomString& qualifiedName, std:
return true;
}

ExceptionOr<void> Element::setAttribute(const AtomString& qualifiedName, const AtomString& value)
static ExceptionOr<String> getTrustedTypesCompliantAttributeValue(const String& attributeType, const TrustedTypeOrString& value, Element* element, String sink)
{
auto stringValueHolder = WTF::switchOn(value,
[&](const String& str) -> ExceptionOr<String> {
if (attributeType.isNull())
return String(str);
return trustedTypeCompliantString(stringToTrustedType(attributeType), *(element->document().scriptExecutionContext()), str, sink);
},
[](const RefPtr<TrustedHTML>& trustedHTML) -> ExceptionOr<String> {
return trustedHTML->toString();
},
[](const RefPtr<TrustedScript>& trustedScript) -> ExceptionOr<String> {
return trustedScript->toString();
},
[](const RefPtr<TrustedScriptURL>& trustedScriptURL) -> ExceptionOr<String> {
return trustedScriptURL->toString();
}
);
if (stringValueHolder.hasException())
return stringValueHolder.releaseException();

return stringValueHolder.releaseReturnValue();
}

ExceptionOr<void> Element::setAttribute(const AtomString& qualifiedName, const TrustedTypeOrString& value)
{
if (!Document::isValidName(qualifiedName))
return Exception { ExceptionCode::InvalidCharacterError, makeString("Invalid qualified name: '", qualifiedName, "'") };
Expand All @@ -2009,8 +2034,18 @@ ExceptionOr<void> 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<AtomString>(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 { };
}

Expand Down Expand Up @@ -2314,7 +2349,7 @@ void Element::setElementsArrayAttribute(const QualifiedName& attributeName, std:

auto newElements = copyToVectorOf<WeakPtr<Element, WeakPtrImplWithEventTargetData>>(*elements);
explicitlySetAttrElementsMap().set(attributeName, WTFMove(newElements));

if (CheckedPtr cache = document().existingAXObjectCache()) {
for (auto element : elements.value()) {
// FIXME: Should this pass `element` instead of `*this`?
Expand Down Expand Up @@ -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(' ');
Expand Down Expand Up @@ -3395,12 +3430,23 @@ ExceptionOr<QualifiedName> Element::parseAttributeName(const AtomString& namespa
return parsedAttributeName;
}

ExceptionOr<void> Element::setAttributeNS(const AtomString& namespaceURI, const AtomString& qualifiedName, const AtomString& value)
ExceptionOr<void> 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<AtomString>(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 { };
}

Expand Down
8 changes: 6 additions & 2 deletions Source/WebCore/dom/Element.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 <JavaScriptCore/Forward.h>

Expand Down Expand Up @@ -119,6 +122,7 @@ struct ShadowRootInit;

using ElementName = NodeName;
using ExplicitlySetAttrElementsMap = HashMap<QualifiedName, Vector<WeakPtr<Element, WeakPtrImplWithEventTargetData>>>;
using TrustedTypeOrString = std::variant<RefPtr<TrustedHTML>, RefPtr<TrustedScript>, RefPtr<TrustedScriptURL>, AtomString>;

// https://drafts.csswg.org/css-contain/#relevant-to-the-user
enum class ContentRelevancy : uint8_t {
Expand Down Expand Up @@ -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<void> setAttribute(const AtomString& qualifiedName, const AtomString& value);
WEBCORE_EXPORT ExceptionOr<void> setAttribute(const AtomString& qualifiedName, const TrustedTypeOrString& value);
static ExceptionOr<QualifiedName> parseAttributeName(const AtomString& namespaceURI, const AtomString& qualifiedName);
WEBCORE_EXPORT ExceptionOr<void> setAttributeNS(const AtomString& namespaceURI, const AtomString& qualifiedName, const AtomString& value);
WEBCORE_EXPORT ExceptionOr<void> setAttributeNS(const AtomString& namespaceURI, const AtomString& qualifiedName, const TrustedTypeOrString& value);

ExceptionOr<bool> toggleAttribute(const AtomString& qualifiedName, std::optional<bool> force);

Expand Down
6 changes: 4 additions & 2 deletions Source/WebCore/dom/Element.idl
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@
sequence<DOMString> 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);
Expand Down Expand Up @@ -106,3 +106,5 @@ Element includes NonDocumentTypeChildNode;
Element includes ParentNode;
Element includes Slotable;
Element includes InnerHTML;

typedef (TrustedHTML or TrustedScript or TrustedScriptURL) TrustedType;
2 changes: 1 addition & 1 deletion Source/WebCore/dom/TrustedScript.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@

namespace WebCore {

class TrustedScript final : public ScriptWrappable, public RefCounted<TrustedScript> {
class WEBCORE_EXPORT TrustedScript final : public ScriptWrappable, public RefCounted<TrustedScript> {
WTF_MAKE_ISO_ALLOCATED(TrustedScript);
public:
static Ref<TrustedScript> create(const String& data);
Expand Down
2 changes: 1 addition & 1 deletion Source/WebCore/dom/TrustedScriptURL.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@

namespace WebCore {

class TrustedScriptURL : public ScriptWrappable, public RefCounted<TrustedScriptURL> {
class WEBCORE_EXPORT TrustedScriptURL : public ScriptWrappable, public RefCounted<TrustedScriptURL> {
WTF_MAKE_ISO_ALLOCATED(TrustedScriptURL);
public:
static Ref<TrustedScriptURL> create(const String& data);
Expand Down
38 changes: 38 additions & 0 deletions Source/WebCore/dom/TrustedType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
#include "TrustedTypePolicyFactory.h"
#include "WindowOrWorkerGlobalScopeTrustedTypes.h"
#include "WorkerGlobalScope.h"
#include "XLinkNames.h"
#include <JavaScriptCore/HeapInlines.h>
#include <JavaScriptCore/JSCInlines.h>
#include <JavaScriptCore/JSCJSValueInlines.h>
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -173,6 +187,30 @@ ExceptionOr<String> 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<String> requireTrustedTypesForPreNavigationCheckPasses(ScriptExecutionContext& scriptExecutionContext, const String& urlString)
{
Expand Down
2 changes: 2 additions & 0 deletions Source/WebCore/dom/TrustedType.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ enum class TrustedType : int8_t {
};

ASCIILiteral trustedTypeToString(TrustedType);
TrustedType stringToTrustedType(String);
ASCIILiteral trustedTypeToCallbackName(TrustedType);

WEBCORE_EXPORT std::variant<std::monostate, Exception, Ref<TrustedHTML>, Ref<TrustedScript>, Ref<TrustedScriptURL>> processValueWithDefaultPolicy(ScriptExecutionContext&, TrustedType, const String& input, const String& sink);
Expand All @@ -55,4 +56,5 @@ WEBCORE_EXPORT ExceptionOr<String> requireTrustedTypesForPreNavigationCheckPasse

ExceptionOr<RefPtr<Text>> processNodeOrStringAsTrustedType(Ref<Document>, RefPtr<Node> parent, std::variant<RefPtr<Node>, String, RefPtr<TrustedScript>>);

WEBCORE_EXPORT String getTrustedTypeForAttribute(const String& tagName, const String& attributeParameter, const String& elementNamespace, const String& attributeNamespace);
} // namespace WebCore
21 changes: 1 addition & 20 deletions Source/WebCore/dom/TrustedTypePolicyFactory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -108,26 +108,7 @@ Ref<TrustedScript> 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
Expand Down

0 comments on commit 54ca5d3

Please sign in to comment.