Bug 1915045 Ensure decode tasks are scheduled on BufferingState::Enter() r=media...
[gecko.git] / js / xpconnect / tests / browser / browser_weak_xpcwjs.js
blobb8c8c2f85db8555887b73cc47d2f389fd49adb8d
1 /* Any copyright is dedicated to the Public Domain.
2    http://creativecommons.org/publicdomain/zero/1.0/ */
4 "use strict";
6 // Some basic tests of the lifetime of an XPCWJS with a weak reference.
8 // Create a weak reference, with a single-element weak map.
9 let make_weak_ref = function (obj) {
10   let m = new WeakMap();
11   m.set(obj, {});
12   return m;
15 // Check to see if a weak reference is dead.
16 let weak_ref_dead = function (r) {
17   return !SpecialPowers.nondeterministicGetWeakMapKeys(r).length;
20 add_task(async function gc_wwjs() {
21   // This subtest checks that a WJS with only a weak reference to it gets
22   // cleaned up, if its JS object is garbage, after just a GC.
23   // For the browser, this probably isn't important, but tests seem to rely
24   // on it.
25   const TEST_PREF = "wjs.pref1";
26   let wjs_weak_ref = null;
27   let observed_count = 0;
29   {
30     Services.prefs.clearUserPref(TEST_PREF);
32     // Create the observer object.
33     let observer1 = {
34       QueryInterface: ChromeUtils.generateQI(["nsISupportsWeakReference"]),
35       observe() {
36         observed_count += 1;
37         info(TEST_PREF + " pref observer.");
38       },
39     };
41     // Register the weak observer.
42     Services.prefs.addObserver(TEST_PREF, observer1, true);
44     // Invoke the observer to make sure it is doing something.
45     info("Flipping the pref " + TEST_PREF);
46     Services.prefs.setBoolPref(TEST_PREF, true);
47     is(observed_count, 1, "Ran observer1 once after first flip.");
49     wjs_weak_ref = make_weak_ref(observer1);
51     // Exit the scope, making observer1 garbage.
52   }
54   // Run the GC.
55   info("Running the GC.");
56   SpecialPowers.forceGC();
58   // Flip the pref again to make sure that the observer doesn't run.
59   info("Flipping the pref " + TEST_PREF);
60   Services.prefs.setBoolPref(TEST_PREF, false);
62   is(observed_count, 1, "After GC, don't run the observer.");
63   ok(weak_ref_dead(wjs_weak_ref), "WJS with weak ref should be freed.");
65   Services.prefs.clearUserPref(TEST_PREF);
66 });
68 add_task(async function alive_wwjs() {
69   // This subtest checks that a WJS with only a weak reference should not get
70   // cleaned up if the underlying JS object is held alive (here, via the
71   // variable |observer2|).
72   const TEST_PREF = "wjs.pref2";
73   let observed_count = 0;
75   Services.prefs.clearUserPref(TEST_PREF);
76   let observer2 = {
77     QueryInterface: ChromeUtils.generateQI(["nsISupportsWeakReference"]),
78     observe() {
79       observed_count += 1;
80       info(TEST_PREF + " pref observer");
81     },
82   };
83   Services.prefs.addObserver(TEST_PREF, observer2, true);
85   Services.prefs.setBoolPref(TEST_PREF, true);
86   is(observed_count, 1, "Run observer2 once after first flip.");
88   await new Promise(resolve =>
89     SpecialPowers.exactGC(() => {
90       SpecialPowers.forceCC();
91       SpecialPowers.forceGC();
92       SpecialPowers.forceCC();
94       Services.prefs.setBoolPref(TEST_PREF, false);
96       is(observed_count, 2, "Run observer2 again after second flip.");
98       Services.prefs.removeObserver(TEST_PREF, observer2);
99       Services.prefs.clearUserPref(TEST_PREF);
101       resolve();
102     })
103   );
106 add_task(async function cc_wwjs() {
107   // This subtest checks that a WJS with only a weak reference to it, where the
108   // underlying JS object is part of a garbage cycle, gets cleaned up after a
109   // cycle collection. It also checks that things held alive by the JS object
110   // don't end up in an unlinked state, although that's mostly for fun, because
111   // it is redundant with checking that the JS object gets cleaned up.
112   const TEST_PREF = "wjs.pref3";
113   let wjs_weak_ref = null;
114   let observed_count = 0;
115   let canary_count;
117   {
118     Services.prefs.clearUserPref(TEST_PREF);
120     // Set up a canary object that lets us detect unlinking.
121     // (When an nsArrayCC is unlinked, all of the elements are removed.)
122     // This is needed to distinguish the case where the observer was unlinked
123     // without removing the weak reference from the case where we did not
124     // collect the observer at all.
125     let canary = Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray);
126     let someString = Cc["@mozilla.org/supports-string;1"].createInstance(
127       Ci.nsISupportsString
128     );
129     someString.data = "canary";
130     canary.appendElement(someString);
131     canary.appendElement(someString);
132     is(canary.Count(), 2, "The canary array should have two elements");
134     // Create the observer object.
135     let observer3 = {
136       QueryInterface: ChromeUtils.generateQI(["nsISupportsWeakReference"]),
137       canary,
138       cycle: new DOMMatrix(),
139       observe() {
140         observed_count += 1;
141         canary_count = this.canary.Count();
142         info(TEST_PREF + " pref observer. Canary count: " + canary_count);
143       },
144     };
146     // Set up a cycle between C++ and JS that requires the CC to collect.
147     // |cycle| is a random WebIDL object that we can set an expando on to
148     // create a nice clean cycle that doesn't involve any weird XPConnect stuff.
149     observer3.cycle.backEdge = observer3;
151     // Register the weak observer.
152     Services.prefs.addObserver(TEST_PREF, observer3, true);
154     // Invoke the observer to make sure it is doing something.
155     info("Flipping the pref " + TEST_PREF);
156     canary_count = -1;
157     Services.prefs.setBoolPref(TEST_PREF, true);
158     is(
159       canary_count,
160       2,
161       "Observer ran with expected value while observer3 is alive."
162     );
163     is(observed_count, 1, "Ran observer3 once after first flip.");
165     wjs_weak_ref = make_weak_ref(observer3);
167     // Exit the scope, making observer3 and canary garbage.
168   }
170   // Run the GC. This is necessary to mark observer3 gray so the CC
171   // might consider it to be garbage. This won't free it because it is held
172   // alive from C++ (namely the DOMMatrix via its expando).
173   info("Running the GC.");
174   SpecialPowers.forceGC();
176   // Note: Don't flip the pref here. Doing so will run the observer, which will
177   // cause it to get marked black again, preventing it from being freed.
178   // For the same reason, don't call weak_ref_dead(wjs_weak_ref) here.
180   // Run the CC. This should detect that the cycle between observer3 and the
181   // DOMMatrix is garbage, unlinking the DOMMatrix and the canary. Also, the
182   // weak reference for the WJS for observer3 should get cleared because the
183   // underlying JS object has been identifed as garbage. You can add logging to
184   // nsArrayCC's unlink method to see the canary getting unlinked.
185   info("Running the CC.");
186   SpecialPowers.forceCC();
188   // Flip the pref again to make sure that the observer doesn't run.
189   info("Flipping the pref " + TEST_PREF);
190   canary_count = -1;
191   Services.prefs.setBoolPref(TEST_PREF, false);
193   isnot(
194     canary_count,
195     0,
196     "After CC, don't run the observer with an unlinked canary."
197   );
198   isnot(
199     canary_count,
200     2,
201     "After CC, don't run the observer after it is garbage."
202   );
203   is(canary_count, -1, "After CC, don't run the observer.");
204   is(observed_count, 1, "After CC, don't run the observer.");
206   ok(
207     !weak_ref_dead(wjs_weak_ref),
208     "WJS with weak ref shouldn't be freed by the CC."
209   );
211   // Now that the CC has identified observer3 as garbage, running the GC again
212   // should free it.
213   info("Running the GC again.");
214   SpecialPowers.forceGC();
216   ok(weak_ref_dead(wjs_weak_ref), "WJS with weak ref should be freed.");
218   info("Flipping the pref " + TEST_PREF);
219   canary_count = -1;
220   Services.prefs.setBoolPref(TEST_PREF, true);
222   // Note: the original implementation of weak references for WJS fails most of
223   // the prior canary_count tests, but passes these.
224   isnot(
225     canary_count,
226     0,
227     "After GC, don't run the observer with an unlinked canary."
228   );
229   isnot(
230     canary_count,
231     2,
232     "After GC, don't run the observer after it is garbage."
233   );
234   is(canary_count, -1, "After GC, don't run the observer.");
235   is(observed_count, 1, "After GC, don't run the observer.");
237   Services.prefs.clearUserPref(TEST_PREF);