/
reference-cycle-leaks.html
186 lines (156 loc) · 6.24 KB
/
reference-cycle-leaks.html
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
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
<!DOCTYPE html>
<script src="../../resources/js-test.js"></script>
<script>
description('Tests for leaks caused by reference cycles that pass through the DOM implementation');
function checkForNodeLeaks(testFunction, underlyingClass)
{
// Bump this number as high as we need to, to get reproducible results.
const repetitions = 40;
gc();
const beforeCount = internals.numberOfLiveNodes();
for (var i = 0; i < repetitions; ++i)
testFunction();
gc();
const leaks = internals.numberOfLiveNodes() - beforeCount;
if (leaks == repetitions)
return "leaked";
if (leaks < repetitions / 10)
return "did not leak";
return "leaked an unexpected number of nodes: " + leaks + " leaks in " + repetitions + " runs";
}
function emptyFunction()
{
}
function createNode()
{
document.createTextNode("");
}
function createEventListenerCycle()
{
const leakDetectionNode = document.createTextNode("");
leakDetectionNode.addEventListener("x", function () { return leakDetectionNode; });
}
function createPromiseCycle()
{
const leakDetectionNode = document.createTextNode("");
const promise = new Promise(function (resolve, reject) { });
promise.cycle = promise;
promise.leakDetectionNode = leakDetectionNode;
}
function createTreeWalkerNodeCycle()
{
const leakDetectionNode = document.createTextNode("");
leakDetectionNode.treeWalker = document.createTreeWalker(leakDetectionNode);
}
function createTreeWalkerFilterCycle()
{
const leakDetectionNode = document.createTextNode("");
const filter = { leakDetectionNode: leakDetectionNode, acceptNode: function(node) { return leakDetectionNode; } };
leakDetectionNode.treeWalker = document.createTreeWalker(document, 0, filter);
}
function createRangeCycle()
{
const leakDetectionNode = document.createTextNode("");
const range = new Range();
range.setStart(leakDetectionNode, 0);
leakDetectionNode.range = range;
}
function createStaticRangeCycle()
{
const leakDetectionNode = document.createTextNode("");
leakDetectionNode.staticRange = new StaticRange({startContainer: leakDetectionNode, startOffset: 0, endContainer: leakDetectionNode, endOffset: 0});
}
function createCustomEventDetailsCycle()
{
const leakDetectionNode = document.createTextNode("");
leakDetectionNode.event = new CustomEvent("x", { detail: leakDetectionNode });
}
function createErrorEventDataCycle()
{
const leakDetectionNode = document.createTextNode("");
leakDetectionNode.event = new ErrorEvent("x", { error: leakDetectionNode });
}
function createExtendableMessageEventDataCycle()
{
const leakDetectionNode = document.createTextNode("");
leakDetectionNode.event = new ExtendableMessageEvent("x", { data: leakDetectionNode });
}
function createMessageEventDataCycle()
{
const leakDetectionNode = document.createTextNode("");
leakDetectionNode.event = new MessageEvent("x", { data: leakDetectionNode });
}
function createPaymentMethodChangeEventMethodDetailsCycle()
{
const leakDetectionNode = document.createTextNode("");
leakDetectionNode.event = new PaymentMethodChangeEvent("x", { methodDetails: leakDetectionNode });
}
function createPromiseRejectionEventPromiseCycle()
{
const leakDetectionNode = document.createTextNode("");
const promise = new Promise(function (resolve, reject) { });
promise.leakDetectionNode = leakDetectionNode;
leakDetectionNode.event = new PromiseRejectionEvent("x", { promise: promise });
}
function createPromiseRejectionEventPromiseFunctionCycle()
{
const leakDetectionNode = document.createTextNode("");
const promise = new Promise(function (resolve, reject) { return leakDetectionNode; });
leakDetectionNode.event = new PromiseRejectionEvent("x", { promise: promise });
}
function createPromiseRejectionEventReasonCycle()
{
const leakDetectionNode = document.createTextNode("");
const promise = new Promise(function (resolve, reject) { });
leakDetectionNode.event = new PromiseRejectionEvent("x", { promise: promise, reason: leakDetectionNode });
}
function createPopStateEventStateCycle()
{
const leakDetectionNode = document.createTextNode("");
leakDetectionNode.event = new PopStateEvent("x", { state: leakDetectionNode });
}
// Reference cycle of IDBRequest and IDBCursor is tested in
// storage/indexeddb/value-cursor-cycle.html and LayoutTests/storage/indexeddb/result-request-cycle.html.
// PaymentResponse details reference cycle is tested in
// http/tests/paymentrequest/payment-response-reference-cycle-leak.https.html.
function createRTCStatsReportCycle()
{
// FIXME: Need to write this test and reorganize so it can be asynchronous.
// Get an RTCStatsReport.
// Get one of the objects from the map.
// Add a property to that object that references the report.
// Add another property to that object that references a leak detection node.
}
function runLeakTest(testFunctionName, underlyingClassName)
{
if (underlyingClassName && !(underlyingClassName in window))
debug('---- Did not test ' + underlyingClassName + ' because it is not enabled.');
else
shouldBeEqualToString('checkForNodeLeaks(' + testFunctionName + ')', 'did not leak');
}
function startTest()
{
if (!window.internals || !internals.numberOfLiveNodes) {
testFailed('Test requires windows.internals, so must be run inside WebKitTestRunner');
return;
}
runLeakTest('emptyFunction');
runLeakTest('createNode');
runLeakTest('createEventListenerCycle');
runLeakTest('createTreeWalkerNodeCycle');
runLeakTest('createTreeWalkerFilterCycle');
runLeakTest('createRangeCycle');
runLeakTest('createStaticRangeCycle');
runLeakTest('createPromiseCycle');
runLeakTest('createCustomEventDetailsCycle');
runLeakTest('createErrorEventDataCycle');
runLeakTest('createExtendableMessageEventDataCycle', 'ExtendableMessageEvent');
runLeakTest('createMessageEventDataCycle');
runLeakTest('createPaymentMethodChangeEventMethodDetailsCycle', 'PaymentMethodChangeEvent');
runLeakTest('createPopStateEventStateCycle');
runLeakTest('createPromiseRejectionEventPromiseCycle');
runLeakTest('createPromiseRejectionEventPromiseFunctionCycle');
runLeakTest('createPromiseRejectionEventReasonCycle');
}
startTest();
</script>