aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Brúnó Salomon <35275408+bru02@users.noreply.github.com> 2023-08-19 04:59:23 +0200
committerGravatar GitHub <noreply@github.com> 2023-08-18 19:59:23 -0700
commitc2ec47ff320fc52298d9488ebbc2d89a19a0ec8a (patch)
treef19b054ad4358b667c6fe4dd728164424e222fa4
parent26036a390b71e079e31694d2dcf64572e30db74f (diff)
downloadbun-c2ec47ff320fc52298d9488ebbc2d89a19a0ec8a.tar.gz
bun-c2ec47ff320fc52298d9488ebbc2d89a19a0ec8a.tar.zst
bun-c2ec47ff320fc52298d9488ebbc2d89a19a0ec8a.zip
feat: add self-closing & can-have-content (#4206)
-rw-r--r--packages/bun-types/html-rewriter.d.ts8
-rw-r--r--src/bun.js/api/html_rewriter.classes.ts6
-rw-r--r--src/bun.js/api/html_rewriter.zig70
-rw-r--r--src/bun.js/bindings/ZigGeneratedClasses.cpp32
-rw-r--r--src/bun.js/bindings/generated_classes.zig8
-rw-r--r--src/deps/lol-html.zig10
-rw-r--r--test/js/workerd/html-rewriter.test.js39
7 files changed, 147 insertions, 26 deletions
diff --git a/packages/bun-types/html-rewriter.d.ts b/packages/bun-types/html-rewriter.d.ts
index 7f260fd5a..8cc4e9657 100644
--- a/packages/bun-types/html-rewriter.d.ts
+++ b/packages/bun-types/html-rewriter.d.ts
@@ -50,6 +50,14 @@ declare namespace HTMLRewriterTypes {
tagName: string;
readonly attributes: IterableIterator<string[]>;
readonly removed: boolean;
+ /** Whether the element is explicitly self-closing, e.g. `<foo />` */
+ readonly selfClosing: boolean;
+ /**
+ * Whether the element can have inner content. Returns `true` unless
+ * - the element is an [HTML void element](https://html.spec.whatwg.org/multipage/syntax.html#void-elements)
+ * - or it's self-closing in a foreign context (eg. in SVG, MathML).
+ */
+ readonly canHaveContent: boolean;
readonly namespaceURI: string;
getAttribute(name: string): string | null;
hasAttribute(name: string): boolean;
diff --git a/src/bun.js/api/html_rewriter.classes.ts b/src/bun.js/api/html_rewriter.classes.ts
index a447a0edc..f904f6f29 100644
--- a/src/bun.js/api/html_rewriter.classes.ts
+++ b/src/bun.js/api/html_rewriter.classes.ts
@@ -251,6 +251,12 @@ export default [
removed: {
getter: "getRemoved",
},
+ selfClosing: {
+ getter: "getSelfClosing",
+ },
+ canHaveContent: {
+ getter: "getCanHaveContent",
+ },
namespaceURI: {
getter: "getNamespaceURI",
cache: true,
diff --git a/src/bun.js/api/html_rewriter.zig b/src/bun.js/api/html_rewriter.zig
index 0c1cbebab..fd0b5dd9e 100644
--- a/src/bun.js/api/html_rewriter.zig
+++ b/src/bun.js/api/html_rewriter.zig
@@ -950,7 +950,7 @@ pub const TextChunk = struct {
pub usingnamespace JSC.Codegen.JSTextChunk;
- fn contentHandler(this: *TextChunk, comptime Callback: (fn (*LOLHTML.TextChunk, []const u8, bool) LOLHTML.Error!void), callFrame: *JSC.CallFrame, globalObject: *JSGlobalObject, content: ZigString, contentOptions: ?ContentOptions) JSValue {
+ fn contentHandler(this: *TextChunk, comptime Callback: (fn (*LOLHTML.TextChunk, []const u8, bool) LOLHTML.Error!void), thisObject: JSValue, globalObject: *JSGlobalObject, content: ZigString, contentOptions: ?ContentOptions) JSValue {
if (this.text_chunk == null)
return JSC.JSValue.jsUndefined();
var content_slice = content.toSlice(bun.default_allocator);
@@ -962,7 +962,7 @@ pub const TextChunk = struct {
contentOptions != null and contentOptions.?.html,
) catch return throwLOLHTMLError(globalObject);
- return callFrame.this();
+ return thisObject;
}
pub fn before_(
@@ -972,7 +972,7 @@ pub const TextChunk = struct {
content: ZigString,
contentOptions: ?ContentOptions,
) JSValue {
- return this.contentHandler(LOLHTML.TextChunk.before, callFrame, globalObject, content, contentOptions);
+ return this.contentHandler(LOLHTML.TextChunk.before, callFrame.this(), globalObject, content, contentOptions);
}
pub fn after_(
@@ -982,7 +982,7 @@ pub const TextChunk = struct {
content: ZigString,
contentOptions: ?ContentOptions,
) JSValue {
- return this.contentHandler(LOLHTML.TextChunk.after, callFrame, globalObject, content, contentOptions);
+ return this.contentHandler(LOLHTML.TextChunk.after, callFrame.this(), globalObject, content, contentOptions);
}
pub fn replace_(
@@ -992,7 +992,7 @@ pub const TextChunk = struct {
content: ZigString,
contentOptions: ?ContentOptions,
) JSValue {
- return this.contentHandler(LOLHTML.TextChunk.replace, callFrame, globalObject, content, contentOptions);
+ return this.contentHandler(LOLHTML.TextChunk.replace, callFrame.this(), globalObject, content, contentOptions);
}
pub const before = JSC.wrapInstanceMethod(TextChunk, "before_", false);
@@ -1093,7 +1093,7 @@ pub const DocEnd = struct {
pub usingnamespace JSC.Codegen.JSDocEnd;
- fn contentHandler(this: *DocEnd, comptime Callback: (fn (*LOLHTML.DocEnd, []const u8, bool) LOLHTML.Error!void), callFrame: *JSC.CallFrame, globalObject: *JSGlobalObject, content: ZigString, contentOptions: ?ContentOptions) JSValue {
+ fn contentHandler(this: *DocEnd, comptime Callback: (fn (*LOLHTML.DocEnd, []const u8, bool) LOLHTML.Error!void), thisObject: JSValue, globalObject: *JSGlobalObject, content: ZigString, contentOptions: ?ContentOptions) JSValue {
if (this.doc_end == null)
return JSValue.jsNull();
@@ -1106,7 +1106,7 @@ pub const DocEnd = struct {
contentOptions != null and contentOptions.?.html,
) catch return throwLOLHTMLError(globalObject);
- return callFrame.this();
+ return thisObject;
}
pub fn append_(
@@ -1116,7 +1116,7 @@ pub const DocEnd = struct {
content: ZigString,
contentOptions: ?ContentOptions,
) JSValue {
- return this.contentHandler(LOLHTML.DocEnd.append, callFrame, globalObject, content, contentOptions);
+ return this.contentHandler(LOLHTML.DocEnd.append, callFrame.this(), globalObject, content, contentOptions);
}
pub const append = JSC.wrapInstanceMethod(DocEnd, "append_", false);
@@ -1132,7 +1132,7 @@ pub const Comment = struct {
pub usingnamespace JSC.Codegen.JSComment;
- fn contentHandler(this: *Comment, comptime Callback: (fn (*LOLHTML.Comment, []const u8, bool) LOLHTML.Error!void), callFrame: *JSC.CallFrame, globalObject: *JSGlobalObject, content: ZigString, contentOptions: ?ContentOptions) JSValue {
+ fn contentHandler(this: *Comment, comptime Callback: (fn (*LOLHTML.Comment, []const u8, bool) LOLHTML.Error!void), thisObject: JSValue, globalObject: *JSGlobalObject, content: ZigString, contentOptions: ?ContentOptions) JSValue {
if (this.comment == null)
return JSValue.jsNull();
var content_slice = content.toSlice(bun.default_allocator);
@@ -1144,7 +1144,7 @@ pub const Comment = struct {
contentOptions != null and contentOptions.?.html,
) catch return throwLOLHTMLError(globalObject);
- return callFrame.this();
+ return thisObject;
}
pub fn before_(
@@ -1154,7 +1154,7 @@ pub const Comment = struct {
content: ZigString,
contentOptions: ?ContentOptions,
) JSValue {
- return this.contentHandler(LOLHTML.Comment.before, callFrame, globalObject, content, contentOptions);
+ return this.contentHandler(LOLHTML.Comment.before, callFrame.this(), globalObject, content, contentOptions);
}
pub fn after_(
@@ -1164,7 +1164,7 @@ pub const Comment = struct {
content: ZigString,
contentOptions: ?ContentOptions,
) JSValue {
- return this.contentHandler(LOLHTML.Comment.after, callFrame, globalObject, content, contentOptions);
+ return this.contentHandler(LOLHTML.Comment.after, callFrame.this(), globalObject, content, contentOptions);
}
pub fn replace_(
@@ -1174,7 +1174,7 @@ pub const Comment = struct {
content: ZigString,
contentOptions: ?ContentOptions,
) JSValue {
- return this.contentHandler(LOLHTML.Comment.replace, callFrame, globalObject, content, contentOptions);
+ return this.contentHandler(LOLHTML.Comment.replace, callFrame.this(), globalObject, content, contentOptions);
}
pub const before = JSC.wrapInstanceMethod(Comment, "before_", false);
@@ -1253,7 +1253,7 @@ pub const EndTag = struct {
pub usingnamespace JSC.Codegen.JSEndTag;
- fn contentHandler(this: *EndTag, comptime Callback: (fn (*LOLHTML.EndTag, []const u8, bool) LOLHTML.Error!void), callFrame: *JSC.CallFrame, globalObject: *JSGlobalObject, content: ZigString, contentOptions: ?ContentOptions) JSValue {
+ fn contentHandler(this: *EndTag, comptime Callback: (fn (*LOLHTML.EndTag, []const u8, bool) LOLHTML.Error!void), thisObject: JSValue, globalObject: *JSGlobalObject, content: ZigString, contentOptions: ?ContentOptions) JSValue {
if (this.end_tag == null)
return JSValue.jsNull();
@@ -1266,7 +1266,7 @@ pub const EndTag = struct {
contentOptions != null and contentOptions.?.html,
) catch return throwLOLHTMLError(globalObject);
- return callFrame.this();
+ return thisObject;
}
pub fn before_(
@@ -1276,7 +1276,7 @@ pub const EndTag = struct {
content: ZigString,
contentOptions: ?ContentOptions,
) JSValue {
- return this.contentHandler(LOLHTML.EndTag.before, callFrame, globalObject, content, contentOptions);
+ return this.contentHandler(LOLHTML.EndTag.before, callFrame.this(), globalObject, content, contentOptions);
}
pub fn after_(
@@ -1286,7 +1286,7 @@ pub const EndTag = struct {
content: ZigString,
contentOptions: ?ContentOptions,
) JSValue {
- return this.contentHandler(LOLHTML.EndTag.after, callFrame, globalObject, content, contentOptions);
+ return this.contentHandler(LOLHTML.EndTag.after, callFrame.this(), globalObject, content, contentOptions);
}
pub fn replace_(
@@ -1296,7 +1296,7 @@ pub const EndTag = struct {
content: ZigString,
contentOptions: ?ContentOptions,
) JSValue {
- return this.contentHandler(LOLHTML.EndTag.replace, callFrame, globalObject, content, contentOptions);
+ return this.contentHandler(LOLHTML.EndTag.replace, callFrame.this(), globalObject, content, contentOptions);
}
pub const before = JSC.wrapInstanceMethod(EndTag, "before_", false);
@@ -1481,7 +1481,7 @@ pub const Element = struct {
pub const setAttribute = JSC.wrapInstanceMethod(Element, "setAttribute_", false);
pub const removeAttribute = JSC.wrapInstanceMethod(Element, "removeAttribute_", false);
- fn contentHandler(this: *Element, comptime Callback: (fn (*LOLHTML.Element, []const u8, bool) LOLHTML.Error!void), callFrame: *JSC.CallFrame, globalObject: *JSGlobalObject, content: ZigString, contentOptions: ?ContentOptions) JSValue {
+ fn contentHandler(this: *Element, comptime Callback: (fn (*LOLHTML.Element, []const u8, bool) LOLHTML.Error!void), thisObject: JSValue, globalObject: *JSGlobalObject, content: ZigString, contentOptions: ?ContentOptions) JSValue {
if (this.element == null)
return JSValue.jsUndefined();
@@ -1494,7 +1494,7 @@ pub const Element = struct {
contentOptions != null and contentOptions.?.html,
) catch return throwLOLHTMLError(globalObject);
- return callFrame.this();
+ return thisObject;
}
/// Inserts content before the element.
@@ -1502,7 +1502,7 @@ pub const Element = struct {
return contentHandler(
this,
LOLHTML.Element.before,
- callFrame,
+ callFrame.this(),
globalObject,
content,
contentOptions,
@@ -1514,7 +1514,7 @@ pub const Element = struct {
return contentHandler(
this,
LOLHTML.Element.after,
- callFrame,
+ callFrame.this(),
globalObject,
content,
contentOptions,
@@ -1526,7 +1526,7 @@ pub const Element = struct {
return contentHandler(
this,
LOLHTML.Element.prepend,
- callFrame,
+ callFrame.this(),
globalObject,
content,
contentOptions,
@@ -1538,7 +1538,7 @@ pub const Element = struct {
return contentHandler(
this,
LOLHTML.Element.append,
- callFrame,
+ callFrame.this(),
globalObject,
content,
contentOptions,
@@ -1550,7 +1550,7 @@ pub const Element = struct {
return contentHandler(
this,
LOLHTML.Element.replace,
- callFrame,
+ callFrame.this(),
globalObject,
content,
contentOptions,
@@ -1562,7 +1562,7 @@ pub const Element = struct {
return contentHandler(
this,
LOLHTML.Element.setInnerContent,
- callFrame,
+ callFrame.this(),
globalObject,
content,
contentOptions,
@@ -1636,6 +1636,24 @@ pub const Element = struct {
return JSValue.jsBoolean(this.element.?.isRemoved());
}
+ pub fn getSelfClosing(
+ this: *Element,
+ _: *JSGlobalObject,
+ ) callconv(.C) JSValue {
+ if (this.element == null)
+ return JSValue.jsUndefined();
+ return JSValue.jsBoolean(this.element.?.isSelfClosing());
+ }
+
+ pub fn getCanHaveContent(
+ this: *Element,
+ _: *JSGlobalObject,
+ ) callconv(.C) JSValue {
+ if (this.element == null)
+ return JSValue.jsUndefined();
+ return JSValue.jsBoolean(this.element.?.canHaveContent());
+ }
+
pub fn getNamespaceURI(
this: *Element,
globalObject: *JSGlobalObject,
diff --git a/src/bun.js/bindings/ZigGeneratedClasses.cpp b/src/bun.js/bindings/ZigGeneratedClasses.cpp
index 066732db9..0b1e1702b 100644
--- a/src/bun.js/bindings/ZigGeneratedClasses.cpp
+++ b/src/bun.js/bindings/ZigGeneratedClasses.cpp
@@ -6054,6 +6054,9 @@ JSC_DECLARE_CUSTOM_GETTER(ElementPrototype__attributesGetterWrap);
extern "C" EncodedJSValue ElementPrototype__before(void* ptr, JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame);
JSC_DECLARE_HOST_FUNCTION(ElementPrototype__beforeCallback);
+extern "C" JSC::EncodedJSValue ElementPrototype__getCanHaveContent(void* ptr, JSC::JSGlobalObject* lexicalGlobalObject);
+JSC_DECLARE_CUSTOM_GETTER(ElementPrototype__canHaveContentGetterWrap);
+
extern "C" EncodedJSValue ElementPrototype__getAttribute(void* ptr, JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame);
JSC_DECLARE_HOST_FUNCTION(ElementPrototype__getAttributeCallback);
@@ -6084,6 +6087,9 @@ JSC_DECLARE_CUSTOM_GETTER(ElementPrototype__removedGetterWrap);
extern "C" EncodedJSValue ElementPrototype__replace(void* ptr, JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame);
JSC_DECLARE_HOST_FUNCTION(ElementPrototype__replaceCallback);
+extern "C" JSC::EncodedJSValue ElementPrototype__getSelfClosing(void* ptr, JSC::JSGlobalObject* lexicalGlobalObject);
+JSC_DECLARE_CUSTOM_GETTER(ElementPrototype__selfClosingGetterWrap);
+
extern "C" EncodedJSValue ElementPrototype__setAttribute(void* ptr, JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame);
JSC_DECLARE_HOST_FUNCTION(ElementPrototype__setAttributeCallback);
@@ -6103,6 +6109,7 @@ static const HashTableValue JSElementPrototypeTableValues[] = {
{ "append"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function | PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::NativeFunctionType, ElementPrototype__appendCallback, 1 } },
{ "attributes"_s, static_cast<unsigned>(JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute | PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::GetterSetterType, ElementPrototype__attributesGetterWrap, 0 } },
{ "before"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function | PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::NativeFunctionType, ElementPrototype__beforeCallback, 1 } },
+ { "canHaveContent"_s, static_cast<unsigned>(JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute | PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::GetterSetterType, ElementPrototype__canHaveContentGetterWrap, 0 } },
{ "getAttribute"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function | PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::NativeFunctionType, ElementPrototype__getAttributeCallback, 1 } },
{ "hasAttribute"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function | PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::NativeFunctionType, ElementPrototype__hasAttributeCallback, 1 } },
{ "namespaceURI"_s, static_cast<unsigned>(JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute | PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::GetterSetterType, ElementPrototype__namespaceURIGetterWrap, 0 } },
@@ -6113,6 +6120,7 @@ static const HashTableValue JSElementPrototypeTableValues[] = {
{ "removeAttribute"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function | PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::NativeFunctionType, ElementPrototype__removeAttributeCallback, 1 } },
{ "removed"_s, static_cast<unsigned>(JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute | PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::GetterSetterType, ElementPrototype__removedGetterWrap, 0 } },
{ "replace"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function | PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::NativeFunctionType, ElementPrototype__replaceCallback, 1 } },
+ { "selfClosing"_s, static_cast<unsigned>(JSC::PropertyAttribute::ReadOnly | JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute | PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::GetterSetterType, ElementPrototype__selfClosingGetterWrap, 0 } },
{ "setAttribute"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function | PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::NativeFunctionType, ElementPrototype__setAttributeCallback, 2 } },
{ "setInnerContent"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function | PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::NativeFunctionType, ElementPrototype__setInnerContentCallback, 1 } },
{ "tagName"_s, static_cast<unsigned>(JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DOMAttribute | PropertyAttribute::DontDelete), NoIntrinsic, { HashTableValue::GetterSetterType, ElementPrototype__tagNameGetterWrap, ElementPrototype__tagNameSetterWrap } }
@@ -6228,6 +6236,18 @@ JSC_DEFINE_HOST_FUNCTION(ElementPrototype__beforeCallback, (JSGlobalObject * lex
return ElementPrototype__before(thisObject->wrapped(), lexicalGlobalObject, callFrame);
}
+JSC_DEFINE_CUSTOM_GETTER(ElementPrototype__canHaveContentGetterWrap, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, PropertyName attributeName))
+{
+ auto& vm = lexicalGlobalObject->vm();
+ Zig::GlobalObject* globalObject = reinterpret_cast<Zig::GlobalObject*>(lexicalGlobalObject);
+ auto throwScope = DECLARE_THROW_SCOPE(vm);
+ JSElement* thisObject = jsCast<JSElement*>(JSValue::decode(thisValue));
+ JSC::EnsureStillAliveScope thisArg = JSC::EnsureStillAliveScope(thisObject);
+ JSC::EncodedJSValue result = ElementPrototype__getCanHaveContent(thisObject->wrapped(), globalObject);
+ RETURN_IF_EXCEPTION(throwScope, {});
+ RELEASE_AND_RETURN(throwScope, result);
+}
+
JSC_DEFINE_HOST_FUNCTION(ElementPrototype__getAttributeCallback, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame))
{
auto& vm = lexicalGlobalObject->vm();
@@ -6495,6 +6515,18 @@ JSC_DEFINE_HOST_FUNCTION(ElementPrototype__replaceCallback, (JSGlobalObject * le
return ElementPrototype__replace(thisObject->wrapped(), lexicalGlobalObject, callFrame);
}
+JSC_DEFINE_CUSTOM_GETTER(ElementPrototype__selfClosingGetterWrap, (JSGlobalObject * lexicalGlobalObject, EncodedJSValue thisValue, PropertyName attributeName))
+{
+ auto& vm = lexicalGlobalObject->vm();
+ Zig::GlobalObject* globalObject = reinterpret_cast<Zig::GlobalObject*>(lexicalGlobalObject);
+ auto throwScope = DECLARE_THROW_SCOPE(vm);
+ JSElement* thisObject = jsCast<JSElement*>(JSValue::decode(thisValue));
+ JSC::EnsureStillAliveScope thisArg = JSC::EnsureStillAliveScope(thisObject);
+ JSC::EncodedJSValue result = ElementPrototype__getSelfClosing(thisObject->wrapped(), globalObject);
+ RETURN_IF_EXCEPTION(throwScope, {});
+ RELEASE_AND_RETURN(throwScope, result);
+}
+
JSC_DEFINE_HOST_FUNCTION(ElementPrototype__setAttributeCallback, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame))
{
auto& vm = lexicalGlobalObject->vm();
diff --git a/src/bun.js/bindings/generated_classes.zig b/src/bun.js/bindings/generated_classes.zig
index 70cf4728d..bf1942fd6 100644
--- a/src/bun.js/bindings/generated_classes.zig
+++ b/src/bun.js/bindings/generated_classes.zig
@@ -1824,6 +1824,9 @@ pub const JSElement = struct {
if (@TypeOf(Element.before) != CallbackType)
@compileLog("Expected Element.before to be a callback but received " ++ @typeName(@TypeOf(Element.before)));
+ if (@TypeOf(Element.getCanHaveContent) != GetterType)
+ @compileLog("Expected Element.getCanHaveContent to be a getter");
+
if (@TypeOf(Element.getAttribute) != CallbackType)
@compileLog("Expected Element.getAttribute to be a callback but received " ++ @typeName(@TypeOf(Element.getAttribute)));
if (@TypeOf(Element.hasAttribute) != CallbackType)
@@ -1846,6 +1849,9 @@ pub const JSElement = struct {
if (@TypeOf(Element.replace) != CallbackType)
@compileLog("Expected Element.replace to be a callback but received " ++ @typeName(@TypeOf(Element.replace)));
+ if (@TypeOf(Element.getSelfClosing) != GetterType)
+ @compileLog("Expected Element.getSelfClosing to be a getter");
+
if (@TypeOf(Element.setAttribute) != CallbackType)
@compileLog("Expected Element.setAttribute to be a callback but received " ++ @typeName(@TypeOf(Element.setAttribute)));
if (@TypeOf(Element.setInnerContent) != CallbackType)
@@ -1862,8 +1868,10 @@ pub const JSElement = struct {
@export(Element.finalize, .{ .name = "ElementClass__finalize" });
@export(Element.getAttribute, .{ .name = "ElementPrototype__getAttribute" });
@export(Element.getAttributes, .{ .name = "ElementPrototype__getAttributes" });
+ @export(Element.getCanHaveContent, .{ .name = "ElementPrototype__getCanHaveContent" });
@export(Element.getNamespaceURI, .{ .name = "ElementPrototype__getNamespaceURI" });
@export(Element.getRemoved, .{ .name = "ElementPrototype__getRemoved" });
+ @export(Element.getSelfClosing, .{ .name = "ElementPrototype__getSelfClosing" });
@export(Element.getTagName, .{ .name = "ElementPrototype__getTagName" });
@export(Element.hasAttribute, .{ .name = "ElementPrototype__hasAttribute" });
@export(Element.onEndTag, .{ .name = "ElementPrototype__onEndTag" });
diff --git a/src/deps/lol-html.zig b/src/deps/lol-html.zig
index cd183ee5f..9a9137856 100644
--- a/src/deps/lol-html.zig
+++ b/src/deps/lol-html.zig
@@ -391,6 +391,8 @@ pub const Element = opaque {
extern fn lol_html_element_remove(element: *const Element) void;
extern fn lol_html_element_remove_and_keep_content(element: *const Element) void;
extern fn lol_html_element_is_removed(element: *const Element) bool;
+ extern fn lol_html_element_is_self_closing(element: *const Element) bool;
+ extern fn lol_html_element_can_have_content(element: *const Element) bool;
extern fn lol_html_element_user_data_set(element: *const Element, user_data: ?*anyopaque) void;
extern fn lol_html_element_user_data_get(element: *const Element) ?*anyopaque;
extern fn lol_html_element_add_end_tag_handler(element: *Element, end_tag_handler: lol_html_end_tag_handler_t, user_data: ?*anyopaque) c_int;
@@ -493,6 +495,14 @@ pub const Element = opaque {
auto_disable();
return lol_html_element_is_removed(element);
}
+ pub fn isSelfClosing(element: *const Element) bool {
+ auto_disable();
+ return lol_html_element_is_self_closing(element);
+ }
+ pub fn canHaveContent(element: *const Element) bool {
+ auto_disable();
+ return lol_html_element_can_have_content(element);
+ }
pub fn setUserData(element: *const Element, user_data: ?*anyopaque) void {
auto_disable();
lol_html_element_user_data_set(element, user_data);
diff --git a/test/js/workerd/html-rewriter.test.js b/test/js/workerd/html-rewriter.test.js
index 44961df3b..58411209a 100644
--- a/test/js/workerd/html-rewriter.test.js
+++ b/test/js/workerd/html-rewriter.test.js
@@ -315,6 +315,45 @@ describe("HTMLRewriter", () => {
expect(lastInTextNode).toBeBoolean();
});
+
+ it("it supports selfClosing", async () => {
+ const selfClosing = {}
+ await new HTMLRewriter()
+ .on("*", {
+ element(el) {
+ selfClosing[el.tagName] = el.selfClosing;
+ },
+ })
+
+ .transform(new Response("<p>Lorem ipsum!<br></p><div />"))
+ .text();
+
+ expect(selfClosing).toEqual({
+ p: false,
+ br: false,
+ div: true,
+ });
+ });
+
+ it("it supports canHaveContent", async () => {
+ const canHaveContent = {}
+ await new HTMLRewriter()
+ .on("*", {
+ element(el) {
+ canHaveContent[el.tagName] = el.canHaveContent;
+ },
+ })
+ .transform(new Response("<p>Lorem ipsum!<br></p><div /><svg><circle /></svg>"))
+ .text();
+
+ expect(canHaveContent).toEqual({
+ p: true,
+ br: false,
+ div: true,
+ svg: true,
+ circle: false,
+ });
+ });
});
// By not segfaulting, this test passes