mirror of
https://github.com/lightpanda-io/browser.git
synced 2025-12-17 08:48:58 +00:00
Replace SessionState directly with the Page.
This commit is contained in:
@@ -20,7 +20,7 @@ const std = @import("std");
|
|||||||
const builtin = @import("builtin");
|
const builtin = @import("builtin");
|
||||||
|
|
||||||
const JsObject = @import("../env.zig").Env.JsObject;
|
const JsObject = @import("../env.zig").Env.JsObject;
|
||||||
const SessionState = @import("../env.zig").SessionState;
|
const Page = @import("../page.zig").Page;
|
||||||
|
|
||||||
const log = if (builtin.is_test) &test_capture else @import("../../log.zig");
|
const log = if (builtin.is_test) &test_capture else @import("../../log.zig");
|
||||||
|
|
||||||
@@ -29,49 +29,49 @@ pub const Console = struct {
|
|||||||
timers: std.StringHashMapUnmanaged(u32) = .{},
|
timers: std.StringHashMapUnmanaged(u32) = .{},
|
||||||
counts: std.StringHashMapUnmanaged(u32) = .{},
|
counts: std.StringHashMapUnmanaged(u32) = .{},
|
||||||
|
|
||||||
pub fn _log(_: *const Console, values: []JsObject, state: *SessionState) !void {
|
pub fn _log(_: *const Console, values: []JsObject, page: *Page) !void {
|
||||||
if (values.len == 0) {
|
if (values.len == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
log.info(.console, "info", .{ .args = try serializeValues(values, state) });
|
log.info(.console, "info", .{ .args = try serializeValues(values, page) });
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn _info(console: *const Console, values: []JsObject, state: *SessionState) !void {
|
pub fn _info(console: *const Console, values: []JsObject, page: *Page) !void {
|
||||||
return console._log(values, state);
|
return console._log(values, page);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn _debug(_: *const Console, values: []JsObject, state: *SessionState) !void {
|
pub fn _debug(_: *const Console, values: []JsObject, page: *Page) !void {
|
||||||
if (values.len == 0) {
|
if (values.len == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
log.debug(.console, "debug", .{ .args = try serializeValues(values, state) });
|
log.debug(.console, "debug", .{ .args = try serializeValues(values, page) });
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn _warn(_: *const Console, values: []JsObject, state: *SessionState) !void {
|
pub fn _warn(_: *const Console, values: []JsObject, page: *Page) !void {
|
||||||
if (values.len == 0) {
|
if (values.len == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
log.warn(.console, "warn", .{ .args = try serializeValues(values, state) });
|
log.warn(.console, "warn", .{ .args = try serializeValues(values, page) });
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn _error(_: *const Console, values: []JsObject, state: *SessionState) !void {
|
pub fn _error(_: *const Console, values: []JsObject, page: *Page) !void {
|
||||||
if (values.len == 0) {
|
if (values.len == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
log.info(.console, "error", .{ .args = try serializeValues(values, state) });
|
log.info(.console, "error", .{ .args = try serializeValues(values, page) });
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn _clear(_: *const Console) void {}
|
pub fn _clear(_: *const Console) void {}
|
||||||
|
|
||||||
pub fn _count(self: *Console, label_: ?[]const u8, state: *SessionState) !void {
|
pub fn _count(self: *Console, label_: ?[]const u8, page: *Page) !void {
|
||||||
const label = label_ orelse "default";
|
const label = label_ orelse "default";
|
||||||
const gop = try self.counts.getOrPut(state.arena, label);
|
const gop = try self.counts.getOrPut(page.arena, label);
|
||||||
|
|
||||||
var current: u32 = 0;
|
var current: u32 = 0;
|
||||||
if (gop.found_existing) {
|
if (gop.found_existing) {
|
||||||
current = gop.value_ptr.*;
|
current = gop.value_ptr.*;
|
||||||
} else {
|
} else {
|
||||||
gop.key_ptr.* = try state.arena.dupe(u8, label);
|
gop.key_ptr.* = try page.arena.dupe(u8, label);
|
||||||
}
|
}
|
||||||
|
|
||||||
const count = current + 1;
|
const count = current + 1;
|
||||||
@@ -89,15 +89,15 @@ pub const Console = struct {
|
|||||||
log.info(.console, "count reset", .{ .label = label, .count = kv.value });
|
log.info(.console, "count reset", .{ .label = label, .count = kv.value });
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn _time(self: *Console, label_: ?[]const u8, state: *SessionState) !void {
|
pub fn _time(self: *Console, label_: ?[]const u8, page: *Page) !void {
|
||||||
const label = label_ orelse "default";
|
const label = label_ orelse "default";
|
||||||
const gop = try self.timers.getOrPut(state.arena, label);
|
const gop = try self.timers.getOrPut(page.arena, label);
|
||||||
|
|
||||||
if (gop.found_existing) {
|
if (gop.found_existing) {
|
||||||
log.info(.console, "duplicate timer", .{ .label = label });
|
log.info(.console, "duplicate timer", .{ .label = label });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
gop.key_ptr.* = try state.arena.dupe(u8, label);
|
gop.key_ptr.* = try page.arena.dupe(u8, label);
|
||||||
gop.value_ptr.* = timestamp();
|
gop.value_ptr.* = timestamp();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -122,19 +122,19 @@ pub const Console = struct {
|
|||||||
log.warn(.console, "timer stop", .{ .label = label, .elapsed = elapsed - kv.value });
|
log.warn(.console, "timer stop", .{ .label = label, .elapsed = elapsed - kv.value });
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn _assert(_: *Console, assertion: JsObject, values: []JsObject, state: *SessionState) !void {
|
pub fn _assert(_: *Console, assertion: JsObject, values: []JsObject, page: *Page) !void {
|
||||||
if (assertion.isTruthy()) {
|
if (assertion.isTruthy()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var serialized_values: []const u8 = "";
|
var serialized_values: []const u8 = "";
|
||||||
if (values.len > 0) {
|
if (values.len > 0) {
|
||||||
serialized_values = try serializeValues(values, state);
|
serialized_values = try serializeValues(values, page);
|
||||||
}
|
}
|
||||||
log.info(.console, "assertion failed", .{ .values = serialized_values });
|
log.info(.console, "assertion failed", .{ .values = serialized_values });
|
||||||
}
|
}
|
||||||
|
|
||||||
fn serializeValues(values: []JsObject, state: *SessionState) ![]const u8 {
|
fn serializeValues(values: []JsObject, page: *Page) ![]const u8 {
|
||||||
const arena = state.call_arena;
|
const arena = page.call_arena;
|
||||||
var arr: std.ArrayListUnmanaged(u8) = .{};
|
var arr: std.ArrayListUnmanaged(u8) = .{};
|
||||||
try arr.appendSlice(arena, try values[0].toString());
|
try arr.appendSlice(arena, try values[0].toString());
|
||||||
for (values[1..]) |value| {
|
for (values[1..]) |value| {
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ const std = @import("std");
|
|||||||
|
|
||||||
const CSSParser = @import("./css_parser.zig").CSSParser;
|
const CSSParser = @import("./css_parser.zig").CSSParser;
|
||||||
const CSSValueAnalyzer = @import("./css_value_analyzer.zig").CSSValueAnalyzer;
|
const CSSValueAnalyzer = @import("./css_value_analyzer.zig").CSSValueAnalyzer;
|
||||||
const SessionState = @import("../env.zig").SessionState;
|
const Page = @import("../page.zig").Page;
|
||||||
|
|
||||||
pub const Interfaces = .{
|
pub const Interfaces = .{
|
||||||
CSSStyleDeclaration,
|
CSSStyleDeclaration,
|
||||||
@@ -47,17 +47,17 @@ pub const CSSStyleDeclaration = struct {
|
|||||||
return self._getPropertyValue("float");
|
return self._getPropertyValue("float");
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_cssFloat(self: *CSSStyleDeclaration, value: ?[]const u8, state: *SessionState) !void {
|
pub fn set_cssFloat(self: *CSSStyleDeclaration, value: ?[]const u8, page: *Page) !void {
|
||||||
const final_value = value orelse "";
|
const final_value = value orelse "";
|
||||||
return self._setProperty("float", final_value, null, state);
|
return self._setProperty("float", final_value, null, page);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_cssText(self: *const CSSStyleDeclaration, state: *SessionState) ![]const u8 {
|
pub fn get_cssText(self: *const CSSStyleDeclaration, page: *Page) ![]const u8 {
|
||||||
var buffer: std.ArrayListUnmanaged(u8) = .empty;
|
var buffer: std.ArrayListUnmanaged(u8) = .empty;
|
||||||
const writer = buffer.writer(state.call_arena);
|
const writer = buffer.writer(page.call_arena);
|
||||||
for (self.order.items) |name| {
|
for (self.order.items) |name| {
|
||||||
const prop = self.store.get(name).?;
|
const prop = self.store.get(name).?;
|
||||||
const escaped = try CSSValueAnalyzer.escapeCSSValue(state.call_arena, prop.value);
|
const escaped = try CSSValueAnalyzer.escapeCSSValue(page.call_arena, prop.value);
|
||||||
try writer.print("{s}: {s}", .{ name, escaped });
|
try writer.print("{s}: {s}", .{ name, escaped });
|
||||||
if (prop.priority) try writer.writeAll(" !important");
|
if (prop.priority) try writer.writeAll(" !important");
|
||||||
try writer.writeAll("; ");
|
try writer.writeAll("; ");
|
||||||
@@ -66,18 +66,18 @@ pub const CSSStyleDeclaration = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO Propagate also upward to parent node
|
// TODO Propagate also upward to parent node
|
||||||
pub fn set_cssText(self: *CSSStyleDeclaration, text: []const u8, state: *SessionState) !void {
|
pub fn set_cssText(self: *CSSStyleDeclaration, text: []const u8, page: *Page) !void {
|
||||||
self.store.clearRetainingCapacity();
|
self.store.clearRetainingCapacity();
|
||||||
self.order.clearRetainingCapacity();
|
self.order.clearRetainingCapacity();
|
||||||
|
|
||||||
// call_arena is safe here, because _setProperty will dupe the name
|
// call_arena is safe here, because _setProperty will dupe the name
|
||||||
// using the state's longer-living arena.
|
// using the page's longer-living arena.
|
||||||
const declarations = try CSSParser.parseDeclarations(state.call_arena, text);
|
const declarations = try CSSParser.parseDeclarations(page.call_arena, text);
|
||||||
|
|
||||||
for (declarations) |decl| {
|
for (declarations) |decl| {
|
||||||
if (!CSSValueAnalyzer.isValidPropertyName(decl.name)) continue;
|
if (!CSSValueAnalyzer.isValidPropertyName(decl.name)) continue;
|
||||||
const priority: ?[]const u8 = if (decl.is_important) "important" else null;
|
const priority: ?[]const u8 = if (decl.is_important) "important" else null;
|
||||||
try self._setProperty(decl.name, decl.value, priority, state);
|
try self._setProperty(decl.name, decl.value, priority, page);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -119,19 +119,19 @@ pub const CSSStyleDeclaration = struct {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// safe to return, since it's in our state.arena
|
// safe to return, since it's in our page.arena
|
||||||
return prop.value.value;
|
return prop.value.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn _setProperty(self: *CSSStyleDeclaration, name: []const u8, value: []const u8, priority: ?[]const u8, state: *SessionState) !void {
|
pub fn _setProperty(self: *CSSStyleDeclaration, name: []const u8, value: []const u8, priority: ?[]const u8, page: *Page) !void {
|
||||||
const owned_value = try state.arena.dupe(u8, value);
|
const owned_value = try page.arena.dupe(u8, value);
|
||||||
const is_important = priority != null and std.ascii.eqlIgnoreCase(priority.?, "important");
|
const is_important = priority != null and std.ascii.eqlIgnoreCase(priority.?, "important");
|
||||||
|
|
||||||
const gop = try self.store.getOrPut(state.arena, name);
|
const gop = try self.store.getOrPut(page.arena, name);
|
||||||
if (!gop.found_existing) {
|
if (!gop.found_existing) {
|
||||||
const owned_name = try state.arena.dupe(u8, name);
|
const owned_name = try page.arena.dupe(u8, name);
|
||||||
gop.key_ptr.* = owned_name;
|
gop.key_ptr.* = owned_name;
|
||||||
try self.order.append(state.arena, owned_name);
|
try self.order.append(page.arena, owned_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
gop.value_ptr.* = .{ .value = owned_value, .priority = is_important };
|
gop.value_ptr.* = .{ .value = owned_value, .priority = is_important };
|
||||||
@@ -177,7 +177,7 @@ test "CSSOM.CSSStyleDeclaration" {
|
|||||||
.{ "style.setProperty('color', 'green')", "undefined" },
|
.{ "style.setProperty('color', 'green')", "undefined" },
|
||||||
.{ "style.getPropertyValue('color')", "green" },
|
.{ "style.getPropertyValue('color')", "green" },
|
||||||
.{ "style.length", "4" },
|
.{ "style.length", "4" },
|
||||||
.{ "style.color", "green"},
|
.{ "style.color", "green" },
|
||||||
|
|
||||||
.{ "style.setProperty('padding', '10px', 'important')", "undefined" },
|
.{ "style.setProperty('padding', '10px', 'important')", "undefined" },
|
||||||
.{ "style.getPropertyValue('padding')", "10px" },
|
.{ "style.getPropertyValue('padding')", "10px" },
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ const parser = @import("../netsurf.zig");
|
|||||||
|
|
||||||
const CharacterData = @import("character_data.zig").CharacterData;
|
const CharacterData = @import("character_data.zig").CharacterData;
|
||||||
|
|
||||||
const SessionState = @import("../env.zig").SessionState;
|
const Page = @import("../page.zig").Page;
|
||||||
|
|
||||||
// https://dom.spec.whatwg.org/#interface-comment
|
// https://dom.spec.whatwg.org/#interface-comment
|
||||||
pub const Comment = struct {
|
pub const Comment = struct {
|
||||||
@@ -28,9 +28,9 @@ pub const Comment = struct {
|
|||||||
pub const prototype = *CharacterData;
|
pub const prototype = *CharacterData;
|
||||||
pub const subtype = .node;
|
pub const subtype = .node;
|
||||||
|
|
||||||
pub fn constructor(data: ?[]const u8, state: *const SessionState) !*parser.Comment {
|
pub fn constructor(data: ?[]const u8, page: *const Page) !*parser.Comment {
|
||||||
return parser.documentCreateComment(
|
return parser.documentCreateComment(
|
||||||
parser.documentHTMLToDocument(state.window.document),
|
parser.documentHTMLToDocument(page.window.document),
|
||||||
data orelse "",
|
data orelse "",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
|
||||||
const parser = @import("../netsurf.zig");
|
const parser = @import("../netsurf.zig");
|
||||||
const SessionState = @import("../env.zig").SessionState;
|
const Page = @import("../page.zig").Page;
|
||||||
|
|
||||||
const Node = @import("node.zig").Node;
|
const Node = @import("node.zig").Node;
|
||||||
const NodeList = @import("nodelist.zig").NodeList;
|
const NodeList = @import("nodelist.zig").NodeList;
|
||||||
@@ -42,14 +42,14 @@ pub const Document = struct {
|
|||||||
pub const prototype = *Node;
|
pub const prototype = *Node;
|
||||||
pub const subtype = .node;
|
pub const subtype = .node;
|
||||||
|
|
||||||
pub fn constructor(state: *const SessionState) !*parser.DocumentHTML {
|
pub fn constructor(page: *const Page) !*parser.DocumentHTML {
|
||||||
const doc = try parser.documentCreateDocument(
|
const doc = try parser.documentCreateDocument(
|
||||||
try parser.documentHTMLGetTitle(state.window.document),
|
try parser.documentHTMLGetTitle(page.window.document),
|
||||||
);
|
);
|
||||||
|
|
||||||
// we have to work w/ document instead of html document.
|
// we have to work w/ document instead of html document.
|
||||||
const ddoc = parser.documentHTMLToDocument(doc);
|
const ddoc = parser.documentHTMLToDocument(doc);
|
||||||
const ccur = parser.documentHTMLToDocument(state.window.document);
|
const ccur = parser.documentHTMLToDocument(page.window.document);
|
||||||
try parser.documentSetDocumentURI(ddoc, try parser.documentGetDocumentURI(ccur));
|
try parser.documentSetDocumentURI(ddoc, try parser.documentGetDocumentURI(ccur));
|
||||||
try parser.documentSetInputEncoding(ddoc, try parser.documentGetInputEncoding(ccur));
|
try parser.documentSetInputEncoding(ddoc, try parser.documentGetInputEncoding(ccur));
|
||||||
|
|
||||||
@@ -141,18 +141,17 @@ pub const Document = struct {
|
|||||||
pub fn _getElementsByTagName(
|
pub fn _getElementsByTagName(
|
||||||
self: *parser.Document,
|
self: *parser.Document,
|
||||||
tag_name: []const u8,
|
tag_name: []const u8,
|
||||||
state: *SessionState,
|
page: *Page,
|
||||||
) !collection.HTMLCollection {
|
) !collection.HTMLCollection {
|
||||||
return try collection.HTMLCollectionByTagName(state.arena, parser.documentToNode(self), tag_name, true);
|
return try collection.HTMLCollectionByTagName(page.arena, parser.documentToNode(self), tag_name, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn _getElementsByClassName(
|
pub fn _getElementsByClassName(
|
||||||
self: *parser.Document,
|
self: *parser.Document,
|
||||||
classNames: []const u8,
|
classNames: []const u8,
|
||||||
state: *SessionState,
|
page: *Page,
|
||||||
) !collection.HTMLCollection {
|
) !collection.HTMLCollection {
|
||||||
const allocator = state.arena;
|
return try collection.HTMLCollectionByClassName(page.arena, parser.documentToNode(self), classNames, true);
|
||||||
return try collection.HTMLCollectionByClassName(allocator, parser.documentToNode(self), classNames, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn _createDocumentFragment(self: *parser.Document) !*parser.DocumentFragment {
|
pub fn _createDocumentFragment(self: *parser.Document) !*parser.DocumentFragment {
|
||||||
@@ -214,20 +213,18 @@ pub const Document = struct {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn _querySelector(self: *parser.Document, selector: []const u8, state: *SessionState) !?ElementUnion {
|
pub fn _querySelector(self: *parser.Document, selector: []const u8, page: *Page) !?ElementUnion {
|
||||||
if (selector.len == 0) return null;
|
if (selector.len == 0) return null;
|
||||||
|
|
||||||
const allocator = state.arena;
|
const n = try css.querySelector(page.arena, parser.documentToNode(self), selector);
|
||||||
const n = try css.querySelector(allocator, parser.documentToNode(self), selector);
|
|
||||||
|
|
||||||
if (n == null) return null;
|
if (n == null) return null;
|
||||||
|
|
||||||
return try Element.toInterface(parser.nodeToElement(n.?));
|
return try Element.toInterface(parser.nodeToElement(n.?));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn _querySelectorAll(self: *parser.Document, selector: []const u8, state: *SessionState) !NodeList {
|
pub fn _querySelectorAll(self: *parser.Document, selector: []const u8, page: *Page) !NodeList {
|
||||||
const allocator = state.arena;
|
return css.querySelectorAll(page.arena, parser.documentToNode(self), selector);
|
||||||
return css.querySelectorAll(allocator, parser.documentToNode(self), selector);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn _prepend(self: *parser.Document, nodes: []const Node.NodeOrText) !void {
|
pub fn _prepend(self: *parser.Document, nodes: []const Node.NodeOrText) !void {
|
||||||
@@ -249,7 +246,9 @@ pub const Document = struct {
|
|||||||
|
|
||||||
const testing = @import("../../testing.zig");
|
const testing = @import("../../testing.zig");
|
||||||
test "Browser.DOM.Document" {
|
test "Browser.DOM.Document" {
|
||||||
var runner = try testing.jsRunner(testing.tracking_allocator, .{});
|
var runner = try testing.jsRunner(testing.tracking_allocator, .{
|
||||||
|
.url = "about:blank",
|
||||||
|
});
|
||||||
defer runner.deinit();
|
defer runner.deinit();
|
||||||
|
|
||||||
try runner.testCases(&.{
|
try runner.testCases(&.{
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
const parser = @import("../netsurf.zig");
|
const parser = @import("../netsurf.zig");
|
||||||
const SessionState = @import("../env.zig").SessionState;
|
const Page = @import("../page.zig").Page;
|
||||||
|
|
||||||
const Node = @import("node.zig").Node;
|
const Node = @import("node.zig").Node;
|
||||||
|
|
||||||
@@ -27,9 +27,9 @@ pub const DocumentFragment = struct {
|
|||||||
pub const prototype = *Node;
|
pub const prototype = *Node;
|
||||||
pub const subtype = .node;
|
pub const subtype = .node;
|
||||||
|
|
||||||
pub fn constructor(state: *const SessionState) !*parser.DocumentFragment {
|
pub fn constructor(page: *const Page) !*parser.DocumentFragment {
|
||||||
return parser.documentCreateDocumentFragment(
|
return parser.documentCreateDocumentFragment(
|
||||||
parser.documentHTMLToDocument(state.window.document),
|
parser.documentHTMLToDocument(page.window.document),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
|
||||||
const parser = @import("../netsurf.zig");
|
const parser = @import("../netsurf.zig");
|
||||||
const SessionState = @import("../env.zig").SessionState;
|
const Page = @import("../page.zig").Page;
|
||||||
|
|
||||||
const css = @import("css.zig");
|
const css = @import("css.zig");
|
||||||
const log = @import("../../log.zig");
|
const log = @import("../../log.zig");
|
||||||
@@ -102,14 +102,14 @@ pub const Element = struct {
|
|||||||
return try parser.nodeGetAttributes(parser.elementToNode(self)) orelse unreachable;
|
return try parser.nodeGetAttributes(parser.elementToNode(self)) orelse unreachable;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_innerHTML(self: *parser.Element, state: *SessionState) ![]const u8 {
|
pub fn get_innerHTML(self: *parser.Element, page: *Page) ![]const u8 {
|
||||||
var buf = std.ArrayList(u8).init(state.arena);
|
var buf = std.ArrayList(u8).init(page.arena);
|
||||||
try dump.writeChildren(parser.elementToNode(self), buf.writer());
|
try dump.writeChildren(parser.elementToNode(self), buf.writer());
|
||||||
return buf.items;
|
return buf.items;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_outerHTML(self: *parser.Element, state: *SessionState) ![]const u8 {
|
pub fn get_outerHTML(self: *parser.Element, page: *Page) ![]const u8 {
|
||||||
var buf = std.ArrayList(u8).init(state.arena);
|
var buf = std.ArrayList(u8).init(page.arena);
|
||||||
try dump.writeNode(parser.elementToNode(self), buf.writer());
|
try dump.writeNode(parser.elementToNode(self), buf.writer());
|
||||||
return buf.items;
|
return buf.items;
|
||||||
}
|
}
|
||||||
@@ -138,10 +138,10 @@ pub const Element = struct {
|
|||||||
|
|
||||||
// The closest() method of the Element interface traverses the element and its parents (heading toward the document root) until it finds a node that matches the specified CSS selector.
|
// The closest() method of the Element interface traverses the element and its parents (heading toward the document root) until it finds a node that matches the specified CSS selector.
|
||||||
// Returns the closest ancestor Element or itself, which matches the selectors. If there are no such element, null.
|
// Returns the closest ancestor Element or itself, which matches the selectors. If there are no such element, null.
|
||||||
pub fn _closest(self: *parser.Element, selector: []const u8, state: *SessionState) !?*parser.Element {
|
pub fn _closest(self: *parser.Element, selector: []const u8, page: *Page) !?*parser.Element {
|
||||||
const cssParse = @import("../css/css.zig").parse;
|
const cssParse = @import("../css/css.zig").parse;
|
||||||
const CssNodeWrap = @import("../css/libdom.zig").Node;
|
const CssNodeWrap = @import("../css/libdom.zig").Node;
|
||||||
const select = try cssParse(state.call_arena, selector, .{});
|
const select = try cssParse(page.call_arena, selector, .{});
|
||||||
|
|
||||||
var current: CssNodeWrap = .{ .node = parser.elementToNode(self) };
|
var current: CssNodeWrap = .{ .node = parser.elementToNode(self) };
|
||||||
while (true) {
|
while (true) {
|
||||||
@@ -249,10 +249,10 @@ pub const Element = struct {
|
|||||||
pub fn _getElementsByTagName(
|
pub fn _getElementsByTagName(
|
||||||
self: *parser.Element,
|
self: *parser.Element,
|
||||||
tag_name: []const u8,
|
tag_name: []const u8,
|
||||||
state: *SessionState,
|
page: *Page,
|
||||||
) !collection.HTMLCollection {
|
) !collection.HTMLCollection {
|
||||||
return try collection.HTMLCollectionByTagName(
|
return try collection.HTMLCollectionByTagName(
|
||||||
state.arena,
|
page.arena,
|
||||||
parser.elementToNode(self),
|
parser.elementToNode(self),
|
||||||
tag_name,
|
tag_name,
|
||||||
false,
|
false,
|
||||||
@@ -262,10 +262,10 @@ pub const Element = struct {
|
|||||||
pub fn _getElementsByClassName(
|
pub fn _getElementsByClassName(
|
||||||
self: *parser.Element,
|
self: *parser.Element,
|
||||||
classNames: []const u8,
|
classNames: []const u8,
|
||||||
state: *SessionState,
|
page: *Page,
|
||||||
) !collection.HTMLCollection {
|
) !collection.HTMLCollection {
|
||||||
return try collection.HTMLCollectionByClassName(
|
return try collection.HTMLCollectionByClassName(
|
||||||
state.arena,
|
page.arena,
|
||||||
parser.elementToNode(self),
|
parser.elementToNode(self),
|
||||||
classNames,
|
classNames,
|
||||||
false,
|
false,
|
||||||
@@ -328,18 +328,18 @@ pub const Element = struct {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn _querySelector(self: *parser.Element, selector: []const u8, state: *SessionState) !?Union {
|
pub fn _querySelector(self: *parser.Element, selector: []const u8, page: *Page) !?Union {
|
||||||
if (selector.len == 0) return null;
|
if (selector.len == 0) return null;
|
||||||
|
|
||||||
const n = try css.querySelector(state.arena, parser.elementToNode(self), selector);
|
const n = try css.querySelector(page.arena, parser.elementToNode(self), selector);
|
||||||
|
|
||||||
if (n == null) return null;
|
if (n == null) return null;
|
||||||
|
|
||||||
return try toInterface(parser.nodeToElement(n.?));
|
return try toInterface(parser.nodeToElement(n.?));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn _querySelectorAll(self: *parser.Element, selector: []const u8, state: *SessionState) !NodeList {
|
pub fn _querySelectorAll(self: *parser.Element, selector: []const u8, page: *Page) !NodeList {
|
||||||
return css.querySelectorAll(state.arena, parser.elementToNode(self), selector);
|
return css.querySelectorAll(page.arena, parser.elementToNode(self), selector);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn _prepend(self: *parser.Element, nodes: []const Node.NodeOrText) !void {
|
pub fn _prepend(self: *parser.Element, nodes: []const Node.NodeOrText) !void {
|
||||||
@@ -366,40 +366,40 @@ pub const Element = struct {
|
|||||||
|
|
||||||
// A DOMRect object providing information about the size of an element and its position relative to the viewport.
|
// A DOMRect object providing information about the size of an element and its position relative to the viewport.
|
||||||
// Returns a 0 DOMRect object if the element is eventually detached from the main window
|
// Returns a 0 DOMRect object if the element is eventually detached from the main window
|
||||||
pub fn _getBoundingClientRect(self: *parser.Element, state: *SessionState) !DOMRect {
|
pub fn _getBoundingClientRect(self: *parser.Element, page: *Page) !DOMRect {
|
||||||
// Since we are lazy rendering we need to do this check. We could store the renderer in a viewport such that it could cache these, but it would require tracking changes.
|
// Since we are lazy rendering we need to do this check. We could store the renderer in a viewport such that it could cache these, but it would require tracking changes.
|
||||||
const root = try parser.nodeGetRootNode(parser.elementToNode(self));
|
const root = try parser.nodeGetRootNode(parser.elementToNode(self));
|
||||||
if (root != parser.documentToNode(parser.documentHTMLToDocument(state.window.document))) {
|
if (root != parser.documentToNode(parser.documentHTMLToDocument(page.window.document))) {
|
||||||
return DOMRect{ .x = 0, .y = 0, .width = 0, .height = 0 };
|
return DOMRect{ .x = 0, .y = 0, .width = 0, .height = 0 };
|
||||||
}
|
}
|
||||||
return state.renderer.getRect(self);
|
return page.renderer.getRect(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns a collection of DOMRect objects that indicate the bounding rectangles for each CSS border box in a client.
|
// Returns a collection of DOMRect objects that indicate the bounding rectangles for each CSS border box in a client.
|
||||||
// We do not render so it only always return the element's bounding rect.
|
// We do not render so it only always return the element's bounding rect.
|
||||||
// Returns an empty array if the element is eventually detached from the main window
|
// Returns an empty array if the element is eventually detached from the main window
|
||||||
pub fn _getClientRects(self: *parser.Element, state: *SessionState) ![]DOMRect {
|
pub fn _getClientRects(self: *parser.Element, page: *Page) ![]DOMRect {
|
||||||
const root = try parser.nodeGetRootNode(parser.elementToNode(self));
|
const root = try parser.nodeGetRootNode(parser.elementToNode(self));
|
||||||
if (root != parser.documentToNode(parser.documentHTMLToDocument(state.window.document))) {
|
if (root != parser.documentToNode(parser.documentHTMLToDocument(page.window.document))) {
|
||||||
return &.{};
|
return &.{};
|
||||||
}
|
}
|
||||||
const heap_ptr = try state.call_arena.create(DOMRect);
|
const heap_ptr = try page.call_arena.create(DOMRect);
|
||||||
heap_ptr.* = try state.renderer.getRect(self);
|
heap_ptr.* = try page.renderer.getRect(self);
|
||||||
return heap_ptr[0..1];
|
return heap_ptr[0..1];
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_clientWidth(_: *parser.Element, state: *SessionState) u32 {
|
pub fn get_clientWidth(_: *parser.Element, page: *Page) u32 {
|
||||||
return state.renderer.width();
|
return page.renderer.width();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_clientHeight(_: *parser.Element, state: *SessionState) u32 {
|
pub fn get_clientHeight(_: *parser.Element, page: *Page) u32 {
|
||||||
return state.renderer.height();
|
return page.renderer.height();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn _matches(self: *parser.Element, selectors: []const u8, state: *SessionState) !bool {
|
pub fn _matches(self: *parser.Element, selectors: []const u8, page: *Page) !bool {
|
||||||
const cssParse = @import("../css/css.zig").parse;
|
const cssParse = @import("../css/css.zig").parse;
|
||||||
const CssNodeWrap = @import("../css/libdom.zig").Node;
|
const CssNodeWrap = @import("../css/libdom.zig").Node;
|
||||||
const s = try cssParse(state.call_arena, selectors, .{});
|
const s = try cssParse(page.call_arena, selectors, .{});
|
||||||
return s.match(CssNodeWrap{ .node = parser.elementToNode(self) });
|
return s.match(CssNodeWrap{ .node = parser.elementToNode(self) });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
|
|
||||||
const Env = @import("../env.zig").Env;
|
const Env = @import("../env.zig").Env;
|
||||||
const parser = @import("../netsurf.zig");
|
const parser = @import("../netsurf.zig");
|
||||||
const SessionState = @import("../env.zig").SessionState;
|
const Page = @import("../page.zig").Page;
|
||||||
|
|
||||||
const EventHandler = @import("../events/event.zig").EventHandler;
|
const EventHandler = @import("../events/event.zig").EventHandler;
|
||||||
|
|
||||||
@@ -59,7 +59,7 @@ pub const EventTarget = struct {
|
|||||||
typ: []const u8,
|
typ: []const u8,
|
||||||
cbk: Env.Function,
|
cbk: Env.Function,
|
||||||
opts_: ?AddEventListenerOpts,
|
opts_: ?AddEventListenerOpts,
|
||||||
state: *SessionState,
|
page: *Page,
|
||||||
) !void {
|
) !void {
|
||||||
var capture = false;
|
var capture = false;
|
||||||
if (opts_) |opts| {
|
if (opts_) |opts| {
|
||||||
@@ -91,7 +91,7 @@ pub const EventTarget = struct {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const eh = try EventHandler.init(state.arena, try cbk.withThis(self));
|
const eh = try EventHandler.init(page.arena, try cbk.withThis(self));
|
||||||
|
|
||||||
try parser.eventTargetAddEventListener(
|
try parser.eventTargetAddEventListener(
|
||||||
self,
|
self,
|
||||||
@@ -132,10 +132,6 @@ pub const EventTarget = struct {
|
|||||||
pub fn _dispatchEvent(self: *parser.EventTarget, event: *parser.Event) !bool {
|
pub fn _dispatchEvent(self: *parser.EventTarget, event: *parser.Event) !bool {
|
||||||
return try parser.eventTargetDispatchEvent(self, event);
|
return try parser.eventTargetDispatchEvent(self, event);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deinit(self: *parser.EventTarget, state: *SessionState) void {
|
|
||||||
parser.eventTargetRemoveAllEventListeners(self, state.arena) catch unreachable;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const testing = @import("../../testing.zig");
|
const testing = @import("../../testing.zig");
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ const std = @import("std");
|
|||||||
|
|
||||||
const log = @import("../../log.zig");
|
const log = @import("../../log.zig");
|
||||||
const parser = @import("../netsurf.zig");
|
const parser = @import("../netsurf.zig");
|
||||||
const SessionState = @import("../env.zig").SessionState;
|
const Page = @import("../page.zig").Page;
|
||||||
|
|
||||||
const Env = @import("../env.zig").Env;
|
const Env = @import("../env.zig").Env;
|
||||||
const Element = @import("element.zig").Element;
|
const Element = @import("element.zig").Element;
|
||||||
@@ -39,17 +39,17 @@ pub const Interfaces = .{
|
|||||||
// The returned Entries are phony, they always indicate full intersection.
|
// The returned Entries are phony, they always indicate full intersection.
|
||||||
// https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver
|
// https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver
|
||||||
pub const IntersectionObserver = struct {
|
pub const IntersectionObserver = struct {
|
||||||
|
page: *Page,
|
||||||
callback: Env.Function,
|
callback: Env.Function,
|
||||||
options: IntersectionObserverOptions,
|
options: IntersectionObserverOptions,
|
||||||
state: *SessionState,
|
|
||||||
|
|
||||||
observed_entries: std.ArrayListUnmanaged(IntersectionObserverEntry),
|
observed_entries: std.ArrayListUnmanaged(IntersectionObserverEntry),
|
||||||
|
|
||||||
// new IntersectionObserver(callback)
|
// new IntersectionObserver(callback)
|
||||||
// new IntersectionObserver(callback, options) [not supported yet]
|
// new IntersectionObserver(callback, options) [not supported yet]
|
||||||
pub fn constructor(callback: Env.Function, options_: ?IntersectionObserverOptions, state: *SessionState) !IntersectionObserver {
|
pub fn constructor(callback: Env.Function, options_: ?IntersectionObserverOptions, page: *Page) !IntersectionObserver {
|
||||||
var options = IntersectionObserverOptions{
|
var options = IntersectionObserverOptions{
|
||||||
.root = parser.documentToNode(parser.documentHTMLToDocument(state.window.document)),
|
.root = parser.documentToNode(parser.documentHTMLToDocument(page.window.document)),
|
||||||
.rootMargin = "0px 0px 0px 0px",
|
.rootMargin = "0px 0px 0px 0px",
|
||||||
.threshold = &.{0.0},
|
.threshold = &.{0.0},
|
||||||
};
|
};
|
||||||
@@ -60,9 +60,9 @@ pub const IntersectionObserver = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return .{
|
return .{
|
||||||
|
.page = page,
|
||||||
.callback = callback,
|
.callback = callback,
|
||||||
.options = options,
|
.options = options,
|
||||||
.state = state,
|
|
||||||
.observed_entries = .{},
|
.observed_entries = .{},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -78,8 +78,8 @@ pub const IntersectionObserver = struct {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
try self.observed_entries.append(self.state.arena, .{
|
try self.observed_entries.append(self.page.arena, .{
|
||||||
.state = self.state,
|
.page = self.page,
|
||||||
.target = target_element,
|
.target = target_element,
|
||||||
.options = &self.options,
|
.options = &self.options,
|
||||||
});
|
});
|
||||||
@@ -113,13 +113,13 @@ const IntersectionObserverOptions = struct {
|
|||||||
// https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserverEntry
|
// https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserverEntry
|
||||||
// https://w3c.github.io/IntersectionObserver/#intersection-observer-entry
|
// https://w3c.github.io/IntersectionObserver/#intersection-observer-entry
|
||||||
pub const IntersectionObserverEntry = struct {
|
pub const IntersectionObserverEntry = struct {
|
||||||
state: *SessionState,
|
page: *Page,
|
||||||
target: *parser.Element,
|
target: *parser.Element,
|
||||||
options: *IntersectionObserverOptions,
|
options: *IntersectionObserverOptions,
|
||||||
|
|
||||||
// Returns the bounds rectangle of the target element as a DOMRectReadOnly. The bounds are computed as described in the documentation for Element.getBoundingClientRect().
|
// Returns the bounds rectangle of the target element as a DOMRectReadOnly. The bounds are computed as described in the documentation for Element.getBoundingClientRect().
|
||||||
pub fn get_boundingClientRect(self: *const IntersectionObserverEntry) !Element.DOMRect {
|
pub fn get_boundingClientRect(self: *const IntersectionObserverEntry) !Element.DOMRect {
|
||||||
return Element._getBoundingClientRect(self.target, self.state);
|
return Element._getBoundingClientRect(self.target, self.page);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the ratio of the intersectionRect to the boundingClientRect.
|
// Returns the ratio of the intersectionRect to the boundingClientRect.
|
||||||
@@ -129,10 +129,14 @@ pub const IntersectionObserverEntry = struct {
|
|||||||
|
|
||||||
// Returns a DOMRectReadOnly representing the target's visible area.
|
// Returns a DOMRectReadOnly representing the target's visible area.
|
||||||
pub fn get_intersectionRect(self: *const IntersectionObserverEntry) !Element.DOMRect {
|
pub fn get_intersectionRect(self: *const IntersectionObserverEntry) !Element.DOMRect {
|
||||||
return Element._getBoundingClientRect(self.target, self.state);
|
return Element._getBoundingClientRect(self.target, self.page);
|
||||||
}
|
}
|
||||||
|
|
||||||
// A Boolean value which is true if the target element intersects with the intersection observer's root. If this is true, then, the IntersectionObserverEntry describes a transition into a state of intersection; if it's false, then you know the transition is from intersecting to not-intersecting.
|
// A Boolean value which is true if the target element intersects with the
|
||||||
|
// intersection observer's root. If this is true, then, the
|
||||||
|
// IntersectionObserverEntry describes a transition into a state of
|
||||||
|
// intersection; if it's false, then you know the transition is from
|
||||||
|
// intersecting to not-intersecting.
|
||||||
pub fn get_isIntersecting(_: *const IntersectionObserverEntry) bool {
|
pub fn get_isIntersecting(_: *const IntersectionObserverEntry) bool {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -140,8 +144,8 @@ pub const IntersectionObserverEntry = struct {
|
|||||||
// Returns a DOMRectReadOnly for the intersection observer's root.
|
// Returns a DOMRectReadOnly for the intersection observer's root.
|
||||||
pub fn get_rootBounds(self: *const IntersectionObserverEntry) !Element.DOMRect {
|
pub fn get_rootBounds(self: *const IntersectionObserverEntry) !Element.DOMRect {
|
||||||
const root = self.options.root.?;
|
const root = self.options.root.?;
|
||||||
if (@intFromPtr(root) == @intFromPtr(self.state.window.document)) {
|
if (@intFromPtr(root) == @intFromPtr(self.page.window.document)) {
|
||||||
return self.state.renderer.boundingRect();
|
return self.page.renderer.boundingRect();
|
||||||
}
|
}
|
||||||
|
|
||||||
const root_type = try parser.nodeType(root);
|
const root_type = try parser.nodeType(root);
|
||||||
@@ -156,7 +160,7 @@ pub const IntersectionObserverEntry = struct {
|
|||||||
else => return error.InvalidState,
|
else => return error.InvalidState,
|
||||||
}
|
}
|
||||||
|
|
||||||
return Element._getBoundingClientRect(element, self.state);
|
return Element._getBoundingClientRect(element, self.page);
|
||||||
}
|
}
|
||||||
|
|
||||||
// The Element whose intersection with the root changed.
|
// The Element whose intersection with the root changed.
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ const Allocator = std.mem.Allocator;
|
|||||||
|
|
||||||
const log = @import("../../log.zig");
|
const log = @import("../../log.zig");
|
||||||
const parser = @import("../netsurf.zig");
|
const parser = @import("../netsurf.zig");
|
||||||
const SessionState = @import("../env.zig").SessionState;
|
const Page = @import("../page.zig").Page;
|
||||||
|
|
||||||
const Env = @import("../env.zig").Env;
|
const Env = @import("../env.zig").Env;
|
||||||
const NodeList = @import("nodelist.zig").NodeList;
|
const NodeList = @import("nodelist.zig").NodeList;
|
||||||
@@ -42,11 +42,11 @@ pub const MutationObserver = struct {
|
|||||||
// execute our callback with it.
|
// execute our callback with it.
|
||||||
observed: std.ArrayListUnmanaged(*MutationRecord),
|
observed: std.ArrayListUnmanaged(*MutationRecord),
|
||||||
|
|
||||||
pub fn constructor(cbk: Env.Function, state: *SessionState) !MutationObserver {
|
pub fn constructor(cbk: Env.Function, page: *Page) !MutationObserver {
|
||||||
return .{
|
return .{
|
||||||
.cbk = cbk,
|
.cbk = cbk,
|
||||||
.observed = .{},
|
.observed = .{},
|
||||||
.arena = state.arena,
|
.arena = page.arena,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ const log = @import("../../log.zig");
|
|||||||
const parser = @import("../netsurf.zig");
|
const parser = @import("../netsurf.zig");
|
||||||
const generate = @import("../../runtime/generate.zig");
|
const generate = @import("../../runtime/generate.zig");
|
||||||
|
|
||||||
const SessionState = @import("../env.zig").SessionState;
|
const Page = @import("../page.zig").Page;
|
||||||
const EventTarget = @import("event_target.zig").EventTarget;
|
const EventTarget = @import("event_target.zig").EventTarget;
|
||||||
|
|
||||||
// DOM
|
// DOM
|
||||||
@@ -278,8 +278,8 @@ pub const Node = struct {
|
|||||||
return try parser.nodeHasChildNodes(self);
|
return try parser.nodeHasChildNodes(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_childNodes(self: *parser.Node, state: *SessionState) !NodeList {
|
pub fn get_childNodes(self: *parser.Node, page: *Page) !NodeList {
|
||||||
const allocator = state.arena;
|
const allocator = page.arena;
|
||||||
var list: NodeList = .{};
|
var list: NodeList = .{};
|
||||||
|
|
||||||
var n = try parser.nodeFirstChild(self) orelse return list;
|
var n = try parser.nodeFirstChild(self) orelse return list;
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ const std = @import("std");
|
|||||||
|
|
||||||
const parser = @import("../netsurf.zig");
|
const parser = @import("../netsurf.zig");
|
||||||
const Node = @import("node.zig").Node;
|
const Node = @import("node.zig").Node;
|
||||||
const SessionState = @import("../env.zig").SessionState;
|
const Page = @import("../page.zig").Page;
|
||||||
|
|
||||||
// https://dom.spec.whatwg.org/#processinginstruction
|
// https://dom.spec.whatwg.org/#processinginstruction
|
||||||
pub const ProcessingInstruction = struct {
|
pub const ProcessingInstruction = struct {
|
||||||
@@ -39,9 +39,9 @@ pub const ProcessingInstruction = struct {
|
|||||||
// There's something wrong when we try to clone a ProcessInstruction normally.
|
// There's something wrong when we try to clone a ProcessInstruction normally.
|
||||||
// The resulting object can't be cast back into a node (it crashes). This is
|
// The resulting object can't be cast back into a node (it crashes). This is
|
||||||
// a simple workaround.
|
// a simple workaround.
|
||||||
pub fn _cloneNode(self: *parser.ProcessingInstruction, _: ?bool, state: *SessionState) !*parser.ProcessingInstruction {
|
pub fn _cloneNode(self: *parser.ProcessingInstruction, _: ?bool, page: *Page) !*parser.ProcessingInstruction {
|
||||||
return try parser.documentCreateProcessingInstruction(
|
return try parser.documentCreateProcessingInstruction(
|
||||||
@ptrCast(state.window.document),
|
@ptrCast(page.window.document),
|
||||||
try get_target(self),
|
try get_target(self),
|
||||||
(try get_data(self)) orelse "",
|
(try get_data(self)) orelse "",
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
const parser = @import("../netsurf.zig");
|
const parser = @import("../netsurf.zig");
|
||||||
const SessionState = @import("../env.zig").SessionState;
|
const Page = @import("../page.zig").Page;
|
||||||
|
|
||||||
const CharacterData = @import("character_data.zig").CharacterData;
|
const CharacterData = @import("character_data.zig").CharacterData;
|
||||||
const CDATASection = @import("cdata_section.zig").CDATASection;
|
const CDATASection = @import("cdata_section.zig").CDATASection;
|
||||||
@@ -32,9 +32,9 @@ pub const Text = struct {
|
|||||||
pub const prototype = *CharacterData;
|
pub const prototype = *CharacterData;
|
||||||
pub const subtype = .node;
|
pub const subtype = .node;
|
||||||
|
|
||||||
pub fn constructor(data: ?[]const u8, state: *const SessionState) !*parser.Text {
|
pub fn constructor(data: ?[]const u8, page: *const Page) !*parser.Text {
|
||||||
return parser.documentCreateTextNode(
|
return parser.documentCreateTextNode(
|
||||||
parser.documentHTMLToDocument(state.window.document),
|
parser.documentHTMLToDocument(page.window.document),
|
||||||
data orelse "",
|
data orelse "",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ const parser = @import("../netsurf.zig");
|
|||||||
|
|
||||||
const NodeFilter = @import("node_filter.zig").NodeFilter;
|
const NodeFilter = @import("node_filter.zig").NodeFilter;
|
||||||
const Env = @import("../env.zig").Env;
|
const Env = @import("../env.zig").Env;
|
||||||
const SessionState = @import("../env.zig").SessionState;
|
const Page = @import("../page.zig").Page;
|
||||||
|
|
||||||
// https://developer.mozilla.org/en-US/docs/Web/API/TreeWalker
|
// https://developer.mozilla.org/en-US/docs/Web/API/TreeWalker
|
||||||
pub const TreeWalker = struct {
|
pub const TreeWalker = struct {
|
||||||
|
|||||||
@@ -156,6 +156,9 @@ fn writeEscapedAttributeValue(writer: anytype, value: []const u8) !void {
|
|||||||
|
|
||||||
const testing = std.testing;
|
const testing = std.testing;
|
||||||
test "dump.writeHTML" {
|
test "dump.writeHTML" {
|
||||||
|
try parser.init();
|
||||||
|
defer parser.deinit();
|
||||||
|
|
||||||
try testWriteHTML(
|
try testWriteHTML(
|
||||||
"<div id=\"content\">Over 9000!</div>",
|
"<div id=\"content\">Over 9000!</div>",
|
||||||
"<div id=\"content\">Over 9000!</div>",
|
"<div id=\"content\">Over 9000!</div>",
|
||||||
|
|||||||
@@ -1,29 +1,24 @@
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
|
||||||
const parser = @import("netsurf.zig");
|
const Page = @import("page.zig").Page;
|
||||||
const URL = @import("../url.zig").URL;
|
|
||||||
const js = @import("../runtime/js.zig");
|
const js = @import("../runtime/js.zig");
|
||||||
const storage = @import("storage/storage.zig");
|
|
||||||
const generate = @import("../runtime/generate.zig");
|
const generate = @import("../runtime/generate.zig");
|
||||||
const Renderer = @import("renderer.zig").Renderer;
|
|
||||||
const Loop = @import("../runtime/loop.zig").Loop;
|
|
||||||
const RequestFactory = @import("../http/client.zig").RequestFactory;
|
|
||||||
|
|
||||||
const WebApis = struct {
|
const WebApis = struct {
|
||||||
// Wrapped like this for debug ergonomics.
|
// Wrapped like this for debug ergonomics.
|
||||||
// When we create our Env, a few lines down, we define it as:
|
// When we create our Env, a few lines down, we define it as:
|
||||||
// pub const Env = js.Env(*SessionState, WebApis);
|
// pub const Env = js.Env(*Page, WebApis);
|
||||||
//
|
//
|
||||||
// If there's a compile time error witht he Env, it's type will be readable,
|
// If there's a compile time error witht he Env, it's type will be readable,
|
||||||
// i.e.: runtime.js.Env(*browser.env.SessionState, browser.env.WebApis)
|
// i.e.: runtime.js.Env(*browser.env.Page, browser.env.WebApis)
|
||||||
//
|
//
|
||||||
// But if we didn't wrap it in the struct, like we once didn't, and defined
|
// But if we didn't wrap it in the struct, like we once didn't, and defined
|
||||||
// env as:
|
// env as:
|
||||||
// pub const Env = js.Env(*SessionState, Interfaces);
|
// pub const Env = js.Env(*Page, Interfaces);
|
||||||
//
|
//
|
||||||
// Because Interfaces is an anynoumous type, it doesn't have a friendly name
|
// Because Interfaces is an anynoumous type, it doesn't have a friendly name
|
||||||
// and errors would be something like:
|
// and errors would be something like:
|
||||||
// runtime.js.Env(*browser.env.SessionState, .{...A HUNDRED TYPES...})
|
// runtime.js.Env(*browser.Page, .{...A HUNDRED TYPES...})
|
||||||
pub const Interfaces = generate.Tuple(.{
|
pub const Interfaces = generate.Tuple(.{
|
||||||
@import("crypto/crypto.zig").Crypto,
|
@import("crypto/crypto.zig").Crypto,
|
||||||
@import("console/console.zig").Console,
|
@import("console/console.zig").Console,
|
||||||
@@ -44,41 +39,5 @@ const WebApis = struct {
|
|||||||
pub const JsThis = Env.JsThis;
|
pub const JsThis = Env.JsThis;
|
||||||
pub const JsObject = Env.JsObject;
|
pub const JsObject = Env.JsObject;
|
||||||
pub const Function = Env.Function;
|
pub const Function = Env.Function;
|
||||||
pub const Env = js.Env(*SessionState, WebApis);
|
pub const Env = js.Env(*Page, WebApis);
|
||||||
|
pub const Global = @import("html/window.zig").Window;
|
||||||
const Window = @import("html/window.zig").Window;
|
|
||||||
pub const Global = Window;
|
|
||||||
|
|
||||||
pub const SessionState = struct {
|
|
||||||
loop: *Loop,
|
|
||||||
url: *const URL,
|
|
||||||
window: *Window,
|
|
||||||
renderer: *Renderer,
|
|
||||||
arena: std.mem.Allocator,
|
|
||||||
cookie_jar: *storage.CookieJar,
|
|
||||||
request_factory: RequestFactory,
|
|
||||||
|
|
||||||
// dangerous, but set by the JS framework
|
|
||||||
// shorter-lived than the arena above, which
|
|
||||||
// exists for the entire rendering of the page
|
|
||||||
call_arena: std.mem.Allocator = undefined,
|
|
||||||
|
|
||||||
pub fn getOrCreateNodeWrapper(self: *SessionState, comptime T: type, node: *parser.Node) !*T {
|
|
||||||
if (try self.getNodeWrapper(T, node)) |wrap| {
|
|
||||||
return wrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
const wrap = try self.arena.create(T);
|
|
||||||
wrap.* = T{};
|
|
||||||
|
|
||||||
parser.nodeSetEmbedderData(node, wrap);
|
|
||||||
return wrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn getNodeWrapper(_: *SessionState, comptime T: type, node: *parser.Node) !?*T {
|
|
||||||
if (parser.nodeGetEmbedderData(node)) |wrap| {
|
|
||||||
return @alignCast(@ptrCast(wrap));
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
|
||||||
const parser = @import("../netsurf.zig");
|
const parser = @import("../netsurf.zig");
|
||||||
const SessionState = @import("../env.zig").SessionState;
|
const Page = @import("../page.zig").Page;
|
||||||
|
|
||||||
const Window = @import("window.zig").Window;
|
const Window = @import("window.zig").Window;
|
||||||
const Element = @import("../dom/element.zig").Element;
|
const Element = @import("../dom/element.zig").Element;
|
||||||
@@ -86,18 +86,18 @@ pub const HTMLDocument = struct {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_cookie(_: *parser.DocumentHTML, state: *SessionState) ![]const u8 {
|
pub fn get_cookie(_: *parser.DocumentHTML, page: *Page) ![]const u8 {
|
||||||
var buf: std.ArrayListUnmanaged(u8) = .{};
|
var buf: std.ArrayListUnmanaged(u8) = .{};
|
||||||
try state.cookie_jar.forRequest(&state.url.uri, buf.writer(state.arena), .{ .navigation = true });
|
try page.cookie_jar.forRequest(&page.url.uri, buf.writer(page.arena), .{ .navigation = true });
|
||||||
return buf.items;
|
return buf.items;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_cookie(_: *parser.DocumentHTML, cookie_str: []const u8, state: *SessionState) ![]const u8 {
|
pub fn set_cookie(_: *parser.DocumentHTML, cookie_str: []const u8, page: *Page) ![]const u8 {
|
||||||
// we use the cookie jar's allocator to parse the cookie because it
|
// we use the cookie jar's allocator to parse the cookie because it
|
||||||
// outlives the page's arena.
|
// outlives the page's arena.
|
||||||
const c = try Cookie.parse(state.cookie_jar.allocator, &state.url.uri, cookie_str);
|
const c = try Cookie.parse(page.cookie_jar.allocator, &page.url.uri, cookie_str);
|
||||||
errdefer c.deinit();
|
errdefer c.deinit();
|
||||||
try state.cookie_jar.add(c, std.time.timestamp());
|
try page.cookie_jar.add(c, std.time.timestamp());
|
||||||
return cookie_str;
|
return cookie_str;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,8 +110,8 @@ pub const HTMLDocument = struct {
|
|||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn _getElementsByName(self: *parser.DocumentHTML, name: []const u8, state: *SessionState) !NodeList {
|
pub fn _getElementsByName(self: *parser.DocumentHTML, name: []const u8, page: *Page) !NodeList {
|
||||||
const arena = state.arena;
|
const arena = page.arena;
|
||||||
var list: NodeList = .{};
|
var list: NodeList = .{};
|
||||||
|
|
||||||
if (name.len == 0) return list;
|
if (name.len == 0) return list;
|
||||||
@@ -130,24 +130,24 @@ pub const HTMLDocument = struct {
|
|||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_images(self: *parser.DocumentHTML, state: *SessionState) !collection.HTMLCollection {
|
pub fn get_images(self: *parser.DocumentHTML, page: *Page) !collection.HTMLCollection {
|
||||||
return try collection.HTMLCollectionByTagName(state.arena, parser.documentHTMLToNode(self), "img", false);
|
return try collection.HTMLCollectionByTagName(page.arena, parser.documentHTMLToNode(self), "img", false);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_embeds(self: *parser.DocumentHTML, state: *SessionState) !collection.HTMLCollection {
|
pub fn get_embeds(self: *parser.DocumentHTML, page: *Page) !collection.HTMLCollection {
|
||||||
return try collection.HTMLCollectionByTagName(state.arena, parser.documentHTMLToNode(self), "embed", false);
|
return try collection.HTMLCollectionByTagName(page.arena, parser.documentHTMLToNode(self), "embed", false);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_plugins(self: *parser.DocumentHTML, state: *SessionState) !collection.HTMLCollection {
|
pub fn get_plugins(self: *parser.DocumentHTML, page: *Page) !collection.HTMLCollection {
|
||||||
return get_embeds(self, state);
|
return get_embeds(self, page);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_forms(self: *parser.DocumentHTML, state: *SessionState) !collection.HTMLCollection {
|
pub fn get_forms(self: *parser.DocumentHTML, page: *Page) !collection.HTMLCollection {
|
||||||
return try collection.HTMLCollectionByTagName(state.arena, parser.documentHTMLToNode(self), "form", false);
|
return try collection.HTMLCollectionByTagName(page.arena, parser.documentHTMLToNode(self), "form", false);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_scripts(self: *parser.DocumentHTML, state: *SessionState) !collection.HTMLCollection {
|
pub fn get_scripts(self: *parser.DocumentHTML, page: *Page) !collection.HTMLCollection {
|
||||||
return try collection.HTMLCollectionByTagName(state.arena, parser.documentHTMLToNode(self), "script", false);
|
return try collection.HTMLCollectionByTagName(page.arena, parser.documentHTMLToNode(self), "script", false);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_applets(_: *parser.DocumentHTML) !collection.HTMLCollection {
|
pub fn get_applets(_: *parser.DocumentHTML) !collection.HTMLCollection {
|
||||||
@@ -182,12 +182,12 @@ pub const HTMLDocument = struct {
|
|||||||
return "off";
|
return "off";
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_defaultView(_: *parser.DocumentHTML, state: *const SessionState) *Window {
|
pub fn get_defaultView(_: *parser.DocumentHTML, page: *Page) *Window {
|
||||||
return state.window;
|
return &page.window;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_readyState(node: *parser.DocumentHTML, state: *SessionState) ![]const u8 {
|
pub fn get_readyState(node: *parser.DocumentHTML, page: *Page) ![]const u8 {
|
||||||
const self = try state.getOrCreateNodeWrapper(HTMLDocument, @ptrCast(node));
|
const self = try page.getOrCreateNodeWrapper(HTMLDocument, @ptrCast(node));
|
||||||
return @tagName(self.ready_state);
|
return @tagName(self.ready_state);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -232,41 +232,41 @@ pub const HTMLDocument = struct {
|
|||||||
// Since LightPanda requires the client to know what they are clicking on we do not return the underlying element at this moment
|
// Since LightPanda requires the client to know what they are clicking on we do not return the underlying element at this moment
|
||||||
// This can currenty only happen if the first pixel is clicked without having rendered any element. This will change when css properties are supported.
|
// This can currenty only happen if the first pixel is clicked without having rendered any element. This will change when css properties are supported.
|
||||||
// This returns an ElementUnion instead of a *Parser.Element in case the element somehow hasn't passed through the js runtime yet.
|
// This returns an ElementUnion instead of a *Parser.Element in case the element somehow hasn't passed through the js runtime yet.
|
||||||
pub fn _elementFromPoint(_: *parser.DocumentHTML, x: f32, y: f32, state: *SessionState) !?ElementUnion {
|
pub fn _elementFromPoint(_: *parser.DocumentHTML, x: f32, y: f32, page: *Page) !?ElementUnion {
|
||||||
const ix: i32 = @intFromFloat(@floor(x));
|
const ix: i32 = @intFromFloat(@floor(x));
|
||||||
const iy: i32 = @intFromFloat(@floor(y));
|
const iy: i32 = @intFromFloat(@floor(y));
|
||||||
const element = state.renderer.getElementAtPosition(ix, iy) orelse return null;
|
const element = page.renderer.getElementAtPosition(ix, iy) orelse return null;
|
||||||
// TODO if pointer-events set to none the underlying element should be returned (parser.documentGetDocumentElement(self.document);?)
|
// TODO if pointer-events set to none the underlying element should be returned (parser.documentGetDocumentElement(self.document);?)
|
||||||
return try Element.toInterface(element);
|
return try Element.toInterface(element);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns an array of all elements at the specified coordinates (relative to the viewport). The elements are ordered from the topmost to the bottommost box of the viewport.
|
// Returns an array of all elements at the specified coordinates (relative to the viewport). The elements are ordered from the topmost to the bottommost box of the viewport.
|
||||||
pub fn _elementsFromPoint(_: *parser.DocumentHTML, x: f32, y: f32, state: *SessionState) ![]ElementUnion {
|
pub fn _elementsFromPoint(_: *parser.DocumentHTML, x: f32, y: f32, page: *Page) ![]ElementUnion {
|
||||||
const ix: i32 = @intFromFloat(@floor(x));
|
const ix: i32 = @intFromFloat(@floor(x));
|
||||||
const iy: i32 = @intFromFloat(@floor(y));
|
const iy: i32 = @intFromFloat(@floor(y));
|
||||||
const element = state.renderer.getElementAtPosition(ix, iy) orelse return &.{};
|
const element = page.renderer.getElementAtPosition(ix, iy) orelse return &.{};
|
||||||
// TODO if pointer-events set to none the underlying element should be returned (parser.documentGetDocumentElement(self.document);?)
|
// TODO if pointer-events set to none the underlying element should be returned (parser.documentGetDocumentElement(self.document);?)
|
||||||
|
|
||||||
var list: std.ArrayListUnmanaged(ElementUnion) = .empty;
|
var list: std.ArrayListUnmanaged(ElementUnion) = .empty;
|
||||||
try list.ensureTotalCapacity(state.call_arena, 3);
|
try list.ensureTotalCapacity(page.call_arena, 3);
|
||||||
list.appendAssumeCapacity(try Element.toInterface(element));
|
list.appendAssumeCapacity(try Element.toInterface(element));
|
||||||
|
|
||||||
// Since we are using a flat renderer there is no hierarchy of elements. What we do know is that the element is part of the main document.
|
// Since we are using a flat renderer there is no hierarchy of elements. What we do know is that the element is part of the main document.
|
||||||
// Thus we can add the HtmlHtmlElement and it's child HTMLBodyElement to the returned list.
|
// Thus we can add the HtmlHtmlElement and it's child HTMLBodyElement to the returned list.
|
||||||
// TBD Should we instead return every parent that is an element? Note that a child does not physically need to be overlapping the parent.
|
// TBD Should we instead return every parent that is an element? Note that a child does not physically need to be overlapping the parent.
|
||||||
// Should we do a render pass on demand?
|
// Should we do a render pass on demand?
|
||||||
const doc_elem = try parser.documentGetDocumentElement(parser.documentHTMLToDocument(state.window.document)) orelse {
|
const doc_elem = try parser.documentGetDocumentElement(parser.documentHTMLToDocument(page.window.document)) orelse {
|
||||||
return list.items;
|
return list.items;
|
||||||
};
|
};
|
||||||
if (try parser.documentHTMLBody(state.window.document)) |body| {
|
if (try parser.documentHTMLBody(page.window.document)) |body| {
|
||||||
list.appendAssumeCapacity(try Element.toInterface(parser.bodyToElement(body)));
|
list.appendAssumeCapacity(try Element.toInterface(parser.bodyToElement(body)));
|
||||||
}
|
}
|
||||||
list.appendAssumeCapacity(try Element.toInterface(doc_elem));
|
list.appendAssumeCapacity(try Element.toInterface(doc_elem));
|
||||||
return list.items;
|
return list.items;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn documentIsLoaded(html_doc: *parser.DocumentHTML, state: *SessionState) !void {
|
pub fn documentIsLoaded(html_doc: *parser.DocumentHTML, page: *Page) !void {
|
||||||
const self = try state.getOrCreateNodeWrapper(HTMLDocument, @ptrCast(html_doc));
|
const self = try page.getOrCreateNodeWrapper(HTMLDocument, @ptrCast(html_doc));
|
||||||
self.ready_state = .interactive;
|
self.ready_state = .interactive;
|
||||||
|
|
||||||
const evt = try parser.eventCreate();
|
const evt = try parser.eventCreate();
|
||||||
@@ -276,8 +276,8 @@ pub const HTMLDocument = struct {
|
|||||||
_ = try parser.eventTargetDispatchEvent(parser.toEventTarget(parser.DocumentHTML, html_doc), evt);
|
_ = try parser.eventTargetDispatchEvent(parser.toEventTarget(parser.DocumentHTML, html_doc), evt);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn documentIsComplete(html_doc: *parser.DocumentHTML, state: *SessionState) !void {
|
pub fn documentIsComplete(html_doc: *parser.DocumentHTML, page: *Page) !void {
|
||||||
const self = try state.getOrCreateNodeWrapper(HTMLDocument, @ptrCast(html_doc));
|
const self = try page.getOrCreateNodeWrapper(HTMLDocument, @ptrCast(html_doc));
|
||||||
self.ready_state = .complete;
|
self.ready_state = .complete;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -383,12 +383,12 @@ test "Browser.HTML.Document" {
|
|||||||
.{ "document.readyState", "loading" },
|
.{ "document.readyState", "loading" },
|
||||||
}, .{});
|
}, .{});
|
||||||
|
|
||||||
try HTMLDocument.documentIsLoaded(runner.window.document, &runner.state);
|
try HTMLDocument.documentIsLoaded(runner.page.window.document, runner.page);
|
||||||
try runner.testCases(&.{
|
try runner.testCases(&.{
|
||||||
.{ "document.readyState", "interactive" },
|
.{ "document.readyState", "interactive" },
|
||||||
}, .{});
|
}, .{});
|
||||||
|
|
||||||
try HTMLDocument.documentIsComplete(runner.window.document, &runner.state);
|
try HTMLDocument.documentIsComplete(runner.page.window.document, runner.page);
|
||||||
try runner.testCases(&.{
|
try runner.testCases(&.{
|
||||||
.{ "document.readyState", "complete" },
|
.{ "document.readyState", "complete" },
|
||||||
}, .{});
|
}, .{});
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ const std = @import("std");
|
|||||||
|
|
||||||
const parser = @import("../netsurf.zig");
|
const parser = @import("../netsurf.zig");
|
||||||
const generate = @import("../../runtime/generate.zig");
|
const generate = @import("../../runtime/generate.zig");
|
||||||
const SessionState = @import("../env.zig").SessionState;
|
const Page = @import("../page.zig").Page;
|
||||||
|
|
||||||
const URL = @import("../url/url.zig").URL;
|
const URL = @import("../url/url.zig").URL;
|
||||||
const Node = @import("../dom/node.zig").Node;
|
const Node = @import("../dom/node.zig").Node;
|
||||||
@@ -111,8 +111,8 @@ pub const HTMLElement = struct {
|
|||||||
|
|
||||||
style: CSSStyleDeclaration = .empty,
|
style: CSSStyleDeclaration = .empty,
|
||||||
|
|
||||||
pub fn get_style(e: *parser.ElementHTML, state: *SessionState) !*CSSStyleDeclaration {
|
pub fn get_style(e: *parser.ElementHTML, page: *Page) !*CSSStyleDeclaration {
|
||||||
const self = try state.getOrCreateNodeWrapper(HTMLElement, @ptrCast(e));
|
const self = try page.getOrCreateNodeWrapper(HTMLElement, @ptrCast(e));
|
||||||
return &self.style;
|
return &self.style;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -228,26 +228,26 @@ pub const HTMLAnchorElement = struct {
|
|||||||
return try parser.nodeSetTextContent(parser.anchorToNode(self), v);
|
return try parser.nodeSetTextContent(parser.anchorToNode(self), v);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline fn url(self: *parser.Anchor, state: *SessionState) !URL {
|
inline fn url(self: *parser.Anchor, page: *Page) !URL {
|
||||||
const href = try parser.anchorGetHref(self);
|
const href = try parser.anchorGetHref(self);
|
||||||
return URL.constructor(href, null, state); // TODO inject base url
|
return URL.constructor(href, null, page); // TODO inject base url
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO return a disposable string
|
// TODO return a disposable string
|
||||||
pub fn get_origin(self: *parser.Anchor, state: *SessionState) ![]const u8 {
|
pub fn get_origin(self: *parser.Anchor, page: *Page) ![]const u8 {
|
||||||
var u = try url(self, state);
|
var u = try url(self, page);
|
||||||
return try u.get_origin(state);
|
return try u.get_origin(page);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO return a disposable string
|
// TODO return a disposable string
|
||||||
pub fn get_protocol(self: *parser.Anchor, state: *SessionState) ![]const u8 {
|
pub fn get_protocol(self: *parser.Anchor, page: *Page) ![]const u8 {
|
||||||
var u = try url(self, state);
|
var u = try url(self, page);
|
||||||
return u.get_protocol(state);
|
return u.get_protocol(page);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_protocol(self: *parser.Anchor, v: []const u8, state: *SessionState) !void {
|
pub fn set_protocol(self: *parser.Anchor, v: []const u8, page: *Page) !void {
|
||||||
const arena = state.arena;
|
const arena = page.arena;
|
||||||
var u = try url(self, state);
|
var u = try url(self, page);
|
||||||
|
|
||||||
u.uri.scheme = v;
|
u.uri.scheme = v;
|
||||||
const href = try u.toString(arena);
|
const href = try u.toString(arena);
|
||||||
@@ -255,12 +255,12 @@ pub const HTMLAnchorElement = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO return a disposable string
|
// TODO return a disposable string
|
||||||
pub fn get_host(self: *parser.Anchor, state: *SessionState) ![]const u8 {
|
pub fn get_host(self: *parser.Anchor, page: *Page) ![]const u8 {
|
||||||
var u = try url(self, state);
|
var u = try url(self, page);
|
||||||
return try u.get_host(state);
|
return try u.get_host(page);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_host(self: *parser.Anchor, v: []const u8, state: *SessionState) !void {
|
pub fn set_host(self: *parser.Anchor, v: []const u8, page: *Page) !void {
|
||||||
// search : separator
|
// search : separator
|
||||||
var p: ?u16 = null;
|
var p: ?u16 = null;
|
||||||
var h: []const u8 = undefined;
|
var h: []const u8 = undefined;
|
||||||
@@ -272,8 +272,8 @@ pub const HTMLAnchorElement = struct {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const arena = state.arena;
|
const arena = page.arena;
|
||||||
var u = try url(self, state);
|
var u = try url(self, page);
|
||||||
|
|
||||||
if (p) |pp| {
|
if (p) |pp| {
|
||||||
u.uri.host = .{ .raw = h };
|
u.uri.host = .{ .raw = h };
|
||||||
@@ -288,28 +288,28 @@ pub const HTMLAnchorElement = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO return a disposable string
|
// TODO return a disposable string
|
||||||
pub fn get_hostname(self: *parser.Anchor, state: *SessionState) ![]const u8 {
|
pub fn get_hostname(self: *parser.Anchor, page: *Page) ![]const u8 {
|
||||||
var u = try url(self, state);
|
var u = try url(self, page);
|
||||||
return try state.arena.dupe(u8, u.get_hostname());
|
return try page.arena.dupe(u8, u.get_hostname());
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_hostname(self: *parser.Anchor, v: []const u8, state: *SessionState) !void {
|
pub fn set_hostname(self: *parser.Anchor, v: []const u8, page: *Page) !void {
|
||||||
const arena = state.arena;
|
const arena = page.arena;
|
||||||
var u = try url(self, state);
|
var u = try url(self, page);
|
||||||
u.uri.host = .{ .raw = v };
|
u.uri.host = .{ .raw = v };
|
||||||
const href = try u.toString(arena);
|
const href = try u.toString(arena);
|
||||||
try parser.anchorSetHref(self, href);
|
try parser.anchorSetHref(self, href);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO return a disposable string
|
// TODO return a disposable string
|
||||||
pub fn get_port(self: *parser.Anchor, state: *SessionState) ![]const u8 {
|
pub fn get_port(self: *parser.Anchor, page: *Page) ![]const u8 {
|
||||||
var u = try url(self, state);
|
var u = try url(self, page);
|
||||||
return try u.get_port(state);
|
return try u.get_port(page);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_port(self: *parser.Anchor, v: ?[]const u8, state: *SessionState) !void {
|
pub fn set_port(self: *parser.Anchor, v: ?[]const u8, page: *Page) !void {
|
||||||
const arena = state.arena;
|
const arena = page.arena;
|
||||||
var u = try url(self, state);
|
var u = try url(self, page);
|
||||||
|
|
||||||
if (v != null and v.?.len > 0) {
|
if (v != null and v.?.len > 0) {
|
||||||
u.uri.port = try std.fmt.parseInt(u16, v.?, 10);
|
u.uri.port = try std.fmt.parseInt(u16, v.?, 10);
|
||||||
@@ -322,14 +322,14 @@ pub const HTMLAnchorElement = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO return a disposable string
|
// TODO return a disposable string
|
||||||
pub fn get_username(self: *parser.Anchor, state: *SessionState) ![]const u8 {
|
pub fn get_username(self: *parser.Anchor, page: *Page) ![]const u8 {
|
||||||
var u = try url(self, state);
|
var u = try url(self, page);
|
||||||
return try state.arena.dupe(u8, u.get_username());
|
return try page.arena.dupe(u8, u.get_username());
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_username(self: *parser.Anchor, v: ?[]const u8, state: *SessionState) !void {
|
pub fn set_username(self: *parser.Anchor, v: ?[]const u8, page: *Page) !void {
|
||||||
const arena = state.arena;
|
const arena = page.arena;
|
||||||
var u = try url(self, state);
|
var u = try url(self, page);
|
||||||
|
|
||||||
if (v) |vv| {
|
if (v) |vv| {
|
||||||
u.uri.user = .{ .raw = vv };
|
u.uri.user = .{ .raw = vv };
|
||||||
@@ -342,14 +342,14 @@ pub const HTMLAnchorElement = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO return a disposable string
|
// TODO return a disposable string
|
||||||
pub fn get_password(self: *parser.Anchor, state: *SessionState) ![]const u8 {
|
pub fn get_password(self: *parser.Anchor, page: *Page) ![]const u8 {
|
||||||
var u = try url(self, state);
|
var u = try url(self, page);
|
||||||
return try state.arena.dupe(u8, u.get_password());
|
return try page.arena.dupe(u8, u.get_password());
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_password(self: *parser.Anchor, v: ?[]const u8, state: *SessionState) !void {
|
pub fn set_password(self: *parser.Anchor, v: ?[]const u8, page: *Page) !void {
|
||||||
const arena = state.arena;
|
const arena = page.arena;
|
||||||
var u = try url(self, state);
|
var u = try url(self, page);
|
||||||
|
|
||||||
if (v) |vv| {
|
if (v) |vv| {
|
||||||
u.uri.password = .{ .raw = vv };
|
u.uri.password = .{ .raw = vv };
|
||||||
@@ -362,14 +362,14 @@ pub const HTMLAnchorElement = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO return a disposable string
|
// TODO return a disposable string
|
||||||
pub fn get_pathname(self: *parser.Anchor, state: *SessionState) ![]const u8 {
|
pub fn get_pathname(self: *parser.Anchor, page: *Page) ![]const u8 {
|
||||||
var u = try url(self, state);
|
var u = try url(self, page);
|
||||||
return try state.arena.dupe(u8, u.get_pathname());
|
return try page.arena.dupe(u8, u.get_pathname());
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_pathname(self: *parser.Anchor, v: []const u8, state: *SessionState) !void {
|
pub fn set_pathname(self: *parser.Anchor, v: []const u8, page: *Page) !void {
|
||||||
const arena = state.arena;
|
const arena = page.arena;
|
||||||
var u = try url(self, state);
|
var u = try url(self, page);
|
||||||
u.uri.path = .{ .raw = v };
|
u.uri.path = .{ .raw = v };
|
||||||
const href = try u.toString(arena);
|
const href = try u.toString(arena);
|
||||||
|
|
||||||
@@ -377,14 +377,14 @@ pub const HTMLAnchorElement = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO return a disposable string
|
// TODO return a disposable string
|
||||||
pub fn get_search(self: *parser.Anchor, state: *SessionState) ![]const u8 {
|
pub fn get_search(self: *parser.Anchor, page: *Page) ![]const u8 {
|
||||||
var u = try url(self, state);
|
var u = try url(self, page);
|
||||||
return try u.get_search(state);
|
return try u.get_search(page);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_search(self: *parser.Anchor, v: ?[]const u8, state: *SessionState) !void {
|
pub fn set_search(self: *parser.Anchor, v: ?[]const u8, page: *Page) !void {
|
||||||
const arena = state.arena;
|
const arena = page.arena;
|
||||||
var u = try url(self, state);
|
var u = try url(self, page);
|
||||||
|
|
||||||
if (v) |vv| {
|
if (v) |vv| {
|
||||||
u.uri.query = .{ .raw = vv };
|
u.uri.query = .{ .raw = vv };
|
||||||
@@ -397,14 +397,14 @@ pub const HTMLAnchorElement = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO return a disposable string
|
// TODO return a disposable string
|
||||||
pub fn get_hash(self: *parser.Anchor, state: *SessionState) ![]const u8 {
|
pub fn get_hash(self: *parser.Anchor, page: *Page) ![]const u8 {
|
||||||
var u = try url(self, state);
|
var u = try url(self, page);
|
||||||
return try u.get_hash(state);
|
return try u.get_hash(page);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_hash(self: *parser.Anchor, v: ?[]const u8, state: *SessionState) !void {
|
pub fn set_hash(self: *parser.Anchor, v: ?[]const u8, page: *Page) !void {
|
||||||
const arena = state.arena;
|
const arena = page.arena;
|
||||||
var u = try url(self, state);
|
var u = try url(self, page);
|
||||||
|
|
||||||
if (v) |vv| {
|
if (v) |vv| {
|
||||||
u.uri.fragment = .{ .raw = vv };
|
u.uri.fragment = .{ .raw = vv };
|
||||||
@@ -567,8 +567,8 @@ pub const HTMLImageElement = struct {
|
|||||||
pub const subtype = .node;
|
pub const subtype = .node;
|
||||||
pub const js_name = "Image";
|
pub const js_name = "Image";
|
||||||
|
|
||||||
pub fn constructor(width: ?u32, height: ?u32, state: *const SessionState) !*parser.Image {
|
pub fn constructor(width: ?u32, height: ?u32, page: *const Page) !*parser.Image {
|
||||||
const element = try parser.documentCreateElement(parser.documentHTMLToDocument(state.window.document), "img");
|
const element = try parser.documentCreateElement(parser.documentHTMLToDocument(page.window.document), "img");
|
||||||
const image: *parser.Image = @ptrCast(element);
|
const image: *parser.Image = @ptrCast(element);
|
||||||
if (width) |width_| try parser.imageSetWidth(image, width_);
|
if (width) |width_| try parser.imageSetWidth(image, width_);
|
||||||
if (height) |height_| try parser.imageSetHeight(image, height_);
|
if (height) |height_| try parser.imageSetHeight(image, height_);
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
// You should have received a copy of the GNU Affero General Public License
|
// 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/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
const SessionState = @import("../env.zig").SessionState;
|
const Page = @import("../page.zig").Page;
|
||||||
|
|
||||||
const URL = @import("../url/url.zig").URL;
|
const URL = @import("../url/url.zig").URL;
|
||||||
|
|
||||||
@@ -24,18 +24,18 @@ const URL = @import("../url/url.zig").URL;
|
|||||||
pub const Location = struct {
|
pub const Location = struct {
|
||||||
url: ?URL = null,
|
url: ?URL = null,
|
||||||
|
|
||||||
pub fn get_href(self: *Location, state: *SessionState) ![]const u8 {
|
pub fn get_href(self: *Location, page: *Page) ![]const u8 {
|
||||||
if (self.url) |*u| return u.get_href(state);
|
if (self.url) |*u| return u.get_href(page);
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_protocol(self: *Location, state: *SessionState) ![]const u8 {
|
pub fn get_protocol(self: *Location, page: *Page) ![]const u8 {
|
||||||
if (self.url) |*u| return u.get_protocol(state);
|
if (self.url) |*u| return u.get_protocol(page);
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_host(self: *Location, state: *SessionState) ![]const u8 {
|
pub fn get_host(self: *Location, page: *Page) ![]const u8 {
|
||||||
if (self.url) |*u| return u.get_host(state);
|
if (self.url) |*u| return u.get_host(page);
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -44,8 +44,8 @@ pub const Location = struct {
|
|||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_port(self: *Location, state: *SessionState) ![]const u8 {
|
pub fn get_port(self: *Location, page: *Page) ![]const u8 {
|
||||||
if (self.url) |*u| return u.get_port(state);
|
if (self.url) |*u| return u.get_port(page);
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,18 +54,18 @@ pub const Location = struct {
|
|||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_search(self: *Location, state: *SessionState) ![]const u8 {
|
pub fn get_search(self: *Location, page: *Page) ![]const u8 {
|
||||||
if (self.url) |*u| return u.get_search(state);
|
if (self.url) |*u| return u.get_search(page);
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_hash(self: *Location, state: *SessionState) ![]const u8 {
|
pub fn get_hash(self: *Location, page: *Page) ![]const u8 {
|
||||||
if (self.url) |*u| return u.get_hash(state);
|
if (self.url) |*u| return u.get_hash(page);
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_origin(self: *Location, state: *SessionState) ![]const u8 {
|
pub fn get_origin(self: *Location, page: *Page) ![]const u8 {
|
||||||
if (self.url) |*u| return u.get_origin(state);
|
if (self.url) |*u| return u.get_origin(page);
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,8 +82,8 @@ pub const Location = struct {
|
|||||||
// TODO
|
// TODO
|
||||||
pub fn _reload(_: *Location) !void {}
|
pub fn _reload(_: *Location) !void {}
|
||||||
|
|
||||||
pub fn _toString(self: *Location, state: *SessionState) ![]const u8 {
|
pub fn _toString(self: *Location, page: *Page) ![]const u8 {
|
||||||
return try self.get_href(state);
|
return try self.get_href(page);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ const std = @import("std");
|
|||||||
|
|
||||||
const parser = @import("../netsurf.zig");
|
const parser = @import("../netsurf.zig");
|
||||||
const HTMLElement = @import("elements.zig").HTMLElement;
|
const HTMLElement = @import("elements.zig").HTMLElement;
|
||||||
const SessionState = @import("../env.zig").SessionState;
|
const Page = @import("../page.zig").Page;
|
||||||
|
|
||||||
pub const HTMLSelectElement = struct {
|
pub const HTMLSelectElement = struct {
|
||||||
pub const Self = parser.Select;
|
pub const Self = parser.Select;
|
||||||
@@ -69,8 +69,8 @@ pub const HTMLSelectElement = struct {
|
|||||||
return parser.selectSetMultiple(select, multiple);
|
return parser.selectSetMultiple(select, multiple);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_selectedIndex(select: *parser.Select, state: *SessionState) !i32 {
|
pub fn get_selectedIndex(select: *parser.Select, page: *Page) !i32 {
|
||||||
const self = try state.getOrCreateNodeWrapper(HTMLSelectElement, @ptrCast(select));
|
const self = try page.getOrCreateNodeWrapper(HTMLSelectElement, @ptrCast(select));
|
||||||
const selected_index = try parser.selectGetSelectedIndex(select);
|
const selected_index = try parser.selectGetSelectedIndex(select);
|
||||||
|
|
||||||
// See the explicit_index_set field documentation
|
// See the explicit_index_set field documentation
|
||||||
@@ -88,8 +88,8 @@ pub const HTMLSelectElement = struct {
|
|||||||
|
|
||||||
// Libdom's dom_html_select_select_set_selected_index will crash if index
|
// Libdom's dom_html_select_select_set_selected_index will crash if index
|
||||||
// is out of range, and it doesn't properly unset options
|
// is out of range, and it doesn't properly unset options
|
||||||
pub fn set_selectedIndex(select: *parser.Select, index: i32, state: *SessionState) !void {
|
pub fn set_selectedIndex(select: *parser.Select, index: i32, page: *Page) !void {
|
||||||
var self = try state.getOrCreateNodeWrapper(HTMLSelectElement, @ptrCast(select));
|
var self = try page.getOrCreateNodeWrapper(HTMLSelectElement, @ptrCast(select));
|
||||||
self.explicit_index_set = true;
|
self.explicit_index_set = true;
|
||||||
|
|
||||||
const options = try parser.selectGetOptions(select);
|
const options = try parser.selectGetOptions(select);
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ const std = @import("std");
|
|||||||
const log = @import("../../log.zig");
|
const log = @import("../../log.zig");
|
||||||
const parser = @import("../netsurf.zig");
|
const parser = @import("../netsurf.zig");
|
||||||
const Function = @import("../env.zig").Function;
|
const Function = @import("../env.zig").Function;
|
||||||
const SessionState = @import("../env.zig").SessionState;
|
const Page = @import("../page.zig").Page;
|
||||||
const Loop = @import("../../runtime/loop.zig").Loop;
|
const Loop = @import("../../runtime/loop.zig").Loop;
|
||||||
|
|
||||||
const Navigator = @import("navigator.zig").Navigator;
|
const Navigator = @import("navigator.zig").Navigator;
|
||||||
@@ -130,15 +130,15 @@ pub const Window = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// The interior height of the window in pixels, including the height of the horizontal scroll bar, if present.
|
// The interior height of the window in pixels, including the height of the horizontal scroll bar, if present.
|
||||||
pub fn get_innerHeight(_: *Window, state: *SessionState) u32 {
|
pub fn get_innerHeight(_: *Window, page: *Page) u32 {
|
||||||
// We do not have scrollbars or padding so this is the same as Element.clientHeight
|
// We do not have scrollbars or padding so this is the same as Element.clientHeight
|
||||||
return state.renderer.height();
|
return page.renderer.height();
|
||||||
}
|
}
|
||||||
|
|
||||||
// The interior width of the window in pixels. That includes the width of the vertical scroll bar, if one is present.
|
// The interior width of the window in pixels. That includes the width of the vertical scroll bar, if one is present.
|
||||||
pub fn get_innerWidth(_: *Window, state: *SessionState) u32 {
|
pub fn get_innerWidth(_: *Window, page: *Page) u32 {
|
||||||
// We do not have scrollbars or padding so this is the same as Element.clientWidth
|
// We do not have scrollbars or padding so this is the same as Element.clientWidth
|
||||||
return state.renderer.width();
|
return page.renderer.width();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_name(self: *Window) []const u8 {
|
pub fn get_name(self: *Window) []const u8 {
|
||||||
@@ -182,40 +182,40 @@ pub const Window = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO handle callback arguments.
|
// TODO handle callback arguments.
|
||||||
pub fn _setTimeout(self: *Window, cbk: Function, delay: ?u32, state: *SessionState) !u32 {
|
pub fn _setTimeout(self: *Window, cbk: Function, delay: ?u32, page: *Page) !u32 {
|
||||||
return self.createTimeout(cbk, delay, state, false);
|
return self.createTimeout(cbk, delay, page, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO handle callback arguments.
|
// TODO handle callback arguments.
|
||||||
pub fn _setInterval(self: *Window, cbk: Function, delay: ?u32, state: *SessionState) !u32 {
|
pub fn _setInterval(self: *Window, cbk: Function, delay: ?u32, page: *Page) !u32 {
|
||||||
return self.createTimeout(cbk, delay, state, true);
|
return self.createTimeout(cbk, delay, page, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn _clearTimeout(self: *Window, id: u32, state: *SessionState) !void {
|
pub fn _clearTimeout(self: *Window, id: u32, page: *Page) !void {
|
||||||
const kv = self.timers.fetchRemove(id) orelse return;
|
const kv = self.timers.fetchRemove(id) orelse return;
|
||||||
try state.loop.cancel(kv.value.loop_id);
|
try page.loop.cancel(kv.value.loop_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn _clearInterval(self: *Window, id: u32, state: *SessionState) !void {
|
pub fn _clearInterval(self: *Window, id: u32, page: *Page) !void {
|
||||||
const kv = self.timers.fetchRemove(id) orelse return;
|
const kv = self.timers.fetchRemove(id) orelse return;
|
||||||
try state.loop.cancel(kv.value.loop_id);
|
try page.loop.cancel(kv.value.loop_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn _matchMedia(_: *const Window, media: []const u8, state: *SessionState) !MediaQueryList {
|
pub fn _matchMedia(_: *const Window, media: []const u8, page: *Page) !MediaQueryList {
|
||||||
return .{
|
return .{
|
||||||
.matches = false, // TODO?
|
.matches = false, // TODO?
|
||||||
.media = try state.arena.dupe(u8, media),
|
.media = try page.arena.dupe(u8, media),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn createTimeout(self: *Window, cbk: Function, delay_: ?u32, state: *SessionState, comptime repeat: bool) !u32 {
|
fn createTimeout(self: *Window, cbk: Function, delay_: ?u32, page: *Page, comptime repeat: bool) !u32 {
|
||||||
if (self.timers.count() > 512) {
|
if (self.timers.count() > 512) {
|
||||||
return error.TooManyTimeout;
|
return error.TooManyTimeout;
|
||||||
}
|
}
|
||||||
const timer_id = self.timer_id +% 1;
|
const timer_id = self.timer_id +% 1;
|
||||||
self.timer_id = timer_id;
|
self.timer_id = timer_id;
|
||||||
|
|
||||||
const arena = state.arena;
|
const arena = page.arena;
|
||||||
|
|
||||||
const gop = try self.timers.getOrPut(arena, timer_id);
|
const gop = try self.timers.getOrPut(arena, timer_id);
|
||||||
if (gop.found_existing) {
|
if (gop.found_existing) {
|
||||||
@@ -235,7 +235,7 @@ pub const Window = struct {
|
|||||||
.node = .{ .func = TimerCallback.run },
|
.node = .{ .func = TimerCallback.run },
|
||||||
.repeat = if (repeat) delay else null,
|
.repeat = if (repeat) delay else null,
|
||||||
};
|
};
|
||||||
callback.loop_id = try state.loop.timeout(delay, &callback.node);
|
callback.loop_id = try page.loop.timeout(delay, &callback.node);
|
||||||
|
|
||||||
gop.value_ptr.* = callback;
|
gop.value_ptr.* = callback;
|
||||||
return timer_id;
|
return timer_id;
|
||||||
|
|||||||
@@ -22,16 +22,16 @@ const builtin = @import("builtin");
|
|||||||
const Allocator = std.mem.Allocator;
|
const Allocator = std.mem.Allocator;
|
||||||
|
|
||||||
const Dump = @import("dump.zig");
|
const Dump = @import("dump.zig");
|
||||||
|
const Env = @import("env.zig").Env;
|
||||||
const Mime = @import("mime.zig").Mime;
|
const Mime = @import("mime.zig").Mime;
|
||||||
const DataURI = @import("datauri.zig").DataURI;
|
const DataURI = @import("datauri.zig").DataURI;
|
||||||
const Session = @import("session.zig").Session;
|
const Session = @import("session.zig").Session;
|
||||||
const Renderer = @import("renderer.zig").Renderer;
|
const Renderer = @import("renderer.zig").Renderer;
|
||||||
const SessionState = @import("env.zig").SessionState;
|
|
||||||
const Window = @import("html/window.zig").Window;
|
const Window = @import("html/window.zig").Window;
|
||||||
const Walker = @import("dom/walker.zig").WalkerDepthFirst;
|
const Walker = @import("dom/walker.zig").WalkerDepthFirst;
|
||||||
const Env = @import("env.zig").Env;
|
|
||||||
const Loop = @import("../runtime/loop.zig").Loop;
|
const Loop = @import("../runtime/loop.zig").Loop;
|
||||||
const HTMLDocument = @import("html/document.zig").HTMLDocument;
|
const HTMLDocument = @import("html/document.zig").HTMLDocument;
|
||||||
|
const RequestFactory = @import("../http/client.zig").RequestFactory;
|
||||||
|
|
||||||
const URL = @import("../url.zig").URL;
|
const URL = @import("../url.zig").URL;
|
||||||
|
|
||||||
@@ -48,13 +48,22 @@ const polyfill = @import("polyfill/polyfill.zig");
|
|||||||
// The page handle all its memory in an arena allocator. The arena is reseted
|
// The page handle all its memory in an arena allocator. The arena is reseted
|
||||||
// when end() is called.
|
// when end() is called.
|
||||||
pub const Page = struct {
|
pub const Page = struct {
|
||||||
|
// Our event loop
|
||||||
|
loop: *Loop,
|
||||||
|
|
||||||
|
cookie_jar: *storage.CookieJar,
|
||||||
|
|
||||||
|
// Pre-configured http/cilent.zig used to make HTTP requests.
|
||||||
|
request_factory: RequestFactory,
|
||||||
|
|
||||||
session: *Session,
|
session: *Session,
|
||||||
|
|
||||||
// an arena with a lifetime for the entire duration of the page
|
// An arena with a lifetime for the entire duration of the page
|
||||||
arena: Allocator,
|
arena: Allocator,
|
||||||
|
|
||||||
// Gets injected into any WebAPI method that needs it
|
// Managed by the JS runtime, meant to have a much shorter life than the
|
||||||
state: SessionState,
|
// above arena. It should only be used by WebAPIs.
|
||||||
|
call_arena: Allocator,
|
||||||
|
|
||||||
// Serves are the root object of our JavaScript environment
|
// Serves are the root object of our JavaScript environment
|
||||||
window: Window,
|
window: Window,
|
||||||
@@ -62,6 +71,8 @@ pub const Page = struct {
|
|||||||
// The URL of the page
|
// The URL of the page
|
||||||
url: URL,
|
url: URL,
|
||||||
|
|
||||||
|
// If the body of the main page isn't HTML, we capture its raw bytes here
|
||||||
|
// (currently, this is only useful in fetch mode with the --dump option)
|
||||||
raw_data: ?[]const u8,
|
raw_data: ?[]const u8,
|
||||||
|
|
||||||
renderer: Renderer,
|
renderer: Renderer,
|
||||||
@@ -70,6 +81,8 @@ pub const Page = struct {
|
|||||||
|
|
||||||
window_clicked_event_node: parser.EventNode,
|
window_clicked_event_node: parser.EventNode,
|
||||||
|
|
||||||
|
// Our JavaScript context for this specific page. This is what we use to
|
||||||
|
// execute any JavaScript
|
||||||
scope: *Env.Scope,
|
scope: *Env.Scope,
|
||||||
|
|
||||||
// List of modules currently fetched/loaded.
|
// List of modules currently fetched/loaded.
|
||||||
@@ -87,21 +100,17 @@ pub const Page = struct {
|
|||||||
.raw_data = null,
|
.raw_data = null,
|
||||||
.url = URL.empty,
|
.url = URL.empty,
|
||||||
.session = session,
|
.session = session,
|
||||||
|
.call_arena = undefined,
|
||||||
|
.loop = browser.app.loop,
|
||||||
.renderer = Renderer.init(arena),
|
.renderer = Renderer.init(arena),
|
||||||
|
.cookie_jar = &session.cookie_jar,
|
||||||
.microtask_node = .{ .func = microtaskCallback },
|
.microtask_node = .{ .func = microtaskCallback },
|
||||||
.window_clicked_event_node = .{ .func = windowClicked },
|
.window_clicked_event_node = .{ .func = windowClicked },
|
||||||
.state = .{
|
.request_factory = browser.http_client.requestFactory(browser.notification),
|
||||||
.arena = arena,
|
.scope = undefined,
|
||||||
.url = &self.url,
|
|
||||||
.window = &self.window,
|
|
||||||
.renderer = &self.renderer,
|
|
||||||
.loop = browser.app.loop,
|
|
||||||
.cookie_jar = &session.cookie_jar,
|
|
||||||
.request_factory = browser.http_client.requestFactory(browser.notification),
|
|
||||||
},
|
|
||||||
.scope = try session.executor.startScope(&self.window, &self.state, self, true),
|
|
||||||
.module_map = .empty,
|
.module_map = .empty,
|
||||||
};
|
};
|
||||||
|
self.scope = try session.executor.startScope(&self.window, self, self, true);
|
||||||
|
|
||||||
// load polyfills
|
// load polyfills
|
||||||
try polyfill.load(self.arena, self.scope);
|
try polyfill.load(self.arena, self.scope);
|
||||||
@@ -180,7 +189,7 @@ pub const Page = struct {
|
|||||||
try self.loadHTMLDoc(fbs.reader(), "utf-8");
|
try self.loadHTMLDoc(fbs.reader(), "utf-8");
|
||||||
// We do not processHTMLDoc here as we know we don't have any scripts
|
// We do not processHTMLDoc here as we know we don't have any scripts
|
||||||
// This assumption may be false when CDP Page.addScriptToEvaluateOnNewDocument is implemented
|
// This assumption may be false when CDP Page.addScriptToEvaluateOnNewDocument is implemented
|
||||||
try HTMLDocument.documentIsComplete(self.window.document, &self.state);
|
try HTMLDocument.documentIsComplete(self.window.document, self);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -243,7 +252,7 @@ pub const Page = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// https://html.spec.whatwg.org/#read-html
|
// https://html.spec.whatwg.org/#read-html
|
||||||
fn loadHTMLDoc(self: *Page, reader: anytype, charset: []const u8) !void {
|
pub fn loadHTMLDoc(self: *Page, reader: anytype, charset: []const u8) !void {
|
||||||
const ccharset = try self.arena.dupeZ(u8, charset);
|
const ccharset = try self.arena.dupeZ(u8, charset);
|
||||||
|
|
||||||
const html_doc = try parser.documentHTMLParse(reader, ccharset);
|
const html_doc = try parser.documentHTMLParse(reader, ccharset);
|
||||||
@@ -352,7 +361,7 @@ pub const Page = struct {
|
|||||||
// at the point where all subresources apart from async script elements
|
// at the point where all subresources apart from async script elements
|
||||||
// have loaded.
|
// have loaded.
|
||||||
// https://html.spec.whatwg.org/#reporting-document-loading-status
|
// https://html.spec.whatwg.org/#reporting-document-loading-status
|
||||||
try HTMLDocument.documentIsLoaded(html_doc, &self.state);
|
try HTMLDocument.documentIsLoaded(html_doc, self);
|
||||||
|
|
||||||
// eval async scripts.
|
// eval async scripts.
|
||||||
for (async_scripts.items) |script| {
|
for (async_scripts.items) |script| {
|
||||||
@@ -363,7 +372,7 @@ pub const Page = struct {
|
|||||||
try parser.documentHTMLSetCurrentScript(html_doc, null);
|
try parser.documentHTMLSetCurrentScript(html_doc, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
try HTMLDocument.documentIsComplete(html_doc, &self.state);
|
try HTMLDocument.documentIsComplete(html_doc, self);
|
||||||
|
|
||||||
// dispatch window.load event
|
// dispatch window.load event
|
||||||
const loadevt = try parser.eventCreate();
|
const loadevt = try parser.eventCreate();
|
||||||
@@ -470,7 +479,7 @@ pub const Page = struct {
|
|||||||
errdefer request.deinit();
|
errdefer request.deinit();
|
||||||
|
|
||||||
var arr: std.ArrayListUnmanaged(u8) = .{};
|
var arr: std.ArrayListUnmanaged(u8) = .{};
|
||||||
try self.state.cookie_jar.forRequest(&url.uri, arr.writer(self.arena), opts);
|
try self.cookie_jar.forRequest(&url.uri, arr.writer(self.arena), opts);
|
||||||
|
|
||||||
if (arr.items.len > 0) {
|
if (arr.items.len > 0) {
|
||||||
try request.addHeader("Cookie", arr.items, .{});
|
try request.addHeader("Cookie", arr.items, .{});
|
||||||
@@ -534,126 +543,145 @@ pub const Page = struct {
|
|||||||
.session = self.session,
|
.session = self.session,
|
||||||
.href = try arena.dupe(u8, href),
|
.href = try arena.dupe(u8, href),
|
||||||
};
|
};
|
||||||
_ = try self.state.loop.timeout(0, &navi.navigate_node);
|
_ = try self.loop.timeout(0, &navi.navigate_node);
|
||||||
},
|
},
|
||||||
else => {},
|
else => {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const DelayedNavigation = struct {
|
pub fn getOrCreateNodeWrapper(self: *Page, comptime T: type, node: *parser.Node) !*T {
|
||||||
navigate_node: Loop.CallbackNode = .{ .func = DelayedNavigation.delayNavigate },
|
if (try self.getNodeWrapper(T, node)) |wrap| {
|
||||||
session: *Session,
|
return wrap;
|
||||||
href: []const u8,
|
|
||||||
|
|
||||||
fn delayNavigate(node: *Loop.CallbackNode, repeat_delay: *?u63) void {
|
|
||||||
_ = repeat_delay;
|
|
||||||
const self: *DelayedNavigation = @fieldParentPtr("navigate_node", node);
|
|
||||||
self.session.pageNavigate(self.href) catch |err| {
|
|
||||||
// TODO: should we trigger a specific event here?
|
|
||||||
log.err(.page, "delayed navigation error", .{ .err = err });
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const wrap = try self.arena.create(T);
|
||||||
|
wrap.* = T{};
|
||||||
|
|
||||||
|
parser.nodeSetEmbedderData(node, wrap);
|
||||||
|
return wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getNodeWrapper(_: *Page, comptime T: type, node: *parser.Node) !?*T {
|
||||||
|
if (parser.nodeGetEmbedderData(node)) |wrap| {
|
||||||
|
return @alignCast(@ptrCast(wrap));
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const DelayedNavigation = struct {
|
||||||
|
navigate_node: Loop.CallbackNode = .{ .func = DelayedNavigation.delay_navigate },
|
||||||
|
session: *Session,
|
||||||
|
href: []const u8,
|
||||||
|
|
||||||
|
fn delay_navigate(node: *Loop.CallbackNode, repeat_delay: *?u63) void {
|
||||||
|
_ = repeat_delay;
|
||||||
|
const self: *DelayedNavigation = @fieldParentPtr("navigate_node", node);
|
||||||
|
self.session.pageNavigate(self.href) catch |err| {
|
||||||
|
// TODO: should we trigger a specific event here?
|
||||||
|
log.err(.page, "delayed navigation error", .{ .err = err });
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const Script = struct {
|
||||||
|
kind: Kind,
|
||||||
|
is_async: bool,
|
||||||
|
is_defer: bool,
|
||||||
|
src: ?[]const u8,
|
||||||
|
element: *parser.Element,
|
||||||
|
// The javascript to load after we successfully load the script
|
||||||
|
onload: ?[]const u8,
|
||||||
|
|
||||||
|
// The javascript to load if we have an error executing the script
|
||||||
|
// For now, we ignore this, since we still have a lot of errors that we
|
||||||
|
// shouldn't
|
||||||
|
//onerror: ?[]const u8,
|
||||||
|
|
||||||
|
const Kind = enum {
|
||||||
|
module,
|
||||||
|
javascript,
|
||||||
};
|
};
|
||||||
|
|
||||||
const Script = struct {
|
fn init(e: *parser.Element) !?Script {
|
||||||
kind: Kind,
|
// ignore non-script tags
|
||||||
is_async: bool,
|
const tag = try parser.elementHTMLGetTagType(@as(*parser.ElementHTML, @ptrCast(e)));
|
||||||
is_defer: bool,
|
if (tag != .script) {
|
||||||
src: ?[]const u8,
|
|
||||||
element: *parser.Element,
|
|
||||||
// The javascript to load after we successfully load the script
|
|
||||||
onload: ?[]const u8,
|
|
||||||
|
|
||||||
// The javascript to load if we have an error executing the script
|
|
||||||
// For now, we ignore this, since we still have a lot of errors that we
|
|
||||||
// shouldn't
|
|
||||||
//onerror: ?[]const u8,
|
|
||||||
|
|
||||||
const Kind = enum {
|
|
||||||
module,
|
|
||||||
javascript,
|
|
||||||
};
|
|
||||||
|
|
||||||
fn init(e: *parser.Element) !?Script {
|
|
||||||
// ignore non-script tags
|
|
||||||
const tag = try parser.elementHTMLGetTagType(@as(*parser.ElementHTML, @ptrCast(e)));
|
|
||||||
if (tag != .script) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (try parser.elementGetAttribute(e, "nomodule") != null) {
|
|
||||||
// these scripts should only be loaded if we don't support modules
|
|
||||||
// but since we do support modules, we can just skip them.
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const kind = parseKind(try parser.elementGetAttribute(e, "type")) orelse {
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
|
|
||||||
return .{
|
|
||||||
.kind = kind,
|
|
||||||
.element = e,
|
|
||||||
.src = try parser.elementGetAttribute(e, "src"),
|
|
||||||
.onload = try parser.elementGetAttribute(e, "onload"),
|
|
||||||
.is_async = try parser.elementGetAttribute(e, "async") != null,
|
|
||||||
.is_defer = try parser.elementGetAttribute(e, "defer") != null,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// > type
|
|
||||||
// > Attribute is not set (default), an empty string, or a JavaScript MIME
|
|
||||||
// > type indicates that the script is a "classic script", containing
|
|
||||||
// > JavaScript code.
|
|
||||||
// https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#attribute_is_not_set_default_an_empty_string_or_a_javascript_mime_type
|
|
||||||
fn parseKind(script_type_: ?[]const u8) ?Kind {
|
|
||||||
const script_type = script_type_ orelse return .javascript;
|
|
||||||
if (script_type.len == 0) {
|
|
||||||
return .javascript;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (std.mem.eql(u8, script_type, "application/javascript")) return .javascript;
|
|
||||||
if (std.mem.eql(u8, script_type, "text/javascript")) return .javascript;
|
|
||||||
if (std.mem.eql(u8, script_type, "module")) return .module;
|
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn eval(self: *const Script, page: *Page, body: []const u8) !void {
|
if (try parser.elementGetAttribute(e, "nomodule") != null) {
|
||||||
var try_catch: Env.TryCatch = undefined;
|
// these scripts should only be loaded if we don't support modules
|
||||||
try_catch.init(page.scope);
|
// but since we do support modules, we can just skip them.
|
||||||
defer try_catch.deinit();
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
const src = self.src orelse "inline";
|
const kind = parseKind(try parser.elementGetAttribute(e, "type")) orelse {
|
||||||
const res = switch (self.kind) {
|
return null;
|
||||||
.javascript => page.scope.exec(body, src),
|
};
|
||||||
.module => blk: {
|
|
||||||
switch (try page.scope.module(body, src)) {
|
return .{
|
||||||
.value => |v| break :blk v,
|
.kind = kind,
|
||||||
.exception => |e| {
|
.element = e,
|
||||||
log.warn(.page, "eval module", .{ .src = src, .err = try e.exception(page.arena) });
|
.src = try parser.elementGetAttribute(e, "src"),
|
||||||
return error.JsErr;
|
.onload = try parser.elementGetAttribute(e, "onload"),
|
||||||
},
|
.is_async = try parser.elementGetAttribute(e, "async") != null,
|
||||||
}
|
.is_defer = try parser.elementGetAttribute(e, "defer") != null,
|
||||||
},
|
};
|
||||||
} catch {
|
}
|
||||||
|
|
||||||
|
// > type
|
||||||
|
// > Attribute is not set (default), an empty string, or a JavaScript MIME
|
||||||
|
// > type indicates that the script is a "classic script", containing
|
||||||
|
// > JavaScript code.
|
||||||
|
// https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#attribute_is_not_set_default_an_empty_string_or_a_javascript_mime_type
|
||||||
|
fn parseKind(script_type_: ?[]const u8) ?Kind {
|
||||||
|
const script_type = script_type_ orelse return .javascript;
|
||||||
|
if (script_type.len == 0) {
|
||||||
|
return .javascript;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (std.mem.eql(u8, script_type, "application/javascript")) return .javascript;
|
||||||
|
if (std.mem.eql(u8, script_type, "text/javascript")) return .javascript;
|
||||||
|
if (std.mem.eql(u8, script_type, "module")) return .module;
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn eval(self: *const Script, page: *Page, body: []const u8) !void {
|
||||||
|
var try_catch: Env.TryCatch = undefined;
|
||||||
|
try_catch.init(page.scope);
|
||||||
|
defer try_catch.deinit();
|
||||||
|
|
||||||
|
const src = self.src orelse "inline";
|
||||||
|
const res = switch (self.kind) {
|
||||||
|
.javascript => page.scope.exec(body, src),
|
||||||
|
.module => blk: {
|
||||||
|
switch (try page.scope.module(body, src)) {
|
||||||
|
.value => |v| break :blk v,
|
||||||
|
.exception => |e| {
|
||||||
|
log.warn(.page, "eval module", .{ .src = src, .err = try e.exception(page.arena) });
|
||||||
|
return error.JsErr;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
} catch {
|
||||||
|
if (try try_catch.err(page.arena)) |msg| {
|
||||||
|
log.warn(.page, "eval script", .{ .src = src, .err = msg });
|
||||||
|
}
|
||||||
|
return error.JsErr;
|
||||||
|
};
|
||||||
|
_ = res;
|
||||||
|
|
||||||
|
if (self.onload) |onload| {
|
||||||
|
_ = page.scope.exec(onload, "script_on_load") catch {
|
||||||
if (try try_catch.err(page.arena)) |msg| {
|
if (try try_catch.err(page.arena)) |msg| {
|
||||||
log.warn(.page, "eval script", .{ .src = src, .err = msg });
|
log.warn(.page, "eval onload", .{ .src = src, .err = msg });
|
||||||
}
|
}
|
||||||
return error.JsErr;
|
return error.JsErr;
|
||||||
};
|
};
|
||||||
_ = res;
|
|
||||||
|
|
||||||
if (self.onload) |onload| {
|
|
||||||
_ = page.scope.exec(onload, "script_on_load") catch {
|
|
||||||
if (try try_catch.err(page.arena)) |msg| {
|
|
||||||
log.warn(.page, "eval onload", .{ .src = src, .err = msg });
|
|
||||||
}
|
|
||||||
return error.JsErr;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const NavigateReason = enum {
|
pub const NavigateReason = enum {
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ test "Browser.fetch" {
|
|||||||
var runner = try testing.jsRunner(testing.tracking_allocator, .{});
|
var runner = try testing.jsRunner(testing.tracking_allocator, .{});
|
||||||
defer runner.deinit();
|
defer runner.deinit();
|
||||||
|
|
||||||
try @import("polyfill.zig").load(testing.allocator, runner.scope);
|
try @import("polyfill.zig").load(testing.allocator, runner.page.scope);
|
||||||
|
|
||||||
try runner.testCases(&.{
|
try runner.testCases(&.{
|
||||||
.{
|
.{
|
||||||
@@ -31,16 +31,16 @@ test "Browser.fetch" {
|
|||||||
.{ "ok", "true" },
|
.{ "ok", "true" },
|
||||||
}, .{});
|
}, .{});
|
||||||
|
|
||||||
try runner.testCases(&.{
|
// try runner.testCases(&.{
|
||||||
.{
|
// .{
|
||||||
\\ var ok2 = false;
|
// \\ var ok2 = false;
|
||||||
\\ const request2 = new Request("http://127.0.0.1:9582/loader");
|
// \\ const request2 = new Request("http://127.0.0.1:9582/loader");
|
||||||
\\ (async function () { resp = await fetch(request2); ok2 = resp.ok; }());
|
// \\ (async function () { resp = await fetch(request2); ok2 = resp.ok; }());
|
||||||
\\ false;
|
// \\ false;
|
||||||
,
|
// ,
|
||||||
"false",
|
// "false",
|
||||||
},
|
// },
|
||||||
// all events have been resolved.
|
// // all events have been resolved.
|
||||||
.{ "ok2", "true" },
|
// .{ "ok2", "true" },
|
||||||
}, .{});
|
// }, .{});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -231,7 +231,7 @@ fn areCookiesEqual(a: *const Cookie, b: *const Cookie) bool {
|
|||||||
|
|
||||||
fn areSameSite(origin_uri_: ?*const std.Uri, target_host: []const u8) !bool {
|
fn areSameSite(origin_uri_: ?*const std.Uri, target_host: []const u8) !bool {
|
||||||
const origin_uri = origin_uri_ orelse return true;
|
const origin_uri = origin_uri_ orelse return true;
|
||||||
const origin_host = (origin_uri.host orelse return error.InvalidURI).percent_encoded;
|
const origin_host = (origin_uri.host orelse return error.InvalidURI333).percent_encoded;
|
||||||
|
|
||||||
// common case
|
// common case
|
||||||
if (std.mem.eql(u8, target_host, origin_host)) {
|
if (std.mem.eql(u8, target_host, origin_host)) {
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const SessionState = @import("../env.zig").SessionState;
|
const Page = @import("../page.zig").Page;
|
||||||
|
|
||||||
const query = @import("query.zig");
|
const query = @import("query.zig");
|
||||||
|
|
||||||
@@ -47,9 +47,9 @@ pub const URL = struct {
|
|||||||
pub fn constructor(
|
pub fn constructor(
|
||||||
url: []const u8,
|
url: []const u8,
|
||||||
base: ?[]const u8,
|
base: ?[]const u8,
|
||||||
state: *SessionState,
|
page: *Page,
|
||||||
) !URL {
|
) !URL {
|
||||||
const arena = state.arena;
|
const arena = page.arena;
|
||||||
const raw = try std.mem.concat(arena, u8, &[_][]const u8{ url, base orelse "" });
|
const raw = try std.mem.concat(arena, u8, &[_][]const u8{ url, base orelse "" });
|
||||||
|
|
||||||
const uri = std.Uri.parse(raw) catch return error.TypeError;
|
const uri = std.Uri.parse(raw) catch return error.TypeError;
|
||||||
@@ -66,8 +66,8 @@ pub const URL = struct {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_origin(self: *URL, state: *SessionState) ![]const u8 {
|
pub fn get_origin(self: *URL, page: *Page) ![]const u8 {
|
||||||
var buf = std.ArrayList(u8).init(state.arena);
|
var buf = std.ArrayList(u8).init(page.arena);
|
||||||
try self.uri.writeToStream(.{
|
try self.uri.writeToStream(.{
|
||||||
.scheme = true,
|
.scheme = true,
|
||||||
.authentication = false,
|
.authentication = false,
|
||||||
@@ -82,8 +82,8 @@ pub const URL = struct {
|
|||||||
// get_href returns the URL by writing all its components.
|
// get_href returns the URL by writing all its components.
|
||||||
// The query is replaced by a dump of search params.
|
// The query is replaced by a dump of search params.
|
||||||
//
|
//
|
||||||
pub fn get_href(self: *URL, state: *SessionState) ![]const u8 {
|
pub fn get_href(self: *URL, page: *Page) ![]const u8 {
|
||||||
const arena = state.arena;
|
const arena = page.arena;
|
||||||
// retrieve the query search from search_params.
|
// retrieve the query search from search_params.
|
||||||
const cur = self.uri.query;
|
const cur = self.uri.query;
|
||||||
defer self.uri.query = cur;
|
defer self.uri.query = cur;
|
||||||
@@ -109,8 +109,8 @@ pub const URL = struct {
|
|||||||
return buf.items;
|
return buf.items;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_protocol(self: *URL, state: *SessionState) ![]const u8 {
|
pub fn get_protocol(self: *URL, page: *Page) ![]const u8 {
|
||||||
return try std.mem.concat(state.arena, u8, &[_][]const u8{ self.uri.scheme, ":" });
|
return try std.mem.concat(page.arena, u8, &[_][]const u8{ self.uri.scheme, ":" });
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_username(self: *URL) []const u8 {
|
pub fn get_username(self: *URL) []const u8 {
|
||||||
@@ -121,8 +121,8 @@ pub const URL = struct {
|
|||||||
return uriComponentNullStr(self.uri.password);
|
return uriComponentNullStr(self.uri.password);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_host(self: *URL, state: *SessionState) ![]const u8 {
|
pub fn get_host(self: *URL, page: *Page) ![]const u8 {
|
||||||
var buf = std.ArrayList(u8).init(state.arena);
|
var buf = std.ArrayList(u8).init(page.arena);
|
||||||
|
|
||||||
try self.uri.writeToStream(.{
|
try self.uri.writeToStream(.{
|
||||||
.scheme = false,
|
.scheme = false,
|
||||||
@@ -139,8 +139,8 @@ pub const URL = struct {
|
|||||||
return uriComponentNullStr(self.uri.host);
|
return uriComponentNullStr(self.uri.host);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_port(self: *URL, state: *SessionState) ![]const u8 {
|
pub fn get_port(self: *URL, page: *Page) ![]const u8 {
|
||||||
const arena = state.arena;
|
const arena = page.arena;
|
||||||
if (self.uri.port == null) return try arena.dupe(u8, "");
|
if (self.uri.port == null) return try arena.dupe(u8, "");
|
||||||
|
|
||||||
var buf = std.ArrayList(u8).init(arena);
|
var buf = std.ArrayList(u8).init(arena);
|
||||||
@@ -153,8 +153,8 @@ pub const URL = struct {
|
|||||||
return uriComponentStr(self.uri.path);
|
return uriComponentStr(self.uri.path);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_search(self: *URL, state: *SessionState) ![]const u8 {
|
pub fn get_search(self: *URL, page: *Page) ![]const u8 {
|
||||||
const arena = state.arena;
|
const arena = page.arena;
|
||||||
if (self.search_params.get_size() == 0) return try arena.dupe(u8, "");
|
if (self.search_params.get_size() == 0) return try arena.dupe(u8, "");
|
||||||
|
|
||||||
var buf: std.ArrayListUnmanaged(u8) = .{};
|
var buf: std.ArrayListUnmanaged(u8) = .{};
|
||||||
@@ -164,8 +164,8 @@ pub const URL = struct {
|
|||||||
return buf.items;
|
return buf.items;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_hash(self: *URL, state: *SessionState) ![]const u8 {
|
pub fn get_hash(self: *URL, page: *Page) ![]const u8 {
|
||||||
const arena = state.arena;
|
const arena = page.arena;
|
||||||
if (self.uri.fragment == null) return try arena.dupe(u8, "");
|
if (self.uri.fragment == null) return try arena.dupe(u8, "");
|
||||||
|
|
||||||
return try std.mem.concat(arena, u8, &[_][]const u8{ "#", uriComponentNullStr(self.uri.fragment) });
|
return try std.mem.concat(arena, u8, &[_][]const u8{ "#", uriComponentNullStr(self.uri.fragment) });
|
||||||
@@ -175,8 +175,8 @@ pub const URL = struct {
|
|||||||
return &self.search_params;
|
return &self.search_params;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn _toJSON(self: *URL, state: *SessionState) ![]const u8 {
|
pub fn _toJSON(self: *URL, page: *Page) ![]const u8 {
|
||||||
return try self.get_href(state);
|
return try self.get_href(page);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -200,8 +200,8 @@ fn uriComponentStr(c: std.Uri.Component) []const u8 {
|
|||||||
pub const URLSearchParams = struct {
|
pub const URLSearchParams = struct {
|
||||||
values: query.Values,
|
values: query.Values,
|
||||||
|
|
||||||
pub fn constructor(qs: ?[]const u8, state: *SessionState) !URLSearchParams {
|
pub fn constructor(qs: ?[]const u8, page: *Page) !URLSearchParams {
|
||||||
return init(state.arena, qs);
|
return init(page.arena, qs);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init(arena: std.mem.Allocator, qs: ?[]const u8) !URLSearchParams {
|
pub fn init(arena: std.mem.Allocator, qs: ?[]const u8) !URLSearchParams {
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ const EventTarget = @import("../dom/event_target.zig").EventTarget;
|
|||||||
const EventHandler = @import("../events/event.zig").EventHandler;
|
const EventHandler = @import("../events/event.zig").EventHandler;
|
||||||
|
|
||||||
const parser = @import("../netsurf.zig");
|
const parser = @import("../netsurf.zig");
|
||||||
const SessionState = @import("../env.zig").SessionState;
|
const Page = @import("../page.zig").Page;
|
||||||
|
|
||||||
pub const XMLHttpRequestEventTarget = struct {
|
pub const XMLHttpRequestEventTarget = struct {
|
||||||
pub const prototype = *EventTarget;
|
pub const prototype = *EventTarget;
|
||||||
@@ -86,34 +86,34 @@ pub const XMLHttpRequestEventTarget = struct {
|
|||||||
return self.onloadend_cbk;
|
return self.onloadend_cbk;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_onloadstart(self: *XMLHttpRequestEventTarget, handler: Function, state: *SessionState) !void {
|
pub fn set_onloadstart(self: *XMLHttpRequestEventTarget, handler: Function, page: *Page) !void {
|
||||||
if (self.onloadstart_cbk) |cbk| try self.unregister("loadstart", cbk.id);
|
if (self.onloadstart_cbk) |cbk| try self.unregister("loadstart", cbk.id);
|
||||||
try self.register(state.arena, "loadstart", handler);
|
try self.register(page.arena, "loadstart", handler);
|
||||||
self.onloadstart_cbk = handler;
|
self.onloadstart_cbk = handler;
|
||||||
}
|
}
|
||||||
pub fn set_onprogress(self: *XMLHttpRequestEventTarget, handler: Function, state: *SessionState) !void {
|
pub fn set_onprogress(self: *XMLHttpRequestEventTarget, handler: Function, page: *Page) !void {
|
||||||
if (self.onprogress_cbk) |cbk| try self.unregister("progress", cbk.id);
|
if (self.onprogress_cbk) |cbk| try self.unregister("progress", cbk.id);
|
||||||
try self.register(state.arena, "progress", handler);
|
try self.register(page.arena, "progress", handler);
|
||||||
self.onprogress_cbk = handler;
|
self.onprogress_cbk = handler;
|
||||||
}
|
}
|
||||||
pub fn set_onabort(self: *XMLHttpRequestEventTarget, handler: Function, state: *SessionState) !void {
|
pub fn set_onabort(self: *XMLHttpRequestEventTarget, handler: Function, page: *Page) !void {
|
||||||
if (self.onabort_cbk) |cbk| try self.unregister("abort", cbk.id);
|
if (self.onabort_cbk) |cbk| try self.unregister("abort", cbk.id);
|
||||||
try self.register(state.arena, "abort", handler);
|
try self.register(page.arena, "abort", handler);
|
||||||
self.onabort_cbk = handler;
|
self.onabort_cbk = handler;
|
||||||
}
|
}
|
||||||
pub fn set_onload(self: *XMLHttpRequestEventTarget, handler: Function, state: *SessionState) !void {
|
pub fn set_onload(self: *XMLHttpRequestEventTarget, handler: Function, page: *Page) !void {
|
||||||
if (self.onload_cbk) |cbk| try self.unregister("load", cbk.id);
|
if (self.onload_cbk) |cbk| try self.unregister("load", cbk.id);
|
||||||
try self.register(state.arena, "load", handler);
|
try self.register(page.arena, "load", handler);
|
||||||
self.onload_cbk = handler;
|
self.onload_cbk = handler;
|
||||||
}
|
}
|
||||||
pub fn set_ontimeout(self: *XMLHttpRequestEventTarget, handler: Function, state: *SessionState) !void {
|
pub fn set_ontimeout(self: *XMLHttpRequestEventTarget, handler: Function, page: *Page) !void {
|
||||||
if (self.ontimeout_cbk) |cbk| try self.unregister("timeout", cbk.id);
|
if (self.ontimeout_cbk) |cbk| try self.unregister("timeout", cbk.id);
|
||||||
try self.register(state.arena, "timeout", handler);
|
try self.register(page.arena, "timeout", handler);
|
||||||
self.ontimeout_cbk = handler;
|
self.ontimeout_cbk = handler;
|
||||||
}
|
}
|
||||||
pub fn set_onloadend(self: *XMLHttpRequestEventTarget, handler: Function, state: *SessionState) !void {
|
pub fn set_onloadend(self: *XMLHttpRequestEventTarget, handler: Function, page: *Page) !void {
|
||||||
if (self.onloadend_cbk) |cbk| try self.unregister("loadend", cbk.id);
|
if (self.onloadend_cbk) |cbk| try self.unregister("loadend", cbk.id);
|
||||||
try self.register(state.arena, "loadend", handler);
|
try self.register(page.arena, "loadend", handler);
|
||||||
self.onloadend_cbk = handler;
|
self.onloadend_cbk = handler;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ const Allocator = std.mem.Allocator;
|
|||||||
const log = @import("../../log.zig");
|
const log = @import("../../log.zig");
|
||||||
const parser = @import("../netsurf.zig");
|
const parser = @import("../netsurf.zig");
|
||||||
const iterator = @import("../iterator/iterator.zig");
|
const iterator = @import("../iterator/iterator.zig");
|
||||||
const SessionState = @import("../env.zig").SessionState;
|
const Page = @import("../page.zig").Page;
|
||||||
|
|
||||||
pub const Interfaces = .{
|
pub const Interfaces = .{
|
||||||
FormData,
|
FormData,
|
||||||
@@ -53,19 +53,19 @@ pub const Interfaces = .{
|
|||||||
pub const FormData = struct {
|
pub const FormData = struct {
|
||||||
entries: std.ArrayListUnmanaged(Entry),
|
entries: std.ArrayListUnmanaged(Entry),
|
||||||
|
|
||||||
pub fn constructor(form_: ?*parser.Form, submitter_: ?*parser.ElementHTML, state: *SessionState) !FormData {
|
pub fn constructor(form_: ?*parser.Form, submitter_: ?*parser.ElementHTML, page: *Page) !FormData {
|
||||||
const form = form_ orelse return .{ .entries = .empty };
|
const form = form_ orelse return .{ .entries = .empty };
|
||||||
return fromForm(form, submitter_, state, .{});
|
return fromForm(form, submitter_, page, .{});
|
||||||
}
|
}
|
||||||
|
|
||||||
const FromFormOpts = struct {
|
const FromFormOpts = struct {
|
||||||
// Uses the state.arena if null. This is needed for when we're handling
|
// Uses the page.arena if null. This is needed for when we're handling
|
||||||
// form submission from the Page, and we want to capture the form within
|
// form submission from the Page, and we want to capture the form within
|
||||||
// the session's transfer_arena.
|
// the session's transfer_arena.
|
||||||
allocator: ?Allocator = null,
|
allocator: ?Allocator = null,
|
||||||
};
|
};
|
||||||
pub fn fromForm(form: *parser.Form, submitter_: ?*parser.ElementHTML, state: *SessionState, opts: FromFormOpts) !FormData {
|
pub fn fromForm(form: *parser.Form, submitter_: ?*parser.ElementHTML, page: *Page, opts: FromFormOpts) !FormData {
|
||||||
const entries = try collectForm(opts.allocator orelse state.arena, form, submitter_, state);
|
const entries = try collectForm(opts.allocator orelse page.arena, form, submitter_, page);
|
||||||
return .{ .entries = entries };
|
return .{ .entries = entries };
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,8 +74,8 @@ pub const FormData = struct {
|
|||||||
return result.entry.value;
|
return result.entry.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn _getAll(self: *const FormData, key: []const u8, state: *SessionState) ![][]const u8 {
|
pub fn _getAll(self: *const FormData, key: []const u8, page: *Page) ![][]const u8 {
|
||||||
const arena = state.call_arena;
|
const arena = page.call_arena;
|
||||||
var arr: std.ArrayListUnmanaged([]const u8) = .empty;
|
var arr: std.ArrayListUnmanaged([]const u8) = .empty;
|
||||||
for (self.entries.items) |entry| {
|
for (self.entries.items) |entry| {
|
||||||
if (std.mem.eql(u8, key, entry.key)) {
|
if (std.mem.eql(u8, key, entry.key)) {
|
||||||
@@ -91,15 +91,15 @@ pub const FormData = struct {
|
|||||||
|
|
||||||
// TODO: value should be a string or blog
|
// TODO: value should be a string or blog
|
||||||
// TODO: another optional parameter for the filename
|
// TODO: another optional parameter for the filename
|
||||||
pub fn _set(self: *FormData, key: []const u8, value: []const u8, state: *SessionState) !void {
|
pub fn _set(self: *FormData, key: []const u8, value: []const u8, page: *Page) !void {
|
||||||
self._delete(key);
|
self._delete(key);
|
||||||
return self._append(key, value, state);
|
return self._append(key, value, page);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: value should be a string or blog
|
// TODO: value should be a string or blog
|
||||||
// TODO: another optional parameter for the filename
|
// TODO: another optional parameter for the filename
|
||||||
pub fn _append(self: *FormData, key: []const u8, value: []const u8, state: *SessionState) !void {
|
pub fn _append(self: *FormData, key: []const u8, value: []const u8, page: *Page) !void {
|
||||||
const arena = state.arena;
|
const arena = page.arena;
|
||||||
return self.entries.append(arena, .{ .key = try arena.dupe(u8, key), .value = try arena.dupe(u8, value) });
|
return self.entries.append(arena, .{ .key = try arena.dupe(u8, key), .value = try arena.dupe(u8, value) });
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -198,7 +198,7 @@ const EntryIterator = struct {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
fn collectForm(arena: Allocator, form: *parser.Form, submitter_: ?*parser.ElementHTML, state: *SessionState) !std.ArrayListUnmanaged(Entry) {
|
fn collectForm(arena: Allocator, form: *parser.Form, submitter_: ?*parser.ElementHTML, page: *Page) !std.ArrayListUnmanaged(Entry) {
|
||||||
const collection = try parser.formGetCollection(form);
|
const collection = try parser.formGetCollection(form);
|
||||||
const len = try parser.htmlCollectionGetLength(collection);
|
const len = try parser.htmlCollectionGetLength(collection);
|
||||||
|
|
||||||
@@ -252,7 +252,7 @@ fn collectForm(arena: Allocator, form: *parser.Form, submitter_: ?*parser.Elemen
|
|||||||
},
|
},
|
||||||
.select => {
|
.select => {
|
||||||
const select: *parser.Select = @ptrCast(node);
|
const select: *parser.Select = @ptrCast(node);
|
||||||
try collectSelectValues(arena, select, name, &entries, state);
|
try collectSelectValues(arena, select, name, &entries, page);
|
||||||
},
|
},
|
||||||
.textarea => {
|
.textarea => {
|
||||||
const textarea: *parser.TextArea = @ptrCast(node);
|
const textarea: *parser.TextArea = @ptrCast(node);
|
||||||
@@ -275,12 +275,12 @@ fn collectForm(arena: Allocator, form: *parser.Form, submitter_: ?*parser.Elemen
|
|||||||
return entries;
|
return entries;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn collectSelectValues(arena: Allocator, select: *parser.Select, name: []const u8, entries: *std.ArrayListUnmanaged(Entry), state: *SessionState) !void {
|
fn collectSelectValues(arena: Allocator, select: *parser.Select, name: []const u8, entries: *std.ArrayListUnmanaged(Entry), page: *Page) !void {
|
||||||
const HTMLSelectElement = @import("../html/select.zig").HTMLSelectElement;
|
const HTMLSelectElement = @import("../html/select.zig").HTMLSelectElement;
|
||||||
|
|
||||||
// Go through the HTMLSelectElement because it has specific logic for handling
|
// Go through the HTMLSelectElement because it has specific logic for handling
|
||||||
// the default selected option, which libdom doesn't properly handle
|
// the default selected option, which libdom doesn't properly handle
|
||||||
const selected_index = try HTMLSelectElement.get_selectedIndex(select, state);
|
const selected_index = try HTMLSelectElement.get_selectedIndex(select, page);
|
||||||
if (selected_index == -1) {
|
if (selected_index == -1) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ const URL = @import("../../url.zig").URL;
|
|||||||
const Mime = @import("../mime.zig").Mime;
|
const Mime = @import("../mime.zig").Mime;
|
||||||
const parser = @import("../netsurf.zig");
|
const parser = @import("../netsurf.zig");
|
||||||
const http = @import("../../http/client.zig");
|
const http = @import("../../http/client.zig");
|
||||||
const SessionState = @import("../env.zig").SessionState;
|
const Page = @import("../page.zig").Page;
|
||||||
const CookieJar = @import("../storage/storage.zig").CookieJar;
|
const CookieJar = @import("../storage/storage.zig").CookieJar;
|
||||||
|
|
||||||
// XHR interfaces
|
// XHR interfaces
|
||||||
@@ -238,8 +238,8 @@ pub const XMLHttpRequest = struct {
|
|||||||
|
|
||||||
const min_delay: u64 = 50000000; // 50ms
|
const min_delay: u64 = 50000000; // 50ms
|
||||||
|
|
||||||
pub fn constructor(session_state: *SessionState) !XMLHttpRequest {
|
pub fn constructor(page: *Page) !XMLHttpRequest {
|
||||||
const arena = session_state.arena;
|
const arena = page.arena;
|
||||||
return .{
|
return .{
|
||||||
.arena = arena,
|
.arena = arena,
|
||||||
.headers = Headers.init(arena),
|
.headers = Headers.init(arena),
|
||||||
@@ -247,8 +247,8 @@ pub const XMLHttpRequest = struct {
|
|||||||
.method = undefined,
|
.method = undefined,
|
||||||
.state = .unsent,
|
.state = .unsent,
|
||||||
.url = null,
|
.url = null,
|
||||||
.origin_url = session_state.url,
|
.origin_url = &page.url,
|
||||||
.cookie_jar = session_state.cookie_jar,
|
.cookie_jar = page.cookie_jar,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -408,7 +408,7 @@ pub const XMLHttpRequest = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO body can be either a XMLHttpRequestBodyInit or a document
|
// TODO body can be either a XMLHttpRequestBodyInit or a document
|
||||||
pub fn _send(self: *XMLHttpRequest, body: ?[]const u8, session_state: *SessionState) !void {
|
pub fn _send(self: *XMLHttpRequest, body: ?[]const u8, page: *Page) !void {
|
||||||
if (self.state != .opened) return DOMError.InvalidState;
|
if (self.state != .opened) return DOMError.InvalidState;
|
||||||
if (self.send_flag) return DOMError.InvalidState;
|
if (self.send_flag) return DOMError.InvalidState;
|
||||||
|
|
||||||
@@ -416,7 +416,7 @@ pub const XMLHttpRequest = struct {
|
|||||||
|
|
||||||
self.send_flag = true;
|
self.send_flag = true;
|
||||||
|
|
||||||
self.request = try session_state.request_factory.create(self.method, &self.url.?.uri);
|
self.request = try page.request_factory.create(self.method, &self.url.?.uri);
|
||||||
var request = &self.request.?;
|
var request = &self.request.?;
|
||||||
errdefer request.deinit();
|
errdefer request.deinit();
|
||||||
|
|
||||||
@@ -426,7 +426,7 @@ pub const XMLHttpRequest = struct {
|
|||||||
|
|
||||||
{
|
{
|
||||||
var arr: std.ArrayListUnmanaged(u8) = .{};
|
var arr: std.ArrayListUnmanaged(u8) = .{};
|
||||||
try self.cookie_jar.forRequest(&self.url.?.uri, arr.writer(session_state.arena), .{
|
try self.cookie_jar.forRequest(&self.url.?.uri, arr.writer(page.arena), .{
|
||||||
.navigation = false,
|
.navigation = false,
|
||||||
.origin_uri = &self.origin_url.uri,
|
.origin_uri = &self.origin_url.uri,
|
||||||
});
|
});
|
||||||
@@ -442,12 +442,12 @@ pub const XMLHttpRequest = struct {
|
|||||||
// var used_body: ?XMLHttpRequestBodyInit = null;
|
// var used_body: ?XMLHttpRequestBodyInit = null;
|
||||||
if (body) |b| {
|
if (body) |b| {
|
||||||
if (self.method != .GET and self.method != .HEAD) {
|
if (self.method != .GET and self.method != .HEAD) {
|
||||||
request.body = try session_state.arena.dupe(u8, b);
|
request.body = try page.arena.dupe(u8, b);
|
||||||
try request.addHeader("Content-Type", "text/plain; charset=UTF-8", .{});
|
try request.addHeader("Content-Type", "text/plain; charset=UTF-8", .{});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
try request.sendAsync(session_state.loop, self, .{});
|
try request.sendAsync(page.loop, self, .{});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn onHttpResponse(self: *XMLHttpRequest, progress_: anyerror!http.Progress) !void {
|
pub fn onHttpResponse(self: *XMLHttpRequest, progress_: anyerror!http.Progress) !void {
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
//
|
//
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
|
||||||
const SessionState = @import("../env.zig").SessionState;
|
const Page = @import("../page.zig").Page;
|
||||||
|
|
||||||
const dump = @import("../dump.zig");
|
const dump = @import("../dump.zig");
|
||||||
const parser = @import("../netsurf.zig");
|
const parser = @import("../netsurf.zig");
|
||||||
@@ -33,8 +33,8 @@ pub const XMLSerializer = struct {
|
|||||||
return .{};
|
return .{};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn _serializeToString(_: *const XMLSerializer, root: *parser.Node, state: *SessionState) ![]const u8 {
|
pub fn _serializeToString(_: *const XMLSerializer, root: *parser.Node, page: *Page) ![]const u8 {
|
||||||
var buf = std.ArrayList(u8).init(state.arena);
|
var buf = std.ArrayList(u8).init(page.arena);
|
||||||
if (try parser.nodeType(root) == .document) {
|
if (try parser.nodeType(root) == .document) {
|
||||||
try dump.writeHTML(@as(*parser.Document, @ptrCast(root)), buf.writer());
|
try dump.writeHTML(@as(*parser.Document, @ptrCast(root)), buf.writer());
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -555,7 +555,7 @@ const IsolatedWorld = struct {
|
|||||||
// Currently we have only 1 page/frame and thus also only 1 state in the isolate world.
|
// Currently we have only 1 page/frame and thus also only 1 state in the isolate world.
|
||||||
pub fn createContext(self: *IsolatedWorld, page: *Page) !void {
|
pub fn createContext(self: *IsolatedWorld, page: *Page) !void {
|
||||||
if (self.executor.scope != null) return error.Only1IsolatedContextSupported;
|
if (self.executor.scope != null) return error.Only1IsolatedContextSupported;
|
||||||
_ = try self.executor.startScope(&page.window, &page.state, {}, false);
|
_ = try self.executor.startScope(&page.window, page, {}, false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -383,6 +383,7 @@ fn getContentQuads(cmd: anytype) !void {
|
|||||||
})) orelse return error.InvalidParams;
|
})) orelse return error.InvalidParams;
|
||||||
|
|
||||||
const bc = cmd.browser_context orelse return error.BrowserContextNotLoaded;
|
const bc = cmd.browser_context orelse return error.BrowserContextNotLoaded;
|
||||||
|
const page = bc.session.currentPage() orelse return error.PageNotLoaded;
|
||||||
|
|
||||||
const node = try getNode(cmd.arena, bc, params.nodeId, params.backendNodeId, params.objectId);
|
const node = try getNode(cmd.arena, bc, params.nodeId, params.backendNodeId, params.objectId);
|
||||||
|
|
||||||
@@ -397,7 +398,7 @@ fn getContentQuads(cmd: anytype) !void {
|
|||||||
// Elements like SVGElement may have multiple quads.
|
// Elements like SVGElement may have multiple quads.
|
||||||
|
|
||||||
const element = parser.nodeToElement(node._node);
|
const element = parser.nodeToElement(node._node);
|
||||||
const rect = try Element._getBoundingClientRect(element, &bc.session.page.?.state);
|
const rect = try Element._getBoundingClientRect(element, page);
|
||||||
const quad = rectToQuad(rect);
|
const quad = rectToQuad(rect);
|
||||||
|
|
||||||
return cmd.sendResult(.{ .quads = &.{quad} }, .{});
|
return cmd.sendResult(.{ .quads = &.{quad} }, .{});
|
||||||
@@ -411,6 +412,7 @@ fn getBoxModel(cmd: anytype) !void {
|
|||||||
})) orelse return error.InvalidParams;
|
})) orelse return error.InvalidParams;
|
||||||
|
|
||||||
const bc = cmd.browser_context orelse return error.BrowserContextNotLoaded;
|
const bc = cmd.browser_context orelse return error.BrowserContextNotLoaded;
|
||||||
|
const page = bc.session.currentPage() orelse return error.PageNotLoaded;
|
||||||
|
|
||||||
const node = try getNode(cmd.arena, bc, params.nodeId, params.backendNodeId, params.objectId);
|
const node = try getNode(cmd.arena, bc, params.nodeId, params.backendNodeId, params.objectId);
|
||||||
|
|
||||||
@@ -418,7 +420,7 @@ fn getBoxModel(cmd: anytype) !void {
|
|||||||
if (try parser.nodeType(node._node) != .element) return error.NodeIsNotAnElement;
|
if (try parser.nodeType(node._node) != .element) return error.NodeIsNotAnElement;
|
||||||
const element = parser.nodeToElement(node._node);
|
const element = parser.nodeToElement(node._node);
|
||||||
|
|
||||||
const rect = try Element._getBoundingClientRect(element, &bc.session.page.?.state);
|
const rect = try Element._getBoundingClientRect(element, page);
|
||||||
const quad = rectToQuad(rect);
|
const quad = rectToQuad(rect);
|
||||||
|
|
||||||
return cmd.sendResult(.{ .model = BoxModel{
|
return cmd.sendResult(.{ .model = BoxModel{
|
||||||
|
|||||||
@@ -113,10 +113,10 @@ fn run(arena: Allocator, test_file: []const u8, loader: *FileLoader, err_out: *?
|
|||||||
});
|
});
|
||||||
defer runner.deinit();
|
defer runner.deinit();
|
||||||
|
|
||||||
try polyfill.load(arena, runner.scope);
|
try polyfill.load(arena, runner.page.scope);
|
||||||
|
|
||||||
// loop over the scripts.
|
// loop over the scripts.
|
||||||
const doc = parser.documentHTMLToDocument(runner.state.window.document);
|
const doc = parser.documentHTMLToDocument(runner.page.window.document);
|
||||||
const scripts = try parser.documentGetElementsByTagName(doc, "script");
|
const scripts = try parser.documentGetElementsByTagName(doc, "script");
|
||||||
const script_count = try parser.nodeListLength(scripts);
|
const script_count = try parser.nodeListLength(scripts);
|
||||||
for (0..script_count) |i| {
|
for (0..script_count) |i| {
|
||||||
@@ -147,7 +147,7 @@ fn run(arena: Allocator, test_file: []const u8, loader: *FileLoader, err_out: *?
|
|||||||
|
|
||||||
try parser.eventInit(loadevt, "load", .{});
|
try parser.eventInit(loadevt, "load", .{});
|
||||||
_ = try parser.eventTargetDispatchEvent(
|
_ = try parser.eventTargetDispatchEvent(
|
||||||
parser.toEventTarget(@TypeOf(runner.window), &runner.window),
|
parser.toEventTarget(@TypeOf(runner.page.window), &runner.page.window),
|
||||||
loadevt,
|
loadevt,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -155,9 +155,9 @@ fn run(arena: Allocator, test_file: []const u8, loader: *FileLoader, err_out: *?
|
|||||||
{
|
{
|
||||||
// wait for all async executions
|
// wait for all async executions
|
||||||
var try_catch: Env.TryCatch = undefined;
|
var try_catch: Env.TryCatch = undefined;
|
||||||
try_catch.init(runner.scope);
|
try_catch.init(runner.page.scope);
|
||||||
defer try_catch.deinit();
|
defer try_catch.deinit();
|
||||||
try runner.loop.run();
|
try runner.page.loop.run();
|
||||||
|
|
||||||
if (try_catch.hasCaught()) {
|
if (try_catch.hasCaught()) {
|
||||||
err_out.* = (try try_catch.err(arena)) orelse "unknwon error";
|
err_out.* = (try try_catch.err(arena)) orelse "unknwon error";
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ const generate = @import("generate.zig");
|
|||||||
pub const allocator = std.testing.allocator;
|
pub const allocator = std.testing.allocator;
|
||||||
|
|
||||||
// Very similar to the JSRunner in src/testing.zig, but it isn't tied to the
|
// Very similar to the JSRunner in src/testing.zig, but it isn't tied to the
|
||||||
// browser.Env or the browser.SessionState
|
// browser.Env or the *Page state
|
||||||
pub fn Runner(comptime State: type, comptime Global: type, comptime types: anytype) type {
|
pub fn Runner(comptime State: type, comptime Global: type, comptime types: anytype) type {
|
||||||
const AdjustedTypes = if (Global == void) generate.Tuple(.{ types, DefaultGlobal }) else types;
|
const AdjustedTypes = if (Global == void) generate.Tuple(.{ types, DefaultGlobal }) else types;
|
||||||
|
|
||||||
|
|||||||
@@ -183,7 +183,7 @@ test "telemetry: getOrCreateId" {
|
|||||||
}
|
}
|
||||||
|
|
||||||
test "telemetry: sends event to provider" {
|
test "telemetry: sends event to provider" {
|
||||||
var app = testing.app(.{});
|
var app = testing.createApp(.{});
|
||||||
defer app.deinit();
|
defer app.deinit();
|
||||||
|
|
||||||
var telemetry = TelemetryT(MockProvider).init(app, .serve);
|
var telemetry = TelemetryT(MockProvider).init(app, .serve);
|
||||||
|
|||||||
136
src/testing.zig
136
src/testing.zig
@@ -171,7 +171,7 @@ pub fn print(comptime fmt: []const u8, args: anytype) void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// dummy opts incase we want to add something, and not have to break all the callers
|
// dummy opts incase we want to add something, and not have to break all the callers
|
||||||
pub fn app(_: anytype) *App {
|
pub fn createApp(_: anytype) *App {
|
||||||
return App.init(allocator, .{ .run_mode = .serve }) catch unreachable;
|
return App.init(allocator, .{ .run_mode = .serve }) catch unreachable;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -367,114 +367,79 @@ pub const tracking_allocator = @import("root").tracking_allocator.allocator();
|
|||||||
pub const JsRunner = struct {
|
pub const JsRunner = struct {
|
||||||
const URL = @import("url.zig").URL;
|
const URL = @import("url.zig").URL;
|
||||||
const Env = @import("browser/env.zig").Env;
|
const Env = @import("browser/env.zig").Env;
|
||||||
const Loop = @import("runtime/loop.zig").Loop;
|
const Page = @import("browser/page.zig").Page;
|
||||||
const HttpClient = @import("http/client.zig").Client;
|
const Browser = @import("browser/browser.zig").Browser;
|
||||||
const storage = @import("browser/storage/storage.zig");
|
|
||||||
const Window = @import("browser/html/window.zig").Window;
|
|
||||||
const Renderer = @import("browser/renderer.zig").Renderer;
|
|
||||||
const SessionState = @import("browser/env.zig").SessionState;
|
|
||||||
|
|
||||||
url: URL,
|
app: *App,
|
||||||
env: *Env,
|
page: *Page,
|
||||||
loop: Loop,
|
browser: *Browser,
|
||||||
window: Window,
|
|
||||||
state: SessionState,
|
|
||||||
arena: Allocator,
|
|
||||||
renderer: Renderer,
|
|
||||||
http_client: HttpClient,
|
|
||||||
scope: *Env.Scope,
|
|
||||||
executor: Env.ExecutionWorld,
|
|
||||||
storage_shelf: storage.Shelf,
|
|
||||||
cookie_jar: storage.CookieJar,
|
|
||||||
|
|
||||||
fn init(parent_allocator: Allocator, opts: RunnerOpts) !*JsRunner {
|
fn init(alloc: Allocator, opts: RunnerOpts) !JsRunner {
|
||||||
parser.deinit();
|
parser.deinit();
|
||||||
try parser.init();
|
|
||||||
|
|
||||||
const aa = try parent_allocator.create(std.heap.ArenaAllocator);
|
var app = try App.init(alloc, .{
|
||||||
aa.* = std.heap.ArenaAllocator.init(parent_allocator);
|
.run_mode = .serve,
|
||||||
errdefer aa.deinit();
|
|
||||||
|
|
||||||
const arena = aa.allocator();
|
|
||||||
const self = try arena.create(JsRunner);
|
|
||||||
self.arena = arena;
|
|
||||||
|
|
||||||
self.env = try Env.init(arena, .{});
|
|
||||||
errdefer self.env.deinit();
|
|
||||||
|
|
||||||
self.url = try URL.parse(opts.url, null);
|
|
||||||
|
|
||||||
self.renderer = Renderer.init(arena);
|
|
||||||
self.cookie_jar = storage.CookieJar.init(arena);
|
|
||||||
self.loop = try Loop.init(arena);
|
|
||||||
errdefer self.loop.deinit();
|
|
||||||
|
|
||||||
var html = std.io.fixedBufferStream(opts.html);
|
|
||||||
const document = try parser.documentHTMLParse(html.reader(), "UTF-8");
|
|
||||||
|
|
||||||
self.window = try Window.create(null, null);
|
|
||||||
try self.window.replaceDocument(document);
|
|
||||||
try self.window.replaceLocation(.{
|
|
||||||
.url = try self.url.toWebApi(arena),
|
|
||||||
});
|
|
||||||
|
|
||||||
self.http_client = try HttpClient.init(arena, 1, .{
|
|
||||||
.tls_verify_host = false,
|
.tls_verify_host = false,
|
||||||
});
|
});
|
||||||
|
errdefer app.deinit();
|
||||||
|
|
||||||
self.state = .{
|
const browser = try alloc.create(Browser);
|
||||||
.arena = arena,
|
errdefer alloc.destroy(browser);
|
||||||
.loop = &self.loop,
|
|
||||||
.url = &self.url,
|
browser.* = try Browser.init(app);
|
||||||
.window = &self.window,
|
errdefer browser.deinit();
|
||||||
.renderer = &self.renderer,
|
|
||||||
.cookie_jar = &self.cookie_jar,
|
var session = try browser.newSession();
|
||||||
.request_factory = self.http_client.requestFactory(null),
|
|
||||||
|
var page = try session.createPage();
|
||||||
|
|
||||||
|
// a bit hacky, but since we aren't going through page.navigate, there's
|
||||||
|
// some minimum setup we need to do
|
||||||
|
page.url = try URL.parse(opts.url, null);
|
||||||
|
try page.window.replaceLocation(.{
|
||||||
|
.url = try page.url.toWebApi(page.arena),
|
||||||
|
});
|
||||||
|
|
||||||
|
var html = std.io.fixedBufferStream(opts.html);
|
||||||
|
try page.loadHTMLDoc(html.reader(), "UTF-8");
|
||||||
|
|
||||||
|
return .{
|
||||||
|
.app = app,
|
||||||
|
.page = page,
|
||||||
|
.browser = browser,
|
||||||
};
|
};
|
||||||
|
|
||||||
self.storage_shelf = storage.Shelf.init(arena);
|
|
||||||
self.window.setStorageShelf(&self.storage_shelf);
|
|
||||||
|
|
||||||
self.executor = try self.env.newExecutionWorld();
|
|
||||||
errdefer self.executor.deinit();
|
|
||||||
|
|
||||||
self.scope = try self.executor.startScope(&self.window, &self.state, {}, true);
|
|
||||||
return self;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deinit(self: *JsRunner) void {
|
pub fn deinit(self: *JsRunner) void {
|
||||||
self.loop.deinit();
|
self.browser.deinit();
|
||||||
self.executor.deinit();
|
self.app.allocator.destroy(self.browser);
|
||||||
self.env.deinit();
|
self.app.deinit();
|
||||||
self.http_client.deinit();
|
|
||||||
self.storage_shelf.deinit();
|
|
||||||
|
|
||||||
const arena: *std.heap.ArenaAllocator = @ptrCast(@alignCast(self.arena.ptr));
|
|
||||||
arena.deinit();
|
|
||||||
arena.child_allocator.destroy(arena);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const RunOpts = struct {};
|
const RunOpts = struct {};
|
||||||
pub const Case = std.meta.Tuple(&.{ []const u8, ?[]const u8 });
|
pub const Case = std.meta.Tuple(&.{ []const u8, ?[]const u8 });
|
||||||
pub fn testCases(self: *JsRunner, cases: []const Case, _: RunOpts) !void {
|
pub fn testCases(self: *JsRunner, cases: []const Case, _: RunOpts) !void {
|
||||||
|
const scope = self.page.scope;
|
||||||
|
const arena = self.page.arena;
|
||||||
|
|
||||||
const start = try std.time.Instant.now();
|
const start = try std.time.Instant.now();
|
||||||
|
|
||||||
for (cases, 0..) |case, i| {
|
for (cases, 0..) |case, i| {
|
||||||
var try_catch: Env.TryCatch = undefined;
|
var try_catch: Env.TryCatch = undefined;
|
||||||
try_catch.init(self.scope);
|
try_catch.init(scope);
|
||||||
defer try_catch.deinit();
|
defer try_catch.deinit();
|
||||||
|
|
||||||
const value = self.scope.exec(case.@"0", null) catch |err| {
|
const value = scope.exec(case.@"0", null) catch |err| {
|
||||||
if (try try_catch.err(self.arena)) |msg| {
|
if (try try_catch.err(arena)) |msg| {
|
||||||
std.debug.print("{s}\n\nCase: {d}\n{s}\n", .{ msg, i + 1, case.@"0" });
|
std.debug.print("{s}\n\nCase: {d}\n{s}\n", .{ msg, i + 1, case.@"0" });
|
||||||
}
|
}
|
||||||
return err;
|
return err;
|
||||||
};
|
};
|
||||||
try self.loop.run();
|
try self.page.loop.run();
|
||||||
@import("root").js_runner_duration += std.time.Instant.since(try std.time.Instant.now(), start);
|
@import("root").js_runner_duration += std.time.Instant.since(try std.time.Instant.now(), start);
|
||||||
|
|
||||||
if (case.@"1") |expected| {
|
if (case.@"1") |expected| {
|
||||||
const actual = try value.toString(self.arena);
|
const actual = try value.toString(arena);
|
||||||
if (std.mem.eql(u8, expected, actual) == false) {
|
if (std.mem.eql(u8, expected, actual) == false) {
|
||||||
std.debug.print("Expected:\n{s}\n\nGot:\n{s}\n\nCase: {d}\n{s}\n", .{ expected, actual, i + 1, case.@"0" });
|
std.debug.print("Expected:\n{s}\n\nGot:\n{s}\n\nCase: {d}\n{s}\n", .{ expected, actual, i + 1, case.@"0" });
|
||||||
return error.UnexpectedResult;
|
return error.UnexpectedResult;
|
||||||
@@ -488,12 +453,15 @@ pub const JsRunner = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn eval(self: *JsRunner, src: []const u8, name: ?[]const u8, err_msg: *?[]const u8) !Env.Value {
|
pub fn eval(self: *JsRunner, src: []const u8, name: ?[]const u8, err_msg: *?[]const u8) !Env.Value {
|
||||||
|
const scope = self.page.scope;
|
||||||
|
const arena = self.page.arena;
|
||||||
|
|
||||||
var try_catch: Env.TryCatch = undefined;
|
var try_catch: Env.TryCatch = undefined;
|
||||||
try_catch.init(self.scope);
|
try_catch.init(scope);
|
||||||
defer try_catch.deinit();
|
defer try_catch.deinit();
|
||||||
|
|
||||||
return self.scope.exec(src, name) catch |err| {
|
return scope.exec(src, name) catch |err| {
|
||||||
if (try try_catch.err(self.arena)) |msg| {
|
if (try try_catch.err(arena)) |msg| {
|
||||||
err_msg.* = msg;
|
err_msg.* = msg;
|
||||||
std.debug.print("Error running script: {s}\n", .{msg});
|
std.debug.print("Error running script: {s}\n", .{msg});
|
||||||
}
|
}
|
||||||
@@ -517,6 +485,6 @@ const RunnerOpts = struct {
|
|||||||
,
|
,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn jsRunner(alloc: Allocator, opts: RunnerOpts) !*JsRunner {
|
pub fn jsRunner(alloc: Allocator, opts: RunnerOpts) !JsRunner {
|
||||||
return JsRunner.init(alloc, opts);
|
return JsRunner.init(alloc, opts);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user