Skip to content

Commit

Permalink
Add CDATA sections
Browse files Browse the repository at this point in the history
Adds the CDATASection class and document.createCDATASection. Fixes #1642.
  • Loading branch information
snuggs authored and domenic committed Dec 19, 2016
1 parent 30f7906 commit fedc336
Show file tree
Hide file tree
Showing 13 changed files with 117 additions and 3 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/dom/nodes/Document-createComment-createTextNode.js
test/web-platform-tests/to-upstream/domparsing/DOMParser-dont-upstream.html
test/window/files
test/window/frame.js
Expand Down
3 changes: 3 additions & 0 deletions benchmark/dom/construction.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,6 @@ exports.createProcessingInstruction = suite(document => {
document.createProcessingInstruction("php", "echo 123; ?");
});

exports.createCDATASection = suite(document => {
document.createCDATASection("foo");
});
5 changes: 3 additions & 2 deletions lib/jsdom/living/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,12 @@ 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.Text = require("./generated/Text").interface;
exports.CDATASection = require("./generated/CDATASection").interface;
exports.ProcessingInstruction = require("./generated/ProcessingInstruction").interface;
exports.Comment = require("./generated/Comment").interface;
exports.DocumentType = require("./generated/DocumentType").interface;
exports.DOMImplementation = require("./generated/DOMImplementation").interface;
exports.ProcessingInstruction = require("./generated/ProcessingInstruction").interface;
exports.Text = require("./generated/Text").interface;

exports.Event = require("./generated/Event").interface;
exports.CustomEvent = require("./generated/CustomEvent").interface;
Expand Down
4 changes: 4 additions & 0 deletions lib/jsdom/living/node.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ module.exports.clone = function (core, node, document, cloneChildren) {
copy = document.createTextNode(node._data);
break;

case NODE_TYPE.CDATA_SECTION_NODE:
copy = document.createCDATASection(node._data);
break;

case NODE_TYPE.COMMENT_NODE:
copy = document.createComment(node._data);
break;
Expand Down
16 changes: 16 additions & 0 deletions lib/jsdom/living/nodes/CDATASection-impl.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
"use strict";

const TextImpl = require("./Text-impl").implementation;
const NODE_TYPE = require("../node-type");

class CDATASectionImpl extends TextImpl {
constructor(args, privateData) {
super(args, privateData);

this.nodeType = NODE_TYPE.CDATA_SECTION_NODE;
}
}

module.exports = {
implementation: CDATASectionImpl
};
3 changes: 3 additions & 0 deletions lib/jsdom/living/nodes/CDATASection.idl
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[Exposed=Window]
interface CDATASection : Text {
};
20 changes: 20 additions & 0 deletions lib/jsdom/living/nodes/Document-impl.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ const listOfElementsWithNamespaceAndLocalName = require("../node").listOfElement
const listOfElementsWithClassNames = require("../node").listOfElementsWithClassNames;
const Comment = require("../generated/Comment");
const ProcessingInstruction = require("../generated/ProcessingInstruction");
const CDATASection = require("../generated/CDATASection");
const Text = require("../generated/Text");
const DocumentFragment = require("../generated/DocumentFragment");
const DOMImplementation = require("../generated/DOMImplementation");
Expand Down Expand Up @@ -645,6 +646,25 @@ class DocumentImpl extends NodeImpl {
});
}

// https://dom.spec.whatwg.org/#dom-document-createcdatasection
createCDATASection(data) {
if (this._parsingMode === "html") {
throw new DOMException(DOMException.NOT_SUPPORTED_ERR,
"Cannot create CDATA sections in HTML documents");
}

if (data.includes("]]>")) {
throw new DOMException(DOMException.INVALID_CHARACTER_ERR,
"CDATA section data cannot contain the string \"]]>\"");
}

return CDATASection.createImpl([], {
core: this._core,
ownerDocument: this,
data
});
}

