Skip to content

Commit

Permalink
Upgrade parse5 to v3.0.2
Browse files Browse the repository at this point in the history
Fixes #1778.

This changes the public API in that the nodeLocation() functions now return differently-structured objects. parse5 has also removed support for serialization of obsolete doctypes.

#1316 is a more ambitious attempt to use parse5's new streaming interface to get correct script execution semantics, but is still not ready; this is just a minimal upgrade to the latest parse5 version.
  • Loading branch information
Zirro authored and domenic committed May 14, 2017
1 parent e1dba03 commit 29c4a7b
Show file tree
Hide file tree
Showing 8 changed files with 57 additions and 71 deletions.
6 changes: 3 additions & 3 deletions README.md
Expand Up @@ -223,9 +223,9 @@ const textNode = pEl.firstChild;
const imgEl = document.querySelector("img");

console.log(dom.nodeLocation(bodyEl)); // null; it's not in the source
console.log(dom.nodeLocation(pEl)); // { start: 0, end: 39, startTag: ..., endTag: ... }
console.log(dom.nodeLocation(textNode)); // { start: 3, end: 13 }
console.log(dom.nodeLocation(imgEl)); // { start: 13, end: 32 }
console.log(dom.nodeLocation(pEl)); // { startOffset: 0, endOffset: 39, startTag: ..., endTag: ... }
console.log(dom.nodeLocation(textNode)); // { startOffset: 3, endOffset: 13 }
console.log(dom.nodeLocation(imgEl)); // { startOffset: 13, endOffset: 32 }
```

Note that this feature only works if you have set the `includeNodeLocations` option; node locations are off by default for performance reasons.
Expand Down
15 changes: 5 additions & 10 deletions lib/jsdom/browser/documentAdapter.js
@@ -1,21 +1,12 @@
"use strict";

const idlUtils = require("../living/generated/utils");

// Tree traversing
exports.getFirstChild = function (node) {
return node.childNodes[0];
};

exports.getChildNodes = function (node) {
// parse5 treats template elements specially, assuming you return an array whose single item is the document fragment
const children = node._templateContents ? [node._templateContents] : [];
if (children.length === 0) {
for (let i = 0; i < node.childNodes.length; ++i) {
children.push(idlUtils.implForWrapper(node.childNodes[i]));
}
}
return children;
return node.childNodes;
};

exports.getParentNode = function (node) {
Expand All @@ -35,6 +26,10 @@ exports.getNamespaceURI = function (element) {
return element.namespaceURI || "http://www.w3.org/1999/xhtml";
};

exports.getTemplateContent = function (node) {
return node.content;
};

exports.getTextNodeContent = function (textNode) {
return textNode.nodeValue;
};
Expand Down
10 changes: 6 additions & 4 deletions lib/jsdom/browser/domtohtml.js
Expand Up @@ -4,15 +4,17 @@ const documentAdapter = require("./documentAdapter");
const NODE_TYPE = require("../living/node-type");
const idlUtils = require("../living/generated/utils");

const serializer = new parse5.TreeSerializer(documentAdapter);

exports.domToHtml = function (iterable) {
let ret = "";
for (const node of iterable) {
if (node.nodeType === NODE_TYPE.DOCUMENT_NODE) {
ret += serializer.serialize(node);
ret += parse5.serialize(node, {
treeAdapter: documentAdapter
});
} else {
ret += serializer.serialize({ childNodes: [idlUtils.wrapperForImpl(node)] });
ret += parse5.serialize({ childNodes: [idlUtils.wrapperForImpl(node)] }, {
treeAdapter: documentAdapter
});
}
}
return ret;
Expand Down
22 changes: 13 additions & 9 deletions lib/jsdom/browser/htmltodom.js
Expand Up @@ -22,8 +22,8 @@ class HtmlToDom {

if (parser.DefaultHandler) {
this.parserType = "htmlparser2";
} else if (parser.Parser && parser.TreeAdapters) {
this.parserType = "parse5v1";
} else if (parser.parse && parser.treeAdapters) {
this.parserType = "parse5";
} else if (parser.moduleName === "HTML5") {
this.parserType = "html5";
} else if (parser.parser) {
Expand Down Expand Up @@ -70,20 +70,24 @@ class HtmlToDom {
return element;
}

_parseWithparse5v1(html, fragment, element, options) {
_parseWithparse5(html, fragment, element, options = {}) {
if (this.parsingMode === "xml") {
throw new Error("Can't parse XML with parse5, please use htmlparser2 instead.");
}

const htmlparser2Adapter = this.parser.TreeAdapters.htmlparser2;
const htmlparser2Adapter = parse5.treeAdapters.htmlparser2;

let dom;
if (fragment) {
const instance = new this.parser.Parser(htmlparser2Adapter);
const parentElement = htmlparser2Adapter.createElement(element.tagName.toLowerCase(), element.namespaceURI, []);
dom = instance.parseFragment(html, parentElement);
const parentElement = htmlparser2Adapter.createElement(
element.tagName.toLowerCase(), element.namespaceURI, []
);

options.treeAdapter = htmlparser2Adapter;
dom = this.parser.parseFragment(parentElement, html, options);
} else {
const instance = new this.parser.Parser(htmlparser2Adapter, options);
dom = instance.parse(html);
options.treeAdapter = htmlparser2Adapter;
dom = this.parser.parse(html, options);
}

const parsed = dom.children;
Expand Down
6 changes: 3 additions & 3 deletions lib/old-api.md
Expand Up @@ -533,9 +533,9 @@ var textNode = pEl.firstChild;
var imgEl = document.querySelector("img");

console.log(jsdom.nodeLocation(bodyEl)); // null; it's not in the source
console.log(jsdom.nodeLocation(pEl)); // { start: 0, end: 39, startTag: ..., endTag: ... }
console.log(jsdom.nodeLocation(textNode)); // { start: 3, end: 13 }
console.log(jsdom.nodeLocation(imgEl)); // { start: 13, end: 32 }
console.log(jsdom.nodeLocation(pEl)); // { startOffset: 0, endOffset: 39, startTag: ..., endTag: ... }
console.log(jsdom.nodeLocation(textNode)); // { startOffset: 3, endOffset: 13 }
console.log(jsdom.nodeLocation(imgEl)); // { startOffset: 13, endOffset: 32 }
```

