Skip to content

Commit 39fa2b3

Browse files
authoredJul 26, 2019
Fix #1881 + related ospec bug (#2492)
* Fix #1881 + related ospec bug * Test duplicate resolves, update changelog
1 parent 90f96eb commit 39fa2b3

File tree

6 files changed

+364
-61
lines changed

6 files changed

+364
-61
lines changed
 

‎docs/change-log.md

+2
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
1717
### Upcoming...
1818
19+
- Ensure vnodes are removed correctly in the face of `onbeforeremove` resolving after new nodes are added ([#2492](https://github.com/MithrilJS/mithril.js/pull/2492) [@isiahmeadows](https://github.com/isiahmeadows))
20+
1921
-->
2022

2123
### v2.0.1

‎ospec/ospec.js

+5-6
Original file line numberDiff line numberDiff line change
@@ -289,12 +289,11 @@ else window.o = m()
289289
Assert.prototype[name] = function assert(value) {
290290
var self = this
291291
var message = serialize(self.value) + "\n " + verb + "\n" + serialize(value)
292-
if (compare(self.value, value)){
293-
succeed(self, message)
294-
return function(message) {
295-
if (!self.pass) self.message = message + "\n\n" + self.message
296-
}
297-
}else fail(self, message)
292+
if (compare(self.value, value)) succeed(self, message)
293+
else fail(self, message)
294+
return function(message) {
295+
if (!self.pass) self.message = message + "\n\n" + self.message
296+
}
298297
}
299298
}
300299
function succeed(assertion, message) {

‎render/render.js

+121-44
Original file line numberDiff line numberDiff line change
@@ -86,9 +86,12 @@ module.exports = function($window) {
8686
}
8787
vnode.dom = temp.firstChild
8888
vnode.domSize = temp.childNodes.length
89+
// Capture nodes to remove, so we don't confuse them.
90+
vnode.instance = []
8991
var fragment = $doc.createDocumentFragment()
9092
var child
9193
while (child = temp.firstChild) {
94+
vnode.instance.push(child)
9295
fragment.appendChild(child)
9396
}
9497
insertNode(parent, fragment, nextSibling)
@@ -272,7 +275,7 @@ module.exports = function($window) {
272275
function updateNodes(parent, old, vnodes, hooks, nextSibling, ns) {
273276
if (old === vnodes || old == null && vnodes == null) return
274277
else if (old == null || old.length === 0) createNodes(parent, vnodes, 0, vnodes.length, hooks, nextSibling, ns)
275-
else if (vnodes == null || vnodes.length === 0) removeNodes(old, 0, old.length)
278+
else if (vnodes == null || vnodes.length === 0) removeNodes(parent, old, 0, old.length)
276279
else {
277280
var isOldKeyed = old[0] != null && old[0].key != null
278281
var isKeyed = vnodes[0] != null && vnodes[0].key != null
@@ -281,7 +284,7 @@ module.exports = function($window) {
281284
if (!isKeyed) while (start < vnodes.length && vnodes[start] == null) start++
282285
if (isKeyed === null && isOldKeyed == null) return // both lists are full of nulls
283286
if (isOldKeyed !== isKeyed) {
284-
removeNodes(old, oldStart, old.length)
287+
removeNodes(parent, old, oldStart, old.length)
285288
createNodes(parent, vnodes, start, vnodes.length, hooks, nextSibling, ns)
286289
} else if (!isKeyed) {
287290
// Don't index past the end of either list (causes deopts).
@@ -295,10 +298,10 @@ module.exports = function($window) {
295298
v = vnodes[start]
296299
if (o === v || o == null && v == null) continue
297300
else if (o == null) createNode(parent, v, hooks, ns, getNextSibling(old, start + 1, nextSibling))
298-
else if (v == null) removeNode(o)
301+
else if (v == null) removeNode(parent, o)
299302
else updateNode(parent, o, v, hooks, getNextSibling(old, start + 1, nextSibling), ns)
300303
}
301-
if (old.length > commonLength) removeNodes(old, start, old.length)
304+
if (old.length > commonLength) removeNodes(parent, old, start, old.length)
302305
if (vnodes.length > commonLength) createNodes(parent, vnodes, start, vnodes.length, hooks, nextSibling, ns)
303306
} else {
304307
// keyed diff
@@ -326,9 +329,9 @@ module.exports = function($window) {
326329
if (start === end) break
327330
if (o.key !== ve.key || oe.key !== v.key) break
328331
topSibling = getNextSibling(old, oldStart, nextSibling)
329-
insertNode(parent, toFragment(oe), topSibling)
332+
moveNodes(parent, oe, topSibling)
330333
if (oe !== v) updateNode(parent, oe, v, hooks, topSibling, ns)
331-
if (++start <= --end) insertNode(parent, toFragment(o), nextSibling)
334+
if (++start <= --end) moveNodes(parent, o, nextSibling)
332335
if (o !== ve) updateNode(parent, o, ve, hooks, nextSibling, ns)
333336
if (ve.dom != null) nextSibling = ve.dom
334337
oldStart++; oldEnd--
@@ -346,7 +349,7 @@ module.exports = function($window) {
346349
oe = old[oldEnd]
347350
ve = vnodes[end]
348351
}
349-
if (start > end) removeNodes(old, oldStart, oldEnd + 1)
352+
if (start > end) removeNodes(parent, old, oldStart, oldEnd + 1)
350353
else if (oldStart > oldEnd) createNodes(parent, vnodes, start, end + 1, hooks, nextSibling, ns)
351354
else {
352355
// inspired by ivi https://github.com/ivijs/ivi/ by Boris Kaul
@@ -367,7 +370,7 @@ module.exports = function($window) {
367370
}
368371
}
369372
nextSibling = originalNextSibling
370-
if (matched !== oldEnd - oldStart + 1) removeNodes(old, oldStart, oldEnd + 1)
373+
if (matched !== oldEnd - oldStart + 1) removeNodes(parent, old, oldStart, oldEnd + 1)
371374
if (matched === 0) createNodes(parent, vnodes, start, end + 1, hooks, nextSibling, ns)
372375
else {
373376
if (pos === -1) {
@@ -380,7 +383,7 @@ module.exports = function($window) {
380383
if (oldIndices[i-start] === -1) createNode(parent, v, hooks, ns, nextSibling)
381384
else {
382385
if (lisIndices[li] === i - start) li--
383-
else insertNode(parent, toFragment(v), nextSibling)
386+
else moveNodes(parent, v, nextSibling)
384387
}
385388
if (v.dom != null) nextSibling = vnodes[i].dom
386389
}
@@ -416,7 +419,7 @@ module.exports = function($window) {
416419
else updateComponent(parent, old, vnode, hooks, nextSibling, ns)
417420
}
418421
else {
419-
removeNode(old)
422+
removeNode(parent, old)
420423
createNode(parent, vnode, hooks, ns, nextSibling)
421424
}
422425
}
@@ -428,7 +431,7 @@ module.exports = function($window) {
428431
}
429432
function updateHTML(parent, old, vnode, ns, nextSibling) {
430433
if (old.children !== vnode.children) {
431-
toFragment(old)
434+
removeHTML(parent, old)
432435
createHTML(parent, vnode, ns, nextSibling)
433436
}
434437
else vnode.dom = old.dom, vnode.domSize = old.domSize
@@ -483,7 +486,7 @@ module.exports = function($window) {
483486
vnode.domSize = vnode.instance.domSize
484487
}
485488
else if (old.instance != null) {
486-
removeNode(old.instance)
489+
removeNode(parent, old.instance)
487490
vnode.dom = undefined
488491
vnode.domSize = 0
489492
}
@@ -550,26 +553,52 @@ module.exports = function($window) {
550553
return result
551554
}
552555

553-
function toFragment(vnode) {
554-
var count = vnode.domSize
555-
if (count != null || vnode.dom == null) {
556-
var fragment = $doc.createDocumentFragment()
557-
if (count > 0) {
558-
var dom = vnode.dom
559-
while (--count) fragment.appendChild(dom.nextSibling)
560-
fragment.insertBefore(dom, fragment.firstChild)
561-
}
562-
return fragment
563-
}
564-
else return vnode.dom
565-
}
566556
function getNextSibling(vnodes, i, nextSibling) {
567557
for (; i < vnodes.length; i++) {
568558
if (vnodes[i] != null && vnodes[i].dom != null) return vnodes[i].dom
569559
}
570560
return nextSibling
571561
}
572562

563+
// This covers a really specific edge case:
564+
// - Parent node is keyed and contains child
565+
// - Child is removed, returns unresolved promise in `onbeforeremove`
566+
// - Parent node is moved in keyed diff
567+
// - Remaining children still need moved appropriately
568+
//
569+
// Ideally, I'd track removed nodes as well, but that introduces a lot more
570+
// complexity and I'm not exactly interested in doing that.
571+
function moveNodes(parent, vnode, nextSibling) {
572+
var frag = $doc.createDocumentFragment()
573+
moveChildToFrag(parent, frag, vnode)
574+
insertNode(parent, frag, nextSibling)
575+
}
576+
function moveChildToFrag(parent, frag, vnode) {
577+
// Dodge the recursion overhead in a few of the most common cases.
578+
while (vnode.dom != null && vnode.dom.parentNode === parent) {
579+
if (typeof vnode.tag !== "string") {
580+
vnode = vnode.instance
581+
if (vnode != null) continue
582+
} else if (vnode.tag === "<") {
583+
for (var i = 0; i < vnode.instance.length; i++) {
584+
frag.appendChild(vnode.instance[i])
585+
}
586+
} else if (vnode.tag !== "[") {
587+
// Don't recurse for text nodes *or* elements, just fragments
588+
frag.appendChild(vnode.dom)
589+
} else if (vnode.children.length === 1) {
590+
vnode = vnode.children[0]
591+
if (vnode != null) continue
592+
} else {
593+
for (var i = 0; i < vnode.children.length; i++) {
594+
var child = vnode.children[i]
595+
if (child != null) moveChildToFrag(parent, frag, child)
596+
}
597+
}
598+
break
599+
}
600+
}
601+
573602
function insertNode(parent, dom, nextSibling) {
574603
if (nextSibling != null) parent.insertBefore(dom, nextSibling)
575604
else parent.appendChild(dom)
@@ -589,41 +618,89 @@ module.exports = function($window) {
589618
}
590619

591620
//remove
592-
function removeNodes(vnodes, start, end) {
621+
function removeNodes(parent, vnodes, start, end) {
593622
for (var i = start; i < end; i++) {
594623
var vnode = vnodes[i]
595-
if (vnode != null) removeNode(vnode)
624+
if (vnode != null) removeNode(parent, vnode)
596625
}
597626
}
598-
function removeNode(vnode) {
599-
var expected = 1, called = 0
627+
function removeNode(parent, vnode) {
628+
var mask = 0
600629
var original = vnode.state
630+
var stateResult, attrsResult
601631
if (typeof vnode.tag !== "string" && typeof vnode.state.onbeforeremove === "function") {
602632
var result = callHook.call(vnode.state.onbeforeremove, vnode)
603633
if (result != null && typeof result.then === "function") {
604-
expected++
605-
result.then(continuation, continuation)
634+
mask = 1
635+
stateResult = result
606636
}
607637
}
608638
if (vnode.attrs && typeof vnode.attrs.onbeforeremove === "function") {
609639
var result = callHook.call(vnode.attrs.onbeforeremove, vnode)
610640
if (result != null && typeof result.then === "function") {
611-
expected++
612-
result.then(continuation, continuation)
613-
}
614-
}
615-
continuation()
616-
function continuation() {
617-
if (++called === expected) {
618-
checkState(vnode, original)
619-
onremove(vnode)
620-
if (vnode.dom) {
621-
var parent = vnode.dom.parentNode
622-
var count = vnode.domSize || 1
623-
while (--count) parent.removeChild(vnode.dom.nextSibling)
641+
// eslint-disable-next-line no-bitwise
642+
mask |= 2
643+
attrsResult = result
644+
}
645+
}
646+
checkState(vnode, original)
647+
648+
// If we can, try to fast-path it and avoid all the overhead of awaiting
649+
if (!mask) {
650+
onremove(vnode)
651+
removeChild(parent, vnode)
652+
} else {
653+
if (stateResult != null) {
654+
var next = function () {
655+
// eslint-disable-next-line no-bitwise
656+
if (mask & 1) { mask &= 2; if (!mask) reallyRemove() }
657+
}
658+
stateResult.then(next, next)
659+
}
660+
if (attrsResult != null) {
661+
var next = function () {
662+
// eslint-disable-next-line no-bitwise
663+
if (mask & 2) { mask &= 1; if (!mask) reallyRemove() }
664+
}
665+
attrsResult.then(next, next)
666+
}
667+
}
668+
669+
function reallyRemove() {
670+
checkState(vnode, original)
671+
onremove(vnode)
672+
removeChild(parent, vnode)
673+
}
674+
}
675+
function removeHTML(parent, vnode) {
676+
for (var i = 0; i < vnode.instance.length; i++) {
677+
parent.removeChild(vnode.instance[i])
678+
}
679+
}
680+
function removeChild(parent, vnode) {
681+
// Dodge the recursion overhead in a few of the most common cases.
682+
while (vnode.dom != null && vnode.dom.parentNode === parent) {
683+
if (typeof vnode.tag !== "string") {
684+
vnode = vnode.instance
685+
if (vnode != null) continue
686+
} else if (vnode.tag === "<") {
687+
removeHTML(parent, vnode)
688+
} else {
689+
if (vnode.tag !== "[") {
624690
parent.removeChild(vnode.dom)
691+
if (!Array.isArray(vnode.children)) break
692+
}
693+
if (vnode.children.length === 1) {
694+
vnode = vnode.children[0]
695+
if (vnode != null) continue
696+
} else {
697+
for (var i = 0; i < vnode.children.length; i++) {
698+
var child = vnode.children[i]
699+
if (child != null) removeChild(parent, child)
700+
}
625701
}
626702
}
703+
break
627704
}
628705
}
629706
function onremove(vnode) {

‎render/tests/test-onremove.js

+218
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,224 @@ o.spec("onremove", function() {
214214
o(vnode.dom).notEquals(updated.dom) // this used to be a recycling pool test
215215
o(onremove.callCount).equals(1)
216216
})
217+
// Warning: this test is complicated because it's replicating a race condition.
218+
o("removes correct nodes when child delays removal, parent removes, then child resolves", function () {
219+
// Sugar over the complexity - I need to test the entire tree for consistency.
220+
function expect(expectedPairs) {
221+
var expected = []
222+
223+
for (var i = 0; i < expectedPairs.length; i++) {
224+
var name = expectedPairs[i][0]
225+
var text = expectedPairs[i][1]
226+
expected.push({
227+
name: name,
228+
firstType: name === "#text" ? null : "#text",
229+
text: text,
230+
})
231+
}
232+
233+
var actual = []
234+
var list = root.firstChild.childNodes
235+
for (var i = 0; i < list.length; i++) {
236+
var current = list[i]
237+
var textNode = current.childNodes.length === 1
238+
? current.firstChild
239+
: current
240+
actual.push({
241+
name: current.nodeName,
242+
firstType: textNode === current ? null : textNode.nodeName,
243+
text: textNode.nodeValue,
244+
})
245+
}
246+
247+
o(actual).deepEquals(expected)
248+
}
249+
250+
var resolve
251+
252+
function update(id, showParent, showChild) {
253+
render(root, [
254+
{tag: "div", children: [
255+
showParent ? {tag: "[", children: [
256+
{tag: "#", children: ""}, // Required
257+
showChild ? {tag: "[", attrs: {
258+
onbeforeremove: function () {
259+
return {then: function (r) { resolve = r }}
260+
},
261+
}, children: [
262+
{tag: "div", text: id},
263+
]} : undefined,
264+
]} : undefined,
265+
]}
266+
])
267+
}
268+
269+
update("1", true, true)
270+
expect([
271+
["#text", ""],
272+
["DIV", "1"],
273+
])
274+
o(resolve).equals(undefined)
275+
276+
update("2", true, false)
277+
expect([
278+
["#text", ""],
279+
["DIV", "1"],
280+
])
281+
o(typeof resolve).equals("function")
282+
var original = resolve
283+
284+
update("3", true, true)
285+
expect([
286+
["#text", ""],
287+
["DIV", "1"],
288+
["DIV", "3"],
289+
])
290+
o(resolve).equals(original)
291+
292+
update("4", false, true)
293+
expect([
294+
["DIV", "1"],
295+
])
296+
o(resolve).equals(original)
297+
298+
update("5", true, true)
299+
expect([
300+
["DIV", "1"],
301+
["#text", ""],
302+
["DIV", "5"],
303+
])
304+
o(resolve).equals(original)
305+
306+
resolve()
307+
expect([
308+
["#text", ""],
309+
["DIV", "5"],
310+
])
311+
o(resolve).equals(original)
312+
313+
update("6", true, true)
314+
expect([
315+
["#text", ""],
316+
["DIV", "6"],
317+
])
318+
o(resolve).equals(original)
319+
})
320+
// Warning: this test is complicated because it's replicating a race condition.
321+
o("removes correct nodes when child delays removal, parent removes, then child resolves + rejects both", function () {
322+
// Sugar over the complexity - I need to test the entire tree for consistency.
323+
function expect(expectedPairs) {
324+
var expected = []
325+
326+
for (var i = 0; i < expectedPairs.length; i++) {
327+
var name = expectedPairs[i][0]
328+
var text = expectedPairs[i][1]
329+
expected.push({
330+
name: name,
331+
firstType: name === "#text" ? null : "#text",
332+
text: text,
333+
})
334+
}
335+
336+
var actual = []
337+
var list = root.firstChild.childNodes
338+
for (var i = 0; i < list.length; i++) {
339+
var current = list[i]
340+
var textNode = current.childNodes.length === 1
341+
? current.firstChild
342+
: current
343+
actual.push({
344+
name: current.nodeName,
345+
firstType: textNode === current ? null : textNode.nodeName,
346+
text: textNode.nodeValue,
347+
})
348+
}
349+
350+
o(actual).deepEquals(expected)
351+
}
352+
353+
var resolve, reject
354+
355+
function update(id, showParent, showChild) {
356+
render(root, [
357+
{tag: "div", children: [
358+
showParent ? {tag: "[", children: [
359+
{tag: "#", children: ""}, // Required
360+
showChild ? {tag: "[", attrs: {
361+
onbeforeremove: function () {
362+
return {then: function (res, rej) {
363+
resolve = res
364+
reject = rej
365+
}}
366+
},
367+
}, children: [
368+
{tag: "div", text: id},
369+
]} : undefined,
370+
]} : undefined,
371+
]}
372+
])
373+
}
374+
375+
update("1", true, true)
376+
expect([
377+
["#text", ""],
378+
["DIV", "1"],
379+
])
380+
o(resolve).equals(undefined)
381+
382+
update("2", true, false)
383+
expect([
384+
["#text", ""],
385+
["DIV", "1"],
386+
])
387+
o(typeof resolve).equals("function")
388+
var originalResolve = resolve
389+
var originalReject = reject
390+
391+
update("3", true, true)
392+
expect([
393+
["#text", ""],
394+
["DIV", "1"],
395+
["DIV", "3"],
396+
])
397+
o(resolve).equals(originalResolve)
398+
o(reject).equals(originalReject)
399+
400+
update("4", false, true)
401+
expect([
402+
["DIV", "1"],
403+
])
404+
o(resolve).equals(originalResolve)
405+
o(reject).equals(originalReject)
406+
407+
update("5", true, true)
408+
expect([
409+
["DIV", "1"],
410+
["#text", ""],
411+
["DIV", "5"],
412+
])
413+
o(resolve).equals(originalResolve)
414+
o(reject).equals(originalReject)
415+
416+
resolve()
417+
reject()
418+
reject()
419+
resolve()
420+
expect([
421+
["#text", ""],
422+
["DIV", "5"],
423+
])
424+
o(resolve).equals(originalResolve)
425+
o(reject).equals(originalReject)
426+
427+
update("6", true, true)
428+
expect([
429+
["#text", ""],
430+
["DIV", "6"],
431+
])
432+
o(resolve).equals(originalResolve)
433+
o(reject).equals(originalReject)
434+
})
217435
})
218436
})
219437
})

‎render/tests/test-updateNodesFuzzer.js

+2-3
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ o.spec("updateNodes keyed list Fuzzer", function() {
1313
render = vdom($window)
1414
})
1515

16-
16+
1717
void [
1818
{delMax: 0, movMax: 50, insMax: 9},
1919
{delMax: 3, movMax: 5, insMax: 5},
@@ -31,7 +31,7 @@ o.spec("updateNodes keyed list Fuzzer", function() {
3131
render(root, test.updated.map(function(x){return {tag: x, key: x}}))
3232

3333
if (root.appendChild.callCount + root.insertBefore.callCount !== test.expected.creations + test.expected.moves) console.log(test, {aC: root.appendChild.callCount, iB: root.insertBefore.callCount}, [].map.call(root.childNodes, function(n){return n.nodeName.toLowerCase()}))
34-
34+
3535
o(root.appendChild.callCount + root.insertBefore.callCount).equals(test.expected.creations + test.expected.moves)("moves")
3636
o(root.removeChild.callCount).equals(test.expected.deletions)("deletions")
3737
o([].map.call(root.childNodes, function(n){return n.nodeName.toLowerCase()})).deepEquals(test.updated)
@@ -154,4 +154,3 @@ function addSpies(node) {
154154
node.insertBefore = o.spy(node.insertBefore)
155155
node.removeChild = o.spy(node.removeChild)
156156
}
157-

‎test-utils/domMock.js

+16-8
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ module.exports = function(options) {
9494
}
9595
else {
9696
this.childNodes.push(child)
97-
if (child.parentNode != null && child.parentNode !== this) child.parentNode.removeChild(child)
97+
if (child.parentNode != null && child.parentNode !== this) removeChild.call(child.parentNode, child)
9898
child.parentNode = this
9999
}
100100
}
@@ -124,14 +124,14 @@ module.exports = function(options) {
124124
this.childNodes.splice.apply(this.childNodes, [refIndex, 0].concat(child.childNodes))
125125
while (child.firstChild) {
126126
var subchild = child.firstChild
127-
child.removeChild(subchild)
127+
removeChild.call(child, subchild)
128128
subchild.parentNode = this
129129
}
130130
child.childNodes = []
131131
}
132132
else {
133133
this.childNodes.splice(refIndex, 0, child)
134-
if (child.parentNode != null && child.parentNode !== this) child.parentNode.removeChild(child)
134+
if (child.parentNode != null && child.parentNode !== this) removeChild.call(child.parentNode, child)
135135
child.parentNode = this
136136
}
137137
}
@@ -214,14 +214,14 @@ module.exports = function(options) {
214214
if (ns != null) element.setAttributeNS(ns, name, value)
215215
else element.setAttribute(name, value)
216216
})
217-
stack[depth].appendChild(element)
217+
appendChild.call(stack[depth], element)
218218
if (!selfClosed && voidElements.indexOf(startTag.toLowerCase()) < 0) stack[++depth] = element
219219
}
220220
else if (endTag) {
221221
depth--
222222
}
223223
else if (text) {
224-
stack[depth].appendChild($window.document.createTextNode(text)) // FIXME handle html entities
224+
appendChild.call(stack[depth], $window.document.createTextNode(text)) // FIXME handle html entities
225225
}
226226
})
227227
}
@@ -319,7 +319,7 @@ module.exports = function(options) {
319319
},
320320
set innerHTML(value) {
321321
var voidElements = ["area", "base", "br", "col", "command", "embed", "hr", "img", "input", "keygen", "link", "meta", "param", "source", "track", "wbr"]
322-
while (this.firstChild) this.removeChild(this.firstChild)
322+
while (this.firstChild) removeChild.call(this, this.firstChild)
323323
var match = value.match(/^<svg xmlns="http:\/\/www\.w3\.org\/2000\/svg">(.*)<\/svg>$/), root, ns
324324
if (match) {
325325
var value = match[1]
@@ -659,12 +659,20 @@ module.exports = function(options) {
659659
nodeType: 3,
660660
nodeName: "#text",
661661
parentNode: null,
662+
get childNodes() { return [] },
663+
get firstChild() { return null },
662664
get nodeValue() {return nodeValue},
663665
set nodeValue(value) {
664666
/*eslint-disable no-implicit-coercion*/
665667
nodeValue = "" + value
666668
/*eslint-enable no-implicit-coercion*/
667669
},
670+
get nextSibling() {
671+
if (this.parentNode == null) return null
672+
var index = this.parentNode.childNodes.indexOf(this)
673+
if (index < 0) throw new TypeError("Parent's childNodes is out of sync")
674+
return this.parentNode.childNodes[index + 1] || null
675+
},
668676
}
669677
},
670678
createDocumentFragment: function() {
@@ -691,9 +699,9 @@ module.exports = function(options) {
691699
},
692700
}
693701
$window.document.documentElement = $window.document.createElement("html")
694-
$window.document.documentElement.appendChild($window.document.createElement("head"))
702+
appendChild.call($window.document.documentElement, $window.document.createElement("head"))
695703
$window.document.body = $window.document.createElement("body")
696-
$window.document.documentElement.appendChild($window.document.body)
704+
appendChild.call($window.document.documentElement, $window.document.body)
697705
activeElement = $window.document.body
698706

699707
if (options.spy) $window.__getSpies = getSpies

0 commit comments

Comments
 (0)
Please sign in to comment.