From 841d292c0f07fb517d1997a8290753b7eb1ba89c Mon Sep 17 00:00:00 2001 From: Tim Perry Date: Tue, 15 Nov 2016 15:52:54 +0100 Subject: [PATCH] Make key properties writable to allow polyfilling Many polyfills rewrite built-ins on the DOM to add new functionality and support new upcoming specs. In the browser, this is fine - DOM properties are reliably writable. In Domino they weren't. The specific changes here are to support the custom-elements polyfill https://github.com/webcomponents/custom-elements/blob/master/src/custom-elements.js but cover the most popular and widely applicable methods. This should probably be extended in future to cover the whole API. Writability is configurable. If you wish to lock down the DOM API, set the global `__domino_frozen__` to `true` before your first `require('domino')`. Fixes: #89 --- CHANGELOG.md | 1 + README.md | 8 ++++++++ lib/Document.js | 7 ++++--- lib/config.js | 7 +++++++ lib/utils.js | 3 ++- test/domino.js | 16 ++++++++++++++++ 6 files changed, 38 insertions(+), 4 deletions(-) create mode 100644 lib/config.js diff --git a/CHANGELOG.md b/CHANGELOG.md index f011bcf..a6b422f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ * Fix unescape mechanism in attribute values. (#95) * Disable nonstandard "ignore case" version of attribute matching. * Add `dom/nodes` tests from w3c/web-platform-tests. (#92, @pimterry) +* Make selected API methods writable to support polyfills. (#89, @pimterry) # domino 1.0.27 (17 Oct 2016) * Fix bug in AFE list replacement over existing bookmark. diff --git a/README.md b/README.md index c3c2d3e..660e11b 100644 --- a/README.md +++ b/README.md @@ -38,6 +38,14 @@ var h1 = document.querySelector('h1'); console.log(h1.innerHTML); ``` +By default many domino methods will be stored in writable properties, to +allow polyfills (as browsers do). You can lock down the implementation +if desired as follows: +```javascript +global.__domino_frozen__ = true; // Must precede any `require('domino')` +var domino = require('domino'); +``` + ## Tests Domino includes test from the [W3C DOM Conformance Suites](http://www.w3.org/DOM/Test/) diff --git a/lib/Document.js b/lib/Document.js index 9064f51..028c830 100644 --- a/lib/Document.js +++ b/lib/Document.js @@ -22,6 +22,7 @@ var svg = require('./svg'); var utils = require('./utils'); var MUTATE = require('./MutationConstants'); var NAMESPACE = utils.NAMESPACE; +var isApiWritable = require("./config").isApiWritable; function Document(isHTML, address) { this.nodeType = Node.DOCUMENT_NODE; @@ -135,7 +136,7 @@ Document.prototype = Object.create(Node.prototype, { if (!xml.isValidName(localName)) utils.InvalidCharacterError(); if (this.isHTML) localName = utils.toASCIILowerCase(localName); return html.createElement(this, localName, null); - }}, + }, writable: isApiWritable }, createElementNS: { value: function(namespace, qualifiedName) { if (!xml.isValidName(qualifiedName)) utils.InvalidCharacterError(); @@ -170,7 +171,7 @@ Document.prototype = Object.create(Node.prototype, { } return new Element(this, localName, namespace, prefix); - }}, + }, writable: isApiWritable }, createEvent: { value: function createEvent(interfaceName) { interfaceName = interfaceName.toLowerCase(); @@ -346,7 +347,7 @@ Document.prototype = Object.create(Node.prototype, { importNode: { value: function importNode(node, deep) { return this.adoptNode(node.cloneNode()); - }}, + }, writable: isApiWritable }, // The following attributes and methods are from the HTML spec URL: { get: utils.nyi }, diff --git a/lib/config.js b/lib/config.js new file mode 100644 index 0000000..abd3475 --- /dev/null +++ b/lib/config.js @@ -0,0 +1,7 @@ +/* + * This file defines Domino behaviour that can be externally configured. + * To change these settings, set the relevant global property *before* + * you call `require("domino")`. + */ + +exports.isApiWritable = !global.__domino_frozen__; diff --git a/lib/utils.js b/lib/utils.js index 7c2a749..f16e4e0 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -1,6 +1,7 @@ "use strict"; var DOMException = require('./DOMException'); var ERR = DOMException; +var isApiWritable = require("./config").isApiWritable; exports.NAMESPACE = { HTML: 'http://www.w3.org/1999/xhtml', @@ -48,7 +49,7 @@ exports.assert = function(expr, msg) { exports.expose = function(src, c) { for (var n in src) { - Object.defineProperty(c.prototype, n, {value: src[n] }); + Object.defineProperty(c.prototype, n, { value: src[n], writable: isApiWritable }); } }; diff --git a/test/domino.js b/test/domino.js index fc4488b..65a50bf 100644 --- a/test/domino.js +++ b/test/domino.js @@ -862,3 +862,19 @@ exports.gh95 = function() { }).should.throw({ name: 'SyntaxError' }); document.querySelectorAll("a[href=foo\\'s]").length.should.equal(1); }; + +exports.propertyWritability = function () { // gh #89 + var window = domino.createWindow(''); + var document = domino.createDocument(); + + function assertWritable(object, property) { + var replacement = function () { }; + object[property] = replacement; + object[property].should.equal(replacement, property + " should be writable"); + } + + assertWritable(window, 'HTMLElement'); + assertWritable(document, 'importNode'); + assertWritable(document, 'createElement'); + assertWritable(document, 'createElementNS'); +}