blob: 3057da8ca8c5d5cb39a73d3fb9683161a8af7c17 (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
|
"use strict";
const { wrapperForImpl } = require("../generated/utils");
// If we were to implement the MutationObserver by spec, the MutationObservers will not be collected by the GC because
// all the MO are kept in a mutation observer list (https://github.com/jsdom/jsdom/pull/2398/files#r238123889). The
// mutation observer list is primarily used to invoke the mutation observer callback in the same order than the
// mutation observer creation.
// In order to get around this issue, we will assign an increasing id for each mutation observer, this way we would be
// able to invoke the callback in the creation order without having to keep a list of all the mutation observers.
let mutationObserverId = 0;
// https://dom.spec.whatwg.org/#mutationobserver
class MutationObserverImpl {
// https://dom.spec.whatwg.org/#dom-mutationobserver-mutationobserver
constructor(globalObject, args) {
const [callback] = args;
this._callback = callback;
this._nodeList = [];
this._recordQueue = [];
this._id = ++mutationObserverId;
}
// https://dom.spec.whatwg.org/#dom-mutationobserver-observe
observe(target, options) {
if (("attributeOldValue" in options || "attributeFilter" in options) && !("attributes" in options)) {
options.attributes = true;
}
if ("characterDataOldValue" in options & !("characterData" in options)) {
options.characterData = true;
}
if (!options.childList && !options.attributes && !options.characterData) {
throw new TypeError("The options object must set at least one of 'attributes', 'characterData', or 'childList' " +
"to true.");
} else if (options.attributeOldValue && !options.attributes) {
throw new TypeError("The options object may only set 'attributeOldValue' to true when 'attributes' is true or " +
"not present.");
} else if (("attributeFilter" in options) && !options.attributes) {
throw new TypeError("The options object may only set 'attributeFilter' when 'attributes' is true or not " +
"present.");
} else if (options.characterDataOldValue && !options.characterData) {
throw new TypeError("The options object may only set 'characterDataOldValue' to true when 'characterData' is " +
"true or not present.");
}
const existingRegisteredObserver = target._registeredObserverList.find(registeredObserver => {
return registeredObserver.observer === this;
});
if (existingRegisteredObserver) {
for (const node of this._nodeList) {
node._registeredObserverList = node._registeredObserverList.filter(registeredObserver => {
return registeredObserver.source !== existingRegisteredObserver;
});
}
existingRegisteredObserver.options = options;
} else {
target._registeredObserverList.push({
observer: this,
options
});
this._nodeList.push(target);
}
}
// https://dom.spec.whatwg.org/#dom-mutationobserver-disconnect
disconnect() {
for (const node of this._nodeList) {
node._registeredObserverList = node._registeredObserverList.filter(registeredObserver => {
return registeredObserver.observer !== this;
});
}
this._recordQueue = [];
}
// https://dom.spec.whatwg.org/#dom-mutationobserver-takerecords
takeRecords() {
// TODO: revisit if https://github.com/jsdom/webidl2js/pull/108 gets fixed.
const records = this._recordQueue.map(wrapperForImpl);
this._recordQueue = [];
return records;
}
}
module.exports = {
implementation: MutationObserverImpl
};
|