aboutsummaryrefslogtreecommitdiff
path: root/src/javascript/jsc/api/html_rewriter.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/javascript/jsc/api/html_rewriter.zig')
-rw-r--r--src/javascript/jsc/api/html_rewriter.zig1886
1 files changed, 0 insertions, 1886 deletions
diff --git a/src/javascript/jsc/api/html_rewriter.zig b/src/javascript/jsc/api/html_rewriter.zig
deleted file mode 100644
index 302af6aac..000000000
--- a/src/javascript/jsc/api/html_rewriter.zig
+++ /dev/null
@@ -1,1886 +0,0 @@
-const std = @import("std");
-const Api = @import("../../../api/schema.zig").Api;
-const FilesystemRouter = @import("../../../router.zig");
-const http = @import("../../../http.zig");
-const JavaScript = @import("../javascript.zig");
-const QueryStringMap = @import("../../../url.zig").QueryStringMap;
-const CombinedScanner = @import("../../../url.zig").CombinedScanner;
-const bun = @import("../../../global.zig");
-const string = bun.string;
-const JSC = @import("../../../jsc.zig");
-const js = JSC.C;
-const WebCore = @import("../webcore/response.zig");
-const Router = @This();
-const Bundler = @import("../../../bundler.zig");
-const VirtualMachine = JavaScript.VirtualMachine;
-const ScriptSrcStream = std.io.FixedBufferStream([]u8);
-const ZigString = JSC.ZigString;
-const Fs = @import("../../../fs.zig");
-const Base = @import("../base.zig");
-const getAllocator = Base.getAllocator;
-const JSObject = JSC.JSObject;
-const JSError = Base.JSError;
-const JSValue = JSC.JSValue;
-const JSGlobalObject = JSC.JSGlobalObject;
-const strings = @import("strings");
-const NewClass = Base.NewClass;
-const To = Base.To;
-const Request = WebCore.Request;
-const d = Base.d;
-const FetchEvent = WebCore.FetchEvent;
-const Response = WebCore.Response;
-const LOLHTML = @import("lolhtml");
-
-const SelectorMap = std.ArrayListUnmanaged(*LOLHTML.HTMLSelector);
-pub const LOLHTMLContext = struct {
- selectors: SelectorMap = .{},
- element_handlers: std.ArrayListUnmanaged(*ElementHandler) = .{},
- document_handlers: std.ArrayListUnmanaged(*DocumentHandler) = .{},
-
- pub fn deinit(this: *LOLHTMLContext, allocator: std.mem.Allocator) void {
- for (this.selectors.items) |selector| {
- selector.deinit();
- }
- this.selectors.deinit(allocator);
- this.selectors = .{};
-
- for (this.element_handlers.items) |handler| {
- handler.deinit();
- }
- this.element_handlers.deinit(allocator);
- this.element_handlers = .{};
-
- for (this.document_handlers.items) |handler| {
- handler.deinit();
- }
- this.document_handlers.deinit(allocator);
- this.document_handlers = .{};
- }
-};
-pub const HTMLRewriter = struct {
- builder: *LOLHTML.HTMLRewriter.Builder,
- context: LOLHTMLContext,
-
- pub const Constructor = JSC.NewConstructor(HTMLRewriter, .{ .constructor = constructor }, .{});
-
- pub const Class = NewClass(
- HTMLRewriter,
- .{ .name = "HTMLRewriter" },
- .{
- .finalize = finalize,
- .on = .{
- .rfn = wrap(HTMLRewriter, "on"),
- },
- .onDocument = .{
- .rfn = wrap(HTMLRewriter, "onDocument"),
- },
- .transform = .{
- .rfn = wrap(HTMLRewriter, "transform"),
- },
- },
- .{},
- );
-
- pub fn constructor(
- ctx: js.JSContextRef,
- _: js.JSObjectRef,
- _: []const js.JSValueRef,
- _: js.ExceptionRef,
- ) js.JSObjectRef {
- var rewriter = bun.default_allocator.create(HTMLRewriter) catch unreachable;
- rewriter.* = HTMLRewriter{
- .builder = LOLHTML.HTMLRewriter.Builder.init(),
- .context = .{},
- };
- return HTMLRewriter.Class.make(ctx, rewriter);
- }
-
- pub fn on(
- this: *HTMLRewriter,
- global: *JSGlobalObject,
- selector_name: ZigString,
- thisObject: JSC.C.JSObjectRef,
- listener: JSValue,
- exception: JSC.C.ExceptionRef,
- ) JSValue {
- var selector_slice = std.fmt.allocPrint(bun.default_allocator, "{}", .{selector_name}) catch unreachable;
-
- var selector = LOLHTML.HTMLSelector.parse(selector_slice) catch
- return throwLOLHTMLError(global);
- var handler_ = ElementHandler.init(global, listener, exception);
- if (exception.* != null) {
- selector.deinit();
- return JSValue.fromRef(exception.*);
- }
- var handler = getAllocator(global.ref()).create(ElementHandler) catch unreachable;
- handler.* = handler_;
-
- this.builder.addElementContentHandlers(
- selector,
-
- ElementHandler,
- ElementHandler.onElement,
- if (handler.onElementCallback != null)
- handler
- else
- null,
-
- ElementHandler,
- ElementHandler.onComment,
- if (handler.onCommentCallback != null)
- handler
- else
- null,
-
- ElementHandler,
- ElementHandler.onText,
- if (handler.onTextCallback != null)
- handler
- else
- null,
- ) catch {
- selector.deinit();
- return throwLOLHTMLError(global);
- };
-
- this.context.selectors.append(bun.default_allocator, selector) catch unreachable;
- this.context.element_handlers.append(bun.default_allocator, handler) catch unreachable;
- return JSValue.fromRef(thisObject);
- }
-
- pub fn onDocument(
- this: *HTMLRewriter,
- global: *JSGlobalObject,
- listener: JSValue,
- thisObject: JSC.C.JSObjectRef,
- exception: JSC.C.ExceptionRef,
- ) JSValue {
- var handler_ = DocumentHandler.init(global, listener, exception);
- if (exception.* != null) {
- return JSValue.fromRef(exception.*);
- }
-
- var handler = getAllocator(global.ref()).create(DocumentHandler) catch unreachable;
- handler.* = handler_;
-
- this.builder.addDocumentContentHandlers(
- DocumentHandler,
- DocumentHandler.onDocType,
- if (handler.onDocTypeCallback != null)
- handler
- else
- null,
-
- DocumentHandler,
- DocumentHandler.onComment,
- if (handler.onCommentCallback != null)
- handler
- else
- null,
-
- DocumentHandler,
- DocumentHandler.onText,
- if (handler.onTextCallback != null)
- handler
- else
- null,
-
- DocumentHandler,
- DocumentHandler.onEnd,
- if (handler.onEndCallback != null)
- handler
- else
- null,
- ) catch {
- return throwLOLHTMLError(global);
- };
-
- this.context.document_handlers.append(bun.default_allocator, handler) catch unreachable;
- return JSValue.fromRef(thisObject);
- }
-
- pub fn finalize(this: *HTMLRewriter) void {
- this.finalizeWithoutDestroy();
- bun.default_allocator.destroy(this);
- }
-
- pub fn finalizeWithoutDestroy(this: *HTMLRewriter) void {
- this.context.deinit(bun.default_allocator);
- }
-
- pub fn beginTransform(this: *HTMLRewriter, global: *JSGlobalObject, response: *Response) JSValue {
- const new_context = this.context;
- this.context = .{};
- return BufferOutputSink.init(new_context, global, response, this.builder);
- }
-
- pub fn returnEmptyResponse(this: *HTMLRewriter, global: *JSGlobalObject, response: *Response) JSValue {
- var result = bun.default_allocator.create(Response) catch unreachable;
-
- response.cloneInto(result, getAllocator(global.ref()), global);
- this.finalizeWithoutDestroy();
- return JSValue.fromRef(Response.makeMaybePooled(global.ref(), result));
- }
-
- pub fn transform(this: *HTMLRewriter, global: *JSGlobalObject, response: *Response) JSValue {
- var input = response.body.slice();
-
- if (input.len == 0 and !(response.body.value == .Blob and response.body.value.Blob.needsToReadFile())) {
- return this.returnEmptyResponse(global, response);
- }
-
- return this.beginTransform(global, response);
- }
-
- pub const HTMLRewriterLoader = struct {
- rewriter: *LOLHTML.HTMLRewriter,
- finalized: bool = false,
- context: LOLHTMLContext,
- chunk_size: usize = 0,
- failed: bool = false,
- output: JSC.WebCore.Sink,
- signal: JSC.WebCore.Signal = .{},
- backpressure: std.fifo.LinearFifo(u8, .Dynamic) = std.fifo.LinearFifo(u8, .Dynamic).init(bun.default_allocator),
-
- pub fn finalize(this: *HTMLRewriterLoader) void {
- if (this.finalized) return;
- this.rewriter.deinit();
- this.backpressure.deinit();
- this.backpressure = std.fifo.LinearFifo(u8, .Dynamic).init(bun.default_allocator);
- this.finalized = true;
- }
-
- pub fn fail(this: *HTMLRewriterLoader, err: JSC.Node.Syscall.Error) void {
- this.signal.close(err);
- this.output.end(err);
- this.failed = true;
- this.finalize();
- }
-
- pub fn connect(this: *HTMLRewriterLoader, signal: JSC.WebCore.Signal) void {
- this.signal = signal;
- }
-
- pub fn writeToDestination(this: *HTMLRewriterLoader, bytes: []const u8) void {
- if (this.backpressure.count > 0) {
- this.backpressure.write(bytes) catch {
- this.fail(JSC.Node.Syscall.Error.oom);
- this.finalize();
- };
- return;
- }
-
- const write_result = this.output.write(.{ .temporary = bun.ByteList.init(bytes) });
-
- switch (write_result) {
- .err => |err| {
- this.fail(err);
- },
- .owned_and_done, .temporary_and_done, .into_array_and_done => {
- this.done();
- },
- .pending => |pending| {
- pending.applyBackpressure(bun.default_allocator, &this.output, pending, bytes);
- },
- .into_array, .owned, .temporary => {
- this.signal.ready(if (this.chunk_size > 0) this.chunk_size else null, null);
- },
- }
- }
-
- pub fn done(
- this: *HTMLRewriterLoader,
- ) void {
- this.output.end(null);
- this.signal.close(null);
- this.finalize();
- }
-
- pub fn setup(
- this: *HTMLRewriterLoader,
- builder: *LOLHTML.HTMLRewriter.Builder,
- context: LOLHTMLContext,
- size_hint: ?usize,
- output: JSC.WebCore.Sink,
- ) ?[]const u8 {
- for (context.document_handlers.items) |doc| {
- doc.ctx = this;
- }
- for (context.element_handlers.items) |doc| {
- doc.ctx = this;
- }
-
- const chunk_size = @maximum(size_hint orelse 16384, 1024);
- this.rewriter = builder.build(
- .UTF8,
- .{
- .preallocated_parsing_buffer_size = chunk_size,
- .max_allowed_memory_usage = std.math.maxInt(u32),
- },
- false,
- HTMLRewriterLoader,
- this,
- HTMLRewriterLoader.writeToDestination,
- HTMLRewriterLoader.done,
- ) catch {
- output.end();
- return LOLHTML.HTMLString.lastError().slice();
- };
-
- this.chunk_size = chunk_size;
- this.context = context;
- this.output = output;
-
- return null;
- }
-
- pub fn sink(this: *HTMLRewriterLoader) JSC.WebCore.Sink {
- return JSC.WebCore.Sink.init(this);
- }
-
- fn writeBytes(this: *HTMLRewriterLoader, bytes: bun.ByteList, comptime deinit_: bool) ?JSC.Node.Syscall.Error {
- this.rewriter.write(bytes.slice()) catch {
- return JSC.Node.Syscall.Error{
- .errno = 1,
- // TODO: make this a union
- .path = bun.default_allocator.dupe(u8, LOLHTML.HTMLString.lastError().slice()) catch unreachable,
- };
- };
- if (comptime deinit_) bytes.listManaged(bun.default_allocator).deinit();
- return null;
- }
-
- pub fn write(this: *HTMLRewriterLoader, data: JSC.WebCore.StreamResult) JSC.WebCore.StreamResult.Writable {
- switch (data) {
- .owned => |bytes| {
- if (this.writeBytes(bytes, true)) |err| {
- return .{ .err = err };
- }
- return .{ .owned = bytes.len };
- },
- .owned_and_done => |bytes| {
- if (this.writeBytes(bytes, true)) |err| {
- return .{ .err = err };
- }
- return .{ .owned_and_done = bytes.len };
- },
- .temporary_and_done => |bytes| {
- if (this.writeBytes(bytes, false)) |err| {
- return .{ .err = err };
- }
- return .{ .temporary_and_done = bytes.len };
- },
- .temporary => |bytes| {
- if (this.writeBytes(bytes, false)) |err| {
- return .{ .err = err };
- }
- return .{ .temporary = bytes.len };
- },
- else => unreachable,
- }
- }
-
- pub fn writeUTF16(this: *HTMLRewriterLoader, data: JSC.WebCore.StreamResult) JSC.WebCore.StreamResult.Writable {
- return JSC.WebCore.Sink.UTF8Fallback.writeUTF16(HTMLRewriterLoader, this, data, write);
- }
-
- pub fn writeLatin1(this: *HTMLRewriterLoader, data: JSC.WebCore.StreamResult) JSC.WebCore.StreamResult.Writable {
- return JSC.WebCore.Sink.UTF8Fallback.writeLatin1(HTMLRewriterLoader, this, data, write);
- }
- };
-
- pub const BufferOutputSink = struct {
- global: *JSGlobalObject,
- bytes: bun.MutableString,
- rewriter: *LOLHTML.HTMLRewriter,
- context: LOLHTMLContext,
- response: *Response,
- input: JSC.WebCore.Blob = undefined,
- pub fn init(context: LOLHTMLContext, global: *JSGlobalObject, original: *Response, builder: *LOLHTML.HTMLRewriter.Builder) JSValue {
- var result = bun.default_allocator.create(Response) catch unreachable;
- var sink = bun.default_allocator.create(BufferOutputSink) catch unreachable;
- sink.* = BufferOutputSink{
- .global = global,
- .bytes = bun.MutableString.initEmpty(bun.default_allocator),
- .rewriter = undefined,
- .context = context,
- .response = result,
- };
-
- for (sink.context.document_handlers.items) |doc| {
- doc.ctx = sink;
- }
- for (sink.context.element_handlers.items) |doc| {
- doc.ctx = sink;
- }
-
- sink.rewriter = builder.build(
- .UTF8,
- .{
- .preallocated_parsing_buffer_size = @maximum(original.body.len(), 1024),
- .max_allowed_memory_usage = std.math.maxInt(u32),
- },
- false,
- BufferOutputSink,
- sink,
- BufferOutputSink.write,
- BufferOutputSink.done,
- ) catch {
- sink.deinit();
- bun.default_allocator.destroy(result);
-
- return throwLOLHTMLError(global);
- };
-
- result.* = Response{
- .allocator = bun.default_allocator,
- .body = .{
- .init = .{
- .status_code = 200,
- },
- .value = .{
- .Locked = .{
- .global = global,
- .task = sink,
- },
- },
- },
- };
-
- result.body.init.headers = original.body.init.headers;
- result.body.init.method = original.body.init.method;
- result.body.init.status_code = original.body.init.status_code;
-
- result.url = bun.default_allocator.dupe(u8, original.url) catch unreachable;
- result.status_text = bun.default_allocator.dupe(u8, original.status_text) catch unreachable;
-
- var input: JSC.WebCore.Blob = original.body.value.use();
-
- const is_pending = input.needsToReadFile();
- defer if (!is_pending) input.detach();
-
- if (is_pending) {
- input.doReadFileInternal(*BufferOutputSink, sink, onFinishedLoading, global);
- } else if (sink.runOutputSink(input.sharedView(), false, false)) |error_value| {
- return error_value;
- }
-
- // Hold off on cloning until we're actually done.
-
- return JSC.JSValue.fromRef(
- Response.makeMaybePooled(sink.global.ref(), sink.response),
- );
- }
-
- pub fn onFinishedLoading(sink: *BufferOutputSink, bytes: JSC.WebCore.Blob.Store.ReadFile.ResultType) void {
- switch (bytes) {
- .err => |err| {
- if (sink.response.body.value == .Locked and @ptrToInt(sink.response.body.value.Locked.task) == @ptrToInt(sink) and
- sink.response.body.value.Locked.promise == null)
- {
- sink.response.body.value = .{ .Empty = .{} };
- // is there a pending promise?
- // we will need to reject it
- } else if (sink.response.body.value == .Locked and @ptrToInt(sink.response.body.value.Locked.task) == @ptrToInt(sink) and
- sink.response.body.value.Locked.promise != null)
- {
- sink.response.body.value.Locked.callback = null;
- sink.response.body.value.Locked.task = null;
- }
-
- sink.response.body.value.toErrorInstance(err.toErrorInstance(sink.global), sink.global);
- sink.rewriter.end() catch {};
- sink.deinit();
- return;
- },
- .result => |data| {
- _ = sink.runOutputSink(data.buf, true, data.is_temporary);
- },
- }
- }
-
- pub fn runOutputSink(
- sink: *BufferOutputSink,
- bytes: []const u8,
- is_async: bool,
- free_bytes_on_end: bool,
- ) ?JSValue {
- defer if (free_bytes_on_end)
- bun.default_allocator.free(bun.constStrToU8(bytes));
-
- sink.bytes.growBy(bytes.len) catch unreachable;
- var global = sink.global;
- var response = sink.response;
-
- sink.rewriter.write(bytes) catch {
- sink.deinit();
- bun.default_allocator.destroy(sink);
-
- if (is_async) {
- response.body.value.toErrorInstance(throwLOLHTMLError(global), global);
-
- return null;
- } else {
- return throwLOLHTMLError(global);
- }
- };
-
- sink.rewriter.end() catch {
- if (!is_async) response.finalize();
- sink.response = undefined;
- sink.deinit();
-
- if (is_async) {
- response.body.value.toErrorInstance(throwLOLHTMLError(global), global);
- return null;
- } else {
- return throwLOLHTMLError(global);
- }
- };
-
- return null;
- }
-
- pub const Sync = enum { suspended, pending, done };
-
- pub fn done(this: *BufferOutputSink) void {
- var prev_value = this.response.body.value;
- var bytes = this.bytes.toOwnedSliceLeaky();
- this.response.body.value = .{
- .Blob = JSC.WebCore.Blob.init(bytes, this.bytes.allocator, this.global),
- };
- prev_value.resolve(
- &this.response.body.value,
- this.global,
- );
- }
-
- pub fn write(this: *BufferOutputSink, bytes: []const u8) void {
- this.bytes.append(bytes) catch unreachable;
- }
-
- pub fn deinit(this: *BufferOutputSink) void {
- this.bytes.deinit();
-
- this.context.deinit(bun.default_allocator);
- }
- };
-
- // pub const StreamOutputSink = struct {
- // global: *JSGlobalObject,
- // rewriter: *LOLHTML.HTMLRewriter,
- // context: LOLHTMLContext,
- // response: *Response,
- // input: JSC.WebCore.Blob = undefined,
- // pub fn init(context: LOLHTMLContext, global: *JSGlobalObject, original: *Response, builder: *LOLHTML.HTMLRewriter.Builder) JSValue {
- // var result = bun.default_allocator.create(Response) catch unreachable;
- // var sink = bun.default_allocator.create(StreamOutputSink) catch unreachable;
- // sink.* = StreamOutputSink{
- // .global = global,
- // .rewriter = undefined,
- // .context = context,
- // .response = result,
- // };
-
- // for (sink.context.document_handlers.items) |doc| {
- // doc.ctx = sink;
- // }
- // for (sink.context.element_handlers.items) |doc| {
- // doc.ctx = sink;
- // }
-
- // sink.rewriter = builder.build(
- // .UTF8,
- // .{
- // .preallocated_parsing_buffer_size = @maximum(original.body.len(), 1024),
- // .max_allowed_memory_usage = std.math.maxInt(u32),
- // },
- // false,
- // StreamOutputSink,
- // sink,
- // StreamOutputSink.write,
- // StreamOutputSink.done,
- // ) catch {
- // sink.deinit();
- // bun.default_allocator.destroy(result);
-
- // return throwLOLHTMLError(global);
- // };
-
- // result.* = Response{
- // .allocator = bun.default_allocator,
- // .body = .{
- // .init = .{
- // .status_code = 200,
- // },
- // .value = .{
- // .Locked = .{
- // .global = global,
- // .task = sink,
- // },
- // },
- // },
- // };
-
- // result.body.init.headers = original.body.init.headers;
- // result.body.init.method = original.body.init.method;
- // result.body.init.status_code = original.body.init.status_code;
-
- // result.url = bun.default_allocator.dupe(u8, original.url) catch unreachable;
- // result.status_text = bun.default_allocator.dupe(u8, original.status_text) catch unreachable;
-
- // var input: JSC.WebCore.Blob = original.body.value.use();
-
- // const is_pending = input.needsToReadFile();
- // defer if (!is_pending) input.detach();
-
- // if (is_pending) {
- // input.doReadFileInternal(*StreamOutputSink, sink, onFinishedLoading, global);
- // } else if (sink.runOutputSink(input.sharedView(), false, false)) |error_value| {
- // return error_value;
- // }
-
- // // Hold off on cloning until we're actually done.
-
- // return JSC.JSValue.fromRef(
- // Response.makeMaybePooled(sink.global.ref(), sink.response),
- // );
- // }
-
- // pub fn runOutputSink(
- // sink: *StreamOutputSink,
- // bytes: []const u8,
- // is_async: bool,
- // free_bytes_on_end: bool,
- // ) ?JSValue {
- // defer if (free_bytes_on_end)
- // bun.default_allocator.free(bun.constStrToU8(bytes));
-
- // return null;
- // }
-
- // pub const Sync = enum { suspended, pending, done };
-
- // pub fn done(this: *StreamOutputSink) void {
- // var prev_value = this.response.body.value;
- // var bytes = this.bytes.toOwnedSliceLeaky();
- // this.response.body.value = .{
- // .Blob = JSC.WebCore.Blob.init(bytes, this.bytes.allocator, this.global),
- // };
- // prev_value.resolve(
- // &this.response.body.value,
- // this.global,
- // );
- // }
-
- // pub fn write(this: *StreamOutputSink, bytes: []const u8) void {
- // this.bytes.append(bytes) catch unreachable;
- // }
-
- // pub fn deinit(this: *StreamOutputSink) void {
- // this.bytes.deinit();
-
- // this.context.deinit(bun.default_allocator);
- // }
- // };
-};
-
-const DocumentHandler = struct {
- onDocTypeCallback: ?JSValue = null,
- onCommentCallback: ?JSValue = null,
- onTextCallback: ?JSValue = null,
- onEndCallback: ?JSValue = null,
- thisObject: JSValue,
- global: *JSGlobalObject,
- ctx: ?*HTMLRewriter.BufferOutputSink = null,
-
- pub const onDocType = HandlerCallback(
- DocumentHandler,
- DocType,
- LOLHTML.DocType,
- "doctype",
- "onDocTypeCallback",
- );
- pub const onComment = HandlerCallback(
- DocumentHandler,
- Comment,
- LOLHTML.Comment,
- "comment",
- "onCommentCallback",
- );
- pub const onText = HandlerCallback(
- DocumentHandler,
- TextChunk,
- LOLHTML.TextChunk,
- "text_chunk",
- "onTextCallback",
- );
- pub const onEnd = HandlerCallback(
- DocumentHandler,
- DocEnd,
- LOLHTML.DocEnd,
- "doc_end",
- "onEndCallback",
- );
-
- pub fn init(global: *JSGlobalObject, thisObject: JSValue, exception: JSC.C.ExceptionRef) DocumentHandler {
- var handler = DocumentHandler{
- .thisObject = thisObject,
- .global = global,
- };
-
- switch (thisObject.jsType()) {
- .Object, .ProxyObject, .Cell, .FinalObject => {},
- else => |kind| {
- JSC.throwInvalidArguments(
- "Expected object but received {s}",
- .{std.mem.span(@tagName(kind))},
- global.ref(),
- exception,
- );
- return undefined;
- },
- }
-
- if (thisObject.get(global, "doctype")) |val| {
- if (val.isUndefinedOrNull() or !val.isCell() or !val.isCallable(global.vm())) {
- JSC.throwInvalidArguments("doctype must be a function", .{}, global.ref(), exception);
- return undefined;
- }
- JSC.C.JSValueProtect(global.ref(), val.asObjectRef());
- handler.onDocTypeCallback = val;
- }
-
- if (thisObject.get(global, "comments")) |val| {
- if (val.isUndefinedOrNull() or !val.isCell() or !val.isCallable(global.vm())) {
- JSC.throwInvalidArguments("comments must be a function", .{}, global.ref(), exception);
- return undefined;
- }
- JSC.C.JSValueProtect(global.ref(), val.asObjectRef());
- handler.onCommentCallback = val;
- }
-
- if (thisObject.get(global, "text")) |val| {
- if (val.isUndefinedOrNull() or !val.isCell() or !val.isCallable(global.vm())) {
- JSC.throwInvalidArguments("text must be a function", .{}, global.ref(), exception);
- return undefined;
- }
- JSC.C.JSValueProtect(global.ref(), val.asObjectRef());
- handler.onTextCallback = val;
- }
-
- if (thisObject.get(global, "end")) |val| {
- if (val.isUndefinedOrNull() or !val.isCell() or !val.isCallable(global.vm())) {
- JSC.throwInvalidArguments("end must be a function", .{}, global.ref(), exception);
- return undefined;
- }
- JSC.C.JSValueProtect(global.ref(), val.asObjectRef());
- handler.onEndCallback = val;
- }
-
- JSC.C.JSValueProtect(global.ref(), thisObject.asObjectRef());
- return handler;
- }
-
- pub fn deinit(this: *DocumentHandler) void {
- if (this.onDocTypeCallback) |cb| {
- JSC.C.JSValueUnprotect(this.global.ref(), cb.asObjectRef());
- this.onDocTypeCallback = null;
- }
-
- if (this.onCommentCallback) |cb| {
- JSC.C.JSValueUnprotect(this.global.ref(), cb.asObjectRef());
- this.onCommentCallback = null;
- }
-
- if (this.onTextCallback) |cb| {
- JSC.C.JSValueUnprotect(this.global.ref(), cb.asObjectRef());
- this.onTextCallback = null;
- }
-
- if (this.onEndCallback) |cb| {
- JSC.C.JSValueUnprotect(this.global.ref(), cb.asObjectRef());
- this.onEndCallback = null;
- }
-
- JSC.C.JSValueUnprotect(this.global.ref(), this.thisObject.asObjectRef());
- }
-};
-
-fn HandlerCallback(
- comptime HandlerType: type,
- comptime ZigType: type,
- comptime LOLHTMLType: type,
- comptime field_name: string,
- comptime callback_name: string,
-) (fn (*HandlerType, *LOLHTMLType) bool) {
- return struct {
- pub fn callback(this: *HandlerType, value: *LOLHTMLType) bool {
- if (comptime JSC.is_bindgen)
- unreachable;
- var zig_element = bun.default_allocator.create(ZigType) catch unreachable;
- @field(zig_element, field_name) = value;
- // At the end of this scope, the value is no longer valid
- var args = [1]JSC.C.JSObjectRef{
- ZigType.Class.make(this.global.ref(), zig_element),
- };
- var result = JSC.C.JSObjectCallAsFunctionReturnValue(
- this.global.ref(),
- @field(this, callback_name).?.asObjectRef(),
- if (comptime @hasField(HandlerType, "thisObject"))
- @field(this, "thisObject").asObjectRef()
- else
- null,
- 1,
- &args,
- );
- var promise_: ?*JSC.JSInternalPromise = null;
- while (!result.isUndefinedOrNull()) {
- if (result.isError() or result.isAggregateError(this.global)) {
- @field(zig_element, field_name) = null;
- return true;
- }
-
- var promise = promise_ orelse JSC.JSInternalPromise.resolvedPromise(this.global, result);
- promise_ = promise;
- JavaScript.VirtualMachine.vm.event_loop.waitForPromise(promise);
-
- switch (promise.status(this.global.vm())) {
- JSC.JSPromise.Status.Pending => unreachable,
- JSC.JSPromise.Status.Rejected => {
- JavaScript.VirtualMachine.vm.defaultErrorHandler(promise.result(this.global.vm()), null);
- @field(zig_element, field_name) = null;
- return false;
- },
- JSC.JSPromise.Status.Fulfilled => {
- result = promise.result(this.global.vm());
- break;
- },
- }
-
- break;
- }
- @field(zig_element, field_name) = null;
- return false;
- }
- }.callback;
-}
-
-const ElementHandler = struct {
- onElementCallback: ?JSValue = null,
- onCommentCallback: ?JSValue = null,
- onTextCallback: ?JSValue = null,
- thisObject: JSValue,
- global: *JSGlobalObject,
- ctx: ?*HTMLRewriter.BufferOutputSink = null,
-
- pub fn init(global: *JSGlobalObject, thisObject: JSValue, exception: JSC.C.ExceptionRef) ElementHandler {
- var handler = ElementHandler{
- .thisObject = thisObject,
- .global = global,
- };
-
- switch (thisObject.jsType()) {
- .Object, .ProxyObject, .Cell, .FinalObject => {},
- else => |kind| {
- JSC.throwInvalidArguments(
- "Expected object but received {s}",
- .{std.mem.span(@tagName(kind))},
- global.ref(),
- exception,
- );
- return undefined;
- },
- }
-
- if (thisObject.get(global, "element")) |val| {
- if (val.isUndefinedOrNull() or !val.isCell() or !val.isCallable(global.vm())) {
- JSC.throwInvalidArguments("element must be a function", .{}, global.ref(), exception);
- return undefined;
- }
- JSC.C.JSValueProtect(global.ref(), val.asObjectRef());
- handler.onElementCallback = val;
- }
-
- if (thisObject.get(global, "comments")) |val| {
- if (val.isUndefinedOrNull() or !val.isCell() or !val.isCallable(global.vm())) {
- JSC.throwInvalidArguments("comments must be a function", .{}, global.ref(), exception);
- return undefined;
- }
- JSC.C.JSValueProtect(global.ref(), val.asObjectRef());
- handler.onCommentCallback = val;
- }
-
- if (thisObject.get(global, "text")) |val| {
- if (val.isUndefinedOrNull() or !val.isCell() or !val.isCallable(global.vm())) {
- JSC.throwInvalidArguments("text must be a function", .{}, global.ref(), exception);
- return undefined;
- }
- JSC.C.JSValueProtect(global.ref(), val.asObjectRef());
- handler.onTextCallback = val;
- }
-
- JSC.C.JSValueProtect(global.ref(), thisObject.asObjectRef());
- return handler;
- }
-
- pub fn deinit(this: *ElementHandler) void {
- if (this.onElementCallback) |cb| {
- JSC.C.JSValueUnprotect(this.global.ref(), cb.asObjectRef());
- this.onElementCallback = null;
- }
-
- if (this.onCommentCallback) |cb| {
- JSC.C.JSValueUnprotect(this.global.ref(), cb.asObjectRef());
- this.onCommentCallback = null;
- }
-
- if (this.onTextCallback) |cb| {
- JSC.C.JSValueUnprotect(this.global.ref(), cb.asObjectRef());
- this.onTextCallback = null;
- }
-
- JSC.C.JSValueUnprotect(this.global.ref(), this.thisObject.asObjectRef());
- }
-
- pub fn onElement(this: *ElementHandler, value: *LOLHTML.Element) bool {
- return HandlerCallback(
- ElementHandler,
- Element,
- LOLHTML.Element,
- "element",
- "onElementCallback",
- )(this, value);
- }
-
- pub const onComment = HandlerCallback(
- ElementHandler,
- Comment,
- LOLHTML.Comment,
- "comment",
- "onCommentCallback",
- );
-
- pub const onText = HandlerCallback(
- ElementHandler,
- TextChunk,
- LOLHTML.TextChunk,
- "text_chunk",
- "onTextCallback",
- );
-};
-
-pub const ContentOptions = struct {
- html: bool = false,
-};
-
-const getterWrap = JSC.getterWrap;
-const setterWrap = JSC.setterWrap;
-const wrap = JSC.wrapAsync;
-
-pub fn free_html_writer_string(_: ?*anyopaque, ptr: ?*anyopaque, len: usize) callconv(.C) void {
- var str = LOLHTML.HTMLString{ .ptr = bun.cast([*]const u8, ptr.?), .len = len };
- str.deinit();
-}
-
-fn throwLOLHTMLError(global: *JSGlobalObject) JSValue {
- var err = LOLHTML.HTMLString.lastError();
- return ZigString.init(err.slice()).toErrorInstance(global);
-}
-
-fn htmlStringValue(input: LOLHTML.HTMLString, globalObject: *JSGlobalObject) JSValue {
- var str = ZigString.init(
- input.slice(),
- );
- str.detectEncoding();
-
- return str.toExternalValueWithCallback(
- globalObject,
- free_html_writer_string,
- );
-}
-
-pub const TextChunk = struct {
- text_chunk: ?*LOLHTML.TextChunk = null,
-
- pub const Class = NewClass(
- TextChunk,
- .{ .name = "TextChunk" },
- .{
- .before = .{
- .rfn = wrap(TextChunk, "before"),
- },
- .after = .{
- .rfn = wrap(TextChunk, "after"),
- },
-
- .replace = .{
- .rfn = wrap(TextChunk, "replace"),
- },
-
- .remove = .{
- .rfn = wrap(TextChunk, "remove"),
- },
- .finalize = finalize,
- },
- .{
- .removed = .{
- .get = getterWrap(TextChunk, "removed"),
- },
- .text = .{
- .get = getterWrap(TextChunk, "getText"),
- },
- },
- );
-
- fn contentHandler(this: *TextChunk, comptime Callback: (fn (*LOLHTML.TextChunk, []const u8, bool) LOLHTML.Error!void), thisObject: js.JSObjectRef, globalObject: *JSGlobalObject, content: ZigString, contentOptions: ?ContentOptions) JSValue {
- if (this.text_chunk == null)
- return JSC.JSValue.jsUndefined();
- var content_slice = content.toSlice(bun.default_allocator);
- defer content_slice.deinit();
-
- Callback(
- this.text_chunk.?,
- content_slice.slice(),
- contentOptions != null and contentOptions.?.html,
- ) catch return throwLOLHTMLError(globalObject);
-
- return JSValue.fromRef(thisObject);
- }
-
- pub fn before(
- this: *TextChunk,
- thisObject: js.JSObjectRef,
- globalObject: *JSGlobalObject,
- content: ZigString,
- contentOptions: ?ContentOptions,
- ) JSValue {
- return this.contentHandler(LOLHTML.TextChunk.before, thisObject, globalObject, content, contentOptions);
- }
-
- pub fn after(
- this: *TextChunk,
- thisObject: js.JSObjectRef,
- globalObject: *JSGlobalObject,
- content: ZigString,
- contentOptions: ?ContentOptions,
- ) JSValue {
- return this.contentHandler(LOLHTML.TextChunk.after, thisObject, globalObject, content, contentOptions);
- }
-
- pub fn replace(
- this: *TextChunk,
- thisObject: js.JSObjectRef,
- globalObject: *JSGlobalObject,
- content: ZigString,
- contentOptions: ?ContentOptions,
- ) JSValue {
- return this.contentHandler(LOLHTML.TextChunk.replace, thisObject, globalObject, content, contentOptions);
- }
-
- pub fn remove(this: *TextChunk, thisObject: js.JSObjectRef) JSValue {
- if (this.text_chunk == null)
- return JSC.JSValue.jsUndefined();
- this.text_chunk.?.remove();
- return JSValue.fromRef(thisObject);
- }
-
- pub fn getText(this: *TextChunk, global: *JSGlobalObject) JSValue {
- if (this.text_chunk == null)
- return JSC.JSValue.jsUndefined();
- return ZigString.init(this.text_chunk.?.getContent().slice()).withEncoding().toValue(global);
- }
-
- pub fn removed(this: *TextChunk, _: *JSGlobalObject) JSValue {
- return JSC.JSValue.jsBoolean(this.text_chunk.?.isRemoved());
- }
-
- pub fn finalize(this: *TextChunk) void {
- this.text_chunk = null;
- bun.default_allocator.destroy(this);
- }
-};
-
-pub const DocType = struct {
- doctype: ?*LOLHTML.DocType = null,
-
- pub fn finalize(this: *DocType) void {
- this.doctype = null;
- bun.default_allocator.destroy(this);
- }
-
- pub const Class = NewClass(
- DocType,
- .{
- .name = "DocType",
- },
- .{
- .finalize = finalize,
- },
- .{
- .name = .{
- .get = getterWrap(DocType, "name"),
- },
- .systemId = .{
- .get = getterWrap(DocType, "systemId"),
- },
-
- .publicId = .{
- .get = getterWrap(DocType, "publicId"),
- },
- },
- );
-
- /// The doctype name.
- pub fn name(this: *DocType, global: *JSGlobalObject) JSValue {
- if (this.doctype == null)
- return JSC.JSValue.jsUndefined();
- const str = this.doctype.?.getName().slice();
- if (str.len == 0)
- return JSValue.jsNull();
- return ZigString.init(str).toValue(global);
- }
-
- pub fn systemId(this: *DocType, global: *JSGlobalObject) JSValue {
- if (this.doctype == null)
- return JSC.JSValue.jsUndefined();
-
- const str = this.doctype.?.getSystemId().slice();
- if (str.len == 0)
- return JSValue.jsNull();
- return ZigString.init(str).toValue(global);
- }
-
- pub fn publicId(this: *DocType, global: *JSGlobalObject) JSValue {
- if (this.doctype == null)
- return JSC.JSValue.jsUndefined();
-
- const str = this.doctype.?.getPublicId().slice();
- if (str.len == 0)
- return JSValue.jsNull();
- return ZigString.init(str).toValue(global);
- }
-};
-
-pub const DocEnd = struct {
- doc_end: ?*LOLHTML.DocEnd,
-
- pub fn finalize(this: *DocEnd) void {
- this.doc_end = null;
- bun.default_allocator.destroy(this);
- }
-
- pub const Class = NewClass(
- DocEnd,
- .{ .name = "DocEnd" },
- .{
- .finalize = finalize,
- .append = .{
- .rfn = wrap(DocEnd, "append"),
- },
- },
- .{},
- );
-
- fn contentHandler(this: *DocEnd, comptime Callback: (fn (*LOLHTML.DocEnd, []const u8, bool) LOLHTML.Error!void), thisObject: js.JSObjectRef, globalObject: *JSGlobalObject, content: ZigString, contentOptions: ?ContentOptions) JSValue {
- if (this.doc_end == null)
- return JSValue.jsNull();
-
- var content_slice = content.toSlice(bun.default_allocator);
- defer content_slice.deinit();
-
- Callback(
- this.doc_end.?,
- content_slice.slice(),
- contentOptions != null and contentOptions.?.html,
- ) catch return throwLOLHTMLError(globalObject);
-
- return JSValue.fromRef(thisObject);
- }
-
- pub fn append(
- this: *DocEnd,
- thisObject: js.JSObjectRef,
- globalObject: *JSGlobalObject,
- content: ZigString,
- contentOptions: ?ContentOptions,
- ) JSValue {
- return this.contentHandler(LOLHTML.DocEnd.append, thisObject, globalObject, content, contentOptions);
- }
-};
-
-pub const Comment = struct {
- comment: ?*LOLHTML.Comment = null,
-
- pub fn finalize(this: *Comment) void {
- this.comment = null;
- bun.default_allocator.destroy(this);
- }
-
- pub const Class = NewClass(
- Comment,
- .{ .name = "Comment" },
- .{
- .before = .{
- .rfn = wrap(Comment, "before"),
- },
- .after = .{
- .rfn = wrap(Comment, "after"),
- },
-
- .replace = .{
- .rfn = wrap(Comment, "replace"),
- },
-
- .remove = .{
- .rfn = wrap(Comment, "remove"),
- },
- .finalize = finalize,
- },
- .{
- .removed = .{
- .get = getterWrap(Comment, "removed"),
- },
- .text = .{
- .get = getterWrap(Comment, "getText"),
- .set = setterWrap(Comment, "setText"),
- },
- },
- );
-
- fn contentHandler(this: *Comment, comptime Callback: (fn (*LOLHTML.Comment, []const u8, bool) LOLHTML.Error!void), thisObject: js.JSObjectRef, globalObject: *JSGlobalObject, content: ZigString, contentOptions: ?ContentOptions) JSValue {
- if (this.comment == null)
- return JSValue.jsNull();
- var content_slice = content.toSlice(bun.default_allocator);
- defer content_slice.deinit();
-
- Callback(
- this.comment.?,
- content_slice.slice(),
- contentOptions != null and contentOptions.?.html,
- ) catch return throwLOLHTMLError(globalObject);
-
- return JSValue.fromRef(thisObject);
- }
-
- pub fn before(
- this: *Comment,
- thisObject: js.JSObjectRef,
- globalObject: *JSGlobalObject,
- content: ZigString,
- contentOptions: ?ContentOptions,
- ) JSValue {
- return this.contentHandler(LOLHTML.Comment.before, thisObject, globalObject, content, contentOptions);
- }
-
- pub fn after(
- this: *Comment,
- thisObject: js.JSObjectRef,
- globalObject: *JSGlobalObject,
- content: ZigString,
- contentOptions: ?ContentOptions,
- ) JSValue {
- return this.contentHandler(LOLHTML.Comment.after, thisObject, globalObject, content, contentOptions);
- }
-
- pub fn replace(
- this: *Comment,
- thisObject: js.JSObjectRef,
- globalObject: *JSGlobalObject,
- content: ZigString,
- contentOptions: ?ContentOptions,
- ) JSValue {
- return this.contentHandler(LOLHTML.Comment.replace, thisObject, globalObject, content, contentOptions);
- }
-
- pub fn remove(this: *Comment, thisObject: js.JSObjectRef) JSValue {
- if (this.comment == null)
- return JSValue.jsNull();
- this.comment.?.remove();
- return JSValue.fromRef(thisObject);
- }
-
- pub fn getText(this: *Comment, global: *JSGlobalObject) JSValue {
- if (this.comment == null)
- return JSValue.jsNull();
- return ZigString.init(this.comment.?.getText().slice()).withEncoding().toValue(global);
- }
-
- pub fn setText(
- this: *Comment,
- value: JSValue,
- exception: JSC.C.ExceptionRef,
- global: *JSGlobalObject,
- ) void {
- if (this.comment == null)
- return;
- var text = value.toSlice(global, bun.default_allocator);
- defer text.deinit();
- this.comment.?.setText(text.slice()) catch {
- exception.* = throwLOLHTMLError(global).asObjectRef();
- };
- }
-
- pub fn removed(this: *Comment, _: *JSGlobalObject) JSValue {
- if (this.comment == null)
- return JSC.JSValue.jsUndefined();
- return JSC.JSValue.jsBoolean(this.comment.?.isRemoved());
- }
-};
-
-pub const EndTag = struct {
- end_tag: ?*LOLHTML.EndTag,
-
- pub fn finalize(this: *EndTag) void {
- this.end_tag = null;
- bun.default_allocator.destroy(this);
- }
-
- pub const Handler = struct {
- callback: ?JSC.JSValue,
- global: *JSGlobalObject,
-
- pub const onEndTag = HandlerCallback(
- Handler,
- EndTag,
- LOLHTML.EndTag,
- "end_tag",
- "callback",
- );
-
- pub const onEndTagHandler = LOLHTML.DirectiveHandler(LOLHTML.EndTag, Handler, onEndTag);
- };
-
- pub const Class = NewClass(
- EndTag,
- .{ .name = "EndTag" },
- .{
- .before = .{
- .rfn = wrap(EndTag, "before"),
- },
- .after = .{
- .rfn = wrap(EndTag, "after"),
- },
-
- .remove = .{
- .rfn = wrap(EndTag, "remove"),
- },
- .finalize = finalize,
- },
- .{
- .name = .{
- .get = getterWrap(EndTag, "getName"),
- .set = setterWrap(EndTag, "setName"),
- },
- },
- );
-
- fn contentHandler(this: *EndTag, comptime Callback: (fn (*LOLHTML.EndTag, []const u8, bool) LOLHTML.Error!void), thisObject: js.JSObjectRef, globalObject: *JSGlobalObject, content: ZigString, contentOptions: ?ContentOptions) JSValue {
- if (this.end_tag == null)
- return JSValue.jsNull();
-
- var content_slice = content.toSlice(bun.default_allocator);
- defer content_slice.deinit();
-
- Callback(
- this.end_tag.?,
- content_slice.slice(),
- contentOptions != null and contentOptions.?.html,
- ) catch return throwLOLHTMLError(globalObject);
-
- return JSValue.fromRef(thisObject);
- }
-
- pub fn before(
- this: *EndTag,
- thisObject: js.JSObjectRef,
- globalObject: *JSGlobalObject,
- content: ZigString,
- contentOptions: ?ContentOptions,
- ) JSValue {
- return this.contentHandler(LOLHTML.EndTag.before, thisObject, globalObject, content, contentOptions);
- }
-
- pub fn after(
- this: *EndTag,
- thisObject: js.JSObjectRef,
- globalObject: *JSGlobalObject,
- content: ZigString,
- contentOptions: ?ContentOptions,
- ) JSValue {
- return this.contentHandler(LOLHTML.EndTag.after, thisObject, globalObject, content, contentOptions);
- }
-
- pub fn replace(
- this: *EndTag,
- thisObject: js.JSObjectRef,
- globalObject: *JSGlobalObject,
- content: ZigString,
- contentOptions: ?ContentOptions,
- ) JSValue {
- return this.contentHandler(LOLHTML.EndTag.replace, thisObject, globalObject, content, contentOptions);
- }
-
- pub fn remove(this: *EndTag, thisObject: js.JSObjectRef) JSValue {
- if (this.end_tag == null)
- return JSC.JSValue.jsUndefined();
-
- this.end_tag.?.remove();
- return JSValue.fromRef(thisObject);
- }
-
- pub fn getName(this: *EndTag, global: *JSGlobalObject) JSValue {
- if (this.end_tag == null)
- return JSC.JSValue.jsUndefined();
-
- return ZigString.init(this.end_tag.?.getName().slice()).withEncoding().toValue(global);
- }
-
- pub fn setName(
- this: *EndTag,
- value: JSValue,
- exception: JSC.C.ExceptionRef,
- global: *JSGlobalObject,
- ) void {
- if (this.end_tag == null)
- return;
- var text = value.toSlice(global, bun.default_allocator);
- defer text.deinit();
- this.end_tag.?.setName(text.slice()) catch {
- exception.* = throwLOLHTMLError(global).asObjectRef();
- };
- }
-};
-
-pub const AttributeIterator = struct {
- iterator: ?*LOLHTML.Attribute.Iterator = null,
-
- const attribute_iterator_path: string = "file:///bun-vfs/lolhtml/AttributeIterator.js";
- const attribute_iterator_code: string =
- \\"use strict";
- \\
- \\class AttributeIterator {
- \\ constructor(internal) {
- \\ this.#iterator = internal;
- \\ }
- \\
- \\ #iterator;
- \\
- \\ [Symbol.iterator]() {
- \\ return this;
- \\ }
- \\
- \\ next() {
- \\ if (this.#iterator === null)
- \\ return {done: true};
- \\ var value = this.#iterator.next();
- \\ if (!value) {
- \\ this.#iterator = null;
- \\ return {done: true};
- \\ }
- \\ return {done: false, value: value};
- \\ }
- \\}
- \\
- \\return new AttributeIterator(internal1);
- ;
- threadlocal var attribute_iterator_class: JSC.C.JSObjectRef = undefined;
- threadlocal var attribute_iterator_loaded: bool = false;
-
- pub fn getAttributeIteratorJSClass(global: *JSGlobalObject) JSValue {
- if (attribute_iterator_loaded)
- return JSC.JSValue.fromRef(attribute_iterator_class);
- attribute_iterator_loaded = true;
- var exception_ptr: ?[*]JSC.JSValueRef = null;
- var name = JSC.C.JSStringCreateStatic("AttributeIteratorGetter", "AttributeIteratorGetter".len);
- var param_name = JSC.C.JSStringCreateStatic("internal1", "internal1".len);
- var attribute_iterator_class_ = JSC.C.JSObjectMakeFunction(
- global.ref(),
- name,
- 1,
- &[_]JSC.C.JSStringRef{param_name},
- JSC.C.JSStringCreateStatic(attribute_iterator_code.ptr, attribute_iterator_code.len),
- JSC.C.JSStringCreateStatic(attribute_iterator_path.ptr, attribute_iterator_path.len),
- 0,
- exception_ptr,
- );
- JSC.C.JSValueProtect(global.ref(), attribute_iterator_class_);
- attribute_iterator_class = attribute_iterator_class_;
- return JSC.JSValue.fromRef(attribute_iterator_class);
- }
-
- pub fn finalize(this: *AttributeIterator) void {
- if (this.iterator) |iter| {
- iter.deinit();
- this.iterator = null;
- }
- bun.default_allocator.destroy(this);
- }
-
- pub const Class = NewClass(
- AttributeIterator,
- .{ .name = "AttributeIterator" },
- .{
- .next = .{
- .rfn = wrap(AttributeIterator, "next"),
- },
- .finalize = finalize,
- },
- .{},
- );
-
- const value_ = ZigString.init("value");
- const done_ = ZigString.init("done");
- pub fn next(
- this: *AttributeIterator,
- globalObject: *JSGlobalObject,
- ) JSValue {
- if (this.iterator == null) {
- return JSC.JSValue.jsNull();
- }
-
- var attribute = this.iterator.?.next() orelse {
- this.iterator.?.deinit();
- this.iterator = null;
- return JSC.JSValue.jsNull();
- };
-
- // TODO: don't clone here
- const value = attribute.value();
- const name = attribute.name();
- defer name.deinit();
- defer value.deinit();
-
- var strs = [2]ZigString{
- ZigString.init(name.slice()),
- ZigString.init(value.slice()),
- };
-
- var valid_strs: []ZigString = strs[0..2];
-
- var array = JSC.JSValue.createStringArray(
- globalObject,
- valid_strs.ptr,
- valid_strs.len,
- true,
- );
-
- return array;
- }
-};
-pub const Element = struct {
- element: ?*LOLHTML.Element = null,
-
- pub const Class = NewClass(
- Element,
- .{ .name = "Element" },
- .{
- .getAttribute = .{
- .rfn = wrap(Element, "getAttribute"),
- },
- .hasAttribute = .{
- .rfn = wrap(Element, "hasAttribute"),
- },
- .setAttribute = .{
- .rfn = wrap(Element, "setAttribute"),
- },
- .removeAttribute = .{
- .rfn = wrap(Element, "removeAttribute"),
- },
- .before = .{
- .rfn = wrap(Element, "before"),
- },
- .after = .{
- .rfn = wrap(Element, "after"),
- },
- .prepend = .{
- .rfn = wrap(Element, "prepend"),
- },
- .append = .{
- .rfn = wrap(Element, "append"),
- },
- .replace = .{
- .rfn = wrap(Element, "replace"),
- },
- .setInnerContent = .{
- .rfn = wrap(Element, "setInnerContent"),
- },
- .remove = .{
- .rfn = wrap(Element, "remove"),
- },
- .removeAndKeepContent = .{
- .rfn = wrap(Element, "removeAndKeepContent"),
- },
- .onEndTag = .{
- .rfn = wrap(Element, "onEndTag"),
- },
- .finalize = finalize,
- },
- .{
- .tagName = .{
- .get = getterWrap(Element, "getTagName"),
- .set = setterWrap(Element, "setTagName"),
- },
- .removed = .{
- .get = getterWrap(Element, "getRemoved"),
- },
- .namespaceURI = .{
- .get = getterWrap(Element, "getNamespaceURI"),
- },
- .attributes = .{
- .get = getterWrap(Element, "getAttributes"),
- },
- },
- );
-
- pub fn finalize(this: *Element) void {
- this.element = null;
- bun.default_allocator.destroy(this);
- }
-
- pub fn onEndTag(
- this: *Element,
- globalObject: *JSGlobalObject,
- function: JSValue,
- thisObject: JSC.C.JSObjectRef,
- ) JSValue {
- if (this.element == null)
- return JSValue.jsNull();
- if (function.isUndefinedOrNull() or !function.isCallable(globalObject.vm())) {
- return ZigString.init("Expected a function").withEncoding().toValue(globalObject);
- }
-
- var end_tag_handler = bun.default_allocator.create(EndTag.Handler) catch unreachable;
- end_tag_handler.* = .{ .global = globalObject, .callback = function };
-
- this.element.?.onEndTag(EndTag.Handler.onEndTagHandler, end_tag_handler) catch {
- bun.default_allocator.destroy(end_tag_handler);
- return throwLOLHTMLError(globalObject);
- };
-
- JSC.C.JSValueProtect(globalObject.ref(), function.asObjectRef());
- return JSValue.fromRef(thisObject);
- }
-
- // // fn wrap(comptime name: string)
-
- /// Returns the value for a given attribute name: ZigString on the element, or null if it is not found.
- pub fn getAttribute(this: *Element, globalObject: *JSGlobalObject, name: ZigString) JSValue {
- if (this.element == null)
- return JSValue.jsNull();
-
- var slice = name.toSlice(bun.default_allocator);
- defer slice.deinit();
- var attr = this.element.?.getAttribute(slice.slice()).slice();
-
- if (attr.len == 0)
- return JSC.JSValue.jsNull();
-
- var str = ZigString.init(
- attr,
- );
-
- return str.toExternalValueWithCallback(
- globalObject,
- free_html_writer_string,
- );
- }
-
- /// Returns a boolean indicating whether an attribute exists on the element.
- pub fn hasAttribute(this: *Element, global: *JSGlobalObject, name: ZigString) JSValue {
- if (this.element == null)
- return JSValue.jsBoolean(false);
-
- var slice = name.toSlice(bun.default_allocator);
- defer slice.deinit();
- return JSValue.jsBoolean(this.element.?.hasAttribute(slice.slice()) catch return throwLOLHTMLError(global));
- }
-
- /// Sets an attribute to a provided value, creating the attribute if it does not exist.
- pub fn setAttribute(this: *Element, thisObject: js.JSObjectRef, globalObject: *JSGlobalObject, name_: ZigString, value_: ZigString) JSValue {
- if (this.element == null)
- return JSValue.jsUndefined();
-
- var name_slice = name_.toSlice(bun.default_allocator);
- defer name_slice.deinit();
-
- var value_slice = value_.toSlice(bun.default_allocator);
- defer value_slice.deinit();
- this.element.?.setAttribute(name_slice.slice(), value_slice.slice()) catch return throwLOLHTMLError(globalObject);
- return JSValue.fromRef(thisObject);
- }
-
- /// Removes the attribute.
- pub fn removeAttribute(this: *Element, thisObject: js.JSObjectRef, globalObject: *JSGlobalObject, name: ZigString) JSValue {
- if (this.element == null)
- return JSValue.jsUndefined();
-
- var name_slice = name.toSlice(bun.default_allocator);
- defer name_slice.deinit();
-
- this.element.?.removeAttribute(
- name_slice.slice(),
- ) catch return throwLOLHTMLError(globalObject);
- return JSValue.fromRef(thisObject);
- }
-
- fn contentHandler(this: *Element, comptime Callback: (fn (*LOLHTML.Element, []const u8, bool) LOLHTML.Error!void), thisObject: js.JSObjectRef, globalObject: *JSGlobalObject, content: ZigString, contentOptions: ?ContentOptions) JSValue {
- if (this.element == null)
- return JSValue.jsUndefined();
-
- var content_slice = content.toSlice(bun.default_allocator);
- defer content_slice.deinit();
-
- Callback(
- this.element.?,
- content_slice.slice(),
- contentOptions != null and contentOptions.?.html,
- ) catch return throwLOLHTMLError(globalObject);
-
- return JSValue.fromRef(thisObject);
- }
-
- /// Inserts content before the element.
- pub fn before(this: *Element, thisObject: js.JSObjectRef, globalObject: *JSGlobalObject, content: ZigString, contentOptions: ?ContentOptions) JSValue {
- return contentHandler(
- this,
- LOLHTML.Element.before,
- thisObject,
- globalObject,
- content,
- contentOptions,
- );
- }
-
- /// Inserts content right after the element.
- pub fn after(this: *Element, thisObject: js.JSObjectRef, globalObject: *JSGlobalObject, content: ZigString, contentOptions: ?ContentOptions) JSValue {
- return contentHandler(
- this,
- LOLHTML.Element.after,
- thisObject,
- globalObject,
- content,
- contentOptions,
- );
- }
-
- /// Inserts content right after the start tag of the element.
- pub fn prepend(this: *Element, thisObject: js.JSObjectRef, globalObject: *JSGlobalObject, content: ZigString, contentOptions: ?ContentOptions) JSValue {
- return contentHandler(
- this,
- LOLHTML.Element.prepend,
- thisObject,
- globalObject,
- content,
- contentOptions,
- );
- }
-
- /// Inserts content right before the end tag of the element.
- pub fn append(this: *Element, thisObject: js.JSObjectRef, globalObject: *JSGlobalObject, content: ZigString, contentOptions: ?ContentOptions) JSValue {
- return contentHandler(
- this,
- LOLHTML.Element.append,
- thisObject,
- globalObject,
- content,
- contentOptions,
- );
- }
-
- /// Removes the element and inserts content in place of it.
- pub fn replace(this: *Element, thisObject: js.JSObjectRef, globalObject: *JSGlobalObject, content: ZigString, contentOptions: ?ContentOptions) JSValue {
- return contentHandler(
- this,
- LOLHTML.Element.replace,
- thisObject,
- globalObject,
- content,
- contentOptions,
- );
- }
-
- /// Replaces content of the element.
- pub fn setInnerContent(this: *Element, thisObject: js.JSObjectRef, globalObject: *JSGlobalObject, content: ZigString, contentOptions: ?ContentOptions) JSValue {
- return contentHandler(
- this,
- LOLHTML.Element.setInnerContent,
- thisObject,
- globalObject,
- content,
- contentOptions,
- );
- }
-
- /// Removes the element with all its content.
- pub fn remove(this: *Element, thisObject: js.JSObjectRef) JSValue {
- if (this.element == null)
- return JSValue.jsUndefined();
-
- this.element.?.remove();
- return JSValue.fromRef(thisObject);
- }
-
- /// Removes the start tag and end tag of the element but keeps its inner content intact.
- pub fn removeAndKeepContent(this: *Element, thisObject: js.JSObjectRef) JSValue {
- if (this.element == null)
- return JSValue.jsUndefined();
-
- this.element.?.removeAndKeepContent();
- return JSValue.fromRef(thisObject);
- }
-
- pub fn getTagName(this: *Element, globalObject: *JSGlobalObject) JSValue {
- if (this.element == null)
- return JSValue.jsUndefined();
-
- return htmlStringValue(this.element.?.tagName(), globalObject);
- }
-
- pub fn setTagName(this: *Element, value: JSValue, exception: JSC.C.ExceptionRef, global: *JSGlobalObject) void {
- if (this.element == null)
- return;
-
- var text = value.toSlice(global, bun.default_allocator);
- defer text.deinit();
-
- this.element.?.setTagName(text.slice()) catch {
- exception.* = throwLOLHTMLError(global).asObjectRef();
- };
- }
-
- pub fn getRemoved(this: *Element, _: *JSGlobalObject) JSValue {
- if (this.element == null)
- return JSValue.jsUndefined();
- return JSC.JSValue.jsBoolean(this.element.?.isRemoved());
- }
-
- pub fn getNamespaceURI(this: *Element, globalObject: *JSGlobalObject) JSValue {
- if (this.element == null)
- return JSValue.jsUndefined();
-
- return ZigString.init(std.mem.span(this.element.?.namespaceURI())).toValue(globalObject);
- }
-
- pub fn getAttributes(this: *Element, globalObject: *JSGlobalObject) JSValue {
- if (this.element == null)
- return JSValue.jsUndefined();
-
- var iter = this.element.?.attributes() orelse return throwLOLHTMLError(globalObject);
- var attr_iter = bun.default_allocator.create(AttributeIterator) catch unreachable;
- attr_iter.* = .{ .iterator = iter };
- var attr = AttributeIterator.Class.make(globalObject.ref(), attr_iter);
- JSC.C.JSValueProtect(globalObject.ref(), attr);
- defer JSC.C.JSValueUnprotect(globalObject.ref(), attr);
- return JSC.JSValue.fromRef(
- JSC.C.JSObjectCallAsFunction(
- globalObject.ref(),
- AttributeIterator.getAttributeIteratorJSClass(globalObject).asObjectRef(),
- null,
- 1,
- @ptrCast([*]JSC.C.JSObjectRef, &attr),
- null,
- ),
- );
- }
-};