createTextNode(data) {
return Text.createImpl([], {
core: this._core,
Expand Down
1 change: 1 addition & 0 deletions lib/jsdom/living/nodes/Document.idl
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ interface Document : Node {
[NewObject] Element createElementNS(DOMString? namespace, DOMString qualifiedName);
[NewObject] DocumentFragment createDocumentFragment();
[NewObject] Text createTextNode(DOMString data);
[NewObject] CDATASection createCDATASection(DOMString data);
[NewObject] Comment createComment(DOMString data);
[NewObject] ProcessingInstruction createProcessingInstruction(DOMString target, DOMString data);

Expand Down
7 changes: 6 additions & 1 deletion lib/jsdom/living/nodes/Node-impl.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ function isObsoleteNodeType(node) {
return node.nodeType === NODE_TYPE.ENTITY_NODE ||
node.nodeType === NODE_TYPE.ENTITY_REFERENCE_NODE ||
node.nodeType === NODE_TYPE.NOTATION_NODE ||
// node.nodeType === NODE_TYPE.ATTRIBUTE_NODE || // this is missing how do we handle?
node.nodeType === NODE_TYPE.CDATA_SECTION_NODE;
}

Expand Down Expand Up @@ -89,6 +90,7 @@ class NodeImpl extends EventTargetImpl {
get nodeValue() {
if (this.nodeType === NODE_TYPE.TEXT_NODE ||
this.nodeType === NODE_TYPE.COMMENT_NODE ||
this.nodeType === NODE_TYPE.CDATA_SECTION_NODE ||
this.nodeType === NODE_TYPE.PROCESSING_INSTRUCTION_NODE) {
return this._data;
}
Expand All @@ -99,6 +101,7 @@ class NodeImpl extends EventTargetImpl {
set nodeValue(value) {
if (this.nodeType === NODE_TYPE.TEXT_NODE ||
this.nodeType === NODE_TYPE.COMMENT_NODE ||
this.nodeType === NODE_TYPE.CDATA_SECTION_NODE ||
this.nodeType === NODE_TYPE.PROCESSING_INSTRUCTION_NODE) {
this.replaceData(0, this.length, value);
}
Expand All @@ -114,6 +117,8 @@ class NodeImpl extends EventTargetImpl {
return this.tagName;
case NODE_TYPE.TEXT_NODE:
return "#text";
case NODE_TYPE.CDATA_SECTION_NODE:
return "#cdata-section";
case NODE_TYPE.PROCESSING_INSTRUCTION_NODE:
return this.target;
case NODE_TYPE.COMMENT_NODE:
Expand Down Expand Up @@ -432,7 +437,7 @@ class NodeImpl extends EventTargetImpl {
case NODE_TYPE.ELEMENT_NODE:
text = "";
for (const child of domSymbolTree.treeIterator(this)) {
if (child.nodeType === NODE_TYPE.TEXT_NODE) {
if (child.nodeType === NODE_TYPE.TEXT_NODE || child.nodeType === NODE_TYPE.CDATA_SECTION_NODE) {
text += child.nodeValue;
}
}
Expand Down
2 changes: 2 additions & 0 deletions test/web-platform-tests/to-upstream.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ describe("Local tests in Web Platform Test format (to-upstream)", () => {
"dom/events/EventTarget-add-remove-listener.html",
"dom/events/EventTarget-prototype-constructor.html",
"dom/events/EventTarget-this-of-listener.html",
"dom/nodes/Document-createCDATASection.html",
"dom/nodes/Document-createCDATASection.xhtml",
"dom/nodes/Element-hasAttribute.html",
"dom/nodes/Element-removeAttribute.html",
"dom/nodes/Element-setAttribute.html",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>document.createCDATASection must throw in HTML documents</title>
<link rel="help" href="https://dom.spec.whatwg.org/#dom-document-createcdatasection">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>

<script>
"use strict";

assert_throws("NotSupportedError", () => document.createCDATASection("foo"));

done();
</script>
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8"/>
<title>document.createCDATASection</title>
<link rel="help" href="https://dom.spec.whatwg.org/#dom-document-createcdatasection"/>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="Document-createComment-createTextNode.js"></script>
</head>

<body>
<script>
"use strict";
test_create("createCDATASection", CDATASection, 4, "#cdata-section");

test(() => {
assert_throws("InvalidCharacterError", () => document.createCDATASection(" ]]> "));
}, "Creating a CDATA section containing the string \"]]>\" must throw");
</script>
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
function test_create(method, iface, nodeType, nodeName) {
["\u000b", "a -- b", "a-", "-b", null, undefined].forEach(function(value) {
test(function() {
var c = document[method](value);
var expected = String(value);
assert_true(c instanceof iface);
assert_true(c instanceof CharacterData);
assert_true(c instanceof Node);
assert_equals(c.ownerDocument, document);
assert_equals(c.data, expected, "data");
assert_equals(c.nodeValue, expected, "nodeValue");
assert_equals(c.textContent, expected, "textContent");
assert_equals(c.length, expected.length);
assert_equals(c.nodeType, nodeType);
assert_equals(c.nodeName, nodeName);
assert_equals(c.hasChildNodes(), false);
assert_equals(c.childNodes.length, 0);
assert_equals(c.firstChild, null);
assert_equals(c.lastChild, null);
}, method + "(" + format_value(value) + ")");
});
}

0 comments on commit fedc336

Please sign in to comment.