Skip to content

Commit

Permalink
Implement an initial version of DOMParser
Browse files Browse the repository at this point in the history
It's missing:

- <parsererror> elements when parsing XML strings fails
- copying the active document's URL

Also adds the empty-per-spec XMLDocument interface.

Closes jsdom#1341. Part of jsdom#1368.
  • Loading branch information
domenic authored and nhunzaker committed Dec 22, 2016
1 parent d36c84d commit 5085d2e
Show file tree
Hide file tree
Showing 10 changed files with 188 additions and 0 deletions.
1 change: 1 addition & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ test/level2
test/level3
test/sizzle
test/web-platform-tests/tests
test/web-platform-tests/to-upstream/domparsing/DOMParser-dont-upstream.html
test/window/files
test/window/frame.js
test/window/script.js
Expand Down
48 changes: 48 additions & 0 deletions lib/jsdom/living/domparsing/DOMParser-impl.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
"use strict";
const Document = require("../generated/Document");
const core = require("..");
const applyDocumentFeatures = require("../../browser/documentfeatures").applyDocumentFeatures;

exports.implementation = class DOMParserImpl {
parseFromString(string, contentType) {
switch (String(contentType)) {
case "text/html": {
return createScriptingDisabledDocument("html", contentType, string);
}

case "text/xml":
case "application/xml":
case "application/xhtml+xml":
case "image/svg+xml": {
// TODO: use a strict XML parser (sax's strict mode might work?) and create parsererror elements
return createScriptingDisabledDocument("xml", contentType, string);
}

default:
throw new TypeError("Invalid contentType");
}
}
};

function createScriptingDisabledDocument(parsingMode, contentType, string) {
const document = Document.createImpl([], {
core,
options: {
parsingMode,
encoding: "UTF-8",
contentType
// TODO: somehow set URL to active document's URL
}
});

// "scripting enabled" set to false
applyDocumentFeatures(document, {
FetchExternalResources: [],
ProcessExternalResources: false,
SkipExternalResources: false
});

document._htmlToDom.appendHtmlToDocument(string, document);
document.close();
return document;
}
13 changes: 13 additions & 0 deletions lib/jsdom/living/domparsing/DOMParser.idl
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[Constructor]
interface DOMParser {
[NewObject]
Document parseFromString(DOMString str, SupportedType type);
};

enum SupportedType {
"text/html",
"text/xml",
"application/xml",
"application/xhtml+xml",
"image/svg+xml"
};
3 changes: 3 additions & 0 deletions lib/jsdom/living/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ exports.Node = require("./generated/Node").interface;
exports.Element = require("./generated/Element").interface;
exports.DocumentFragment = require("./generated/DocumentFragment").interface;
exports.Document = exports.HTMLDocument = require("./generated/Document").interface;
exports.XMLDocument = require("./generated/XMLDocument").interface;
exports.CharacterData = require("./generated/CharacterData").interface;
exports.Comment = require("./generated/Comment").interface;
exports.DocumentType = require("./generated/DocumentType").interface;
Expand All @@ -31,6 +32,8 @@ exports.EventTarget = require("./generated/EventTarget").interface;
exports.Location = require("./generated/Location").interface;
exports.History = require("./generated/History").interface;

exports.DOMParser = require("./generated/DOMParser").interface;

require("./register-elements")(exports);

// These need to be cleaned up...
Expand Down
4 changes: 4 additions & 0 deletions lib/jsdom/living/nodes/XMLDocument-impl.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
"use strict";
const DocumentImpl = require("./Document-impl").implementation;

