mirror of
https://github.com/jlengrand/webcomponentsjs.git
synced 2026-03-10 08:51:22 +00:00
Merge pull request #644 from bbsteventill/541-memory-leak-ie11
fixed memory leak while importing html elements in IE
This commit is contained in:
@@ -251,7 +251,10 @@ function takeRecords(node) {
|
||||
while (node.parentNode) {
|
||||
node = node.parentNode;
|
||||
}
|
||||
var observer = node.__observer;
|
||||
|
||||
// The node is a ShadowRoot, an IE will have a memory leak if you put the observer
|
||||
// directly on the ShadowRoot, so put it on the head so it does not leak
|
||||
var observer = node.head.__observer;
|
||||
if (observer) {
|
||||
handler(node, observer.takeRecords());
|
||||
takeMutations();
|
||||
@@ -260,19 +263,29 @@ function takeRecords(node) {
|
||||
|
||||
var forEach = Array.prototype.forEach.call.bind(Array.prototype.forEach);
|
||||
|
||||
|
||||
// observe a node tree; bail if it's already being observed.
|
||||
function observe(inRoot) {
|
||||
if (inRoot.__observer) {
|
||||
|
||||
if (inRoot && inRoot.head && inRoot.head.__observer) {
|
||||
return;
|
||||
}
|
||||
// For each ShadowRoot, we create a new MutationObserver, so the root can be
|
||||
// garbage collected once all references to the `inRoot` node are gone.
|
||||
// Give the handler access to the root so that an 'in document' check can
|
||||
// be done.
|
||||
|
||||
// originally the observer was on the ShadowRoot (inRoot) (single observer);
|
||||
// this causes a memory leak within IE. To fix this, we must put a an observer
|
||||
// on both the head and body nodes on the ShadowRoot
|
||||
var observer = new MutationObserver(handler.bind(this, inRoot));
|
||||
observer.observe(inRoot, {childList: true, subtree: true});
|
||||
inRoot.__observer = observer;
|
||||
observer.observe(inRoot.head, {childList: true, subtree: true});
|
||||
observer.observe(inRoot.body, {childList: true, subtree: true});
|
||||
|
||||
// this needs to be on head or it will leak in IE
|
||||
// IE does not like it when you have non-standard attributes on root dom's, so put
|
||||
// the observer on the head element
|
||||
// this is used to check if the observer has been attached already (above)
|
||||
inRoot.head.__observer = observer;
|
||||
}
|
||||
|
||||
// upgrade an entire document and observe it for elements changes.
|
||||
|
||||
@@ -40,7 +40,10 @@ Observer.prototype = {
|
||||
},
|
||||
|
||||
observe: function(root) {
|
||||
this.mo.observe(root, {childList: true, subtree: true});
|
||||
// IE will leak if you put an observer on a root shadow document
|
||||
// so observe changes to both the head and body
|
||||
this.mo.observe(root.head, {childList: true, subtree: true});
|
||||
this.mo.observe(root.body, {childList: true, subtree: true});
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
@@ -74,7 +74,9 @@ var importer = {
|
||||
// generate an HTMLDocument from data
|
||||
doc = err ? null : makeDocument(resource, redirectedUrl || url);
|
||||
if (doc) {
|
||||
doc.__importLink = elt;
|
||||
// IE will leak if you put the node directly on the ShadowRoot (doc)
|
||||
// instead appending to ShadowRoot head for reference
|
||||
doc.head.__importLink = elt;
|
||||
// note, we cannot use MO to detect parsed nodes because
|
||||
// SD polyfill does not report these as mutations.
|
||||
this.bootDocument(doc);
|
||||
|
||||
@@ -161,8 +161,10 @@ var importParser = {
|
||||
|
||||
rootImportForElement: function(elt) {
|
||||
var n = elt;
|
||||
while (n.ownerDocument.__importLink) {
|
||||
n = n.ownerDocument.__importLink;
|
||||
// IE will leak if you put the import link directly on the ownerDocument (shadow root)
|
||||
// so the link is appended on the head element.
|
||||
while (n.ownerDocument.head.__importLink) {
|
||||
n = n.ownerDocument.head.__importLink;
|
||||
}
|
||||
return n;
|
||||
},
|
||||
|
||||
@@ -76,6 +76,11 @@
|
||||
var renderer = scope.getRendererForHost(this);
|
||||
renderer.invalidate();
|
||||
|
||||
// This is needed for IE - if you put elements directly on the root shadow
|
||||
// IE will leak, create head and body so we can append observers, etc.
|
||||
newShadowRoot.head = document.createElement('head');
|
||||
newShadowRoot.body = document.createElement('body');
|
||||
|
||||
return newShadowRoot;
|
||||
},
|
||||
|
||||
|
||||
@@ -46,11 +46,16 @@
|
||||
|
||||
test('HTMLImports whenready detail', function(done) {
|
||||
HTMLImports.whenReady(function(detail) {
|
||||
chai.assert.equal(detail.allImports.length, 2);
|
||||
chai.assert.equal(detail.loadedImports.length, 2);
|
||||
|
||||
chai.expect(detail.allImports[0].href).to.contain('imports/load-1.html');
|
||||
chai.expect(detail.allImports[1].href).to.contain('imports/load-2.html');
|
||||
var allImports = document.querySelectorAll('link[rel="import"]');
|
||||
|
||||
chai.assert.equal(detail.allImports.length, allImports.length);
|
||||
chai.assert.equal(detail.loadedImports.length, allImports.length);
|
||||
|
||||
var importsArray = Array.prototype.slice.call(detail.allImports);
|
||||
|
||||
chai.expect(importsArray.filter(function(el){ return el.href.indexOf('imports/load-1.html') >= 0;}));
|
||||
chai.expect(importsArray.filter(function(el){ return el.href.indexOf('imports/load-2.html') >= 0;}));
|
||||
|
||||
done()
|
||||
});
|
||||
|
||||
@@ -46,9 +46,10 @@
|
||||
|
||||
test('HTMLImports whenready errors', function(done) {
|
||||
HTMLImports.whenReady(function(detail) {
|
||||
chai.assert.equal(detail.allImports.length, 2);
|
||||
var allImports = document.querySelectorAll('link[rel="import"]');
|
||||
chai.assert.equal(detail.allImports.length, allImports.length);
|
||||
chai.assert.equal(detail.errorImports.length, 1);
|
||||
chai.assert.equal(detail.loadedImports.length, 1);
|
||||
chai.assert.equal(detail.loadedImports.length, allImports.length - 1);
|
||||
|
||||
var errorImport = detail.errorImports[0];
|
||||
chai.expect(errorImport.href).to.contain('imports/load-does-not-exist.html');
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
|
||||
function check() {
|
||||
clearTimeout(timeout);
|
||||
chai.assert.equal(document.querySelector('link').import, null, '404\'d link.import is null');
|
||||
chai.assert.equal(document.querySelector('link[href="imports/404-1.html"]').import, null, '404\'d link.import is null');
|
||||
chai.assert.equal(errors, 2, '404\'d generate error event');
|
||||
done();
|
||||
}
|
||||
|
||||
@@ -20,7 +20,9 @@
|
||||
<x-foo>plain</x-foo>
|
||||
<button is="x-baz">plain</button>
|
||||
<script>
|
||||
var isNativeShadowDomSupported;
|
||||
test('smoke', function(done) {
|
||||
isNativeShadowDomSupported = !!unwrap(document.createElement('div')).createShadowRoot;
|
||||
var p = Object.create(HTMLElement.prototype);
|
||||
p.createdCallback = function() {
|
||||
this.textContent = 'custom!';
|
||||
@@ -57,8 +59,13 @@
|
||||
done();
|
||||
}
|
||||
var ob = new MutationObserver(handler);
|
||||
ob.observe(root, {childList: true, subtree: true});
|
||||
root.appendChild(xzot);
|
||||
if( isNativeShadowDomSupported ) {
|
||||
ob.observe(root, {childList: true, subtree: true});
|
||||
root.appendChild(xzot);
|
||||
} else {
|
||||
ob.observe(root.body, {childList: true, subtree: true});
|
||||
root.body.appendChild(xzot);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
Reference in New Issue
Block a user