diff --git a/src/browser/dom/document.zig b/src/browser/dom/document.zig index ea3c8e76..fe4a499a 100644 --- a/src/browser/dom/document.zig +++ b/src/browser/dom/document.zig @@ -129,7 +129,7 @@ pub const Document = struct { pub fn _createElement(self: *parser.Document, tag_name: []const u8, page: *Page) !CreateElementResult { const custom_element = page.window.custom_elements._get(tag_name) orelse { const e = try parser.documentCreateElement(self, tag_name); - return .{.element = try Element.toInterface(e)}; + return .{ .element = try Element.toInterface(e) }; }; var result: Env.Function.Result = undefined; @@ -142,7 +142,7 @@ pub const Document = struct { }); return err; }; - return .{.custom = js_obj}; + return .{ .custom = js_obj }; } pub fn _createElementNS(self: *parser.Document, ns: []const u8, tag_name: []const u8) !ElementUnion { diff --git a/src/browser/html/elements.zig b/src/browser/html/elements.zig index 350c51cc..1bb5b5fa 100644 --- a/src/browser/html/elements.zig +++ b/src/browser/html/elements.zig @@ -115,13 +115,7 @@ pub const HTMLElement = struct { pub const subtype = .node; pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element { - const constructor_name = try js_this.constructorName(page.call_arena); - const tag_name = page.window.custom_elements.names.get(constructor_name) orelse { - return error.IllegalContructor; - }; - - const el = try parser.documentCreateElement(@ptrCast(page.window.document), tag_name); - return el; + return constructHtmlElement(page, js_this); } pub fn get_style(e: *parser.ElementHTML, page: *Page) !*CSSStyleDeclaration { @@ -186,6 +180,10 @@ pub const HTMLMediaElement = struct { pub const Self = parser.MediaElement; pub const prototype = *HTMLElement; pub const subtype = .node; + + pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element { + return constructHtmlElement(page, js_this); + } }; // HTML elements @@ -195,6 +193,10 @@ pub const HTMLUnknownElement = struct { pub const Self = parser.Unknown; pub const prototype = *HTMLElement; pub const subtype = .node; + + pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element { + return constructHtmlElement(page, js_this); + } }; // https://html.spec.whatwg.org/#the-a-element @@ -203,6 +205,10 @@ pub const HTMLAnchorElement = struct { pub const prototype = *HTMLElement; pub const subtype = .node; + pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element { + return constructHtmlElement(page, js_this); + } + pub fn get_target(self: *parser.Anchor) ![]const u8 { return try parser.anchorGetTarget(self); } @@ -440,144 +446,240 @@ pub const HTMLAppletElement = struct { pub const Self = parser.Applet; pub const prototype = *HTMLElement; pub const subtype = .node; + + pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element { + return constructHtmlElement(page, js_this); + } }; pub const HTMLAreaElement = struct { pub const Self = parser.Area; pub const prototype = *HTMLElement; pub const subtype = .node; + + pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element { + return constructHtmlElement(page, js_this); + } }; pub const HTMLAudioElement = struct { pub const Self = parser.Audio; pub const prototype = *HTMLMediaElement; pub const subtype = .node; + + pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element { + return constructHtmlElement(page, js_this); + } }; pub const HTMLBRElement = struct { pub const Self = parser.BR; pub const prototype = *HTMLElement; pub const subtype = .node; + + pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element { + return constructHtmlElement(page, js_this); + } }; pub const HTMLBaseElement = struct { pub const Self = parser.Base; pub const prototype = *HTMLElement; pub const subtype = .node; + + pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element { + return constructHtmlElement(page, js_this); + } }; pub const HTMLBodyElement = struct { pub const Self = parser.Body; pub const prototype = *HTMLElement; pub const subtype = .node; + + pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element { + return constructHtmlElement(page, js_this); + } }; pub const HTMLButtonElement = struct { pub const Self = parser.Button; pub const prototype = *HTMLElement; pub const subtype = .node; + + pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element { + return constructHtmlElement(page, js_this); + } }; pub const HTMLCanvasElement = struct { pub const Self = parser.Canvas; pub const prototype = *HTMLElement; pub const subtype = .node; + + pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element { + return constructHtmlElement(page, js_this); + } }; pub const HTMLDListElement = struct { pub const Self = parser.DList; pub const prototype = *HTMLElement; pub const subtype = .node; + + pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element { + return constructHtmlElement(page, js_this); + } }; pub const HTMLDataElement = struct { pub const Self = parser.Data; pub const prototype = *HTMLElement; pub const subtype = .node; + + pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element { + return constructHtmlElement(page, js_this); + } }; pub const HTMLDataListElement = struct { pub const Self = parser.DataList; pub const prototype = *HTMLElement; pub const subtype = .node; + + pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element { + return constructHtmlElement(page, js_this); + } }; pub const HTMLDialogElement = struct { pub const Self = parser.Dialog; pub const prototype = *HTMLElement; pub const subtype = .node; + + pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element { + return constructHtmlElement(page, js_this); + } }; pub const HTMLDirectoryElement = struct { pub const Self = parser.Directory; pub const prototype = *HTMLElement; pub const subtype = .node; + + pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element { + return constructHtmlElement(page, js_this); + } }; pub const HTMLDivElement = struct { pub const Self = parser.Div; pub const prototype = *HTMLElement; pub const subtype = .node; + + pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element { + return constructHtmlElement(page, js_this); + } }; pub const HTMLEmbedElement = struct { pub const Self = parser.Embed; pub const prototype = *HTMLElement; pub const subtype = .node; + + pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element { + return constructHtmlElement(page, js_this); + } }; pub const HTMLFieldSetElement = struct { pub const Self = parser.FieldSet; pub const prototype = *HTMLElement; pub const subtype = .node; + + pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element { + return constructHtmlElement(page, js_this); + } }; pub const HTMLFontElement = struct { pub const Self = parser.Font; pub const prototype = *HTMLElement; pub const subtype = .node; + + pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element { + return constructHtmlElement(page, js_this); + } }; pub const HTMLFrameElement = struct { pub const Self = parser.Frame; pub const prototype = *HTMLElement; pub const subtype = .node; + + pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element { + return constructHtmlElement(page, js_this); + } }; pub const HTMLFrameSetElement = struct { pub const Self = parser.FrameSet; pub const prototype = *HTMLElement; pub const subtype = .node; + + pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element { + return constructHtmlElement(page, js_this); + } }; pub const HTMLHRElement = struct { pub const Self = parser.HR; pub const prototype = *HTMLElement; pub const subtype = .node; + + pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element { + return constructHtmlElement(page, js_this); + } }; pub const HTMLHeadElement = struct { pub const Self = parser.Head; pub const prototype = *HTMLElement; pub const subtype = .node; + + pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element { + return constructHtmlElement(page, js_this); + } }; pub const HTMLHeadingElement = struct { pub const Self = parser.Heading; pub const prototype = *HTMLElement; pub const subtype = .node; + + pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element { + return constructHtmlElement(page, js_this); + } }; pub const HTMLHtmlElement = struct { pub const Self = parser.Html; pub const prototype = *HTMLElement; pub const subtype = .node; + + pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element { + return constructHtmlElement(page, js_this); + } }; pub const HTMLIFrameElement = struct { pub const Self = parser.IFrame; pub const prototype = *HTMLElement; pub const subtype = .node; + + pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element { + return constructHtmlElement(page, js_this); + } }; pub const HTMLImageElement = struct { @@ -585,6 +687,10 @@ pub const HTMLImageElement = struct { pub const prototype = *HTMLElement; pub const subtype = .node; + pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element { + return constructHtmlElement(page, js_this); + } + pub fn get_alt(self: *parser.Image) ![]const u8 { return try parser.imageGetAlt(self); } @@ -644,6 +750,10 @@ pub const HTMLInputElement = struct { pub const prototype = *HTMLElement; pub const subtype = .node; + pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element { + return constructHtmlElement(page, js_this); + } + pub fn get_defaultValue(self: *parser.Input) ![]const u8 { return try parser.inputGetDefaultValue(self); } @@ -732,114 +842,190 @@ pub const HTMLLIElement = struct { pub const Self = parser.LI; pub const prototype = *HTMLElement; pub const subtype = .node; + + pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element { + return constructHtmlElement(page, js_this); + } }; pub const HTMLLabelElement = struct { pub const Self = parser.Label; pub const prototype = *HTMLElement; pub const subtype = .node; + + pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element { + return constructHtmlElement(page, js_this); + } }; pub const HTMLLegendElement = struct { pub const Self = parser.Legend; pub const prototype = *HTMLElement; pub const subtype = .node; + + pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element { + return constructHtmlElement(page, js_this); + } }; pub const HTMLLinkElement = struct { pub const Self = parser.Link; pub const prototype = *HTMLElement; pub const subtype = .node; + + pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element { + return constructHtmlElement(page, js_this); + } }; pub const HTMLMapElement = struct { pub const Self = parser.Map; pub const prototype = *HTMLElement; pub const subtype = .node; + + pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element { + return constructHtmlElement(page, js_this); + } }; pub const HTMLMetaElement = struct { pub const Self = parser.Meta; pub const prototype = *HTMLElement; pub const subtype = .node; + + pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element { + return constructHtmlElement(page, js_this); + } }; pub const HTMLMeterElement = struct { pub const Self = parser.Meter; pub const prototype = *HTMLElement; pub const subtype = .node; + + pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element { + return constructHtmlElement(page, js_this); + } }; pub const HTMLModElement = struct { pub const Self = parser.Mod; pub const prototype = *HTMLElement; pub const subtype = .node; + + pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element { + return constructHtmlElement(page, js_this); + } }; pub const HTMLOListElement = struct { pub const Self = parser.OList; pub const prototype = *HTMLElement; pub const subtype = .node; + + pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element { + return constructHtmlElement(page, js_this); + } }; pub const HTMLObjectElement = struct { pub const Self = parser.Object; pub const prototype = *HTMLElement; pub const subtype = .node; + + pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element { + return constructHtmlElement(page, js_this); + } }; pub const HTMLOptGroupElement = struct { pub const Self = parser.OptGroup; pub const prototype = *HTMLElement; pub const subtype = .node; + + pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element { + return constructHtmlElement(page, js_this); + } }; pub const HTMLOptionElement = struct { pub const Self = parser.Option; pub const prototype = *HTMLElement; pub const subtype = .node; + + pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element { + return constructHtmlElement(page, js_this); + } }; pub const HTMLOutputElement = struct { pub const Self = parser.Output; pub const prototype = *HTMLElement; pub const subtype = .node; + + pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element { + return constructHtmlElement(page, js_this); + } }; pub const HTMLParagraphElement = struct { pub const Self = parser.Paragraph; pub const prototype = *HTMLElement; pub const subtype = .node; + + pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element { + return constructHtmlElement(page, js_this); + } }; pub const HTMLParamElement = struct { pub const Self = parser.Param; pub const prototype = *HTMLElement; pub const subtype = .node; + + pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element { + return constructHtmlElement(page, js_this); + } }; pub const HTMLPictureElement = struct { pub const Self = parser.Picture; pub const prototype = *HTMLElement; pub const subtype = .node; + + pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element { + return constructHtmlElement(page, js_this); + } }; pub const HTMLPreElement = struct { pub const Self = parser.Pre; pub const prototype = *HTMLElement; pub const subtype = .node; + + pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element { + return constructHtmlElement(page, js_this); + } }; pub const HTMLProgressElement = struct { pub const Self = parser.Progress; pub const prototype = *HTMLElement; pub const subtype = .node; + + pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element { + return constructHtmlElement(page, js_this); + } }; pub const HTMLQuoteElement = struct { pub const Self = parser.Quote; pub const prototype = *HTMLElement; pub const subtype = .node; + + pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element { + return constructHtmlElement(page, js_this); + } }; // https://html.spec.whatwg.org/#the-script-element @@ -848,6 +1034,10 @@ pub const HTMLScriptElement = struct { pub const prototype = *HTMLElement; pub const subtype = .node; + pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element { + return constructHtmlElement(page, js_this); + } + pub fn get_src(self: *parser.Script) !?[]const u8 { return try parser.elementGetAttribute( parser.scriptToElt(self), @@ -982,96 +1172,160 @@ pub const HTMLSourceElement = struct { pub const Self = parser.Source; pub const prototype = *HTMLElement; pub const subtype = .node; + + pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element { + return constructHtmlElement(page, js_this); + } }; pub const HTMLSpanElement = struct { pub const Self = parser.Span; pub const prototype = *HTMLElement; pub const subtype = .node; + + pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element { + return constructHtmlElement(page, js_this); + } }; pub const HTMLStyleElement = struct { pub const Self = parser.Style; pub const prototype = *HTMLElement; pub const subtype = .node; + + pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element { + return constructHtmlElement(page, js_this); + } }; pub const HTMLTableElement = struct { pub const Self = parser.Table; pub const prototype = *HTMLElement; pub const subtype = .node; + + pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element { + return constructHtmlElement(page, js_this); + } }; pub const HTMLTableCaptionElement = struct { pub const Self = parser.TableCaption; pub const prototype = *HTMLElement; pub const subtype = .node; + + pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element { + return constructHtmlElement(page, js_this); + } }; pub const HTMLTableCellElement = struct { pub const Self = parser.TableCell; pub const prototype = *HTMLElement; pub const subtype = .node; + + pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element { + return constructHtmlElement(page, js_this); + } }; pub const HTMLTableColElement = struct { pub const Self = parser.TableCol; pub const prototype = *HTMLElement; pub const subtype = .node; + + pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element { + return constructHtmlElement(page, js_this); + } }; pub const HTMLTableRowElement = struct { pub const Self = parser.TableRow; pub const prototype = *HTMLElement; pub const subtype = .node; + + pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element { + return constructHtmlElement(page, js_this); + } }; pub const HTMLTableSectionElement = struct { pub const Self = parser.TableSection; pub const prototype = *HTMLElement; pub const subtype = .node; + + pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element { + return constructHtmlElement(page, js_this); + } }; pub const HTMLTemplateElement = struct { pub const Self = parser.Template; pub const prototype = *HTMLElement; pub const subtype = .node; + + pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element { + return constructHtmlElement(page, js_this); + } }; pub const HTMLTextAreaElement = struct { pub const Self = parser.TextArea; pub const prototype = *HTMLElement; pub const subtype = .node; + + pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element { + return constructHtmlElement(page, js_this); + } }; pub const HTMLTimeElement = struct { pub const Self = parser.Time; pub const prototype = *HTMLElement; pub const subtype = .node; + + pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element { + return constructHtmlElement(page, js_this); + } }; pub const HTMLTitleElement = struct { pub const Self = parser.Title; pub const prototype = *HTMLElement; pub const subtype = .node; + + pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element { + return constructHtmlElement(page, js_this); + } }; pub const HTMLTrackElement = struct { pub const Self = parser.Track; pub const prototype = *HTMLElement; pub const subtype = .node; + + pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element { + return constructHtmlElement(page, js_this); + } }; pub const HTMLUListElement = struct { pub const Self = parser.UList; pub const prototype = *HTMLElement; pub const subtype = .node; + + pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element { + return constructHtmlElement(page, js_this); + } }; pub const HTMLVideoElement = struct { pub const Self = parser.Video; pub const prototype = *HTMLElement; pub const subtype = .node; + + pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element { + return constructHtmlElement(page, js_this); + } }; pub fn toInterface(comptime T: type, e: *parser.Element) !T { @@ -1149,6 +1403,17 @@ pub fn toInterface(comptime T: type, e: *parser.Element) !T { }; } +fn constructHtmlElement(page: *Page, js_this: Env.JsThis) !*parser.Element { + const constructor_name = try js_this.constructorName(page.call_arena); + std.debug.print("constructor: {s}\n", .{constructor_name}); + if (!page.window.custom_elements.lookup.contains(constructor_name)) { + return error.IllegalContructor; + } + + const el = try parser.documentCreateElement(@ptrCast(page.window.document), constructor_name); + return el; +} + const testing = @import("../../testing.zig"); test "Browser.HTML.Element" { var runner = try testing.jsRunner(testing.tracking_allocator, .{}); diff --git a/src/browser/webcomponents/custom_element_registry.zig b/src/browser/webcomponents/custom_element_registry.zig index 66abe0f6..3e429a67 100644 --- a/src/browser/webcomponents/custom_element_registry.zig +++ b/src/browser/webcomponents/custom_element_registry.zig @@ -26,10 +26,6 @@ const Page = @import("../page.zig").Page; const Element = @import("../dom/element.zig").Element; pub const CustomElementRegistry = struct { - // JS FunctionName -> Definition Name, so that, given a function, we can - // create the element with the right tag - names: std.StringHashMapUnmanaged([]const u8) = .empty, - // tag_name -> Function lookup: std.StringHashMapUnmanaged(Env.Function) = .empty, @@ -37,18 +33,14 @@ pub const CustomElementRegistry = struct { log.info(.browser, "define custom element", .{ .name = tag_name }); const arena = page.arena; - const gop = try self.lookup.getOrPut(arena, tag_name); - if (gop.found_existing) { - return error.DuplicateCustomElement; + if (!gop.found_existing) { + errdefer _ = self.lookup.remove(tag_name); + const owned_tag_name = try arena.dupe(u8, tag_name); + gop.key_ptr.* = owned_tag_name; } - errdefer _ = self.lookup.remove(tag_name); - - const owned_tag_name = try arena.dupe(u8, tag_name); - gop.key_ptr.* = owned_tag_name; gop.value_ptr.* = fun; - - try self.names.putNoClobber(arena, try fun.getName(arena), owned_tag_name); + fun.setName(tag_name); } pub fn _get(self: *CustomElementRegistry, name: []const u8) ?Env.Function { diff --git a/src/runtime/js.zig b/src/runtime/js.zig index 76a5ee0a..5f6e95b4 100644 --- a/src/runtime/js.zig +++ b/src/runtime/js.zig @@ -1262,6 +1262,11 @@ pub fn Env(comptime State: type, comptime WebApis: type) type { return valueToString(allocator, name, self.js_context.isolate, self.js_context.v8_context); } + pub fn setName(self: *const Function, name: []const u8) void { + const v8_name = v8.String.initUtf8(self.js_context.isolate, name); + self.func.castToFunction().setName(v8_name); + } + pub fn withThis(self: *const Function, value: anytype) !Function { const this_obj = if (@TypeOf(value) == JsObject) value.js_obj