mirror of
https://github.com/lightpanda-io/browser.git
synced 2025-10-29 07:03:29 +00:00
Merge pull request #64 from Browsercore/domdoc-gelementsby
document.getElementsByTagName
This commit is contained in:
@@ -7,6 +7,7 @@ const Case = jsruntime.test_utils.Case;
|
|||||||
const checkCases = jsruntime.test_utils.checkCases;
|
const checkCases = jsruntime.test_utils.checkCases;
|
||||||
|
|
||||||
const Node = @import("node.zig").Node;
|
const Node = @import("node.zig").Node;
|
||||||
|
const HTMLCollection = @import("html_collection.zig").HTMLCollection;
|
||||||
|
|
||||||
const Element = @import("element.zig").Element;
|
const Element = @import("element.zig").Element;
|
||||||
const ElementUnion = @import("element.zig").Union;
|
const ElementUnion = @import("element.zig").Union;
|
||||||
@@ -34,6 +35,22 @@ pub const Document = struct {
|
|||||||
const e = parser.documentCreateElement(self, tag_name);
|
const e = parser.documentCreateElement(self, tag_name);
|
||||||
return Element.toInterface(e);
|
return Element.toInterface(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We can't simply use libdom dom_document_get_elements_by_tag_name here.
|
||||||
|
// Indeed, netsurf implemented a previous dom spec when
|
||||||
|
// getElementsByTagName returned a NodeList.
|
||||||
|
// But since
|
||||||
|
// https://github.com/whatwg/dom/commit/190700b7c12ecfd3b5ebdb359ab1d6ea9cbf7749
|
||||||
|
// the spec changed to return an HTMLCollection instead.
|
||||||
|
// That's why we reimplemented getElementsByTagName by using an
|
||||||
|
// HTMLCollection in zig here.
|
||||||
|
pub fn _getElementsByTagName(self: *parser.Document, tag_name: []const u8) HTMLCollection {
|
||||||
|
const root = parser.documentGetDocumentElement(self);
|
||||||
|
return HTMLCollection{
|
||||||
|
.root = parser.elementToNode(root),
|
||||||
|
.match = tag_name,
|
||||||
|
};
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Tests
|
// Tests
|
||||||
@@ -58,6 +75,19 @@ pub fn testExecFn(
|
|||||||
};
|
};
|
||||||
try checkCases(js_env, &getElementById);
|
try checkCases(js_env, &getElementById);
|
||||||
|
|
||||||
|
var getElementsByTagName = [_]Case{
|
||||||
|
.{ .src = "let getElementsByTagName = document.getElementsByTagName('p')", .ex = "undefined" },
|
||||||
|
.{ .src = "getElementsByTagName.length", .ex = "2" },
|
||||||
|
.{ .src = "getElementsByTagName.item(0).localName", .ex = "p" },
|
||||||
|
.{ .src = "getElementsByTagName.item(1).localName", .ex = "p" },
|
||||||
|
.{ .src = "let getElementsByTagNameAll = document.getElementsByTagName('*')", .ex = "undefined" },
|
||||||
|
.{ .src = "getElementsByTagNameAll.length", .ex = "8" },
|
||||||
|
.{ .src = "getElementsByTagNameAll.item(0).localName", .ex = "html" },
|
||||||
|
.{ .src = "getElementsByTagNameAll.item(7).localName", .ex = "p" },
|
||||||
|
.{ .src = "getElementsByTagNameAll.namedItem('para-empty-child').localName", .ex = "span" },
|
||||||
|
};
|
||||||
|
try checkCases(js_env, &getElementsByTagName);
|
||||||
|
|
||||||
const tags = comptime parser.Tag.all();
|
const tags = comptime parser.Tag.all();
|
||||||
comptime var createElements: [(tags.len) * 2]Case = undefined;
|
comptime var createElements: [(tags.len) * 2]Case = undefined;
|
||||||
inline for (tags, 0..) |tag, i| {
|
inline for (tags, 0..) |tag, i| {
|
||||||
|
|||||||
210
src/dom/html_collection.zig
Normal file
210
src/dom/html_collection.zig
Normal file
@@ -0,0 +1,210 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
const parser = @import("../netsurf.zig");
|
||||||
|
|
||||||
|
const jsruntime = @import("jsruntime");
|
||||||
|
const Case = jsruntime.test_utils.Case;
|
||||||
|
const checkCases = jsruntime.test_utils.checkCases;
|
||||||
|
|
||||||
|
const utils = @import("utils.z");
|
||||||
|
const Element = @import("element.zig").Element;
|
||||||
|
const Union = @import("element.zig").Union;
|
||||||
|
|
||||||
|
// WEB IDL https://dom.spec.whatwg.org/#htmlcollection
|
||||||
|
// HTMLCollection is re implemented in zig here because libdom
|
||||||
|
// dom_html_collection expects a comparison function callback as arguement.
|
||||||
|
// But we wanted a dynamically comparison here, according to the match tagname.
|
||||||
|
pub const HTMLCollection = struct {
|
||||||
|
pub const mem_guarantied = true;
|
||||||
|
|
||||||
|
root: *parser.Node,
|
||||||
|
// match is used to select node against their name.
|
||||||
|
// match comparison is case insensitive.
|
||||||
|
match: []const u8,
|
||||||
|
|
||||||
|
// save a state for the collection to improve the _item speed.
|
||||||
|
cur_idx: ?u32 = undefined,
|
||||||
|
cur_node: ?*parser.Node = undefined,
|
||||||
|
|
||||||
|
// get_next iterates over the DOM tree to return the next following node or
|
||||||
|
// null at the end.
|
||||||
|
//
|
||||||
|
// This implementation is a zig version of Netsurf code.
|
||||||
|
// http://source.netsurf-browser.org/libdom.git/tree/src/html/html_collection.c#n177
|
||||||
|
//
|
||||||
|
// The iteration is a depth first as required by the specification.
|
||||||
|
// https://dom.spec.whatwg.org/#htmlcollection
|
||||||
|
// https://dom.spec.whatwg.org/#concept-tree-order
|
||||||
|
fn get_next(root: *parser.Node, cur: *parser.Node) ?*parser.Node {
|
||||||
|
// TODO deinit next
|
||||||
|
if (parser.nodeFirstChild(cur)) |next| {
|
||||||
|
return next;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO deinit next
|
||||||
|
if (parser.nodeNextSibling(cur)) |next| {
|
||||||
|
return next;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO deinit parent
|
||||||
|
// Back to the parent of cur.
|
||||||
|
// If cur has no parent, then the iteration is over.
|
||||||
|
var parent = parser.nodeParentNode(cur) orelse return null;
|
||||||
|
|
||||||
|
// TODO deinit lastchild
|
||||||
|
var lastchild = parser.nodeLastChild(parent);
|
||||||
|
var prev = cur;
|
||||||
|
while (prev != root and prev == lastchild) {
|
||||||
|
prev = parent;
|
||||||
|
|
||||||
|
// TODO deinit parent
|
||||||
|
// Back to the prev's parent.
|
||||||
|
// If prev has no parent, then the loop must stop.
|
||||||
|
parent = parser.nodeParentNode(prev) orelse break;
|
||||||
|
|
||||||
|
// TODO deinit lastchild
|
||||||
|
lastchild = parser.nodeLastChild(parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prev == root) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return parser.nodeNextSibling(prev);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// get_length computes the collection's length dynamically according to
|
||||||
|
/// the current root structure.
|
||||||
|
// TODO: nodes retrieved must be de-referenced.
|
||||||
|
pub fn get_length(self: *HTMLCollection, allocator: std.mem.Allocator) !u32 {
|
||||||
|
var len: u32 = 0;
|
||||||
|
var node: *parser.Node = self.root;
|
||||||
|
var ntype: parser.NodeType = undefined;
|
||||||
|
|
||||||
|
const imatch = try std.ascii.allocUpperString(allocator, self.match);
|
||||||
|
defer allocator.free(imatch);
|
||||||
|
|
||||||
|
const is_wildcard = std.mem.eql(u8, self.match, "*");
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
ntype = parser.nodeType(node);
|
||||||
|
if (ntype == .element) {
|
||||||
|
if (is_wildcard or std.mem.eql(u8, imatch, parser.nodeName(node))) {
|
||||||
|
len += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
node = get_next(self.root, node) orelse break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn _item(self: *HTMLCollection, allocator: std.mem.Allocator, index: u32) !?Union {
|
||||||
|
var i: u32 = 0;
|
||||||
|
var node: *parser.Node = self.root;
|
||||||
|
var ntype: parser.NodeType = undefined;
|
||||||
|
|
||||||
|
const is_wildcard = std.mem.eql(u8, self.match, "*");
|
||||||
|
|
||||||
|
// Use the current state to improve speed if possible.
|
||||||
|
if (self.cur_idx != null and index >= self.cur_idx.?) {
|
||||||
|
i = self.cur_idx.?;
|
||||||
|
node = self.cur_node.?;
|
||||||
|
}
|
||||||
|
|
||||||
|
const imatch = try std.ascii.allocUpperString(allocator, self.match);
|
||||||
|
defer allocator.free(imatch);
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
ntype = parser.nodeType(node);
|
||||||
|
if (ntype == .element) {
|
||||||
|
if (is_wildcard or std.mem.eql(u8, imatch, parser.nodeName(node))) {
|
||||||
|
// check if we found the searched element.
|
||||||
|
if (i == index) {
|
||||||
|
// save the current state
|
||||||
|
self.cur_node = node;
|
||||||
|
self.cur_idx = i;
|
||||||
|
|
||||||
|
const e = @as(*parser.Element, @ptrCast(node));
|
||||||
|
return Element.toInterface(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
node = get_next(self.root, node) orelse break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn _namedItem(self: *HTMLCollection, allocator: std.mem.Allocator, name: []const u8) !?Union {
|
||||||
|
if (name.len == 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var node: *parser.Node = self.root;
|
||||||
|
var ntype: parser.NodeType = undefined;
|
||||||
|
|
||||||
|
const is_wildcard = std.mem.eql(u8, self.match, "*");
|
||||||
|
|
||||||
|
const imatch = try std.ascii.allocUpperString(allocator, self.match);
|
||||||
|
defer allocator.free(imatch);
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
ntype = parser.nodeType(node);
|
||||||
|
if (ntype == .element) {
|
||||||
|
if (is_wildcard or std.mem.eql(u8, imatch, parser.nodeName(node))) {
|
||||||
|
const elem = @as(*parser.Element, @ptrCast(node));
|
||||||
|
|
||||||
|
var attr = parser.elementGetAttribute(elem, "id");
|
||||||
|
// check if the node id corresponds to the name argument.
|
||||||
|
if (attr != null and std.mem.eql(u8, name, attr.?)) {
|
||||||
|
return Element.toInterface(elem);
|
||||||
|
}
|
||||||
|
|
||||||
|
attr = parser.elementGetAttribute(elem, "name");
|
||||||
|
// check if the node id corresponds to the name argument.
|
||||||
|
if (attr != null and std.mem.eql(u8, name, attr.?)) {
|
||||||
|
return Element.toInterface(elem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
node = get_next(self.root, node) orelse break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Tests
|
||||||
|
// -----
|
||||||
|
|
||||||
|
pub fn testExecFn(
|
||||||
|
_: std.mem.Allocator,
|
||||||
|
js_env: *jsruntime.Env,
|
||||||
|
comptime _: []jsruntime.API,
|
||||||
|
) !void {
|
||||||
|
var getElementsByTagName = [_]Case{
|
||||||
|
.{ .src = "let getElementsByTagName = document.getElementsByTagName('p')", .ex = "undefined" },
|
||||||
|
.{ .src = "getElementsByTagName.length", .ex = "2" },
|
||||||
|
.{ .src = "let getElementsByTagNameCI = document.getElementsByTagName('P')", .ex = "undefined" },
|
||||||
|
.{ .src = "getElementsByTagNameCI.length", .ex = "2" },
|
||||||
|
.{ .src = "getElementsByTagName.item(0).localName", .ex = "p" },
|
||||||
|
.{ .src = "getElementsByTagName.item(1).localName", .ex = "p" },
|
||||||
|
.{ .src = "let getElementsByTagNameAll = document.getElementsByTagName('*')", .ex = "undefined" },
|
||||||
|
.{ .src = "getElementsByTagNameAll.length", .ex = "8" },
|
||||||
|
.{ .src = "getElementsByTagNameAll.item(0).localName", .ex = "html" },
|
||||||
|
.{ .src = "getElementsByTagNameAll.item(0).localName", .ex = "html" },
|
||||||
|
.{ .src = "getElementsByTagNameAll.item(1).localName", .ex = "head" },
|
||||||
|
.{ .src = "getElementsByTagNameAll.item(0).localName", .ex = "html" },
|
||||||
|
.{ .src = "getElementsByTagNameAll.item(2).localName", .ex = "body" },
|
||||||
|
.{ .src = "getElementsByTagNameAll.item(3).localName", .ex = "div" },
|
||||||
|
.{ .src = "getElementsByTagNameAll.item(7).localName", .ex = "p" },
|
||||||
|
.{ .src = "getElementsByTagNameAll.namedItem('para-empty-child').localName", .ex = "span" },
|
||||||
|
};
|
||||||
|
try checkCases(js_env, &getElementsByTagName);
|
||||||
|
}
|
||||||
@@ -14,6 +14,7 @@ const EventTarget = @import("event_target.zig").EventTarget;
|
|||||||
const CData = @import("character_data.zig");
|
const CData = @import("character_data.zig");
|
||||||
const Element = @import("element.zig").Element;
|
const Element = @import("element.zig").Element;
|
||||||
const Document = @import("document.zig").Document;
|
const Document = @import("document.zig").Document;
|
||||||
|
const HTMLCollection = @import("html_collection.zig").HTMLCollection;
|
||||||
|
|
||||||
// HTML
|
// HTML
|
||||||
const HTML = @import("../html/html.zig");
|
const HTML = @import("../html/html.zig");
|
||||||
@@ -25,6 +26,7 @@ pub const Interfaces = generate.Tuple(.{
|
|||||||
CData.Interfaces,
|
CData.Interfaces,
|
||||||
Element,
|
Element,
|
||||||
Document,
|
Document,
|
||||||
|
HTMLCollection,
|
||||||
|
|
||||||
HTML.Interfaces,
|
HTML.Interfaces,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -205,6 +205,11 @@ fn runWPT(arena: *std.heap.ArenaAllocator, comptime apis: []jsruntime.API, f: []
|
|||||||
\\ return true;
|
\\ return true;
|
||||||
\\};
|
\\};
|
||||||
\\window.removeEventListener = function () {};
|
\\window.removeEventListener = function () {};
|
||||||
|
\\
|
||||||
|
\\console = [];
|
||||||
|
\\console.log = function () {
|
||||||
|
\\ console.push(...arguments);
|
||||||
|
\\};
|
||||||
;
|
;
|
||||||
res = try evalJS(js_env, alloc, init, "init");
|
res = try evalJS(js_env, alloc, init, "init");
|
||||||
if (!res.success) {
|
if (!res.success) {
|
||||||
@@ -247,6 +252,12 @@ fn runWPT(arena: *std.heap.ArenaAllocator, comptime apis: []jsruntime.API, f: []
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// display console logs
|
||||||
|
res = try evalJS(js_env, alloc, "console.join(', ');", "console");
|
||||||
|
if (res.result.len > 0) {
|
||||||
|
std.debug.print("-- CONSOLE LOG\n{s}\n--\n", .{res.result});
|
||||||
|
}
|
||||||
|
|
||||||
// Check the final test status.
|
// Check the final test status.
|
||||||
res = try evalJS(js_env, alloc, "report.status;", "teststatus");
|
res = try evalJS(js_env, alloc, "report.status;", "teststatus");
|
||||||
if (!res.success) {
|
if (!res.success) {
|
||||||
|
|||||||
@@ -597,6 +597,9 @@ pub fn textSplitText(text: *Text, offset: u32) *Text {
|
|||||||
// Comment
|
// Comment
|
||||||
pub const Comment = c.dom_comment;
|
pub const Comment = c.dom_comment;
|
||||||
|
|
||||||
|
// Attribute
|
||||||
|
pub const Attribute = c.dom_attr;
|
||||||
|
|
||||||
// Element
|
// Element
|
||||||
pub const Element = c.dom_element;
|
pub const Element = c.dom_element;
|
||||||
|
|
||||||
@@ -618,6 +621,11 @@ pub fn elementGetAttribute(elem: *Element, name: []const u8) ?[]const u8 {
|
|||||||
return stringToData(s.?);
|
return stringToData(s.?);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// elementToNode is an helper to convert an element to a node.
|
||||||
|
pub inline fn elementToNode(e: *Element) *Node {
|
||||||
|
return @as(*Node, @ptrCast(e));
|
||||||
|
}
|
||||||
|
|
||||||
// ElementHTML
|
// ElementHTML
|
||||||
pub const ElementHTML = c.dom_html_element;
|
pub const ElementHTML = c.dom_html_element;
|
||||||
|
|
||||||
@@ -731,6 +739,13 @@ pub inline fn documentGetElementsByTagName(doc: *Document, tagname: []const u8)
|
|||||||
return nlist.?;
|
return nlist.?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// documentGetDocumentElement returns the root document element.
|
||||||
|
pub inline fn documentGetDocumentElement(doc: *Document) *Element {
|
||||||
|
var elem: ?*Element = undefined;
|
||||||
|
_ = documentVtable(doc).dom_document_get_document_element.?(doc, &elem);
|
||||||
|
return elem.?;
|
||||||
|
}
|
||||||
|
|
||||||
pub inline fn documentCreateElement(doc: *Document, tag_name: []const u8) *Element {
|
pub inline fn documentCreateElement(doc: *Document, tag_name: []const u8) *Element {
|
||||||
var elem: ?*Element = undefined;
|
var elem: ?*Element = undefined;
|
||||||
_ = documentVtable(doc).dom_document_create_element.?(doc, stringFromData(tag_name), &elem);
|
_ = documentVtable(doc).dom_document_create_element.?(doc, stringFromData(tag_name), &elem);
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ const HTMLDocumentTestExecFn = @import("html/document.zig").testExecFn;
|
|||||||
const nodeTestExecFn = @import("dom/node.zig").testExecFn;
|
const nodeTestExecFn = @import("dom/node.zig").testExecFn;
|
||||||
const characterDataTestExecFn = @import("dom/character_data.zig").testExecFn;
|
const characterDataTestExecFn = @import("dom/character_data.zig").testExecFn;
|
||||||
const textTestExecFn = @import("dom/text.zig").testExecFn;
|
const textTestExecFn = @import("dom/text.zig").testExecFn;
|
||||||
|
const HTMLCollectionTestExecFn = @import("dom/html_collection.zig").testExecFn;
|
||||||
|
|
||||||
var doc: *parser.DocumentHTML = undefined;
|
var doc: *parser.DocumentHTML = undefined;
|
||||||
|
|
||||||
@@ -51,6 +52,7 @@ fn testsAllExecFn(
|
|||||||
nodeTestExecFn,
|
nodeTestExecFn,
|
||||||
characterDataTestExecFn,
|
characterDataTestExecFn,
|
||||||
textTestExecFn,
|
textTestExecFn,
|
||||||
|
HTMLCollectionTestExecFn,
|
||||||
};
|
};
|
||||||
|
|
||||||
inline for (testFns) |testFn| {
|
inline for (testFns) |testFn| {
|
||||||
|
|||||||
208
tests/wpt/dom/nodes/Document-Element-getElementsByTagName.js
Normal file
208
tests/wpt/dom/nodes/Document-Element-getElementsByTagName.js
Normal file
@@ -0,0 +1,208 @@
|
|||||||
|
function test_getElementsByTagName(context, element) {
|
||||||
|
// TODO: getElementsByTagName("*")
|
||||||
|
test(function() {
|
||||||
|
assert_false(context.getElementsByTagName("html") instanceof NodeList,
|
||||||
|
"Should not return a NodeList")
|
||||||
|
assert_true(context.getElementsByTagName("html") instanceof HTMLCollection,
|
||||||
|
"Should return an HTMLCollection")
|
||||||
|
}, "Interfaces")
|
||||||
|
|
||||||
|
test(function() {
|
||||||
|
var firstCollection = context.getElementsByTagName("html"),
|
||||||
|
secondCollection = context.getElementsByTagName("html")
|
||||||
|
assert_true(firstCollection !== secondCollection ||
|
||||||
|
firstCollection === secondCollection)
|
||||||
|
}, "Caching is allowed")
|
||||||
|
|
||||||
|
test(function() {
|
||||||
|
var l = context.getElementsByTagName("nosuchtag")
|
||||||
|
l[5] = "foopy"
|
||||||
|
assert_equals(l[5], undefined)
|
||||||
|
assert_equals(l.item(5), null)
|
||||||
|
}, "Shouldn't be able to set unsigned properties on a HTMLCollection (non-strict mode)")
|
||||||
|
|
||||||
|
test(function() {
|
||||||
|
var l = context.getElementsByTagName("nosuchtag")
|
||||||
|
assert_throws_js(TypeError, function() {
|
||||||
|
"use strict";
|
||||||
|
l[5] = "foopy"
|
||||||
|
})
|
||||||
|
assert_equals(l[5], undefined)
|
||||||
|
assert_equals(l.item(5), null)
|
||||||
|
}, "Shouldn't be able to set unsigned properties on a HTMLCollection (strict mode)")
|
||||||
|
|
||||||
|
test(function() {
|
||||||
|
var l = context.getElementsByTagName("nosuchtag")
|
||||||
|
var fn = l.item;
|
||||||
|
assert_equals(fn, HTMLCollection.prototype.item);
|
||||||
|
l.item = "pass"
|
||||||
|
assert_equals(l.item, "pass")
|
||||||
|
assert_equals(HTMLCollection.prototype.item, fn);
|
||||||
|
}, "Should be able to set expando shadowing a proto prop (item)")
|
||||||
|
|
||||||
|
test(function() {
|
||||||
|
var l = context.getElementsByTagName("nosuchtag")
|
||||||
|
var fn = l.namedItem;
|
||||||
|
assert_equals(fn, HTMLCollection.prototype.namedItem);
|
||||||
|
l.namedItem = "pass"
|
||||||
|
assert_equals(l.namedItem, "pass")
|
||||||
|
assert_equals(HTMLCollection.prototype.namedItem, fn);
|
||||||
|
}, "Should be able to set expando shadowing a proto prop (namedItem)")
|
||||||
|
|
||||||
|
test(function() {
|
||||||
|
var t1 = element.appendChild(document.createElement("pre"));
|
||||||
|
t1.id = "x";
|
||||||
|
var t2 = element.appendChild(document.createElement("pre"));
|
||||||
|
t2.setAttribute("name", "y");
|
||||||
|
var t3 = element.appendChild(document.createElementNS("", "pre"));
|
||||||
|
t3.setAttribute("id", "z");
|
||||||
|
var t4 = element.appendChild(document.createElementNS("", "pre"));
|
||||||
|
t4.setAttribute("name", "w");
|
||||||
|
this.add_cleanup(function() {
|
||||||
|
element.removeChild(t1)
|
||||||
|
element.removeChild(t2)
|
||||||
|
element.removeChild(t3)
|
||||||
|
element.removeChild(t4)
|
||||||
|
});
|
||||||
|
|
||||||
|
var list = context.getElementsByTagName('pre');
|
||||||
|
var pre = list[0];
|
||||||
|
assert_equals(pre.id, "x");
|
||||||
|
|
||||||
|
var exposedNames = { 'x': 0, 'y': 1, 'z': 2 };
|
||||||
|
for (var exposedName in exposedNames) {
|
||||||
|
assert_equals(list[exposedName], list[exposedNames[exposedName]]);
|
||||||
|
assert_equals(list[exposedName], list.namedItem(exposedName));
|
||||||
|
assert_true(exposedName in list, "'" + exposedName + "' in list");
|
||||||
|
assert_true(list.hasOwnProperty(exposedName),
|
||||||
|
"list.hasOwnProperty('" + exposedName + "')");
|
||||||
|
}
|
||||||
|
|
||||||
|
var unexposedNames = ["w"];
|
||||||
|
for (var unexposedName of unexposedNames) {
|
||||||
|
assert_false(unexposedName in list);
|
||||||
|
assert_false(list.hasOwnProperty(unexposedName));
|
||||||
|
assert_equals(list[unexposedName], undefined);
|
||||||
|
assert_equals(list.namedItem(unexposedName), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_array_equals(Object.getOwnPropertyNames(list).sort(),
|
||||||
|
["0", "1", "2", "3", "x", "y", "z"]);
|
||||||
|
|
||||||
|
var desc = Object.getOwnPropertyDescriptor(list, '0');
|
||||||
|
assert_equals(typeof desc, "object", "descriptor should be an object");
|
||||||
|
assert_true(desc.enumerable, "desc.enumerable");
|
||||||
|
assert_true(desc.configurable, "desc.configurable");
|
||||||
|
|
||||||
|
desc = Object.getOwnPropertyDescriptor(list, 'x');
|
||||||
|
assert_equals(typeof desc, "object", "descriptor should be an object");
|
||||||
|
assert_false(desc.enumerable, "desc.enumerable");
|
||||||
|
assert_true(desc.configurable, "desc.configurable");
|
||||||
|
}, "hasOwnProperty, getOwnPropertyDescriptor, getOwnPropertyNames")
|
||||||
|
|
||||||
|
test(function() {
|
||||||
|
assert_equals(document.createElementNS("http://www.w3.org/1999/xhtml", "i").localName, "i") // Sanity
|
||||||
|
var t = element.appendChild(document.createElementNS("http://www.w3.org/1999/xhtml", "I"))
|
||||||
|
this.add_cleanup(function() {element.removeChild(t)})
|
||||||
|
assert_equals(t.localName, "I")
|
||||||
|
assert_equals(t.tagName, "I")
|
||||||
|
assert_equals(context.getElementsByTagName("I").length, 0)
|
||||||
|
assert_equals(context.getElementsByTagName("i").length, 0)
|
||||||
|
}, "HTML element with uppercase tagName never matches in HTML Documents")
|
||||||
|
|
||||||
|
test(function() {
|
||||||
|
var t = element.appendChild(document.createElementNS("test", "st"))
|
||||||
|
this.add_cleanup(function() {element.removeChild(t)})
|
||||||
|
assert_array_equals(context.getElementsByTagName("st"), [t])
|
||||||
|
assert_array_equals(context.getElementsByTagName("ST"), [])
|
||||||
|
}, "Element in non-HTML namespace, no prefix, lowercase name")
|
||||||
|
|
||||||
|
test(function() {
|
||||||
|
var t = element.appendChild(document.createElementNS("test", "ST"))
|
||||||
|
this.add_cleanup(function() {element.removeChild(t)})
|
||||||
|
assert_array_equals(context.getElementsByTagName("ST"), [t])
|
||||||
|
assert_array_equals(context.getElementsByTagName("st"), [])
|
||||||
|
}, "Element in non-HTML namespace, no prefix, uppercase name")
|
||||||
|
|
||||||
|
test(function() {
|
||||||
|
var t = element.appendChild(document.createElementNS("test", "te:st"))
|
||||||
|
this.add_cleanup(function() {element.removeChild(t)})
|
||||||
|
assert_array_equals(context.getElementsByTagName("st"), [])
|
||||||
|
assert_array_equals(context.getElementsByTagName("ST"), [])
|
||||||
|
assert_array_equals(context.getElementsByTagName("te:st"), [t])
|
||||||
|
assert_array_equals(context.getElementsByTagName("te:ST"), [])
|
||||||
|
}, "Element in non-HTML namespace, prefix, lowercase name")
|
||||||
|
|
||||||
|
test(function() {
|
||||||
|
var t = element.appendChild(document.createElementNS("test", "te:ST"))
|
||||||
|
this.add_cleanup(function() {element.removeChild(t)})
|
||||||
|
assert_array_equals(context.getElementsByTagName("st"), [])
|
||||||
|
assert_array_equals(context.getElementsByTagName("ST"), [])
|
||||||
|
assert_array_equals(context.getElementsByTagName("te:st"), [])
|
||||||
|
assert_array_equals(context.getElementsByTagName("te:ST"), [t])
|
||||||
|
}, "Element in non-HTML namespace, prefix, uppercase name")
|
||||||
|
|
||||||
|
test(function() {
|
||||||
|
var t = element.appendChild(document.createElement("aÇ"))
|
||||||
|
this.add_cleanup(function() {element.removeChild(t)})
|
||||||
|
assert_equals(t.localName, "aÇ")
|
||||||
|
assert_array_equals(context.getElementsByTagName("AÇ"), [t], "All uppercase input")
|
||||||
|
assert_array_equals(context.getElementsByTagName("aÇ"), [t], "Ascii lowercase input")
|
||||||
|
assert_array_equals(context.getElementsByTagName("aç"), [], "All lowercase input")
|
||||||
|
}, "Element in HTML namespace, no prefix, non-ascii characters in name")
|
||||||
|
|
||||||
|
test(function() {
|
||||||
|
var t = element.appendChild(document.createElementNS("test", "AÇ"))
|
||||||
|
this.add_cleanup(function() {element.removeChild(t)})
|
||||||
|
assert_array_equals(context.getElementsByTagName("AÇ"), [t])
|
||||||
|
assert_array_equals(context.getElementsByTagName("aÇ"), [])
|
||||||
|
assert_array_equals(context.getElementsByTagName("aç"), [])
|
||||||
|
}, "Element in non-HTML namespace, non-ascii characters in name")
|
||||||
|
|
||||||
|
test(function() {
|
||||||
|
var t = element.appendChild(document.createElementNS("http://www.w3.org/1999/xhtml", "test:aÇ"))
|
||||||
|
this.add_cleanup(function() {element.removeChild(t)})
|
||||||
|
assert_array_equals(context.getElementsByTagName("TEST:AÇ"), [t], "All uppercase input")
|
||||||
|
assert_array_equals(context.getElementsByTagName("test:aÇ"), [t], "Ascii lowercase input")
|
||||||
|
assert_array_equals(context.getElementsByTagName("test:aç"), [], "All lowercase input")
|
||||||
|
}, "Element in HTML namespace, prefix, non-ascii characters in name")
|
||||||
|
|
||||||
|
test(function() {
|
||||||
|
var t = element.appendChild(document.createElementNS("test", "TEST:AÇ"))
|
||||||
|
this.add_cleanup(function() {element.removeChild(t)})
|
||||||
|
assert_array_equals(context.getElementsByTagName("TEST:AÇ"), [t], "All uppercase input")
|
||||||
|
assert_array_equals(context.getElementsByTagName("test:aÇ"), [], "Ascii lowercase input")
|
||||||
|
assert_array_equals(context.getElementsByTagName("test:aç"), [], "All lowercase input")
|
||||||
|
}, "Element in non-HTML namespace, prefix, non-ascii characters in name")
|
||||||
|
|
||||||
|
test(function() {
|
||||||
|
var actual = context.getElementsByTagName("*");
|
||||||
|
var expected = [];
|
||||||
|
var get_elements = function(node) {
|
||||||
|
for (var i = 0; i < node.childNodes.length; i++) {
|
||||||
|
var child = node.childNodes[i];
|
||||||
|
if (child.nodeType === child.ELEMENT_NODE) {
|
||||||
|
expected.push(child);
|
||||||
|
get_elements(child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
get_elements(context);
|
||||||
|
assert_array_equals(actual, expected);
|
||||||
|
}, "getElementsByTagName('*')")
|
||||||
|
|
||||||
|
test(function() {
|
||||||
|
var t1 = element.appendChild(document.createElement("abc"));
|
||||||
|
this.add_cleanup(function() {element.removeChild(t1)});
|
||||||
|
|
||||||
|
var l = context.getElementsByTagName("abc");
|
||||||
|
assert_true(l instanceof HTMLCollection);
|
||||||
|
assert_equals(l.length, 1);
|
||||||
|
|
||||||
|
var t2 = element.appendChild(document.createElement("abc"));
|
||||||
|
assert_equals(l.length, 2);
|
||||||
|
|
||||||
|
element.removeChild(t2);
|
||||||
|
assert_equals(l.length, 1);
|
||||||
|
}, "getElementsByTagName() should be a live collection");
|
||||||
|
}
|
||||||
11
tests/wpt/dom/nodes/Document-getElementsByTagName.html
Normal file
11
tests/wpt/dom/nodes/Document-getElementsByTagName.html
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<meta charset=utf-8>
|
||||||
|
<title>Document.getElementsByTagName</title>
|
||||||
|
<link rel=help href="https://dom.spec.whatwg.org/#dom-document-getelementsbytagname">
|
||||||
|
<script src="/resources/testharness.js"></script>
|
||||||
|
<script src="/resources/testharnessreport.js"></script>
|
||||||
|
<script src="Document-Element-getElementsByTagName.js"></script>
|
||||||
|
<div id="log"></div>
|
||||||
|
<script>
|
||||||
|
test_getElementsByTagName(document, document.body);
|
||||||
|
</script>
|
||||||
Reference in New Issue
Block a user