Permalink
Browse files

NodeList snapshots are now cached. Results in a 10% performance impro…

…vement when running the whole test-suite.
  • Loading branch information...
1 parent 160473b commit c90ad35372319bf8bf213daae4960f6ef5c04192 Felix Gnass committed Nov 22, 2010
Showing with 50 additions and 40 deletions.
  1. +1 −5 lib/jsdom/browser/index.js
  2. +37 −20 lib/jsdom/level1/core.js
  3. +1 −5 lib/jsdom/level2/core.js
  4. +11 −10 lib/jsdom/level2/html.js
@@ -222,11 +222,7 @@ var browserAugmentation = exports.browserAugmentation = function(dom, options) {
return false;
}
- var disableLiveLists = this.ownerDocument &&
- this.ownerDocument.implementation &&
- this.ownerDocument.implementation.hasFeature("DisableLiveLists");
-
- return new dom.NodeList(dom.mapper(this, filterByClassName), !disableLiveLists);
+ return new dom.NodeList(this.ownerDocument || this, dom.mapper(this, filterByClassName));
};
dom.Element.prototype.__defineGetter__('sourceIndex', function() {
@@ -100,21 +100,26 @@ core.DOMException.prototype = {
core.DOMException.prototype.__proto__ = Error.prototype;
-core.NodeList = function(query, live) {
+core.NodeList = function(element, query) {
+ this._element = element;
this._query = query;
+ this._version = -1;
this.update();
};
core.NodeList.prototype = {
update: function() {
- for (var i = 0; i < this._length; i++) {
- this[i] = undefined;
- }
- var nodes = this._snapshot = this._query();
- this._length = nodes.length;
- for (var i = 0; i < nodes.length; i++) {
- this[i] = nodes[i];
+ if (this._version < this._element._version) {
+ for (var i = 0; i < this._length; i++) {
+ this[i] = undefined;
+ }
+ var nodes = this._snapshot = this._query();
+ this._length = nodes.length;
+ for (var i = 0; i < nodes.length; i++) {
+ this[i] = nodes[i];
+ }
+ this._version = this._element._version;
}
- return nodes;
+ return this._snapshot;
},
toArray: function() {
return this.update();
@@ -173,13 +178,14 @@ core.DOMImplementation.prototype = {
core.Node = function (ownerDocument) {
var self = this;
this._childNodes = [];
- this._children = new core.NodeList(function() { return self._childNodes });
+ this._version = 0;
+ this._children = new core.NodeList(this, function() { return self._childNodes });
this._nodeValue = null;
this._parentNode = null;
this._ownerDocument = ownerDocument;
this._attributes = new core.AttrNodeMap(this.ownerDocument, this);
- this._nodeName = null;
- this._readonly = false;
+ this._nodeName = null;
+ this._readonly = false;
this.style = {
position: 'static'
};
@@ -263,7 +269,7 @@ core.Node.prototype = {
if (this._parentNode._childNodes[index] === this) {
index++;
}
-
+
// Swizec - some scripts hang here because null is not returned
if (index > this._parentNode._childNodes.length) {
return null;
@@ -343,10 +349,12 @@ core.Node.prototype = {
tmpNode = newChild.removeChild(newChild.firstChild);
this.insertBefore(tmpNode, refChild);
}
-
+
} else {
this._childNodes.splice(i,0,newChild);
newChild._parentNode = this;
+ this._modified();
+
}
found = true;
break;
@@ -360,6 +368,13 @@ core.Node.prototype = {
}
}, // raises(DOMException);
+ _modified: function() {
+ this._version++;
+ if (this.ownerDocument) {
+ this.ownerDocument._version++;
+ }
+ },
+
/* returns Node */
replaceChild : function(/* Node */ newChild, /* Node */ oldChild){
@@ -406,6 +421,7 @@ core.Node.prototype = {
this._childNodes.splice(i,0, newChild);
}
+ this._modified();
return oldChild;
}
}
@@ -427,6 +443,7 @@ core.Node.prototype = {
child = this._childNodes[i];
this._childNodes.splice(i,1);
oldChild._parentNode = null;
+ this._modified();
if (child.id) {
if (child._ownerDocument._ids) {
@@ -503,6 +520,7 @@ core.Node.prototype = {
// Attach the parent node.
newChild._parentNode = this;
this._childNodes.push(newChild);
+ this._modified();
if (newChild.id) {
if (!this._ownerDocument._ids) {
@@ -721,6 +739,7 @@ core.NamedNodeMap.prototype = {
arg._parentNode = this;
arg._specified = true;
this._nodes[arg.name] = arg;
+ this._ownerDocument._modified();
return ret;
}, // raises: function(DOMException) {},
@@ -739,6 +758,7 @@ core.NamedNodeMap.prototype = {
var prev = this._nodes[name] || null;
this._nodes[name] = null;
delete this._nodes[name];
+ this._ownerDocument._modified();
this._length--;
return prev;
@@ -760,7 +780,7 @@ core.NamedNodeMap.prototype = {
};
core.AttrNodeMap = function(document, parentNode) {
- core.NamedNodeMap.call(this,document);
+ core.NamedNodeMap.call(this, document);
this._parentNode = parentNode;
};
@@ -985,11 +1005,7 @@ core.Element.prototype = {
return false;
}
- var disableLiveLists = this.ownerDocument &&
- this.ownerDocument.implementation &&
- this.ownerDocument.implementation.hasFeature("DisableLiveLists");
-
- return new core.NodeList(core.mapper(this, filterByTagName), !disableLiveLists);
+ return new core.NodeList(this.ownerDocument || this, core.mapper(this, filterByTagName));
},
};
core.Element.prototype.__proto__ = core.Node.prototype;
@@ -1399,6 +1415,7 @@ core.Attr.prototype = {
this._childNodes.length = 0;
this._childNodes.push(this.ownerDocument.createTextNode(value));
+ this._modified();
this._specified = true;
this._nodeValue = value;
},
@@ -281,11 +281,7 @@ core.Element.prototype.getElementsByTagNameNS = function(/* String */ namespaceU
return false;
}
- var disableLiveLists = this.ownerDocument &&
- this.ownerDocument.implementation &&
- this.ownerDocument.implementation.hasFeature("DisableLiveLists");
-
- return new core.NodeList(core.mapper(this, filterByTagName), !disableLiveLists);
+ return new core.NodeList(this.ownerDocument || this, core.mapper(this, filterByTagName));
};
core.Element.prototype.hasAttribute = function(/* string */name)
@@ -152,8 +152,8 @@ core.DOMImplementation.prototype._features = {
};
-core.HTMLCollection = function(query) {
- core.NodeList.call(this, query);
+core.HTMLCollection = function(element, query) {
+ core.NodeList.call(this, element, query);
};
core.HTMLCollection.prototype = {
namedItem : function(name) {
@@ -194,7 +194,8 @@ function closest(e, tagName) {
}
function descendants(e, tagName, recursive) {
- return new core.HTMLCollection(core.mapper(e, function(n) {
+ var owner = recursive ? e.ownerDocument || e : e;
+ return new core.HTMLCollection(owner, core.mapper(e, function(n) {
return n.nodeName === tagName;
}, recursive));
}
@@ -271,7 +272,7 @@ core.HTMLDocument.prototype = {
return this.getElementsByTagName('IMG');
},
get applets() {
- return new core.HTMLCollection(core.mapper(this, function(el) {
+ return new core.HTMLCollection(this, core.mapper(this, function(el) {
if (el && el.tagName) {
var upper = el.tagName.toUpperCase();
if (upper === "APPLET") {
@@ -285,7 +286,7 @@ core.HTMLDocument.prototype = {
}));
},
get links() {
- return new core.HTMLCollection(core.mapper(this, function(el) {
+ return new core.HTMLCollection(this, core.mapper(this, function(el) {
if (el && el.tagName) {
var upper = el.tagName.toUpperCase();
if (upper === "AREA" || (upper === "A" && el.href)) {
@@ -325,7 +326,7 @@ core.HTMLDocument.prototype = {
},
getElementsByName : function(elementName) {
- return new core.HTMLCollection(core.mapper(this, function(el) {
+ return new core.HTMLCollection(this, core.mapper(this, function(el) {
return (el.getAttribute && el.getAttribute("name") === elementName);
}));
},
@@ -394,7 +395,7 @@ define('HTMLFormElement', {
tagName: 'FORM',
proto: {
get elements() {
- return new core.HTMLCollection(core.mapper(this, function(e) {
+ return new core.HTMLCollection(this.ownerDocument, core.mapper(this, function(e) {
return listedElements.test(e.nodeName) ; // TODO exclude <input type="image">
}));
},
@@ -521,7 +522,7 @@ define('HTMLSelectElement', {
tagName: 'SELECT',
proto: {
get options() {
- return new core.HTMLOptionsCollection(core.mapper(this, function(n) {
+ return new core.HTMLOptionsCollection(this, core.mapper(this, function(n) {
return n.nodeName == 'OPTION';
}));
},
@@ -1144,7 +1145,7 @@ define('HTMLTableElement', {
get rows() {
if (!this._rows) {
var table = this;
- this._rows = new core.HTMLCollection(function() {
+ this._rows = new core.HTMLCollection(this.ownerDocument, function() {
var sections = [table.tHead].concat(table.tBodies.toArray(), table.tFoot).filter(function(s) { return !!s });
if (sections.length == 0) {
@@ -1326,7 +1327,7 @@ define('HTMLTableRowElement', {
proto: {
get cells() {
if (!this._cells) {
- this._cells = new core.HTMLCollection(core.mapper(this, function(n) {
+ this._cells = new core.HTMLCollection(this, core.mapper(this, function(n) {
return n.nodeName == 'TD' || n.nodeName == 'TH';
}, false));
}

0 comments on commit c90ad35

Please sign in to comment.