Skip to content

Commit f56b0a5

Browse files
committed
Merge branch 'main' into crypto_get_random_values_fix
2 parents 8d3a042 + 3f9b256 commit f56b0a5

File tree

10 files changed

+650
-114
lines changed

10 files changed

+650
-114
lines changed

src/app.zig

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ const Allocator = std.mem.Allocator;
33

44
const log = @import("log.zig");
55
const Loop = @import("runtime/loop.zig").Loop;
6-
const HttpClient = @import("http/client.zig").Client;
6+
const http = @import("http/client.zig");
7+
78
const Telemetry = @import("telemetry/telemetry.zig").Telemetry;
89
const Notification = @import("notification.zig").Notification;
910

@@ -14,7 +15,7 @@ pub const App = struct {
1415
config: Config,
1516
allocator: Allocator,
1617
telemetry: Telemetry,
17-
http_client: HttpClient,
18+
http_client: http.Client,
1819
app_dir_path: ?[]const u8,
1920
notification: *Notification,
2021

@@ -29,6 +30,8 @@ pub const App = struct {
2930
run_mode: RunMode,
3031
tls_verify_host: bool = true,
3132
http_proxy: ?std.Uri = null,
33+
proxy_type: ?http.ProxyType = null,
34+
proxy_auth: ?http.ProxyAuth = null,
3235
};
3336

3437
pub fn init(allocator: Allocator, config: Config) !*App {
@@ -52,9 +55,11 @@ pub const App = struct {
5255
.telemetry = undefined,
5356
.app_dir_path = app_dir_path,
5457
.notification = notification,
55-
.http_client = try HttpClient.init(allocator, .{
58+
.http_client = try http.Client.init(allocator, .{
5659
.max_concurrent = 3,
5760
.http_proxy = config.http_proxy,
61+
.proxy_type = config.proxy_type,
62+
.proxy_auth = config.proxy_auth,
5863
.tls_verify_host = config.tls_verify_host,
5964
}),
6065
.config = config,

src/browser/State.zig

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828

2929
const Env = @import("env.zig").Env;
3030
const parser = @import("netsurf.zig");
31+
const DataSet = @import("html/DataSet.zig");
3132
const CSSStyleDeclaration = @import("cssom/css_style_declaration.zig").CSSStyleDeclaration;
3233

3334
// for HTMLScript (but probably needs to be added to more)
@@ -36,6 +37,7 @@ onerror: ?Env.Function = null,
3637

3738
// for HTMLElement
3839
style: CSSStyleDeclaration = .empty,
40+
dataset: ?DataSet = null,
3941

4042
// for html/document
4143
ready_state: ReadyState = .loading,

src/browser/css/css.zig

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ pub const Interfaces = .{
2929

3030
// https://developer.mozilla.org/en-US/docs/Web/API/CSS
3131
pub const Css = struct {
32+
_not_empty: bool = true,
33+
3234
pub fn _supports(_: *Css, _: []const u8, _: ?[]const u8) bool {
3335
// TODO: Actually respond with which CSS features we support.
3436
return true;

src/browser/html/DataSet.zig

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
// Copyright (C) 2023-2024 Lightpanda (Selecy SAS)
2+
//
3+
// Francis Bouvier <francis@lightpanda.io>
4+
// Pierre Tachoire <pierre@lightpanda.io>
5+
//
6+
// This program is free software: you can redistribute it and/or modify
7+
// it under the terms of the GNU Affero General Public License as
8+
// published by the Free Software Foundation, either version 3 of the
9+
// License, or (at your option) any later version.
10+
//
11+
// This program is distributed in the hope that it will be useful,
12+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
// GNU Affero General Public License for more details.
15+
//
16+
// You should have received a copy of the GNU Affero General Public License
17+
// along with this program. If not, see <https://www.gnu.org/licenses/>.
18+
const std = @import("std");
19+
const parser = @import("../netsurf.zig");
20+
const Page = @import("../page.zig").Page;
21+
22+
const Allocator = std.mem.Allocator;
23+
24+
const DataSet = @This();
25+
26+
element: *parser.Element,
27+
28+
const GetResult = union(enum) {
29+
value: []const u8,
30+
undefined: void,
31+
};
32+
pub fn named_get(self: *const DataSet, name: []const u8, _: *bool, page: *Page) !GetResult {
33+
const normalized_name = try normalize(page.call_arena, name);
34+
if (try parser.elementGetAttribute(self.element, normalized_name)) |value| {
35+
return .{ .value = value };
36+
}
37+
return .{ .undefined = {} };
38+
}
39+
40+
pub fn named_set(self: *DataSet, name: []const u8, value: []const u8, _: *bool, page: *Page) !void {
41+
const normalized_name = try normalize(page.call_arena, name);
42+
try parser.elementSetAttribute(self.element, normalized_name, value);
43+
}
44+
45+
pub fn named_delete(self: *DataSet, name: []const u8, _: *bool, page: *Page) !void {
46+
const normalized_name = try normalize(page.call_arena, name);
47+
try parser.elementRemoveAttribute(self.element, normalized_name);
48+
}
49+
50+
fn normalize(allocator: Allocator, name: []const u8) ![]const u8 {
51+
var upper_count: usize = 0;
52+
for (name) |c| {
53+
if (std.ascii.isUpper(c)) {
54+
upper_count += 1;
55+
}
56+
}
57+
// for every upper-case letter, we'll probably need a dash before it
58+
// and we need the 'data-' prefix
59+
var normalized = try allocator.alloc(u8, name.len + upper_count + 5);
60+
61+
@memcpy(normalized[0..5], "data-");
62+
if (upper_count == 0) {
63+
@memcpy(normalized[5..], name);
64+
return normalized;
65+
}
66+
67+
var pos: usize = 5;
68+
for (name) |c| {
69+
if (std.ascii.isUpper(c)) {
70+
normalized[pos] = '-';
71+
pos += 1;
72+
normalized[pos] = c + 32;
73+
} else {
74+
normalized[pos] = c;
75+
}
76+
pos += 1;
77+
}
78+
return normalized;
79+
}
80+
81+
const testing = @import("../../testing.zig");
82+
test "Browser.HTML.DataSet" {
83+
var runner = try testing.jsRunner(testing.tracking_allocator, .{ .html = "" });
84+
defer runner.deinit();
85+
86+
try runner.testCases(&.{
87+
.{ "let el1 = document.createElement('div')", null },
88+
.{ "el1.dataset.x", "undefined" },
89+
.{ "el1.dataset.x = '123'", "123" },
90+
.{ "delete el1.dataset.x", "true" },
91+
.{ "el1.dataset.x", "undefined" },
92+
.{ "delete el1.dataset.other", "true" }, // yes, this is right
93+
94+
.{ "let ds1 = el1.dataset", null },
95+
.{ "ds1.helloWorld = 'yes'", null },
96+
.{ "el1.getAttribute('data-hello-world')", "yes" },
97+
.{ "el1.setAttribute('data-this-will-work', 'positive')", null },
98+
.{ "ds1.thisWillWork", "positive" },
99+
}, .{});
100+
}

src/browser/html/elements.zig

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ const urlStitch = @import("../../url.zig").URL.stitch;
2727
const URL = @import("../url/url.zig").URL;
2828
const Node = @import("../dom/node.zig").Node;
2929
const Element = @import("../dom/element.zig").Element;
30+
const DataSet = @import("DataSet.zig");
3031

3132
const CSSStyleDeclaration = @import("../cssom/css_style_declaration.zig").CSSStyleDeclaration;
3233

@@ -122,6 +123,15 @@ pub const HTMLElement = struct {
122123
return &state.style;
123124
}
124125

126+
pub fn get_dataset(e: *parser.ElementHTML, page: *Page) !*DataSet {
127+
const state = try page.getOrCreateNodeState(@ptrCast(e));
128+
if (state.dataset) |*ds| {
129+
return ds;
130+
}
131+
state.dataset = DataSet{ .element = @ptrCast(e) };
132+
return &state.dataset.?;
133+
}
134+
125135
pub fn get_innerText(e: *parser.ElementHTML) ![]const u8 {
126136
const n = @as(*parser.Node, @ptrCast(e));
127137
return try parser.nodeTextContent(n) orelse "";
@@ -1561,6 +1571,13 @@ test "Browser.HTML.Element" {
15611571
}, .{});
15621572
}
15631573

1574+
test "Browser.HTML.Element.DataSet" {
1575+
var runner = try testing.jsRunner(testing.tracking_allocator, .{ .html = "<div id=x data-power='over 9000' data-empty data-some-long-key=ok></div>" });
1576+
defer runner.deinit();
1577+
1578+
try runner.testCases(&.{ .{ "let div = document.getElementById('x')", null }, .{ "div.dataset.nope", "undefined" }, .{ "div.dataset.power", "over 9000" }, .{ "div.dataset.empty", "" }, .{ "div.dataset.someLongKey", "ok" }, .{ "delete div.dataset.power", "true" }, .{ "div.dataset.power", "undefined" } }, .{});
1579+
}
1580+
15641581
test "Browser.HTML.HtmlInputElement.properties" {
15651582
var runner = try testing.jsRunner(testing.tracking_allocator, .{ .url = "https://lightpanda.io/noslashattheend" });
15661583
defer runner.deinit();

src/browser/html/html.zig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ pub const Interfaces = .{
3636
History,
3737
Location,
3838
MediaQueryList,
39+
@import("DataSet.zig"),
3940
@import("screen.zig").Interfaces,
4041
@import("error_event.zig").ErrorEvent,
4142
};

0 commit comments

Comments
 (0)