diff options
Diffstat (limited to 'toolkit/components/extensions/test/xpcshell/test_ext_runtime_ports_gc.js')
-rw-r--r-- | toolkit/components/extensions/test/xpcshell/test_ext_runtime_ports_gc.js | 170 |
1 files changed, 170 insertions, 0 deletions
diff --git a/toolkit/components/extensions/test/xpcshell/test_ext_runtime_ports_gc.js b/toolkit/components/extensions/test/xpcshell/test_ext_runtime_ports_gc.js new file mode 100644 index 0000000000..dd47744a97 --- /dev/null +++ b/toolkit/components/extensions/test/xpcshell/test_ext_runtime_ports_gc.js @@ -0,0 +1,170 @@ +"use strict"; + +let gcExperimentAPIs = { + gcHelper: { + schema: "schema.json", + child: { + scopes: ["addon_child"], + script: "child.js", + paths: [["gcHelper"]], + }, + }, +}; + +let gcExperimentFiles = { + "schema.json": JSON.stringify([ + { + namespace: "gcHelper", + functions: [ + { + name: "forceGarbageCollect", + type: "function", + parameters: [], + async: true, + }, + { + name: "registerWitness", + type: "function", + parameters: [ + { + name: "obj", + // Expected type is "object", but using "any" here to ensure that + // the parameter is untouched (not normalized). + type: "any", + }, + ], + returns: { type: "number" }, + }, + { + name: "isGarbageCollected", + type: "function", + parameters: [ + { + name: "witnessId", + description: "return value of registerWitness", + type: "number", + }, + ], + returns: { type: "boolean" }, + }, + ], + }, + ]), + "child.js": () => { + let { setTimeout } = ChromeUtils.importESModule( + "resource://gre/modules/Timer.sys.mjs" + ); + /* globals ExtensionAPI */ + this.gcHelper = class extends ExtensionAPI { + getAPI(context) { + let witnesses = new Map(); + return { + gcHelper: { + async forceGarbageCollect() { + // Logic copied from test_ext_contexts_gc.js + for (let i = 0; i < 3; ++i) { + Cu.forceShrinkingGC(); + Cu.forceCC(); + Cu.forceGC(); + await new Promise(resolve => setTimeout(resolve, 0)); + } + }, + registerWitness(obj) { + let witnessId = witnesses.size; + witnesses.set(witnessId, Cu.getWeakReference(obj)); + return witnessId; + }, + isGarbageCollected(witnessId) { + return witnesses.get(witnessId).get() === null; + }, + }, + }; + } + }; + }, +}; + +// Verify that the experiment is working as intended before using it in tests. +add_task(async function test_gc_experiment() { + let extension = ExtensionTestUtils.loadExtension({ + isPrivileged: true, + manifest: { + experiment_apis: gcExperimentAPIs, + }, + files: gcExperimentFiles, + async background() { + let obj1 = {}; + let obj2 = {}; + let witness1 = browser.gcHelper.registerWitness(obj1); + let witness2 = browser.gcHelper.registerWitness(obj2); + obj1 = null; + await browser.gcHelper.forceGarbageCollect(); + browser.test.assertTrue( + browser.gcHelper.isGarbageCollected(witness1), + "obj1 should have been garbage-collected" + ); + browser.test.assertFalse( + browser.gcHelper.isGarbageCollected(witness2), + "obj2 should not have been garbage-collected" + ); + + browser.test.sendMessage("done"); + }, + }); + + await extension.startup(); + await extension.awaitMessage("done"); + await extension.unload(); +}); + +add_task(async function test_port_gc() { + let extension = ExtensionTestUtils.loadExtension({ + isPrivileged: true, + manifest: { + experiment_apis: gcExperimentAPIs, + }, + files: gcExperimentFiles, + async background() { + let witnessPortSender; + let witnessPortReceiver; + + browser.runtime.onConnect.addListener(port => { + browser.test.assertEq("daName", port.name, "expected port"); + witnessPortReceiver = browser.gcHelper.registerWitness(port); + port.disconnect(); + }); + + // runtime.connect() only triggers onConnect for different contexts, + // so create a frame to have a different context. + // A blank frame in a moz-extension:-document will have access to the + // extension APIs. + let frameWindow = await new Promise(resolve => { + let f = document.createElement("iframe"); + f.onload = () => resolve(f.contentWindow); + document.body.append(f); + }); + await new Promise(resolve => { + let port = frameWindow.browser.runtime.connect({ name: "daName" }); + witnessPortSender = browser.gcHelper.registerWitness(port); + port.onDisconnect.addListener(() => resolve()); + }); + + await browser.gcHelper.forceGarbageCollect(); + + browser.test.assertTrue( + browser.gcHelper.isGarbageCollected(witnessPortSender), + "runtime.connect() port should have been garbage-collected" + ); + browser.test.assertTrue( + browser.gcHelper.isGarbageCollected(witnessPortReceiver), + "runtime.onConnect port should have been garbage-collected" + ); + + browser.test.sendMessage("done"); + }, + }); + + await extension.startup(); + await extension.awaitMessage("done"); + await extension.unload(); +}); |