mirror of
https://github.com/oven-sh/bun
synced 2026-02-13 12:29:07 +00:00
872 lines
36 KiB
Zig
872 lines
36 KiB
Zig
pub const Error = error{Fail};
|
|
pub const MemorySettings = extern struct {
|
|
preallocated_parsing_buffer_size: usize,
|
|
max_allowed_memory_usage: usize,
|
|
};
|
|
|
|
pub const SourceLocationBytes = extern struct {
|
|
start: usize,
|
|
end: usize,
|
|
};
|
|
|
|
inline fn auto_disable() void {
|
|
if (comptime bun.FeatureFlags.disable_lolhtml)
|
|
unreachable;
|
|
}
|
|
|
|
/// rust panics if the pointer itself is zero, even if the passed length is zero
|
|
/// to work around that, we use a static null-terminated pointer
|
|
/// https://github.com/oven-sh/bun/issues/2323
|
|
fn ptrWithoutPanic(buf: []const u8) [*]const u8 {
|
|
const null_terminated_ptr = struct {
|
|
// we must use a static pointer so the lifetime of this pointer is long enough
|
|
const null_terminated_ptr: []const u8 = &[_]u8{0};
|
|
}.null_terminated_ptr;
|
|
|
|
if (buf.len == 0)
|
|
return null_terminated_ptr.ptr;
|
|
|
|
return buf.ptr;
|
|
}
|
|
|
|
pub const HTMLRewriter = opaque {
|
|
extern fn lol_html_rewriter_write(rewriter: *HTMLRewriter, chunk: [*]const u8, chunk_len: usize) c_int;
|
|
extern fn lol_html_rewriter_end(rewriter: *HTMLRewriter) c_int;
|
|
extern fn lol_html_rewriter_free(rewriter: *HTMLRewriter) void;
|
|
|
|
pub fn write(rewriter: *HTMLRewriter, chunk: []const u8) Error!void {
|
|
auto_disable();
|
|
const ptr = ptrWithoutPanic(chunk);
|
|
const rc = rewriter.lol_html_rewriter_write(ptr, chunk.len);
|
|
if (rc < 0)
|
|
return error.Fail;
|
|
}
|
|
|
|
/// Completes rewriting and flushes the remaining output.
|
|
///
|
|
/// Returns 0 in case of success and -1 otherwise. The actual error message
|
|
/// can be obtained using `lol_html_take_last_error` function.
|
|
///
|
|
/// WARNING: after calling this function, further attempts to use the rewriter
|
|
/// (other than `lol_html_rewriter_free`) will cause a thread panic.
|
|
pub fn end(rewriter: *HTMLRewriter) Error!void {
|
|
auto_disable();
|
|
|
|
if (rewriter.lol_html_rewriter_end() < 0)
|
|
return error.Fail;
|
|
}
|
|
|
|
pub fn deinit(this: *HTMLRewriter) void {
|
|
auto_disable();
|
|
this.lol_html_rewriter_free();
|
|
}
|
|
|
|
pub const Builder = opaque {
|
|
extern fn lol_html_rewriter_builder_new() *HTMLRewriter.Builder;
|
|
extern fn lol_html_rewriter_builder_add_element_content_handlers(
|
|
builder: *HTMLRewriter.Builder,
|
|
selector: *const HTMLSelector,
|
|
element_handler: ?lol_html_element_handler_t,
|
|
element_handler_user_data: ?*anyopaque,
|
|
comment_handler: ?lol_html_comment_handler_t,
|
|
comment_handler_user_data: ?*anyopaque,
|
|
text_handler: ?lol_html_text_handler_handler_t,
|
|
text_handler_user_data: ?*anyopaque,
|
|
) c_int;
|
|
extern fn lol_html_rewriter_builder_free(builder: *HTMLRewriter.Builder) void;
|
|
extern fn lol_html_rewriter_build(
|
|
builder: *HTMLRewriter.Builder,
|
|
encoding: [*]const u8,
|
|
encoding_len: usize,
|
|
memory_settings: MemorySettings,
|
|
output_sink: ?*const fn ([*]const u8, usize, *anyopaque) callconv(.C) void,
|
|
output_sink_user_data: *anyopaque,
|
|
strict: bool,
|
|
) ?*HTMLRewriter;
|
|
extern fn unstable_lol_html_rewriter_build_with_esi_tags(
|
|
builder: *HTMLRewriter.Builder,
|
|
encoding: [*]const u8,
|
|
encoding_len: usize,
|
|
memory_settings: MemorySettings,
|
|
output_sink: ?*const fn ([*]const u8, usize, *anyopaque) callconv(.C) void,
|
|
output_sink_user_data: *anyopaque,
|
|
strict: bool,
|
|
) ?*HTMLRewriter;
|
|
|
|
pub fn deinit(this: *HTMLRewriter.Builder) void {
|
|
auto_disable();
|
|
this.lol_html_rewriter_builder_free();
|
|
}
|
|
|
|
extern fn lol_html_rewriter_builder_add_document_content_handlers(
|
|
builder: *HTMLRewriter.Builder,
|
|
doctype_handler: ?DirectiveFunctionType(DocType),
|
|
doctype_handler_user_data: ?*anyopaque,
|
|
comment_handler: ?lol_html_comment_handler_t,
|
|
comment_handler_user_data: ?*anyopaque,
|
|
text_handler: ?lol_html_text_handler_handler_t,
|
|
text_handler_user_data: ?*anyopaque,
|
|
doc_end_handler: ?lol_html_doc_end_handler_t,
|
|
doc_end_user_data: ?*anyopaque,
|
|
) void;
|
|
|
|
pub fn init() *HTMLRewriter.Builder {
|
|
auto_disable();
|
|
return lol_html_rewriter_builder_new();
|
|
}
|
|
|
|
/// Adds document-level content handlers to the builder.
|
|
///
|
|
/// If a particular handler is not required then NULL can be passed
|
|
/// instead. Don't use stub handlers in this case as this affects
|
|
/// performance - rewriter skips parsing of the content that doesn't
|
|
/// need to be processed.
|
|
///
|
|
/// Each handler can optionally have associated user data which will be
|
|
/// passed to the handler on each invocation along with the rewritable
|
|
/// unit argument.
|
|
///
|
|
/// If any of handlers return LOL_HTML_STOP directive then rewriting
|
|
/// stops immediately and `write()` or `end()` of the rewriter methods
|
|
/// return an error code.
|
|
///
|
|
/// WARNING: Pointers passed to handlers are valid only during the
|
|
/// handler execution. So they should never be leaked outside of handlers.
|
|
pub fn addDocumentContentHandlers(
|
|
builder: *HTMLRewriter.Builder,
|
|
comptime DocTypeHandler: type,
|
|
comptime doctype_handler: ?DirectiveFunctionTypeForHandler(DocType, DocTypeHandler),
|
|
doctype_handler_data: ?*DocTypeHandler,
|
|
comptime CommentHandler: type,
|
|
comptime comment_handler: ?DirectiveFunctionTypeForHandler(Comment, CommentHandler),
|
|
comment_handler_data: ?*CommentHandler,
|
|
comptime TextChunkHandler: type,
|
|
comptime text_chunk_handler: ?DirectiveFunctionTypeForHandler(TextChunk, TextChunkHandler),
|
|
text_chunk_handler_data: ?*TextChunkHandler,
|
|
comptime DocEndHandler: type,
|
|
comptime end_tag_handler: ?DirectiveFunctionTypeForHandler(DocEnd, DocEndHandler),
|
|
end_tag_handler_data: ?*DocEndHandler,
|
|
) void {
|
|
auto_disable();
|
|
|
|
builder.lol_html_rewriter_builder_add_document_content_handlers(
|
|
if (doctype_handler_data != null)
|
|
DirectiveHandler(DocType, DocTypeHandler, doctype_handler.?)
|
|
else
|
|
null,
|
|
doctype_handler_data,
|
|
if (comment_handler_data != null)
|
|
DirectiveHandler(Comment, CommentHandler, comment_handler.?)
|
|
else
|
|
null,
|
|
comment_handler_data,
|
|
if (text_chunk_handler_data != null)
|
|
DirectiveHandler(TextChunk, TextChunkHandler, text_chunk_handler.?)
|
|
else
|
|
null,
|
|
text_chunk_handler_data,
|
|
if (end_tag_handler_data != null)
|
|
DirectiveHandler(DocEnd, DocEndHandler, end_tag_handler.?)
|
|
else
|
|
null,
|
|
end_tag_handler_data,
|
|
);
|
|
}
|
|
|
|
/// Adds element content handlers to the builder for the
|
|
/// given CSS selector.
|
|
///
|
|
/// Selector should be a valid UTF8-string.
|
|
///
|
|
/// If a particular handler is not required then NULL can be passed
|
|
/// instead. Don't use stub handlers in this case as this affects
|
|
/// performance - rewriter skips parsing of the content that doesn't
|
|
/// need to be processed.
|
|
///
|
|
/// Each handler can optionally have associated user data which will be
|
|
/// passed to the handler on each invocation along with the rewritable
|
|
/// unit argument.
|
|
///
|
|
/// If any of handlers return LOL_HTML_STOP directive then rewriting
|
|
/// stops immediately and `write()` or `end()` of the rewriter methods
|
|
/// return an error code.
|
|
///
|
|
/// Returns 0 in case of success and -1 otherwise. The actual error message
|
|
/// can be obtained using `lol_html_take_last_error` function.
|
|
///
|
|
/// WARNING: Pointers passed to handlers are valid only during the
|
|
/// handler execution. So they should never be leaked outside of handlers.
|
|
pub fn addElementContentHandlers(
|
|
builder: *HTMLRewriter.Builder,
|
|
selector: *HTMLSelector,
|
|
comptime ElementHandler: type,
|
|
comptime element_handler: ?DirectiveFunctionTypeForHandler(Element, ElementHandler),
|
|
element_handler_data: ?*ElementHandler,
|
|
comptime CommentHandler: type,
|
|
comptime comment_handler: ?DirectiveFunctionTypeForHandler(Comment, CommentHandler),
|
|
comment_handler_data: ?*CommentHandler,
|
|
comptime TextChunkHandler: type,
|
|
comptime text_chunk_handler: ?DirectiveFunctionTypeForHandler(TextChunk, TextChunkHandler),
|
|
text_chunk_handler_data: ?*TextChunkHandler,
|
|
) Error!void {
|
|
auto_disable();
|
|
return switch (builder.lol_html_rewriter_builder_add_element_content_handlers(
|
|
selector,
|
|
if (element_handler != null and element_handler_data != null)
|
|
DirectiveHandler(Element, ElementHandler, element_handler.?)
|
|
else
|
|
null,
|
|
element_handler_data,
|
|
if (comment_handler != null and comment_handler_data != null)
|
|
DirectiveHandler(Comment, CommentHandler, comment_handler.?)
|
|
else
|
|
null,
|
|
comment_handler_data,
|
|
if (text_chunk_handler != null and text_chunk_handler_data != null)
|
|
DirectiveHandler(TextChunk, TextChunkHandler, text_chunk_handler.?)
|
|
else
|
|
null,
|
|
text_chunk_handler_data,
|
|
)) {
|
|
-1 => error.Fail,
|
|
0 => {},
|
|
else => unreachable,
|
|
};
|
|
}
|
|
|
|
pub fn build(
|
|
builder: *HTMLRewriter.Builder,
|
|
encoding: Encoding,
|
|
memory_settings: MemorySettings,
|
|
strict: bool,
|
|
comptime OutputSink: type,
|
|
output_sink: *OutputSink,
|
|
comptime Writer: (fn (*OutputSink, bytes: []const u8) void),
|
|
comptime Done: (fn (*OutputSink) void),
|
|
) Error!*HTMLRewriter {
|
|
auto_disable();
|
|
|
|
const encoding_ = Encoding.label.getAssertContains(encoding);
|
|
return builder.lol_html_rewriter_build(
|
|
encoding_.ptr,
|
|
encoding_.len,
|
|
memory_settings,
|
|
OutputSinkFunction(OutputSink, Writer, Done),
|
|
output_sink,
|
|
strict,
|
|
) orelse return error.Fail;
|
|
}
|
|
|
|
fn OutputSinkFunction(
|
|
comptime OutputSinkType: type,
|
|
comptime Writer: (fn (*OutputSinkType, bytes: []const u8) void),
|
|
comptime Done: (fn (*OutputSinkType) void),
|
|
) (fn ([*]const u8, usize, *anyopaque) callconv(.C) void) {
|
|
return struct {
|
|
fn writeChunk(ptr: [*]const u8, len: usize, user_data: *anyopaque) callconv(.C) void {
|
|
auto_disable();
|
|
|
|
@setRuntimeSafety(false);
|
|
const this = @as(*OutputSinkType, @ptrCast(@alignCast(user_data)));
|
|
switch (len) {
|
|
0 => Done(this),
|
|
else => Writer(this, ptr[0..len]),
|
|
}
|
|
}
|
|
}.writeChunk;
|
|
}
|
|
};
|
|
};
|
|
|
|
pub const HTMLSelector = opaque {
|
|
extern fn lol_html_selector_parse(selector: [*]const u8, selector_len: usize) ?*HTMLSelector;
|
|
extern fn lol_html_selector_free(selector: *HTMLSelector) void;
|
|
|
|
/// Frees the memory held by the parsed selector object.
|
|
pub fn deinit(selector: *HTMLSelector) void {
|
|
auto_disable();
|
|
selector.lol_html_selector_free();
|
|
}
|
|
|
|
/// Parses given CSS selector string.
|
|
///
|
|
/// Returns NULL if parsing error occurs. The actual error message
|
|
/// can be obtained using `lol_html_take_last_error` function.
|
|
///
|
|
/// WARNING: Selector SHOULD NOT be deallocated if there are any active rewriter
|
|
/// builders that accepted it as an argument to `lol_html_rewriter_builder_add_element_content_handlers()`
|
|
/// method. Deallocate all dependant rewriter builders first and then
|
|
/// use `lol_html_selector_free` function to free the selector.
|
|
pub fn parse(selector: []const u8) Error!*HTMLSelector {
|
|
auto_disable();
|
|
|
|
if (lol_html_selector_parse(ptrWithoutPanic(selector), selector.len)) |ptr|
|
|
return ptr
|
|
else
|
|
return error.Fail;
|
|
}
|
|
};
|
|
pub const TextChunk = opaque {
|
|
extern fn lol_html_text_chunk_content_get(chunk: *const TextChunk) TextChunk.Content;
|
|
extern fn lol_html_text_chunk_is_last_in_text_node(chunk: *const TextChunk) bool;
|
|
extern fn lol_html_text_chunk_before(chunk: *TextChunk, content: [*]const u8, content_len: usize, is_html: bool) c_int;
|
|
extern fn lol_html_text_chunk_after(chunk: *TextChunk, content: [*]const u8, content_len: usize, is_html: bool) c_int;
|
|
extern fn lol_html_text_chunk_replace(chunk: *TextChunk, content: [*]const u8, content_len: usize, is_html: bool) c_int;
|
|
extern fn lol_html_text_chunk_remove(chunk: *TextChunk) void;
|
|
extern fn lol_html_text_chunk_is_removed(chunk: *const TextChunk) bool;
|
|
extern fn lol_html_text_chunk_user_data_set(chunk: *const TextChunk, user_data: ?*anyopaque) void;
|
|
extern fn lol_html_text_chunk_user_data_get(chunk: *const TextChunk) ?*anyopaque;
|
|
extern fn lol_html_text_chunk_source_location_bytes(chunk: *const TextChunk) SourceLocationBytes;
|
|
|
|
pub const Content = extern struct {
|
|
ptr: [*]const u8,
|
|
len: usize,
|
|
|
|
pub fn slice(this: Content) []const u8 {
|
|
auto_disable();
|
|
return this.ptr[0..this.len];
|
|
}
|
|
};
|
|
|
|
pub fn getContent(this: *const TextChunk) TextChunk.Content {
|
|
auto_disable();
|
|
return this.lol_html_text_chunk_content_get();
|
|
}
|
|
pub fn isLastInTextNode(this: *const TextChunk) bool {
|
|
auto_disable();
|
|
return this.lol_html_text_chunk_is_last_in_text_node();
|
|
}
|
|
/// Inserts the content string before the text chunk either as raw text or as HTML.
|
|
///
|
|
/// Content should be a valid UTF8-string.
|
|
///
|
|
/// Returns 0 in case of success and -1 otherwise. The actual error message
|
|
/// can be obtained using `lol_html_take_last_error` function.
|
|
pub fn before(this: *TextChunk, content: []const u8, is_html: bool) Error!void {
|
|
auto_disable();
|
|
if (this.lol_html_text_chunk_before(ptrWithoutPanic(content), content.len, is_html) < 0)
|
|
return error.Fail;
|
|
}
|
|
/// Inserts the content string after the text chunk either as raw text or as HTML.
|
|
///
|
|
/// Content should be a valid UTF8-string.
|
|
///
|
|
/// Returns 0 in case of success and -1 otherwise. The actual error message
|
|
/// can be obtained using `lol_html_take_last_error` function.
|
|
pub fn after(this: *TextChunk, content: []const u8, is_html: bool) Error!void {
|
|
auto_disable();
|
|
if (this.lol_html_text_chunk_after(ptrWithoutPanic(content), content.len, is_html) < 0)
|
|
return error.Fail;
|
|
}
|
|
// Replace the text chunk with the content of the string which is interpreted
|
|
// either as raw text or as HTML.
|
|
//
|
|
// Content should be a valid UTF8-string.
|
|
//
|
|
// Returns 0 in case of success and -1 otherwise. The actual error message
|
|
// can be obtained using `lol_html_take_last_error` function.
|
|
pub fn replace(this: *TextChunk, content: []const u8, is_html: bool) Error!void {
|
|
auto_disable();
|
|
if (this.lol_html_text_chunk_replace(ptrWithoutPanic(content), content.len, is_html) < 0)
|
|
return error.Fail;
|
|
}
|
|
/// Removes the text chunk.
|
|
pub fn remove(this: *TextChunk) void {
|
|
auto_disable();
|
|
return this.lol_html_text_chunk_remove();
|
|
}
|
|
pub fn isRemoved(this: *const TextChunk) bool {
|
|
auto_disable();
|
|
return this.lol_html_text_chunk_is_removed();
|
|
}
|
|
pub fn setUserData(this: *const TextChunk, comptime Type: type, value: ?*Type) void {
|
|
auto_disable();
|
|
return this.lol_html_text_chunk_user_data_set(value);
|
|
}
|
|
pub fn getUserData(this: *const TextChunk, comptime Type: type) ?*Type {
|
|
auto_disable();
|
|
return @as(?*Type, @ptrCast(@alignCast(this.lol_html_text_chunk_user_data_get())));
|
|
}
|
|
pub fn getSourceLocationBytes(this: *const TextChunk) SourceLocationBytes {
|
|
auto_disable();
|
|
return this.lol_html_text_chunk_source_location_bytes();
|
|
}
|
|
};
|
|
pub const Element = opaque {
|
|
extern fn lol_html_element_get_attribute(element: *const Element, name: [*]const u8, name_len: usize) HTMLString;
|
|
extern fn lol_html_element_has_attribute(element: *const Element, name: [*]const u8, name_len: usize) c_int;
|
|
extern fn lol_html_element_set_attribute(element: *Element, name: [*]const u8, name_len: usize, value: [*]const u8, value_len: usize) c_int;
|
|
extern fn lol_html_element_remove_attribute(element: *Element, name: [*]const u8, name_len: usize) c_int;
|
|
extern fn lol_html_element_before(element: *Element, content: [*]const u8, content_len: usize, is_html: bool) c_int;
|
|
extern fn lol_html_element_prepend(element: *Element, content: [*]const u8, content_len: usize, is_html: bool) c_int;
|
|
extern fn lol_html_element_append(element: *Element, content: [*]const u8, content_len: usize, is_html: bool) c_int;
|
|
extern fn lol_html_element_after(element: *Element, content: [*]const u8, content_len: usize, is_html: bool) c_int;
|
|
extern fn lol_html_element_set_inner_content(element: *Element, content: [*]const u8, content_len: usize, is_html: bool) c_int;
|
|
extern fn lol_html_element_replace(element: *Element, content: [*]const u8, content_len: usize, is_html: bool) c_int;
|
|
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;
|
|
extern fn lol_html_element_clear_end_tag_handlers(element: *Element) void;
|
|
extern fn lol_html_element_source_location_bytes(element: *const Element) SourceLocationBytes;
|
|
|
|
pub fn getAttribute(element: *const Element, name: []const u8) HTMLString {
|
|
auto_disable();
|
|
return lol_html_element_get_attribute(element, ptrWithoutPanic(name), name.len);
|
|
}
|
|
pub fn hasAttribute(element: *const Element, name: []const u8) Error!bool {
|
|
auto_disable();
|
|
return switch (lol_html_element_has_attribute(element, ptrWithoutPanic(name), name.len)) {
|
|
0 => false,
|
|
1 => true,
|
|
-1 => error.Fail,
|
|
else => unreachable,
|
|
};
|
|
}
|
|
pub fn setAttribute(element: *Element, name: []const u8, value: []const u8) Error!void {
|
|
auto_disable();
|
|
return switch (lol_html_element_set_attribute(element, ptrWithoutPanic(name), name.len, ptrWithoutPanic(value), value.len)) {
|
|
0 => {},
|
|
-1 => error.Fail,
|
|
else => unreachable,
|
|
};
|
|
}
|
|
pub fn removeAttribute(element: *Element, name: []const u8) Error!void {
|
|
auto_disable();
|
|
return switch (lol_html_element_remove_attribute(element, ptrWithoutPanic(name), name.len)) {
|
|
0 => {},
|
|
-1 => error.Fail,
|
|
else => unreachable,
|
|
};
|
|
}
|
|
pub fn before(element: *Element, content: []const u8, is_html: bool) Error!void {
|
|
auto_disable();
|
|
return switch (lol_html_element_before(element, ptrWithoutPanic(content), content.len, is_html)) {
|
|
0 => {},
|
|
-1 => error.Fail,
|
|
else => unreachable,
|
|
};
|
|
}
|
|
pub fn prepend(element: *Element, content: []const u8, is_html: bool) Error!void {
|
|
auto_disable();
|
|
return switch (lol_html_element_prepend(element, ptrWithoutPanic(content), content.len, is_html)) {
|
|
0 => {},
|
|
-1 => error.Fail,
|
|
else => unreachable,
|
|
};
|
|
}
|
|
pub fn append(element: *Element, content: []const u8, is_html: bool) Error!void {
|
|
auto_disable();
|
|
return switch (lol_html_element_append(element, ptrWithoutPanic(content), content.len, is_html)) {
|
|
0 => {},
|
|
-1 => error.Fail,
|
|
else => unreachable,
|
|
};
|
|
}
|
|
pub fn after(element: *Element, content: []const u8, is_html: bool) Error!void {
|
|
auto_disable();
|
|
return switch (lol_html_element_after(element, ptrWithoutPanic(content), content.len, is_html)) {
|
|
0 => {},
|
|
-1 => error.Fail,
|
|
else => unreachable,
|
|
};
|
|
}
|
|
pub fn setInnerContent(element: *Element, content: []const u8, is_html: bool) Error!void {
|
|
auto_disable();
|
|
|
|
return switch (lol_html_element_set_inner_content(element, ptrWithoutPanic(content), content.len, is_html)) {
|
|
0 => {},
|
|
-1 => error.Fail,
|
|
else => unreachable,
|
|
};
|
|
}
|
|
/// Replaces the element with the provided text or HTML content.
|
|
///
|
|
/// Content should be a valid UTF8-string.
|
|
///
|
|
/// Returns 0 in case of success and -1 otherwise. The actual error message
|
|
/// can be obtained using `lol_html_take_last_error` function.
|
|
pub fn replace(element: *Element, content: []const u8, is_html: bool) Error!void {
|
|
auto_disable();
|
|
return switch (lol_html_element_replace(element, ptrWithoutPanic(content), content.len, is_html)) {
|
|
0 => {},
|
|
-1 => error.Fail,
|
|
else => unreachable,
|
|
};
|
|
}
|
|
pub fn remove(element: *const Element) void {
|
|
auto_disable();
|
|
lol_html_element_remove(element);
|
|
}
|
|
// Removes the element, but leaves its inner content intact.
|
|
pub fn removeAndKeepContent(element: *const Element) void {
|
|
auto_disable();
|
|
lol_html_element_remove_and_keep_content(element);
|
|
}
|
|
pub fn isRemoved(element: *const Element) bool {
|
|
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);
|
|
}
|
|
pub fn getUserData(element: *const Element, comptime Type: type) ?*Type {
|
|
auto_disable();
|
|
return @as(?*Element, @ptrCast(@alignCast(lol_html_element_user_data_get(element))));
|
|
}
|
|
pub fn onEndTag(element: *Element, end_tag_handler: lol_html_end_tag_handler_t, user_data: ?*anyopaque) Error!void {
|
|
auto_disable();
|
|
|
|
lol_html_element_clear_end_tag_handlers(element);
|
|
|
|
return switch (lol_html_element_add_end_tag_handler(element, end_tag_handler, user_data)) {
|
|
0 => {},
|
|
-1 => error.Fail,
|
|
else => unreachable,
|
|
};
|
|
}
|
|
|
|
extern fn lol_html_element_tag_name_get(element: *const Element) HTMLString;
|
|
extern fn lol_html_element_tag_name_set(element: *Element, name: [*]const u8, name_len: usize) c_int;
|
|
extern fn lol_html_element_namespace_uri_get(element: *const Element) [*:0]const u8;
|
|
extern fn lol_html_attributes_iterator_get(element: *const Element) ?*Attribute.Iterator;
|
|
|
|
pub fn tagName(element: *const Element) HTMLString {
|
|
return lol_html_element_tag_name_get(element);
|
|
}
|
|
|
|
pub fn setTagName(element: *Element, name: []const u8) Error!void {
|
|
return switch (lol_html_element_tag_name_set(element, ptrWithoutPanic(name), name.len)) {
|
|
0 => {},
|
|
-1 => error.Fail,
|
|
else => unreachable,
|
|
};
|
|
}
|
|
|
|
pub fn namespaceURI(element: *const Element) [*:0]const u8 {
|
|
return lol_html_element_namespace_uri_get(element);
|
|
}
|
|
|
|
pub fn attributes(element: *const Element) ?*Attribute.Iterator {
|
|
return lol_html_attributes_iterator_get(element);
|
|
}
|
|
|
|
pub fn getSourceLocationBytes(element: *const Element) SourceLocationBytes {
|
|
auto_disable();
|
|
return lol_html_element_source_location_bytes(element);
|
|
}
|
|
};
|
|
|
|
pub const HTMLString = extern struct {
|
|
ptr: [*]const u8,
|
|
len: usize,
|
|
|
|
extern fn lol_html_str_free(str: HTMLString) void;
|
|
pub fn deinit(this: HTMLString) void {
|
|
auto_disable();
|
|
// if (this.len > 0) {
|
|
lol_html_str_free(this);
|
|
// }
|
|
}
|
|
|
|
pub extern fn lol_html_take_last_error(...) HTMLString;
|
|
|
|
pub fn lastError() HTMLString {
|
|
auto_disable();
|
|
return lol_html_take_last_error();
|
|
}
|
|
|
|
pub fn slice(this: HTMLString) []const u8 {
|
|
auto_disable();
|
|
@setRuntimeSafety(false);
|
|
return this.ptr[0..this.len];
|
|
}
|
|
|
|
fn deinit_external(_: [*]u8, ptr: *anyopaque, len: u32) callconv(.C) void {
|
|
auto_disable();
|
|
lol_html_str_free(.{ .ptr = @as([*]const u8, @ptrCast(ptr)), .len = len });
|
|
}
|
|
|
|
pub fn toString(this: HTMLString) bun.String {
|
|
const bytes = this.slice();
|
|
if (bytes.len > 0 and bun.strings.isAllASCII(bytes)) {
|
|
return bun.String.createExternal([*]u8, bytes, true, @constCast(bytes.ptr), &deinit_external);
|
|
}
|
|
defer this.deinit();
|
|
return bun.String.cloneUTF8(bytes);
|
|
}
|
|
|
|
pub fn toJS(this: HTMLString, globalThis: *bun.jsc.JSGlobalObject) bun.jsc.JSValue {
|
|
var str = this.toString();
|
|
defer str.deref();
|
|
return str.toJS(globalThis);
|
|
}
|
|
};
|
|
|
|
pub const EndTag = opaque {
|
|
extern fn lol_html_end_tag_before(end_tag: *EndTag, content: [*]const u8, content_len: usize, is_html: bool) c_int;
|
|
extern fn lol_html_end_tag_after(end_tag: *EndTag, content: [*]const u8, content_len: usize, is_html: bool) c_int;
|
|
extern fn lol_html_end_tag_remove(end_tag: *EndTag) void;
|
|
extern fn lol_html_end_tag_name_get(end_tag: *const EndTag) HTMLString;
|
|
extern fn lol_html_end_tag_name_set(end_tag: *EndTag, name: [*]const u8, name_len: usize) c_int;
|
|
extern fn lol_html_end_tag_source_location_bytes(end_tag: *const EndTag) SourceLocationBytes;
|
|
|
|
pub fn before(end_tag: *EndTag, content: []const u8, is_html: bool) Error!void {
|
|
auto_disable();
|
|
return switch (lol_html_end_tag_before(end_tag, ptrWithoutPanic(content), content.len, is_html)) {
|
|
0 => {},
|
|
-1 => error.Fail,
|
|
else => unreachable,
|
|
};
|
|
}
|
|
|
|
pub fn after(end_tag: *EndTag, content: []const u8, is_html: bool) Error!void {
|
|
auto_disable();
|
|
return switch (lol_html_end_tag_after(end_tag, ptrWithoutPanic(content), content.len, is_html)) {
|
|
0 => {},
|
|
-1 => error.Fail,
|
|
else => unreachable,
|
|
};
|
|
}
|
|
pub fn remove(end_tag: *EndTag) void {
|
|
auto_disable();
|
|
lol_html_end_tag_remove(end_tag);
|
|
}
|
|
|
|
pub fn getName(end_tag: *const EndTag) HTMLString {
|
|
auto_disable();
|
|
return lol_html_end_tag_name_get(end_tag);
|
|
}
|
|
|
|
pub fn setName(end_tag: *EndTag, name: []const u8) Error!void {
|
|
auto_disable();
|
|
return switch (lol_html_end_tag_name_set(end_tag, ptrWithoutPanic(name), name.len)) {
|
|
0 => {},
|
|
-1 => error.Fail,
|
|
else => unreachable,
|
|
};
|
|
}
|
|
|
|
pub fn getSourceLocationBytes(end_tag: *const EndTag) SourceLocationBytes {
|
|
auto_disable();
|
|
return lol_html_end_tag_source_location_bytes(end_tag);
|
|
}
|
|
};
|
|
|
|
pub const Attribute = opaque {
|
|
extern fn lol_html_attribute_name_get(attribute: *const Attribute) HTMLString;
|
|
extern fn lol_html_attribute_value_get(attribute: *const Attribute) HTMLString;
|
|
pub fn name(this: *const Attribute) HTMLString {
|
|
auto_disable();
|
|
return this.lol_html_attribute_name_get();
|
|
}
|
|
pub fn value(this: *const Attribute) HTMLString {
|
|
auto_disable();
|
|
return this.lol_html_attribute_value_get();
|
|
}
|
|
|
|
pub const Iterator = opaque {
|
|
extern fn lol_html_attributes_iterator_free(iterator: *Attribute.Iterator) void;
|
|
extern fn lol_html_attributes_iterator_next(iterator: *Attribute.Iterator) ?*const Attribute;
|
|
|
|
pub fn next(this: *Iterator) ?*const Attribute {
|
|
auto_disable();
|
|
return lol_html_attributes_iterator_next(this);
|
|
}
|
|
|
|
pub fn deinit(this: *Iterator) void {
|
|
auto_disable();
|
|
lol_html_attributes_iterator_free(this);
|
|
}
|
|
};
|
|
};
|
|
|
|
pub const Comment = opaque {
|
|
extern fn lol_html_comment_text_get(comment: *const Comment) HTMLString;
|
|
extern fn lol_html_comment_text_set(comment: *Comment, text: [*]const u8, text_len: usize) c_int;
|
|
extern fn lol_html_comment_before(comment: *Comment, content: [*]const u8, content_len: usize, is_html: bool) c_int;
|
|
extern fn lol_html_comment_after(comment: *Comment, content: [*]const u8, content_len: usize, is_html: bool) c_int;
|
|
extern fn lol_html_comment_replace(comment: *Comment, content: [*]const u8, content_len: usize, is_html: bool) c_int;
|
|
extern fn lol_html_comment_remove(comment: *Comment) void;
|
|
extern fn lol_html_comment_is_removed(comment: *const Comment) bool;
|
|
extern fn lol_html_comment_user_data_set(comment: *const Comment, user_data: ?*anyopaque) void;
|
|
extern fn lol_html_comment_user_data_get(comment: *const Comment) ?*anyopaque;
|
|
extern fn lol_html_comment_source_location_bytes(comment: *const Comment) SourceLocationBytes;
|
|
|
|
pub fn getText(comment: *const Comment) HTMLString {
|
|
auto_disable();
|
|
return lol_html_comment_text_get(comment);
|
|
}
|
|
|
|
pub fn setText(comment: *Comment, text: []const u8) Error!void {
|
|
auto_disable();
|
|
return switch (lol_html_comment_text_set(comment, ptrWithoutPanic(text), text.len)) {
|
|
0 => {},
|
|
-1 => error.Fail,
|
|
else => unreachable,
|
|
};
|
|
}
|
|
|
|
pub fn before(comment: *Comment, content: []const u8, is_html: bool) Error!void {
|
|
auto_disable();
|
|
return switch (lol_html_comment_before(comment, ptrWithoutPanic(content), content.len, is_html)) {
|
|
0 => {},
|
|
-1 => error.Fail,
|
|
else => unreachable,
|
|
};
|
|
}
|
|
|
|
pub fn replace(comment: *Comment, content: []const u8, is_html: bool) Error!void {
|
|
auto_disable();
|
|
return switch (lol_html_comment_before(comment, ptrWithoutPanic(content), content.len, is_html)) {
|
|
0 => {},
|
|
-1 => error.Fail,
|
|
else => unreachable,
|
|
};
|
|
}
|
|
|
|
pub fn after(comment: *Comment, content: []const u8, is_html: bool) Error!void {
|
|
auto_disable();
|
|
return switch (lol_html_comment_after(comment, ptrWithoutPanic(content), content.len, is_html)) {
|
|
0 => {},
|
|
-1 => error.Fail,
|
|
else => unreachable,
|
|
};
|
|
}
|
|
|
|
pub const isRemoved = lol_html_comment_is_removed;
|
|
pub const remove = lol_html_comment_remove;
|
|
|
|
pub fn getSourceLocationBytes(comment: *const Comment) SourceLocationBytes {
|
|
auto_disable();
|
|
return lol_html_comment_source_location_bytes(comment);
|
|
}
|
|
};
|
|
|
|
pub const Directive = enum(c_uint) {
|
|
@"continue" = 0,
|
|
stop = 1,
|
|
};
|
|
pub const lol_html_comment_handler_t = *const fn (*Comment, ?*anyopaque) callconv(.C) Directive;
|
|
pub const lol_html_text_handler_handler_t = *const fn (*TextChunk, ?*anyopaque) callconv(.C) Directive;
|
|
pub const lol_html_element_handler_t = *const fn (*Element, ?*anyopaque) callconv(.C) Directive;
|
|
pub const lol_html_doc_end_handler_t = *const fn (*DocEnd, ?*anyopaque) callconv(.C) Directive;
|
|
pub const lol_html_end_tag_handler_t = *const fn (*EndTag, ?*anyopaque) callconv(.C) Directive;
|
|
pub const DocEnd = opaque {
|
|
extern fn lol_html_doc_end_append(doc_end: ?*DocEnd, content: [*]const u8, content_len: usize, is_html: bool) c_int;
|
|
|
|
pub fn append(this: *DocEnd, content: []const u8, is_html: bool) Error!void {
|
|
auto_disable();
|
|
return switch (lol_html_doc_end_append(this, ptrWithoutPanic(content), content.len, is_html)) {
|
|
0 => {},
|
|
-1 => error.Fail,
|
|
else => unreachable,
|
|
};
|
|
}
|
|
};
|
|
|
|
fn DirectiveFunctionType(comptime Container: type) type {
|
|
return *const fn (*Container, ?*anyopaque) callconv(.C) Directive;
|
|
}
|
|
|
|
fn DirectiveFunctionTypeForHandler(comptime Container: type, comptime UserDataType: type) type {
|
|
return *const fn (*UserDataType, *Container) bool;
|
|
}
|
|
|
|
fn DocTypeHandlerCallback(comptime UserDataType: type) type {
|
|
return *const fn (*DocType, *UserDataType) bool;
|
|
}
|
|
|
|
pub fn DirectiveHandler(comptime Container: type, comptime UserDataType: type, comptime Callback: (*const fn (this: *UserDataType, container: *Container) bool)) DirectiveFunctionType(Container) {
|
|
return struct {
|
|
pub fn callback(this: *Container, user_data: ?*anyopaque) callconv(.C) Directive {
|
|
auto_disable();
|
|
return @as(
|
|
Directive,
|
|
@enumFromInt(@as(
|
|
c_uint,
|
|
@intFromBool(
|
|
Callback(
|
|
@as(
|
|
*UserDataType,
|
|
@ptrCast(@alignCast(
|
|
user_data.?,
|
|
)),
|
|
),
|
|
this,
|
|
),
|
|
),
|
|
)),
|
|
);
|
|
}
|
|
}.callback;
|
|
}
|
|
|
|
pub const DocType = opaque {
|
|
extern fn lol_html_doctype_name_get(doctype: *const DocType) HTMLString;
|
|
extern fn lol_html_doctype_public_id_get(doctype: *const DocType) HTMLString;
|
|
extern fn lol_html_doctype_system_id_get(doctype: *const DocType) HTMLString;
|
|
extern fn lol_html_doctype_user_data_set(doctype: *const DocType, user_data: ?*anyopaque) void;
|
|
extern fn lol_html_doctype_user_data_get(doctype: *const DocType) ?*anyopaque;
|
|
extern fn lol_html_doctype_remove(doctype: *DocType) void;
|
|
extern fn lol_html_doctype_is_removed(doctype: *const DocType) bool;
|
|
extern fn lol_html_doctype_source_location_bytes(doctype: *const DocType) SourceLocationBytes;
|
|
|
|
pub const Callback = *const fn (*DocType, ?*anyopaque) callconv(.C) Directive;
|
|
|
|
pub fn getName(this: *const DocType) HTMLString {
|
|
auto_disable();
|
|
return this.lol_html_doctype_name_get();
|
|
}
|
|
pub fn getPublicId(this: *const DocType) HTMLString {
|
|
auto_disable();
|
|
return this.lol_html_doctype_public_id_get();
|
|
}
|
|
pub fn getSystemId(this: *const DocType) HTMLString {
|
|
auto_disable();
|
|
return this.lol_html_doctype_system_id_get();
|
|
}
|
|
pub fn remove(this: *DocType) void {
|
|
auto_disable();
|
|
return this.lol_html_doctype_remove();
|
|
}
|
|
pub fn isRemoved(this: *const DocType) bool {
|
|
auto_disable();
|
|
return this.lol_html_doctype_is_removed();
|
|
}
|
|
pub fn getSourceLocationBytes(this: *const DocType) SourceLocationBytes {
|
|
auto_disable();
|
|
return this.lol_html_doctype_source_location_bytes();
|
|
}
|
|
};
|
|
|
|
pub const Encoding = enum {
|
|
UTF8,
|
|
UTF16,
|
|
|
|
const Label = std.enums.EnumMap(Encoding, []const u8);
|
|
pub const label: Label = brk: {
|
|
var labels = Label{};
|
|
labels.put(.UTF8, "UTF-8");
|
|
labels.put(.UTF16, "UTF-16");
|
|
|
|
break :brk labels;
|
|
};
|
|
};
|
|
|
|
const bun = @import("bun");
|
|
const std = @import("std");
|