mirror of
https://github.com/lightpanda-io/browser.git
synced 2025-12-16 16:28:58 +00:00
Improve DOMImplementation, DocumentType and DOMException
This commit is contained in:
@@ -1492,6 +1492,35 @@ pub fn createCDATASection(self: *Page, data: []const u8) !*Node {
|
||||
return cd.asNode();
|
||||
}
|
||||
|
||||
pub fn createProcessingInstruction(self: *Page, target: []const u8, data: []const u8) !*Node {
|
||||
// Validate target doesn't contain "?>"
|
||||
if (std.mem.indexOf(u8, target, "?>") != null) {
|
||||
return error.InvalidCharacterError;
|
||||
}
|
||||
|
||||
// Validate target follows XML name rules (similar to attribute name validation)
|
||||
try Element.Attribute.validateAttributeName(target);
|
||||
|
||||
const owned_target = try self.dupeString(target);
|
||||
const owned_data = try self.dupeString(data);
|
||||
|
||||
const pi = try self._factory.create(CData.ProcessingInstruction{
|
||||
._proto = undefined,
|
||||
._target = owned_target,
|
||||
});
|
||||
|
||||
const cd = try self._factory.node(CData{
|
||||
._proto = undefined,
|
||||
._type = .{ .processing_instruction = pi },
|
||||
._data = owned_data,
|
||||
});
|
||||
|
||||
// Set up the back pointer from ProcessingInstruction to CData
|
||||
pi._proto = cd;
|
||||
|
||||
return cd.asNode();
|
||||
}
|
||||
|
||||
pub fn dupeString(self: *Page, value: []const u8) ![]const u8 {
|
||||
if (String.intern(value)) |v| {
|
||||
return v;
|
||||
|
||||
@@ -74,6 +74,12 @@ fn _deep(node: *Node, opts: Opts, comptime force_slot: bool, writer: *std.Io.Wri
|
||||
try writer.writeAll("<!--");
|
||||
try writer.writeAll(cd.getData());
|
||||
try writer.writeAll("-->");
|
||||
} else if (node.is(Node.CData.ProcessingInstruction)) |pi| {
|
||||
try writer.writeAll("<?");
|
||||
try writer.writeAll(pi._target);
|
||||
try writer.writeAll(" ");
|
||||
try writer.writeAll(cd.getData());
|
||||
try writer.writeAll("?>");
|
||||
} else {
|
||||
if (shouldEscapeText(node._parent)) {
|
||||
try writeEscapedText(cd.getData(), writer);
|
||||
|
||||
@@ -492,6 +492,7 @@ pub const JsApis = flattenTypes(&.{
|
||||
@import("../webapi/cdata/Comment.zig"),
|
||||
@import("../webapi/cdata/Text.zig"),
|
||||
@import("../webapi/cdata/CDATASection.zig"),
|
||||
@import("../webapi/cdata/ProcessingInstruction.zig"),
|
||||
@import("../webapi/collections.zig"),
|
||||
@import("../webapi/Console.zig"),
|
||||
@import("../webapi/Crypto.zig"),
|
||||
@@ -506,6 +507,7 @@ pub const JsApis = flattenTypes(&.{
|
||||
@import("../webapi/css/StyleSheetList.zig"),
|
||||
@import("../webapi/Document.zig"),
|
||||
@import("../webapi/HTMLDocument.zig"),
|
||||
@import("../webapi/XMLDocument.zig"),
|
||||
@import("../webapi/History.zig"),
|
||||
@import("../webapi/KeyValueList.zig"),
|
||||
@import("../webapi/DocumentFragment.zig"),
|
||||
|
||||
135
src/browser/tests/domexception.html
Normal file
135
src/browser/tests/domexception.html
Normal file
@@ -0,0 +1,135 @@
|
||||
<!DOCTYPE html>
|
||||
<head>
|
||||
<title>DOMException Test</title>
|
||||
<script src="testing.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
</body>
|
||||
|
||||
<script id=constructor_no_args>
|
||||
{
|
||||
const ex = new DOMException();
|
||||
testing.expectEqual('Error', ex.toString());
|
||||
testing.expectEqual('Error', ex.name);
|
||||
testing.expectEqual('', ex.message);
|
||||
testing.expectEqual(0, ex.code);
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id=constructor_with_message>
|
||||
{
|
||||
const ex = new DOMException('Something went wrong');
|
||||
testing.expectEqual('Error', ex.name);
|
||||
testing.expectEqual('Something went wrong', ex.message);
|
||||
testing.expectEqual(0, ex.code);
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id=constructor_with_message_and_name>
|
||||
{
|
||||
const ex = new DOMException('Custom error message', 'NotFoundError');
|
||||
testing.expectEqual('NotFoundError', ex.name);
|
||||
testing.expectEqual('Custom error message', ex.message);
|
||||
testing.expectEqual(8, ex.code);
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id=legacy_error_codes>
|
||||
{
|
||||
// Test standard errors with legacy codes
|
||||
const errors = [
|
||||
{ name: 'IndexSizeError', code: 1 },
|
||||
{ name: 'HierarchyRequestError', code: 3 },
|
||||
{ name: 'WrongDocumentError', code: 4 },
|
||||
{ name: 'InvalidCharacterError', code: 5 },
|
||||
{ name: 'NoModificationAllowedError', code: 7 },
|
||||
{ name: 'NotFoundError', code: 8 },
|
||||
{ name: 'NotSupportedError', code: 9 },
|
||||
{ name: 'InUseAttributeError', code: 10 },
|
||||
{ name: 'InvalidStateError', code: 11 },
|
||||
{ name: 'SyntaxError', code: 12 },
|
||||
{ name: 'InvalidModificationError', code: 13 },
|
||||
{ name: 'NamespaceError', code: 14 },
|
||||
{ name: 'InvalidAccessError', code: 15 },
|
||||
{ name: 'SecurityError', code: 18 },
|
||||
{ name: 'NetworkError', code: 19 },
|
||||
{ name: 'AbortError', code: 20 },
|
||||
{ name: 'URLMismatchError', code: 21 },
|
||||
{ name: 'QuotaExceededError', code: 22 },
|
||||
{ name: 'TimeoutError', code: 23 },
|
||||
{ name: 'InvalidNodeTypeError', code: 24 },
|
||||
{ name: 'DataCloneError', code: 25 },
|
||||
];
|
||||
|
||||
for (const { name, code } of errors) {
|
||||
const ex = new DOMException('test', name);
|
||||
testing.expectEqual(name, ex.name);
|
||||
testing.expectEqual(code, ex.code);
|
||||
testing.expectEqual('test', ex.message);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id=custom_error_name>
|
||||
{
|
||||
// Non-standard error names should have code 0
|
||||
const ex = new DOMException('Custom message', 'MyCustomError');
|
||||
testing.expectEqual('MyCustomError', ex.name);
|
||||
testing.expectEqual('Custom message', ex.message);
|
||||
testing.expectEqual(0, ex.code);
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id=modern_errors_no_code>
|
||||
{
|
||||
// Modern errors that don't have legacy codes
|
||||
const modernErrors = [
|
||||
'EncodingError',
|
||||
'NotReadableError',
|
||||
'UnknownError',
|
||||
'ConstraintError',
|
||||
'DataError',
|
||||
'TransactionInactiveError',
|
||||
'ReadOnlyError',
|
||||
'VersionError',
|
||||
'OperationError',
|
||||
'NotAllowedError'
|
||||
];
|
||||
|
||||
for (const name of modernErrors) {
|
||||
const ex = new DOMException('test', name);
|
||||
testing.expectEqual(name, ex.name);
|
||||
testing.expectEqual(0, ex.code);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id=thrown_exception>
|
||||
{
|
||||
try {
|
||||
throw new DOMException('Operation failed', 'InvalidStateError');
|
||||
} catch (e) {
|
||||
testing.expectEqual('InvalidStateError', e.name);
|
||||
testing.expectEqual('Operation failed', e.message);
|
||||
testing.expectEqual(11, e.code);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div id="content">
|
||||
<a id="link" href="foo" class="ok">OK</a>
|
||||
</div>
|
||||
<!-- <script id=hierarchy_error>
|
||||
let link = $('#link');
|
||||
let content = $('#content');
|
||||
|
||||
testing.withError((err) => {
|
||||
const msg = "Failed to execute 'appendChild' on 'Node': The new child element contains the parent.";
|
||||
testing.expectEqual(3, err.code);
|
||||
testing.expectEqual(msg, err.message);
|
||||
testing.expectEqual('HierarchyRequestError: ' + msg, err.toString());
|
||||
testing.expectEqual(true, err instanceof DOMException);
|
||||
testing.expectEqual(true, err instanceof Error);
|
||||
}, () => link.appendChild(content));
|
||||
</script> -->
|
||||
@@ -55,7 +55,177 @@
|
||||
const impl = document.implementation;
|
||||
const doctype = impl.createDocumentType('html', null, null);
|
||||
testing.expectEqual('html', doctype.name);
|
||||
testing.expectEqual('', doctype.publicId);
|
||||
testing.expectEqual('', doctype.systemId);
|
||||
testing.expectEqual('null', doctype.publicId);
|
||||
testing.expectEqual('null', doctype.systemId);
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id=createHTMLDocument_no_title>
|
||||
{
|
||||
const impl = document.implementation;
|
||||
const doc = impl.createHTMLDocument();
|
||||
|
||||
testing.expectEqual(true, doc instanceof HTMLDocument);
|
||||
testing.expectEqual(true, doc instanceof Document);
|
||||
testing.expectEqual(9, doc.nodeType);
|
||||
testing.expectEqual('complete', doc.readyState);
|
||||
|
||||
// Should have DOCTYPE
|
||||
testing.expectEqual(true, doc.firstChild instanceof DocumentType);
|
||||
testing.expectEqual('html', doc.firstChild.name);
|
||||
|
||||
// Should have html element
|
||||
const html = doc.documentElement;
|
||||
testing.expectEqual(true, html !== null);
|
||||
testing.expectEqual('HTML', html.tagName);
|
||||
|
||||
// Should have head
|
||||
const head = doc.head;
|
||||
testing.expectEqual(true, head !== null);
|
||||
testing.expectEqual('HEAD', head.tagName);
|
||||
|
||||
// Should have body
|
||||
const body = doc.body;
|
||||
testing.expectEqual(true, body !== null);
|
||||
testing.expectEqual('BODY', body.tagName);
|
||||
|
||||
// Title should be empty when not provided
|
||||
testing.expectEqual('', doc.title);
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id=createHTMLDocument_with_title>
|
||||
{
|
||||
const impl = document.implementation;
|
||||
const doc = impl.createHTMLDocument('Test Document');
|
||||
|
||||
testing.expectEqual('Test Document', doc.title);
|
||||
|
||||
// Should have title element in head
|
||||
const titleElement = doc.head.querySelector('title');
|
||||
testing.expectEqual(true, titleElement !== null);
|
||||
testing.expectEqual('Test Document', titleElement.textContent);
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id=createHTMLDocument_structure>
|
||||
{
|
||||
const impl = document.implementation;
|
||||
const doc = impl.createHTMLDocument('My Doc');
|
||||
|
||||
// Verify complete structure: DOCTYPE -> html -> (head, body)
|
||||
testing.expectEqual(2, doc.childNodes.length); // DOCTYPE and html
|
||||
|
||||
const html = doc.documentElement;
|
||||
testing.expectEqual(2, html.childNodes.length); // head and body
|
||||
|
||||
testing.expectEqual(doc.head, html.firstChild);
|
||||
testing.expectEqual(doc.body, html.lastChild);
|
||||
|
||||
// Head should contain only title when title is provided
|
||||
testing.expectEqual(1, doc.head.childNodes.length);
|
||||
testing.expectEqual('TITLE', doc.head.firstChild.tagName);
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id=createHTMLDocument_manipulation>
|
||||
{
|
||||
const impl = document.implementation;
|
||||
const doc = impl.createHTMLDocument();
|
||||
|
||||
// Should be able to manipulate the created document
|
||||
const div = doc.createElement('div');
|
||||
div.textContent = 'Hello World';
|
||||
doc.body.appendChild(div);
|
||||
|
||||
testing.expectEqual(1, doc.body.childNodes.length);
|
||||
testing.expectEqual('Hello World', doc.body.firstChild.textContent);
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id=createDocument_minimal>
|
||||
{
|
||||
const impl = document.implementation;
|
||||
const doc = impl.createDocument(null, null, null);
|
||||
|
||||
testing.expectEqual(true, doc instanceof Document);
|
||||
testing.expectEqual(9, doc.nodeType);
|
||||
testing.expectEqual('[object XMLDocument]', doc.toString());
|
||||
|
||||
// Should be empty - no doctype, no root element
|
||||
testing.expectEqual(0, doc.childNodes.length);
|
||||
testing.expectEqual(null, doc.documentElement);
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id=createDocument_with_root>
|
||||
{
|
||||
const impl = document.implementation;
|
||||
const doc = impl.createDocument(null, 'root', null);
|
||||
|
||||
testing.expectEqual(1, doc.childNodes.length);
|
||||
|
||||
const root = doc.documentElement;
|
||||
testing.expectEqual(true, root !== null);
|
||||
// TODO: XML documents should preserve case, but we currently uppercase
|
||||
testing.expectEqual('ROOT', root.tagName);
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id=createDocument_with_namespace>
|
||||
{
|
||||
const impl = document.implementation;
|
||||
const doc = impl.createDocument('http://www.w3.org/2000/svg', 'svg', null);
|
||||
|
||||
const root = doc.documentElement;
|
||||
testing.expectEqual('svg', root.nodeName);
|
||||
testing.expectEqual('http://www.w3.org/2000/svg', root.namespaceURI);
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id=createDocument_with_doctype>
|
||||
{
|
||||
const impl = document.implementation;
|
||||
const doctype = impl.createDocumentType('svg', '-//W3C//DTD SVG 1.1//EN', 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd');
|
||||
const doc = impl.createDocument('http://www.w3.org/2000/svg', 'svg', doctype);
|
||||
|
||||
testing.expectEqual(2, doc.childNodes.length);
|
||||
|
||||
// First child should be doctype
|
||||
testing.expectEqual(true, doc.firstChild instanceof DocumentType);
|
||||
testing.expectEqual('svg', doc.firstChild.name);
|
||||
|
||||
// Second child should be root element
|
||||
testing.expectEqual('svg', doc.documentElement.nodeName);
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id=createDocument_qualified_name>
|
||||
{
|
||||
const impl = document.implementation;
|
||||
const doc = impl.createDocument('http://example.com', 'prefix:localName', null);
|
||||
|
||||
const root = doc.documentElement;
|
||||
// TODO: XML documents should preserve case, but we currently uppercase
|
||||
testing.expectEqual('prefix:LOCALNAME', root.tagName);
|
||||
// TODO: Custom namespaces are being overridden to XHTML namespace
|
||||
testing.expectEqual('http://www.w3.org/1999/xhtml', root.namespaceURI);
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id=createDocument_manipulation>
|
||||
{
|
||||
const impl = document.implementation;
|
||||
const doc = impl.createDocument(null, 'root', null);
|
||||
|
||||
// Should be able to manipulate the created document
|
||||
const child = doc.createElement('child');
|
||||
child.textContent = 'Test';
|
||||
doc.documentElement.appendChild(child);
|
||||
|
||||
testing.expectEqual(1, doc.documentElement.childNodes.length);
|
||||
// TODO: XML documents should preserve case, but we currently uppercase
|
||||
testing.expectEqual('CHILD', doc.documentElement.firstChild.tagName);
|
||||
testing.expectEqual('Test', doc.documentElement.firstChild.textContent);
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
testing.expectEqual("[object HTMLDocument]", doc.toString());
|
||||
testing.expectEqual("foo", doc.title);
|
||||
testing.expectEqual("[object HTMLBodyElement]", doc.body.toString());
|
||||
testing.expectEqual("[object Document]", impl.createDocument(null, 'foo').toString());
|
||||
testing.expectEqual("[object XMLDocument]", impl.createDocument(null, 'foo').toString());
|
||||
testing.expectEqual("[object DocumentType]", impl.createDocumentType('foo', 'bar', 'baz').toString());
|
||||
testing.expectEqual(true, impl.hasFeature());
|
||||
</script>
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
|
||||
testing.withError((err) => {
|
||||
testing.expectEqual(3, err.code);
|
||||
testing.expectEqual("HierarchyError", err.name);
|
||||
testing.expectEqual("HierarchyRequestError", err.name);
|
||||
testing.expectEqual("Hierarchy Error", err.message);
|
||||
}, () => d1.replaceChild(c4, c3));
|
||||
|
||||
|
||||
171
src/browser/tests/processing_instruction.html
Normal file
171
src/browser/tests/processing_instruction.html
Normal file
@@ -0,0 +1,171 @@
|
||||
<!DOCTYPE html>
|
||||
<script src="testing.js"></script>
|
||||
|
||||
<script id=basic>
|
||||
{
|
||||
const pi = document.createProcessingInstruction('xml-stylesheet', 'href="style.css"');
|
||||
testing.expectEqual('object', typeof pi);
|
||||
testing.expectEqual(true, pi instanceof ProcessingInstruction);
|
||||
testing.expectEqual(true, pi instanceof Node);
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id=properties>
|
||||
{
|
||||
const pi = document.createProcessingInstruction('foo', 'bar');
|
||||
testing.expectEqual('foo', pi.target);
|
||||
testing.expectEqual('bar', pi.data);
|
||||
testing.expectEqual(3, pi.length);
|
||||
testing.expectEqual(7, pi.nodeType);
|
||||
testing.expectEqual('foo', pi.nodeName);
|
||||
testing.expectEqual('bar', pi.nodeValue);
|
||||
testing.expectEqual('bar', pi.textContent);
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id=empty_data>
|
||||
{
|
||||
const pi = document.createProcessingInstruction('target', '');
|
||||
testing.expectEqual('target', pi.target);
|
||||
testing.expectEqual('', pi.data);
|
||||
testing.expectEqual(0, pi.length);
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id=set_data>
|
||||
{
|
||||
const pi = document.createProcessingInstruction('foo', 'bar');
|
||||
pi.data = 'baz';
|
||||
testing.expectEqual('baz', pi.data);
|
||||
testing.expectEqual('foo', pi.target); // target shouldn't change
|
||||
testing.expectEqual(3, pi.length);
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id=set_textContent>
|
||||
{
|
||||
const pi = document.createProcessingInstruction('target', 'original');
|
||||
pi.textContent = 'modified';
|
||||
testing.expectEqual('modified', pi.data);
|
||||
testing.expectEqual('modified', pi.textContent);
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id=set_nodeValue>
|
||||
{
|
||||
const pi = document.createProcessingInstruction('target', 'original');
|
||||
pi.nodeValue = 'changed';
|
||||
testing.expectEqual('changed', pi.data);
|
||||
testing.expectEqual('changed', pi.nodeValue);
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id=cloneNode>
|
||||
{
|
||||
const pi = document.createProcessingInstruction('xml-stylesheet', 'href="style.css"');
|
||||
const clone = pi.cloneNode();
|
||||
testing.expectEqual('xml-stylesheet', clone.target);
|
||||
testing.expectEqual('href="style.css"', clone.data);
|
||||
testing.expectEqual(7, clone.nodeType);
|
||||
testing.expectEqual(true, clone instanceof ProcessingInstruction);
|
||||
|
||||
// Clone should be a different object
|
||||
testing.expectEqual(false, pi === clone);
|
||||
|
||||
// Modifying clone shouldn't affect original
|
||||
clone.data = 'different';
|
||||
testing.expectEqual('href="style.css"', pi.data);
|
||||
testing.expectEqual('different', clone.data);
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id=isEqualNode>
|
||||
{
|
||||
const pi1 = document.createProcessingInstruction('target1', 'data1');
|
||||
const pi2 = document.createProcessingInstruction('target2', 'data2');
|
||||
const pi3 = document.createProcessingInstruction('target1', 'data1');
|
||||
const pi4 = document.createProcessingInstruction('target1', 'data2');
|
||||
|
||||
testing.expectEqual(true, pi1.isEqualNode(pi1));
|
||||
testing.expectEqual(true, pi1.isEqualNode(pi3));
|
||||
testing.expectEqual(false, pi1.isEqualNode(pi2));
|
||||
testing.expectEqual(false, pi2.isEqualNode(pi3));
|
||||
testing.expectEqual(false, pi1.isEqualNode(pi4)); // different data
|
||||
testing.expectEqual(false, pi1.isEqualNode(document));
|
||||
testing.expectEqual(false, document.isEqualNode(pi1));
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id=invalid_target_question_mark_gt>
|
||||
{
|
||||
try {
|
||||
document.createProcessingInstruction('tar?>get', 'data');
|
||||
testing.fail('Should throw InvalidCharacterError for "?>" in target');
|
||||
} catch (e) {
|
||||
testing.expectEqual('InvalidCharacterError', e.name);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id=invalid_target_empty>
|
||||
{
|
||||
try {
|
||||
document.createProcessingInstruction('', 'data');
|
||||
testing.fail('Should throw InvalidCharacterError for empty target');
|
||||
} catch (e) {
|
||||
testing.expectEqual('InvalidCharacterError', e.name);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id=invalid_target_starts_with_number>
|
||||
{
|
||||
try {
|
||||
document.createProcessingInstruction('0target', 'data');
|
||||
testing.fail('Should throw InvalidCharacterError for target starting with number');
|
||||
} catch (e) {
|
||||
testing.expectEqual('InvalidCharacterError', e.name);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id=valid_target_with_colon>
|
||||
{
|
||||
// xml:foo should be valid
|
||||
const pi = document.createProcessingInstruction('xml:stylesheet', 'data');
|
||||
testing.expectEqual('xml:stylesheet', pi.target);
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id=characterData_methods>
|
||||
{
|
||||
const pi = document.createProcessingInstruction('target', 'abcdef');
|
||||
|
||||
// substringData
|
||||
testing.expectEqual('bcd', pi.substringData(1, 3));
|
||||
testing.expectEqual('def', pi.substringData(3, 10)); // should clamp to end
|
||||
|
||||
// appendData
|
||||
pi.appendData('ghi');
|
||||
testing.expectEqual('abcdefghi', pi.data);
|
||||
|
||||
// insertData
|
||||
pi.insertData(3, 'XXX');
|
||||
testing.expectEqual('abcXXXdefghi', pi.data);
|
||||
|
||||
// deleteData
|
||||
pi.deleteData(3, 3);
|
||||
testing.expectEqual('abcdefghi', pi.data);
|
||||
|
||||
// replaceData
|
||||
pi.replaceData(3, 3, 'YYY');
|
||||
testing.expectEqual('abcYYYghi', pi.data);
|
||||
}
|
||||
</script>
|
||||
|
||||
<script id=owner_document>
|
||||
{
|
||||
const pi = document.createProcessingInstruction('target', 'data');
|
||||
testing.expectEqual(document, pi.ownerDocument);
|
||||
}
|
||||
</script>
|
||||
@@ -25,6 +25,7 @@ const Node = @import("Node.zig");
|
||||
pub const Text = @import("cdata/Text.zig");
|
||||
pub const Comment = @import("cdata/Comment.zig");
|
||||
pub const CDATASection = @import("cdata/CDATASection.zig");
|
||||
pub const ProcessingInstruction = @import("cdata/ProcessingInstruction.zig");
|
||||
|
||||
const CData = @This();
|
||||
|
||||
@@ -38,6 +39,7 @@ pub const Type = union(enum) {
|
||||
// This should be under Text, but that would require storing a _type union
|
||||
// in text, which would add 8 bytes to every text node.
|
||||
cdata_section: CDATASection,
|
||||
processing_instruction: *ProcessingInstruction,
|
||||
};
|
||||
|
||||
pub fn asNode(self: *CData) *Node {
|
||||
@@ -58,6 +60,7 @@ pub fn className(self: *const CData) []const u8 {
|
||||
.text => "[object Text]",
|
||||
.comment => "[object Comment]",
|
||||
.cdata_section => "[object CDATASection]",
|
||||
.processing_instruction => "[object ProcessingInstruction]",
|
||||
};
|
||||
}
|
||||
|
||||
@@ -139,6 +142,7 @@ pub fn format(self: *const CData, writer: *std.io.Writer) !void {
|
||||
.text => writer.print("<text>{s}</text>", .{self._data}),
|
||||
.comment => writer.print("<!-- {s} -->", .{self._data}),
|
||||
.cdata_section => writer.print("<![CDATA[{s}]]>", .{self._data}),
|
||||
.processing_instruction => |pi| writer.print("<?{s} {s}?>", .{ pi._target, self._data }),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -147,6 +151,18 @@ pub fn getLength(self: *const CData) usize {
|
||||
}
|
||||
|
||||
pub fn isEqualNode(self: *const CData, other: *const CData) bool {
|
||||
if (std.meta.activeTag(self._type) != std.meta.activeTag(other._type)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (self._type == .processing_instruction) {
|
||||
@branchHint(.unlikely);
|
||||
if (std.mem.eql(u8, self._type.processing_instruction._target, other._type.processing_instruction._target) == false) {
|
||||
return false;
|
||||
}
|
||||
// if the _targets are equal, we still want to compare the data
|
||||
}
|
||||
|
||||
return std.mem.eql(u8, self.getData(), other.getData());
|
||||
}
|
||||
|
||||
|
||||
@@ -16,14 +16,24 @@
|
||||
// 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 js = @import("../js/js.zig");
|
||||
const Page = @import("../Page.zig");
|
||||
|
||||
const DOMException = @This();
|
||||
_code: Code = .none,
|
||||
|
||||
pub fn init() DOMException {
|
||||
return .{};
|
||||
_code: Code = .none,
|
||||
_custom_message: ?[]const u8 = null,
|
||||
_custom_name: ?[]const u8 = null,
|
||||
|
||||
pub fn init(message: ?[]const u8, name: ?[]const u8) DOMException {
|
||||
// If name is provided, try to map it to a legacy code
|
||||
const code = if (name) |n| Code.fromName(n) else .none;
|
||||
return .{
|
||||
._code = code,
|
||||
._custom_message = message,
|
||||
._custom_name = name,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn fromError(err: anyerror) ?DOMException {
|
||||
@@ -34,6 +44,21 @@ pub fn fromError(err: anyerror) ?DOMException {
|
||||
error.NotSupported => .{ ._code = .not_supported },
|
||||
error.HierarchyError => .{ ._code = .hierarchy_error },
|
||||
error.IndexSizeError => .{ ._code = .index_size_error },
|
||||
error.InvalidStateError => .{ ._code = .invalid_state_error },
|
||||
error.WrongDocument => .{ ._code = .wrong_document_error },
|
||||
error.NoModificationAllowed => .{ ._code = .no_modification_allowed_error },
|
||||
error.InUseAttribute => .{ ._code = .inuse_attribute_error },
|
||||
error.InvalidModification => .{ ._code = .invalid_modification_error },
|
||||
error.NamespaceError => .{ ._code = .namespace_error },
|
||||
error.InvalidAccess => .{ ._code = .invalid_access_error },
|
||||
error.SecurityError => .{ ._code = .security_error },
|
||||
error.NetworkError => .{ ._code = .network_error },
|
||||
error.AbortError => .{ ._code = .abort_error },
|
||||
error.URLMismatch => .{ ._code = .url_mismatch_error },
|
||||
error.QuotaExceeded => .{ ._code = .quota_exceeded_error },
|
||||
error.TimeoutError => .{ ._code = .timeout_error },
|
||||
error.InvalidNodeType => .{ ._code = .invalid_node_type_error },
|
||||
error.DataClone => .{ ._code = .data_clone_error },
|
||||
else => null,
|
||||
};
|
||||
}
|
||||
@@ -43,18 +68,40 @@ pub fn getCode(self: *const DOMException) u8 {
|
||||
}
|
||||
|
||||
pub fn getName(self: *const DOMException) []const u8 {
|
||||
if (self._custom_name) |name| {
|
||||
return name;
|
||||
}
|
||||
|
||||
return switch (self._code) {
|
||||
.none => "Error",
|
||||
.index_size_error => "IndexSizeError",
|
||||
.hierarchy_error => "HierarchyRequestError",
|
||||
.wrong_document_error => "WrongDocumentError",
|
||||
.invalid_character_error => "InvalidCharacterError",
|
||||
.index_size_error => "IndexSizeErorr",
|
||||
.syntax_error => "SyntaxError",
|
||||
.no_modification_allowed_error => "NoModificationAllowedError",
|
||||
.not_found => "NotFoundError",
|
||||
.not_supported => "NotSupportedError",
|
||||
.hierarchy_error => "HierarchyError",
|
||||
.inuse_attribute_error => "InUseAttributeError",
|
||||
.invalid_state_error => "InvalidStateError",
|
||||
.syntax_error => "SyntaxError",
|
||||
.invalid_modification_error => "InvalidModificationError",
|
||||
.namespace_error => "NamespaceError",
|
||||
.invalid_access_error => "InvalidAccessError",
|
||||
.security_error => "SecurityError",
|
||||
.network_error => "NetworkError",
|
||||
.abort_error => "AbortError",
|
||||
.url_mismatch_error => "URLMismatchError",
|
||||
.quota_exceeded_error => "QuotaExceededError",
|
||||
.timeout_error => "TimeoutError",
|
||||
.invalid_node_type_error => "InvalidNodeTypeError",
|
||||
.data_clone_error => "DataCloneError",
|
||||
};
|
||||
}
|
||||
|
||||
pub fn getMessage(self: *const DOMException) []const u8 {
|
||||
if (self._custom_message) |msg| {
|
||||
return msg;
|
||||
}
|
||||
return switch (self._code) {
|
||||
.none => "",
|
||||
.invalid_character_error => "Error: Invalid Character",
|
||||
@@ -63,17 +110,76 @@ pub fn getMessage(self: *const DOMException) []const u8 {
|
||||
.not_supported => "Not Supported",
|
||||
.not_found => "Not Found",
|
||||
.hierarchy_error => "Hierarchy Error",
|
||||
else => @tagName(self._code),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn toString(self: *const DOMException) []const u8 {
|
||||
if (self._custom_message) |msg| {
|
||||
return msg;
|
||||
}
|
||||
return switch (self._code) {
|
||||
.none => "Error",
|
||||
else => self.getMessage(),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn className(_: *const DOMException) []const u8 {
|
||||
return "[object DOMException]";
|
||||
}
|
||||
|
||||
const Code = enum(u8) {
|
||||
none = 0,
|
||||
index_size_error = 1,
|
||||
hierarchy_error = 3,
|
||||
wrong_document_error = 4,
|
||||
invalid_character_error = 5,
|
||||
no_modification_allowed_error = 7,
|
||||
not_found = 8,
|
||||
not_supported = 9,
|
||||
inuse_attribute_error = 10,
|
||||
invalid_state_error = 11,
|
||||
syntax_error = 12,
|
||||
invalid_modification_error = 13,
|
||||
namespace_error = 14,
|
||||
invalid_access_error = 15,
|
||||
security_error = 18,
|
||||
network_error = 19,
|
||||
abort_error = 20,
|
||||
url_mismatch_error = 21,
|
||||
quota_exceeded_error = 22,
|
||||
timeout_error = 23,
|
||||
invalid_node_type_error = 24,
|
||||
data_clone_error = 25,
|
||||
|
||||
/// Maps a standard error name to its legacy code
|
||||
/// Returns .none (code 0) for non-legacy error names
|
||||
pub fn fromName(name: []const u8) Code {
|
||||
const lookup = std.StaticStringMap(Code).initComptime(.{
|
||||
.{ "IndexSizeError", .index_size_error },
|
||||
.{ "HierarchyRequestError", .hierarchy_error },
|
||||
.{ "WrongDocumentError", .wrong_document_error },
|
||||
.{ "InvalidCharacterError", .invalid_character_error },
|
||||
.{ "NoModificationAllowedError", .no_modification_allowed_error },
|
||||
.{ "NotFoundError", .not_found },
|
||||
.{ "NotSupportedError", .not_supported },
|
||||
.{ "InUseAttributeError", .inuse_attribute_error },
|
||||
.{ "InvalidStateError", .invalid_state_error },
|
||||
.{ "SyntaxError", .syntax_error },
|
||||
.{ "InvalidModificationError", .invalid_modification_error },
|
||||
.{ "NamespaceError", .namespace_error },
|
||||
.{ "InvalidAccessError", .invalid_access_error },
|
||||
.{ "SecurityError", .security_error },
|
||||
.{ "NetworkError", .network_error },
|
||||
.{ "AbortError", .abort_error },
|
||||
.{ "URLMismatchError", .url_mismatch_error },
|
||||
.{ "QuotaExceededError", .quota_exceeded_error },
|
||||
.{ "TimeoutError", .timeout_error },
|
||||
.{ "InvalidNodeTypeError", .invalid_node_type_error },
|
||||
.{ "DataCloneError", .data_clone_error },
|
||||
});
|
||||
return lookup.get(name) orelse .none;
|
||||
}
|
||||
};
|
||||
|
||||
pub const JsApi = struct {
|
||||
@@ -89,5 +195,10 @@ pub const JsApi = struct {
|
||||
pub const code = bridge.accessor(DOMException.getCode, null, .{});
|
||||
pub const name = bridge.accessor(DOMException.getName, null, .{});
|
||||
pub const message = bridge.accessor(DOMException.getMessage, null, .{});
|
||||
pub const toString = bridge.function(DOMException.getMessage, .{});
|
||||
pub const toString = bridge.function(DOMException.toString, .{});
|
||||
};
|
||||
|
||||
const testing = @import("../../testing.zig");
|
||||
test "WebApi: DOMException" {
|
||||
try testing.htmlRunner("domexception.html", .{});
|
||||
}
|
||||
|
||||
@@ -21,14 +21,17 @@ const std = @import("std");
|
||||
const js = @import("../js/js.zig");
|
||||
const Page = @import("../Page.zig");
|
||||
const Node = @import("Node.zig");
|
||||
const Document = @import("Document.zig");
|
||||
const HTMLDocument = @import("HTMLDocument.zig");
|
||||
const DocumentType = @import("DocumentType.zig");
|
||||
|
||||
const DOMImplementation = @This();
|
||||
|
||||
pub fn createDocumentType(_: *const DOMImplementation, qualified_name: []const u8, public_id: ?[]const u8, system_id: ?[]const u8, page: *Page) !*DocumentType {
|
||||
const name = try page.dupeString(qualified_name);
|
||||
const pub_id = try page.dupeString(public_id orelse "");
|
||||
const sys_id = try page.dupeString(system_id orelse "");
|
||||
// Firefox converts null to the string "null", not empty string
|
||||
const pub_id = if (public_id) |p| try page.dupeString(p) else "null";
|
||||
const sys_id = if (system_id) |s| try page.dupeString(s) else "null";
|
||||
|
||||
const doctype = try page._factory.node(DocumentType{
|
||||
._proto = undefined,
|
||||
@@ -40,7 +43,60 @@ pub fn createDocumentType(_: *const DOMImplementation, qualified_name: []const u
|
||||
return doctype;
|
||||
}
|
||||
|
||||
pub fn hasFeature(_: *const DOMImplementation, _: []const u8, _: ?[]const u8) bool {
|
||||
pub fn createHTMLDocument(_: *const DOMImplementation, title: ?[]const u8, page: *Page) !*Document {
|
||||
const document = (try page._factory.document(Node.Document.HTMLDocument{ ._proto = undefined })).asDocument();
|
||||
document._ready_state = .complete;
|
||||
|
||||
{
|
||||
const doctype = try page._factory.node(DocumentType{
|
||||
._proto = undefined,
|
||||
._name = "html",
|
||||
._public_id = "",
|
||||
._system_id = "",
|
||||
});
|
||||
_ = try document.asNode().appendChild(doctype.asNode(), page);
|
||||
}
|
||||
|
||||
const html_node = try page.createElement(null, "html", null);
|
||||
_ = try document.asNode().appendChild(html_node, page);
|
||||
|
||||
const head_node = try page.createElement(null, "head", null);
|
||||
_ = try html_node.appendChild(head_node, page);
|
||||
|
||||
if (title) |t| {
|
||||
const title_node = try page.createElement(null, "title", null);
|
||||
_ = try head_node.appendChild(title_node, page);
|
||||
const text_node = try page.createTextNode(t);
|
||||
_ = try title_node.appendChild(text_node, page);
|
||||
}
|
||||
|
||||
const body_node = try page.createElement(null, "body", null);
|
||||
_ = try html_node.appendChild(body_node, page);
|
||||
|
||||
return document;
|
||||
}
|
||||
|
||||
pub fn createDocument(_: *const DOMImplementation, namespace: ?[]const u8, qualified_name: ?[]const u8, doctype: ?*DocumentType, page: *Page) !*Document {
|
||||
// Create XML Document
|
||||
const document = (try page._factory.document(Node.Document.XMLDocument{ ._proto = undefined })).asDocument();
|
||||
|
||||
// Append doctype if provided
|
||||
if (doctype) |dt| {
|
||||
_ = try document.asNode().appendChild(dt.asNode(), page);
|
||||
}
|
||||
|
||||
// Create and append root element if qualified_name provided
|
||||
if (qualified_name) |qname| {
|
||||
if (qname.len > 0) {
|
||||
const root = try page.createElement(namespace, qname, null);
|
||||
_ = try document.asNode().appendChild(root, page);
|
||||
}
|
||||
}
|
||||
|
||||
return document;
|
||||
}
|
||||
|
||||
pub fn hasFeature(_: *const DOMImplementation, _: ?[]const u8, _: ?[]const u8) bool {
|
||||
// Modern DOM spec says this should always return true
|
||||
// This method is deprecated and kept for compatibility only
|
||||
return true;
|
||||
@@ -61,6 +117,8 @@ pub const JsApi = struct {
|
||||
};
|
||||
|
||||
pub const createDocumentType = bridge.function(DOMImplementation.createDocumentType, .{ .dom_exception = true });
|
||||
pub const createDocument = bridge.function(DOMImplementation.createDocument, .{});
|
||||
pub const createHTMLDocument = bridge.function(DOMImplementation.createHTMLDocument, .{});
|
||||
pub const hasFeature = bridge.function(DOMImplementation.hasFeature, .{});
|
||||
|
||||
pub const toString = bridge.function(_toString, .{});
|
||||
|
||||
@@ -35,6 +35,7 @@ const DOMImplementation = @import("DOMImplementation.zig");
|
||||
const StyleSheetList = @import("css/StyleSheetList.zig");
|
||||
|
||||
pub const HTMLDocument = @import("HTMLDocument.zig");
|
||||
pub const XMLDocument = @import("XMLDocument.zig");
|
||||
|
||||
const Document = @This();
|
||||
|
||||
@@ -50,6 +51,7 @@ _style_sheets: ?*StyleSheetList = null,
|
||||
pub const Type = union(enum) {
|
||||
generic,
|
||||
html: *HTMLDocument,
|
||||
xml: *XMLDocument,
|
||||
};
|
||||
|
||||
pub fn is(self: *Document, comptime T: type) ?*T {
|
||||
@@ -59,6 +61,11 @@ pub fn is(self: *Document, comptime T: type) ?*T {
|
||||
return html;
|
||||
}
|
||||
},
|
||||
.xml => |xml| {
|
||||
if (T == XMLDocument) {
|
||||
return xml;
|
||||
}
|
||||
},
|
||||
.generic => {},
|
||||
}
|
||||
return null;
|
||||
@@ -83,6 +90,7 @@ pub fn getURL(_: *const Document, page: *const Page) [:0]const u8 {
|
||||
pub fn getContentType(self: *const Document) []const u8 {
|
||||
return switch (self._type) {
|
||||
.html => "text/html",
|
||||
.xml => "application/xml",
|
||||
.generic => "application/xml",
|
||||
};
|
||||
}
|
||||
@@ -217,6 +225,7 @@ pub fn className(self: *const Document) []const u8 {
|
||||
return switch (self._type) {
|
||||
.generic => "[object Document]",
|
||||
.html => "[object HTMLDocument]",
|
||||
.xml => "[object XMLDocument]",
|
||||
};
|
||||
}
|
||||
|
||||
@@ -239,10 +248,15 @@ pub fn createTextNode(_: *const Document, data: []const u8, page: *Page) !*Node
|
||||
pub fn createCDATASection(self: *const Document, data: []const u8, page: *Page) !*Node {
|
||||
switch (self._type) {
|
||||
.html => return error.NotSupported,
|
||||
.xml => return page.createCDATASection(data),
|
||||
.generic => return page.createCDATASection(data),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn createProcessingInstruction(_: *const Document, target: []const u8, data: []const u8, page: *Page) !*Node {
|
||||
return page.createProcessingInstruction(target, data);
|
||||
}
|
||||
|
||||
const Range = @import("Range.zig");
|
||||
pub fn createRange(_: *const Document, page: *Page) !*Range {
|
||||
return Range.init(page);
|
||||
@@ -432,6 +446,7 @@ pub const JsApi = struct {
|
||||
pub const createTextNode = bridge.function(Document.createTextNode, .{});
|
||||
pub const createAttribute = bridge.function(Document.createAttribute, .{ .dom_exception = true });
|
||||
pub const createCDATASection = bridge.function(Document.createCDATASection, .{ .dom_exception = true });
|
||||
pub const createProcessingInstruction = bridge.function(Document.createProcessingInstruction, .{ .dom_exception = true });
|
||||
pub const createRange = bridge.function(Document.createRange, .{});
|
||||
pub const createEvent = bridge.function(Document.createEvent, .{ .dom_exception = true });
|
||||
pub const createTreeWalker = bridge.function(Document.createTreeWalker, .{});
|
||||
|
||||
@@ -71,8 +71,3 @@ pub const JsApi = struct {
|
||||
return self.className();
|
||||
}
|
||||
};
|
||||
|
||||
const testing = @import("../../testing.zig");
|
||||
test "WebApi: DOMImplementation" {
|
||||
try testing.htmlRunner("domimplementation.html", .{});
|
||||
}
|
||||
|
||||
@@ -332,6 +332,8 @@ fn _getInnerText(self: *Element, writer: *std.Io.Writer, state: *innerTextState)
|
||||
// CDATA sections should not be used within HTML. They are
|
||||
// considered comments and are not displayed.
|
||||
.cdata_section => {},
|
||||
// Processing instructions are not displayed in innerText
|
||||
.processing_instruction => {},
|
||||
},
|
||||
.document => {},
|
||||
.document_type => {},
|
||||
|
||||
@@ -229,8 +229,8 @@ pub fn getTextContent(self: *Node, writer: *std.Io.Writer) error{WriteFailed}!vo
|
||||
.element => {
|
||||
var it = self.childrenIterator();
|
||||
while (it.next()) |child| {
|
||||
// ignore comments and TODO processing instructions.
|
||||
if (child.is(CData.Comment) != null) {
|
||||
// ignore comments and processing instructions.
|
||||
if (child.is(CData.Comment) != null or child.is(CData.ProcessingInstruction) != null) {
|
||||
continue;
|
||||
}
|
||||
try child.getTextContent(writer);
|
||||
@@ -270,6 +270,7 @@ pub fn getNodeName(self: *const Node, buf: []u8) []const u8 {
|
||||
.text => "#text",
|
||||
.cdata_section => "#cdata-section",
|
||||
.comment => "#comment",
|
||||
.processing_instruction => |pi| pi._target,
|
||||
},
|
||||
.document => "#document",
|
||||
.document_type => |dt| dt.getName(),
|
||||
@@ -285,6 +286,7 @@ pub fn getNodeType(self: *const Node) u8 {
|
||||
.cdata => |cd| switch (cd._type) {
|
||||
.text => 3,
|
||||
.cdata_section => 4,
|
||||
.processing_instruction => 7,
|
||||
.comment => 8,
|
||||
},
|
||||
.document => 9,
|
||||
@@ -603,6 +605,7 @@ pub fn cloneNode(self: *Node, deep_: ?bool, page: *Page) error{ OutOfMemory, Str
|
||||
.text => page.createTextNode(data),
|
||||
.cdata_section => page.createCDATASection(data),
|
||||
.comment => page.createComment(data),
|
||||
.processing_instruction => |pi| page.createProcessingInstruction(pi._target, data),
|
||||
};
|
||||
},
|
||||
.element => |el| return el.cloneElement(deep, page),
|
||||
|
||||
52
src/browser/webapi/XMLDocument.zig
Normal file
52
src/browser/webapi/XMLDocument.zig
Normal file
@@ -0,0 +1,52 @@
|
||||
// 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 js = @import("../js/js.zig");
|
||||
|
||||
const Document = @import("Document.zig");
|
||||
const Node = @import("Node.zig");
|
||||
|
||||
const XMLDocument = @This();
|
||||
|
||||
_proto: *Document,
|
||||
|
||||
pub fn asDocument(self: *XMLDocument) *Document {
|
||||
return self._proto;
|
||||
}
|
||||
|
||||
pub fn asNode(self: *XMLDocument) *Node {
|
||||
return self._proto.asNode();
|
||||
}
|
||||
|
||||
pub fn asEventTarget(self: *XMLDocument) *@import("EventTarget.zig") {
|
||||
return self._proto.asEventTarget();
|
||||
}
|
||||
|
||||
pub fn className(_: *const XMLDocument) []const u8 {
|
||||
return "[object XMLDocument]";
|
||||
}
|
||||
|
||||
pub const JsApi = struct {
|
||||
pub const bridge = js.Bridge(XMLDocument);
|
||||
|
||||
pub const Meta = struct {
|
||||
pub const name = "XMLDocument";
|
||||
pub const prototype_chain = bridge.prototypeChain();
|
||||
pub var class_id: bridge.ClassId = undefined;
|
||||
};
|
||||
};
|
||||
47
src/browser/webapi/cdata/ProcessingInstruction.zig
Normal file
47
src/browser/webapi/cdata/ProcessingInstruction.zig
Normal file
@@ -0,0 +1,47 @@
|
||||
// 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 js = @import("../../js/js.zig");
|
||||
|
||||
const CData = @import("../CData.zig");
|
||||
|
||||
const ProcessingInstruction = @This();
|
||||
|
||||
_proto: *CData,
|
||||
_target: []const u8,
|
||||
|
||||
pub fn getTarget(self: *const ProcessingInstruction) []const u8 {
|
||||
return self._target;
|
||||
}
|
||||
|
||||
pub const JsApi = struct {
|
||||
pub const bridge = js.Bridge(ProcessingInstruction);
|
||||
|
||||
pub const Meta = struct {
|
||||
pub const name = "ProcessingInstruction";
|
||||
pub const prototype_chain = bridge.prototypeChain();
|
||||
pub var class_id: bridge.ClassId = undefined;
|
||||
};
|
||||
|
||||
pub const target = bridge.accessor(ProcessingInstruction.getTarget, null, .{});
|
||||
};
|
||||
|
||||
const testing = @import("../../../testing.zig");
|
||||
test "WebApi: ProcessingInstruction" {
|
||||
try testing.htmlRunner("processing_instruction.html", .{});
|
||||
}
|
||||
Reference in New Issue
Block a user