Skip to content

Commit

Permalink
Switch to WeakMap and String objects to avoid memory leak. Fixes deve…
Browse files Browse the repository at this point in the history
  • Loading branch information
johannesodland committed Oct 22, 2023
1 parent 96fe21e commit 67ff042
Show file tree
Hide file tree
Showing 2 changed files with 33 additions and 28 deletions.
9 changes: 5 additions & 4 deletions src/vhtml.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ let DOMAttributeNames = {
htmlFor: 'for'
};

let sanitized = {};
let sanitized = new WeakMap();

/** Hyperscript reviver that constructs a sanitized HTML string. */
export default function h(name, attrs) {
Expand Down Expand Up @@ -47,14 +47,15 @@ export default function h(name, attrs) {
for (let i=child.length; i--; ) stack.push(child[i]);
}
else {
s += sanitized[child]===true ? child : esc(child);
s += sanitized.has(child) ? child : esc(child);
}
}
}

s += name ? `</${name}>` : '';
}

sanitized[s] = true;
return s;
let res = new String(s);
sanitized.set(res, true);
return res;
}
52 changes: 28 additions & 24 deletions test/vhtml.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,14 @@ import { expect } from 'chai';
/** @jsx h */
/*global describe,it*/

function valueOf(str) {
return str.valueOf();
}

describe('vhtml', () => {
it('should stringify html', () => {
let items = ['one', 'two', 'three'];
expect(
expect(valueOf(
<div class="foo">
<h1>Hi!</h1>
<p>Here is a list of {items.length} items:</p>
Expand All @@ -16,46 +20,46 @@ describe('vhtml', () => {
)) }
</ul>
</div>
).to.equal(
)).to.equal(
`<div class="foo"><h1>Hi!</h1><p>Here is a list of 3 items:</p><ul><li>one</li><li>two</li><li>three</li></ul></div>`
);
});

it('should sanitize children', () => {
expect(
expect(valueOf(
<div>
{ `<strong>blocked</strong>` }
<em>allowed</em>
</div>
).to.equal(
)).to.equal(
`<div>&lt;strong&gt;blocked&lt;/strong&gt;<em>allowed</em></div>`
);
});

it('should sanitize attributes', () => {
expect(
expect(valueOf(
<div onclick={`&<>"'`} />
).to.equal(
)).to.equal(
`<div onclick="&amp;&lt;&gt;&quot;&apos;"></div>`
);
});

it('should not sanitize the "dangerouslySetInnerHTML" attribute, and directly set its `__html` property as innerHTML', () => {
expect(
expect(valueOf(
<div dangerouslySetInnerHTML={{ __html: "<span>Injected HTML</span>" }} />
).to.equal(
)).to.equal(
`<div><span>Injected HTML</span></div>`
);
});

it('should flatten children', () => {
expect(
expect(valueOf(
<div>
{[['a','b']]}
<c>d</c>
{['e',['f'],[['g']]]}
</div>
).to.equal(
)).to.equal(
`<div>ab<c>d</c>efg</div>`
);
});
Expand All @@ -70,7 +74,7 @@ describe('vhtml', () => {
</li>
);

expect(
expect(valueOf(
<div class="foo">
<h1>Hi!</h1>
<ul>
Expand All @@ -81,7 +85,7 @@ describe('vhtml', () => {
)) }
</ul>
</div>
).to.equal(
)).to.equal(
`<div class="foo"><h1>Hi!</h1><ul><li id="0"><h4>one</h4>This is item one!</li><li id="1"><h4>two</h4>This is item two!</li></ul></div>`
);
});
Expand All @@ -95,7 +99,7 @@ describe('vhtml', () => {
</li>
);

expect(
expect(valueOf(
<div class="foo">
<h1>Hi!</h1>
<ul>
Expand All @@ -106,7 +110,7 @@ describe('vhtml', () => {
)) }
</ul>
</div>
).to.equal(
)).to.equal(
`<div class="foo"><h1>Hi!</h1><ul><li><h4></h4></li><li><h4></h4></li></ul></div>`
);
});
Expand All @@ -121,7 +125,7 @@ describe('vhtml', () => {
</li>
);

expect(
expect(valueOf(
<div class="foo">
<h1>Hi!</h1>
<ul>
Expand All @@ -132,13 +136,13 @@ describe('vhtml', () => {
)) }
</ul>
</div>
).to.equal(
)).to.equal(
`<div class="foo"><h1>Hi!</h1><ul><li><h4></h4>This is item one!</li><li><h4></h4>This is item two!</li></ul></div>`
);
});

it('should support empty (void) tags', () => {
expect(
expect(valueOf(
<div>
<area />
<base />
Expand All @@ -161,31 +165,31 @@ describe('vhtml', () => {
<span />
<p />
</div>
).to.equal(
)).to.equal(
`<div><area><base><br><col><command><embed><hr><img><input><keygen><link><meta><param><source><track><wbr><div></div><span></span><p></p></div>`
);
});

it('should handle special prop names', () => {
expect(
expect(valueOf(
<div className="my-class" htmlFor="id" />
).to.equal(
)).to.equal(
'<div class="my-class" for="id"></div>'
);
});

it('should support string fragments', () => {
expect(
expect(valueOf(
h(null, null, "foo", "bar", "baz")
).to.equal(
)).to.equal(
'foobarbaz'
);
});

it('should support element fragments', () => {
expect(
expect(valueOf(
h(null, null, <p>foo</p>, <em>bar</em>, <div class="qqqqqq">baz</div>)
).to.equal(
)).to.equal(
'<p>foo</p><em>bar</em><div class="qqqqqq">baz</div>'
);
});
Expand Down

0 comments on commit 67ff042

Please sign in to comment.