mirror of
https://github.com/lightpanda-io/browser.git
synced 2026-03-22 04:34:44 +00:00
Merge pull request #1661 from lightpanda-io/escape_navigate_url
Some checks failed
e2e-test / zig build release (push) Has been cancelled
e2e-test / demo-scripts (push) Has been cancelled
e2e-test / cdp-and-hyperfine-bench (push) Has been cancelled
e2e-test / perf-fmt (push) Has been cancelled
e2e-test / browser fetch (push) Has been cancelled
zig-test / zig test using v8 in debug mode (push) Has been cancelled
zig-test / zig test (push) Has been cancelled
zig-test / perf-fmt (push) Has been cancelled
Some checks failed
e2e-test / zig build release (push) Has been cancelled
e2e-test / demo-scripts (push) Has been cancelled
e2e-test / cdp-and-hyperfine-bench (push) Has been cancelled
e2e-test / perf-fmt (push) Has been cancelled
e2e-test / browser fetch (push) Has been cancelled
zig-test / zig test using v8 in debug mode (push) Has been cancelled
zig-test / zig test (push) Has been cancelled
zig-test / perf-fmt (push) Has been cancelled
Callers to page.navigate ensure URL is properly encoded.
This commit is contained in:
@@ -30,10 +30,10 @@ pub fn resolve(allocator: Allocator, base: [:0]const u8, path: anytype, comptime
|
|||||||
if (base.len == 0 or isCompleteHTTPUrl(path)) {
|
if (base.len == 0 or isCompleteHTTPUrl(path)) {
|
||||||
if (comptime opts.always_dupe or !isNullTerminated(PT)) {
|
if (comptime opts.always_dupe or !isNullTerminated(PT)) {
|
||||||
const duped = try allocator.dupeZ(u8, path);
|
const duped = try allocator.dupeZ(u8, path);
|
||||||
return encodeURL(allocator, duped, opts);
|
return processResolved(allocator, duped, opts);
|
||||||
}
|
}
|
||||||
if (comptime opts.encode) {
|
if (comptime opts.encode) {
|
||||||
return encodeURL(allocator, path, opts);
|
return processResolved(allocator, path, opts);
|
||||||
}
|
}
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
@@ -41,10 +41,10 @@ pub fn resolve(allocator: Allocator, base: [:0]const u8, path: anytype, comptime
|
|||||||
if (path.len == 0) {
|
if (path.len == 0) {
|
||||||
if (comptime opts.always_dupe) {
|
if (comptime opts.always_dupe) {
|
||||||
const duped = try allocator.dupeZ(u8, base);
|
const duped = try allocator.dupeZ(u8, base);
|
||||||
return encodeURL(allocator, duped, opts);
|
return processResolved(allocator, duped, opts);
|
||||||
}
|
}
|
||||||
if (comptime opts.encode) {
|
if (comptime opts.encode) {
|
||||||
return encodeURL(allocator, base, opts);
|
return processResolved(allocator, base, opts);
|
||||||
}
|
}
|
||||||
return base;
|
return base;
|
||||||
}
|
}
|
||||||
@@ -52,12 +52,12 @@ pub fn resolve(allocator: Allocator, base: [:0]const u8, path: anytype, comptime
|
|||||||
if (path[0] == '?') {
|
if (path[0] == '?') {
|
||||||
const base_path_end = std.mem.indexOfAny(u8, base, "?#") orelse base.len;
|
const base_path_end = std.mem.indexOfAny(u8, base, "?#") orelse base.len;
|
||||||
const result = try std.mem.joinZ(allocator, "", &.{ base[0..base_path_end], path });
|
const result = try std.mem.joinZ(allocator, "", &.{ base[0..base_path_end], path });
|
||||||
return encodeURL(allocator, result, opts);
|
return processResolved(allocator, result, opts);
|
||||||
}
|
}
|
||||||
if (path[0] == '#') {
|
if (path[0] == '#') {
|
||||||
const base_fragment_start = std.mem.indexOfScalar(u8, base, '#') orelse base.len;
|
const base_fragment_start = std.mem.indexOfScalar(u8, base, '#') orelse base.len;
|
||||||
const result = try std.mem.joinZ(allocator, "", &.{ base[0..base_fragment_start], path });
|
const result = try std.mem.joinZ(allocator, "", &.{ base[0..base_fragment_start], path });
|
||||||
return encodeURL(allocator, result, opts);
|
return processResolved(allocator, result, opts);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (std.mem.startsWith(u8, path, "//")) {
|
if (std.mem.startsWith(u8, path, "//")) {
|
||||||
@@ -65,16 +65,16 @@ pub fn resolve(allocator: Allocator, base: [:0]const u8, path: anytype, comptime
|
|||||||
const index = std.mem.indexOfScalar(u8, base, ':') orelse {
|
const index = std.mem.indexOfScalar(u8, base, ':') orelse {
|
||||||
if (comptime isNullTerminated(PT)) {
|
if (comptime isNullTerminated(PT)) {
|
||||||
if (comptime opts.encode) {
|
if (comptime opts.encode) {
|
||||||
return encodeURL(allocator, path, opts);
|
return processResolved(allocator, path, opts);
|
||||||
}
|
}
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
const duped = try allocator.dupeZ(u8, path);
|
const duped = try allocator.dupeZ(u8, path);
|
||||||
return encodeURL(allocator, duped, opts);
|
return processResolved(allocator, duped, opts);
|
||||||
};
|
};
|
||||||
const protocol = base[0 .. index + 1];
|
const protocol = base[0 .. index + 1];
|
||||||
const result = try std.mem.joinZ(allocator, "", &.{ protocol, path });
|
const result = try std.mem.joinZ(allocator, "", &.{ protocol, path });
|
||||||
return encodeURL(allocator, result, opts);
|
return processResolved(allocator, result, opts);
|
||||||
}
|
}
|
||||||
|
|
||||||
const scheme_end = std.mem.indexOf(u8, base, "://");
|
const scheme_end = std.mem.indexOf(u8, base, "://");
|
||||||
@@ -83,7 +83,7 @@ pub fn resolve(allocator: Allocator, base: [:0]const u8, path: anytype, comptime
|
|||||||
|
|
||||||
if (path[0] == '/') {
|
if (path[0] == '/') {
|
||||||
const result = try std.mem.joinZ(allocator, "", &.{ base[0..path_start], path });
|
const result = try std.mem.joinZ(allocator, "", &.{ base[0..path_start], path });
|
||||||
return encodeURL(allocator, result, opts);
|
return processResolved(allocator, result, opts);
|
||||||
}
|
}
|
||||||
|
|
||||||
var normalized_base: []const u8 = base[0..path_start];
|
var normalized_base: []const u8 = base[0..path_start];
|
||||||
@@ -145,14 +145,17 @@ pub fn resolve(allocator: Allocator, base: [:0]const u8, path: anytype, comptime
|
|||||||
|
|
||||||
// we always have an extra space
|
// we always have an extra space
|
||||||
out[out_i] = 0;
|
out[out_i] = 0;
|
||||||
return encodeURL(allocator, out[0..out_i :0], opts);
|
return processResolved(allocator, out[0..out_i :0], opts);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn encodeURL(allocator: Allocator, url: [:0]const u8, comptime opts: ResolveOpts) ![:0]const u8 {
|
fn processResolved(allocator: Allocator, url: [:0]const u8, comptime opts: ResolveOpts) ![:0]const u8 {
|
||||||
if (!comptime opts.encode) {
|
if (!comptime opts.encode) {
|
||||||
return url;
|
return url;
|
||||||
}
|
}
|
||||||
|
return ensureEncoded(allocator, url);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ensureEncoded(allocator: Allocator, url: [:0]const u8) ![:0]const u8 {
|
||||||
const scheme_end = std.mem.indexOf(u8, url, "://");
|
const scheme_end = std.mem.indexOf(u8, url, "://");
|
||||||
const authority_start = if (scheme_end) |end| end + 3 else 0;
|
const authority_start = if (scheme_end) |end| end + 3 else 0;
|
||||||
const path_start = std.mem.indexOfScalarPos(u8, url, authority_start, '/') orelse return url;
|
const path_start = std.mem.indexOfScalarPos(u8, url, authority_start, '/') orelse return url;
|
||||||
@@ -180,7 +183,8 @@ fn encodeURL(allocator: Allocator, url: [:0]const u8, comptime opts: ResolveOpts
|
|||||||
|
|
||||||
if (encoded_path.ptr == path_to_encode.ptr and
|
if (encoded_path.ptr == path_to_encode.ptr and
|
||||||
(encoded_query == null or encoded_query.?.ptr == url[query_start.? + 1 .. query_end].ptr) and
|
(encoded_query == null or encoded_query.?.ptr == url[query_start.? + 1 .. query_end].ptr) and
|
||||||
(encoded_fragment == null or encoded_fragment.?.ptr == url[fragment_start.? + 1 ..].ptr)) {
|
(encoded_fragment == null or encoded_fragment.?.ptr == url[fragment_start.? + 1 ..].ptr))
|
||||||
|
{
|
||||||
// nothing has changed
|
// nothing has changed
|
||||||
return url;
|
return url;
|
||||||
}
|
}
|
||||||
@@ -817,6 +821,127 @@ test "URL: resolve" {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test "URL: ensureEncoded" {
|
||||||
|
defer testing.reset();
|
||||||
|
|
||||||
|
const Case = struct {
|
||||||
|
url: [:0]const u8,
|
||||||
|
expected: [:0]const u8,
|
||||||
|
};
|
||||||
|
|
||||||
|
const cases = [_]Case{
|
||||||
|
.{
|
||||||
|
.url = "https://example.com/over 9000!",
|
||||||
|
.expected = "https://example.com/over%209000!",
|
||||||
|
},
|
||||||
|
.{
|
||||||
|
.url = "http://example.com/hello world.html",
|
||||||
|
.expected = "http://example.com/hello%20world.html",
|
||||||
|
},
|
||||||
|
.{
|
||||||
|
.url = "https://example.com/file[1].html",
|
||||||
|
.expected = "https://example.com/file%5B1%5D.html",
|
||||||
|
},
|
||||||
|
.{
|
||||||
|
.url = "https://example.com/file{name}.html",
|
||||||
|
.expected = "https://example.com/file%7Bname%7D.html",
|
||||||
|
},
|
||||||
|
.{
|
||||||
|
.url = "https://example.com/page?query=hello world",
|
||||||
|
.expected = "https://example.com/page?query=hello%20world",
|
||||||
|
},
|
||||||
|
.{
|
||||||
|
.url = "https://example.com/page?a=1&b=value with spaces",
|
||||||
|
.expected = "https://example.com/page?a=1&b=value%20with%20spaces",
|
||||||
|
},
|
||||||
|
.{
|
||||||
|
.url = "https://example.com/page#section one",
|
||||||
|
.expected = "https://example.com/page#section%20one",
|
||||||
|
},
|
||||||
|
.{
|
||||||
|
.url = "https://example.com/my path?query=my value#my anchor",
|
||||||
|
.expected = "https://example.com/my%20path?query=my%20value#my%20anchor",
|
||||||
|
},
|
||||||
|
.{
|
||||||
|
.url = "https://example.com/already%20encoded",
|
||||||
|
.expected = "https://example.com/already%20encoded",
|
||||||
|
},
|
||||||
|
.{
|
||||||
|
.url = "https://example.com/file%5B1%5D.html",
|
||||||
|
.expected = "https://example.com/file%5B1%5D.html",
|
||||||
|
},
|
||||||
|
.{
|
||||||
|
.url = "https://example.com/caf%C3%A9",
|
||||||
|
.expected = "https://example.com/caf%C3%A9",
|
||||||
|
},
|
||||||
|
.{
|
||||||
|
.url = "https://example.com/page?query=already%20encoded",
|
||||||
|
.expected = "https://example.com/page?query=already%20encoded",
|
||||||
|
},
|
||||||
|
.{
|
||||||
|
.url = "https://example.com/page?a=1&b=value%20here",
|
||||||
|
.expected = "https://example.com/page?a=1&b=value%20here",
|
||||||
|
},
|
||||||
|
.{
|
||||||
|
.url = "https://example.com/page#section%20one",
|
||||||
|
.expected = "https://example.com/page#section%20one",
|
||||||
|
},
|
||||||
|
.{
|
||||||
|
.url = "https://example.com/part%20encoded and not",
|
||||||
|
.expected = "https://example.com/part%20encoded%20and%20not",
|
||||||
|
},
|
||||||
|
.{
|
||||||
|
.url = "https://example.com/page?a=encoded%20value&b=not encoded",
|
||||||
|
.expected = "https://example.com/page?a=encoded%20value&b=not%20encoded",
|
||||||
|
},
|
||||||
|
.{
|
||||||
|
.url = "https://example.com/my%20path?query=not encoded#encoded%20anchor",
|
||||||
|
.expected = "https://example.com/my%20path?query=not%20encoded#encoded%20anchor",
|
||||||
|
},
|
||||||
|
.{
|
||||||
|
.url = "https://example.com/fully%20encoded?query=also%20encoded#and%20this",
|
||||||
|
.expected = "https://example.com/fully%20encoded?query=also%20encoded#and%20this",
|
||||||
|
},
|
||||||
|
.{
|
||||||
|
.url = "https://example.com/path-with_under~tilde",
|
||||||
|
.expected = "https://example.com/path-with_under~tilde",
|
||||||
|
},
|
||||||
|
.{
|
||||||
|
.url = "https://example.com/sub-delims!$&'()*+,;=",
|
||||||
|
.expected = "https://example.com/sub-delims!$&'()*+,;=",
|
||||||
|
},
|
||||||
|
.{
|
||||||
|
.url = "https://example.com",
|
||||||
|
.expected = "https://example.com",
|
||||||
|
},
|
||||||
|
.{
|
||||||
|
.url = "https://example.com?query=value",
|
||||||
|
.expected = "https://example.com?query=value",
|
||||||
|
},
|
||||||
|
.{
|
||||||
|
.url = "https://example.com/clean/path",
|
||||||
|
.expected = "https://example.com/clean/path",
|
||||||
|
},
|
||||||
|
.{
|
||||||
|
.url = "https://example.com/path?clean=query#clean-fragment",
|
||||||
|
.expected = "https://example.com/path?clean=query#clean-fragment",
|
||||||
|
},
|
||||||
|
.{
|
||||||
|
.url = "https://example.com/100% complete",
|
||||||
|
.expected = "https://example.com/100%25%20complete",
|
||||||
|
},
|
||||||
|
.{
|
||||||
|
.url = "https://example.com/path?value=100% done",
|
||||||
|
.expected = "https://example.com/path?value=100%25%20done",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
for (cases) |case| {
|
||||||
|
const result = try ensureEncoded(testing.arena_allocator, case.url);
|
||||||
|
try testing.expectString(case.expected, result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
test "URL: resolve with encoding" {
|
test "URL: resolve with encoding" {
|
||||||
defer testing.reset();
|
defer testing.reset();
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ const lp = @import("lightpanda");
|
|||||||
const id = @import("../id.zig");
|
const id = @import("../id.zig");
|
||||||
const log = @import("../../log.zig");
|
const log = @import("../../log.zig");
|
||||||
const js = @import("../../browser/js/js.zig");
|
const js = @import("../../browser/js/js.zig");
|
||||||
|
const URL = @import("../../browser/URL.zig");
|
||||||
const Page = @import("../../browser/Page.zig");
|
const Page = @import("../../browser/Page.zig");
|
||||||
const timestampF = @import("../../datetime.zig").timestamp;
|
const timestampF = @import("../../datetime.zig").timestamp;
|
||||||
const Notification = @import("../../Notification.zig");
|
const Notification = @import("../../Notification.zig");
|
||||||
@@ -224,7 +225,8 @@ fn navigate(cmd: anytype) !void {
|
|||||||
page = try session.replacePage();
|
page = try session.replacePage();
|
||||||
}
|
}
|
||||||
|
|
||||||
try page.navigate(params.url, .{
|
const encoded_url = try URL.ensureEncoded(page.call_arena, params.url);
|
||||||
|
try page.navigate(encoded_url, .{
|
||||||
.reason = .address_bar,
|
.reason = .address_bar,
|
||||||
.cdp_id = cmd.input.id,
|
.cdp_id = cmd.input.id,
|
||||||
.kind = .{ .push = null },
|
.kind = .{ .push = null },
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ const lp = @import("lightpanda");
|
|||||||
|
|
||||||
const id = @import("../id.zig");
|
const id = @import("../id.zig");
|
||||||
const log = @import("../../log.zig");
|
const log = @import("../../log.zig");
|
||||||
|
const URL = @import("../../browser/URL.zig");
|
||||||
const js = @import("../../browser/js/js.zig");
|
const js = @import("../../browser/js/js.zig");
|
||||||
|
|
||||||
// TODO: hard coded IDs
|
// TODO: hard coded IDs
|
||||||
@@ -218,8 +219,9 @@ fn createTarget(cmd: anytype) !void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!std.mem.eql(u8, "about:blank", params.url)) {
|
if (!std.mem.eql(u8, "about:blank", params.url)) {
|
||||||
|
const encoded_url = try URL.ensureEncoded(page.call_arena, params.url);
|
||||||
try page.navigate(
|
try page.navigate(
|
||||||
params.url,
|
encoded_url,
|
||||||
.{ .reason = .address_bar, .kind = .{ .push = null } },
|
.{ .reason = .address_bar, .kind = .{ .push = null } },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ const std = @import("std");
|
|||||||
pub const App = @import("App.zig");
|
pub const App = @import("App.zig");
|
||||||
pub const Server = @import("Server.zig");
|
pub const Server = @import("Server.zig");
|
||||||
pub const Config = @import("Config.zig");
|
pub const Config = @import("Config.zig");
|
||||||
|
pub const URL = @import("browser/URL.zig");
|
||||||
pub const Page = @import("browser/Page.zig");
|
pub const Page = @import("browser/Page.zig");
|
||||||
pub const Browser = @import("browser/Browser.zig");
|
pub const Browser = @import("browser/Browser.zig");
|
||||||
pub const Session = @import("browser/Session.zig");
|
pub const Session = @import("browser/Session.zig");
|
||||||
@@ -92,7 +93,8 @@ pub fn fetch(app: *App, url: [:0]const u8, opts: FetchOpts) !void {
|
|||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
_ = try page.navigate(url, .{
|
const encoded_url = try URL.ensureEncoded(page.call_arena, url);
|
||||||
|
_ = try page.navigate(encoded_url, .{
|
||||||
.reason = .address_bar,
|
.reason = .address_bar,
|
||||||
.kind = .{ .push = null },
|
.kind = .{ .push = null },
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user