mirror of
https://github.com/lightpanda-io/browser.git
synced 2025-12-15 15:58:57 +00:00
Support Image constructor (i.e. new Image(..))
This commit is contained in:
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
89
src/browser/tests/element/html/image.html
Normal file
89
src/browser/tests/element/html/image.html
Normal file
@@ -0,0 +1,89 @@
|
||||
<!DOCTYPE html>
|
||||
<script src="../../testing.js"></script>
|
||||
|
||||
<script id="Image">
|
||||
{
|
||||
let i1 = new Image()
|
||||
testing.expectEqual(0, i1.width);
|
||||
testing.expectEqual(null, i1.getAttribute('width'));
|
||||
testing.expectEqual(0, i1.height);
|
||||
testing.expectEqual(null, i1.getAttribute('height'));
|
||||
|
||||
let i2 = new Image(10)
|
||||
testing.expectEqual(10, i2.width);
|
||||
testing.expectEqual('10', i2.getAttribute('width'));
|
||||
testing.expectEqual(0, i2.height);
|
||||
testing.expectEqual(null, i2.getAttribute('height'));
|
||||
|
||||
let i3 = new Image(10, 20)
|
||||
testing.expectEqual(10, i3.width);
|
||||
testing.expectEqual('10', i3.getAttribute('width'));
|
||||
testing.expectEqual(20, i3.height);
|
||||
testing.expectEqual('20', i3.getAttribute('height'));
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id="src_alt">
|
||||
{
|
||||
const img = document.createElement('img');
|
||||
|
||||
testing.expectEqual('', img.src);
|
||||
testing.expectEqual('', img.alt);
|
||||
|
||||
img.src = 'test.png';
|
||||
testing.expectEqual('test.png', img.src);
|
||||
testing.expectEqual('test.png', img.getAttribute('src'));
|
||||
|
||||
img.alt = 'Test image';
|
||||
testing.expectEqual('Test image', img.alt);
|
||||
testing.expectEqual('Test image', img.getAttribute('alt'));
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id="width_height_setters">
|
||||
{
|
||||
const img = document.createElement('img');
|
||||
|
||||
img.width = 100;
|
||||
testing.expectEqual(100, img.width);
|
||||
testing.expectEqual('100', img.getAttribute('width'));
|
||||
|
||||
img.height = 200;
|
||||
testing.expectEqual(200, img.height);
|
||||
testing.expectEqual('200', img.getAttribute('height'));
|
||||
|
||||
img.setAttribute('width', '50');
|
||||
testing.expectEqual(50, img.width);
|
||||
|
||||
img.setAttribute('height', 'invalid');
|
||||
testing.expectEqual(0, img.height);
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id="crossOrigin">
|
||||
{
|
||||
const img = document.createElement('img');
|
||||
|
||||
testing.expectEqual(null, img.crossOrigin);
|
||||
|
||||
img.crossOrigin = 'anonymous';
|
||||
testing.expectEqual('anonymous', img.crossOrigin);
|
||||
testing.expectEqual('anonymous', img.getAttribute('crossorigin'));
|
||||
|
||||
img.crossOrigin = null;
|
||||
testing.expectEqual(null, img.crossOrigin);
|
||||
testing.expectEqual(null, img.getAttribute('crossorigin'));
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id="loading">
|
||||
{
|
||||
const img = document.createElement('img');
|
||||
|
||||
testing.expectEqual('eager', img.loading);
|
||||
|
||||
img.loading = 'lazy';
|
||||
testing.expectEqual('lazy', img.loading);
|
||||
testing.expectEqual('lazy', img.getAttribute('loading'));
|
||||
}
|
||||
</script>
|
||||
@@ -1,42 +1,120 @@
|
||||
// Copyright (C) 2023-2025 Lightpanda (Selecy SAS)
|
||||
//
|
||||
// Francis Bouvier <francis@lightpanda.io>
|
||||
// Pierre Tachoire <pierre@lightpanda.io>
|
||||
//
|
||||
// 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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
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", .{});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user