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", .{});
+}