|
1 <?xml version="1.0"?> |
|
2 <?xml-stylesheet type="text/css" href="chrome://global/skin"?> |
|
3 <?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?> |
|
4 <!-- |
|
5 https://bugzilla.mozilla.org/show_bug.cgi?id=668855 |
|
6 --> |
|
7 <window title="Mozilla Bug " |
|
8 xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> |
|
9 <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/> |
|
10 |
|
11 <!-- test results are displayed in the html:body --> |
|
12 <body xmlns="http://www.w3.org/1999/xhtml"> |
|
13 <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=" |
|
14 target="_blank">Mozilla Bug 668855</a> |
|
15 </body> |
|
16 |
|
17 <!-- test code goes here --> |
|
18 <script type="application/javascript"> |
|
19 <![CDATA[ |
|
20 /** Test for Bug 668855 **/ |
|
21 |
|
22 let Cu = Components.utils; |
|
23 let Ci = Components.interfaces; |
|
24 |
|
25 /* Create a weak reference, with a single-element weak map. */ |
|
26 let make_weak_ref = function (obj) { |
|
27 let m = new WeakMap; |
|
28 m.set(obj, {}); |
|
29 return m; |
|
30 }; |
|
31 |
|
32 /* Check to see if a weak reference is dead. */ |
|
33 let weak_ref_dead = function (r) { |
|
34 return Cu.nondeterministicGetWeakMapKeys(r).length == 0; |
|
35 } |
|
36 |
|
37 /* Deterministically grab an arbitrary DOM element. */ |
|
38 let get_live_dom = function () { |
|
39 let elems = document.getElementsByTagName("a"); |
|
40 return elems[0]; |
|
41 }; |
|
42 |
|
43 |
|
44 /* Test case from bug 653248, adapted into a standard test. |
|
45 |
|
46 This is a dead cycle involving a DOM edge, so the cycle collector can free it. Keys and |
|
47 values reachable only from XPConnect must be marked gray for this to work, and the cycle collector |
|
48 must know the proper structure of the heap. |
|
49 |
|
50 */ |
|
51 let make_gray_loop = function () { |
|
52 let map = new WeakMap; |
|
53 let div = document.createElement("div"); |
|
54 let key = {}; |
|
55 div.setUserData("entrain", {m:map, k:key}, null); |
|
56 //div.entrain = {m:map, k:key}; This is not sufficient to cause a leak in Fx9 |
|
57 map.set(key, div); |
|
58 return make_weak_ref(map); |
|
59 }; |
|
60 |
|
61 let weakref = make_gray_loop(); |
|
62 |
|
63 |
|
64 /* Combinations of live and dead gray maps/keys. */ |
|
65 let basic_weak_ref = null; |
|
66 let basic_map_weak_ref = null; |
|
67 let black_map = new WeakMap; |
|
68 let black_key = {}; |
|
69 |
|
70 let basic_unit_tests = function () { |
|
71 let live_dom = get_live_dom(); |
|
72 let dead_dom = document.createElement("div"); |
|
73 let live_map = new WeakMap; |
|
74 let dead_map = new WeakMap; |
|
75 let live_key = {}; |
|
76 let dead_key = {}; |
|
77 |
|
78 // put the live/dead maps/keys into the appropriate DOM elements |
|
79 live_dom.basic_unit_tests = {m:live_map, k:live_key}; |
|
80 dead_dom.setUserData("hook", {m:dead_map, k:dead_key}, null); |
|
81 // dead_dom.hook = {m:dead_map, k:dead_key}; |
|
82 |
|
83 // Create a dead value, and a weak ref to it. |
|
84 // The loop keeps dead_dom alive unless the CC is smart enough to kill it. |
|
85 let dead_val = {loop:dead_dom}; |
|
86 basic_weak_ref = make_weak_ref(dead_val); |
|
87 basic_map_weak_ref = make_weak_ref(dead_map); |
|
88 |
|
89 // set up the actual entries. most will die. |
|
90 live_map.set(live_key, {my_key:'live_live'}); |
|
91 live_map.set(dead_key, dead_val); |
|
92 live_map.set(black_key, {my_key:'live_black'}); |
|
93 |
|
94 dead_map.set(live_key, dead_val); |
|
95 dead_map.set(dead_key, dead_val); |
|
96 dead_map.set(black_key, dead_val); |
|
97 |
|
98 black_map.set(live_key, {my_key:'black_live'}); |
|
99 black_map.set(dead_key, dead_val); |
|
100 black_map.set(black_key, {my_key:'black_black'}); |
|
101 |
|
102 }; |
|
103 |
|
104 basic_unit_tests(); |
|
105 |
|
106 |
|
107 let check_basic_unit = function () { |
|
108 let live_dom = get_live_dom(); |
|
109 let live_map = live_dom.basic_unit_tests.m; |
|
110 let live_key = live_dom.basic_unit_tests.k; |
|
111 |
|
112 // check the dead elements |
|
113 ok(weak_ref_dead(basic_weak_ref), "Dead value was kept alive."); |
|
114 ok(weak_ref_dead(basic_map_weak_ref), "Dead map was kept alive."); |
|
115 |
|
116 // check the live gray map |
|
117 is(live_map.get(live_key).my_key, 'live_live', |
|
118 "Live key should have the same value in live map."); |
|
119 is(live_map.get(black_key).my_key, 'live_black', |
|
120 "Black key should have the same value in live map."); |
|
121 is(Cu.nondeterministicGetWeakMapKeys(live_map).length, 2, |
|
122 "Live map should have two entries."); |
|
123 |
|
124 // check the live black map |
|
125 is(black_map.get(live_key).my_key, 'black_live', |
|
126 "Live key should have the same value in black map."); |
|
127 is(black_map.get(black_key).my_key, 'black_black', |
|
128 "Black key should have the same value in black map."); |
|
129 is(Cu.nondeterministicGetWeakMapKeys(black_map).length, 2, |
|
130 "Black map should have two entries."); |
|
131 |
|
132 }; |
|
133 |
|
134 |
|
135 /* live gray chained weak map entries, involving the cycle collector. */ |
|
136 let chainm = new WeakMap; |
|
137 let num_chains = 5; |
|
138 |
|
139 let nested_cc_maps = function () { |
|
140 let dom = get_live_dom(); |
|
141 for(let i = 0; i < num_chains; i++) { |
|
142 let k = {count:i}; |
|
143 dom.key = k; |
|
144 dom0 = document.createElement("div"); |
|
145 chainm.set(k, {d:dom0}); |
|
146 dom = document.createElement("div"); |
|
147 dom0.appendChild(dom); |
|
148 }; |
|
149 }; |
|
150 |
|
151 let check_nested_cc_maps = function () { |
|
152 let dom = get_live_dom(); |
|
153 let all_ok = true; |
|
154 for(let i = 0; i < num_chains; i++) { |
|
155 let k = dom.key; |
|
156 all_ok = all_ok && k.count == i; |
|
157 dom = chainm.get(k).d.firstChild; |
|
158 }; |
|
159 ok(all_ok, "Count was invalid on a key in chained weak map entries."); |
|
160 }; |
|
161 |
|
162 nested_cc_maps(); |
|
163 |
|
164 |
|
165 /* black weak map, chained garbage cycle involving DOM */ |
|
166 let garbage_map = new WeakMap; |
|
167 |
|
168 let chained_garbage_maps = function () { |
|
169 let dom0 = document.createElement("div"); |
|
170 let dom = dom0; |
|
171 for(let i = 0; i < num_chains; i++) { |
|
172 let k = {}; |
|
173 dom.key = k; |
|
174 let new_dom = document.createElement("div"); |
|
175 garbage_map.set(k, {val_child:new_dom}); |
|
176 dom = document.createElement("div"); |
|
177 new_dom.appendChild(dom); |
|
178 }; |
|
179 // tie the knot |
|
180 dom.appendChild(dom0); |
|
181 }; |
|
182 |
|
183 chained_garbage_maps(); |
|
184 |
|
185 |
|
186 /* black weak map, chained garbage cycle involving DOM, XPCWN keys */ |
|
187 let wn_garbage_map = new WeakMap; |
|
188 |
|
189 let wn_chained_garbage_maps = function () { |
|
190 let dom0 = document.createElement("div"); |
|
191 let dom = dom0; |
|
192 for(let i = 0; i < num_chains; i++) { |
|
193 let new_dom = document.createElement("div"); |
|
194 wn_garbage_map.set(dom, {wn_val_child:new_dom}); |
|
195 dom = document.createElement("div"); |
|
196 new_dom.appendChild(dom); |
|
197 }; |
|
198 // tie the knot |
|
199 dom.appendChild(dom0); |
|
200 }; |
|
201 |
|
202 wn_chained_garbage_maps(); |
|
203 |
|
204 |
|
205 /* The cycle collector shouldn't remove a live wrapped native key. */ |
|
206 |
|
207 let wn_live_map = new WeakMap; |
|
208 |
|
209 let make_live_map = function () { |
|
210 let live = get_live_dom(); |
|
211 wn_live_map.set(live, {}); |
|
212 ok(wn_live_map.has(get_live_dom()), "Live map should have live DOM node before GC."); |
|
213 } |
|
214 |
|
215 make_live_map(); |
|
216 |
|
217 let unpreservable_native_key = function () { |
|
218 // We only allow natives that support wrapper preservation to be used as weak |
|
219 // map keys. We should be able to try to add unpreservable natives as keys without |
|
220 // crashing (bug 711616), but we should throw an error (bug 761620). |
|
221 |
|
222 let dummy_test_map = new WeakMap; |
|
223 |
|
224 let rule_fail = false; |
|
225 let got_rule = false; |
|
226 try { |
|
227 var rule = document.styleSheets[0].cssRules[0]; |
|
228 got_rule = true; |
|
229 dummy_test_map.set(rule, 1); |
|
230 } catch (e) { |
|
231 rule_fail = true; |
|
232 } |
|
233 ok(got_rule, "Got the CSS rule"); |
|
234 ok(rule_fail, "Using a CSS rule as a weak map key should produce an exception because it can't be wrapper preserved."); |
|
235 |
|
236 } |
|
237 |
|
238 unpreservable_native_key(); |
|
239 |
|
240 /* set up for running precise GC/CC then checking the results */ |
|
241 |
|
242 SimpleTest.waitForExplicitFinish(); |
|
243 |
|
244 Cu.schedulePreciseGC(function () { |
|
245 SpecialPowers.DOMWindowUtils.cycleCollect(); |
|
246 SpecialPowers.DOMWindowUtils.garbageCollect(); |
|
247 SpecialPowers.DOMWindowUtils.garbageCollect(); |
|
248 |
|
249 ok(weak_ref_dead(weakref), "Garbage gray cycle should be collected."); |
|
250 |
|
251 check_nested_cc_maps(); |
|
252 |
|
253 is(Cu.nondeterministicGetWeakMapKeys(garbage_map).length, 0, "Chained garbage weak map entries should not leak."); |
|
254 |
|
255 check_basic_unit(); |
|
256 |
|
257 // fixed by Bug 680937 |
|
258 is(Cu.nondeterministicGetWeakMapKeys(wn_garbage_map).length, 0, |
|
259 "Chained garbage WN weak map entries should not leak."); |
|
260 |
|
261 // fixed by Bug 680937 |
|
262 is(Cu.nondeterministicGetWeakMapKeys(wn_live_map).length, 1, |
|
263 "Live weak map wrapped native key should not be removed."); |
|
264 |
|
265 ok(wn_live_map.has(get_live_dom()), "Live map should have live dom."); |
|
266 |
|
267 SimpleTest.finish(); |
|
268 }); |
|
269 |
|
270 ]]> |
|
271 </script> |
|
272 </window> |