diff --git a/src/browser/dom/document.zig b/src/browser/dom/document.zig index fe4a499a..a9fc365b 100644 --- a/src/browser/dom/document.zig +++ b/src/browser/dom/document.zig @@ -32,6 +32,7 @@ const css = @import("css.zig"); const Element = @import("element.zig").Element; const ElementUnion = @import("element.zig").Union; const TreeWalker = @import("tree_walker.zig").TreeWalker; +const Range = @import("range.zig").Range; const Env = @import("../env.zig").Env; @@ -290,6 +291,10 @@ pub const Document = struct { const state = try page.getOrCreateNodeState(@alignCast(@ptrCast(self))); state.active_element = @ptrCast(e); } + + pub fn _createRange(_: *parser.Document) Range { + return Range.constructor(); + } }; const testing = @import("../../testing.zig"); diff --git a/src/browser/dom/dom.zig b/src/browser/dom/dom.zig index ecc8ede2..a82a80ed 100644 --- a/src/browser/dom/dom.zig +++ b/src/browser/dom/dom.zig @@ -47,4 +47,5 @@ pub const Interfaces = .{ NodeFilter, @import("performance.zig").Interfaces, PerformanceObserver, + @import("range.zig").Interfaces, }; diff --git a/src/browser/dom/range.zig b/src/browser/dom/range.zig new file mode 100644 index 00000000..b6a637a4 --- /dev/null +++ b/src/browser/dom/range.zig @@ -0,0 +1,120 @@ +// Copyright (C) 2023-2024 Lightpanda (Selecy SAS) +// +// Francis Bouvier +// Pierre Tachoire +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +const std = @import("std"); + +const parser = @import("../netsurf.zig"); +const Page = @import("../page.zig").Page; + +pub const Interfaces = .{ + AbstractRange, + Range, +}; + +pub const AbstractRange = struct { + collapsed: bool, + end_container: *parser.Node, + end_offset: i32, + start_container: *parser.Node, + start_offset: i32, + + pub fn updateCollapsed(self: *AbstractRange) void { + // TODO: Eventually, compare properly. + self.collapsed = false; + } + + pub fn get_collapsed(self: *const AbstractRange) bool { + return self.collapsed; + } + + pub fn get_endContainer(self: *const AbstractRange) *parser.Node { + return self.end_container; + } + + pub fn get_endOffset(self: *const AbstractRange) i32 { + return self.end_offset; + } + + pub fn get_startContainer(self: *const AbstractRange) *parser.Node { + return self.start_container; + } + + pub fn get_startOffset(self: *const AbstractRange) i32 { + return self.start_offset; + } +}; + +pub const Range = struct { + pub const prototype = *AbstractRange; + + proto: AbstractRange, + + pub fn constructor() Range { + const proto: AbstractRange = .{ + .collapsed = true, + .end_container = undefined, + .end_offset = 0, + .start_container = undefined, + .start_offset = 0, + }; + + return .{ .proto = proto }; + } + + pub fn _setStart(self: *Range, node: *parser.Node, offset: i32) void { + self.proto.start_container = node; + self.proto.start_offset = offset; + self.proto.updateCollapsed(); + } + + pub fn _setEnd(self: *Range, node: *parser.Node, offset: i32) void { + self.proto.end_container = node; + self.proto.end_offset = offset; + self.proto.updateCollapsed(); + } + + pub fn _createContextualFragment(_: *Range, fragment: []const u8, page: *Page) !*parser.DocumentFragment { + const document_html = page.window.document; + const document = parser.documentHTMLToDocument(document_html); + const doc_frag = try parser.documentParseFragmentFromStr(document, fragment); + return doc_frag; + } +}; + +const testing = @import("../../testing.zig"); +test "Browser.Range" { + var runner = try testing.jsRunner(testing.tracking_allocator, .{}); + defer runner.deinit(); + + try runner.testCases(&.{ + // Test Range constructor + .{ "let range = new Range()", "undefined" }, + .{ "range instanceof Range", "true" }, + .{ "range instanceof AbstractRange", "true" }, + + // Test initial state - collapsed range + .{ "range.collapsed", "true" }, + .{ "range.startOffset", "0" }, + .{ "range.endOffset", "0" }, + + // Test document.createRange() + .{ "let docRange = document.createRange()", "undefined" }, + .{ "docRange instanceof Range", "true" }, + .{ "docRange.collapsed", "true" }, + }, .{}); +}