diff --git a/src/browser/js/Env.zig b/src/browser/js/Env.zig index 2c87510f..052c1916 100644 --- a/src/browser/js/Env.zig +++ b/src/browser/js/Env.zig @@ -333,19 +333,6 @@ fn generateConstructor(comptime JsApi: type, isolate: v8.Isolate) v8.FunctionTem return template; } -// fn generateUndetectable(comptime Struct: type, template: v8.ObjectTemplate) void { -// const has_js_call_as_function = @hasDecl(Struct, "jsCallAsFunction"); - -// if (has_js_call_as_function) { - -// if (@hasDecl(Struct, "htmldda") and Struct.htmldda) { -// if (!has_js_call_as_function) { -// @compileError(@typeName(Struct) ++ ": htmldda required jsCallAsFunction to be defined. This is a hard-coded requirement in V8, because mark_as_undetectable only exists for HTMLAllCollection which is also callable."); -// } -// template.markAsUndetectable(); -// } -// } - pub fn protoIndexLookup(comptime JsApi: type) ?bridge.JsApiLookup.BackingInt { @setEvalBranchQuota(2000); comptime { diff --git a/src/browser/js/ExecutionWorld.zig b/src/browser/js/ExecutionWorld.zig index 72383343..77a5865d 100644 --- a/src/browser/js/ExecutionWorld.zig +++ b/src/browser/js/ExecutionWorld.zig @@ -116,8 +116,9 @@ pub fn createContext(self: *ExecutionWorld, page: *Page, enter: bool, global_cal // are now going to get associated with our global instance. inline for (JsApis, 0..) |JsApi, i| { if (@hasDecl(JsApi.Meta, "name")) { - const class_name = v8.String.initUtf8(isolate, JsApi.Meta.name); - global_template.set(class_name.toName(), templates[i], v8.PropertyAttribute.None); + const class_name = if (@hasDecl(JsApi.Meta, "constructor_alias")) JsApi.Meta.constructor_alias else JsApi.Meta.name; + const v8_class_name = v8.String.initUtf8(isolate, class_name); + global_template.set(v8_class_name.toName(), templates[i], v8.PropertyAttribute.None); } } diff --git a/src/browser/tests/element/html/image.html b/src/browser/tests/element/html/image.html new file mode 100644 index 00000000..5ad6454d --- /dev/null +++ b/src/browser/tests/element/html/image.html @@ -0,0 +1,89 @@ + + + + + + + + + + + + diff --git a/src/browser/webapi/element/html/Image.zig b/src/browser/webapi/element/html/Image.zig index 2cbd2634..86f82d1d 100644 --- a/src/browser/webapi/element/html/Image.zig +++ b/src/browser/webapi/element/html/Image.zig @@ -1,42 +1,120 @@ -// Copyright (C) 2023-2025 Lightpanda (Selecy SAS) -// -// Francis Bouvier -// Pierre Tachoire -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as -// published by the Free Software Foundation, either version 3 of the -// License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - +const std = @import("std"); const js = @import("../../../js/js.zig"); +const Page = @import("../../../Page.zig"); const Node = @import("../../Node.zig"); const Element = @import("../../Element.zig"); const HtmlElement = @import("../Html.zig"); +pub fn registerTypes() []const type { + return &.{ + Image, + // Factory, + }; +} + const Image = @This(); _proto: *HtmlElement, +pub fn constructor(w_: ?u32, h_: ?u32, page: *Page) !*Image { + const node = try page.createElement(null, "img", null); + const el = node.as(Element); + + if (w_) |w| blk: { + const w_string = std.fmt.bufPrint(&page.buf, "{d}", .{w}) catch break :blk; + try el.setAttributeSafe("width", w_string, page); + } + if (h_) |h| blk: { + const h_string = std.fmt.bufPrint(&page.buf, "{d}", .{h}) catch break :blk; + try el.setAttributeSafe("height", h_string, page); + } + return el.as(Image); +} + pub fn asElement(self: *Image) *Element { return self._proto._proto; } +pub fn asConstElement(self: *const Image) *const Element { + return self._proto._proto; +} pub fn asNode(self: *Image) *Node { return self.asElement().asNode(); } +pub fn getSrc(self: *const Image) []const u8 { + return self.asConstElement().getAttributeSafe("src") orelse ""; +} + +pub fn setSrc(self: *Image, value: []const u8, page: *Page) !void { + try self.asElement().setAttributeSafe("src", value, page); +} + +pub fn getAlt(self: *const Image) []const u8 { + return self.asConstElement().getAttributeSafe("alt") orelse ""; +} + +pub fn setAlt(self: *Image, value: []const u8, page: *Page) !void { + try self.asElement().setAttributeSafe("alt", value, page); +} + +pub fn getWidth(self: *const Image) u32 { + const attr = self.asConstElement().getAttributeSafe("width") orelse return 0; + return std.fmt.parseUnsigned(u32, attr, 10) catch 0; +} + +pub fn setWidth(self: *Image, value: u32, page: *Page) !void { + const str = try std.fmt.allocPrint(page.call_arena, "{d}", .{value}); + try self.asElement().setAttributeSafe("width", str, page); +} + +pub fn getHeight(self: *const Image) u32 { + const attr = self.asConstElement().getAttributeSafe("height") orelse return 0; + return std.fmt.parseUnsigned(u32, attr, 10) catch 0; +} + +pub fn setHeight(self: *Image, value: u32, page: *Page) !void { + const str = try std.fmt.allocPrint(page.call_arena, "{d}", .{value}); + try self.asElement().setAttributeSafe("height", str, page); +} + +pub fn getCrossOrigin(self: *const Image) ?[]const u8 { + return self.asConstElement().getAttributeSafe("crossorigin"); +} + +pub fn setCrossOrigin(self: *Image, value: ?[]const u8, page: *Page) !void { + if (value) |v| { + return self.asElement().setAttributeSafe("crossorigin", v, page); + } + return self.asElement().removeAttribute("crossorigin", page); +} + +pub fn getLoading(self: *const Image) []const u8 { + return self.asConstElement().getAttributeSafe("loading") orelse "eager"; +} + +pub fn setLoading(self: *Image, value: []const u8, page: *Page) !void { + try self.asElement().setAttributeSafe("loading", value, page); +} + pub const JsApi = struct { pub const bridge = js.Bridge(Image); pub const Meta = struct { pub const name = "HTMLImageElement"; + pub const constructor_alias = "Image"; pub const prototype_chain = bridge.prototypeChain(); pub var class_id: bridge.ClassId = undefined; }; + + pub const constructor = bridge.constructor(Image.constructor, .{}); + pub const src = bridge.accessor(Image.getSrc, Image.setSrc, .{}); + pub const alt = bridge.accessor(Image.getAlt, Image.setAlt, .{}); + pub const width = bridge.accessor(Image.getWidth, Image.setWidth, .{}); + pub const height = bridge.accessor(Image.getHeight, Image.setHeight, .{}); + pub const crossOrigin = bridge.accessor(Image.getCrossOrigin, Image.setCrossOrigin, .{}); + pub const loading = bridge.accessor(Image.getLoading, Image.setLoading, .{}); }; + +const testing = @import("../../../../testing.zig"); +test "WebApi: HTML.Image" { + try testing.htmlRunner("element/html/image.html", .{}); +}