mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-12-07 21:59:54 +00:00
LibWeb: Implement TrustedTypes spec for set_attribute on Element
This part of the spec is still under a PR in GitHub, but it should be safe to implement like it is.
This commit is contained in:
parent
5381146e85
commit
df543cf31a
Notes:
github-actions[bot]
2025-10-27 16:16:43 +00:00
Author: https://github.com/tete17
Commit: df543cf31a
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/6057
Reviewed-by: https://github.com/AtkinsSJ
Reviewed-by: https://github.com/Lubrsi ✅
7 changed files with 108 additions and 29 deletions
|
|
@ -89,6 +89,7 @@
|
|||
#include <LibWeb/Painting/ViewportPaintable.h>
|
||||
#include <LibWeb/SVG/SVGAElement.h>
|
||||
#include <LibWeb/Selection/Selection.h>
|
||||
#include <LibWeb/TrustedTypes/TrustedTypePolicy.h>
|
||||
#include <LibWeb/WebIDL/AbstractOperations.h>
|
||||
#include <LibWeb/WebIDL/DOMException.h>
|
||||
#include <LibWeb/WebIDL/ExceptionOr.h>
|
||||
|
|
@ -206,38 +207,49 @@ GC::Ptr<Attr> Element::get_attribute_node_ns(Optional<FlyString> const& namespac
|
|||
return m_attributes->get_attribute_ns(namespace_, name);
|
||||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#dom-element-setattribute
|
||||
WebIDL::ExceptionOr<void> Element::set_attribute(FlyString const& name, String const& value)
|
||||
// FIXME: Trusted Types integration with DOM is still under review https://github.com/whatwg/dom/pull/1268
|
||||
// https://whatpr.org/dom/1268.html#dom-element-setattribute
|
||||
WebIDL::ExceptionOr<void> Element::set_attribute(FlyString qualified_name, Variant<GC::Root<TrustedTypes::TrustedHTML>, GC::Root<TrustedTypes::TrustedScript>, GC::Root<TrustedTypes::TrustedScriptURL>, Utf16String> const& value)
|
||||
{
|
||||
// 1. If qualifiedName is not a valid attribute local name, then throw an "InvalidCharacterError" DOMException.
|
||||
if (!is_valid_attribute_local_name(name))
|
||||
if (!is_valid_attribute_local_name(qualified_name))
|
||||
return WebIDL::InvalidCharacterError::create(realm(), "Attribute name must not be empty or contain invalid characters"_utf16);
|
||||
|
||||
// 2. If this is in the HTML namespace and its node document is an HTML document, then set qualifiedName to qualifiedName in ASCII lowercase.
|
||||
bool insert_as_lowercase = namespace_uri() == Namespace::HTML && document().document_type() == Document::Type::HTML;
|
||||
// 2. If this is in the HTML namespace and its node document is an HTML document, then set qualifiedName to
|
||||
// qualifiedName in ASCII lowercase.
|
||||
if (namespace_uri() == Namespace::HTML && document().document_type() == Document::Type::HTML)
|
||||
qualified_name = qualified_name.to_ascii_lowercase();
|
||||
|
||||
// 3. Let attribute be the first attribute in this’s attribute list whose qualified name is qualifiedName, and null otherwise.
|
||||
auto* attribute = attributes()->get_attribute(name);
|
||||
// 3. Let verifiedValue be the result of calling get Trusted Types-compliant attribute value
|
||||
// with qualifiedName, null, this, and value.
|
||||
auto const verified_value = TRY(TrustedTypes::get_trusted_types_compliant_attribute_value(qualified_name, {}, *this, value));
|
||||
|
||||
// 4. If attribute is null, create an attribute whose local name is qualifiedName, value is value, and node document
|
||||
// 4. Let attribute be the first attribute in this’s attribute list whose qualified name is qualifiedName, and null otherwise.
|
||||
auto* attribute = attributes()->get_attribute(qualified_name);
|
||||
|
||||
// 5. If attribute is null, create an attribute whose local name is qualifiedName, value is verifiedValue, and node document
|
||||
// is this’s node document, then append this attribute to this, and then return.
|
||||
if (!attribute) {
|
||||
auto new_attribute = Attr::create(document(), insert_as_lowercase ? name.to_ascii_lowercase() : name, value);
|
||||
auto new_attribute = Attr::create(document(), qualified_name, verified_value.to_utf8_but_should_be_ported_to_utf16());
|
||||
m_attributes->append_attribute(new_attribute);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
// 5. Change attribute to value.
|
||||
attribute->change_attribute(value);
|
||||
// 6. Change attribute to verifiedValue.
|
||||
attribute->change_attribute(verified_value.to_utf8_but_should_be_ported_to_utf16());
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#dom-element-setattribute
|
||||
WebIDL::ExceptionOr<void> Element::set_attribute(FlyString const& name, Utf16String const& value)
|
||||
// FIXME: Trusted Types integration with DOM is still under review https://github.com/whatwg/dom/pull/1268
|
||||
// https://whatpr.org/dom/1268.html#dom-element-setattribute
|
||||
WebIDL::ExceptionOr<void> Element::set_attribute(FlyString qualified_name, Variant<GC::Root<TrustedTypes::TrustedHTML>, GC::Root<TrustedTypes::TrustedScript>, GC::Root<TrustedTypes::TrustedScriptURL>, String> const& value)
|
||||
{
|
||||
return set_attribute(name, value.to_utf8_but_should_be_ported_to_utf16());
|
||||
return set_attribute(move(qualified_name),
|
||||
value.visit(
|
||||
[](auto const& trusted_type) -> Variant<GC::Root<TrustedTypes::TrustedHTML>, GC::Root<TrustedTypes::TrustedScript>, GC::Root<TrustedTypes::TrustedScriptURL>, Utf16String> { return trusted_type; },
|
||||
[](String const& string) -> Variant<GC::Root<TrustedTypes::TrustedHTML>, GC::Root<TrustedTypes::TrustedScript>, GC::Root<TrustedTypes::TrustedScriptURL>, Utf16String> { return Utf16String::from_utf8(string); }));
|
||||
}
|
||||
|
||||
// https://dom.spec.whatwg.org/#valid-namespace-prefix
|
||||
|
|
|
|||
|
|
@ -32,6 +32,9 @@
|
|||
#include <LibWeb/HTML/ScrollOptions.h>
|
||||
#include <LibWeb/HTML/TagNames.h>
|
||||
#include <LibWeb/IntersectionObserver/IntersectionObserver.h>
|
||||
#include <LibWeb/TrustedTypes/TrustedHTML.h>
|
||||
#include <LibWeb/TrustedTypes/TrustedScript.h>
|
||||
#include <LibWeb/TrustedTypes/TrustedScriptURL.h>
|
||||
#include <LibWeb/WebIDL/ExceptionOr.h>
|
||||
#include <LibWeb/WebIDL/Types.h>
|
||||
|
||||
|
|
@ -146,8 +149,8 @@ public:
|
|||
|
||||
Optional<String> lang() const;
|
||||
|
||||
WebIDL::ExceptionOr<void> set_attribute(FlyString const& name, String const& value);
|
||||
WebIDL::ExceptionOr<void> set_attribute(FlyString const& name, Utf16String const& value);
|
||||
WebIDL::ExceptionOr<void> set_attribute(FlyString qualified_name, Variant<GC::Root<TrustedTypes::TrustedHTML>, GC::Root<TrustedTypes::TrustedScript>, GC::Root<TrustedTypes::TrustedScriptURL>, String> const& value);
|
||||
WebIDL::ExceptionOr<void> set_attribute(FlyString qualified_name, Variant<GC::Root<TrustedTypes::TrustedHTML>, GC::Root<TrustedTypes::TrustedScript>, GC::Root<TrustedTypes::TrustedScriptURL>, Utf16String> const& value);
|
||||
|
||||
WebIDL::ExceptionOr<void> set_attribute_ns(Optional<FlyString> const& namespace_, FlyString const& qualified_name, String const& value);
|
||||
void set_attribute_value(FlyString const& local_name, String const& value, Optional<FlyString> const& prefix = {}, Optional<FlyString> const& namespace_ = {});
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
#import <Geometry/DOMRectList.idl>
|
||||
#import <HTML/HTMLSlotElement.idl>
|
||||
#import <HTML/Window.idl>
|
||||
#import <TrustedTypes/TrustedTypePolicy.idl>
|
||||
|
||||
enum ScrollLogicalPosition { "start", "center", "end", "nearest" };
|
||||
// https://drafts.csswg.org/cssom-view-1/#dictdef-scrollintoviewoptions
|
||||
|
|
@ -52,7 +53,7 @@ interface Element : Node {
|
|||
sequence<DOMString> getAttributeNames();
|
||||
DOMString? getAttribute(DOMString qualifiedName);
|
||||
DOMString? getAttributeNS([FlyString] DOMString? namespace, [FlyString] DOMString localName);
|
||||
[CEReactions] undefined setAttribute(DOMString qualifiedName, DOMString value);
|
||||
[CEReactions] undefined setAttribute(DOMString qualifiedName, (TrustedType or Utf16DOMString) value);
|
||||
[CEReactions] undefined setAttributeNS([FlyString] DOMString? namespace , [FlyString] DOMString qualifiedName , DOMString value);
|
||||
[CEReactions] undefined removeAttribute([FlyString] DOMString qualifiedName);
|
||||
[CEReactions] undefined removeAttributeNS([FlyString] DOMString? namespace, [FlyString] DOMString localName);
|
||||
|
|
|
|||
|
|
@ -10,7 +10,10 @@
|
|||
#include <LibJS/Runtime/Realm.h>
|
||||
#include <LibJS/Runtime/Value.h>
|
||||
#include <LibWeb/Bindings/Intrinsics.h>
|
||||
#include <LibWeb/HTML/TagNames.h>
|
||||
#include <LibWeb/HTML/WindowOrWorkerGlobalScope.h>
|
||||
#include <LibWeb/Namespace.h>
|
||||
#include <LibWeb/SVG/TagNames.h>
|
||||
#include <LibWeb/TrustedTypes/RequireTrustedTypesForDirective.h>
|
||||
#include <LibWeb/TrustedTypes/TrustedHTML.h>
|
||||
#include <LibWeb/TrustedTypes/TrustedScript.h>
|
||||
|
|
@ -340,4 +343,68 @@ WebIDL::ExceptionOr<Utf16String> get_trusted_type_compliant_string(TrustedTypeNa
|
|||
});
|
||||
}
|
||||
|
||||
// https://w3c.github.io/trusted-types/dist/spec/#validate-attribute-mutation
|
||||
WebIDL::ExceptionOr<Utf16String> get_trusted_types_compliant_attribute_value(FlyString const& attribute_name, Optional<Utf16String> attribute_ns, const DOM::Element& element, Variant<GC::Root<TrustedHTML>, GC::Root<TrustedScript>, GC::Root<TrustedScriptURL>, Utf16String> const& new_value)
|
||||
{
|
||||
// 1. If attributeNs is the empty string, set attributeNs to null.
|
||||
if (attribute_ns.has_value() && attribute_ns.value().is_empty())
|
||||
attribute_ns.clear();
|
||||
|
||||
// 2. Set attributeData to the result of Get Trusted Type data for attribute algorithm, with the following arguments:
|
||||
// element
|
||||
// attributeName
|
||||
// attributeNs
|
||||
auto const attribute_data = get_trusted_type_data_for_attribute(
|
||||
element_interface_name(Utf16String::from_utf8(element.local_name()), attribute_ns.has_value() ? attribute_ns.value() : Utf16String::from_utf8(Namespace::HTML)),
|
||||
Utf16String::from_utf8(attribute_name),
|
||||
attribute_ns);
|
||||
|
||||
// 3. If attributeData is null, then:
|
||||
if (!attribute_data.has_value()) {
|
||||
// 1. If newValue is a string, return newValue.
|
||||
if (new_value.has<Utf16String>())
|
||||
return new_value.get<Utf16String>();
|
||||
|
||||
// 2. Assert: newValue is TrustedHTML or TrustedScript or TrustedScriptURL.
|
||||
VERIFY(new_value.has<GC::Root<TrustedHTML>>() || new_value.has<GC::Root<TrustedScript>>() || new_value.has<GC::Root<TrustedScriptURL>>());
|
||||
|
||||
// 3. Return value’s associated data.
|
||||
// FIXME: This is badly worded in the spec it should say "Return stringified newvalues's"
|
||||
return new_value.downcast<TrustedType>().visit([](auto& value) { return value->to_string(); });
|
||||
}
|
||||
|
||||
// 4. Let expectedType be the value of the fourth member of attributeData.
|
||||
auto const expected_type = attribute_data->trusted_type;
|
||||
|
||||
// 5. Let sink be the value of the fifth member of attributeData.
|
||||
auto const sink = attribute_data->sink;
|
||||
|
||||
// 6. Return the result of executing Get Trusted Type compliant string with the following arguments:
|
||||
// expectedType
|
||||
// newValue as input
|
||||
// element’s node document’s relevant global object as global
|
||||
// sink
|
||||
// 'script' as sinkGroup
|
||||
return get_trusted_type_compliant_string(
|
||||
expected_type,
|
||||
HTML::relevant_global_object(element.document()),
|
||||
new_value,
|
||||
sink,
|
||||
Script.to_string());
|
||||
}
|
||||
|
||||
Utf16String element_interface_name(Utf16String const& local_name, Utf16String const& element_ns)
|
||||
{
|
||||
// FIXME: We don't have a method in ElementFactory that can give us the interface name but these are all the cases
|
||||
// we care about in the table in get_trusted_type_data_for_attribute function
|
||||
if (local_name == HTML::TagNames::iframe && element_ns == Namespace::HTML)
|
||||
return "HTMLIFrameElement"_utf16;
|
||||
if (local_name == HTML::TagNames::script && element_ns == Namespace::HTML)
|
||||
return "HTMLScriptElement"_utf16;
|
||||
if (local_name == SVG::TagNames::script && element_ns == Namespace::SVG)
|
||||
return "SVGScriptElement"_utf16;
|
||||
|
||||
return "Element"_utf16;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -69,4 +69,8 @@ WebIDL::ExceptionOr<Optional<TrustedType>> process_value_with_a_default_policy(T
|
|||
|
||||
WebIDL::ExceptionOr<Utf16String> get_trusted_type_compliant_string(TrustedTypeName, JS::Object&, Variant<GC::Root<TrustedHTML>, GC::Root<TrustedScript>, GC::Root<TrustedScriptURL>, Utf16String> input, InjectionSink sink, String sink_group);
|
||||
|
||||
WebIDL::ExceptionOr<Utf16String> get_trusted_types_compliant_attribute_value(FlyString const& attribute_name, Optional<Utf16String> attribute_ns, const DOM::Element& element, Variant<GC::Root<TrustedHTML>, GC::Root<TrustedScript>, GC::Root<TrustedScriptURL>, Utf16String> const& new_value);
|
||||
|
||||
Utf16String element_interface_name(Utf16String const& local_name, Utf16String const& element_ns);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,9 @@
|
|||
#import <TrustedTypes/TrustedScript.idl>
|
||||
#import <TrustedTypes/TrustedScriptURL.idl>
|
||||
|
||||
// https://www.w3.org/TR/trusted-types/#typedefdef-trustedtype
|
||||
typedef (TrustedHTML or TrustedScript or TrustedScriptURL) TrustedType;
|
||||
|
||||
// https://w3c.github.io/trusted-types/dist/spec/#trusted-type-policy
|
||||
[Exposed=(Window,Worker)]
|
||||
interface TrustedTypePolicy {
|
||||
|
|
|
|||
|
|
@ -48,19 +48,8 @@ Optional<Utf16String> TrustedTypePolicyFactory::get_attribute_type(Utf16String c
|
|||
if (attr_ns.has_value() && attr_ns.value().is_empty())
|
||||
attr_ns.clear();
|
||||
|
||||
// FIXME: We don't have a method in ElementFactory that can give us the interface name but these are all the cases
|
||||
// we care about in the table in get_trusted_type_data_for_attribute function
|
||||
// 5. Let interface be the element interface for localName and elementNs.
|
||||
Utf16String interface;
|
||||
if (local_name == HTML::TagNames::iframe && element_ns == Namespace::HTML) {
|
||||
interface = "HTMLIFrameElement"_utf16;
|
||||
} else if (local_name == HTML::TagNames::script && element_ns == Namespace::HTML) {
|
||||
interface = "HTMLScriptElement"_utf16;
|
||||
} else if (local_name == SVG::TagNames::script && element_ns == Namespace::SVG) {
|
||||
interface = "SVGScriptElement"_utf16;
|
||||
} else {
|
||||
interface = "Element"_utf16;
|
||||
}
|
||||
Utf16String const interface = element_interface_name(local_name, element_ns.value());
|
||||
|
||||
// 6. Let expectedType be null.
|
||||
Optional<Utf16String> expected_type {};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue