Skip to content

Commit

Permalink
un-monkey-patch OpenElementStack to avoid leaks
Browse files Browse the repository at this point in the history
A monkey-patch as modified by commit 75a921e
maintains a reference to the most recent JSDOMParse5Adapter (and all its
options) through the push/pop closures. Because the monkey-patches are
never cleaned up, there is always a reference to the most recent
JSDOMParse5Adapter and its related objects.

This fixes jestjs/jest#9507
This may fix jsdom#2757
  • Loading branch information
terite committed Feb 8, 2020
1 parent b4b5a54 commit e7f1f0b
Showing 1 changed file with 40 additions and 24 deletions.
64 changes: 40 additions & 24 deletions lib/jsdom/browser/parser/html.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,34 @@ const OpenElementStack = require("parse5/lib/parser/open-element-stack");
const OpenElementStackOriginalPop = OpenElementStack.prototype.pop;
const OpenElementStackOriginalPush = OpenElementStack.prototype.push;

// Horrible monkey-patch to implement https://github.com/inikulin/parse5/issues/237
function wrapOpenElementStack(adapter) {
OpenElementStack.prototype.push = function (...args) {
OpenElementStackOriginalPush.apply(this, args);
adapter._currentElement = this.current;

const after = this.items[this.stackTop];
if (after._pushedOnStackOfOpenElements) {
after._pushedOnStackOfOpenElements();
}
};
OpenElementStack.prototype.pop = function (...args) {
const before = this.items[this.stackTop];

OpenElementStackOriginalPop.apply(this, args);
adapter._currentElement = this.current;

if (before._poppedOffStackOfOpenElements) {
before._poppedOffStackOfOpenElements();
}
};
}

function unwrapOpenElementStack() {
OpenElementStack.prototype.pop = OpenElementStackOriginalPop;
OpenElementStack.prototype.push = OpenElementStackOriginalPush;
}

class JSDOMParse5Adapter {
constructor(documentImpl) {
this._documentImpl = documentImpl;
Expand All @@ -24,28 +52,6 @@ class JSDOMParse5Adapter {
// Since the createElement hook doesn't provide the parent element, we keep track of this using _currentElement:
// https://github.com/inikulin/parse5/issues/285
this._currentElement = undefined;

// Horrible monkey-patch to implement https://github.com/inikulin/parse5/issues/237
const adapter = this;
OpenElementStack.prototype.push = function (...args) {
OpenElementStackOriginalPush.apply(this, args);
adapter._currentElement = this.current;

const after = this.items[this.stackTop];
if (after._pushedOnStackOfOpenElements) {
after._pushedOnStackOfOpenElements();
}
};
OpenElementStack.prototype.pop = function (...args) {
const before = this.items[this.stackTop];

OpenElementStackOriginalPop.apply(this, args);
adapter._currentElement = this.current;

if (before._poppedOffStackOfOpenElements) {
before._poppedOffStackOfOpenElements();
}
};
}

_ownerDocument() {
Expand Down Expand Up @@ -165,15 +171,25 @@ function parseFragment(markup, contextElement) {
treeAdapter: new JSDOMParse5Adapter(ownerDocument)
});

return parse5.parseFragment(contextElement, markup, config);
wrapOpenElementStack(config.treeAdapter);
try {
return parse5.parseFragment(contextElement, markup, config);
} finally {
unwrapOpenElementStack();
}
}

function parseIntoDocument(markup, ownerDocument) {
const config = Object.assign({}, ownerDocument._parseOptions, {
treeAdapter: new JSDOMParse5Adapter(ownerDocument)
});

return parse5.parse(markup, config);
wrapOpenElementStack(config.treeAdapter);
try {
return parse5.parse(markup, config);
} finally {
unwrapOpenElementStack();
}
}

module.exports = {
Expand Down

0 comments on commit e7f1f0b

Please sign in to comment.