diff --git a/src/browser/tests/document/document.html b/src/browser/tests/document/document.html
index ede2b507..eb69f5d8 100644
--- a/src/browser/tests/document/document.html
+++ b/src/browser/tests/document/document.html
@@ -380,6 +380,53 @@
testing.expectEqual(0, nd.childElementCount);
+
+
diff --git a/src/browser/tests/element/selector_invalid.html b/src/browser/tests/element/selector_invalid.html
index c0d16d59..7112ab85 100644
--- a/src/browser/tests/element/selector_invalid.html
+++ b/src/browser/tests/element/selector_invalid.html
@@ -10,9 +10,9 @@
const container = $('#container');
// Empty functional pseudo-classes should error
- testing.expectError("Error: InvalidPseudoClass", () => container.querySelector(':has()'));
- testing.expectError("Error: InvalidPseudoClass", () => container.querySelector(':not()'));
- testing.expectError("Error: InvalidPseudoClass", () => container.querySelector(':lang()'));
+ testing.expectError("SyntaxError", () => container.querySelector(':has()'));
+ testing.expectError("SyntaxError", () => container.querySelector(':not()'));
+ testing.expectError("SyntaxError", () => container.querySelector(':lang()'));
}
@@ -21,9 +21,9 @@
const container = $('#container');
// Invalid nth patterns
- testing.expectError("Error: InvalidNthPattern", () => container.querySelector(':nth-child(foo)'));
- testing.expectError("Error: InvalidNthPattern", () => container.querySelector(':nth-child(-)'));
- testing.expectError("Error: InvalidNthPattern", () => container.querySelector(':nth-child(+)'));
+ testing.expectError("SyntaxError", () => container.querySelector(':nth-child(foo)'));
+ testing.expectError("SyntaxError", () => container.querySelector(':nth-child(-)'));
+ testing.expectError("SyntaxError", () => container.querySelector(':nth-child(+)'));
}
@@ -32,9 +32,9 @@
const container = $('#container');
// Unknown pseudo-classes
- testing.expectError("Error: UnknownPseudoClass", () => container.querySelector(':unknown'));
- testing.expectError("Error: UnknownPseudoClass", () => container.querySelector(':not-a-real-pseudo'));
- testing.expectError("Error: UnknownPseudoClass", () => container.querySelector(':fake(test)'));
+ testing.expectError("SyntaxError", () => container.querySelector(':unknown'));
+ testing.expectError("SyntaxError", () => container.querySelector(':not-a-real-pseudo'));
+ testing.expectError("SyntaxError", () => container.querySelector(':fake(test)'));
}
@@ -53,8 +53,8 @@
const container = $('#container');
// Combinators with nothing after
- testing.expectError("Error: InvalidSelector", () => container.querySelector('p >'));
- testing.expectError("Error: InvalidSelector", () => container.querySelector('p +'));
- testing.expectError("Error: InvalidSelector", () => container.querySelector('p ~'));
+ testing.expectError("SyntaxError", () => container.querySelector('p >'));
+ testing.expectError("SyntaxError", () => container.querySelector('p +'));
+ testing.expectError("SyntaxError", () => container.querySelector('p ~'));
}
diff --git a/src/browser/tests/page/meta.html b/src/browser/tests/page/meta.html
index 3c03f403..98fb1688 100644
--- a/src/browser/tests/page/meta.html
+++ b/src/browser/tests/page/meta.html
@@ -30,7 +30,8 @@
testing.expectEqual('undefined', typeof plainDoc.scripts);
testing.expectEqual('undefined', typeof plainDoc.links);
testing.expectEqual('undefined', typeof plainDoc.forms);
- testing.expectEqual('undefined', typeof plainDoc.location);
+ // location lives on Document (returns null for non-HTMLDocument).
+ testing.expectEqual(null, plainDoc.location);
// Both should have common Document properties
testing.expectEqual('string', typeof document.URL);
diff --git a/src/browser/webapi/DOMImplementation.zig b/src/browser/webapi/DOMImplementation.zig
index 777a9571..280db6a8 100644
--- a/src/browser/webapi/DOMImplementation.zig
+++ b/src/browser/webapi/DOMImplementation.zig
@@ -78,7 +78,7 @@ pub fn createDocument(_: *const DOMImplementation, namespace_: ?[]const u8, qual
// Create and append root element if qualified_name provided
if (qualified_name) |qname| {
if (qname.len > 0) {
- const namespace = if (namespace_) |ns| Node.Element.Namespace.parse(ns) else .xml;
+ const namespace = Node.Element.Namespace.parse(namespace_);
const root = try frame.createElementNS(namespace, qname, null);
_ = try document.asNode().appendChild(root, frame);
}
diff --git a/src/browser/webapi/Document.zig b/src/browser/webapi/Document.zig
index 747e08c2..95c57793 100644
--- a/src/browser/webapi/Document.zig
+++ b/src/browser/webapi/Document.zig
@@ -119,7 +119,18 @@ pub fn asEventTarget(self: *Document) *@import("EventTarget.zig") {
}
pub fn getURL(self: *const Document, frame: *const Frame) [:0]const u8 {
- return self._url orelse frame.url;
+ return self._url orelse (self._frame orelse frame).url;
+}
+
+pub fn getLocation(self: *const Document) ?*Location {
+ if (self._type != .html) return null;
+ const doc_frame = self._frame orelse return null;
+ return doc_frame.window._location;
+}
+
+pub fn setLocation(self: *Document, url: [:0]const u8, frame: *Frame) !void {
+ if (self._type != .html) return;
+ return frame.scheduleNavigation(url, .{ .reason = .script, .kind = .{ .push = null } }, .{ .script = self._frame });
}
pub fn getContentType(self: *const Document) []const u8 {
@@ -277,11 +288,11 @@ pub fn getSelection(self: *Document) *Selection {
}
pub fn querySelector(self: *Document, input: String, frame: *Frame) !?*Element {
- return Selector.querySelector(self.asNode(), input.str(), frame);
+ return Selector.querySelector(self.asNode(), input.str(), frame) catch |err| Selector.mapErrorToDOM(err);
}
pub fn querySelectorAll(self: *Document, input: String, frame: *Frame) !*Selector.List {
- return Selector.querySelectorAll(self.asNode(), input.str(), frame);
+ return Selector.querySelectorAll(self.asNode(), input.str(), frame) catch |err| Selector.mapErrorToDOM(err);
}
pub fn getImplementation(self: *Document, frame: *Frame) !*DOMImplementation {
@@ -465,15 +476,21 @@ pub fn getFonts(self: *Document, frame: *Frame) !*FontFaceSet {
return fonts;
}
-pub fn adoptNode(_: *const Document, node: *Node, frame: *Frame) !*Node {
+pub fn adoptNode(self: *Document, node: *Node, frame: *Frame) !*Node {
if (node._type == .document) {
return error.NotSupported;
}
+ const old_owner = node.ownerDocument(frame) orelse frame.document;
+
if (node._parent) |parent| {
frame.removeNode(parent, node, .{ .will_be_reconnected = false });
}
+ if (old_owner != self) {
+ try frame.adoptNodeTree(node, old_owner, self);
+ }
+
return node;
}
@@ -1029,6 +1046,7 @@ pub const JsApi = struct {
pub const onselectionchange = bridge.accessor(Document.getOnSelectionChange, Document.setOnSelectionChange, .{});
pub const URL = bridge.accessor(Document.getURL, null, .{});
+ pub const location = bridge.accessor(Document.getLocation, Document.setLocation, .{});
pub const documentURI = bridge.accessor(Document.getURL, null, .{});
pub const documentElement = bridge.accessor(Document.getDocumentElement, null, .{});
pub const scrollingElement = bridge.accessor(Document.getDocumentElement, null, .{});
diff --git a/src/browser/webapi/DocumentFragment.zig b/src/browser/webapi/DocumentFragment.zig
index 186bc68a..b55050f2 100644
--- a/src/browser/webapi/DocumentFragment.zig
+++ b/src/browser/webapi/DocumentFragment.zig
@@ -84,11 +84,11 @@ pub fn getElementById(self: *DocumentFragment, id: []const u8) ?*Element {
}
pub fn querySelector(self: *DocumentFragment, selector: []const u8, frame: *Frame) !?*Element {
- return Selector.querySelector(self.asNode(), selector, frame);
+ return Selector.querySelector(self.asNode(), selector, frame) catch |err| Selector.mapErrorToDOM(err);
}
pub fn querySelectorAll(self: *DocumentFragment, input: []const u8, frame: *Frame) !*Selector.List {
- return Selector.querySelectorAll(self.asNode(), input, frame);
+ return Selector.querySelectorAll(self.asNode(), input, frame) catch |err| Selector.mapErrorToDOM(err);
}
pub fn getChildren(self: *DocumentFragment, frame: *Frame) !collections.NodeLive(.child_elements) {
diff --git a/src/browser/webapi/Element.zig b/src/browser/webapi/Element.zig
index 4de1a732..058875a6 100644
--- a/src/browser/webapi/Element.zig
+++ b/src/browser/webapi/Element.zig
@@ -1071,15 +1071,15 @@ pub fn getChildElementCount(self: *Element) usize {
}
pub fn matches(self: *Element, selector: []const u8, frame: *Frame) !bool {
- return Selector.matches(self, selector, frame);
+ return Selector.matches(self, selector, frame) catch |err| Selector.mapErrorToDOM(err);
}
pub fn querySelector(self: *Element, selector: []const u8, frame: *Frame) !?*Element {
- return Selector.querySelector(self.asNode(), selector, frame);
+ return Selector.querySelector(self.asNode(), selector, frame) catch |err| Selector.mapErrorToDOM(err);
}
pub fn querySelectorAll(self: *Element, input: []const u8, frame: *Frame) !*Selector.List {
- return Selector.querySelectorAll(self.asNode(), input, frame);
+ return Selector.querySelectorAll(self.asNode(), input, frame) catch |err| Selector.mapErrorToDOM(err);
}
pub fn getAnimations(_: *const Element) []*Animation {
diff --git a/src/browser/webapi/HTMLDocument.zig b/src/browser/webapi/HTMLDocument.zig
index 41782cc8..19e462a1 100644
--- a/src/browser/webapi/HTMLDocument.zig
+++ b/src/browser/webapi/HTMLDocument.zig
@@ -196,15 +196,6 @@ pub fn getCurrentScript(self: *const HTMLDocument) ?*Element.Html.Script {
return self._proto._current_script;
}
-pub fn getLocation(self: *const HTMLDocument) ?*@import("Location.zig") {
- const frame = self._proto._frame orelse return null;
- return frame.window._location;
-}
-
-pub fn setLocation(self: *HTMLDocument, url: [:0]const u8, frame: *Frame) !void {
- return frame.scheduleNavigation(url, .{ .reason = .script, .kind = .{ .push = null } }, .{ .script = self._proto._frame });
-}
-
pub fn getDir(self: *HTMLDocument) []const u8 {
const el = self._proto.getDocumentElement() orelse return "";
const html = el.is(Element.Html) orelse return "";
@@ -311,7 +302,6 @@ pub const JsApi = struct {
pub const applets = bridge.accessor(HTMLDocument.getApplets, null, .{});
pub const plugins = bridge.accessor(HTMLDocument.getEmbeds, null, .{});
pub const currentScript = bridge.accessor(HTMLDocument.getCurrentScript, null, .{});
- pub const location = bridge.accessor(HTMLDocument.getLocation, HTMLDocument.setLocation, .{});
pub const all = bridge.accessor(HTMLDocument.getAll, null, .{});
pub const cookie = bridge.accessor(HTMLDocument.getCookie, HTMLDocument.setCookie, .{});
pub const doctype = bridge.accessor(HTMLDocument.getDocType, null, .{});
diff --git a/src/browser/webapi/Node.zig b/src/browser/webapi/Node.zig
index 7df1fd6a..c26411ed 100644
--- a/src/browser/webapi/Node.zig
+++ b/src/browser/webapi/Node.zig
@@ -166,7 +166,7 @@ pub fn findAdjacentNodes(self: *Node, position: []const u8) !struct { *Node, ?*N
// Returned if:
// * position is not one of the four listed values.
// * The input is XML that is not well-formed.
- return error.Syntax;
+ return error.SyntaxError;
}
pub fn firstChild(self: *const Node) ?*Node {
diff --git a/src/browser/webapi/selector/Selector.zig b/src/browser/webapi/selector/Selector.zig
index 2591ce6c..7322e02e 100644
--- a/src/browser/webapi/selector/Selector.zig
+++ b/src/browser/webapi/selector/Selector.zig
@@ -28,6 +28,22 @@ pub const List = @import("List.zig");
const String = lp.String;
const Allocator = std.mem.Allocator;
+// translate a Selector error to a DOMException known type.
+pub fn mapErrorToDOM(err: anyerror) anyerror {
+ return switch (err) {
+ error.InvalidSelector,
+ error.InvalidAttributeSelector,
+ error.InvalidIDSelector,
+ error.InvalidClassSelector,
+ error.UnknownPseudoClass,
+ error.InvalidTagSelector,
+ error.InvalidPseudoClass,
+ error.InvalidNthPattern,
+ => error.SyntaxError,
+ else => err,
+ };
+}
+
pub fn parseLeaky(arena: Allocator, input: []const u8) !Parsed {
if (input.len == 0) {
return error.SyntaxError;