mirror of
https://github.com/lightpanda-io/browser.git
synced 2025-10-29 15:13:28 +00:00
Compare commits
1 Commits
compilatio
...
url-set-pa
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1dab607369 |
9
.github/workflows/build.yml
vendored
9
.github/workflows/build.yml
vendored
@@ -98,8 +98,6 @@ jobs:
|
||||
ARCH: aarch64
|
||||
OS: macos
|
||||
|
||||
# macos-14 runs on arm CPU. see
|
||||
# https://github.com/actions/runner-images?tab=readme-ov-file
|
||||
runs-on: macos-14
|
||||
timeout-minutes: 15
|
||||
|
||||
@@ -138,12 +136,7 @@ jobs:
|
||||
ARCH: x86_64
|
||||
OS: macos
|
||||
|
||||
# macos-13 runs on x86 CPU. see
|
||||
# https://github.com/actions/runner-images?tab=readme-ov-file
|
||||
# If we want to build for macos-14 or superior, we need to switch to
|
||||
# macos-14-large.
|
||||
# No need for now, but maybe we will need it in the short term.
|
||||
runs-on: macos-13
|
||||
runs-on: macos-14
|
||||
timeout-minutes: 15
|
||||
|
||||
steps:
|
||||
|
||||
@@ -29,7 +29,6 @@
|
||||
const Env = @import("env.zig").Env;
|
||||
const parser = @import("netsurf.zig");
|
||||
const DataSet = @import("html/DataSet.zig");
|
||||
const ShadowRoot = @import("dom/shadow_root.zig").ShadowRoot;
|
||||
const CSSStyleDeclaration = @import("cssom/css_style_declaration.zig").CSSStyleDeclaration;
|
||||
|
||||
// for HTMLScript (but probably needs to be added to more)
|
||||
@@ -63,8 +62,6 @@ explicit_index_set: bool = false,
|
||||
|
||||
template_content: ?*parser.DocumentFragment = null,
|
||||
|
||||
shadow_root: ?*ShadowRoot = null,
|
||||
|
||||
const ReadyState = enum {
|
||||
loading,
|
||||
interactive,
|
||||
|
||||
@@ -204,7 +204,7 @@ pub const Parser = struct {
|
||||
}
|
||||
|
||||
const c = p.s[p.i];
|
||||
if (!(nameStart(c) or c == '\\')) {
|
||||
if (!nameStart(c) or c == '\\') {
|
||||
return ParseError.ExpectedSelector;
|
||||
}
|
||||
|
||||
@@ -582,7 +582,6 @@ pub const Parser = struct {
|
||||
.only_of_type => return .{ .pseudo_class_only_child = true },
|
||||
.input, .empty, .root, .link => return .{ .pseudo_class = pseudo_class },
|
||||
.enabled, .disabled, .checked => return .{ .pseudo_class = pseudo_class },
|
||||
.visible => return .{ .pseudo_class = pseudo_class },
|
||||
.lang => {
|
||||
if (!p.consumeParenthesis()) return ParseError.ExpectedParenthesis;
|
||||
if (p.i == p.s.len) return ParseError.UnmatchParenthesis;
|
||||
@@ -606,7 +605,7 @@ pub const Parser = struct {
|
||||
.after, .backdrop, .before, .cue, .first_letter => return .{ .pseudo_element = pseudo_class },
|
||||
.first_line, .grammar_error, .marker, .placeholder => return .{ .pseudo_element = pseudo_class },
|
||||
.selection, .spelling_error => return .{ .pseudo_element = pseudo_class },
|
||||
.modal, .popover_open => return .{ .pseudo_element = pseudo_class },
|
||||
.modal => return .{ .pseudo_element = pseudo_class },
|
||||
}
|
||||
}
|
||||
|
||||
@@ -950,36 +949,3 @@ test "parser.parseString" {
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
test "parser.parse" {
|
||||
var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
|
||||
defer arena.deinit();
|
||||
const alloc = arena.allocator();
|
||||
|
||||
const testcases = [_]struct {
|
||||
s: []const u8, // given value
|
||||
exp: Selector, // expected value
|
||||
err: bool = false,
|
||||
}{
|
||||
.{ .s = "root", .exp = .{ .tag = "root" } },
|
||||
.{ .s = ".root", .exp = .{ .class = "root" } },
|
||||
.{ .s = ":root", .exp = .{ .pseudo_class = .root } },
|
||||
.{ .s = ".\\:bar", .exp = .{ .class = ":bar" } },
|
||||
.{ .s = ".foo\\:bar", .exp = .{ .class = "foo:bar" } },
|
||||
};
|
||||
|
||||
for (testcases) |tc| {
|
||||
var p = Parser{ .s = tc.s, .opts = .{} };
|
||||
const sel = p.parse(alloc) catch |e| {
|
||||
// if error was expected, continue.
|
||||
if (tc.err) continue;
|
||||
|
||||
std.debug.print("test case {s}\n", .{tc.s});
|
||||
return e;
|
||||
};
|
||||
std.testing.expectEqualDeep(tc.exp, sel) catch |e| {
|
||||
std.debug.print("test case {s} : {}\n", .{ tc.s, sel });
|
||||
return e;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,8 +99,6 @@ pub const PseudoClass = enum {
|
||||
selection,
|
||||
spelling_error,
|
||||
modal,
|
||||
popover_open,
|
||||
visible,
|
||||
|
||||
pub const Error = error{
|
||||
InvalidPseudoClass,
|
||||
@@ -116,108 +114,52 @@ pub const PseudoClass = enum {
|
||||
}
|
||||
|
||||
pub fn parse(s: []const u8) Error!PseudoClass {
|
||||
const longest_selector = "nth-last-of-type";
|
||||
if (s.len > longest_selector.len) {
|
||||
return Error.InvalidPseudoClass;
|
||||
}
|
||||
|
||||
var buf: [longest_selector.len]u8 = undefined;
|
||||
const selector = std.ascii.lowerString(&buf, s);
|
||||
|
||||
switch (selector.len) {
|
||||
3 => switch (@as(u24, @bitCast(selector[0..3].*))) {
|
||||
asUint(u24, "cue") => return .cue,
|
||||
asUint(u24, "has") => return .has,
|
||||
asUint(u24, "not") => return .not,
|
||||
else => {},
|
||||
},
|
||||
4 => switch (@as(u32, @bitCast(selector[0..4].*))) {
|
||||
asUint(u32, "lang") => return .lang,
|
||||
asUint(u32, "link") => return .link,
|
||||
asUint(u32, "root") => return .root,
|
||||
else => {},
|
||||
},
|
||||
5 => switch (@as(u40, @bitCast(selector[0..5].*))) {
|
||||
asUint(u40, "after") => return .after,
|
||||
asUint(u40, "empty") => return .empty,
|
||||
asUint(u40, "focus") => return .focus,
|
||||
asUint(u40, "hover") => return .hover,
|
||||
asUint(u40, "input") => return .input,
|
||||
asUint(u40, "modal") => return .modal,
|
||||
else => {},
|
||||
},
|
||||
6 => switch (@as(u48, @bitCast(selector[0..6].*))) {
|
||||
asUint(u48, "active") => return .active,
|
||||
asUint(u48, "before") => return .before,
|
||||
asUint(u48, "marker") => return .marker,
|
||||
asUint(u48, "target") => return .target,
|
||||
else => {},
|
||||
},
|
||||
7 => switch (@as(u56, @bitCast(selector[0..7].*))) {
|
||||
asUint(u56, "checked") => return .checked,
|
||||
asUint(u56, "enabled") => return .enabled,
|
||||
asUint(u56, "matches") => return .matches,
|
||||
asUint(u56, "visited") => return .visited,
|
||||
asUint(u56, "visible") => return .visible,
|
||||
else => {},
|
||||
},
|
||||
8 => switch (@as(u64, @bitCast(selector[0..8].*))) {
|
||||
asUint(u64, "backdrop") => return .backdrop,
|
||||
asUint(u64, "contains") => return .contains,
|
||||
asUint(u64, "disabled") => return .disabled,
|
||||
asUint(u64, "haschild") => return .haschild,
|
||||
else => {},
|
||||
},
|
||||
9 => switch (@as(u72, @bitCast(selector[0..9].*))) {
|
||||
asUint(u72, "nth-child") => return .nth_child,
|
||||
asUint(u72, "selection") => return .selection,
|
||||
else => {},
|
||||
},
|
||||
10 => switch (@as(u80, @bitCast(selector[0..10].*))) {
|
||||
asUint(u80, "first-line") => return .first_line,
|
||||
asUint(u80, "last-child") => return .last_child,
|
||||
asUint(u80, "matchesown") => return .matchesown,
|
||||
asUint(u80, "only-child") => return .only_child,
|
||||
else => {},
|
||||
},
|
||||
11 => switch (@as(u88, @bitCast(selector[0..11].*))) {
|
||||
asUint(u88, "containsown") => return .containsown,
|
||||
asUint(u88, "first-child") => return .first_child,
|
||||
asUint(u88, "nth-of-type") => return .nth_of_type,
|
||||
asUint(u88, "placeholder") => return .placeholder,
|
||||
else => {},
|
||||
},
|
||||
12 => switch (@as(u96, @bitCast(selector[0..12].*))) {
|
||||
asUint(u96, "first-letter") => return .first_letter,
|
||||
asUint(u96, "last-of-type") => return .last_of_type,
|
||||
asUint(u96, "only-of-type") => return .only_of_type,
|
||||
asUint(u96, "popover-open") => return .popover_open,
|
||||
else => {},
|
||||
},
|
||||
13 => switch (@as(u104, @bitCast(selector[0..13].*))) {
|
||||
asUint(u104, "first-of-type") => return .first_of_type,
|
||||
asUint(u104, "grammar-error") => return .grammar_error,
|
||||
else => {},
|
||||
},
|
||||
14 => switch (@as(u112, @bitCast(selector[0..14].*))) {
|
||||
asUint(u112, "nth-last-child") => return .nth_last_child,
|
||||
asUint(u112, "spelling-error") => return .spelling_error,
|
||||
else => {},
|
||||
},
|
||||
16 => switch (@as(u128, @bitCast(selector[0..16].*))) {
|
||||
asUint(u128, "nth-last-of-type") => return .nth_last_of_type,
|
||||
else => {},
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
if (std.ascii.eqlIgnoreCase(s, "not")) return .not;
|
||||
if (std.ascii.eqlIgnoreCase(s, "has")) return .has;
|
||||
if (std.ascii.eqlIgnoreCase(s, "haschild")) return .haschild;
|
||||
if (std.ascii.eqlIgnoreCase(s, "contains")) return .contains;
|
||||
if (std.ascii.eqlIgnoreCase(s, "containsown")) return .containsown;
|
||||
if (std.ascii.eqlIgnoreCase(s, "matches")) return .matches;
|
||||
if (std.ascii.eqlIgnoreCase(s, "matchesown")) return .matchesown;
|
||||
if (std.ascii.eqlIgnoreCase(s, "nth-child")) return .nth_child;
|
||||
if (std.ascii.eqlIgnoreCase(s, "nth-last-child")) return .nth_last_child;
|
||||
if (std.ascii.eqlIgnoreCase(s, "nth-of-type")) return .nth_of_type;
|
||||
if (std.ascii.eqlIgnoreCase(s, "nth-last-of-type")) return .nth_last_of_type;
|
||||
if (std.ascii.eqlIgnoreCase(s, "first-child")) return .first_child;
|
||||
if (std.ascii.eqlIgnoreCase(s, "last-child")) return .last_child;
|
||||
if (std.ascii.eqlIgnoreCase(s, "first-of-type")) return .first_of_type;
|
||||
if (std.ascii.eqlIgnoreCase(s, "last-of-type")) return .last_of_type;
|
||||
if (std.ascii.eqlIgnoreCase(s, "only-child")) return .only_child;
|
||||
if (std.ascii.eqlIgnoreCase(s, "only-of-type")) return .only_of_type;
|
||||
if (std.ascii.eqlIgnoreCase(s, "input")) return .input;
|
||||
if (std.ascii.eqlIgnoreCase(s, "empty")) return .empty;
|
||||
if (std.ascii.eqlIgnoreCase(s, "root")) return .root;
|
||||
if (std.ascii.eqlIgnoreCase(s, "link")) return .link;
|
||||
if (std.ascii.eqlIgnoreCase(s, "lang")) return .lang;
|
||||
if (std.ascii.eqlIgnoreCase(s, "enabled")) return .enabled;
|
||||
if (std.ascii.eqlIgnoreCase(s, "disabled")) return .disabled;
|
||||
if (std.ascii.eqlIgnoreCase(s, "checked")) return .checked;
|
||||
if (std.ascii.eqlIgnoreCase(s, "visited")) return .visited;
|
||||
if (std.ascii.eqlIgnoreCase(s, "hover")) return .hover;
|
||||
if (std.ascii.eqlIgnoreCase(s, "active")) return .active;
|
||||
if (std.ascii.eqlIgnoreCase(s, "focus")) return .focus;
|
||||
if (std.ascii.eqlIgnoreCase(s, "target")) return .target;
|
||||
if (std.ascii.eqlIgnoreCase(s, "after")) return .after;
|
||||
if (std.ascii.eqlIgnoreCase(s, "backdrop")) return .backdrop;
|
||||
if (std.ascii.eqlIgnoreCase(s, "before")) return .before;
|
||||
if (std.ascii.eqlIgnoreCase(s, "cue")) return .cue;
|
||||
if (std.ascii.eqlIgnoreCase(s, "first-letter")) return .first_letter;
|
||||
if (std.ascii.eqlIgnoreCase(s, "first-line")) return .first_line;
|
||||
if (std.ascii.eqlIgnoreCase(s, "grammar-error")) return .grammar_error;
|
||||
if (std.ascii.eqlIgnoreCase(s, "marker")) return .marker;
|
||||
if (std.ascii.eqlIgnoreCase(s, "placeholder")) return .placeholder;
|
||||
if (std.ascii.eqlIgnoreCase(s, "selection")) return .selection;
|
||||
if (std.ascii.eqlIgnoreCase(s, "spelling-error")) return .spelling_error;
|
||||
if (std.ascii.eqlIgnoreCase(s, "modal")) return .modal;
|
||||
return Error.InvalidPseudoClass;
|
||||
}
|
||||
};
|
||||
|
||||
fn asUint(comptime T: type, comptime string: []const u8) T {
|
||||
return @bitCast(string[0..string.len].*);
|
||||
}
|
||||
|
||||
pub const Selector = union(enum) {
|
||||
pub const Error = error{
|
||||
UnknownCombinedCombinator,
|
||||
@@ -569,8 +511,6 @@ pub const Selector = union(enum) {
|
||||
// TODO implement using the url fragment.
|
||||
// see https://developer.mozilla.org/en-US/docs/Web/CSS/:target
|
||||
.target => return false,
|
||||
// visible always returns true.
|
||||
.visible => return true,
|
||||
|
||||
// all others pseudo class are handled by specialized
|
||||
// pseudo_class_X selectors.
|
||||
|
||||
@@ -32,7 +32,6 @@ 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 CSSStyleSheet = @import("../cssom/css_stylesheet.zig").CSSStyleSheet;
|
||||
const Range = @import("range.zig").Range;
|
||||
|
||||
const Env = @import("../env.zig").Env;
|
||||
@@ -123,11 +122,28 @@ pub const Document = struct {
|
||||
return try Element.toInterface(e);
|
||||
}
|
||||
|
||||
pub fn _createElement(self: *parser.Document, tag_name: []const u8) !ElementUnion {
|
||||
// The element’s namespace is the HTML namespace when document is an HTML document
|
||||
// https://dom.spec.whatwg.org/#ref-for-dom-document-createelement%E2%91%A0
|
||||
const e = try parser.documentCreateElementNS(self, "http://www.w3.org/1999/xhtml", tag_name);
|
||||
return Element.toInterface(e);
|
||||
const CreateElementResult = union(enum) {
|
||||
element: ElementUnion,
|
||||
custom: Env.JsObject,
|
||||
};
|
||||
|
||||
pub fn _createElement(self: *parser.Document, tag_name: []const u8, page: *Page) !CreateElementResult {
|
||||
const custom_element = page.window.custom_elements._get(tag_name) orelse {
|
||||
const e = try parser.documentCreateElement(self, tag_name);
|
||||
return .{ .element = try Element.toInterface(e) };
|
||||
};
|
||||
|
||||
var result: Env.Function.Result = undefined;
|
||||
const js_obj = custom_element.newInstance(&result) catch |err| {
|
||||
log.fatal(.user_script, "newInstance error", .{
|
||||
.err = result.exception,
|
||||
.stack = result.stack,
|
||||
.tag_name = tag_name,
|
||||
.source = "createElement",
|
||||
});
|
||||
return err;
|
||||
};
|
||||
return .{ .custom = js_obj };
|
||||
}
|
||||
|
||||
pub fn _createElementNS(self: *parser.Document, ns: []const u8, tag_name: []const u8) !ElementUnion {
|
||||
@@ -279,11 +295,6 @@ pub const Document = struct {
|
||||
pub fn _createRange(_: *parser.Document, page: *Page) Range {
|
||||
return Range.constructor(page);
|
||||
}
|
||||
|
||||
// TODO: dummy implementation
|
||||
pub fn get_styleSheets(_: *parser.Document) []CSSStyleSheet {
|
||||
return &.{};
|
||||
}
|
||||
};
|
||||
|
||||
const testing = @import("../../testing.zig");
|
||||
@@ -452,9 +463,6 @@ test "Browser.DOM.Document" {
|
||||
,
|
||||
"1",
|
||||
},
|
||||
|
||||
.{ "document.querySelectorAll('.\\\\:popover-open').length", "0" },
|
||||
.{ "document.querySelectorAll('.foo\\\\:bar').length", "0" },
|
||||
}, .{});
|
||||
|
||||
try runner.testCases(&.{
|
||||
@@ -463,10 +471,6 @@ test "Browser.DOM.Document" {
|
||||
.{ "document.activeElement === document.getElementById('link')", "true" },
|
||||
}, .{});
|
||||
|
||||
try runner.testCases(&.{
|
||||
.{ "document.styleSheets.length", "0" },
|
||||
}, .{});
|
||||
|
||||
// this test breaks the doc structure, keep it at the end of the test
|
||||
// suite.
|
||||
try runner.testCases(&.{
|
||||
|
||||
@@ -30,8 +30,6 @@ const Node = @import("node.zig").Node;
|
||||
const Walker = @import("walker.zig").WalkerDepthFirst;
|
||||
const NodeList = @import("nodelist.zig").NodeList;
|
||||
const HTMLElem = @import("../html/elements.zig");
|
||||
const ShadowRoot = @import("../dom/shadow_root.zig").ShadowRoot;
|
||||
|
||||
pub const Union = @import("../html/elements.zig").Union;
|
||||
|
||||
// WEB IDL https://dom.spec.whatwg.org/#element
|
||||
@@ -129,40 +127,16 @@ pub const Element = struct {
|
||||
// remove existing children
|
||||
try Node.removeChildren(node);
|
||||
|
||||
// I'm not sure what the exact behavior is supposed to be. Initially,
|
||||
// we were only copying the body of the document fragment. But it seems
|
||||
// like head elements should be copied too. Specifically, some sites
|
||||
// create script tags via innerHTML, which we need to capture.
|
||||
// If you play with this in a browser, you should notice that the
|
||||
// behavior is different depending on whether you're in a blank page
|
||||
// or an actual document. In a blank page, something like:
|
||||
// x.innerHTML = '<script></script>';
|
||||
// does _not_ create an empty script, but in a real page, it does. Weird.
|
||||
const fragment_node = parser.documentFragmentToNode(fragment);
|
||||
const html = try parser.nodeFirstChild(fragment_node) orelse return;
|
||||
const head = try parser.nodeFirstChild(html) orelse return;
|
||||
{
|
||||
// First, copy some of the head element
|
||||
const children = try parser.nodeGetChildNodes(head);
|
||||
const ln = try parser.nodeListLength(children);
|
||||
for (0..ln) |_| {
|
||||
// always index 0, because nodeAppendChild moves the node out of
|
||||
// the nodeList and into the new tree
|
||||
const child = try parser.nodeListItem(children, 0) orelse continue;
|
||||
_ = try parser.nodeAppendChild(node, child);
|
||||
}
|
||||
}
|
||||
// get fragment body children
|
||||
const children = try parser.documentFragmentBodyChildren(fragment) orelse return;
|
||||
|
||||
{
|
||||
const body = try parser.nodeNextSibling(head) orelse return;
|
||||
const children = try parser.nodeGetChildNodes(body);
|
||||
const ln = try parser.nodeListLength(children);
|
||||
for (0..ln) |_| {
|
||||
// always index 0, because nodeAppendChild moves the node out of
|
||||
// the nodeList and into the new tree
|
||||
const child = try parser.nodeListItem(children, 0) orelse continue;
|
||||
_ = try parser.nodeAppendChild(node, child);
|
||||
}
|
||||
// append children to the node
|
||||
const ln = try parser.nodeListLength(children);
|
||||
for (0..ln) |_| {
|
||||
// always index 0, because ndoeAppendChild moves the node out of
|
||||
// the nodeList and into the new tree
|
||||
const child = try parser.nodeListItem(children, 0) orelse continue;
|
||||
_ = try parser.nodeAppendChild(node, child);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -186,14 +160,8 @@ pub const Element = struct {
|
||||
}
|
||||
}
|
||||
|
||||
// don't use parser.nodeHasAttributes(...) because that returns true/false
|
||||
// based on the type, e.g. a node never as attributes, an element always has
|
||||
// attributes. But, Element.hasAttributes is supposed to return true only
|
||||
// if the element has at least 1 attribute.
|
||||
pub fn _hasAttributes(self: *parser.Element) !bool {
|
||||
// an element _must_ have at least an empty attribute
|
||||
const node_map = try parser.nodeGetAttributes(parser.elementToNode(self)) orelse unreachable;
|
||||
return try parser.namedNodeMapGetLength(node_map) > 0;
|
||||
return try parser.nodeHasAttributes(parser.elementToNode(self));
|
||||
}
|
||||
|
||||
pub fn _getAttribute(self: *parser.Element, qname: []const u8) !?[]const u8 {
|
||||
@@ -461,44 +429,6 @@ pub const Element = struct {
|
||||
_ = opts;
|
||||
return true;
|
||||
}
|
||||
|
||||
const AttachShadowOpts = struct {
|
||||
mode: []const u8, // must be specified
|
||||
};
|
||||
pub fn _attachShadow(self: *parser.Element, opts: AttachShadowOpts, page: *Page) !*ShadowRoot {
|
||||
const mode = std.meta.stringToEnum(ShadowRoot.Mode, opts.mode) orelse return error.InvalidArgument;
|
||||
const state = try page.getOrCreateNodeState(@alignCast(@ptrCast(self)));
|
||||
if (state.shadow_root) |sr| {
|
||||
if (mode != sr.mode) {
|
||||
// this is the behavior per the spec
|
||||
return error.NotSupportedError;
|
||||
}
|
||||
|
||||
// TODO: the existing shadow root should be cleared!
|
||||
return sr;
|
||||
}
|
||||
|
||||
// Not sure what to do if there is no owner document
|
||||
const doc = try parser.nodeOwnerDocument(@ptrCast(self)) orelse return error.InvalidArgument;
|
||||
const fragment = try parser.documentCreateDocumentFragment(doc);
|
||||
const sr = try page.arena.create(ShadowRoot);
|
||||
sr.* = .{
|
||||
.host = self,
|
||||
.mode = mode,
|
||||
.proto = fragment,
|
||||
};
|
||||
state.shadow_root = sr;
|
||||
return sr;
|
||||
}
|
||||
|
||||
pub fn get_shadowRoot(self: *parser.Element, page: *Page) ?*ShadowRoot {
|
||||
const state = page.getNodeState(@alignCast(@ptrCast(self))) orelse return null;
|
||||
const sr = state.shadow_root orelse return null;
|
||||
if (sr.mode == .closed) {
|
||||
return null;
|
||||
}
|
||||
return sr;
|
||||
}
|
||||
};
|
||||
|
||||
// Tests
|
||||
@@ -749,13 +679,4 @@ test "Browser.DOM.Element" {
|
||||
.{ "div1.innerHTML = \" <link/><table></table><a href='/a'>a</a><input type='checkbox'/>\"", null },
|
||||
.{ "div1.getElementsByTagName('a').length", "1" },
|
||||
}, .{});
|
||||
|
||||
try runner.testCases(&.{
|
||||
.{ "document.createElement('a').hasAttributes()", "false" },
|
||||
.{ "var fc; (fc = document.createElement('div')).innerHTML = '<script><\\/script>'", null },
|
||||
.{ "fc.outerHTML", "<div><script></script></div>" },
|
||||
|
||||
.{ "fc; (fc = document.createElement('div')).innerHTML = '<script><\\/script><p>hello</p>'", null },
|
||||
.{ "fc.outerHTML", "<div><script></script><p>hello</p></div>" },
|
||||
}, .{});
|
||||
}
|
||||
|
||||
@@ -17,39 +17,10 @@
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
const std = @import("std");
|
||||
const Env = @import("../env.zig").Env;
|
||||
|
||||
const PerformanceEntry = @import("performance.zig").PerformanceEntry;
|
||||
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/PerformanceObserver
|
||||
pub const PerformanceObserver = struct {
|
||||
pub const _supportedEntryTypes = [0][]const u8{};
|
||||
|
||||
pub fn constructor(cbk: Env.Function) PerformanceObserver {
|
||||
_ = cbk;
|
||||
return .{};
|
||||
}
|
||||
|
||||
pub fn _observe(self: *const PerformanceObserver, options_: ?Options) void {
|
||||
_ = self;
|
||||
_ = options_;
|
||||
return;
|
||||
}
|
||||
|
||||
pub fn _disconnect(self: *PerformanceObserver) void {
|
||||
_ = self;
|
||||
}
|
||||
|
||||
pub fn _takeRecords(_: *const PerformanceObserver) []PerformanceEntry {
|
||||
return &[_]PerformanceEntry{};
|
||||
}
|
||||
};
|
||||
|
||||
const Options = struct {
|
||||
buffered: ?bool = null,
|
||||
durationThreshold: ?f64 = null,
|
||||
entryTypes: ?[]const []const u8 = null,
|
||||
type: ?[]const u8 = null,
|
||||
};
|
||||
|
||||
const testing = @import("../../testing.zig");
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
// Copyright (C) 2023-2025 Lightpanda (Selecy SAS)
|
||||
//
|
||||
// Francis Bouvier <francis@lightpanda.io>
|
||||
// Pierre Tachoire <pierre@lightpanda.io>
|
||||
//
|
||||
// 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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
const std = @import("std");
|
||||
const parser = @import("../netsurf.zig");
|
||||
const Element = @import("element.zig").Element;
|
||||
const ElementUnion = @import("element.zig").Union;
|
||||
|
||||
// WEB IDL https://dom.spec.whatwg.org/#interface-shadowroot
|
||||
pub const ShadowRoot = struct {
|
||||
pub const prototype = *parser.DocumentFragment;
|
||||
pub const subtype = .node;
|
||||
|
||||
mode: Mode,
|
||||
host: *parser.Element,
|
||||
proto: *parser.DocumentFragment,
|
||||
|
||||
pub const Mode = enum {
|
||||
open,
|
||||
closed,
|
||||
};
|
||||
|
||||
pub fn get_host(self: *const ShadowRoot) !ElementUnion {
|
||||
return Element.toInterface(self.host);
|
||||
}
|
||||
};
|
||||
|
||||
const testing = @import("../../testing.zig");
|
||||
test "Browser.DOM.ShadowRoot" {
|
||||
defer testing.reset();
|
||||
|
||||
var runner = try testing.jsRunner(testing.tracking_allocator, .{ .html = "" });
|
||||
defer runner.deinit();
|
||||
|
||||
try runner.testCases(&.{
|
||||
.{ "const div1 = document.createElement('div');", null },
|
||||
.{ "let sr1 = div1.attachShadow({mode: 'open'})", null },
|
||||
.{ "sr1.host == div1", "true" },
|
||||
.{ "div1.attachShadow({mode: 'open'}) == sr1", "true" },
|
||||
.{ "div1.shadowRoot == sr1", "true" },
|
||||
|
||||
.{ "try { div1.attachShadow({mode: 'closed'}) } catch (e) { e }", "Error: NotSupportedError" },
|
||||
}, .{});
|
||||
|
||||
try runner.testCases(&.{
|
||||
.{ "const div2 = document.createElement('di2');", null },
|
||||
.{ "let sr2 = div2.attachShadow({mode: 'closed'})", null },
|
||||
.{ "sr2.host == div2", "true" },
|
||||
.{ "div2.shadowRoot", "null" }, // null when attached with 'closed'
|
||||
}, .{});
|
||||
}
|
||||
@@ -25,7 +25,6 @@ const WebApis = struct {
|
||||
@import("css/css.zig").Interfaces,
|
||||
@import("cssom/cssom.zig").Interfaces,
|
||||
@import("dom/dom.zig").Interfaces,
|
||||
@import("dom/shadow_root.zig").ShadowRoot,
|
||||
@import("encoding/text_encoder.zig").Interfaces,
|
||||
@import("events/event.zig").Interfaces,
|
||||
@import("html/html.zig").Interfaces,
|
||||
@@ -35,6 +34,7 @@ const WebApis = struct {
|
||||
@import("xhr/xhr.zig").Interfaces,
|
||||
@import("xhr/form_data.zig").Interfaces,
|
||||
@import("xmlserializer/xmlserializer.zig").Interfaces,
|
||||
@import("webcomponents/webcomponents.zig").Interfaces,
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -42,12 +42,8 @@ pub const HTMLDocument = struct {
|
||||
// JS funcs
|
||||
// --------
|
||||
|
||||
pub fn get_domain(self: *parser.DocumentHTML, page: *Page) ![]const u8 {
|
||||
// libdom's document_html get_domain always returns null, this is
|
||||
// the way MDN recommends getting the domain anyways, since document.domain
|
||||
// is deprecated.
|
||||
const location = try parser.documentHTMLGetLocation(Location, self) orelse return "";
|
||||
return location.get_host(page);
|
||||
pub fn get_domain(self: *parser.DocumentHTML) ![]const u8 {
|
||||
return try parser.documentHTMLGetDomain(self);
|
||||
}
|
||||
|
||||
pub fn set_domain(_: *parser.DocumentHTML, _: []const u8) ![]const u8 {
|
||||
@@ -311,7 +307,7 @@ test "Browser.HTML.Document" {
|
||||
}, .{});
|
||||
|
||||
try runner.testCases(&.{
|
||||
.{ "document.domain", "lightpanda.io" },
|
||||
.{ "document.domain", "" },
|
||||
.{ "document.referrer", "" },
|
||||
.{ "document.title", "" },
|
||||
.{ "document.body.localName", "body" },
|
||||
|
||||
@@ -114,6 +114,10 @@ pub const HTMLElement = struct {
|
||||
pub const prototype = *Element;
|
||||
pub const subtype = .node;
|
||||
|
||||
pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element {
|
||||
return constructHtmlElement(page, js_this);
|
||||
}
|
||||
|
||||
pub fn get_style(e: *parser.ElementHTML, page: *Page) !*CSSStyleDeclaration {
|
||||
const state = try page.getOrCreateNodeState(@ptrCast(e));
|
||||
return &state.style;
|
||||
@@ -185,6 +189,10 @@ pub const HTMLMediaElement = struct {
|
||||
pub const Self = parser.MediaElement;
|
||||
pub const prototype = *HTMLElement;
|
||||
pub const subtype = .node;
|
||||
|
||||
pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element {
|
||||
return constructHtmlElement(page, js_this);
|
||||
}
|
||||
};
|
||||
|
||||
// HTML elements
|
||||
@@ -194,6 +202,10 @@ pub const HTMLUnknownElement = struct {
|
||||
pub const Self = parser.Unknown;
|
||||
pub const prototype = *HTMLElement;
|
||||
pub const subtype = .node;
|
||||
|
||||
pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element {
|
||||
return constructHtmlElement(page, js_this);
|
||||
}
|
||||
};
|
||||
|
||||
// https://html.spec.whatwg.org/#the-a-element
|
||||
@@ -202,6 +214,10 @@ pub const HTMLAnchorElement = struct {
|
||||
pub const prototype = *HTMLElement;
|
||||
pub const subtype = .node;
|
||||
|
||||
pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element {
|
||||
return constructHtmlElement(page, js_this);
|
||||
}
|
||||
|
||||
pub fn get_target(self: *parser.Anchor) ![]const u8 {
|
||||
return try parser.anchorGetTarget(self);
|
||||
}
|
||||
@@ -449,144 +465,240 @@ pub const HTMLAppletElement = struct {
|
||||
pub const Self = parser.Applet;
|
||||
pub const prototype = *HTMLElement;
|
||||
pub const subtype = .node;
|
||||
|
||||
pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element {
|
||||
return constructHtmlElement(page, js_this);
|
||||
}
|
||||
};
|
||||
|
||||
pub const HTMLAreaElement = struct {
|
||||
pub const Self = parser.Area;
|
||||
pub const prototype = *HTMLElement;
|
||||
pub const subtype = .node;
|
||||
|
||||
pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element {
|
||||
return constructHtmlElement(page, js_this);
|
||||
}
|
||||
};
|
||||
|
||||
pub const HTMLAudioElement = struct {
|
||||
pub const Self = parser.Audio;
|
||||
pub const prototype = *HTMLMediaElement;
|
||||
pub const subtype = .node;
|
||||
|
||||
pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element {
|
||||
return constructHtmlElement(page, js_this);
|
||||
}
|
||||
};
|
||||
|
||||
pub const HTMLBRElement = struct {
|
||||
pub const Self = parser.BR;
|
||||
pub const prototype = *HTMLElement;
|
||||
pub const subtype = .node;
|
||||
|
||||
pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element {
|
||||
return constructHtmlElement(page, js_this);
|
||||
}
|
||||
};
|
||||
|
||||
pub const HTMLBaseElement = struct {
|
||||
pub const Self = parser.Base;
|
||||
pub const prototype = *HTMLElement;
|
||||
pub const subtype = .node;
|
||||
|
||||
pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element {
|
||||
return constructHtmlElement(page, js_this);
|
||||
}
|
||||
};
|
||||
|
||||
pub const HTMLBodyElement = struct {
|
||||
pub const Self = parser.Body;
|
||||
pub const prototype = *HTMLElement;
|
||||
pub const subtype = .node;
|
||||
|
||||
pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element {
|
||||
return constructHtmlElement(page, js_this);
|
||||
}
|
||||
};
|
||||
|
||||
pub const HTMLButtonElement = struct {
|
||||
pub const Self = parser.Button;
|
||||
pub const prototype = *HTMLElement;
|
||||
pub const subtype = .node;
|
||||
|
||||
pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element {
|
||||
return constructHtmlElement(page, js_this);
|
||||
}
|
||||
};
|
||||
|
||||
pub const HTMLCanvasElement = struct {
|
||||
pub const Self = parser.Canvas;
|
||||
pub const prototype = *HTMLElement;
|
||||
pub const subtype = .node;
|
||||
|
||||
pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element {
|
||||
return constructHtmlElement(page, js_this);
|
||||
}
|
||||
};
|
||||
|
||||
pub const HTMLDListElement = struct {
|
||||
pub const Self = parser.DList;
|
||||
pub const prototype = *HTMLElement;
|
||||
pub const subtype = .node;
|
||||
|
||||
pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element {
|
||||
return constructHtmlElement(page, js_this);
|
||||
}
|
||||
};
|
||||
|
||||
pub const HTMLDataElement = struct {
|
||||
pub const Self = parser.Data;
|
||||
pub const prototype = *HTMLElement;
|
||||
pub const subtype = .node;
|
||||
|
||||
pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element {
|
||||
return constructHtmlElement(page, js_this);
|
||||
}
|
||||
};
|
||||
|
||||
pub const HTMLDataListElement = struct {
|
||||
pub const Self = parser.DataList;
|
||||
pub const prototype = *HTMLElement;
|
||||
pub const subtype = .node;
|
||||
|
||||
pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element {
|
||||
return constructHtmlElement(page, js_this);
|
||||
}
|
||||
};
|
||||
|
||||
pub const HTMLDialogElement = struct {
|
||||
pub const Self = parser.Dialog;
|
||||
pub const prototype = *HTMLElement;
|
||||
pub const subtype = .node;
|
||||
|
||||
pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element {
|
||||
return constructHtmlElement(page, js_this);
|
||||
}
|
||||
};
|
||||
|
||||
pub const HTMLDirectoryElement = struct {
|
||||
pub const Self = parser.Directory;
|
||||
pub const prototype = *HTMLElement;
|
||||
pub const subtype = .node;
|
||||
|
||||
pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element {
|
||||
return constructHtmlElement(page, js_this);
|
||||
}
|
||||
};
|
||||
|
||||
pub const HTMLDivElement = struct {
|
||||
pub const Self = parser.Div;
|
||||
pub const prototype = *HTMLElement;
|
||||
pub const subtype = .node;
|
||||
|
||||
pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element {
|
||||
return constructHtmlElement(page, js_this);
|
||||
}
|
||||
};
|
||||
|
||||
pub const HTMLEmbedElement = struct {
|
||||
pub const Self = parser.Embed;
|
||||
pub const prototype = *HTMLElement;
|
||||
pub const subtype = .node;
|
||||
|
||||
pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element {
|
||||
return constructHtmlElement(page, js_this);
|
||||
}
|
||||
};
|
||||
|
||||
pub const HTMLFieldSetElement = struct {
|
||||
pub const Self = parser.FieldSet;
|
||||
pub const prototype = *HTMLElement;
|
||||
pub const subtype = .node;
|
||||
|
||||
pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element {
|
||||
return constructHtmlElement(page, js_this);
|
||||
}
|
||||
};
|
||||
|
||||
pub const HTMLFontElement = struct {
|
||||
pub const Self = parser.Font;
|
||||
pub const prototype = *HTMLElement;
|
||||
pub const subtype = .node;
|
||||
|
||||
pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element {
|
||||
return constructHtmlElement(page, js_this);
|
||||
}
|
||||
};
|
||||
|
||||
pub const HTMLFrameElement = struct {
|
||||
pub const Self = parser.Frame;
|
||||
pub const prototype = *HTMLElement;
|
||||
pub const subtype = .node;
|
||||
|
||||
pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element {
|
||||
return constructHtmlElement(page, js_this);
|
||||
}
|
||||
};
|
||||
|
||||
pub const HTMLFrameSetElement = struct {
|
||||
pub const Self = parser.FrameSet;
|
||||
pub const prototype = *HTMLElement;
|
||||
pub const subtype = .node;
|
||||
|
||||
pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element {
|
||||
return constructHtmlElement(page, js_this);
|
||||
}
|
||||
};
|
||||
|
||||
pub const HTMLHRElement = struct {
|
||||
pub const Self = parser.HR;
|
||||
pub const prototype = *HTMLElement;
|
||||
pub const subtype = .node;
|
||||
|
||||
pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element {
|
||||
return constructHtmlElement(page, js_this);
|
||||
}
|
||||
};
|
||||
|
||||
pub const HTMLHeadElement = struct {
|
||||
pub const Self = parser.Head;
|
||||
pub const prototype = *HTMLElement;
|
||||
pub const subtype = .node;
|
||||
|
||||
pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element {
|
||||
return constructHtmlElement(page, js_this);
|
||||
}
|
||||
};
|
||||
|
||||
pub const HTMLHeadingElement = struct {
|
||||
pub const Self = parser.Heading;
|
||||
pub const prototype = *HTMLElement;
|
||||
pub const subtype = .node;
|
||||
|
||||
pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element {
|
||||
return constructHtmlElement(page, js_this);
|
||||
}
|
||||
};
|
||||
|
||||
pub const HTMLHtmlElement = struct {
|
||||
pub const Self = parser.Html;
|
||||
pub const prototype = *HTMLElement;
|
||||
pub const subtype = .node;
|
||||
|
||||
pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element {
|
||||
return constructHtmlElement(page, js_this);
|
||||
}
|
||||
};
|
||||
|
||||
pub const HTMLIFrameElement = struct {
|
||||
pub const Self = parser.IFrame;
|
||||
pub const prototype = *HTMLElement;
|
||||
pub const subtype = .node;
|
||||
|
||||
pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element {
|
||||
return constructHtmlElement(page, js_this);
|
||||
}
|
||||
};
|
||||
|
||||
pub const HTMLImageElement = struct {
|
||||
@@ -594,6 +706,10 @@ pub const HTMLImageElement = struct {
|
||||
pub const prototype = *HTMLElement;
|
||||
pub const subtype = .node;
|
||||
|
||||
pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element {
|
||||
return constructHtmlElement(page, js_this);
|
||||
}
|
||||
|
||||
pub fn get_alt(self: *parser.Image) ![]const u8 {
|
||||
return try parser.imageGetAlt(self);
|
||||
}
|
||||
@@ -653,6 +769,10 @@ pub const HTMLInputElement = struct {
|
||||
pub const prototype = *HTMLElement;
|
||||
pub const subtype = .node;
|
||||
|
||||
pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element {
|
||||
return constructHtmlElement(page, js_this);
|
||||
}
|
||||
|
||||
pub fn get_defaultValue(self: *parser.Input) ![]const u8 {
|
||||
return try parser.inputGetDefaultValue(self);
|
||||
}
|
||||
@@ -741,18 +861,30 @@ pub const HTMLLIElement = struct {
|
||||
pub const Self = parser.LI;
|
||||
pub const prototype = *HTMLElement;
|
||||
pub const subtype = .node;
|
||||
|
||||
pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element {
|
||||
return constructHtmlElement(page, js_this);
|
||||
}
|
||||
};
|
||||
|
||||
pub const HTMLLabelElement = struct {
|
||||
pub const Self = parser.Label;
|
||||
pub const prototype = *HTMLElement;
|
||||
pub const subtype = .node;
|
||||
|
||||
pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element {
|
||||
return constructHtmlElement(page, js_this);
|
||||
}
|
||||
};
|
||||
|
||||
pub const HTMLLegendElement = struct {
|
||||
pub const Self = parser.Legend;
|
||||
pub const prototype = *HTMLElement;
|
||||
pub const subtype = .node;
|
||||
|
||||
pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element {
|
||||
return constructHtmlElement(page, js_this);
|
||||
}
|
||||
};
|
||||
|
||||
pub const HTMLLinkElement = struct {
|
||||
@@ -760,6 +892,10 @@ pub const HTMLLinkElement = struct {
|
||||
pub const prototype = *HTMLElement;
|
||||
pub const subtype = .node;
|
||||
|
||||
pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element {
|
||||
return constructHtmlElement(page, js_this);
|
||||
}
|
||||
|
||||
pub fn get_href(self: *parser.Link) ![]const u8 {
|
||||
return try parser.linkGetHref(self);
|
||||
}
|
||||
@@ -774,90 +910,150 @@ pub const HTMLMapElement = struct {
|
||||
pub const Self = parser.Map;
|
||||
pub const prototype = *HTMLElement;
|
||||
pub const subtype = .node;
|
||||
|
||||
pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element {
|
||||
return constructHtmlElement(page, js_this);
|
||||
}
|
||||
};
|
||||
|
||||
pub const HTMLMetaElement = struct {
|
||||
pub const Self = parser.Meta;
|
||||
pub const prototype = *HTMLElement;
|
||||
pub const subtype = .node;
|
||||
|
||||
pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element {
|
||||
return constructHtmlElement(page, js_this);
|
||||
}
|
||||
};
|
||||
|
||||
pub const HTMLMeterElement = struct {
|
||||
pub const Self = parser.Meter;
|
||||
pub const prototype = *HTMLElement;
|
||||
pub const subtype = .node;
|
||||
|
||||
pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element {
|
||||
return constructHtmlElement(page, js_this);
|
||||
}
|
||||
};
|
||||
|
||||
pub const HTMLModElement = struct {
|
||||
pub const Self = parser.Mod;
|
||||
pub const prototype = *HTMLElement;
|
||||
pub const subtype = .node;
|
||||
|
||||
pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element {
|
||||
return constructHtmlElement(page, js_this);
|
||||
}
|
||||
};
|
||||
|
||||
pub const HTMLOListElement = struct {
|
||||
pub const Self = parser.OList;
|
||||
pub const prototype = *HTMLElement;
|
||||
pub const subtype = .node;
|
||||
|
||||
pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element {
|
||||
return constructHtmlElement(page, js_this);
|
||||
}
|
||||
};
|
||||
|
||||
pub const HTMLObjectElement = struct {
|
||||
pub const Self = parser.Object;
|
||||
pub const prototype = *HTMLElement;
|
||||
pub const subtype = .node;
|
||||
|
||||
pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element {
|
||||
return constructHtmlElement(page, js_this);
|
||||
}
|
||||
};
|
||||
|
||||
pub const HTMLOptGroupElement = struct {
|
||||
pub const Self = parser.OptGroup;
|
||||
pub const prototype = *HTMLElement;
|
||||
pub const subtype = .node;
|
||||
|
||||
pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element {
|
||||
return constructHtmlElement(page, js_this);
|
||||
}
|
||||
};
|
||||
|
||||
pub const HTMLOptionElement = struct {
|
||||
pub const Self = parser.Option;
|
||||
pub const prototype = *HTMLElement;
|
||||
pub const subtype = .node;
|
||||
|
||||
pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element {
|
||||
return constructHtmlElement(page, js_this);
|
||||
}
|
||||
};
|
||||
|
||||
pub const HTMLOutputElement = struct {
|
||||
pub const Self = parser.Output;
|
||||
pub const prototype = *HTMLElement;
|
||||
pub const subtype = .node;
|
||||
|
||||
pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element {
|
||||
return constructHtmlElement(page, js_this);
|
||||
}
|
||||
};
|
||||
|
||||
pub const HTMLParagraphElement = struct {
|
||||
pub const Self = parser.Paragraph;
|
||||
pub const prototype = *HTMLElement;
|
||||
pub const subtype = .node;
|
||||
|
||||
pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element {
|
||||
return constructHtmlElement(page, js_this);
|
||||
}
|
||||
};
|
||||
|
||||
pub const HTMLParamElement = struct {
|
||||
pub const Self = parser.Param;
|
||||
pub const prototype = *HTMLElement;
|
||||
pub const subtype = .node;
|
||||
|
||||
pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element {
|
||||
return constructHtmlElement(page, js_this);
|
||||
}
|
||||
};
|
||||
|
||||
pub const HTMLPictureElement = struct {
|
||||
pub const Self = parser.Picture;
|
||||
pub const prototype = *HTMLElement;
|
||||
pub const subtype = .node;
|
||||
|
||||
pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element {
|
||||
return constructHtmlElement(page, js_this);
|
||||
}
|
||||
};
|
||||
|
||||
pub const HTMLPreElement = struct {
|
||||
pub const Self = parser.Pre;
|
||||
pub const prototype = *HTMLElement;
|
||||
pub const subtype = .node;
|
||||
|
||||
pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element {
|
||||
return constructHtmlElement(page, js_this);
|
||||
}
|
||||
};
|
||||
|
||||
pub const HTMLProgressElement = struct {
|
||||
pub const Self = parser.Progress;
|
||||
pub const prototype = *HTMLElement;
|
||||
pub const subtype = .node;
|
||||
|
||||
pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element {
|
||||
return constructHtmlElement(page, js_this);
|
||||
}
|
||||
};
|
||||
|
||||
pub const HTMLQuoteElement = struct {
|
||||
pub const Self = parser.Quote;
|
||||
pub const prototype = *HTMLElement;
|
||||
pub const subtype = .node;
|
||||
|
||||
pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element {
|
||||
return constructHtmlElement(page, js_this);
|
||||
}
|
||||
};
|
||||
|
||||
// https://html.spec.whatwg.org/#the-script-element
|
||||
@@ -866,6 +1062,10 @@ pub const HTMLScriptElement = struct {
|
||||
pub const prototype = *HTMLElement;
|
||||
pub const subtype = .node;
|
||||
|
||||
pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element {
|
||||
return constructHtmlElement(page, js_this);
|
||||
}
|
||||
|
||||
pub fn get_src(self: *parser.Script) !?[]const u8 {
|
||||
return try parser.elementGetAttribute(
|
||||
parser.scriptToElt(self),
|
||||
@@ -1000,54 +1200,90 @@ pub const HTMLSourceElement = struct {
|
||||
pub const Self = parser.Source;
|
||||
pub const prototype = *HTMLElement;
|
||||
pub const subtype = .node;
|
||||
|
||||
pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element {
|
||||
return constructHtmlElement(page, js_this);
|
||||
}
|
||||
};
|
||||
|
||||
pub const HTMLSpanElement = struct {
|
||||
pub const Self = parser.Span;
|
||||
pub const prototype = *HTMLElement;
|
||||
pub const subtype = .node;
|
||||
|
||||
pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element {
|
||||
return constructHtmlElement(page, js_this);
|
||||
}
|
||||
};
|
||||
|
||||
pub const HTMLStyleElement = struct {
|
||||
pub const Self = parser.Style;
|
||||
pub const prototype = *HTMLElement;
|
||||
pub const subtype = .node;
|
||||
|
||||
pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element {
|
||||
return constructHtmlElement(page, js_this);
|
||||
}
|
||||
};
|
||||
|
||||
pub const HTMLTableElement = struct {
|
||||
pub const Self = parser.Table;
|
||||
pub const prototype = *HTMLElement;
|
||||
pub const subtype = .node;
|
||||
|
||||
pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element {
|
||||
return constructHtmlElement(page, js_this);
|
||||
}
|
||||
};
|
||||
|
||||
pub const HTMLTableCaptionElement = struct {
|
||||
pub const Self = parser.TableCaption;
|
||||
pub const prototype = *HTMLElement;
|
||||
pub const subtype = .node;
|
||||
|
||||
pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element {
|
||||
return constructHtmlElement(page, js_this);
|
||||
}
|
||||
};
|
||||
|
||||
pub const HTMLTableCellElement = struct {
|
||||
pub const Self = parser.TableCell;
|
||||
pub const prototype = *HTMLElement;
|
||||
pub const subtype = .node;
|
||||
|
||||
pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element {
|
||||
return constructHtmlElement(page, js_this);
|
||||
}
|
||||
};
|
||||
|
||||
pub const HTMLTableColElement = struct {
|
||||
pub const Self = parser.TableCol;
|
||||
pub const prototype = *HTMLElement;
|
||||
pub const subtype = .node;
|
||||
|
||||
pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element {
|
||||
return constructHtmlElement(page, js_this);
|
||||
}
|
||||
};
|
||||
|
||||
pub const HTMLTableRowElement = struct {
|
||||
pub const Self = parser.TableRow;
|
||||
pub const prototype = *HTMLElement;
|
||||
pub const subtype = .node;
|
||||
|
||||
pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element {
|
||||
return constructHtmlElement(page, js_this);
|
||||
}
|
||||
};
|
||||
|
||||
pub const HTMLTableSectionElement = struct {
|
||||
pub const Self = parser.TableSection;
|
||||
pub const prototype = *HTMLElement;
|
||||
pub const subtype = .node;
|
||||
|
||||
pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element {
|
||||
return constructHtmlElement(page, js_this);
|
||||
}
|
||||
};
|
||||
|
||||
pub const HTMLTemplateElement = struct {
|
||||
@@ -1055,6 +1291,10 @@ pub const HTMLTemplateElement = struct {
|
||||
pub const prototype = *HTMLElement;
|
||||
pub const subtype = .node;
|
||||
|
||||
pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element {
|
||||
return constructHtmlElement(page, js_this);
|
||||
}
|
||||
|
||||
pub fn get_content(self: *parser.Template, page: *Page) !*parser.DocumentFragment {
|
||||
const state = try page.getOrCreateNodeState(@alignCast(@ptrCast(self)));
|
||||
if (state.template_content) |tc| {
|
||||
@@ -1070,36 +1310,60 @@ pub const HTMLTextAreaElement = struct {
|
||||
pub const Self = parser.TextArea;
|
||||
pub const prototype = *HTMLElement;
|
||||
pub const subtype = .node;
|
||||
|
||||
pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element {
|
||||
return constructHtmlElement(page, js_this);
|
||||
}
|
||||
};
|
||||
|
||||
pub const HTMLTimeElement = struct {
|
||||
pub const Self = parser.Time;
|
||||
pub const prototype = *HTMLElement;
|
||||
pub const subtype = .node;
|
||||
|
||||
pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element {
|
||||
return constructHtmlElement(page, js_this);
|
||||
}
|
||||
};
|
||||
|
||||
pub const HTMLTitleElement = struct {
|
||||
pub const Self = parser.Title;
|
||||
pub const prototype = *HTMLElement;
|
||||
pub const subtype = .node;
|
||||
|
||||
pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element {
|
||||
return constructHtmlElement(page, js_this);
|
||||
}
|
||||
};
|
||||
|
||||
pub const HTMLTrackElement = struct {
|
||||
pub const Self = parser.Track;
|
||||
pub const prototype = *HTMLElement;
|
||||
pub const subtype = .node;
|
||||
|
||||
pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element {
|
||||
return constructHtmlElement(page, js_this);
|
||||
}
|
||||
};
|
||||
|
||||
pub const HTMLUListElement = struct {
|
||||
pub const Self = parser.UList;
|
||||
pub const prototype = *HTMLElement;
|
||||
pub const subtype = .node;
|
||||
|
||||
pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element {
|
||||
return constructHtmlElement(page, js_this);
|
||||
}
|
||||
};
|
||||
|
||||
pub const HTMLVideoElement = struct {
|
||||
pub const Self = parser.Video;
|
||||
pub const prototype = *HTMLElement;
|
||||
pub const subtype = .node;
|
||||
|
||||
pub fn constructor(page: *Page, js_this: Env.JsThis) !*parser.Element {
|
||||
return constructHtmlElement(page, js_this);
|
||||
}
|
||||
};
|
||||
|
||||
pub fn toInterface(comptime T: type, e: *parser.Element) !T {
|
||||
@@ -1177,6 +1441,16 @@ pub fn toInterface(comptime T: type, e: *parser.Element) !T {
|
||||
};
|
||||
}
|
||||
|
||||
fn constructHtmlElement(page: *Page, js_this: Env.JsThis) !*parser.Element {
|
||||
const constructor_name = try js_this.constructorName(page.call_arena);
|
||||
if (!page.window.custom_elements.lookup.contains(constructor_name)) {
|
||||
return error.IllegalContructor;
|
||||
}
|
||||
|
||||
const el = try parser.documentCreateElement(@ptrCast(page.window.document), constructor_name);
|
||||
return el;
|
||||
}
|
||||
|
||||
const testing = @import("../../testing.zig");
|
||||
test "Browser.HTML.Element" {
|
||||
var runner = try testing.jsRunner(testing.tracking_allocator, .{});
|
||||
|
||||
@@ -33,6 +33,7 @@ const EventTarget = @import("../dom/event_target.zig").EventTarget;
|
||||
const MediaQueryList = @import("media_query_list.zig").MediaQueryList;
|
||||
const Performance = @import("../dom/performance.zig").Performance;
|
||||
const CSSStyleDeclaration = @import("../cssom/css_style_declaration.zig").CSSStyleDeclaration;
|
||||
const CustomElementRegistry = @import("../webcomponents/custom_element_registry.zig").CustomElementRegistry;
|
||||
const Screen = @import("screen.zig").Screen;
|
||||
const Css = @import("../css/css.zig").Css;
|
||||
|
||||
@@ -60,6 +61,7 @@ pub const Window = struct {
|
||||
console: Console = .{},
|
||||
navigator: Navigator = .{},
|
||||
performance: Performance,
|
||||
custom_elements: CustomElementRegistry = .{},
|
||||
screen: Screen = .{},
|
||||
css: Css = .{},
|
||||
|
||||
@@ -167,6 +169,10 @@ pub const Window = struct {
|
||||
return &self.performance;
|
||||
}
|
||||
|
||||
pub fn get_customElements(self: *Window) *CustomElementRegistry {
|
||||
return &self.custom_elements;
|
||||
}
|
||||
|
||||
pub fn get_screen(self: *Window) *Screen {
|
||||
return &self.screen;
|
||||
}
|
||||
|
||||
@@ -1925,6 +1925,18 @@ pub inline fn documentFragmentToNode(doc: *DocumentFragment) *Node {
|
||||
return @as(*Node, @alignCast(@ptrCast(doc)));
|
||||
}
|
||||
|
||||
pub fn documentFragmentBodyChildren(doc: *DocumentFragment) !?*NodeList {
|
||||
const node = documentFragmentToNode(doc);
|
||||
const html = try nodeFirstChild(node) orelse return null;
|
||||
// TODO unref
|
||||
const head = try nodeFirstChild(html) orelse return null;
|
||||
// TODO unref
|
||||
const body = try nodeNextSibling(head) orelse return null;
|
||||
// TODO unref
|
||||
|
||||
return try nodeGetChildNodes(body);
|
||||
}
|
||||
|
||||
// Document Position
|
||||
|
||||
pub const DocumentPosition = enum(u32) {
|
||||
@@ -2372,6 +2384,14 @@ pub inline fn documentHTMLSetBody(doc_html: *DocumentHTML, elt: ?*ElementHTML) !
|
||||
try DOMErr(err);
|
||||
}
|
||||
|
||||
pub inline fn documentHTMLGetDomain(doc: *DocumentHTML) ![]const u8 {
|
||||
var s: ?*String = undefined;
|
||||
const err = documentHTMLVtable(doc).get_domain.?(doc, &s);
|
||||
try DOMErr(err);
|
||||
if (s == null) return "";
|
||||
return strToData(s.?);
|
||||
}
|
||||
|
||||
pub inline fn documentHTMLGetReferrer(doc: *DocumentHTML) ![]const u8 {
|
||||
var s: ?*String = undefined;
|
||||
const err = documentHTMLVtable(doc).get_referrer.?(doc, &s);
|
||||
|
||||
@@ -95,8 +95,6 @@ pub const Page = struct {
|
||||
|
||||
state_pool: *std.heap.MemoryPool(State),
|
||||
|
||||
polyfill_loader: polyfill.Loader = .{},
|
||||
|
||||
pub fn init(self: *Page, arena: Allocator, session: *Session) !void {
|
||||
const browser = session.browser;
|
||||
self.* = .{
|
||||
@@ -119,10 +117,10 @@ pub const Page = struct {
|
||||
}),
|
||||
.main_context = undefined,
|
||||
};
|
||||
self.main_context = try session.executor.createJsContext(&self.window, self, self, true, .{
|
||||
.global_callback = Env.GlobalMissingCallback.init(&self.polyfill_loader),
|
||||
.compilation_callback = Env.CompilationCallback.init(&self.polyfill_loader),
|
||||
});
|
||||
self.main_context = try session.executor.createJsContext(&self.window, self, self, true);
|
||||
|
||||
// load polyfills
|
||||
try polyfill.load(self.arena, self.main_context);
|
||||
|
||||
// message loop must run only non-test env
|
||||
if (comptime !builtin.is_test) {
|
||||
@@ -369,6 +367,18 @@ pub const Page = struct {
|
||||
const e = parser.nodeToElement(current);
|
||||
const tag = try parser.elementHTMLGetTagType(@as(*parser.ElementHTML, @ptrCast(e)));
|
||||
|
||||
// if (tag == .undef) {
|
||||
// const tag_name = try parser.nodeLocalName(@ptrCast(e));
|
||||
// const custom_elements = &self.window.custom_elements;
|
||||
// if (custom_elements._get(tag_name)) |construct| {
|
||||
// try construct.printFunc();
|
||||
// // This is just here for testing for now.
|
||||
// // var result: Env.Function.Result = undefined;
|
||||
// // _ = try construct.newInstance(*parser.Element, &result);
|
||||
// log.info(.browser, "Registered WebComponent Found", .{ .element_name = tag_name });
|
||||
// }
|
||||
// }
|
||||
|
||||
if (tag != .script) {
|
||||
// ignore non-js script.
|
||||
continue;
|
||||
|
||||
@@ -16,6 +16,8 @@ test "Browser.fetch" {
|
||||
var runner = try testing.jsRunner(testing.tracking_allocator, .{});
|
||||
defer runner.deinit();
|
||||
|
||||
try @import("polyfill.zig").load(testing.allocator, runner.page.main_context);
|
||||
|
||||
try runner.testCases(&.{
|
||||
.{
|
||||
\\ var ok = false;
|
||||
|
||||
@@ -23,104 +23,25 @@ const log = @import("../../log.zig");
|
||||
const Allocator = std.mem.Allocator;
|
||||
const Env = @import("../env.zig").Env;
|
||||
|
||||
pub const Loader = struct {
|
||||
state: enum { empty, loading } = .empty,
|
||||
|
||||
done: struct {
|
||||
fetch: bool = false,
|
||||
webcomponents: bool = false,
|
||||
} = .{},
|
||||
|
||||
fn load(self: *Loader, comptime name: []const u8, source: []const u8, js_context: *Env.JsContext) void {
|
||||
var try_catch: Env.TryCatch = undefined;
|
||||
try_catch.init(js_context);
|
||||
defer try_catch.deinit();
|
||||
|
||||
self.state = .loading;
|
||||
defer self.state = .empty;
|
||||
|
||||
log.debug(.js, "polyfill load", .{ .name = name });
|
||||
_ = js_context.exec(source, name) catch |err| {
|
||||
log.fatal(.app, "polyfill error", .{
|
||||
.name = name,
|
||||
.err = try_catch.err(js_context.call_arena) catch @errorName(err) orelse @errorName(err),
|
||||
});
|
||||
};
|
||||
|
||||
@field(self.done, name) = true;
|
||||
}
|
||||
|
||||
// CompilationCallback implementation
|
||||
pub fn script(self: *Loader, src: []const u8, _: ?[]const u8, js_context: *Env.JsContext) void {
|
||||
if (!self.done.webcomponents and containsWebcomponents(src)) {
|
||||
const source = @import("webcomponents.zig").source;
|
||||
self.load("webcomponents", source, js_context);
|
||||
}
|
||||
}
|
||||
|
||||
// CompilationCallback implementation
|
||||
pub fn module(self: *Loader, src: []const u8, _: []const u8, js_context: *Env.JsContext) void {
|
||||
if (!self.done.webcomponents and containsWebcomponents(src)) {
|
||||
const source = @import("webcomponents.zig").source;
|
||||
self.load("webcomponents", source, js_context);
|
||||
}
|
||||
}
|
||||
|
||||
// GlobalMissingCallback implementation
|
||||
pub fn missing(self: *Loader, name: []const u8, js_context: *Env.JsContext) bool {
|
||||
// Avoid recursive calls during polyfill loading.
|
||||
if (self.state == .loading) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!self.done.fetch and isFetch(name)) {
|
||||
const source = @import("fetch.zig").source;
|
||||
self.load("fetch", source, js_context);
|
||||
|
||||
// We return false here: We want v8 to continue the calling chain
|
||||
// to finally find the polyfill we just inserted. If we want to
|
||||
// return false and stops the call chain, we have to use
|
||||
// `info.GetReturnValue.Set()` function, or `undefined` will be
|
||||
// returned immediately.
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!self.done.webcomponents and isWebcomponents(name)) {
|
||||
const source = @import("webcomponents.zig").source;
|
||||
self.load("webcomponents", source, js_context);
|
||||
|
||||
// We return false here: We want v8 to continue the calling chain
|
||||
// to finally find the polyfill we just inserted. If we want to
|
||||
// return false and stops the call chain, we have to use
|
||||
// `info.GetReturnValue.Set()` function, or `undefined` will be
|
||||
// returned immediately.
|
||||
return false;
|
||||
}
|
||||
|
||||
if (comptime builtin.mode == .Debug) {
|
||||
log.debug(.unknown_prop, "unkown global property", .{
|
||||
.info = "but the property can exist in pure JS",
|
||||
.property = name,
|
||||
});
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
fn isFetch(name: []const u8) bool {
|
||||
if (std.mem.eql(u8, name, "fetch")) return true;
|
||||
if (std.mem.eql(u8, name, "Request")) return true;
|
||||
if (std.mem.eql(u8, name, "Response")) return true;
|
||||
if (std.mem.eql(u8, name, "Headers")) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
fn isWebcomponents(name: []const u8) bool {
|
||||
if (std.mem.eql(u8, name, "customElements")) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
fn containsWebcomponents(src: []const u8) bool {
|
||||
return std.mem.indexOf(u8, src, " extends ") != null;
|
||||
}
|
||||
const modules = [_]struct {
|
||||
name: []const u8,
|
||||
source: []const u8,
|
||||
}{
|
||||
.{ .name = "polyfill-fetch", .source = @import("fetch.zig").source },
|
||||
};
|
||||
|
||||
pub fn load(allocator: Allocator, js_context: *Env.JsContext) !void {
|
||||
var try_catch: Env.TryCatch = undefined;
|
||||
try_catch.init(js_context);
|
||||
defer try_catch.deinit();
|
||||
|
||||
for (modules) |m| {
|
||||
_ = js_context.exec(m.source, m.name) catch |err| {
|
||||
if (try try_catch.err(allocator)) |msg| {
|
||||
defer allocator.free(msg);
|
||||
log.fatal(.app, "polyfill error", .{ .name = m.name, .err = msg });
|
||||
}
|
||||
return err;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
/**
|
||||
@license @nocompile
|
||||
Copyright (c) 2018 The Polymer Project Authors. All rights reserved.
|
||||
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
|
||||
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
|
||||
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
|
||||
Code distributed by Google as part of the polymer project is also
|
||||
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
|
||||
*/
|
||||
(function(){/*
|
||||
|
||||
Copyright (c) 2016 The Polymer Project Authors. All rights reserved.
|
||||
This code may only be used under the BSD style license found at
|
||||
http://polymer.github.io/LICENSE.txt The complete set of authors may be found
|
||||
at http://polymer.github.io/AUTHORS.txt The complete set of contributors may
|
||||
be found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by
|
||||
Google as part of the polymer project is also subject to an additional IP
|
||||
rights grant found at http://polymer.github.io/PATENTS.txt
|
||||
*/
|
||||
'use strict';var n=window.Document.prototype.createElement,p=window.Document.prototype.createElementNS,aa=window.Document.prototype.importNode,ba=window.Document.prototype.prepend,ca=window.Document.prototype.append,da=window.DocumentFragment.prototype.prepend,ea=window.DocumentFragment.prototype.append,q=window.Node.prototype.cloneNode,r=window.Node.prototype.appendChild,t=window.Node.prototype.insertBefore,u=window.Node.prototype.removeChild,v=window.Node.prototype.replaceChild,w=Object.getOwnPropertyDescriptor(window.Node.prototype,
|
||||
"textContent"),y=window.Element.prototype.attachShadow,z=Object.getOwnPropertyDescriptor(window.Element.prototype,"innerHTML"),A=window.Element.prototype.getAttribute,B=window.Element.prototype.setAttribute,C=window.Element.prototype.removeAttribute,D=window.Element.prototype.toggleAttribute,E=window.Element.prototype.getAttributeNS,F=window.Element.prototype.setAttributeNS,G=window.Element.prototype.removeAttributeNS,H=window.Element.prototype.insertAdjacentElement,fa=window.Element.prototype.insertAdjacentHTML,
|
||||
ha=window.Element.prototype.prepend,ia=window.Element.prototype.append,ja=window.Element.prototype.before,ka=window.Element.prototype.after,la=window.Element.prototype.replaceWith,ma=window.Element.prototype.remove,na=window.HTMLElement,I=Object.getOwnPropertyDescriptor(window.HTMLElement.prototype,"innerHTML"),oa=window.HTMLElement.prototype.insertAdjacentElement,pa=window.HTMLElement.prototype.insertAdjacentHTML;var qa=new Set;"annotation-xml color-profile font-face font-face-src font-face-uri font-face-format font-face-name missing-glyph".split(" ").forEach(function(a){return qa.add(a)});function ra(a){var b=qa.has(a);a=/^[a-z][.0-9_a-z]*-[-.0-9_a-z]*$/.test(a);return!b&&a}var sa=document.contains?document.contains.bind(document):document.documentElement.contains.bind(document.documentElement);
|
||||
function J(a){var b=a.isConnected;if(void 0!==b)return b;if(sa(a))return!0;for(;a&&!(a.__CE_isImportDocument||a instanceof Document);)a=a.parentNode||(window.ShadowRoot&&a instanceof ShadowRoot?a.host:void 0);return!(!a||!(a.__CE_isImportDocument||a instanceof Document))}function K(a){var b=a.children;if(b)return Array.prototype.slice.call(b);b=[];for(a=a.firstChild;a;a=a.nextSibling)a.nodeType===Node.ELEMENT_NODE&&b.push(a);return b}
|
||||
function L(a,b){for(;b&&b!==a&&!b.nextSibling;)b=b.parentNode;return b&&b!==a?b.nextSibling:null}
|
||||
function M(a,b,d){for(var f=a;f;){if(f.nodeType===Node.ELEMENT_NODE){var c=f;b(c);var e=c.localName;if("link"===e&&"import"===c.getAttribute("rel")){f=c.import;void 0===d&&(d=new Set);if(f instanceof Node&&!d.has(f))for(d.add(f),f=f.firstChild;f;f=f.nextSibling)M(f,b,d);f=L(a,c);continue}else if("template"===e){f=L(a,c);continue}if(c=c.__CE_shadowRoot)for(c=c.firstChild;c;c=c.nextSibling)M(c,b,d)}f=f.firstChild?f.firstChild:L(a,f)}};function N(){var a=!(null===O||void 0===O||!O.noDocumentConstructionObserver),b=!(null===O||void 0===O||!O.shadyDomFastWalk);this.m=[];this.g=[];this.j=!1;this.shadyDomFastWalk=b;this.I=!a}function P(a,b,d,f){var c=window.ShadyDOM;if(a.shadyDomFastWalk&&c&&c.inUse){if(b.nodeType===Node.ELEMENT_NODE&&d(b),b.querySelectorAll)for(a=c.nativeMethods.querySelectorAll.call(b,"*"),b=0;b<a.length;b++)d(a[b])}else M(b,d,f)}function ta(a,b){a.j=!0;a.m.push(b)}function ua(a,b){a.j=!0;a.g.push(b)}
|
||||
function Q(a,b){a.j&&P(a,b,function(d){return R(a,d)})}function R(a,b){if(a.j&&!b.__CE_patched){b.__CE_patched=!0;for(var d=0;d<a.m.length;d++)a.m[d](b);for(d=0;d<a.g.length;d++)a.g[d](b)}}function S(a,b){var d=[];P(a,b,function(c){return d.push(c)});for(b=0;b<d.length;b++){var f=d[b];1===f.__CE_state?a.connectedCallback(f):T(a,f)}}function U(a,b){var d=[];P(a,b,function(c){return d.push(c)});for(b=0;b<d.length;b++){var f=d[b];1===f.__CE_state&&a.disconnectedCallback(f)}}
|
||||
function V(a,b,d){d=void 0===d?{}:d;var f=d.J,c=d.upgrade||function(g){return T(a,g)},e=[];P(a,b,function(g){a.j&&R(a,g);if("link"===g.localName&&"import"===g.getAttribute("rel")){var h=g.import;h instanceof Node&&(h.__CE_isImportDocument=!0,h.__CE_registry=document.__CE_registry);h&&"complete"===h.readyState?h.__CE_documentLoadHandled=!0:g.addEventListener("load",function(){var k=g.import;if(!k.__CE_documentLoadHandled){k.__CE_documentLoadHandled=!0;var l=new Set;f&&(f.forEach(function(m){return l.add(m)}),
|
||||
l.delete(k));V(a,k,{J:l,upgrade:c})}})}else e.push(g)},f);for(b=0;b<e.length;b++)c(e[b])}
|
||||
function T(a,b){try{var d=b.ownerDocument,f=d.__CE_registry;var c=f&&(d.defaultView||d.__CE_isImportDocument)?W(f,b.localName):void 0;if(c&&void 0===b.__CE_state){c.constructionStack.push(b);try{try{if(new c.constructorFunction!==b)throw Error("The custom element constructor did not produce the element being upgraded.");}finally{c.constructionStack.pop()}}catch(k){throw b.__CE_state=2,k;}b.__CE_state=1;b.__CE_definition=c;if(c.attributeChangedCallback&&b.hasAttributes()){var e=c.observedAttributes;
|
||||
for(c=0;c<e.length;c++){var g=e[c],h=b.getAttribute(g);null!==h&&a.attributeChangedCallback(b,g,null,h,null)}}J(b)&&a.connectedCallback(b)}}catch(k){X(k)}}N.prototype.connectedCallback=function(a){var b=a.__CE_definition;if(b.connectedCallback)try{b.connectedCallback.call(a)}catch(d){X(d)}};N.prototype.disconnectedCallback=function(a){var b=a.__CE_definition;if(b.disconnectedCallback)try{b.disconnectedCallback.call(a)}catch(d){X(d)}};
|
||||
N.prototype.attributeChangedCallback=function(a,b,d,f,c){var e=a.__CE_definition;if(e.attributeChangedCallback&&-1<e.observedAttributes.indexOf(b))try{e.attributeChangedCallback.call(a,b,d,f,c)}catch(g){X(g)}};
|
||||
function va(a,b,d,f){var c=b.__CE_registry;if(c&&(null===f||"http://www.w3.org/1999/xhtml"===f)&&(c=W(c,d)))try{var e=new c.constructorFunction;if(void 0===e.__CE_state||void 0===e.__CE_definition)throw Error("Failed to construct '"+d+"': The returned value was not constructed with the HTMLElement constructor.");if("http://www.w3.org/1999/xhtml"!==e.namespaceURI)throw Error("Failed to construct '"+d+"': The constructed element's namespace must be the HTML namespace.");if(e.hasAttributes())throw Error("Failed to construct '"+
|
||||
d+"': The constructed element must not have any attributes.");if(null!==e.firstChild)throw Error("Failed to construct '"+d+"': The constructed element must not have any children.");if(null!==e.parentNode)throw Error("Failed to construct '"+d+"': The constructed element must not have a parent node.");if(e.ownerDocument!==b)throw Error("Failed to construct '"+d+"': The constructed element's owner document is incorrect.");if(e.localName!==d)throw Error("Failed to construct '"+d+"': The constructed element's local name is incorrect.");
|
||||
return e}catch(g){return X(g),b=null===f?n.call(b,d):p.call(b,f,d),Object.setPrototypeOf(b,HTMLUnknownElement.prototype),b.__CE_state=2,b.__CE_definition=void 0,R(a,b),b}b=null===f?n.call(b,d):p.call(b,f,d);R(a,b);return b}
|
||||
function X(a){var b="",d="",f=0,c=0;a instanceof Error?(b=a.message,d=a.sourceURL||a.fileName||"",f=a.line||a.lineNumber||0,c=a.column||a.columnNumber||0):b="Uncaught "+String(a);var e=void 0;void 0===ErrorEvent.prototype.initErrorEvent?e=new ErrorEvent("error",{cancelable:!0,message:b,filename:d,lineno:f,colno:c,error:a}):(e=document.createEvent("ErrorEvent"),e.initErrorEvent("error",!1,!0,b,d,f),e.preventDefault=function(){Object.defineProperty(this,"defaultPrevented",{configurable:!0,get:function(){return!0}})});
|
||||
void 0===e.error&&Object.defineProperty(e,"error",{configurable:!0,enumerable:!0,get:function(){return a}});window.dispatchEvent(e);e.defaultPrevented||console.error(a)};function wa(){var a=this;this.g=void 0;this.F=new Promise(function(b){a.l=b})}wa.prototype.resolve=function(a){if(this.g)throw Error("Already resolved.");this.g=a;this.l(a)};function xa(a){var b=document;this.l=void 0;this.h=a;this.g=b;V(this.h,this.g);"loading"===this.g.readyState&&(this.l=new MutationObserver(this.G.bind(this)),this.l.observe(this.g,{childList:!0,subtree:!0}))}function ya(a){a.l&&a.l.disconnect()}xa.prototype.G=function(a){var b=this.g.readyState;"interactive"!==b&&"complete"!==b||ya(this);for(b=0;b<a.length;b++)for(var d=a[b].addedNodes,f=0;f<d.length;f++)V(this.h,d[f])};function Y(a){this.s=new Map;this.u=new Map;this.C=new Map;this.A=!1;this.B=new Map;this.o=function(b){return b()};this.i=!1;this.v=[];this.h=a;this.D=a.I?new xa(a):void 0}Y.prototype.H=function(a,b){var d=this;if(!(b instanceof Function))throw new TypeError("Custom element constructor getters must be functions.");za(this,a);this.s.set(a,b);this.v.push(a);this.i||(this.i=!0,this.o(function(){return Aa(d)}))};
|
||||
Y.prototype.define=function(a,b){var d=this;if(!(b instanceof Function))throw new TypeError("Custom element constructors must be functions.");za(this,a);Ba(this,a,b);this.v.push(a);this.i||(this.i=!0,this.o(function(){return Aa(d)}))};function za(a,b){if(!ra(b))throw new SyntaxError("The element name '"+b+"' is not valid.");if(W(a,b))throw Error("A custom element with name '"+(b+"' has already been defined."));if(a.A)throw Error("A custom element is already being defined.");}
|
||||
function Ba(a,b,d){a.A=!0;var f;try{var c=d.prototype;if(!(c instanceof Object))throw new TypeError("The custom element constructor's prototype is not an object.");var e=function(m){var x=c[m];if(void 0!==x&&!(x instanceof Function))throw Error("The '"+m+"' callback must be a function.");return x};var g=e("connectedCallback");var h=e("disconnectedCallback");var k=e("adoptedCallback");var l=(f=e("attributeChangedCallback"))&&d.observedAttributes||[]}catch(m){throw m;}finally{a.A=!1}d={localName:b,
|
||||
constructorFunction:d,connectedCallback:g,disconnectedCallback:h,adoptedCallback:k,attributeChangedCallback:f,observedAttributes:l,constructionStack:[]};a.u.set(b,d);a.C.set(d.constructorFunction,d);return d}Y.prototype.upgrade=function(a){V(this.h,a)};
|
||||
function Aa(a){if(!1!==a.i){a.i=!1;for(var b=[],d=a.v,f=new Map,c=0;c<d.length;c++)f.set(d[c],[]);V(a.h,document,{upgrade:function(k){if(void 0===k.__CE_state){var l=k.localName,m=f.get(l);m?m.push(k):a.u.has(l)&&b.push(k)}}});for(c=0;c<b.length;c++)T(a.h,b[c]);for(c=0;c<d.length;c++){for(var e=d[c],g=f.get(e),h=0;h<g.length;h++)T(a.h,g[h]);(e=a.B.get(e))&&e.resolve(void 0)}d.length=0}}Y.prototype.get=function(a){if(a=W(this,a))return a.constructorFunction};
|
||||
Y.prototype.whenDefined=function(a){if(!ra(a))return Promise.reject(new SyntaxError("'"+a+"' is not a valid custom element name."));var b=this.B.get(a);if(b)return b.F;b=new wa;this.B.set(a,b);var d=this.u.has(a)||this.s.has(a);a=-1===this.v.indexOf(a);d&&a&&b.resolve(void 0);return b.F};Y.prototype.polyfillWrapFlushCallback=function(a){this.D&&ya(this.D);var b=this.o;this.o=function(d){return a(function(){return b(d)})}};
|
||||
function W(a,b){var d=a.u.get(b);if(d)return d;if(d=a.s.get(b)){a.s.delete(b);try{return Ba(a,b,d())}catch(f){X(f)}}}Y.prototype.define=Y.prototype.define;Y.prototype.upgrade=Y.prototype.upgrade;Y.prototype.get=Y.prototype.get;Y.prototype.whenDefined=Y.prototype.whenDefined;Y.prototype.polyfillDefineLazy=Y.prototype.H;Y.prototype.polyfillWrapFlushCallback=Y.prototype.polyfillWrapFlushCallback;function Z(a,b,d){function f(c){return function(e){for(var g=[],h=0;h<arguments.length;++h)g[h]=arguments[h];h=[];for(var k=[],l=0;l<g.length;l++){var m=g[l];m instanceof Element&&J(m)&&k.push(m);if(m instanceof DocumentFragment)for(m=m.firstChild;m;m=m.nextSibling)h.push(m);else h.push(m)}c.apply(this,g);for(g=0;g<k.length;g++)U(a,k[g]);if(J(this))for(g=0;g<h.length;g++)k=h[g],k instanceof Element&&S(a,k)}}void 0!==d.prepend&&(b.prepend=f(d.prepend));void 0!==d.append&&(b.append=f(d.append))};function Ca(a){Document.prototype.createElement=function(b){return va(a,this,b,null)};Document.prototype.importNode=function(b,d){b=aa.call(this,b,!!d);this.__CE_registry?V(a,b):Q(a,b);return b};Document.prototype.createElementNS=function(b,d){return va(a,this,d,b)};Z(a,Document.prototype,{prepend:ba,append:ca})};function Da(a){function b(f){return function(c){for(var e=[],g=0;g<arguments.length;++g)e[g]=arguments[g];g=[];for(var h=[],k=0;k<e.length;k++){var l=e[k];l instanceof Element&&J(l)&&h.push(l);if(l instanceof DocumentFragment)for(l=l.firstChild;l;l=l.nextSibling)g.push(l);else g.push(l)}f.apply(this,e);for(e=0;e<h.length;e++)U(a,h[e]);if(J(this))for(e=0;e<g.length;e++)h=g[e],h instanceof Element&&S(a,h)}}var d=Element.prototype;void 0!==ja&&(d.before=b(ja));void 0!==ka&&(d.after=b(ka));void 0!==la&&
|
||||
(d.replaceWith=function(f){for(var c=[],e=0;e<arguments.length;++e)c[e]=arguments[e];e=[];for(var g=[],h=0;h<c.length;h++){var k=c[h];k instanceof Element&&J(k)&&g.push(k);if(k instanceof DocumentFragment)for(k=k.firstChild;k;k=k.nextSibling)e.push(k);else e.push(k)}h=J(this);la.apply(this,c);for(c=0;c<g.length;c++)U(a,g[c]);if(h)for(U(a,this),c=0;c<e.length;c++)g=e[c],g instanceof Element&&S(a,g)});void 0!==ma&&(d.remove=function(){var f=J(this);ma.call(this);f&&U(a,this)})};function Ea(a){function b(c,e){Object.defineProperty(c,"innerHTML",{enumerable:e.enumerable,configurable:!0,get:e.get,set:function(g){var h=this,k=void 0;J(this)&&(k=[],P(a,this,function(x){x!==h&&k.push(x)}));e.set.call(this,g);if(k)for(var l=0;l<k.length;l++){var m=k[l];1===m.__CE_state&&a.disconnectedCallback(m)}this.ownerDocument.__CE_registry?V(a,this):Q(a,this);return g}})}function d(c,e){c.insertAdjacentElement=function(g,h){var k=J(h);g=e.call(this,g,h);k&&U(a,h);J(g)&&S(a,h);return g}}function f(c,
|
||||
e){function g(h,k){for(var l=[];h!==k;h=h.nextSibling)l.push(h);for(k=0;k<l.length;k++)V(a,l[k])}c.insertAdjacentHTML=function(h,k){h=h.toLowerCase();if("beforebegin"===h){var l=this.previousSibling;e.call(this,h,k);g(l||this.parentNode.firstChild,this)}else if("afterbegin"===h)l=this.firstChild,e.call(this,h,k),g(this.firstChild,l);else if("beforeend"===h)l=this.lastChild,e.call(this,h,k),g(l||this.firstChild,null);else if("afterend"===h)l=this.nextSibling,e.call(this,h,k),g(this.nextSibling,l);
|
||||
else throw new SyntaxError("The value provided ("+String(h)+") is not one of 'beforebegin', 'afterbegin', 'beforeend', or 'afterend'.");}}y&&(Element.prototype.attachShadow=function(c){c=y.call(this,c);if(a.j&&!c.__CE_patched){c.__CE_patched=!0;for(var e=0;e<a.m.length;e++)a.m[e](c)}return this.__CE_shadowRoot=c});z&&z.get?b(Element.prototype,z):I&&I.get?b(HTMLElement.prototype,I):ua(a,function(c){b(c,{enumerable:!0,configurable:!0,get:function(){return q.call(this,!0).innerHTML},set:function(e){var g=
|
||||
"template"===this.localName,h=g?this.content:this,k=p.call(document,this.namespaceURI,this.localName);for(k.innerHTML=e;0<h.childNodes.length;)u.call(h,h.childNodes[0]);for(e=g?k.content:k;0<e.childNodes.length;)r.call(h,e.childNodes[0])}})});Element.prototype.setAttribute=function(c,e){if(1!==this.__CE_state)return B.call(this,c,e);var g=A.call(this,c);B.call(this,c,e);e=A.call(this,c);a.attributeChangedCallback(this,c,g,e,null)};Element.prototype.setAttributeNS=function(c,e,g){if(1!==this.__CE_state)return F.call(this,
|
||||
c,e,g);var h=E.call(this,c,e);F.call(this,c,e,g);g=E.call(this,c,e);a.attributeChangedCallback(this,e,h,g,c)};Element.prototype.removeAttribute=function(c){if(1!==this.__CE_state)return C.call(this,c);var e=A.call(this,c);C.call(this,c);null!==e&&a.attributeChangedCallback(this,c,e,null,null)};D&&(Element.prototype.toggleAttribute=function(c,e){if(1!==this.__CE_state)return D.call(this,c,e);var g=A.call(this,c),h=null!==g;e=D.call(this,c,e);h!==e&&a.attributeChangedCallback(this,c,g,e?"":null,null);
|
||||
return e});Element.prototype.removeAttributeNS=function(c,e){if(1!==this.__CE_state)return G.call(this,c,e);var g=E.call(this,c,e);G.call(this,c,e);var h=E.call(this,c,e);g!==h&&a.attributeChangedCallback(this,e,g,h,c)};oa?d(HTMLElement.prototype,oa):H&&d(Element.prototype,H);pa?f(HTMLElement.prototype,pa):fa&&f(Element.prototype,fa);Z(a,Element.prototype,{prepend:ha,append:ia});Da(a)};var Fa={};function Ga(a){function b(){var d=this.constructor;var f=document.__CE_registry.C.get(d);if(!f)throw Error("Failed to construct a custom element: The constructor was not registered with `customElements`.");var c=f.constructionStack;if(0===c.length)return c=n.call(document,f.localName),Object.setPrototypeOf(c,d.prototype),c.__CE_state=1,c.__CE_definition=f,R(a,c),c;var e=c.length-1,g=c[e];if(g===Fa)throw Error("Failed to construct '"+f.localName+"': This element was already constructed.");c[e]=Fa;
|
||||
Object.setPrototypeOf(g,d.prototype);R(a,g);return g}b.prototype=na.prototype;Object.defineProperty(HTMLElement.prototype,"constructor",{writable:!0,configurable:!0,enumerable:!1,value:b});window.HTMLElement=b};function Ha(a){function b(d,f){Object.defineProperty(d,"textContent",{enumerable:f.enumerable,configurable:!0,get:f.get,set:function(c){if(this.nodeType===Node.TEXT_NODE)f.set.call(this,c);else{var e=void 0;if(this.firstChild){var g=this.childNodes,h=g.length;if(0<h&&J(this)){e=Array(h);for(var k=0;k<h;k++)e[k]=g[k]}}f.set.call(this,c);if(e)for(c=0;c<e.length;c++)U(a,e[c])}}})}Node.prototype.insertBefore=function(d,f){if(d instanceof DocumentFragment){var c=K(d);d=t.call(this,d,f);if(J(this))for(f=
|
||||
0;f<c.length;f++)S(a,c[f]);return d}c=d instanceof Element&&J(d);f=t.call(this,d,f);c&&U(a,d);J(this)&&S(a,d);return f};Node.prototype.appendChild=function(d){if(d instanceof DocumentFragment){var f=K(d);d=r.call(this,d);if(J(this))for(var c=0;c<f.length;c++)S(a,f[c]);return d}f=d instanceof Element&&J(d);c=r.call(this,d);f&&U(a,d);J(this)&&S(a,d);return c};Node.prototype.cloneNode=function(d){d=q.call(this,!!d);this.ownerDocument.__CE_registry?V(a,d):Q(a,d);return d};Node.prototype.removeChild=function(d){var f=
|
||||
d instanceof Element&&J(d),c=u.call(this,d);f&&U(a,d);return c};Node.prototype.replaceChild=function(d,f){if(d instanceof DocumentFragment){var c=K(d);d=v.call(this,d,f);if(J(this))for(U(a,f),f=0;f<c.length;f++)S(a,c[f]);return d}c=d instanceof Element&&J(d);var e=v.call(this,d,f),g=J(this);g&&U(a,f);c&&U(a,d);g&&S(a,d);return e};w&&w.get?b(Node.prototype,w):ta(a,function(d){b(d,{enumerable:!0,configurable:!0,get:function(){for(var f=[],c=this.firstChild;c;c=c.nextSibling)c.nodeType!==Node.COMMENT_NODE&&
|
||||
f.push(c.textContent);return f.join("")},set:function(f){for(;this.firstChild;)u.call(this,this.firstChild);null!=f&&""!==f&&r.call(this,document.createTextNode(f))}})})};var O=window.customElements;function Ia(){var a=new N;Ga(a);Ca(a);Z(a,DocumentFragment.prototype,{prepend:da,append:ea});Ha(a);Ea(a);window.CustomElementRegistry=Y;a=new Y(a);document.__CE_registry=a;Object.defineProperty(window,"customElements",{configurable:!0,enumerable:!0,value:a})}O&&!O.forcePolyfill&&"function"==typeof O.define&&"function"==typeof O.get||Ia();window.__CE_installPolyfill=Ia;/*
|
||||
|
||||
Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
|
||||
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
|
||||
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
|
||||
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
|
||||
Code distributed by Google as part of the polymer project is also
|
||||
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
|
||||
*/
|
||||
}).call(this);
|
||||
@@ -1,33 +0,0 @@
|
||||
// webcomponents.js code comes from
|
||||
// https://github.com/webcomponents/polyfills/tree/master/packages/webcomponentsjs
|
||||
//
|
||||
// The original code source is available in a "BSD style license".
|
||||
//
|
||||
// This is the `webcomponents-ce.js` bundle
|
||||
pub const source = @embedFile("webcomponents.js");
|
||||
|
||||
const testing = @import("../../testing.zig");
|
||||
test "Browser.webcomponents" {
|
||||
var runner = try testing.jsRunner(testing.tracking_allocator, .{ .html = "<div id=main></div>" });
|
||||
defer runner.deinit();
|
||||
|
||||
try runner.testCases(&.{
|
||||
.{
|
||||
\\ class LightPanda extends HTMLElement {
|
||||
\\ constructor() {
|
||||
\\ super();
|
||||
\\ }
|
||||
\\ connectedCallback() {
|
||||
\\ this.append('connected')
|
||||
\\ }
|
||||
\\ }
|
||||
\\ window.customElements.define("lightpanda-test", LightPanda);
|
||||
\\ const main = document.getElementById('main');
|
||||
\\ main.appendChild(document.createElement('lightpanda-test'));
|
||||
,
|
||||
null,
|
||||
},
|
||||
|
||||
.{ "main.innerHTML", "<lightpanda-test>connected</lightpanda-test>" },
|
||||
}, .{});
|
||||
}
|
||||
@@ -229,6 +229,19 @@ pub const URL = struct {
|
||||
pub fn _toJSON(self: *URL, page: *Page) ![]const u8 {
|
||||
return self.get_href(page);
|
||||
}
|
||||
|
||||
pub fn set_pathname(self: *URL, fragment: []const u8, page: *Page) !void {
|
||||
// pathname must always start with a '/';
|
||||
const real_path = blk: {
|
||||
if (std.mem.startsWith(u8, fragment, "/")) {
|
||||
break :blk try page.arena.dupe(u8, fragment);
|
||||
} else {
|
||||
break :blk try std.fmt.allocPrint(page.arena, "/{s}", .{fragment});
|
||||
}
|
||||
};
|
||||
|
||||
self.uri.path = .{ .percent_encoded = real_path };
|
||||
}
|
||||
};
|
||||
|
||||
// uriComponentNullStr converts an optional std.Uri.Component to string value.
|
||||
|
||||
@@ -16,8 +16,6 @@
|
||||
// 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/>.
|
||||
|
||||
// Currently not used. Relying on polyfill instead
|
||||
|
||||
const std = @import("std");
|
||||
const log = @import("../../log.zig");
|
||||
const v8 = @import("v8");
|
||||
|
||||
23
src/browser/webcomponents/webcomponents.zig
Normal file
23
src/browser/webcomponents/webcomponents.zig
Normal file
@@ -0,0 +1,23 @@
|
||||
// Copyright (C) 2023-2024 Lightpanda (Selecy SAS)
|
||||
//
|
||||
// Francis Bouvier <francis@lightpanda.io>
|
||||
// Pierre Tachoire <pierre@lightpanda.io>
|
||||
//
|
||||
// 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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
const CustomElementRegistry = @import("custom_element_registry.zig").CustomElementRegistry;
|
||||
|
||||
pub const Interfaces = .{
|
||||
CustomElementRegistry,
|
||||
};
|
||||
@@ -30,8 +30,6 @@ const Inspector = @import("../browser/env.zig").Env.Inspector;
|
||||
const Incrementing = @import("../id.zig").Incrementing;
|
||||
const Notification = @import("../notification.zig").Notification;
|
||||
|
||||
const polyfill = @import("../browser/polyfill/polyfill.zig");
|
||||
|
||||
pub const URL_BASE = "chrome://newtab/";
|
||||
pub const LOADER_ID = "LOADERID24DD2FD56CF1EF33C965C79C";
|
||||
|
||||
@@ -556,10 +554,6 @@ const IsolatedWorld = struct {
|
||||
executor: Env.ExecutionWorld,
|
||||
grant_universal_access: bool,
|
||||
|
||||
// Polyfill loader for the isolated world.
|
||||
// We want to load polyfill in the world's context.
|
||||
polyfill_loader: polyfill.Loader = .{},
|
||||
|
||||
pub fn deinit(self: *IsolatedWorld) void {
|
||||
self.executor.deinit();
|
||||
}
|
||||
@@ -575,16 +569,7 @@ const IsolatedWorld = struct {
|
||||
// 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 {
|
||||
if (self.executor.js_context != null) return error.Only1IsolatedContextSupported;
|
||||
_ = try self.executor.createJsContext(
|
||||
&page.window,
|
||||
page,
|
||||
{},
|
||||
false,
|
||||
.{
|
||||
.global_callback = Env.GlobalMissingCallback.init(&self.polyfill_loader),
|
||||
.compilation_callback = Env.CompilationCallback.init(&self.polyfill_loader),
|
||||
},
|
||||
);
|
||||
_ = try self.executor.createJsContext(&page.window, page, {}, false);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -284,6 +284,9 @@ pub fn pageCreated(bc: anytype, page: *Page) !void {
|
||||
if (bc.isolated_world) |*isolated_world| {
|
||||
// We need to recreate the isolated world context
|
||||
try isolated_world.createContext(page);
|
||||
|
||||
const polyfill = @import("../../browser/polyfill/polyfill.zig");
|
||||
try polyfill.load(bc.arena, &isolated_world.executor.js_context.?);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -39,13 +39,12 @@ pub const Scope = enum {
|
||||
unknown_prop,
|
||||
web_api,
|
||||
xhr,
|
||||
polyfill,
|
||||
};
|
||||
|
||||
const Opts = struct {
|
||||
format: Format = if (is_debug) .pretty else .logfmt,
|
||||
level: Level = if (is_debug) .info else .warn,
|
||||
filter_scopes: []const Scope = &.{},
|
||||
filter_scopes: []const Scope = &.{.unknown_prop},
|
||||
};
|
||||
|
||||
pub var opts = Opts{};
|
||||
|
||||
@@ -126,6 +126,8 @@ fn run(
|
||||
});
|
||||
defer runner.deinit();
|
||||
|
||||
try polyfill.load(arena, runner.page.main_context);
|
||||
|
||||
// loop over the scripts.
|
||||
const doc = parser.documentHTMLToDocument(runner.page.window.document);
|
||||
const scripts = try parser.documentGetElementsByTagName(doc, "script");
|
||||
|
||||
@@ -364,25 +364,13 @@ pub fn Env(comptime State: type, comptime WebApis: type) type {
|
||||
self.context_arena.deinit();
|
||||
}
|
||||
|
||||
pub const CreateJsContextOpt = struct {
|
||||
global_callback: ?GlobalMissingCallback = null,
|
||||
compilation_callback: ?CompilationCallback = null,
|
||||
};
|
||||
|
||||
// Only the top JsContext in the Main ExecutionWorld should hold a handle_scope.
|
||||
// A v8.HandleScope is like an arena. Once created, any "Local" that
|
||||
// v8 creates will be released (or at least, releasable by the v8 GC)
|
||||
// when the handle_scope is freed.
|
||||
// We also maintain our own "context_arena" which allows us to have
|
||||
// all page related memory easily managed.
|
||||
pub fn createJsContext(
|
||||
self: *ExecutionWorld,
|
||||
global: anytype,
|
||||
state: State,
|
||||
module_loader: anytype,
|
||||
enter: bool,
|
||||
opt: CreateJsContextOpt,
|
||||
) !*JsContext {
|
||||
pub fn createJsContext(self: *ExecutionWorld, global: anytype, state: State, module_loader: anytype, enter: bool) !*JsContext {
|
||||
std.debug.assert(self.js_context == null);
|
||||
|
||||
const ModuleLoader = switch (@typeInfo(@TypeOf(module_loader))) {
|
||||
@@ -411,30 +399,6 @@ pub fn Env(comptime State: type, comptime WebApis: type) type {
|
||||
const global_template = js_global.getInstanceTemplate();
|
||||
global_template.setInternalFieldCount(1);
|
||||
|
||||
// Configure the missing property callback on the global
|
||||
// object.
|
||||
if (opt.global_callback != null) {
|
||||
const configuration = v8.NamedPropertyHandlerConfiguration{
|
||||
.getter = struct {
|
||||
fn callback(c_name: ?*const v8.C_Name, raw_info: ?*const v8.C_PropertyCallbackInfo) callconv(.c) u8 {
|
||||
const info = v8.PropertyCallbackInfo.initFromV8(raw_info);
|
||||
const _isolate = info.getIsolate();
|
||||
const v8_context = _isolate.getCurrentContext();
|
||||
|
||||
const js_context: *JsContext = @ptrFromInt(v8_context.getEmbedderData(1).castTo(v8.BigInt).getUint64());
|
||||
|
||||
const property = valueToString(js_context.call_arena, .{ .handle = c_name.? }, _isolate, v8_context) catch "???";
|
||||
if (js_context.global_callback.?.missing(property, js_context)) {
|
||||
return v8.Intercepted.Yes;
|
||||
}
|
||||
return v8.Intercepted.No;
|
||||
}
|
||||
}.callback,
|
||||
.flags = v8.PropertyHandlerFlags.NonMasking | v8.PropertyHandlerFlags.OnlyInterceptStrings,
|
||||
};
|
||||
global_template.setNamedProperty(configuration, null);
|
||||
}
|
||||
|
||||
// All the FunctionTemplates that we created and setup in Env.init
|
||||
// are now going to get associated with our global instance.
|
||||
inline for (Types, 0..) |s, i| {
|
||||
@@ -517,8 +481,6 @@ pub fn Env(comptime State: type, comptime WebApis: type) type {
|
||||
.ptr = safe_module_loader,
|
||||
.func = ModuleLoader.fetchModuleSource,
|
||||
},
|
||||
.global_callback = opt.global_callback,
|
||||
.compilation_callback = opt.compilation_callback,
|
||||
};
|
||||
|
||||
var js_context = &self.js_context.?;
|
||||
@@ -671,12 +633,6 @@ pub fn Env(comptime State: type, comptime WebApis: type) type {
|
||||
// necessary to lookup/store the dependent module in the module_cache.
|
||||
module_identifier: std.AutoHashMapUnmanaged(u32, []const u8) = .empty,
|
||||
|
||||
// Global callback is called when a property is missing on the
|
||||
// global object.
|
||||
global_callback: ?GlobalMissingCallback = null,
|
||||
|
||||
compilation_callback: ?CompilationCallback = null,
|
||||
|
||||
const ModuleLoader = struct {
|
||||
ptr: *anyopaque,
|
||||
func: *const fn (ptr: *anyopaque, specifier: []const u8) anyerror!?[]const u8,
|
||||
@@ -763,8 +719,6 @@ pub fn Env(comptime State: type, comptime WebApis: type) type {
|
||||
}
|
||||
|
||||
pub fn exec(self: *JsContext, src: []const u8, name: ?[]const u8) !Value {
|
||||
if (self.compilation_callback) |cbk| cbk.script(src, name, self);
|
||||
|
||||
const isolate = self.isolate;
|
||||
const v8_context = self.v8_context;
|
||||
|
||||
@@ -788,8 +742,6 @@ pub fn Env(comptime State: type, comptime WebApis: type) type {
|
||||
// compile and eval a JS module
|
||||
// It doesn't wait for callbacks execution
|
||||
pub fn module(self: *JsContext, src: []const u8, url: []const u8, cacheable: bool) !void {
|
||||
if (self.compilation_callback) |cbk| cbk.module(src, url, self);
|
||||
|
||||
if (!cacheable) {
|
||||
return self.moduleNoCache(src, url);
|
||||
}
|
||||
@@ -2313,6 +2265,11 @@ pub fn Env(comptime State: type, comptime WebApis: type) type {
|
||||
|
||||
fn generateNamedIndexer(comptime Struct: type, template_proto: v8.ObjectTemplate) void {
|
||||
if (@hasDecl(Struct, "named_get") == false) {
|
||||
if (comptime builtin.mode == .Debug) {
|
||||
if (log.enabled(.unknown_prop, .debug)) {
|
||||
generateDebugNamedIndexer(Struct, template_proto);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2372,6 +2329,31 @@ pub fn Env(comptime State: type, comptime WebApis: type) type {
|
||||
template_proto.setNamedProperty(configuration, null);
|
||||
}
|
||||
|
||||
fn generateDebugNamedIndexer(comptime Struct: type, template_proto: v8.ObjectTemplate) void {
|
||||
const configuration = v8.NamedPropertyHandlerConfiguration{
|
||||
.getter = struct {
|
||||
fn callback(c_name: ?*const v8.C_Name, raw_info: ?*const v8.C_PropertyCallbackInfo) callconv(.c) u8 {
|
||||
const info = v8.PropertyCallbackInfo.initFromV8(raw_info);
|
||||
const isolate = info.getIsolate();
|
||||
const v8_context = isolate.getCurrentContext();
|
||||
|
||||
const js_context: *JsContext = @ptrFromInt(v8_context.getEmbedderData(1).castTo(v8.BigInt).getUint64());
|
||||
|
||||
const property = valueToString(js_context.call_arena, .{ .handle = c_name.? }, isolate, v8_context) catch "???";
|
||||
log.debug(.unknown_prop, "unkown property", .{ .@"struct" = @typeName(Struct), .property = property });
|
||||
return v8.Intercepted.No;
|
||||
}
|
||||
}.callback,
|
||||
|
||||
// This is really cool. Without this, we'd intercept _all_ properties
|
||||
// even those explicitly set. So, node.length for example would get routed
|
||||
// to our `named_get`, rather than a `get_length`. This might be
|
||||
// useful if we run into a type that we can't model properly in Zig.
|
||||
.flags = v8.PropertyHandlerFlags.OnlyInterceptStrings | v8.PropertyHandlerFlags.NonMasking,
|
||||
};
|
||||
template_proto.setNamedProperty(configuration, null);
|
||||
}
|
||||
|
||||
fn generateUndetectable(comptime Struct: type, template: v8.ObjectTemplate) void {
|
||||
const has_js_call_as_function = @hasDecl(Struct, "jsCallAsFunction");
|
||||
|
||||
@@ -2595,72 +2577,6 @@ pub fn Env(comptime State: type, comptime WebApis: type) type {
|
||||
self.callScopeEndFn(self.ptr);
|
||||
}
|
||||
};
|
||||
|
||||
// Callback called on global's property mssing.
|
||||
// Return true to intercept the exectution or false to let the call
|
||||
// continue the chain.
|
||||
pub const GlobalMissingCallback = struct {
|
||||
ptr: *anyopaque,
|
||||
missingFn: *const fn (ptr: *anyopaque, name: []const u8, ctx: *JsContext) bool,
|
||||
|
||||
pub fn init(ptr: anytype) GlobalMissingCallback {
|
||||
const T = @TypeOf(ptr);
|
||||
const ptr_info = @typeInfo(T);
|
||||
|
||||
const gen = struct {
|
||||
pub fn missing(pointer: *anyopaque, name: []const u8, ctx: *JsContext) bool {
|
||||
const self: T = @ptrCast(@alignCast(pointer));
|
||||
return ptr_info.pointer.child.missing(self, name, ctx);
|
||||
}
|
||||
};
|
||||
|
||||
return .{
|
||||
.ptr = ptr,
|
||||
.missingFn = gen.missing,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn missing(self: GlobalMissingCallback, name: []const u8, ctx: *JsContext) bool {
|
||||
return self.missingFn(self.ptr, name, ctx);
|
||||
}
|
||||
};
|
||||
|
||||
// CompilationCallback called before script and module compilation.
|
||||
pub const CompilationCallback = struct {
|
||||
ptr: *anyopaque,
|
||||
scriptFn: *const fn (ptr: *anyopaque, source: []const u8, name: ?[]const u8, ctx: *JsContext) void,
|
||||
moduleFn: *const fn (ptr: *anyopaque, source: []const u8, url: []const u8, ctx: *JsContext) void,
|
||||
|
||||
pub fn init(ptr: anytype) CompilationCallback {
|
||||
const T = @TypeOf(ptr);
|
||||
const ptr_info = @typeInfo(T);
|
||||
|
||||
const gen = struct {
|
||||
pub fn script(pointer: *anyopaque, source: []const u8, name: ?[]const u8, ctx: *JsContext) void {
|
||||
const self: T = @ptrCast(@alignCast(pointer));
|
||||
return ptr_info.pointer.child.script(self, source, name, ctx);
|
||||
}
|
||||
pub fn module(pointer: *anyopaque, source: []const u8, url: []const u8, ctx: *JsContext) void {
|
||||
const self: T = @ptrCast(@alignCast(pointer));
|
||||
return ptr_info.pointer.child.module(self, source, url, ctx);
|
||||
}
|
||||
};
|
||||
|
||||
return .{
|
||||
.ptr = ptr,
|
||||
.scriptFn = gen.script,
|
||||
.moduleFn = gen.module,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn script(self: CompilationCallback, source: []const u8, name: ?[]const u8, ctx: *JsContext) void {
|
||||
return self.scriptFn(self.ptr, source, name, ctx);
|
||||
}
|
||||
|
||||
pub fn module(self: CompilationCallback, source: []const u8, url: []const u8, ctx: *JsContext) void {
|
||||
return self.moduleFn(self.ptr, source, url, ctx);
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -336,8 +336,4 @@ test "JS: primitive types" {
|
||||
.{ "p.returnFloat32()", "1.100000023841858,-200.03500366210938,0.0003000000142492354" },
|
||||
.{ "p.returnFloat64()", "8881.22284,-4928.3838122,-0.00004" },
|
||||
}, .{});
|
||||
|
||||
try runner.testCases(&.{
|
||||
.{ "'foo\\\\:bar'", "foo\\:bar" },
|
||||
}, .{});
|
||||
}
|
||||
|
||||
@@ -53,7 +53,6 @@ pub fn Runner(comptime State: type, comptime Global: type, comptime types: anyty
|
||||
state,
|
||||
{},
|
||||
true,
|
||||
.{},
|
||||
);
|
||||
return self;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user