mirror of
https://github.com/lightpanda-io/browser.git
synced 2025-10-28 22:53:28 +00:00
Implement select.options
Add HTMLOptionsCollection and enhance HTMLOptionElement API. Amazon.
This commit is contained in:
@@ -149,7 +149,9 @@ pub const Document = struct {
|
||||
tag_name: []const u8,
|
||||
page: *Page,
|
||||
) !collection.HTMLCollection {
|
||||
return try collection.HTMLCollectionByTagName(page.arena, parser.documentToNode(self), tag_name, true);
|
||||
return try collection.HTMLCollectionByTagName(page.arena, parser.documentToNode(self), tag_name, .{
|
||||
.include_root = true,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn _getElementsByClassName(
|
||||
@@ -157,7 +159,9 @@ pub const Document = struct {
|
||||
classNames: []const u8,
|
||||
page: *Page,
|
||||
) !collection.HTMLCollection {
|
||||
return try collection.HTMLCollectionByClassName(page.arena, parser.documentToNode(self), classNames, true);
|
||||
return try collection.HTMLCollectionByClassName(page.arena, parser.documentToNode(self), classNames, .{
|
||||
.include_root = true,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn _createDocumentFragment(self: *parser.Document) !*parser.DocumentFragment {
|
||||
@@ -201,7 +205,9 @@ pub const Document = struct {
|
||||
// ParentNode
|
||||
// https://dom.spec.whatwg.org/#parentnode
|
||||
pub fn get_children(self: *parser.Document) !collection.HTMLCollection {
|
||||
return try collection.HTMLCollectionChildren(parser.documentToNode(self), false);
|
||||
return collection.HTMLCollectionChildren(parser.documentToNode(self), .{
|
||||
.include_root = false,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn get_firstElementChild(self: *parser.Document) !?ElementUnion {
|
||||
|
||||
@@ -79,7 +79,9 @@ pub const DocumentFragment = struct {
|
||||
}
|
||||
|
||||
pub fn get_children(self: *parser.DocumentFragment) !collection.HTMLCollection {
|
||||
return collection.HTMLCollectionChildren(parser.documentFragmentToNode(self), false);
|
||||
return collection.HTMLCollectionChildren(parser.documentFragmentToNode(self), .{
|
||||
.include_root = false,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -294,7 +294,7 @@ pub const Element = struct {
|
||||
page.arena,
|
||||
parser.elementToNode(self),
|
||||
tag_name,
|
||||
false,
|
||||
.{ .include_root = false },
|
||||
);
|
||||
}
|
||||
|
||||
@@ -307,14 +307,16 @@ pub const Element = struct {
|
||||
page.arena,
|
||||
parser.elementToNode(self),
|
||||
classNames,
|
||||
false,
|
||||
.{ .include_root = false },
|
||||
);
|
||||
}
|
||||
|
||||
// ParentNode
|
||||
// https://dom.spec.whatwg.org/#parentnode
|
||||
pub fn get_children(self: *parser.Element) !collection.HTMLCollection {
|
||||
return try collection.HTMLCollectionChildren(parser.elementToNode(self), false);
|
||||
return collection.HTMLCollectionChildren(parser.elementToNode(self), .{
|
||||
.include_root = false,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn get_firstElementChild(self: *parser.Element) !?Union {
|
||||
|
||||
@@ -72,13 +72,14 @@ pub fn HTMLCollectionByTagName(
|
||||
arena: Allocator,
|
||||
root: ?*parser.Node,
|
||||
tag_name: []const u8,
|
||||
include_root: bool,
|
||||
opts: Opts,
|
||||
) !HTMLCollection {
|
||||
return HTMLCollection{
|
||||
.root = root,
|
||||
.walker = .{ .walkerDepthFirst = .{} },
|
||||
.matcher = .{ .matchByTagName = try MatchByTagName.init(arena, tag_name) },
|
||||
.include_root = include_root,
|
||||
.mutable = opts.mutable,
|
||||
.include_root = opts.include_root,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -109,13 +110,14 @@ pub fn HTMLCollectionByClassName(
|
||||
arena: Allocator,
|
||||
root: ?*parser.Node,
|
||||
classNames: []const u8,
|
||||
include_root: bool,
|
||||
opts: Opts,
|
||||
) !HTMLCollection {
|
||||
return HTMLCollection{
|
||||
.root = root,
|
||||
.walker = .{ .walkerDepthFirst = .{} },
|
||||
.matcher = .{ .matchByClassName = try MatchByClassName.init(arena, classNames) },
|
||||
.include_root = include_root,
|
||||
.mutable = opts.mutable,
|
||||
.include_root = opts.include_root,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -139,13 +141,14 @@ pub fn HTMLCollectionByName(
|
||||
arena: Allocator,
|
||||
root: ?*parser.Node,
|
||||
name: []const u8,
|
||||
include_root: bool,
|
||||
opts: Opts,
|
||||
) !HTMLCollection {
|
||||
return HTMLCollection{
|
||||
.root = root,
|
||||
.walker = .{ .walkerDepthFirst = .{} },
|
||||
.matcher = .{ .matchByName = try MatchByName.init(arena, name) },
|
||||
.include_root = include_root,
|
||||
.mutable = opts.mutable,
|
||||
.include_root = opts.include_root,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -189,13 +192,14 @@ pub const HTMLAllCollection = struct {
|
||||
|
||||
pub fn HTMLCollectionChildren(
|
||||
root: ?*parser.Node,
|
||||
include_root: bool,
|
||||
) !HTMLCollection {
|
||||
opts: Opts,
|
||||
) HTMLCollection {
|
||||
return HTMLCollection{
|
||||
.root = root,
|
||||
.walker = .{ .walkerChildren = .{} },
|
||||
.matcher = .{ .matchTrue = .{} },
|
||||
.include_root = include_root,
|
||||
.mutable = opts.mutable,
|
||||
.include_root = opts.include_root,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -224,13 +228,14 @@ pub const MatchByLinks = struct {
|
||||
|
||||
pub fn HTMLCollectionByLinks(
|
||||
root: ?*parser.Node,
|
||||
include_root: bool,
|
||||
opts: Opts,
|
||||
) !HTMLCollection {
|
||||
return HTMLCollection{
|
||||
.root = root,
|
||||
.walker = .{ .walkerDepthFirst = .{} },
|
||||
.matcher = .{ .matchByLinks = MatchByLinks{} },
|
||||
.include_root = include_root,
|
||||
.mutable = opts.mutable,
|
||||
.include_root = opts.include_root,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -249,13 +254,14 @@ pub const MatchByAnchors = struct {
|
||||
|
||||
pub fn HTMLCollectionByAnchors(
|
||||
root: ?*parser.Node,
|
||||
include_root: bool,
|
||||
opts: Opts,
|
||||
) !HTMLCollection {
|
||||
return HTMLCollection{
|
||||
.root = root,
|
||||
.walker = .{ .walkerDepthFirst = .{} },
|
||||
.matcher = .{ .matchByAnchors = MatchByAnchors{} },
|
||||
.include_root = include_root,
|
||||
.mutable = opts.mutable,
|
||||
.include_root = opts.include_root,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -285,6 +291,11 @@ pub const HTMLCollectionIterator = struct {
|
||||
}
|
||||
};
|
||||
|
||||
const Opts = struct {
|
||||
include_root: bool,
|
||||
mutable: bool = false,
|
||||
};
|
||||
|
||||
// WEB IDL https://dom.spec.whatwg.org/#htmlcollection
|
||||
// HTMLCollection is re implemented in zig here because libdom
|
||||
// dom_html_collection expects a comparison function callback as arguement.
|
||||
@@ -300,6 +311,8 @@ pub const HTMLCollection = struct {
|
||||
// itself.
|
||||
include_root: bool = false,
|
||||
|
||||
mutable: bool = false,
|
||||
|
||||
// save a state for the collection to improve the _item speed.
|
||||
cur_idx: ?u32 = null,
|
||||
cur_node: ?*parser.Node = null,
|
||||
@@ -350,12 +363,14 @@ pub const HTMLCollection = struct {
|
||||
var node: *parser.Node = undefined;
|
||||
|
||||
// Use the current state to improve speed if possible.
|
||||
if (self.cur_idx != null and index >= self.cur_idx.?) {
|
||||
if (self.mutable == false and self.cur_idx != null and index >= self.cur_idx.?) {
|
||||
i = self.cur_idx.?;
|
||||
node = self.cur_node.?;
|
||||
} else {
|
||||
node = try self.start() orelse return null;
|
||||
}
|
||||
// i = 0;
|
||||
// node = try self.start() orelse return null;
|
||||
|
||||
while (true) {
|
||||
if (try parser.nodeType(node) == .element) {
|
||||
|
||||
@@ -118,7 +118,9 @@ pub const HTMLDocument = struct {
|
||||
if (name.len == 0) return list;
|
||||
|
||||
const root = parser.documentHTMLToNode(self);
|
||||
var c = try collection.HTMLCollectionByName(arena, root, name, false);
|
||||
var c = try collection.HTMLCollectionByName(arena, root, name, .{
|
||||
.include_root = false,
|
||||
});
|
||||
|
||||
const ln = try c.get_length();
|
||||
var i: u32 = 0;
|
||||
@@ -132,11 +134,15 @@ pub const HTMLDocument = struct {
|
||||
}
|
||||
|
||||
pub fn get_images(self: *parser.DocumentHTML, page: *Page) !collection.HTMLCollection {
|
||||
return try collection.HTMLCollectionByTagName(page.arena, parser.documentHTMLToNode(self), "img", false);
|
||||
return try collection.HTMLCollectionByTagName(page.arena, parser.documentHTMLToNode(self), "img", .{
|
||||
.include_root = false,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn get_embeds(self: *parser.DocumentHTML, page: *Page) !collection.HTMLCollection {
|
||||
return try collection.HTMLCollectionByTagName(page.arena, parser.documentHTMLToNode(self), "embed", false);
|
||||
return try collection.HTMLCollectionByTagName(page.arena, parser.documentHTMLToNode(self), "embed", .{
|
||||
.include_root = false,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn get_plugins(self: *parser.DocumentHTML, page: *Page) !collection.HTMLCollection {
|
||||
@@ -144,11 +150,15 @@ pub const HTMLDocument = struct {
|
||||
}
|
||||
|
||||
pub fn get_forms(self: *parser.DocumentHTML, page: *Page) !collection.HTMLCollection {
|
||||
return try collection.HTMLCollectionByTagName(page.arena, parser.documentHTMLToNode(self), "form", false);
|
||||
return try collection.HTMLCollectionByTagName(page.arena, parser.documentHTMLToNode(self), "form", .{
|
||||
.include_root = false,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn get_scripts(self: *parser.DocumentHTML, page: *Page) !collection.HTMLCollection {
|
||||
return try collection.HTMLCollectionByTagName(page.arena, parser.documentHTMLToNode(self), "script", false);
|
||||
return try collection.HTMLCollectionByTagName(page.arena, parser.documentHTMLToNode(self), "script", .{
|
||||
.include_root = false,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn get_applets(_: *parser.DocumentHTML) !collection.HTMLCollection {
|
||||
@@ -156,11 +166,15 @@ pub const HTMLDocument = struct {
|
||||
}
|
||||
|
||||
pub fn get_links(self: *parser.DocumentHTML) !collection.HTMLCollection {
|
||||
return try collection.HTMLCollectionByLinks(parser.documentHTMLToNode(self), false);
|
||||
return try collection.HTMLCollectionByLinks(parser.documentHTMLToNode(self), .{
|
||||
.include_root = false,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn get_anchors(self: *parser.DocumentHTML) !collection.HTMLCollection {
|
||||
return try collection.HTMLCollectionByAnchors(parser.documentHTMLToNode(self), false);
|
||||
return try collection.HTMLCollectionByAnchors(parser.documentHTMLToNode(self), .{
|
||||
.include_root = false,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn get_all(self: *parser.DocumentHTML) collection.HTMLAllCollection {
|
||||
|
||||
@@ -75,7 +75,6 @@ pub const Interfaces = .{
|
||||
HTMLOListElement,
|
||||
HTMLObjectElement,
|
||||
HTMLOptGroupElement,
|
||||
HTMLOptionElement,
|
||||
HTMLOutputElement,
|
||||
HTMLParagraphElement,
|
||||
HTMLParamElement,
|
||||
@@ -102,7 +101,7 @@ pub const Interfaces = .{
|
||||
HTMLVideoElement,
|
||||
|
||||
@import("form.zig").HTMLFormElement,
|
||||
@import("select.zig").HTMLSelectElement,
|
||||
@import("select.zig").Interfaces,
|
||||
};
|
||||
|
||||
pub const Union = generate.Union(Interfaces);
|
||||
@@ -813,12 +812,6 @@ pub const HTMLOptGroupElement = struct {
|
||||
pub const subtype = .node;
|
||||
};
|
||||
|
||||
pub const HTMLOptionElement = struct {
|
||||
pub const Self = parser.Option;
|
||||
pub const prototype = *HTMLElement;
|
||||
pub const subtype = .node;
|
||||
};
|
||||
|
||||
pub const HTMLOutputElement = struct {
|
||||
pub const Self = parser.Output;
|
||||
pub const prototype = *HTMLElement;
|
||||
|
||||
@@ -18,8 +18,16 @@
|
||||
const std = @import("std");
|
||||
|
||||
const parser = @import("../netsurf.zig");
|
||||
const HTMLElement = @import("elements.zig").HTMLElement;
|
||||
const collection = @import("../dom/html_collection.zig");
|
||||
|
||||
const Page = @import("../page.zig").Page;
|
||||
const HTMLElement = @import("elements.zig").HTMLElement;
|
||||
|
||||
pub const Interfaces = .{
|
||||
HTMLSelectElement,
|
||||
HTMLOptionElement,
|
||||
HTMLOptionsCollection,
|
||||
};
|
||||
|
||||
pub const HTMLSelectElement = struct {
|
||||
pub const Self = parser.Select;
|
||||
@@ -89,6 +97,105 @@ pub const HTMLSelectElement = struct {
|
||||
try parser.optionSetSelected(option, true);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_options(select: *parser.Select) HTMLOptionsCollection {
|
||||
return .{
|
||||
.select = select,
|
||||
.proto = collection.HTMLCollectionChildren(@alignCast(@ptrCast(select)), .{
|
||||
.mutable = true,
|
||||
.include_root = false,
|
||||
}),
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
pub const HTMLOptionElement = struct {
|
||||
pub const Self = parser.Option;
|
||||
pub const prototype = *HTMLElement;
|
||||
pub const subtype = .node;
|
||||
|
||||
pub fn get_value(self: *parser.Option) ![]const u8 {
|
||||
return parser.optionGetValue(self);
|
||||
}
|
||||
pub fn set_value(self: *parser.Option, value: []const u8) !void {
|
||||
return parser.optionSetValue(self, value);
|
||||
}
|
||||
|
||||
pub fn get_label(self: *parser.Option) ![]const u8 {
|
||||
return parser.optionGetLabel(self);
|
||||
}
|
||||
pub fn set_label(self: *parser.Option, label: []const u8) !void {
|
||||
return parser.optionSetLabel(self, label);
|
||||
}
|
||||
|
||||
pub fn get_selected(self: *parser.Option) !bool {
|
||||
return parser.optionGetSelected(self);
|
||||
}
|
||||
pub fn set_selected(self: *parser.Option, value: bool) !void {
|
||||
return parser.optionSetSelected(self, value);
|
||||
}
|
||||
|
||||
pub fn get_disabled(self: *parser.Option) !bool {
|
||||
return parser.optionGetDisabled(self);
|
||||
}
|
||||
pub fn set_disabled(self: *parser.Option, value: bool) !void {
|
||||
return parser.optionSetDisabled(self, value);
|
||||
}
|
||||
|
||||
pub fn get_text(self: *parser.Option) ![]const u8 {
|
||||
return parser.optionGetText(self);
|
||||
}
|
||||
|
||||
pub fn get_form(self: *parser.Option) !?*parser.Form {
|
||||
return parser.optionGetForm(self);
|
||||
}
|
||||
};
|
||||
|
||||
pub const HTMLOptionsCollection = struct {
|
||||
pub const prototype = *collection.HTMLCollection;
|
||||
|
||||
proto: collection.HTMLCollection,
|
||||
select: *parser.Select,
|
||||
|
||||
pub fn get_selectedIndex(self: *HTMLOptionsCollection, page: *Page) !i32 {
|
||||
return HTMLSelectElement.get_selectedIndex(self.select, page);
|
||||
}
|
||||
|
||||
pub fn set_selectedIndex(self: *HTMLOptionsCollection, index: i32, page: *Page) !void {
|
||||
return HTMLSelectElement.set_selectedIndex(self.select, index, page);
|
||||
}
|
||||
|
||||
const BeforeOpts = union(enum) {
|
||||
index: u32,
|
||||
option: *parser.Option,
|
||||
};
|
||||
pub fn _add(self: *HTMLOptionsCollection, option: *parser.Option, before_: ?BeforeOpts) !void {
|
||||
const Node = @import("../dom/node.zig").Node;
|
||||
const before = before_ orelse {
|
||||
return self.appendOption(option);
|
||||
};
|
||||
|
||||
const insert_before: *parser.Node = switch (before) {
|
||||
.option => |o| @alignCast(@ptrCast(o)),
|
||||
.index => |i| (try self.proto.item(i)) orelse return self.appendOption(option),
|
||||
};
|
||||
return Node.before(insert_before, &.{
|
||||
.{ .node = @alignCast(@ptrCast(option)) },
|
||||
});
|
||||
}
|
||||
|
||||
pub fn _remove(self: *HTMLOptionsCollection, index: u32) !void {
|
||||
const Node = @import("../dom/node.zig").Node;
|
||||
const option = (try self.proto.item(index)) orelse return;
|
||||
_ = try Node._removeChild(@alignCast(@ptrCast(self.select)), option);
|
||||
}
|
||||
|
||||
fn appendOption(self: *HTMLOptionsCollection, option: *parser.Option) !void {
|
||||
const Node = @import("../dom/node.zig").Node;
|
||||
return Node.append(@alignCast(@ptrCast(self.select)), &.{
|
||||
.{ .node = @alignCast(@ptrCast(option)) },
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const testing = @import("../../testing.zig");
|
||||
@@ -140,5 +247,32 @@ test "Browser.HTML.Select" {
|
||||
|
||||
.{ "s.selectedIndex = -323", null },
|
||||
.{ "s.selectedIndex", "-1" },
|
||||
|
||||
.{ "let options = s.options", null },
|
||||
.{ "options.length", "2" },
|
||||
.{ "options.item(1).value", "o2" },
|
||||
.{ "options.selectedIndex", "-1" },
|
||||
|
||||
.{ "let o3 = document.createElement('option');", null },
|
||||
.{ "o3.value = 'o3';", null },
|
||||
.{ "options.add(o3)", null },
|
||||
.{ "options.length", "3" },
|
||||
.{ "options.item(2).value", "o3" },
|
||||
|
||||
.{ "let o4 = document.createElement('option');", null },
|
||||
.{ "o4.value = 'o4';", null },
|
||||
.{ "options.add(o4, 1)", null },
|
||||
.{ "options.length", "4" },
|
||||
.{ "options.item(1).value", "o4" },
|
||||
|
||||
.{ "let o5 = document.createElement('option');", null },
|
||||
.{ "o5.value = 'o5';", null },
|
||||
.{ "options.add(o5, o3)", null },
|
||||
.{ "options.length", "5" },
|
||||
.{ "options.item(3).value", "o5" },
|
||||
|
||||
.{ "options.remove(3)", null },
|
||||
.{ "options.length", "4" },
|
||||
.{ "options.item(3).value", "o3" },
|
||||
}, .{});
|
||||
}
|
||||
|
||||
@@ -2590,6 +2590,24 @@ pub fn optionGetValue(option: *Option) ![]const u8 {
|
||||
return strToData(s);
|
||||
}
|
||||
|
||||
pub fn optionSetLabel(input: *Option, label: []const u8) !void {
|
||||
const err = c.dom_html_option_element_set_label(input, try strFromData(label));
|
||||
try DOMErr(err);
|
||||
}
|
||||
|
||||
pub fn optionGetLabel(option: *Option) ![]const u8 {
|
||||
var s_: ?*String = null;
|
||||
const err = c.dom_html_option_element_get_label(option, &s_);
|
||||
try DOMErr(err);
|
||||
const s = s_ orelse return "";
|
||||
return strToData(s);
|
||||
}
|
||||
|
||||
pub fn optionSetValue(input: *Option, value: []const u8) !void {
|
||||
const err = c.dom_html_option_element_set_value(input, try strFromData(value));
|
||||
try DOMErr(err);
|
||||
}
|
||||
|
||||
pub fn optionGetSelected(option: *Option) !bool {
|
||||
var selected: bool = false;
|
||||
const err = c.dom_html_option_element_get_selected(option, &selected);
|
||||
@@ -2597,11 +2615,38 @@ pub fn optionGetSelected(option: *Option) !bool {
|
||||
return selected;
|
||||
}
|
||||
|
||||
pub fn optionSetDisabled(option: *Option, disabled: bool) !void {
|
||||
const err = c.dom_html_option_element_set_disabled(option, disabled);
|
||||
try DOMErr(err);
|
||||
}
|
||||
|
||||
pub fn optionGetDisabled(option: *Option) !bool {
|
||||
var disabled: bool = false;
|
||||
const err = c.dom_html_option_element_get_disabled(option, &disabled);
|
||||
try DOMErr(err);
|
||||
return disabled;
|
||||
}
|
||||
|
||||
pub fn optionSetSelected(option: *Option, selected: bool) !void {
|
||||
const err = c.dom_html_option_element_set_selected(option, selected);
|
||||
try DOMErr(err);
|
||||
}
|
||||
|
||||
pub fn optionGetText(option: *Option) ![]const u8 {
|
||||
var s_: ?*String = null;
|
||||
const err = c.dom_html_option_element_get_text(option, &s_);
|
||||
try DOMErr(err);
|
||||
const s = s_ orelse return "";
|
||||
return strToData(s);
|
||||
}
|
||||
|
||||
pub fn optionGetForm(option: *Option) !?*Form {
|
||||
var form: ?*Form = null;
|
||||
const err = c.dom_html_option_element_get_form(option, &form);
|
||||
try DOMErr(err);
|
||||
return form;
|
||||
}
|
||||
|
||||
// HtmlCollection
|
||||
pub fn htmlCollectionGetLength(collection: *HTMLCollection) !u32 {
|
||||
var len: u32 = 0;
|
||||
|
||||
Reference in New Issue
Block a user