mirror of
https://github.com/lightpanda-io/browser.git
synced 2025-10-29 15:13:28 +00:00
4
.gitignore
vendored
4
.gitignore
vendored
@@ -1,3 +1,7 @@
|
|||||||
zig-cache
|
zig-cache
|
||||||
zig-out
|
zig-out
|
||||||
/vendor/lexbor/
|
/vendor/lexbor/
|
||||||
|
/vendor/netsurf/build/
|
||||||
|
/vendor/netsurf/lib/
|
||||||
|
/vendor/netsurf/include/
|
||||||
|
/vendor/libiconv/
|
||||||
|
|||||||
15
.gitmodules
vendored
15
.gitmodules
vendored
@@ -4,3 +4,18 @@
|
|||||||
[submodule "vendor/lexbor-src"]
|
[submodule "vendor/lexbor-src"]
|
||||||
path = vendor/lexbor-src
|
path = vendor/lexbor-src
|
||||||
url = https://github.com/lexbor/lexbor
|
url = https://github.com/lexbor/lexbor
|
||||||
|
[submodule "vendor/netsurf/libwapcaplet"]
|
||||||
|
path = vendor/netsurf/libwapcaplet
|
||||||
|
url = https://source.netsurf-browser.org/libwapcaplet.git
|
||||||
|
[submodule "vendor/netsurf/libparserutils"]
|
||||||
|
path = vendor/netsurf/libparserutils
|
||||||
|
url = https://source.netsurf-browser.org/libparserutils.git
|
||||||
|
[submodule "vendor/netsurf/libdom"]
|
||||||
|
path = vendor/netsurf/libdom
|
||||||
|
url = https://source.netsurf-browser.org/libdom.git
|
||||||
|
[submodule "vendor/netsurf/share/netsurf-buildsystem"]
|
||||||
|
path = vendor/netsurf/share/netsurf-buildsystem
|
||||||
|
url = https://source.netsurf-browser.org/buildsystem.git
|
||||||
|
[submodule "vendor/netsurf/libhubbub"]
|
||||||
|
path = vendor/netsurf/libhubbub
|
||||||
|
url = https://source.netsurf-browser.org/libhubbub.git
|
||||||
|
|||||||
78
Makefile
78
Makefile
@@ -2,6 +2,7 @@
|
|||||||
# ---------
|
# ---------
|
||||||
|
|
||||||
ZIG := zig
|
ZIG := zig
|
||||||
|
BC := $(dir $(abspath $(lastword $(MAKEFILE_LIST))))
|
||||||
|
|
||||||
# Infos
|
# Infos
|
||||||
# -----
|
# -----
|
||||||
@@ -54,16 +55,85 @@ test:
|
|||||||
# Install and build required dependencies commands
|
# Install and build required dependencies commands
|
||||||
# ------------
|
# ------------
|
||||||
.PHONY: install-submodule
|
.PHONY: install-submodule
|
||||||
.PHONY: install-lexbor install-jsruntime install-jsruntime-dev
|
.PHONY: install-lexbor install-jsruntime install-jsruntime-dev install-libiconv
|
||||||
|
.PHONY: install-netsurf clean-netsurf test-netsurf
|
||||||
.PHONY: install-dev install
|
.PHONY: install-dev install
|
||||||
|
|
||||||
## Install and build dependencies for release
|
## Install and build dependencies for release
|
||||||
install: install-submodule install-lexbor install-jsruntime
|
install: install-submodule install-lexbor install-jsruntime install-netsurf
|
||||||
|
|
||||||
## Install and build dependencies for dev
|
## Install and build dependencies for dev
|
||||||
install-dev: install-submodule install-lexbor install-jsruntime-dev
|
install-dev: install-submodule install-lexbor install-jsruntime-dev install-netsurf
|
||||||
|
|
||||||
|
BC_NS := $(BC)vendor/netsurf
|
||||||
|
UNAME_S := $(shell uname -s)
|
||||||
|
ICONV := $(BC)vendor/libiconv
|
||||||
|
# TODO: add Linux iconv path (I guess it depends on the distro)
|
||||||
|
# TODO: this way of linking libiconv is not ideal. We should have a more generic way
|
||||||
|
# and stick to a specif version. Maybe build from source. Anyway not now.
|
||||||
|
install-netsurf: install-libiconv
|
||||||
|
@printf "\e[36mInstalling NetSurf...\e[0m\n" && \
|
||||||
|
ls $(ICONV) 1> /dev/null || (printf "\e[33mERROR: you need to install libiconv in your system (on MacOS on with Homebrew)\e[0m\n"; exit 1;) && \
|
||||||
|
export PREFIX=$(BC_NS) && \
|
||||||
|
export OPTLDFLAGS="-L$(ICONV)/lib" && \
|
||||||
|
export OPTCFLAGS="-I$(ICONV)/include" && \
|
||||||
|
printf "\e[33mInstalling libwapcaplet...\e[0m\n" && \
|
||||||
|
cd vendor/netsurf/libwapcaplet && \
|
||||||
|
BUILDDIR=$(BC_NS)/build/libwapcaplet make install && \
|
||||||
|
cd ../libparserutils && \
|
||||||
|
printf "\e[33mInstalling libparserutils...\e[0m\n" && \
|
||||||
|
BUILDDIR=$(BC_NS)/build/libparserutils make install && \
|
||||||
|
cd ../libhubbub && \
|
||||||
|
printf "\e[33mInstalling libhubbub...\e[0m\n" && \
|
||||||
|
BUILDDIR=$(BC_NS)/build/libhubbub make install && \
|
||||||
|
rm src/treebuilder/autogenerated-element-type.c && \
|
||||||
|
cd ../libdom && \
|
||||||
|
printf "\e[33mInstalling libdom...\e[0m\n" && \
|
||||||
|
BUILDDIR=$(BC_NS)/build/libdom make install && \
|
||||||
|
printf "\e[33mRunning libdom example...\e[0m\n" && \
|
||||||
|
cd examples && \
|
||||||
|
zig cc \
|
||||||
|
-I$(ICONV)/include \
|
||||||
|
-I$(BC_NS)/include \
|
||||||
|
-L$(ICONV)/lib \
|
||||||
|
-L$(BC_NS)/lib \
|
||||||
|
-liconv \
|
||||||
|
-ldom \
|
||||||
|
-lhubbub \
|
||||||
|
-lparserutils \
|
||||||
|
-lwapcaplet \
|
||||||
|
-o a.out \
|
||||||
|
dom-structure-dump.c \
|
||||||
|
$(ICONV)/lib/libiconv.a && \
|
||||||
|
./a.out > /dev/null && \
|
||||||
|
rm a.out && \
|
||||||
|
printf "\e[36mDone NetSurf $(OS)\e[0m\n"
|
||||||
|
|
||||||
|
clean-netsurf:
|
||||||
|
@printf "\e[36mCleaning NetSurf build...\e[0m\n" && \
|
||||||
|
cd vendor/netsurf && \
|
||||||
|
rm -R build && \
|
||||||
|
rm -R lib && \
|
||||||
|
rm -R include
|
||||||
|
|
||||||
|
test-netsurf:
|
||||||
|
@printf "\e[36mTesting NetSurf...\e[0m\n" && \
|
||||||
|
export PREFIX=$(BC_NS) && \
|
||||||
|
export LDFLAGS="-L$(ICONV)/lib -L$(BC_NS)/lib" && \
|
||||||
|
export CFLAGS="-I$(ICONV)/include -I$(BC_NS)/include" && \
|
||||||
|
cd vendor/netsurf/libdom && \
|
||||||
|
BUILDDIR=$(BC_NS)/build/libdom make test
|
||||||
|
|
||||||
|
install-libiconv:
|
||||||
|
ifeq ("$(wildcard vendor/libiconv/lib/libiconv.a)","")
|
||||||
|
@mkdir -p vendor/libiconv
|
||||||
|
@cd vendor/libiconv && \
|
||||||
|
curl https://ftp.gnu.org/pub/gnu/libiconv/libiconv-1.17.tar.gz | tar -xvzf -
|
||||||
|
@cd vendor/libiconv/libiconv-1.17 && \
|
||||||
|
./configure --prefix=$(BC)vendor/libiconv --enable-static && \
|
||||||
|
make && make install
|
||||||
|
endif
|
||||||
|
|
||||||
## Install and build v8 engine for dev
|
|
||||||
install-lexbor:
|
install-lexbor:
|
||||||
@mkdir -p vendor/lexbor
|
@mkdir -p vendor/lexbor
|
||||||
@cd vendor/lexbor && \
|
@cd vendor/lexbor && \
|
||||||
|
|||||||
10
README.md
10
README.md
@@ -18,7 +18,8 @@ For Debian/Ubuntu based Linux:
|
|||||||
sudo apt install xz-utils \
|
sudo apt install xz-utils \
|
||||||
python3 ca-certificates git \
|
python3 ca-certificates git \
|
||||||
pkg-config libglib2.0-dev \
|
pkg-config libglib2.0-dev \
|
||||||
cmake
|
gperf libexpat1-dev \
|
||||||
|
cmake clang
|
||||||
```
|
```
|
||||||
|
|
||||||
For MacOS, you only need Python 3 and cmake.
|
For MacOS, you only need Python 3 and cmake.
|
||||||
@@ -35,6 +36,13 @@ directory.
|
|||||||
make install-submodule
|
make install-submodule
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Build netsurf
|
||||||
|
|
||||||
|
The command `make install-netsurf` will build netsurf libs used by browsercore.
|
||||||
|
```
|
||||||
|
make install-netsurf
|
||||||
|
```
|
||||||
|
|
||||||
### Build lexbor
|
### Build lexbor
|
||||||
|
|
||||||
The command `make install-lexbor` will build lexbor lib used by browsercore.
|
The command `make install-lexbor` will build lexbor lib used by browsercore.
|
||||||
|
|||||||
28
build.zig
28
build.zig
@@ -78,6 +78,7 @@ fn common(
|
|||||||
) !void {
|
) !void {
|
||||||
try jsruntime_pkgs.add(step, options);
|
try jsruntime_pkgs.add(step, options);
|
||||||
linkLexbor(step);
|
linkLexbor(step);
|
||||||
|
linkNetSurf(step);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn linkLexbor(step: *std.build.LibExeObjStep) void {
|
fn linkLexbor(step: *std.build.LibExeObjStep) void {
|
||||||
@@ -86,3 +87,30 @@ fn linkLexbor(step: *std.build.LibExeObjStep) void {
|
|||||||
step.addObjectFile(.{ .path = lib_path });
|
step.addObjectFile(.{ .path = lib_path });
|
||||||
step.addIncludePath(.{ .path = "vendor/lexbor-src/source" });
|
step.addIncludePath(.{ .path = "vendor/lexbor-src/source" });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn linkNetSurf(step: *std.build.LibExeObjStep) void {
|
||||||
|
|
||||||
|
// iconv
|
||||||
|
step.addObjectFile(.{ .path = "vendor/libiconv/lib/libiconv.a" });
|
||||||
|
step.addIncludePath(.{ .path = "vendor/libiconv/include" });
|
||||||
|
|
||||||
|
// netsurf libs
|
||||||
|
const ns = "vendor/netsurf/";
|
||||||
|
const libs: [4][]const u8 = .{
|
||||||
|
"libdom",
|
||||||
|
"libhubbub",
|
||||||
|
"libparserutils",
|
||||||
|
"libwapcaplet",
|
||||||
|
};
|
||||||
|
inline for (libs) |lib| {
|
||||||
|
step.addObjectFile(.{ .path = ns ++ "/lib/" ++ lib ++ ".a" });
|
||||||
|
step.addIncludePath(.{ .path = ns ++ lib ++ "/src" });
|
||||||
|
}
|
||||||
|
step.addIncludePath(.{ .path = ns ++ "/include" });
|
||||||
|
|
||||||
|
// wrapper
|
||||||
|
const flags = [_][]const u8{};
|
||||||
|
const files: [1][]const u8 = .{ns ++ "wrapper/wrapper.c"};
|
||||||
|
step.addCSourceFiles(&files, &flags);
|
||||||
|
step.addIncludePath(.{ .path = ns ++ "wrapper" });
|
||||||
|
}
|
||||||
|
|||||||
14
src/dom.zig
14
src/dom.zig
@@ -4,12 +4,13 @@ const Console = @import("jsruntime").Console;
|
|||||||
|
|
||||||
// DOM
|
// DOM
|
||||||
const EventTarget = @import("dom/event_target.zig").EventTarget;
|
const EventTarget = @import("dom/event_target.zig").EventTarget;
|
||||||
const Node = @import("dom/node.zig").Node;
|
const N = @import("dom/node.zig");
|
||||||
const Element = @import("dom/element.zig").Element;
|
const Element = @import("dom/element.zig").Element;
|
||||||
const Document = @import("dom/document.zig").Document;
|
const Document = @import("dom/document.zig").Document;
|
||||||
|
|
||||||
// HTML
|
// HTML
|
||||||
pub const HTMLDocument = @import("html/document.zig").HTMLDocument;
|
pub const HTMLDocument = @import("html/document.zig").HTMLDocument;
|
||||||
|
const HTMLElem = @import("html/elements.zig");
|
||||||
|
|
||||||
const E = @import("html/elements.zig");
|
const E = @import("html/elements.zig");
|
||||||
|
|
||||||
@@ -19,14 +20,15 @@ const interfaces = .{
|
|||||||
|
|
||||||
// DOM
|
// DOM
|
||||||
EventTarget,
|
EventTarget,
|
||||||
Node,
|
N.Node,
|
||||||
|
N.Types,
|
||||||
Element,
|
Element,
|
||||||
Document,
|
Document,
|
||||||
|
|
||||||
// HTML
|
// HTML
|
||||||
HTMLDocument,
|
HTMLDocument,
|
||||||
E.HTMLElement,
|
HTMLElem.HTMLElement,
|
||||||
E.HTMLMediaElement,
|
HTMLElem.HTMLMediaElement,
|
||||||
E.HTMLElementsTypes,
|
HTMLElem.Types,
|
||||||
};
|
};
|
||||||
pub const Interfaces = generate.TupleInst(generate.TupleT(interfaces), interfaces);
|
pub const Interfaces = generate.Tuple(interfaces);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
|
||||||
const parser = @import("../parser.zig");
|
const parser = @import("../netsurf.zig");
|
||||||
|
|
||||||
const Node = @import("node.zig").Node;
|
const Node = @import("node.zig").Node;
|
||||||
const Element = @import("element.zig").Element;
|
const Element = @import("element.zig").Element;
|
||||||
@@ -15,19 +15,8 @@ pub const Document = struct {
|
|||||||
// return .{};
|
// return .{};
|
||||||
// }
|
// }
|
||||||
|
|
||||||
pub fn getElementById(self: *parser.Document, elem: *parser.Element, id: []const u8) ?*parser.Element {
|
pub fn getElementById(self: *parser.Document, id: []const u8) ?*parser.Element {
|
||||||
const collection = parser.collectionInit(self, 1);
|
return parser.documentGetElementById(self, id);
|
||||||
defer parser.collectionDeinit(collection);
|
|
||||||
const case_sensitve = true;
|
|
||||||
parser.elementsByAttr(elem, collection, "id", id, case_sensitve) catch |err| {
|
|
||||||
std.debug.print("getElementById error: {s}\n", .{@errorName(err)});
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
if (collection.array.length == 0) {
|
|
||||||
// no results
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return parser.collectionElement(collection, 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// JS funcs
|
// JS funcs
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
|
||||||
const parser = @import("../parser.zig");
|
const parser = @import("../netsurf.zig");
|
||||||
|
|
||||||
const Node = @import("node.zig").Node;
|
const Node = @import("node.zig").Node;
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
const parser = @import("../parser.zig");
|
const parser = @import("../netsurf.zig");
|
||||||
|
|
||||||
pub const EventTarget = struct {
|
pub const EventTarget = struct {
|
||||||
pub const Self = parser.EventTarget;
|
pub const Self = parser.EventTarget;
|
||||||
|
|||||||
@@ -1,28 +1,23 @@
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
|
||||||
const parser = @import("../parser.zig");
|
const generate = @import("../generate.zig");
|
||||||
|
|
||||||
|
const parser = @import("../netsurf.zig");
|
||||||
|
|
||||||
const EventTarget = @import("event_target.zig").EventTarget;
|
const EventTarget = @import("event_target.zig").EventTarget;
|
||||||
|
const HTMLDocument = @import("../html/document.zig").HTMLDocument;
|
||||||
pub fn create_tree(node: ?*parser.Node, _: ?*anyopaque) callconv(.C) parser.Action {
|
const HTMLElem = @import("../html/elements.zig");
|
||||||
if (node == null) {
|
|
||||||
return parser.ActionStop;
|
|
||||||
}
|
|
||||||
const node_type = parser.nodeType(node.?);
|
|
||||||
const node_name = parser.nodeName(node.?);
|
|
||||||
std.debug.print("type: {any}, name: {s}\n", .{ node_type, node_name });
|
|
||||||
if (node_type == parser.NodeType.element) {
|
|
||||||
std.debug.print("yes\n", .{});
|
|
||||||
}
|
|
||||||
return parser.ActionOk;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const Node = struct {
|
pub const Node = struct {
|
||||||
pub const Self = parser.Node;
|
pub const Self = parser.Node;
|
||||||
pub const prototype = *EventTarget;
|
pub const prototype = *EventTarget;
|
||||||
pub const mem_guarantied = true;
|
pub const mem_guarantied = true;
|
||||||
|
|
||||||
pub fn make_tree(self: *parser.Node) !void {
|
|
||||||
try parser.nodeWalk(self, create_tree);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub const Types = generate.Tuple(.{
|
||||||
|
HTMLElem.Types,
|
||||||
|
HTMLDocument,
|
||||||
|
});
|
||||||
|
const Generated = generate.Union.compile(Types);
|
||||||
|
pub const Union = Generated._union;
|
||||||
|
pub const Tags = Generated._enum;
|
||||||
|
|||||||
189
src/generate.zig
189
src/generate.zig
@@ -1,13 +1,33 @@
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const builtin = @import("builtin");
|
const builtin = @import("builtin");
|
||||||
|
|
||||||
|
// Utils
|
||||||
|
// -----
|
||||||
|
|
||||||
|
fn itoa(comptime i: u8) ![]const u8 {
|
||||||
|
var len: usize = undefined;
|
||||||
|
if (i < 10) {
|
||||||
|
len = 1;
|
||||||
|
} else if (i < 100) {
|
||||||
|
len = 2;
|
||||||
|
} else {
|
||||||
|
return error.GenerateTooMuchMembers;
|
||||||
|
}
|
||||||
|
var buf: [len]u8 = undefined;
|
||||||
|
return try std.fmt.bufPrint(buf[0..], "{d}", .{i});
|
||||||
|
}
|
||||||
|
|
||||||
fn fmtName(comptime T: type) []const u8 {
|
fn fmtName(comptime T: type) []const u8 {
|
||||||
var it = std.mem.splitBackwards(u8, @typeName(T), ".");
|
var it = std.mem.splitBackwards(u8, @typeName(T), ".");
|
||||||
return it.first();
|
return it.first();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Union
|
||||||
|
// -----
|
||||||
|
|
||||||
// Generate a flatten tagged Union from various structs and union of structs
|
// Generate a flatten tagged Union from various structs and union of structs
|
||||||
// TODO: make this function more generic
|
// TODO: make this function more generic
|
||||||
|
// TODO: dedup
|
||||||
pub const Union = struct {
|
pub const Union = struct {
|
||||||
_enum: type,
|
_enum: type,
|
||||||
_union: type,
|
_union: type,
|
||||||
@@ -152,38 +172,15 @@ pub const Union = struct {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
fn itoa(comptime i: u8) ![]u8 {
|
// Tuple
|
||||||
var len: usize = undefined;
|
// -----
|
||||||
if (i < 10) {
|
|
||||||
len = 1;
|
|
||||||
} else if (i < 100) {
|
|
||||||
len = 2;
|
|
||||||
} else {
|
|
||||||
return error.GenerateTooMuchMembers;
|
|
||||||
}
|
|
||||||
var buf: [len]u8 = undefined;
|
|
||||||
return try std.fmt.bufPrint(buf[0..], "{d}", .{i});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate a flatten tuple type from various structs and tuple of structs.
|
fn tupleNb(comptime tuple: anytype) usize {
|
||||||
// TODO: make this function more generic
|
var nb = 0;
|
||||||
pub fn TupleT(comptime tuple: anytype) type {
|
for (@typeInfo(@TypeOf(tuple)).Struct.fields) |member| {
|
||||||
|
|
||||||
// check types provided
|
|
||||||
const tuple_T = @TypeOf(tuple);
|
|
||||||
const tuple_info = @typeInfo(tuple_T);
|
|
||||||
if (tuple_info != .Struct or !tuple_info.Struct.is_tuple) {
|
|
||||||
@compileError("GenerateArgNotTuple");
|
|
||||||
}
|
|
||||||
|
|
||||||
const tuple_members = tuple_info.Struct.fields;
|
|
||||||
|
|
||||||
// first iteration to get the total number of members
|
|
||||||
var members_nb = 0;
|
|
||||||
for (tuple_members) |member| {
|
|
||||||
const member_T = @field(tuple, member.name);
|
const member_T = @field(tuple, member.name);
|
||||||
if (@TypeOf(member_T) == type) {
|
if (@TypeOf(member_T) == type) {
|
||||||
members_nb += 1;
|
nb += 1;
|
||||||
} else {
|
} else {
|
||||||
const member_info = @typeInfo(@TypeOf(member_T));
|
const member_info = @typeInfo(@TypeOf(member_T));
|
||||||
if (member_info != .Struct and !member_info.Struct.is_tuple) {
|
if (member_info != .Struct and !member_info.Struct.is_tuple) {
|
||||||
@@ -194,14 +191,82 @@ pub fn TupleT(comptime tuple: anytype) type {
|
|||||||
@compileError("GenerateMemberTupleChildNotType");
|
@compileError("GenerateMemberTupleChildNotType");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
members_nb += member_info.Struct.fields.len;
|
nb += member_info.Struct.fields.len;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return nb;
|
||||||
|
}
|
||||||
|
|
||||||
// second iteration to generate the tuple type
|
fn tupleTypes(comptime nb: usize, comptime tuple: anytype) [nb]type {
|
||||||
var fields: [members_nb]std.builtin.Type.StructField = undefined;
|
var types: [nb]type = undefined;
|
||||||
var done = 0;
|
var done = 0;
|
||||||
while (done < members_nb) {
|
for (@typeInfo(@TypeOf(tuple)).Struct.fields) |member| {
|
||||||
|
const T = @field(tuple, member.name);
|
||||||
|
if (@TypeOf(T) == type) {
|
||||||
|
types[done] = T;
|
||||||
|
done += 1;
|
||||||
|
} else {
|
||||||
|
const info = @typeInfo(@TypeOf(T));
|
||||||
|
for (info.Struct.fields) |field| {
|
||||||
|
types[done] = @field(T, field.name);
|
||||||
|
done += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return types;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn isDup(comptime nb: usize, comptime list: [nb]type, comptime T: type, comptime i: usize) bool {
|
||||||
|
for (list, 0..) |item, index| {
|
||||||
|
if (i >= index) {
|
||||||
|
// check sequentially
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (T == item) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dedupIndexes(comptime nb: usize, comptime types: [nb]type) [nb]i32 {
|
||||||
|
var dedup_indexes: [nb]i32 = undefined;
|
||||||
|
for (types, 0..) |T, i| {
|
||||||
|
if (isDup(nb, types, T, i)) {
|
||||||
|
dedup_indexes[i] = -1;
|
||||||
|
} else {
|
||||||
|
dedup_indexes[i] = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dedup_indexes;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dedupNb(comptime nb: usize, comptime dedup_indexes: [nb]i32) usize {
|
||||||
|
var dedup_nb = 0;
|
||||||
|
for (dedup_indexes) |index| {
|
||||||
|
if (index != -1) {
|
||||||
|
dedup_nb += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dedup_nb;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn TupleT(comptime tuple: anytype) type {
|
||||||
|
@setEvalBranchQuota(100000);
|
||||||
|
|
||||||
|
// logic
|
||||||
|
const nb = tupleNb(tuple);
|
||||||
|
const types = tupleTypes(nb, tuple);
|
||||||
|
const dedup_indexes = dedupIndexes(nb, types);
|
||||||
|
const dedup_nb = dedupNb(nb, dedup_indexes);
|
||||||
|
|
||||||
|
// generate the tuple type
|
||||||
|
var fields: [dedup_nb]std.builtin.Type.StructField = undefined;
|
||||||
|
var done = 0;
|
||||||
|
for (dedup_indexes) |index| {
|
||||||
|
if (index == -1) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
fields[done] = .{
|
fields[done] = .{
|
||||||
.name = try itoa(done),
|
.name = try itoa(done),
|
||||||
.type = type,
|
.type = type,
|
||||||
@@ -221,39 +286,36 @@ pub fn TupleT(comptime tuple: anytype) type {
|
|||||||
return @Type(std.builtin.Type{ .Struct = info });
|
return @Type(std.builtin.Type{ .Struct = info });
|
||||||
}
|
}
|
||||||
|
|
||||||
// Instantiate a flatten tuple from various structs and tuple of structs
|
// Create a flatten tuple from various structs and tuple of structs
|
||||||
// You need to call first TupleT to generate the according type
|
// Duplicates will be removed.
|
||||||
// TODO: make this function more generic
|
// TODO: make this function more generic
|
||||||
pub fn TupleInst(comptime T: type, comptime tuple: anytype) T {
|
pub fn Tuple(comptime tuple: anytype) TupleT(tuple) {
|
||||||
|
|
||||||
// check types provided
|
// check types provided
|
||||||
const tuple_T = @TypeOf(tuple);
|
const tuple_T = @TypeOf(tuple);
|
||||||
const tuple_info = @typeInfo(tuple_T);
|
const tuple_info = @typeInfo(tuple_T);
|
||||||
const tuple_members = tuple_info.Struct.fields;
|
if (tuple_info != .Struct or !tuple_info.Struct.is_tuple) {
|
||||||
|
@compileError("GenerateArgNotTuple");
|
||||||
|
}
|
||||||
|
|
||||||
|
// generate the type
|
||||||
|
const T = TupleT(tuple);
|
||||||
|
|
||||||
|
// logic
|
||||||
|
const nb = tupleNb(tuple);
|
||||||
|
const types = tupleTypes(nb, tuple);
|
||||||
|
const dedup_indexes = dedupIndexes(nb, types);
|
||||||
|
|
||||||
// instantiate the tuple
|
// instantiate the tuple
|
||||||
var t: T = undefined;
|
var t: T = undefined;
|
||||||
var done = 0;
|
var done = 0;
|
||||||
for (tuple_members) |member| {
|
for (dedup_indexes) |index| {
|
||||||
const member_T = @field(tuple, member.name);
|
if (index == -1) {
|
||||||
var member_info: std.builtin.Type = undefined;
|
continue;
|
||||||
if (@TypeOf(member_T) == type) {
|
|
||||||
member_info = @typeInfo(member_T);
|
|
||||||
} else {
|
|
||||||
member_info = @typeInfo(@TypeOf(member_T));
|
|
||||||
}
|
|
||||||
var member_detail = member_info.Struct;
|
|
||||||
if (member_detail.is_tuple) {
|
|
||||||
for (member_detail.fields) |field| {
|
|
||||||
const name = try itoa(done);
|
|
||||||
@field(t, name) = @field(member_T, field.name);
|
|
||||||
done += 1;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
const name = try itoa(done);
|
|
||||||
@field(t, name) = @field(tuple, member.name);
|
|
||||||
done += 1;
|
|
||||||
}
|
}
|
||||||
|
const name = try itoa(done);
|
||||||
|
@field(t, name) = types[index];
|
||||||
|
done += 1;
|
||||||
}
|
}
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
@@ -322,7 +384,7 @@ pub fn tests() !void {
|
|||||||
|
|
||||||
// Tuple from structs
|
// Tuple from structs
|
||||||
const tuple_structs = .{ Astruct, Bstruct };
|
const tuple_structs = .{ Astruct, Bstruct };
|
||||||
const tFromStructs = TupleInst(TupleT(tuple_structs), tuple_structs);
|
const tFromStructs = Tuple(tuple_structs);
|
||||||
const t_from_structs = @typeInfo(@TypeOf(tFromStructs));
|
const t_from_structs = @typeInfo(@TypeOf(tFromStructs));
|
||||||
try std.testing.expect(t_from_structs == .Struct);
|
try std.testing.expect(t_from_structs == .Struct);
|
||||||
try std.testing.expect(t_from_structs.Struct.is_tuple);
|
try std.testing.expect(t_from_structs.Struct.is_tuple);
|
||||||
@@ -332,7 +394,7 @@ pub fn tests() !void {
|
|||||||
|
|
||||||
// Tuple from tuple and structs
|
// Tuple from tuple and structs
|
||||||
const tuple_mix = .{ tFromStructs, Cstruct };
|
const tuple_mix = .{ tFromStructs, Cstruct };
|
||||||
const tFromMix = TupleInst(TupleT(tuple_mix), tuple_mix);
|
const tFromMix = Tuple(tuple_mix);
|
||||||
const t_from_mix = @typeInfo(@TypeOf(tFromMix));
|
const t_from_mix = @typeInfo(@TypeOf(tFromMix));
|
||||||
try std.testing.expect(t_from_mix == .Struct);
|
try std.testing.expect(t_from_mix == .Struct);
|
||||||
try std.testing.expect(t_from_mix.Struct.is_tuple);
|
try std.testing.expect(t_from_mix.Struct.is_tuple);
|
||||||
@@ -341,5 +403,16 @@ pub fn tests() !void {
|
|||||||
try std.testing.expect(@field(tFromMix, "1") == Bstruct);
|
try std.testing.expect(@field(tFromMix, "1") == Bstruct);
|
||||||
try std.testing.expect(@field(tFromMix, "2") == Cstruct);
|
try std.testing.expect(@field(tFromMix, "2") == Cstruct);
|
||||||
|
|
||||||
|
// Tuple with dedup
|
||||||
|
const tuple_dedup = .{ Cstruct, Astruct, tFromStructs, Bstruct };
|
||||||
|
const tFromDedup = Tuple(tuple_dedup);
|
||||||
|
const t_from_dedup = @typeInfo(@TypeOf(tFromDedup));
|
||||||
|
try std.testing.expect(t_from_dedup == .Struct);
|
||||||
|
try std.testing.expect(t_from_dedup.Struct.is_tuple);
|
||||||
|
try std.testing.expect(t_from_dedup.Struct.fields.len == 3);
|
||||||
|
try std.testing.expect(@field(tFromDedup, "0") == Cstruct);
|
||||||
|
try std.testing.expect(@field(tFromDedup, "1") == Astruct);
|
||||||
|
try std.testing.expect(@field(tFromDedup, "2") == Bstruct);
|
||||||
|
|
||||||
std.debug.print("Generate Tuple: OK\n", .{});
|
std.debug.print("Generate Tuple: OK\n", .{});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,13 @@
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
|
||||||
const parser = @import("../parser.zig");
|
const parser = @import("../netsurf.zig");
|
||||||
|
|
||||||
const jsruntime = @import("jsruntime");
|
const jsruntime = @import("jsruntime");
|
||||||
const Case = jsruntime.test_utils.Case;
|
const Case = jsruntime.test_utils.Case;
|
||||||
const checkCases = jsruntime.test_utils.checkCases;
|
const checkCases = jsruntime.test_utils.checkCases;
|
||||||
|
|
||||||
const Document = @import("../dom/document.zig").Document;
|
const Document = @import("../dom/document.zig").Document;
|
||||||
|
const HTMLElem = @import("elements.zig");
|
||||||
const E = @import("elements.zig");
|
|
||||||
|
|
||||||
pub const HTMLDocument = struct {
|
pub const HTMLDocument = struct {
|
||||||
pub const Self = parser.DocumentHTML;
|
pub const Self = parser.DocumentHTML;
|
||||||
@@ -22,18 +21,19 @@ pub const HTMLDocument = struct {
|
|||||||
return parser.documentHTMLBody(self);
|
return parser.documentHTMLBody(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn _getElementById(self: *parser.DocumentHTML, id: []u8) ?*parser.HTMLElement {
|
pub fn _getElementById(self: *parser.DocumentHTML, id: []u8) ?HTMLElem.Union {
|
||||||
const body_html = parser.documentHTMLBody(self);
|
const doc = parser.documentHTMLToDocument(self);
|
||||||
const body_dom = @as(*parser.Element, @ptrCast(body_html));
|
const elem_dom = parser.documentGetElementById(doc, id);
|
||||||
const doc_dom = @as(*parser.Document, @ptrCast(self));
|
if (elem_dom) |elem| {
|
||||||
const elem_dom = Document.getElementById(doc_dom, body_dom, id);
|
return HTMLElem.toInterface(HTMLElem.Union, elem);
|
||||||
return @as(*parser.HTMLElement, @ptrCast(elem_dom));
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn _createElement(self: *parser.DocumentHTML, tag_name: []const u8) E.HTMLElements {
|
pub fn _createElement(self: *parser.DocumentHTML, tag_name: []const u8) HTMLElem.Union {
|
||||||
const doc_dom = parser.documentHTMLToDocument(self);
|
const doc_dom = parser.documentHTMLToDocument(self);
|
||||||
const base = parser.documentCreateElement(doc_dom, tag_name);
|
const base = parser.documentCreateElement(doc_dom, tag_name);
|
||||||
return E.ElementToHTMLElementInterface(base);
|
return HTMLElem.toInterface(HTMLElem.Union, base);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -50,13 +50,14 @@ pub fn testExecFn(
|
|||||||
.{ .src = "document.__proto__.__proto__.constructor.name", .ex = "Document" },
|
.{ .src = "document.__proto__.__proto__.constructor.name", .ex = "Document" },
|
||||||
.{ .src = "document.__proto__.__proto__.__proto__.constructor.name", .ex = "Node" },
|
.{ .src = "document.__proto__.__proto__.__proto__.constructor.name", .ex = "Node" },
|
||||||
.{ .src = "document.__proto__.__proto__.__proto__.__proto__.constructor.name", .ex = "EventTarget" },
|
.{ .src = "document.__proto__.__proto__.__proto__.__proto__.constructor.name", .ex = "EventTarget" },
|
||||||
|
.{ .src = "document.body.localName === 'body'", .ex = "true" },
|
||||||
};
|
};
|
||||||
try checkCases(js_env, &constructor);
|
try checkCases(js_env, &constructor);
|
||||||
|
|
||||||
var getElementById = [_]Case{
|
var getElementById = [_]Case{
|
||||||
.{ .src = "let getElementById = document.getElementById('content')", .ex = "undefined" },
|
.{ .src = "let getElementById = document.getElementById('content')", .ex = "undefined" },
|
||||||
.{ .src = "getElementById.constructor.name", .ex = "HTMLElement" },
|
.{ .src = "getElementById.constructor.name", .ex = "HTMLDivElement" },
|
||||||
.{ .src = "getElementById.localName", .ex = "main" },
|
.{ .src = "getElementById.localName", .ex = "div" },
|
||||||
};
|
};
|
||||||
try checkCases(js_env, &getElementById);
|
try checkCases(js_env, &getElementById);
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
const parser = @import("../parser.zig");
|
const parser = @import("../netsurf.zig");
|
||||||
const generate = @import("../generate.zig");
|
const generate = @import("../generate.zig");
|
||||||
|
|
||||||
const Element = @import("../dom/element.zig").Element;
|
const Element = @import("../dom/element.zig").Element;
|
||||||
@@ -7,12 +7,12 @@ const Element = @import("../dom/element.zig").Element;
|
|||||||
// --------------
|
// --------------
|
||||||
|
|
||||||
pub const HTMLElement = struct {
|
pub const HTMLElement = struct {
|
||||||
pub const Self = parser.HTMLElement;
|
pub const Self = parser.ElementHTML;
|
||||||
pub const prototype = *Element;
|
pub const prototype = *Element;
|
||||||
pub const mem_guarantied = true;
|
pub const mem_guarantied = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const HTMLElementsTypes = .{
|
pub const Types = .{
|
||||||
HTMLUnknownElement,
|
HTMLUnknownElement,
|
||||||
HTMLAnchorElement,
|
HTMLAnchorElement,
|
||||||
HTMLAreaElement,
|
HTMLAreaElement,
|
||||||
@@ -74,9 +74,9 @@ pub const HTMLElementsTypes = .{
|
|||||||
HTMLUListElement,
|
HTMLUListElement,
|
||||||
HTMLVideoElement,
|
HTMLVideoElement,
|
||||||
};
|
};
|
||||||
const HTMLElementsGenerated = generate.Union.compile(HTMLElementsTypes);
|
const Generated = generate.Union.compile(Types);
|
||||||
pub const HTMLElements = HTMLElementsGenerated._union;
|
pub const Union = Generated._union;
|
||||||
pub const HTMLElementsTags = HTMLElementsGenerated._enum;
|
pub const Tags = Generated._enum;
|
||||||
|
|
||||||
// Deprecated HTMLElements in Chrome (2023/03/15)
|
// Deprecated HTMLElements in Chrome (2023/03/15)
|
||||||
// HTMLContentelement
|
// HTMLContentelement
|
||||||
@@ -454,8 +454,8 @@ pub const HTMLVideoElement = struct {
|
|||||||
pub const mem_guarantied = true;
|
pub const mem_guarantied = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn ElementToHTMLElementInterface(elem: *parser.Element) HTMLElements {
|
pub fn toInterface(comptime T: type, elem: *parser.Element) T {
|
||||||
const tag = parser.nodeTag(parser.elementNode(elem));
|
const tag = parser.elementHTMLGetTagType(@as(*parser.ElementHTML, @ptrCast(elem)));
|
||||||
return switch (tag) {
|
return switch (tag) {
|
||||||
.a => .{ .HTMLAnchorElement = @as(*parser.Anchor, @ptrCast(elem)) },
|
.a => .{ .HTMLAnchorElement = @as(*parser.Anchor, @ptrCast(elem)) },
|
||||||
.area => .{ .HTMLAreaElement = @as(*parser.Area, @ptrCast(elem)) },
|
.area => .{ .HTMLAreaElement = @as(*parser.Area, @ptrCast(elem)) },
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ const std = @import("std");
|
|||||||
|
|
||||||
const jsruntime = @import("jsruntime");
|
const jsruntime = @import("jsruntime");
|
||||||
|
|
||||||
const parser = @import("parser.zig");
|
const parser = @import("netsurf.zig");
|
||||||
const DOM = @import("dom.zig");
|
const DOM = @import("dom.zig");
|
||||||
|
|
||||||
const html_test = @import("html_test.zig").html;
|
const html_test = @import("html_test.zig").html;
|
||||||
@@ -55,9 +55,6 @@ pub fn main() !void {
|
|||||||
defer vm.deinit();
|
defer vm.deinit();
|
||||||
|
|
||||||
// document
|
// document
|
||||||
doc = parser.documentHTMLInit();
|
|
||||||
defer parser.documentHTMLDeinit(doc);
|
|
||||||
try parser.documentHTMLParse(doc, html_test);
|
|
||||||
|
|
||||||
// remove socket file of internal server
|
// remove socket file of internal server
|
||||||
// reuse_address (SO_REUSEADDR flag) does not seems to work on unix socket
|
// reuse_address (SO_REUSEADDR flag) does not seems to work on unix socket
|
||||||
@@ -68,6 +65,9 @@ pub fn main() !void {
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
var f = "test.html".*;
|
||||||
|
doc = parser.documentHTMLParse(&f);
|
||||||
|
// TODO: defer doc?
|
||||||
|
|
||||||
// alloc
|
// alloc
|
||||||
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
|
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ const std = @import("std");
|
|||||||
|
|
||||||
const jsruntime = @import("jsruntime");
|
const jsruntime = @import("jsruntime");
|
||||||
|
|
||||||
const parser = @import("parser.zig");
|
const parser = @import("netsurf.zig");
|
||||||
const DOM = @import("dom.zig");
|
const DOM = @import("dom.zig");
|
||||||
|
|
||||||
const html_test = @import("html_test.zig").html;
|
const html_test = @import("html_test.zig").html;
|
||||||
@@ -32,9 +32,9 @@ pub fn main() !void {
|
|||||||
const apis = jsruntime.compile(DOM.Interfaces);
|
const apis = jsruntime.compile(DOM.Interfaces);
|
||||||
|
|
||||||
// document
|
// document
|
||||||
doc = parser.documentHTMLInit();
|
var f = "test.html".*;
|
||||||
defer parser.documentHTMLDeinit(doc);
|
doc = parser.documentHTMLParse(&f);
|
||||||
try parser.documentHTMLParse(doc, html_test);
|
// TODO: defer doc?
|
||||||
|
|
||||||
// create JS vm
|
// create JS vm
|
||||||
const vm = jsruntime.VM.init();
|
const vm = jsruntime.VM.init();
|
||||||
|
|||||||
349
src/netsurf.zig
Normal file
349
src/netsurf.zig
Normal file
@@ -0,0 +1,349 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
const cp = @cImport({
|
||||||
|
@cInclude("wrapper.h");
|
||||||
|
});
|
||||||
|
|
||||||
|
const c = @cImport({
|
||||||
|
@cInclude("core/node.h");
|
||||||
|
@cInclude("core/document.h");
|
||||||
|
@cInclude("core/element.h");
|
||||||
|
|
||||||
|
@cInclude("html/html_document.h");
|
||||||
|
@cInclude("html/html_element.h");
|
||||||
|
@cInclude("html/html_anchor_element.h");
|
||||||
|
@cInclude("html/html_area_element.h");
|
||||||
|
@cInclude("html/html_br_element.h");
|
||||||
|
@cInclude("html/html_base_element.h");
|
||||||
|
@cInclude("html/html_body_element.h");
|
||||||
|
@cInclude("html/html_button_element.h");
|
||||||
|
@cInclude("html/html_canvas_element.h");
|
||||||
|
@cInclude("html/html_dlist_element.h");
|
||||||
|
@cInclude("html/html_div_element.h");
|
||||||
|
@cInclude("html/html_fieldset_element.h");
|
||||||
|
@cInclude("html/html_form_element.h");
|
||||||
|
@cInclude("html/html_frameset_element.h");
|
||||||
|
@cInclude("html/html_hr_element.h");
|
||||||
|
@cInclude("html/html_head_element.h");
|
||||||
|
@cInclude("html/html_heading_element.h");
|
||||||
|
@cInclude("html/html_html_element.h");
|
||||||
|
@cInclude("html/html_iframe_element.h");
|
||||||
|
@cInclude("html/html_image_element.h");
|
||||||
|
@cInclude("html/html_input_element.h");
|
||||||
|
@cInclude("html/html_li_element.h");
|
||||||
|
@cInclude("html/html_label_element.h");
|
||||||
|
@cInclude("html/html_legend_element.h");
|
||||||
|
@cInclude("html/html_link_element.h");
|
||||||
|
@cInclude("html/html_map_element.h");
|
||||||
|
@cInclude("html/html_meta_element.h");
|
||||||
|
@cInclude("html/html_mod_element.h");
|
||||||
|
@cInclude("html/html_olist_element.h");
|
||||||
|
@cInclude("html/html_object_element.h");
|
||||||
|
@cInclude("html/html_opt_group_element.h");
|
||||||
|
@cInclude("html/html_option_element.h");
|
||||||
|
@cInclude("html/html_paragraph_element.h");
|
||||||
|
@cInclude("html/html_pre_element.h");
|
||||||
|
@cInclude("html/html_quote_element.h");
|
||||||
|
@cInclude("html/html_script_element.h");
|
||||||
|
@cInclude("html/html_select_element.h");
|
||||||
|
@cInclude("html/html_style_element.h");
|
||||||
|
@cInclude("html/html_table_element.h");
|
||||||
|
@cInclude("html/html_tablecaption_element.h");
|
||||||
|
@cInclude("html/html_tablecell_element.h");
|
||||||
|
@cInclude("html/html_tablecol_element.h");
|
||||||
|
@cInclude("html/html_tablerow_element.h");
|
||||||
|
@cInclude("html/html_tablesection_element.h");
|
||||||
|
@cInclude("html/html_text_area_element.h");
|
||||||
|
@cInclude("html/html_title_element.h");
|
||||||
|
@cInclude("html/html_ulist_element.h");
|
||||||
|
});
|
||||||
|
|
||||||
|
// Utils
|
||||||
|
const String = c.dom_string;
|
||||||
|
|
||||||
|
inline fn stringToData(s: *String) []const u8 {
|
||||||
|
const data = c.dom_string_data(s);
|
||||||
|
return data[0..c.dom_string_byte_length(s)];
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fn stringFromData(data: []const u8) *String {
|
||||||
|
var s: ?*String = null;
|
||||||
|
_ = c.dom_string_create(data.ptr, data.len, &s);
|
||||||
|
return s.?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tag
|
||||||
|
|
||||||
|
pub const Tag = enum(u8) {
|
||||||
|
a = c.DOM_HTML_ELEMENT_TYPE_A,
|
||||||
|
area = c.DOM_HTML_ELEMENT_TYPE_AREA,
|
||||||
|
audio = c.DOM_HTML_ELEMENT_TYPE_AUDIO,
|
||||||
|
br = c.DOM_HTML_ELEMENT_TYPE_BR,
|
||||||
|
base = c.DOM_HTML_ELEMENT_TYPE_BASE,
|
||||||
|
body = c.DOM_HTML_ELEMENT_TYPE_BODY,
|
||||||
|
button = c.DOM_HTML_ELEMENT_TYPE_BUTTON,
|
||||||
|
canvas = c.DOM_HTML_ELEMENT_TYPE_CANVAS,
|
||||||
|
dl = c.DOM_HTML_ELEMENT_TYPE_DL,
|
||||||
|
dialog = c.DOM_HTML_ELEMENT_TYPE_DIALOG,
|
||||||
|
data = c.DOM_HTML_ELEMENT_TYPE_DATA,
|
||||||
|
div = c.DOM_HTML_ELEMENT_TYPE_DIV,
|
||||||
|
embed = c.DOM_HTML_ELEMENT_TYPE_EMBED,
|
||||||
|
fieldset = c.DOM_HTML_ELEMENT_TYPE_FIELDSET,
|
||||||
|
form = c.DOM_HTML_ELEMENT_TYPE_FORM,
|
||||||
|
frameset = c.DOM_HTML_ELEMENT_TYPE_FRAMESET,
|
||||||
|
hr = c.DOM_HTML_ELEMENT_TYPE_HR,
|
||||||
|
head = c.DOM_HTML_ELEMENT_TYPE_HEAD,
|
||||||
|
h1 = c.DOM_HTML_ELEMENT_TYPE_H1,
|
||||||
|
h2 = c.DOM_HTML_ELEMENT_TYPE_H2,
|
||||||
|
h3 = c.DOM_HTML_ELEMENT_TYPE_H3,
|
||||||
|
h4 = c.DOM_HTML_ELEMENT_TYPE_H4,
|
||||||
|
h5 = c.DOM_HTML_ELEMENT_TYPE_H5,
|
||||||
|
h6 = c.DOM_HTML_ELEMENT_TYPE_H6,
|
||||||
|
html = c.DOM_HTML_ELEMENT_TYPE_HTML,
|
||||||
|
iframe = c.DOM_HTML_ELEMENT_TYPE_IFRAME,
|
||||||
|
img = c.DOM_HTML_ELEMENT_TYPE_IMG,
|
||||||
|
input = c.DOM_HTML_ELEMENT_TYPE_INPUT,
|
||||||
|
li = c.DOM_HTML_ELEMENT_TYPE_LI,
|
||||||
|
label = c.DOM_HTML_ELEMENT_TYPE_LABEL,
|
||||||
|
legend = c.DOM_HTML_ELEMENT_TYPE_LEGEND,
|
||||||
|
link = c.DOM_HTML_ELEMENT_TYPE_LINK,
|
||||||
|
map = c.DOM_HTML_ELEMENT_TYPE_MAP,
|
||||||
|
meta = c.DOM_HTML_ELEMENT_TYPE_META,
|
||||||
|
meter = c.DOM_HTML_ELEMENT_TYPE_METER,
|
||||||
|
ins = c.DOM_HTML_ELEMENT_TYPE_INS,
|
||||||
|
del = c.DOM_HTML_ELEMENT_TYPE_DEL,
|
||||||
|
ol = c.DOM_HTML_ELEMENT_TYPE_OL,
|
||||||
|
object = c.DOM_HTML_ELEMENT_TYPE_OBJECT,
|
||||||
|
optgroup = c.DOM_HTML_ELEMENT_TYPE_OPTGROUP,
|
||||||
|
option = c.DOM_HTML_ELEMENT_TYPE_OPTION,
|
||||||
|
output = c.DOM_HTML_ELEMENT_TYPE_OUTPUT,
|
||||||
|
p = c.DOM_HTML_ELEMENT_TYPE_P,
|
||||||
|
picture = c.DOM_HTML_ELEMENT_TYPE_PICTURE,
|
||||||
|
pre = c.DOM_HTML_ELEMENT_TYPE_PRE,
|
||||||
|
progress = c.DOM_HTML_ELEMENT_TYPE_PROGRESS,
|
||||||
|
blockquote = c.DOM_HTML_ELEMENT_TYPE_BLOCKQUOTE,
|
||||||
|
q = c.DOM_HTML_ELEMENT_TYPE_Q,
|
||||||
|
script = c.DOM_HTML_ELEMENT_TYPE_SCRIPT,
|
||||||
|
select = c.DOM_HTML_ELEMENT_TYPE_SELECT,
|
||||||
|
source = c.DOM_HTML_ELEMENT_TYPE_SOURCE,
|
||||||
|
span = c.DOM_HTML_ELEMENT_TYPE_SPAN,
|
||||||
|
style = c.DOM_HTML_ELEMENT_TYPE_STYLE,
|
||||||
|
table = c.DOM_HTML_ELEMENT_TYPE_TABLE,
|
||||||
|
caption = c.DOM_HTML_ELEMENT_TYPE_CAPTION,
|
||||||
|
th = c.DOM_HTML_ELEMENT_TYPE_TH,
|
||||||
|
td = c.DOM_HTML_ELEMENT_TYPE_TD,
|
||||||
|
col = c.DOM_HTML_ELEMENT_TYPE_COL,
|
||||||
|
tr = c.DOM_HTML_ELEMENT_TYPE_TR,
|
||||||
|
thead = c.DOM_HTML_ELEMENT_TYPE_THEAD,
|
||||||
|
tbody = c.DOM_HTML_ELEMENT_TYPE_TBODY,
|
||||||
|
tfoot = c.DOM_HTML_ELEMENT_TYPE_TFOOT,
|
||||||
|
template = c.DOM_HTML_ELEMENT_TYPE_TEMPLATE,
|
||||||
|
textarea = c.DOM_HTML_ELEMENT_TYPE_TEXTAREA,
|
||||||
|
time = c.DOM_HTML_ELEMENT_TYPE_TIME,
|
||||||
|
title = c.DOM_HTML_ELEMENT_TYPE_TITLE,
|
||||||
|
track = c.DOM_HTML_ELEMENT_TYPE_TRACK,
|
||||||
|
ul = c.DOM_HTML_ELEMENT_TYPE_UL,
|
||||||
|
video = c.DOM_HTML_ELEMENT_TYPE_VIDEO,
|
||||||
|
undef = c.DOM_HTML_ELEMENT_TYPE__UNKNOWN,
|
||||||
|
|
||||||
|
pub fn all() []Tag {
|
||||||
|
comptime {
|
||||||
|
const info = @typeInfo(Tag).Enum;
|
||||||
|
comptime var l: [info.fields.len]Tag = undefined;
|
||||||
|
inline for (info.fields, 0..) |field, i| {
|
||||||
|
l[i] = @as(Tag, @enumFromInt(field.value));
|
||||||
|
}
|
||||||
|
return &l;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn allElements() [][]const u8 {
|
||||||
|
comptime {
|
||||||
|
const tags = all();
|
||||||
|
var names: [tags.len][]const u8 = undefined;
|
||||||
|
inline for (tags, 0..) |tag, i| {
|
||||||
|
names[i] = tag.elementName();
|
||||||
|
}
|
||||||
|
return &names;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn upperName(comptime name: []const u8) []const u8 {
|
||||||
|
comptime {
|
||||||
|
var upper_name: [name.len]u8 = undefined;
|
||||||
|
for (name, 0..) |char, i| {
|
||||||
|
var to_upper = false;
|
||||||
|
if (i == 0) {
|
||||||
|
to_upper = true;
|
||||||
|
} else if (i == 1 and name.len == 2) {
|
||||||
|
to_upper = true;
|
||||||
|
}
|
||||||
|
if (to_upper) {
|
||||||
|
upper_name[i] = std.ascii.toUpper(char);
|
||||||
|
} else {
|
||||||
|
upper_name[i] = char;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &upper_name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn elementName(comptime tag: Tag) []const u8 {
|
||||||
|
return switch (tag) {
|
||||||
|
.a => "Anchor",
|
||||||
|
.dl => "DList",
|
||||||
|
.fieldset => "FieldSet",
|
||||||
|
.frameset => "FrameSet",
|
||||||
|
.h1, .h2, .h3, .h4, .h5, .h6 => "Heading",
|
||||||
|
.iframe => "IFrame",
|
||||||
|
.img => "Image",
|
||||||
|
.ins, .del => "Mod",
|
||||||
|
.ol => "OList",
|
||||||
|
.optgroup => "OptGroup",
|
||||||
|
.p => "Paragraph",
|
||||||
|
.blockquote, .q => "Quote",
|
||||||
|
.caption => "TableCaption",
|
||||||
|
.th, .td => "TableCell",
|
||||||
|
.col => "TableCol",
|
||||||
|
.tr => "TableRow",
|
||||||
|
.thead, .tbody, .tfoot => "TableSection",
|
||||||
|
.textarea => "TextArea",
|
||||||
|
.ul => "UList",
|
||||||
|
.undef => "Unknown",
|
||||||
|
else => upperName(@tagName(tag)),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// EventTarget
|
||||||
|
pub const EventTarget = c.dom_event_target;
|
||||||
|
|
||||||
|
// Node
|
||||||
|
pub const Node = c.dom_node_internal;
|
||||||
|
|
||||||
|
// Element
|
||||||
|
pub const Element = c.dom_element;
|
||||||
|
|
||||||
|
pub fn elementLocalName(elem: *Element) []const u8 {
|
||||||
|
const elem_aligned: *align(8) Element = @alignCast(elem);
|
||||||
|
const node = @as(*Node, @ptrCast(elem_aligned));
|
||||||
|
var s: ?*String = null;
|
||||||
|
_ = c._dom_node_get_local_name(node, &s);
|
||||||
|
var s_lower: ?*String = null;
|
||||||
|
_ = c.dom_string_tolower(s, true, &s_lower);
|
||||||
|
return stringToData(s_lower.?);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ElementHTML
|
||||||
|
pub const ElementHTML = c.dom_html_element;
|
||||||
|
|
||||||
|
pub fn elementHTMLGetTagType(elem_html: *ElementHTML) Tag {
|
||||||
|
var tag_type: c.dom_html_element_type = undefined;
|
||||||
|
_ = c._dom_html_element_get_tag_type(elem_html, &tag_type);
|
||||||
|
return @as(Tag, @enumFromInt(tag_type));
|
||||||
|
}
|
||||||
|
|
||||||
|
// ElementsHTML
|
||||||
|
|
||||||
|
pub const MediaElement = struct { base: c.dom_html_element };
|
||||||
|
|
||||||
|
pub const Unknown = struct { base: c.dom_html_element };
|
||||||
|
pub const Anchor = c.dom_html_anchor_element;
|
||||||
|
pub const Area = c.dom_html_area_element;
|
||||||
|
pub const Audio = struct { base: c.dom_html_element };
|
||||||
|
pub const BR = c.dom_html_br_element;
|
||||||
|
pub const Base = c.dom_html_base_element;
|
||||||
|
pub const Body = c.dom_html_body_element;
|
||||||
|
pub const Button = c.dom_html_button_element;
|
||||||
|
pub const Canvas = c.dom_html_canvas_element;
|
||||||
|
pub const DList = c.dom_html_dlist_element;
|
||||||
|
pub const Data = struct { base: c.dom_html_element };
|
||||||
|
pub const Dialog = struct { base: c.dom_html_element };
|
||||||
|
pub const Div = c.dom_html_div_element;
|
||||||
|
pub const Embed = struct { base: c.dom_html_element };
|
||||||
|
pub const FieldSet = c.dom_html_field_set_element;
|
||||||
|
pub const Form = c.dom_html_form_element;
|
||||||
|
pub const FrameSet = c.dom_html_frame_set_element;
|
||||||
|
pub const HR = c.dom_html_hr_element;
|
||||||
|
pub const Head = c.dom_html_head_element;
|
||||||
|
pub const Heading = c.dom_html_heading_element;
|
||||||
|
pub const Html = c.dom_html_html_element;
|
||||||
|
pub const IFrame = c.dom_html_iframe_element;
|
||||||
|
pub const Image = c.dom_html_image_element;
|
||||||
|
pub const Input = c.dom_html_input_element;
|
||||||
|
pub const LI = c.dom_html_li_element;
|
||||||
|
pub const Label = c.dom_html_label_element;
|
||||||
|
pub const Legend = c.dom_html_legend_element;
|
||||||
|
pub const Link = c.dom_html_link_element;
|
||||||
|
pub const Map = c.dom_html_map_element;
|
||||||
|
pub const Meta = c.dom_html_meta_element;
|
||||||
|
pub const Meter = struct { base: c.dom_html_element };
|
||||||
|
pub const Mod = c.dom_html_mod_element;
|
||||||
|
pub const OList = c.dom_html_olist_element;
|
||||||
|
pub const Object = c.dom_html_object_element;
|
||||||
|
pub const OptGroup = c.dom_html_opt_group_element;
|
||||||
|
pub const Option = c.dom_html_option_element;
|
||||||
|
pub const Output = struct { base: c.dom_html_element };
|
||||||
|
pub const Paragraph = c.dom_html_paragraph_element;
|
||||||
|
pub const Picture = struct { base: c.dom_html_element };
|
||||||
|
pub const Pre = c.dom_html_pre_element;
|
||||||
|
pub const Progress = struct { base: c.dom_html_element };
|
||||||
|
pub const Quote = c.dom_html_quote_element;
|
||||||
|
pub const Script = c.dom_html_script_element;
|
||||||
|
pub const Select = c.dom_html_select_element;
|
||||||
|
pub const Source = struct { base: c.dom_html_element };
|
||||||
|
pub const Span = struct { base: c.dom_html_element };
|
||||||
|
pub const Style = c.dom_html_style_element;
|
||||||
|
pub const Table = c.dom_html_table_element;
|
||||||
|
pub const TableCaption = c.dom_html_table_caption_element;
|
||||||
|
pub const TableCell = c.dom_html_table_cell_element;
|
||||||
|
pub const TableCol = c.dom_html_table_col_element;
|
||||||
|
pub const TableRow = c.dom_html_table_row_element;
|
||||||
|
pub const TableSection = c.dom_html_table_section_element;
|
||||||
|
pub const Template = struct { base: c.dom_html_element };
|
||||||
|
pub const TextArea = c.dom_html_text_area_element;
|
||||||
|
pub const Time = struct { base: c.dom_html_element };
|
||||||
|
pub const Title = c.dom_html_title_element;
|
||||||
|
pub const Track = struct { base: c.dom_html_element };
|
||||||
|
pub const UList = c.dom_html_u_list_element;
|
||||||
|
pub const Video = struct { base: c.dom_html_element };
|
||||||
|
|
||||||
|
// Document
|
||||||
|
pub const Document = c.dom_document;
|
||||||
|
|
||||||
|
pub inline fn documentGetElementById(doc: *Document, id: []const u8) ?*Element {
|
||||||
|
var elem: ?*Element = undefined;
|
||||||
|
_ = c._dom_document_get_element_by_id(doc, stringFromData(id), &elem);
|
||||||
|
return elem;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub inline fn documentCreateElement(doc: *Document, tag_name: []const u8) *Element {
|
||||||
|
var elem: ?*Element = undefined;
|
||||||
|
_ = c._dom_html_document_create_element(doc, stringFromData(tag_name), &elem);
|
||||||
|
return elem.?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// DocumentHTML
|
||||||
|
pub const DocumentHTML = c.dom_html_document;
|
||||||
|
|
||||||
|
pub fn documentHTMLParse(filename: []u8) *DocumentHTML {
|
||||||
|
const doc = cp.wr_create_doc_dom_from_file(filename.ptr);
|
||||||
|
if (doc == null) {
|
||||||
|
@panic("error parser");
|
||||||
|
}
|
||||||
|
const doc_aligned: *align(@alignOf((DocumentHTML))) cp.dom_document = @alignCast(doc.?);
|
||||||
|
return @as(*DocumentHTML, @ptrCast(doc_aligned));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub inline fn documentHTMLToDocument(doc_html: *DocumentHTML) *Document {
|
||||||
|
return @as(*Document, @ptrCast(doc_html));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub inline fn documentHTMLBody(doc_html: *DocumentHTML) ?*Body {
|
||||||
|
var body: ?*ElementHTML = undefined;
|
||||||
|
_ = c._dom_html_document_get_body(doc_html, &body);
|
||||||
|
if (body) |value| {
|
||||||
|
return @as(*Body, @ptrCast(value));
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
@@ -3,12 +3,10 @@ const std = @import("std");
|
|||||||
const jsruntime = @import("jsruntime");
|
const jsruntime = @import("jsruntime");
|
||||||
const generate = @import("generate.zig");
|
const generate = @import("generate.zig");
|
||||||
|
|
||||||
const parser = @import("parser.zig");
|
const parser = @import("netsurf.zig");
|
||||||
const DOM = @import("dom.zig");
|
const DOM = @import("dom.zig");
|
||||||
const testExecFn = @import("html/document.zig").testExecFn;
|
const testExecFn = @import("html/document.zig").testExecFn;
|
||||||
|
|
||||||
const html_test = @import("html_test.zig").html;
|
|
||||||
|
|
||||||
var doc: *parser.DocumentHTML = undefined;
|
var doc: *parser.DocumentHTML = undefined;
|
||||||
|
|
||||||
fn testsExecFn(
|
fn testsExecFn(
|
||||||
@@ -38,9 +36,8 @@ test {
|
|||||||
const apis = jsruntime.compile(DOM.Interfaces);
|
const apis = jsruntime.compile(DOM.Interfaces);
|
||||||
|
|
||||||
// document
|
// document
|
||||||
doc = parser.documentHTMLInit();
|
var f = "test.html".*;
|
||||||
defer parser.documentHTMLDeinit(doc);
|
doc = parser.documentHTMLParse(&f);
|
||||||
try parser.documentHTMLParse(doc, html_test);
|
|
||||||
|
|
||||||
// create JS vm
|
// create JS vm
|
||||||
const vm = jsruntime.VM.init();
|
const vm = jsruntime.VM.init();
|
||||||
|
|||||||
8
test.html
Normal file
8
test.html
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<div id='content'>
|
||||||
|
<a id='link' href='foo'>OK</a>
|
||||||
|
<p id='para-empty'>
|
||||||
|
<span id='para-empty-child'></span>
|
||||||
|
</p>
|
||||||
|
<p id='para'> And</p>
|
||||||
|
<!--comment-->
|
||||||
|
</div>
|
||||||
2
vendor/jsruntime-lib
vendored
2
vendor/jsruntime-lib
vendored
Submodule vendor/jsruntime-lib updated: 978b166c65...1dbd2349f9
1
vendor/netsurf/libdom
vendored
Submodule
1
vendor/netsurf/libdom
vendored
Submodule
Submodule vendor/netsurf/libdom added at 176bab60eb
1
vendor/netsurf/libhubbub
vendored
Submodule
1
vendor/netsurf/libhubbub
vendored
Submodule
Submodule vendor/netsurf/libhubbub added at 873ed6e236
1
vendor/netsurf/libparserutils
vendored
Submodule
1
vendor/netsurf/libparserutils
vendored
Submodule
Submodule vendor/netsurf/libparserutils added at 96cdd0ff11
1
vendor/netsurf/libwapcaplet
vendored
Submodule
1
vendor/netsurf/libwapcaplet
vendored
Submodule
Submodule vendor/netsurf/libwapcaplet added at b5e42b1221
1
vendor/netsurf/share/netsurf-buildsystem
vendored
Submodule
1
vendor/netsurf/share/netsurf-buildsystem
vendored
Submodule
Submodule vendor/netsurf/share/netsurf-buildsystem added at b4ba781fe2
77
vendor/netsurf/wrapper/wrapper.c
vendored
Normal file
77
vendor/netsurf/wrapper/wrapper.c
vendored
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include <dom/dom.h>
|
||||||
|
#include <dom/bindings/hubbub/parser.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a LibDOM document DOM from an HTML file
|
||||||
|
*
|
||||||
|
* \param file The file path
|
||||||
|
* \return pointer to DOM document, or NULL on error
|
||||||
|
*/
|
||||||
|
dom_document *wr_create_doc_dom_from_file(char *filename)
|
||||||
|
{
|
||||||
|
size_t buffer_size = 1024;
|
||||||
|
dom_hubbub_parser *parser = NULL;
|
||||||
|
FILE *handle;
|
||||||
|
int chunk_length;
|
||||||
|
dom_hubbub_error error;
|
||||||
|
dom_hubbub_parser_params params;
|
||||||
|
dom_document *doc;
|
||||||
|
unsigned char buffer[buffer_size];
|
||||||
|
|
||||||
|
params.enc = NULL;
|
||||||
|
params.fix_enc = true;
|
||||||
|
params.enable_script = false;
|
||||||
|
params.msg = NULL;
|
||||||
|
params.script = NULL;
|
||||||
|
params.ctx = NULL;
|
||||||
|
params.daf = NULL;
|
||||||
|
|
||||||
|
/* Create Hubbub parser */
|
||||||
|
error = dom_hubbub_parser_create(¶ms, &parser, &doc);
|
||||||
|
if (error != DOM_HUBBUB_OK) {
|
||||||
|
printf("Can't create Hubbub Parser\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Open input file */
|
||||||
|
handle = fopen(filename, "rb");
|
||||||
|
if (handle == NULL) {
|
||||||
|
dom_hubbub_parser_destroy(parser);
|
||||||
|
printf("Can't open test input file: %s\n", filename);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Parse input file in chunks */
|
||||||
|
chunk_length = buffer_size;
|
||||||
|
while (chunk_length == buffer_size) {
|
||||||
|
chunk_length = fread(buffer, 1, buffer_size, handle);
|
||||||
|
error = dom_hubbub_parser_parse_chunk(parser, buffer,
|
||||||
|
chunk_length);
|
||||||
|
if (error != DOM_HUBBUB_OK) {
|
||||||
|
dom_hubbub_parser_destroy(parser);
|
||||||
|
printf("Parsing errors occur\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Done parsing file */
|
||||||
|
error = dom_hubbub_parser_completed(parser);
|
||||||
|
if (error != DOM_HUBBUB_OK) {
|
||||||
|
dom_hubbub_parser_destroy(parser);
|
||||||
|
printf("Parsing error when construct DOM\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Finished with parser */
|
||||||
|
dom_hubbub_parser_destroy(parser);
|
||||||
|
|
||||||
|
/* Close input file */
|
||||||
|
if (fclose(handle) != 0) {
|
||||||
|
printf("Can't close test input file: %s\n", filename);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return doc;
|
||||||
|
}
|
||||||
8
vendor/netsurf/wrapper/wrapper.h
vendored
Normal file
8
vendor/netsurf/wrapper/wrapper.h
vendored
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
#ifndef wrapper_dom_h_
|
||||||
|
#define wrapper_dom_h_
|
||||||
|
|
||||||
|
#include <dom/dom.h>
|
||||||
|
|
||||||
|
dom_document *wr_create_doc_dom_from_file(char *filename);
|
||||||
|
|
||||||
|
#endif /* wrapper_dom_h_ */
|
||||||
Reference in New Issue
Block a user