exports.implementation = class XMLDocumentImpl extends DocumentImpl {};
1 change: 1 addition & 0 deletions lib/jsdom/living/nodes/XMLDocument.idl
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
interface XMLDocument : Document {};
1 change: 1 addition & 0 deletions scripts/webidl/convert.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,5 @@ doConversion(path.resolve(__dirname, "../../lib/jsdom/living/traversal"))
.then(() => doConversion(path.resolve(__dirname, "../../lib/jsdom/living/window")))
.then(() => doConversion(path.resolve(__dirname, "../../lib/jsdom/living/nodes")))
.then(() => doConversion(path.resolve(__dirname, "../../lib/jsdom/living/navigator")))
.then(() => doConversion(path.resolve(__dirname, "../../lib/jsdom/living/domparsing")))
.done();
2 changes: 2 additions & 0 deletions test/web-platform-tests/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ describe("Web Platform Tests", () => {
"dom/traversal/TreeWalker-traversal-skip.html",
"dom/traversal/TreeWalker-walking-outside-a-tree.html",
"dom/traversal/TreeWalker.html",
// "domparsing/DOMParser-parseFromString-html.html", // needs to get "the active document's URL", which is not possible with one DOMParser shared across all windows
// "domparsing/DOMParser-parseFromString-xml.html", // needs a stricter XML parser, and XMLDocument
"domparsing/insert-adjacent.html",
// "html/browsers/browsing-the-web/history-traversal/PopStateEvent.html", // https://github.com/w3c/web-platform-tests/pull/2964
"html/browsers/browsing-the-web/history-traversal/hashchange_event.html",
Expand Down
1 change: 1 addition & 0 deletions test/web-platform-tests/to-upstream.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ describe("Local tests in Web Platform Test format (to-upstream)", () => {
"dom/nodes/getElementsByClassName-32.html",
"dom/nodes/Node-isEqualNode.html",
"dom/nodes/Node-mutation-adoptNode.html",
"domparsing/DOMParser-dont-upstream.html",
"domparsing/insert-adjacent.html",
"encoding/meta/meta-charset-no-quotes.html",
"encoding/meta/meta-charset-simple-quotes.html",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
<!doctype html>
<title>DOMParser tests that we pass so far, despite not passing any of the files</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script>
// |expected| should be an object indicating the expected type of node.
function assert_node(actual, expected) {
assert_true(actual instanceof expected.type,
'Node type mismatch: actual = ' + actual.nodeType + ', expected = ' + expected.nodeType);
if (typeof(expected.id) !== 'undefined')
assert_equals(actual.id, expected.id, expected.idMessage);
if (typeof(expected.nodeValue) !== 'undefined')
assert_equals(actual.nodeValue, expected.nodeValue, expected.nodeValueMessage);
}

var doc;
setup(function() {
var parser = new DOMParser();
var input = '<html id="root"><head></head><body></body></html>';
doc = parser.parseFromString(input, 'text/html');
});

test(function() {
var root = doc.documentElement;
assert_node(root, { type: HTMLHtmlElement, id: 'root',
idMessage: 'documentElement id attribute should be root.' });
}, 'Parsing of id attribute');

test(function() {
assert_equals(doc.contentType, "text/html")
}, 'contentType');

test(function() {
assert_equals(doc.characterSet, "UTF-8")
}, 'characterSet');

test(function() {
assert_equals(doc.inputEncoding, "UTF-8")
}, 'inputEncoding');

test(function() {
assert_equals(doc.charset, "UTF-8")
}, 'charset');

// Fail due to our lack of multi-global setup
// test(function() {
// var url = document.URL;
// assert_equals(doc.documentURI, url,
// 'The document must have a URL value equal to the URL of the active document.');
// assert_equals(doc.URL, url,
// 'The document must have a URL value equal to the URL of the active document.');
// }, 'URL value');

test(function() {
assert_equals(doc.location, null,
'The document must have a location value of null.');
}, 'Location value');

test(function() {
var soup = "<!DOCTYPE foo></><foo></multiple></>";
var htmldoc = new DOMParser().parseFromString(soup, "text/html");
assert_equals(htmldoc.documentElement.localName, "html");
assert_equals(htmldoc.documentElement.namespaceURI, "http://www.w3.org/1999/xhtml");
}, "DOMParser parses HTML tag soup with no problems");

/////////////////////////////

function checkMetadata(doc, contentType) {
assert_true(doc instanceof Document, "Should be Document");
// assert_equals(doc.URL, document.URL, "URL");
// assert_equals(doc.documentURI, document.URL, "documentURI");
assert_equals(doc.characterSet, "UTF-8", "characterSet");
assert_equals(doc.charset, "UTF-8", "charset");
assert_equals(doc.inputEncoding, "UTF-8", "inputEncoding");
assert_equals(doc.contentType, contentType, "contentType");
assert_equals(doc.location, null, "location");
}

var allowedTypes = ["text/xml", "application/xml", "application/xhtml+xml", "image/svg+xml"];

allowedTypes.forEach(function(type) {
test(function() {
var p = new DOMParser();
var doc = p.parseFromString("<foo/>", type);
assert_true(doc instanceof Document, "Should be Document");
checkMetadata(doc, type);
assert_equals(doc.documentElement.namespaceURI, null);
assert_equals(doc.documentElement.localName, "foo");
assert_equals(doc.documentElement.tagName, "foo");
}, "Should parse correctly in type " + type);

test(function() {
var p = new DOMParser();
var doc = p.parseFromString("<foo/>", type);
assert_false(doc instanceof XMLDocument, "Should not be XMLDocument");
}, "XMLDocument interface for correctly parsed document with type " + type);

// Fail because we don't have a strict XML parser (?)
// test(function() {
// var p = new DOMParser();
// var doc = p.parseFromString("<foo>", type);
// checkMetadata(doc, type);
// assert_equals(doc.documentElement.namespaceURI, "http://www.mozilla.org/newlayout/xml/parsererror.xml");
// assert_equals(doc.documentElement.localName, "parsererror");
// assert_equals(doc.documentElement.tagName, "parsererror");
// }, "Should return an error document for XML wellformedness errors in type " + type);

test(function() {
var p = new DOMParser();
var doc = p.parseFromString("<foo>", type);
assert_false(doc instanceof XMLDocument, "Should not be XMLDocument");
}, "XMLDocument interface for incorrectly parsed document with type " + type);
});
</script>

0 comments on commit 5085d2e

Please sign in to comment.