From 287660999faf51bf8a8f3190a3c498f9d96b802b Mon Sep 17 00:00:00 2001 From: Daniel Freedman Date: Mon, 10 Feb 2014 14:38:53 -0800 Subject: [PATCH] Work around busted IE MutationObserver. Setting innerHTML in IE 11 will result in removedNodes that are always childless. This leaves no way for the CustomElements observer to call those detachedCallbacks. Work around funky XML Namespace for unknown elements in IE Last commit needed for passing tests in IE 11 Work around lack of prototype swizzling in IE 10 customMixin should stop on HTMLElement prototype With be729c3, no call to customMixin will have HTMLUnknownElement.prototype in the chain, only HTMLElement.prototype --- src/CustomElements.js | 8 +- test/js/customElements.js | 2 +- test/js/documentRegister.js | 147 ++++++++++++++++++++++-------------- 3 files changed, 93 insertions(+), 64 deletions(-) diff --git a/src/CustomElements.js b/src/CustomElements.js index 9dc19a7..d097778 100644 --- a/src/CustomElements.js +++ b/src/CustomElements.js @@ -254,11 +254,9 @@ if (useNative) { var used = {}; // start with inSrc var p = inSrc; - // sometimes the default is HTMLUnknownElement.prototype instead of - // HTMLElement.prototype, so we add a test - // the idea is to avoid mixing in native prototypes, so adding - // the second test is WLOG - while (p !== inNative && p !== HTMLUnknownElement.prototype) { + // The default is HTMLElement.prototype, so we add a test to avoid mixing in + // native prototypes + while (p !== inNative && p !== HTMLElement.prototype) { var keys = Object.getOwnPropertyNames(p); for (var i=0, k; k=keys[i]; i++) { if (!used[k]) { diff --git a/test/js/customElements.js b/test/js/customElements.js index 371707b..3e93e3d 100644 --- a/test/js/customElements.js +++ b/test/js/customElements.js @@ -409,7 +409,7 @@ ''; CustomElements.takeRecords(); - work.innerHTML = ''; + work.removeChild(work.firstElementChild); CustomElements.takeRecords(); assert.deepEqual(['a', 'b', 'c', 'd', 'e'], log); }); diff --git a/test/js/documentRegister.js b/test/js/documentRegister.js index 9331dac..580355c 100644 --- a/test/js/documentRegister.js +++ b/test/js/documentRegister.js @@ -15,6 +15,37 @@ function isFormControl(element) return element.form == testForm; } + +/* + * Work around IE's insertion of XML Namespace elements into .outerHTML of HTMLUnknownElements + * + * Clone the input node, insert it into a div, and then read back the outerHTML, which is now stripped of the XML * + * Namespace element + */ +var isIE = navigator.userAgent.indexOf('Trident') > -1; +function assertOuterHTML(element, expected) { + var outerHTML = element.outerHTML; + if (isIE) { + var div = document.createElement('div'); + div.appendChild(element.cloneNode(true)); + outerHTML = div.firstChild.outerHTML; + } + chai.assert.equal(outerHTML, expected); +} + +var hasProto = ({}.__proto__); +function assertInstanceOf(element, constructor) { + if (hasProto) { + chai.assert.instanceOf(element, constructor); + } +} + +function assertNotInstanceOf(element, constructor) { + if (hasProto) { + chai.assert.notInstanceOf(element, constructor); + } +} + suite('register-type-extensions', function() { var assert = chai.assert; @@ -41,12 +72,12 @@ suite('register-type-extensions', function() { suite('generated constructors', function() { test('custom tag', function() { var fooNewed = new fooConstructor(); - assert.equal(fooNewed.outerHTML, fooOuterHTML); - assert.instanceOf(fooNewed, fooConstructor); - assert.instanceOf(fooNewed, HTMLElement); + assertOuterHTML(fooNewed, fooOuterHTML); + assertInstanceOf(fooNewed, fooConstructor); + assertInstanceOf(fooNewed, HTMLElement); // This is part of the Blink tests, but not supported in Firefox with // polyfill. Similar assertions are also commented out below. - // assert.notInstanceOf(fooNewed, HTMLUnknownElement); + // assertNotInstanceOf(fooNewed, HTMLUnknownElement); test('custom tag constructor', function() { assert.equal('a', 'b'); @@ -55,28 +86,28 @@ suite('register-type-extensions', function() { test('type extension', function() { var barNewed = new barConstructor(); - assert.equal(barNewed.outerHTML, barOuterHTML); - assert.instanceOf(barNewed, barConstructor); - assert.instanceOf(barNewed, HTMLInputElement); + assertOuterHTML(barNewed, barOuterHTML); + assertInstanceOf(barNewed, barConstructor); + assertInstanceOf(barNewed, HTMLInputElement); assert.ok(isFormControl(barNewed)); }); test('custom tag deriving from custom tag', function() { var bazNewed = new bazConstructor(); var bazOuterHTML = ''; - assert.equal(bazNewed.outerHTML, bazOuterHTML); - assert.instanceOf(bazNewed, bazConstructor); - assert.instanceOf(bazNewed, HTMLElement); - // assert.notInstanceOf(bazNewed, HTMLUnknownElement); + assertOuterHTML(bazNewed, bazOuterHTML); + assertInstanceOf(bazNewed, bazConstructor); + assertInstanceOf(bazNewed, HTMLElement); + // assertNotInstanceOf(bazNewed, HTMLUnknownElement); }); test('type extension deriving from custom tag', function() { var quxNewed = new quxConstructor(); var quxOuterHTML = ''; - assert.instanceOf(quxNewed, quxConstructor); - assert.instanceOf(quxNewed, barConstructor); - assert.instanceOf(quxNewed, HTMLInputElement); - assert.equal(quxNewed.outerHTML, quxOuterHTML); + assertInstanceOf(quxNewed, quxConstructor); + assertInstanceOf(quxNewed, barConstructor); + assertInstanceOf(quxNewed, HTMLInputElement); + assertOuterHTML(quxNewed, quxOuterHTML); assert.ok(isFormControl(quxNewed)); }); }); @@ -84,80 +115,80 @@ suite('register-type-extensions', function() { suite('single-parameter createElement', function() { test('custom tag', function() { var fooCreated = document.createElement('x-foo-x'); - assert.equal(fooCreated.outerHTML, fooOuterHTML); - assert.instanceOf(fooCreated, fooConstructor); + assertOuterHTML(fooCreated, fooOuterHTML); + assertInstanceOf(fooCreated, fooConstructor); }); test('type extension', function() { var barCreated = document.createElement('x-bar-x'); - assert.equal(barCreated.outerHTML, ''); - assert.notInstanceOf(barCreated, barConstructor); - // assert.notInstanceOf(barCreated, HTMLUnknownElement); - assert.instanceOf(barCreated, HTMLElement); + assertOuterHTML(barCreated, ''); + assertNotInstanceOf(barCreated, barConstructor); + // assertNotInstanceOf(barCreated, HTMLUnknownElement); + assertInstanceOf(barCreated, HTMLElement); }); test('custom tag deriving from custom tag', function() { bazCreated = document.createElement('x-baz'); - assert.equal(bazCreated.outerHTML, ''); - assert.instanceOf(bazCreated, bazConstructor); - // assert.notInstanceOf(bazCreated, HTMLUnknownElement); + assertOuterHTML(bazCreated, ''); + assertInstanceOf(bazCreated, bazConstructor); + // assertNotInstanceOf(bazCreated, HTMLUnknownElement); }); test('type extension deriving from custom tag', function() { quxCreated = document.createElement('x-qux'); - assert.equal(quxCreated.outerHTML, ''); - assert.notInstanceOf(quxCreated, quxConstructor); - // assert.notInstanceOf(quxCreated, HTMLUnknownElement); - assert.instanceOf(quxCreated, HTMLElement); + assertOuterHTML(quxCreated, ''); + assertNotInstanceOf(quxCreated, quxConstructor); + // assertNotInstanceOf(quxCreated, HTMLUnknownElement); + assertInstanceOf(quxCreated, HTMLElement); }); }); suite('createElement with type extensions', function() { test('extension is custom tag', function() { var divFooCreated = document.createElement('div', 'x-foo-x'); - assert.equal(divFooCreated.outerHTML, '
'); - assert.notInstanceOf(divFooCreated, fooConstructor); - assert.instanceOf(divFooCreated, HTMLDivElement); + assertOuterHTML(divFooCreated, '
'); + assertNotInstanceOf(divFooCreated, fooConstructor); + assertInstanceOf(divFooCreated, HTMLDivElement); }); test('valid extension', function() { var inputBarCreated = document.createElement('input', 'x-bar-x'); - assert.equal(inputBarCreated.outerHTML, barOuterHTML); - assert.instanceOf(inputBarCreated, barConstructor); - assert.notInstanceOf(inputBarCreated, HTMLUnknownElement); + assertOuterHTML(inputBarCreated, barOuterHTML); + assertInstanceOf(inputBarCreated, barConstructor); + assertNotInstanceOf(inputBarCreated, HTMLUnknownElement); assert.ok(isFormControl(inputBarCreated)); }); test('type extension of incorrect tag', function() { var divBarCreated = document.createElement('div', 'x-bar-x'); - assert.equal(divBarCreated.outerHTML, '
'); - assert.notInstanceOf(divBarCreated, barConstructor); - assert.instanceOf(divBarCreated, HTMLDivElement); + assertOuterHTML(divBarCreated, '
'); + assertNotInstanceOf(divBarCreated, barConstructor); + assertInstanceOf(divBarCreated, HTMLDivElement); }); test('incorrect extension of custom tag', function() { var fooBarCreated = document.createElement('x-foo-x', 'x-bar-x'); - assert.equal(fooBarCreated.outerHTML, ''); - assert.instanceOf(fooBarCreated, fooConstructor); + assertOuterHTML(fooBarCreated, ''); + assertInstanceOf(fooBarCreated, fooConstructor); }); test('incorrect extension of type extension', function() { var barFooCreated = document.createElement('x-bar-x', 'x-foo-x'); - assert.equal(barFooCreated.outerHTML, ''); - // assert.notInstanceOf(barFooCreated, HTMLUnknownElement); - assert.instanceOf(barFooCreated, HTMLElement); + assertOuterHTML(barFooCreated, ''); + // assertNotInstanceOf(barFooCreated, HTMLUnknownElement); + assertInstanceOf(barFooCreated, HTMLElement); }); test('null type extension', function() { var fooCreatedNull = document.createElement('x-foo-x', null); - assert.equal(fooCreatedNull.outerHTML, fooOuterHTML); - assert.instanceOf(fooCreatedNull, fooConstructor); + assertOuterHTML(fooCreatedNull, fooOuterHTML); + assertInstanceOf(fooCreatedNull, fooConstructor); }); test('empty type extension', function() { fooCreatedEmpty = document.createElement('x-foo-x', ''); - assert.equal(fooCreatedEmpty.outerHTML, fooOuterHTML); - assert.instanceOf(fooCreatedEmpty, fooConstructor); + assertOuterHTML(fooCreatedEmpty, fooOuterHTML); + assertInstanceOf(fooCreatedEmpty, fooConstructor); }); test('invalid tag name', function() { @@ -179,33 +210,33 @@ suite('register-type-extensions', function() { test('custom tag', function() { var fooParsed = createElementFromHTML(''); - assert.instanceOf(fooParsed, fooConstructor); + assertInstanceOf(fooParsed, fooConstructor); }); test('type extension', function() { - var barParsed = createElementFromHTML('') - assert.instanceOf(barParsed, barConstructor); + var barParsed = createElementFromHTML(''); + assertInstanceOf(barParsed, barConstructor); assert.ok(isFormControl(barParsed)); }); test('custom tag as type extension', function() { - var divFooParsed = createElementFromHTML('
') - assert.notInstanceOf(divFooParsed, fooConstructor); - assert.instanceOf(divFooParsed, HTMLDivElement); + var divFooParsed = createElementFromHTML('
'); + assertNotInstanceOf(divFooParsed, fooConstructor); + assertInstanceOf(divFooParsed, HTMLDivElement); }); // Should we upgrade invalid tags to HTMLElement? /*test('type extension as custom tag', function() { var namedBarParsed = createElementFromHTML('') - assert.notInstanceOf(namedBarParsed, barConstructor); - assert.notInstanceOf(namedBarParsed, HTMLUnknownElement); - assert.instanceOf(namedBarParsed, HTMLElement); + assertNotInstanceOf(namedBarParsed, barConstructor); + assertNotInstanceOf(namedBarParsed, HTMLUnknownElement); + assertInstanceOf(namedBarParsed, HTMLElement); });*/ test('type extension of incorrect tag', function() { - var divBarParsed = createElementFromHTML('
') - assert.notInstanceOf(divBarParsed, barConstructor); - assert.instanceOf(divBarParsed, HTMLDivElement); + var divBarParsed = createElementFromHTML('
'); + assertNotInstanceOf(divBarParsed, barConstructor); + assertInstanceOf(divBarParsed, HTMLDivElement); }); }); });