This returns the [parse5 location info](https://www.npmjs.com/package/parse5#options-locationinfo) for the node.
Expand Down
2 changes: 1 addition & 1 deletion package.json
Expand Up @@ -27,7 +27,7 @@
"escodegen": "^1.6.1",
"html-encoding-sniffer": "^1.0.1",
"nwmatcher": ">= 1.3.9 < 2.0.0",
"parse5": "^1.5.1",
"parse5": "^3.0.2",
"pn": "^1.0.0",
"request": "^2.79.0",
"request-promise-native": "^1.0.3",
Expand Down
32 changes: 26 additions & 6 deletions test/api/methods.js
Expand Up @@ -63,18 +63,25 @@ describe("API: JSDOM class's methods", () => {
const node = dom.window.document.querySelector("p");

assert.deepEqual(dom.nodeLocation(node), {
start: 0,
end: 12,
startTag: { start: 0, end: 3 },
endTag: { start: 8, end: 12 }
line: 1,
col: 1,
startOffset: 0,
endOffset: 12,
startTag: { line: 1, col: 1, startOffset: 0, endOffset: 3 },
endTag: { line: 1, col: 9, startOffset: 8, endOffset: 12 }
});
});

it("should give the correct location for a text node", () => {
const dom = new JSDOM(`<p>Hello</p>`, { includeNodeLocations: true });
const node = dom.window.document.querySelector("p").firstChild;

assert.deepEqual(dom.nodeLocation(node), { start: 3, end: 8 });
assert.deepEqual(dom.nodeLocation(node), {
line: 1,
col: 4,
startOffset: 3,
endOffset: 8
});
});

it("should give the correct location for a void element", () => {
Expand All @@ -83,7 +90,20 @@ describe("API: JSDOM class's methods", () => {
</p>`, { includeNodeLocations: true });
const node = dom.window.document.querySelector("img");

assert.deepEqual(dom.nodeLocation(node), { start: 17, end: 36 });
assert.deepEqual(dom.nodeLocation(node), {
attrs: {
src: {
line: 2,
col: 14,
startOffset: 22,
endOffset: 35
}
},
line: 2,
col: 9,
startOffset: 17,
endOffset: 36
});
});
});

Expand Down
35 changes: 0 additions & 35 deletions test/to-port-to-wpts/misc.js
Expand Up @@ -165,41 +165,6 @@ describe("browser/index", () => {
assert.ok(regexp.test(serializeDocument(document)), "HTML 5 doctype did not serialize correctly");
});

specify("serialize_html4_strict_doctype", () => {
const doc = jsdom.jsdom();
const dom = doc.implementation;

const doctype = dom.createDocumentType(
"html",
"-//W3C//DTD HTML 4.01//EN",
"http://www.w3.org/TR/html4/strict.dtd"
);
const document = dom.createDocument(null, null, doctype);
const regexp =
/^\s*<!DOCTYPE html PUBLIC "-\/\/W3C\/\/DTD HTML 4.01\/\/EN" "http:\/\/www.w3.org\/TR\/html4\/strict.dtd">/;
assert.ok(regexp.test(serializeDocument(document)), "HTML 4 strict doctype did not serialize correctly");
});

specify("serialize_system_doctype", () => {
const doc = jsdom.jsdom();
const dom = doc.implementation;

const doctype = dom.createDocumentType("foo", "", "foo.dtd");
const document = dom.createDocument(null, null, doctype);
const regexp = /^\s*<!DOCTYPE foo SYSTEM "foo.dtd">/;
assert.ok(regexp.test(serializeDocument(document)), "Doctype did not serialize correctly");
});

specify("serialize_doctype_containing_quotes", () => {
const doc = jsdom.jsdom();
const dom = doc.implementation;

const doctype = dom.createDocumentType("foo", "", "foo \"bar\".dtd");
const document = dom.createDocument(null, null, doctype);
const regexp = /^\s*<!DOCTYPE foo SYSTEM 'foo "bar"\.dtd'>/;
assert.ok(regexp.test(serializeDocument(document)), "Doctype did not serialize correctly");
});

specify("parse_doctype_containing_newline", () => {
const html = `<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"\n
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">\n<html></html>`;
Expand Down

0 comments on commit 29c4a7b

Please sign in to comment.