summaryrefslogtreecommitdiffstats
path: root/js/xpconnect/tests
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /js/xpconnect/tests
parentInitial commit. (diff)
downloadfirefox-upstream/124.0.1.tar.xz
firefox-upstream/124.0.1.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--js/xpconnect/tests/browser/browser.toml31
-rw-r--r--js/xpconnect/tests/browser/browser_consoleStack.html21
-rw-r--r--js/xpconnect/tests/browser/browser_date_telemetry.js70
-rw-r--r--js/xpconnect/tests/browser/browser_deadObjectOnUnload.html18
-rw-r--r--js/xpconnect/tests/browser/browser_dead_object.js36
-rw-r--r--js/xpconnect/tests/browser/browser_exception_leak.js76
-rw-r--r--js/xpconnect/tests/browser/browser_freeze_builtins.js27
-rw-r--r--js/xpconnect/tests/browser/browser_import_mapped_jsm.js61
-rw-r--r--js/xpconnect/tests/browser/browser_parent_process_hang_telemetry.js60
-rw-r--r--js/xpconnect/tests/browser/browser_promise_userInteractionHandling.html10
-rw-r--r--js/xpconnect/tests/browser/browser_promise_userInteractionHandling.js50
-rw-r--r--js/xpconnect/tests/browser/browser_realm_key_and_document_domain.js28
-rw-r--r--js/xpconnect/tests/browser/browser_realm_key_object_prototype_frame.html11
-rw-r--r--js/xpconnect/tests/browser/browser_realm_key_object_prototype_top.html12
-rw-r--r--js/xpconnect/tests/browser/browser_realm_key_promise_frame.html17
-rw-r--r--js/xpconnect/tests/browser/browser_realm_key_promise_top.html7
-rw-r--r--js/xpconnect/tests/browser/browser_weak_xpcwjs.js238
-rw-r--r--js/xpconnect/tests/browser/browser_weak_xpcwn.js94
-rw-r--r--js/xpconnect/tests/browser/moz.build7
-rw-r--r--js/xpconnect/tests/chrome/bug503926.xhtml30
-rw-r--r--js/xpconnect/tests/chrome/chrome.toml220
-rw-r--r--js/xpconnect/tests/chrome/file_bug1281071.html13
-rw-r--r--js/xpconnect/tests/chrome/file_bug1530146.html6
-rw-r--r--js/xpconnect/tests/chrome/file_bug1530146_inner.html4
-rw-r--r--js/xpconnect/tests/chrome/file_bug484459.html10
-rw-r--r--js/xpconnect/tests/chrome/file_bug618176.xhtml48
-rw-r--r--js/xpconnect/tests/chrome/file_bug996069.html11
-rw-r--r--js/xpconnect/tests/chrome/file_discardSystemSource.html19
-rw-r--r--js/xpconnect/tests/chrome/file_empty.html2
-rw-r--r--js/xpconnect/tests/chrome/file_evalInSandbox.html1
-rw-r--r--js/xpconnect/tests/chrome/file_expandosharing.jsm12
-rw-r--r--js/xpconnect/tests/chrome/moz.build12
-rw-r--r--js/xpconnect/tests/chrome/outoflinexulscript.js5
-rw-r--r--js/xpconnect/tests/chrome/subscript.js4
-rw-r--r--js/xpconnect/tests/chrome/test_APIExposer.xhtml48
-rw-r--r--js/xpconnect/tests/chrome/test_bug1041626.xhtml60
-rw-r--r--js/xpconnect/tests/chrome/test_bug1042436.xhtml54
-rw-r--r--js/xpconnect/tests/chrome/test_bug1065185.html64
-rw-r--r--js/xpconnect/tests/chrome/test_bug1074863.html31
-rw-r--r--js/xpconnect/tests/chrome/test_bug1092477.xhtml33
-rw-r--r--js/xpconnect/tests/chrome/test_bug1124898.html52
-rw-r--r--js/xpconnect/tests/chrome/test_bug1126911.html40
-rw-r--r--js/xpconnect/tests/chrome/test_bug1281071.xhtml32
-rw-r--r--js/xpconnect/tests/chrome/test_bug1390159.xhtml44
-rw-r--r--js/xpconnect/tests/chrome/test_bug1430164.html31
-rw-r--r--js/xpconnect/tests/chrome/test_bug1516237.html48
-rw-r--r--js/xpconnect/tests/chrome/test_bug1530146.html58
-rw-r--r--js/xpconnect/tests/chrome/test_bug361111.xhtml33
-rw-r--r--js/xpconnect/tests/chrome/test_bug448587.xhtml35
-rw-r--r--js/xpconnect/tests/chrome/test_bug484459.xhtml37
-rw-r--r--js/xpconnect/tests/chrome/test_bug500931.xhtml40
-rw-r--r--js/xpconnect/tests/chrome/test_bug503926.xhtml58
-rw-r--r--js/xpconnect/tests/chrome/test_bug533596.xhtml58
-rw-r--r--js/xpconnect/tests/chrome/test_bug571849.xhtml44
-rw-r--r--js/xpconnect/tests/chrome/test_bug610390.xhtml32
-rw-r--r--js/xpconnect/tests/chrome/test_bug614757.xhtml33
-rw-r--r--js/xpconnect/tests/chrome/test_bug616992.xhtml30
-rw-r--r--js/xpconnect/tests/chrome/test_bug618176.xhtml30
-rw-r--r--js/xpconnect/tests/chrome/test_bug654370.xhtml27
-rw-r--r--js/xpconnect/tests/chrome/test_bug658560.xhtml38
-rw-r--r--js/xpconnect/tests/chrome/test_bug658909.xhtml92
-rw-r--r--js/xpconnect/tests/chrome/test_bug664689.xhtml28
-rw-r--r--js/xpconnect/tests/chrome/test_bug679861.xhtml38
-rw-r--r--js/xpconnect/tests/chrome/test_bug706301.xhtml52
-rw-r--r--js/xpconnect/tests/chrome/test_bug720619.xhtml46
-rw-r--r--js/xpconnect/tests/chrome/test_bug726949.xhtml41
-rw-r--r--js/xpconnect/tests/chrome/test_bug732665.xhtml92
-rw-r--r--js/xpconnect/tests/chrome/test_bug732665_meta.js34
-rw-r--r--js/xpconnect/tests/chrome/test_bug738244.xhtml58
-rw-r--r--js/xpconnect/tests/chrome/test_bug743843.xhtml39
-rw-r--r--js/xpconnect/tests/chrome/test_bug760076.xhtml49
-rw-r--r--js/xpconnect/tests/chrome/test_bug760131.html48
-rw-r--r--js/xpconnect/tests/chrome/test_bug763343.xhtml35
-rw-r--r--js/xpconnect/tests/chrome/test_bug771429.xhtml66
-rw-r--r--js/xpconnect/tests/chrome/test_bug773962.xhtml88
-rw-r--r--js/xpconnect/tests/chrome/test_bug792280.xhtml43
-rw-r--r--js/xpconnect/tests/chrome/test_bug793433.xhtml44
-rw-r--r--js/xpconnect/tests/chrome/test_bug795275.xhtml80
-rw-r--r--js/xpconnect/tests/chrome/test_bug799348.xhtml47
-rw-r--r--js/xpconnect/tests/chrome/test_bug801241.xhtml48
-rw-r--r--js/xpconnect/tests/chrome/test_bug812415.xhtml90
-rw-r--r--js/xpconnect/tests/chrome/test_bug853283.xhtml40
-rw-r--r--js/xpconnect/tests/chrome/test_bug853571.xhtml62
-rw-r--r--js/xpconnect/tests/chrome/test_bug858101.xhtml55
-rw-r--r--js/xpconnect/tests/chrome/test_bug860494.xhtml57
-rw-r--r--js/xpconnect/tests/chrome/test_bug865948.xhtml35
-rw-r--r--js/xpconnect/tests/chrome/test_bug866823.xhtml49
-rw-r--r--js/xpconnect/tests/chrome/test_bug895340.xhtml50
-rw-r--r--js/xpconnect/tests/chrome/test_bug932906.xhtml69
-rw-r--r--js/xpconnect/tests/chrome/test_bug996069.xhtml52
-rw-r--r--js/xpconnect/tests/chrome/test_chrometoSource.xhtml70
-rw-r--r--js/xpconnect/tests/chrome/test_cloneInto.xhtml194
-rw-r--r--js/xpconnect/tests/chrome/test_cows.xhtml207
-rw-r--r--js/xpconnect/tests/chrome/test_discardSystemSource.xhtml81
-rw-r--r--js/xpconnect/tests/chrome/test_documentdomain.xhtml100
-rw-r--r--js/xpconnect/tests/chrome/test_doublewrappedcompartments.xhtml41
-rw-r--r--js/xpconnect/tests/chrome/test_envChain_event_handler.html137
-rw-r--r--js/xpconnect/tests/chrome/test_evalInSandbox.xhtml205
-rw-r--r--js/xpconnect/tests/chrome/test_evalInWindow.xhtml71
-rw-r--r--js/xpconnect/tests/chrome/test_exnstack.xhtml68
-rw-r--r--js/xpconnect/tests/chrome/test_expandosharing.xhtml147
-rw-r--r--js/xpconnect/tests/chrome/test_exposeInDerived.xhtml45
-rw-r--r--js/xpconnect/tests/chrome/test_inlineScripts.html53
-rw-r--r--js/xpconnect/tests/chrome/test_localstorage_with_nsEp.xhtml37
-rw-r--r--js/xpconnect/tests/chrome/test_matches.xhtml49
-rw-r--r--js/xpconnect/tests/chrome/test_nodelists.xhtml49
-rw-r--r--js/xpconnect/tests/chrome/test_nsScriptErrorWithStack.html59
-rw-r--r--js/xpconnect/tests/chrome/test_onGarbageCollection.html48
-rw-r--r--js/xpconnect/tests/chrome/test_precisegc.xhtml26
-rw-r--r--js/xpconnect/tests/chrome/test_private_field_cows.xhtml131
-rw-r--r--js/xpconnect/tests/chrome/test_sandboxImport.xhtml37
-rw-r--r--js/xpconnect/tests/chrome/test_scriptSettings.xhtml127
-rw-r--r--js/xpconnect/tests/chrome/test_scripterror.html87
-rw-r--r--js/xpconnect/tests/chrome/test_secureContexts.html58
-rw-r--r--js/xpconnect/tests/chrome/test_sharedChromeCompartment.html63
-rw-r--r--js/xpconnect/tests/chrome/test_weakmap_keys_preserved.xhtml33
-rw-r--r--js/xpconnect/tests/chrome/test_weakmap_keys_preserved2.xhtml80
-rw-r--r--js/xpconnect/tests/chrome/test_weakref.xhtml32
-rw-r--r--js/xpconnect/tests/chrome/test_windowProxyDeadWrapper.html78
-rw-r--r--js/xpconnect/tests/chrome/test_wrappers.xhtml85
-rw-r--r--js/xpconnect/tests/chrome/test_xrayLargeTypedArray.html47
-rw-r--r--js/xpconnect/tests/chrome/test_xrayToJS.xhtml1200
-rw-r--r--js/xpconnect/tests/chrome/test_xrayic.xhtml81
-rw-r--r--js/xpconnect/tests/chrome/utf8_subscript.js5
-rw-r--r--js/xpconnect/tests/chrome/worker_discardSystemSource.js6
-rw-r--r--js/xpconnect/tests/components/native/moz.build24
-rw-r--r--js/xpconnect/tests/components/native/xpctest_attributes.cpp136
-rw-r--r--js/xpconnect/tests/components/native/xpctest_cenums.cpp67
-rw-r--r--js/xpconnect/tests/components/native/xpctest_esmreturncode.cpp20
-rw-r--r--js/xpconnect/tests/components/native/xpctest_module.cpp46
-rw-r--r--js/xpconnect/tests/components/native/xpctest_params.cpp413
-rw-r--r--js/xpconnect/tests/components/native/xpctest_private.h102
-rw-r--r--js/xpconnect/tests/components/native/xpctest_returncode.cpp20
-rw-r--r--js/xpconnect/tests/idl/moz.build17
-rw-r--r--js/xpconnect/tests/idl/xpctest_attributes.idl33
-rw-r--r--js/xpconnect/tests/idl/xpctest_bug809674.idl47
-rw-r--r--js/xpconnect/tests/idl/xpctest_cenums.idl39
-rw-r--r--js/xpconnect/tests/idl/xpctest_esmreturncode.idl45
-rw-r--r--js/xpconnect/tests/idl/xpctest_interfaces.idl27
-rw-r--r--js/xpconnect/tests/idl/xpctest_params.idl120
-rw-r--r--js/xpconnect/tests/idl/xpctest_returncode.idl45
-rw-r--r--js/xpconnect/tests/idl/xpctest_utils.idl19
-rw-r--r--js/xpconnect/tests/marionette/manifest.toml3
-rw-r--r--js/xpconnect/tests/marionette/test_preloader_without_parallel_parsing.py15
-rw-r--r--js/xpconnect/tests/mochitest/bug1681664_helper.js1
-rw-r--r--js/xpconnect/tests/mochitest/bug500931_helper.html8
-rw-r--r--js/xpconnect/tests/mochitest/bug571849_helper.html7
-rw-r--r--js/xpconnect/tests/mochitest/bug589028_helper.html27
-rw-r--r--js/xpconnect/tests/mochitest/bug92773_helper.html7
-rw-r--r--js/xpconnect/tests/mochitest/chrome_wrappers_helper.html29
-rw-r--r--js/xpconnect/tests/mochitest/class_static_worker.js13
-rw-r--r--js/xpconnect/tests/mochitest/file1_bug629227.html32
-rw-r--r--js/xpconnect/tests/mochitest/file2_bug629227.html11
-rw-r--r--js/xpconnect/tests/mochitest/file_bug505915.html10
-rw-r--r--js/xpconnect/tests/mochitest/file_bug605167.html7
-rw-r--r--js/xpconnect/tests/mochitest/file_bug650273.html31
-rw-r--r--js/xpconnect/tests/mochitest/file_bug658560.html4
-rw-r--r--js/xpconnect/tests/mochitest/file_bug706301.html27
-rw-r--r--js/xpconnect/tests/mochitest/file_bug720619.html10
-rw-r--r--js/xpconnect/tests/mochitest/file_bug731471.html5
-rw-r--r--js/xpconnect/tests/mochitest/file_bug738244.html10
-rw-r--r--js/xpconnect/tests/mochitest/file_bug760131.html23
-rw-r--r--js/xpconnect/tests/mochitest/file_bug781476.html15
-rw-r--r--js/xpconnect/tests/mochitest/file_bug789713.html51
-rw-r--r--js/xpconnect/tests/mochitest/file_bug795275.html14
-rw-r--r--js/xpconnect/tests/mochitest/file_bug799348.html11
-rw-r--r--js/xpconnect/tests/mochitest/file_bug802557.html62
-rw-r--r--js/xpconnect/tests/mochitest/file_bug860494.html16
-rw-r--r--js/xpconnect/tests/mochitest/file_crosscompartment_weakmap.html8
-rw-r--r--js/xpconnect/tests/mochitest/file_documentdomain.html41
-rw-r--r--js/xpconnect/tests/mochitest/file_doublewrappedcompartments.html10
-rw-r--r--js/xpconnect/tests/mochitest/file_empty.html3
-rw-r--r--js/xpconnect/tests/mochitest/file_evalInSandbox.html8
-rw-r--r--js/xpconnect/tests/mochitest/file_exnstack.html23
-rw-r--r--js/xpconnect/tests/mochitest/file_expandosharing.html34
-rw-r--r--js/xpconnect/tests/mochitest/file_matches.html1
-rw-r--r--js/xpconnect/tests/mochitest/file_nodelists.html7
-rw-r--r--js/xpconnect/tests/mochitest/file_wrappers-2.html13
-rw-r--r--js/xpconnect/tests/mochitest/file_xrayic.html15
-rw-r--r--js/xpconnect/tests/mochitest/finalizationRegistry_worker.js96
-rw-r--r--js/xpconnect/tests/mochitest/inner.html7
-rw-r--r--js/xpconnect/tests/mochitest/mochitest.toml297
-rw-r--r--js/xpconnect/tests/mochitest/moz.build7
-rw-r--r--js/xpconnect/tests/mochitest/private_field_worker.js21
-rw-r--r--js/xpconnect/tests/mochitest/shadow_realm_module.js1
-rw-r--r--js/xpconnect/tests/mochitest/shadow_realm_worker.js81
-rw-r--r--js/xpconnect/tests/mochitest/test1_bug629331.html19
-rw-r--r--js/xpconnect/tests/mochitest/test2_bug629331.html18
-rw-r--r--js/xpconnect/tests/mochitest/test_bug1005806.html27
-rw-r--r--js/xpconnect/tests/mochitest/test_bug1094930.html29
-rw-r--r--js/xpconnect/tests/mochitest/test_bug1158558.html47
-rw-r--r--js/xpconnect/tests/mochitest/test_bug1448048.html33
-rw-r--r--js/xpconnect/tests/mochitest/test_bug1681664.html42
-rw-r--r--js/xpconnect/tests/mochitest/test_bug384632.html34
-rw-r--r--js/xpconnect/tests/mochitest/test_bug390488.html64
-rw-r--r--js/xpconnect/tests/mochitest/test_bug393269.html46
-rw-r--r--js/xpconnect/tests/mochitest/test_bug396851.html53
-rw-r--r--js/xpconnect/tests/mochitest/test_bug428021.html40
-rw-r--r--js/xpconnect/tests/mochitest/test_bug446584.html47
-rw-r--r--js/xpconnect/tests/mochitest/test_bug462428.html51
-rw-r--r--js/xpconnect/tests/mochitest/test_bug478438.html65
-rw-r--r--js/xpconnect/tests/mochitest/test_bug484107.html99
-rw-r--r--js/xpconnect/tests/mochitest/test_bug500691.html27
-rw-r--r--js/xpconnect/tests/mochitest/test_bug505915.html49
-rw-r--r--js/xpconnect/tests/mochitest/test_bug560351.html36
-rw-r--r--js/xpconnect/tests/mochitest/test_bug585745.html43
-rw-r--r--js/xpconnect/tests/mochitest/test_bug589028.html62
-rw-r--r--js/xpconnect/tests/mochitest/test_bug601299.html18
-rw-r--r--js/xpconnect/tests/mochitest/test_bug605167.html56
-rw-r--r--js/xpconnect/tests/mochitest/test_bug618017.html28
-rw-r--r--js/xpconnect/tests/mochitest/test_bug623437.html43
-rw-r--r--js/xpconnect/tests/mochitest/test_bug628410.html34
-rw-r--r--js/xpconnect/tests/mochitest/test_bug628794.html43
-rw-r--r--js/xpconnect/tests/mochitest/test_bug629227.html47
-rw-r--r--js/xpconnect/tests/mochitest/test_bug629331.html37
-rw-r--r--js/xpconnect/tests/mochitest/test_bug636097.html62
-rw-r--r--js/xpconnect/tests/mochitest/test_bug650273.html42
-rw-r--r--js/xpconnect/tests/mochitest/test_bug655297-1.html49
-rw-r--r--js/xpconnect/tests/mochitest/test_bug655297-2.html49
-rw-r--r--js/xpconnect/tests/mochitest/test_bug661980.html61
-rw-r--r--js/xpconnect/tests/mochitest/test_bug691059.html59
-rw-r--r--js/xpconnect/tests/mochitest/test_bug720619.html55
-rw-r--r--js/xpconnect/tests/mochitest/test_bug731471.html42
-rw-r--r--js/xpconnect/tests/mochitest/test_bug764389.html40
-rw-r--r--js/xpconnect/tests/mochitest/test_bug772288.html50
-rw-r--r--js/xpconnect/tests/mochitest/test_bug781476.html36
-rw-r--r--js/xpconnect/tests/mochitest/test_bug789713.html39
-rw-r--r--js/xpconnect/tests/mochitest/test_bug790732.html55
-rw-r--r--js/xpconnect/tests/mochitest/test_bug793969.html53
-rw-r--r--js/xpconnect/tests/mochitest/test_bug800864.html51
-rw-r--r--js/xpconnect/tests/mochitest/test_bug802557.html116
-rw-r--r--js/xpconnect/tests/mochitest/test_bug803730.html41
-rw-r--r--js/xpconnect/tests/mochitest/test_bug809547.html42
-rw-r--r--js/xpconnect/tests/mochitest/test_bug829872.html52
-rw-r--r--js/xpconnect/tests/mochitest/test_bug862380.html54
-rw-r--r--js/xpconnect/tests/mochitest/test_bug865260.html33
-rw-r--r--js/xpconnect/tests/mochitest/test_bug871887.html43
-rw-r--r--js/xpconnect/tests/mochitest/test_bug912322.html35
-rw-r--r--js/xpconnect/tests/mochitest/test_bug916945.html78
-rw-r--r--js/xpconnect/tests/mochitest/test_bug92773.html43
-rw-r--r--js/xpconnect/tests/mochitest/test_bug940783.html62
-rw-r--r--js/xpconnect/tests/mochitest/test_bug960820.html56
-rw-r--r--js/xpconnect/tests/mochitest/test_bug965082.html39
-rw-r--r--js/xpconnect/tests/mochitest/test_bug993423.html47
-rw-r--r--js/xpconnect/tests/mochitest/test_class_static_block_worker.html32
-rw-r--r--js/xpconnect/tests/mochitest/test_crosscompartment_weakmap.html36
-rw-r--r--js/xpconnect/tests/mochitest/test_enable_privilege.html26
-rw-r--r--js/xpconnect/tests/mochitest/test_finalizationRegistry.html168
-rw-r--r--js/xpconnect/tests/mochitest/test_finalizationRegistryInWorker.html40
-rw-r--r--js/xpconnect/tests/mochitest/test_finalizationRegistry_cleanupSome.html13
-rw-r--r--js/xpconnect/tests/mochitest/test_finalizationRegistry_incumbent.html62
-rw-r--r--js/xpconnect/tests/mochitest/test_frameWrapping.html37
-rw-r--r--js/xpconnect/tests/mochitest/test_getWebIDLCaller.html49
-rw-r--r--js/xpconnect/tests/mochitest/test_getweakmapkeys.html59
-rw-r--r--js/xpconnect/tests/mochitest/test_isRemoteProxy.html55
-rw-r--r--js/xpconnect/tests/mochitest/test_nukeContentWindow.html75
-rw-r--r--js/xpconnect/tests/mochitest/test_paris_weakmap_keys.html94
-rw-r--r--js/xpconnect/tests/mochitest/test_private_field_dom.html221
-rw-r--r--js/xpconnect/tests/mochitest/test_private_field_worker.html27
-rw-r--r--js/xpconnect/tests/mochitest/test_sameOriginPolicy.html109
-rw-r--r--js/xpconnect/tests/mochitest/test_sandbox_fetch.html54
-rw-r--r--js/xpconnect/tests/mochitest/test_shadowRealm.html34
-rw-r--r--js/xpconnect/tests/mochitest/test_shadowRealm_worker.html63
-rw-r--r--js/xpconnect/tests/mochitest/test_spectre_mitigations.html28
-rw-r--r--js/xpconnect/tests/mochitest/test_weakRefs.html80
-rw-r--r--js/xpconnect/tests/mochitest/test_weakRefs_collected_wrapper.html52
-rw-r--r--js/xpconnect/tests/mochitest/test_weakRefs_cross_compartment.html68
-rw-r--r--js/xpconnect/tests/mochitest/test_weakmaps.html264
-rw-r--r--js/xpconnect/tests/moz.build23
-rw-r--r--js/xpconnect/tests/unit/CatBackgroundTaskRegistrationComponents.manifest4
-rw-r--r--js/xpconnect/tests/unit/CatRegistrationComponents.manifest2
-rw-r--r--js/xpconnect/tests/unit/ReturnCodeChild.jsm51
-rw-r--r--js/xpconnect/tests/unit/ReturnCodeChild.sys.mjs49
-rw-r--r--js/xpconnect/tests/unit/TestBlob.jsm48
-rw-r--r--js/xpconnect/tests/unit/TestFile.jsm78
-rw-r--r--js/xpconnect/tests/unit/api_script.js26
-rw-r--r--js/xpconnect/tests/unit/bogus_element_type.jsm1
-rw-r--r--js/xpconnect/tests/unit/bogus_exports_type.jsm1
-rw-r--r--js/xpconnect/tests/unit/bug451678_subscript.js5
-rw-r--r--js/xpconnect/tests/unit/contextual.sys.mjs3
-rw-r--r--js/xpconnect/tests/unit/contextual_worker.js14
-rw-r--r--js/xpconnect/tests/unit/envChain.jsm20
-rw-r--r--js/xpconnect/tests/unit/envChain_subscript.jsm27
-rw-r--r--js/xpconnect/tests/unit/environment_checkscript.jsm13
-rw-r--r--js/xpconnect/tests/unit/environment_loadscript.jsm16
-rw-r--r--js/xpconnect/tests/unit/environment_script.js14
-rw-r--r--js/xpconnect/tests/unit/error_export.sys.mjs2
-rw-r--r--js/xpconnect/tests/unit/error_import.sys.mjs1
-rw-r--r--js/xpconnect/tests/unit/error_other.sys.mjs1
-rw-r--r--js/xpconnect/tests/unit/es6import.js1
-rw-r--r--js/xpconnect/tests/unit/es6module.js6
-rw-r--r--js/xpconnect/tests/unit/es6module_absolute.js4
-rw-r--r--js/xpconnect/tests/unit/es6module_absolute2.js1
-rw-r--r--js/xpconnect/tests/unit/es6module_cycle_a.js9
-rw-r--r--js/xpconnect/tests/unit/es6module_cycle_b.js9
-rw-r--r--js/xpconnect/tests/unit/es6module_cycle_c.js9
-rw-r--r--js/xpconnect/tests/unit/es6module_devtoolsLoader.js1
-rw-r--r--js/xpconnect/tests/unit/es6module_devtoolsLoader.sys.mjs29
-rw-r--r--js/xpconnect/tests/unit/es6module_devtoolsLoader_only.js1
-rw-r--r--js/xpconnect/tests/unit/es6module_dynamic_import.js25
-rw-r--r--js/xpconnect/tests/unit/es6module_dynamic_import2.js1
-rw-r--r--js/xpconnect/tests/unit/es6module_dynamic_import3.js1
-rw-r--r--js/xpconnect/tests/unit/es6module_dynamic_import_missing.js17
-rw-r--r--js/xpconnect/tests/unit/es6module_dynamic_import_runtime_error.js17
-rw-r--r--js/xpconnect/tests/unit/es6module_dynamic_import_runtime_error2.js2
-rw-r--r--js/xpconnect/tests/unit/es6module_dynamic_import_runtime_error3.js2
-rw-r--r--js/xpconnect/tests/unit/es6module_dynamic_import_static.js9
-rw-r--r--js/xpconnect/tests/unit/es6module_dynamic_import_syntax_error.js17
-rw-r--r--js/xpconnect/tests/unit/es6module_dynamic_import_syntax_error2.js1
-rw-r--r--js/xpconnect/tests/unit/es6module_dynamic_import_syntax_error3.js1
-rw-r--r--js/xpconnect/tests/unit/es6module_import_error.js1
-rw-r--r--js/xpconnect/tests/unit/es6module_import_error2.js1
-rw-r--r--js/xpconnect/tests/unit/es6module_loaded-1.sys.mjs1
-rw-r--r--js/xpconnect/tests/unit/es6module_loaded-2.sys.mjs1
-rw-r--r--js/xpconnect/tests/unit/es6module_loaded-3.sys.mjs1
-rw-r--r--js/xpconnect/tests/unit/es6module_missing_import.js1
-rw-r--r--js/xpconnect/tests/unit/es6module_parse_error.js1
-rw-r--r--js/xpconnect/tests/unit/es6module_parse_error_in_import.js1
-rw-r--r--js/xpconnect/tests/unit/es6module_throws.js4
-rw-r--r--js/xpconnect/tests/unit/es6module_top_level_await.js5
-rw-r--r--js/xpconnect/tests/unit/esm_lazy-1.sys.mjs4
-rw-r--r--js/xpconnect/tests/unit/esm_lazy-2.sys.mjs4
-rw-r--r--js/xpconnect/tests/unit/esmified-1.sys.mjs4
-rw-r--r--js/xpconnect/tests/unit/esmified-2.sys.mjs4
-rw-r--r--js/xpconnect/tests/unit/esmified-3.sys.mjs4
-rw-r--r--js/xpconnect/tests/unit/esmified-4.sys.mjs4
-rw-r--r--js/xpconnect/tests/unit/esmified-5.sys.mjs4
-rw-r--r--js/xpconnect/tests/unit/esmified-6.sys.mjs4
-rw-r--r--js/xpconnect/tests/unit/esmified-not-exported.sys.mjs13
-rw-r--r--js/xpconnect/tests/unit/file_simple_script.js1
-rw-r--r--js/xpconnect/tests/unit/frame.js1
-rw-r--r--js/xpconnect/tests/unit/head.js32
-rw-r--r--js/xpconnect/tests/unit/head_ongc.js35
-rw-r--r--js/xpconnect/tests/unit/head_watchdog.js116
-rw-r--r--js/xpconnect/tests/unit/import_non_shared_1.mjs1
-rw-r--r--js/xpconnect/tests/unit/import_shared_in_worker.js36
-rw-r--r--js/xpconnect/tests/unit/import_stack.jsm2
-rw-r--r--js/xpconnect/tests/unit/import_stack.sys.mjs1
-rw-r--r--js/xpconnect/tests/unit/import_stack_static_1.sys.mjs1
-rw-r--r--js/xpconnect/tests/unit/import_stack_static_2.sys.mjs2
-rw-r--r--js/xpconnect/tests/unit/import_stack_static_3.sys.mjs2
-rw-r--r--js/xpconnect/tests/unit/import_stack_static_4.sys.mjs1
-rw-r--r--js/xpconnect/tests/unit/importer.jsm1
-rw-r--r--js/xpconnect/tests/unit/jsm_loaded-1.jsm2
-rw-r--r--js/xpconnect/tests/unit/jsm_loaded-2.jsm2
-rw-r--r--js/xpconnect/tests/unit/jsm_loaded-3.jsm2
-rw-r--r--js/xpconnect/tests/unit/lazy_non_shared_in_worker.js28
-rw-r--r--js/xpconnect/tests/unit/lazy_shared_in_worker.js52
-rw-r--r--js/xpconnect/tests/unit/non_shared_1.mjs19
-rw-r--r--js/xpconnect/tests/unit/non_shared_2.mjs5
-rw-r--r--js/xpconnect/tests/unit/non_shared_nest_import_non_shared_1.mjs7
-rw-r--r--js/xpconnect/tests/unit/non_shared_nest_import_non_shared_2.mjs5
-rw-r--r--js/xpconnect/tests/unit/non_shared_nest_import_non_shared_3.mjs14
-rw-r--r--js/xpconnect/tests/unit/non_shared_nest_import_non_shared_target_1.mjs3
-rw-r--r--js/xpconnect/tests/unit/non_shared_nest_import_non_shared_target_2.mjs3
-rw-r--r--js/xpconnect/tests/unit/non_shared_nest_import_non_shared_target_3.mjs3
-rw-r--r--js/xpconnect/tests/unit/non_shared_nest_import_shared_1.mjs7
-rw-r--r--js/xpconnect/tests/unit/non_shared_nest_import_shared_target_1.sys.mjs3
-rw-r--r--js/xpconnect/tests/unit/non_shared_nest_import_shared_target_2.sys.mjs3
-rw-r--r--js/xpconnect/tests/unit/non_shared_worker_1.js11
-rw-r--r--js/xpconnect/tests/unit/not-esmified-not-exported.jsm20
-rw-r--r--js/xpconnect/tests/unit/recursive_importA.jsm12
-rw-r--r--js/xpconnect/tests/unit/recursive_importB.jsm13
-rw-r--r--js/xpconnect/tests/unit/sync_and_async_in_worker.js124
-rw-r--r--js/xpconnect/tests/unit/syntax_error.jsm1
-rw-r--r--js/xpconnect/tests/unit/test_ComponentEnvironment.js20
-rw-r--r--js/xpconnect/tests/unit/test_Cu_reportError_column.js57
-rw-r--r--js/xpconnect/tests/unit/test_FrameScriptEnvironment.js46
-rw-r--r--js/xpconnect/tests/unit/test_ReadableStream_from.js28
-rw-r--r--js/xpconnect/tests/unit/test_SubscriptLoaderEnvironment.js38
-rw-r--r--js/xpconnect/tests/unit/test_SubscriptLoaderJSMEnvironment.js32
-rw-r--r--js/xpconnect/tests/unit/test_SubscriptLoaderSandboxEnvironment.js35
-rw-r--r--js/xpconnect/tests/unit/test_URLSearchParams.js12
-rw-r--r--js/xpconnect/tests/unit/test_allowWaivers.js29
-rw-r--r--js/xpconnect/tests/unit/test_allowedDomains.js41
-rw-r--r--js/xpconnect/tests/unit/test_allowedDomainsXHR.js135
-rw-r--r--js/xpconnect/tests/unit/test_attributes.js103
-rw-r--r--js/xpconnect/tests/unit/test_blob.js8
-rw-r--r--js/xpconnect/tests/unit/test_blob2.js34
-rw-r--r--js/xpconnect/tests/unit/test_bogus_files.js32
-rw-r--r--js/xpconnect/tests/unit/test_bug1001094.js4
-rw-r--r--js/xpconnect/tests/unit/test_bug1021312.js15
-rw-r--r--js/xpconnect/tests/unit/test_bug1033253.js5
-rw-r--r--js/xpconnect/tests/unit/test_bug1033920.js6
-rw-r--r--js/xpconnect/tests/unit/test_bug1033927.js8
-rw-r--r--js/xpconnect/tests/unit/test_bug1034262.js8
-rw-r--r--js/xpconnect/tests/unit/test_bug1081990.js9
-rw-r--r--js/xpconnect/tests/unit/test_bug1110546.js4
-rw-r--r--js/xpconnect/tests/unit/test_bug1131707.js20
-rw-r--r--js/xpconnect/tests/unit/test_bug1150771.js12
-rw-r--r--js/xpconnect/tests/unit/test_bug1151385.js9
-rw-r--r--js/xpconnect/tests/unit/test_bug1170311.js4
-rw-r--r--js/xpconnect/tests/unit/test_bug1244222.js31
-rw-r--r--js/xpconnect/tests/unit/test_bug1617527.js17
-rw-r--r--js/xpconnect/tests/unit/test_bug267645.js62
-rw-r--r--js/xpconnect/tests/unit/test_bug408412.js12
-rw-r--r--js/xpconnect/tests/unit/test_bug451678.js15
-rw-r--r--js/xpconnect/tests/unit/test_bug604362.js10
-rw-r--r--js/xpconnect/tests/unit/test_bug677864.js9
-rw-r--r--js/xpconnect/tests/unit/test_bug711404.js7
-rw-r--r--js/xpconnect/tests/unit/test_bug742444.js16
-rw-r--r--js/xpconnect/tests/unit/test_bug778409.js10
-rw-r--r--js/xpconnect/tests/unit/test_bug780370.js16
-rw-r--r--js/xpconnect/tests/unit/test_bug809652.js62
-rw-r--r--js/xpconnect/tests/unit/test_bug809674.js76
-rw-r--r--js/xpconnect/tests/unit/test_bug813901.js23
-rw-r--r--js/xpconnect/tests/unit/test_bug845201.js18
-rw-r--r--js/xpconnect/tests/unit/test_bug845862.js7
-rw-r--r--js/xpconnect/tests/unit/test_bug849730.js5
-rw-r--r--js/xpconnect/tests/unit/test_bug851895.js9
-rw-r--r--js/xpconnect/tests/unit/test_bug853709.js30
-rw-r--r--js/xpconnect/tests/unit/test_bug856067.js8
-rw-r--r--js/xpconnect/tests/unit/test_bug867486.js8
-rw-r--r--js/xpconnect/tests/unit/test_bug868675.js29
-rw-r--r--js/xpconnect/tests/unit/test_bug872772.js33
-rw-r--r--js/xpconnect/tests/unit/test_bug885800.js11
-rw-r--r--js/xpconnect/tests/unit/test_bug930091.js27
-rw-r--r--js/xpconnect/tests/unit/test_bug976151.js23
-rw-r--r--js/xpconnect/tests/unit/test_bug_442086.js36
-rw-r--r--js/xpconnect/tests/unit/test_callFunctionWithAsyncStack.js28
-rw-r--r--js/xpconnect/tests/unit/test_cenums.js58
-rw-r--r--js/xpconnect/tests/unit/test_compileScript.js99
-rw-r--r--js/xpconnect/tests/unit/test_components.js24
-rw-r--r--js/xpconnect/tests/unit/test_crypto.js28
-rw-r--r--js/xpconnect/tests/unit/test_css.js9
-rw-r--r--js/xpconnect/tests/unit/test_deepFreezeClone.js31
-rw-r--r--js/xpconnect/tests/unit/test_defineESModuleGetters.js76
-rw-r--r--js/xpconnect/tests/unit/test_defineESModuleGetters_options.js105
-rw-r--r--js/xpconnect/tests/unit/test_defineESModuleGetters_options_worker.js33
-rw-r--r--js/xpconnect/tests/unit/test_defineModuleGetter.js115
-rw-r--r--js/xpconnect/tests/unit/test_envChain_JSM.js40
-rw-r--r--js/xpconnect/tests/unit/test_envChain_frameScript.js211
-rw-r--r--js/xpconnect/tests/unit/test_envChain_subscript.js72
-rw-r--r--js/xpconnect/tests/unit/test_envChain_subscript_in_JSM.js58
-rw-r--r--js/xpconnect/tests/unit/test_error_to_exception.js58
-rw-r--r--js/xpconnect/tests/unit/test_eventSource.js6
-rw-r--r--js/xpconnect/tests/unit/test_exportFunction.js152
-rw-r--r--js/xpconnect/tests/unit/test_file.js11
-rw-r--r--js/xpconnect/tests/unit/test_file2.js60
-rw-r--r--js/xpconnect/tests/unit/test_fileReader.js12
-rw-r--r--js/xpconnect/tests/unit/test_function_names.js37
-rw-r--r--js/xpconnect/tests/unit/test_generateQI.js29
-rw-r--r--js/xpconnect/tests/unit/test_getCallerLocation.js86
-rw-r--r--js/xpconnect/tests/unit/test_getObjectPrincipal.js6
-rw-r--r--js/xpconnect/tests/unit/test_import.js72
-rw-r--r--js/xpconnect/tests/unit/test_import_devtools_loader.js85
-rw-r--r--js/xpconnect/tests/unit/test_import_es6_modules.js250
-rw-r--r--js/xpconnect/tests/unit/test_import_fail.js10
-rw-r--r--js/xpconnect/tests/unit/test_import_from_sandbox.js82
-rw-r--r--js/xpconnect/tests/unit/test_import_global.js47
-rw-r--r--js/xpconnect/tests/unit/test_import_global_contextual.js38
-rw-r--r--js/xpconnect/tests/unit/test_import_global_contextual_worker.js17
-rw-r--r--js/xpconnect/tests/unit/test_import_global_current.js796
-rw-r--r--js/xpconnect/tests/unit/test_import_global_current_worker.js196
-rw-r--r--js/xpconnect/tests/unit/test_import_global_worker.js21
-rw-r--r--js/xpconnect/tests/unit/test_import_shim.js377
-rw-r--r--js/xpconnect/tests/unit/test_import_stack.js39
-rw-r--r--js/xpconnect/tests/unit/test_import_syntax_error.js23
-rw-r--r--js/xpconnect/tests/unit/test_isModuleLoaded.js20
-rw-r--r--js/xpconnect/tests/unit/test_isProxy.js26
-rw-r--r--js/xpconnect/tests/unit/test_js_memory_telemetry.js53
-rw-r--r--js/xpconnect/tests/unit/test_js_weak_references.js45
-rw-r--r--js/xpconnect/tests/unit/test_loadedESModules.js127
-rw-r--r--js/xpconnect/tests/unit/test_localeCompare.js6
-rw-r--r--js/xpconnect/tests/unit/test_malformed_utf8.js74
-rw-r--r--js/xpconnect/tests/unit/test_messageChannel.js29
-rw-r--r--js/xpconnect/tests/unit/test_nuke_sandbox.js50
-rw-r--r--js/xpconnect/tests/unit/test_nuke_sandbox_event_listeners.js89
-rw-r--r--js/xpconnect/tests/unit/test_nuke_webextension_wrappers.js70
-rw-r--r--js/xpconnect/tests/unit/test_onGarbageCollection-01.js69
-rw-r--r--js/xpconnect/tests/unit/test_onGarbageCollection-02.js99
-rw-r--r--js/xpconnect/tests/unit/test_onGarbageCollection-03.js39
-rw-r--r--js/xpconnect/tests/unit/test_onGarbageCollection-04.js72
-rw-r--r--js/xpconnect/tests/unit/test_onGarbageCollection-05.js42
-rw-r--r--js/xpconnect/tests/unit/test_params.js384
-rw-r--r--js/xpconnect/tests/unit/test_print_stderr.js14
-rw-r--r--js/xpconnect/tests/unit/test_private_field_xrays.js56
-rw-r--r--js/xpconnect/tests/unit/test_promise.js7
-rw-r--r--js/xpconnect/tests/unit/test_recursive_import.js17
-rw-r--r--js/xpconnect/tests/unit/test_reflect_parse.js27
-rw-r--r--js/xpconnect/tests/unit/test_resistFingerprinting_date_now.js16
-rw-r--r--js/xpconnect/tests/unit/test_resolve_dead_promise.js39
-rw-r--r--js/xpconnect/tests/unit/test_returncode.js74
-rw-r--r--js/xpconnect/tests/unit/test_rewrap_dead_wrapper.js31
-rw-r--r--js/xpconnect/tests/unit/test_rtcIdentityProvider.js34
-rw-r--r--js/xpconnect/tests/unit/test_sandbox_DOMException.js10
-rw-r--r--js/xpconnect/tests/unit/test_sandbox_atob.js9
-rw-r--r--js/xpconnect/tests/unit/test_sandbox_metadata.js57
-rw-r--r--js/xpconnect/tests/unit/test_sandbox_name.js26
-rw-r--r--js/xpconnect/tests/unit/test_storage.js12
-rw-r--r--js/xpconnect/tests/unit/test_structuredClone.js32
-rw-r--r--js/xpconnect/tests/unit/test_subScriptLoader.js16
-rw-r--r--js/xpconnect/tests/unit/test_symbols_as_weak_keys.js40
-rw-r--r--js/xpconnect/tests/unit/test_tearoffs.js115
-rw-r--r--js/xpconnect/tests/unit/test_textDecoder.js11
-rw-r--r--js/xpconnect/tests/unit/test_uawidget_scope.js55
-rw-r--r--js/xpconnect/tests/unit/test_uninitialized_lexical.js7
-rw-r--r--js/xpconnect/tests/unit/test_unload.js28
-rw-r--r--js/xpconnect/tests/unit/test_url.js9
-rw-r--r--js/xpconnect/tests/unit/test_want_components.js16
-rw-r--r--js/xpconnect/tests/unit/test_wasm_tailcalls_profiler.js122
-rw-r--r--js/xpconnect/tests/unit/test_watchdog_default.js9
-rw-r--r--js/xpconnect/tests/unit/test_watchdog_disable.js8
-rw-r--r--js/xpconnect/tests/unit/test_watchdog_enable.js8
-rw-r--r--js/xpconnect/tests/unit/test_watchdog_hibernate.js49
-rw-r--r--js/xpconnect/tests/unit/test_watchdog_toggle.js10
-rw-r--r--js/xpconnect/tests/unit/test_weak_keys.js45
-rw-r--r--js/xpconnect/tests/unit/test_wrapped_js_enumerator.js71
-rw-r--r--js/xpconnect/tests/unit/test_xpcomutils.js248
-rw-r--r--js/xpconnect/tests/unit/test_xpcwn_instanceof.js23
-rw-r--r--js/xpconnect/tests/unit/test_xpcwn_tamperproof.js180
-rw-r--r--js/xpconnect/tests/unit/test_xray_SavedFrame-02.js71
-rw-r--r--js/xpconnect/tests/unit/test_xray_SavedFrame.js104
-rw-r--r--js/xpconnect/tests/unit/test_xray_instanceof.js206
-rw-r--r--js/xpconnect/tests/unit/test_xray_named_element_access.js21
-rw-r--r--js/xpconnect/tests/unit/test_xray_regexp.js7
-rw-r--r--js/xpconnect/tests/unit/test_xrayed_arguments.js16
-rw-r--r--js/xpconnect/tests/unit/test_xrayed_iterator.js40
-rw-r--r--js/xpconnect/tests/unit/uninitialized_lexical.jsm2
-rw-r--r--js/xpconnect/tests/unit/xpcshell.toml430
520 files changed, 24412 insertions, 0 deletions
diff --git a/js/xpconnect/tests/browser/browser.toml b/js/xpconnect/tests/browser/browser.toml
new file mode 100644
index 0000000000..c7c72c71e7
--- /dev/null
+++ b/js/xpconnect/tests/browser/browser.toml
@@ -0,0 +1,31 @@
+[DEFAULT]
+support-files = [
+ "browser_consoleStack.html",
+ "browser_deadObjectOnUnload.html",
+ "browser_realm_key_object_prototype_top.html",
+ "browser_realm_key_object_prototype_frame.html",
+ "browser_realm_key_promise_top.html",
+ "browser_realm_key_promise_frame.html",
+ "browser_promise_userInteractionHandling.html"
+]
+
+["browser_date_telemetry.js"]
+
+["browser_dead_object.js"]
+
+["browser_exception_leak.js"]
+
+["browser_freeze_builtins.js"]
+
+["browser_import_mapped_jsm.js"]
+
+["browser_parent_process_hang_telemetry.js"]
+
+["browser_promise_userInteractionHandling.js"]
+
+["browser_realm_key_and_document_domain.js"]
+
+["browser_weak_xpcwjs.js"]
+
+["browser_weak_xpcwn.js"]
+
diff --git a/js/xpconnect/tests/browser/browser_consoleStack.html b/js/xpconnect/tests/browser/browser_consoleStack.html
new file mode 100644
index 0000000000..37bfdb32f6
--- /dev/null
+++ b/js/xpconnect/tests/browser/browser_consoleStack.html
@@ -0,0 +1,21 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+Test page for https://bugzilla.mozilla.org/show_bug.cgi?id=1471989
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test page for Bug 1471989</title>
+</head>
+<body onUnload="onUnload();">
+<p><span id="samplepage">sample page</span></p>
+<script type="application/javascript">
+ // Get something sent to ConsoleStorageAPI that has a stack.
+ console.trace("whatever");
+
+ function onUnload() {
+ console.log('in unload');
+ }
+</script>
+</body>
+</html>
diff --git a/js/xpconnect/tests/browser/browser_date_telemetry.js b/js/xpconnect/tests/browser/browser_date_telemetry.js
new file mode 100644
index 0000000000..b9c653db53
--- /dev/null
+++ b/js/xpconnect/tests/browser/browser_date_telemetry.js
@@ -0,0 +1,70 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+const triggers = [
+ "Sep 26 Tues 1995",
+ "Sep 26 1995 Tues",
+ "Sep 26 1995 Tues 09:30",
+ "Sep 26 1995 09:Tues:30",
+ "Sep 26 1995 09:30 Tues GMT",
+ "Sep 26 1995 09:30 GMT Tues",
+
+ "26 Tues Sep 1995",
+ "26 Sep Tues 1995",
+ "26 Sep 1995 Tues",
+
+ "1995-09-26 Tues",
+
+ // Multiple occurences should only trigger 1 counter
+ "Sep 26 Tues 1995 Tues",
+];
+const nonTriggers = [
+ "Sep 26 1995",
+ "Tues Sep 26 1995",
+ "Sep Tues 26 1995",
+
+ // Invalid format shouldn't trigger the counter
+ "Sep 26 Tues 1995 foo",
+];
+
+function getCount() {
+ return Glean.useCounterPage.jsLateWeekday.testGetValue() ?? 0;
+}
+
+/**
+ * Opens and closes a browser tab with minimal JS code which parses
+ * the given Date format.
+ */
+async function parseFormat(format, call = "new Date") {
+ let newTab = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ `data:text/html;charset=utf-8,<script>${call}("${format}")</script>`
+ );
+ BrowserTestUtils.removeTab(newTab);
+}
+
+add_task(async function test_date_telemetry() {
+ let sum = getCount();
+
+ // waitForCondition cannot be used to test if nothing has changed,
+ // so these tests aren't as reliable as the ones in the next loop.
+ // If you encounter an inexplicable failure in any of these tests,
+ // debug by adding a delay to the end of the parseFormat function.
+ for (const format of nonTriggers) {
+ await parseFormat(format);
+ const count = getCount();
+ is(count, sum, `${format} should not trigger telemetry`);
+ sum = count;
+ }
+
+ for (const [i, format] of triggers.entries()) {
+ // Alternate between Date constructor and Date.parse
+ await parseFormat(format, ["new Date", "Date.parse"][i % 2]);
+ await BrowserTestUtils.waitForCondition(() => getCount() > sum);
+ const count = getCount();
+ is(count, sum + 1, `${format} should trigger telemetry`);
+ sum = count;
+ }
+});
diff --git a/js/xpconnect/tests/browser/browser_deadObjectOnUnload.html b/js/xpconnect/tests/browser/browser_deadObjectOnUnload.html
new file mode 100644
index 0000000000..ceb40b20b6
--- /dev/null
+++ b/js/xpconnect/tests/browser/browser_deadObjectOnUnload.html
@@ -0,0 +1,18 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+Test page for https://bugzilla.mozilla.org/show_bug.cgi?id=1242643
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test page for Bug 1242643</title>
+</head>
+<body onUnload="onUnload();">
+<p><span id="samplepage">sample page</span></p>
+<script type="application/javascript">
+ function onUnload() {
+ console.log('in unload');
+ }
+</script>
+</body>
+</html>
diff --git a/js/xpconnect/tests/browser/browser_dead_object.js b/js/xpconnect/tests/browser/browser_dead_object.js
new file mode 100644
index 0000000000..b8b2dd0688
--- /dev/null
+++ b/js/xpconnect/tests/browser/browser_dead_object.js
@@ -0,0 +1,36 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+// For bug 773980, test that Components.utils.isDeadWrapper works as expected.
+
+add_task(async function test() {
+ const url =
+ "http://mochi.test:8888/browser/js/xpconnect/tests/browser/browser_deadObjectOnUnload.html";
+ let newTab = await BrowserTestUtils.openNewForegroundTab(gBrowser, url);
+ let browser = gBrowser.selectedBrowser;
+ let innerWindowId = browser.innerWindowID;
+ let contentDocDead = await ContentTask.spawn(
+ browser,
+ { innerWindowId },
+ async function (args) {
+ let doc = content.document;
+ let { TestUtils } = ChromeUtils.importESModule(
+ "resource://testing-common/TestUtils.sys.mjs"
+ );
+ let promise = TestUtils.topicObserved(
+ "inner-window-nuked",
+ (subject, data) => {
+ let id = subject.QueryInterface(Ci.nsISupportsPRUint64).data;
+ return id == args.innerWindowId;
+ }
+ );
+ content.location = "http://mochi.test:8888/";
+ await promise;
+ return Cu.isDeadWrapper(doc);
+ }
+ );
+ is(contentDocDead, true, "wrapper is dead");
+ BrowserTestUtils.removeTab(newTab);
+});
diff --git a/js/xpconnect/tests/browser/browser_exception_leak.js b/js/xpconnect/tests/browser/browser_exception_leak.js
new file mode 100644
index 0000000000..be860355bc
--- /dev/null
+++ b/js/xpconnect/tests/browser/browser_exception_leak.js
@@ -0,0 +1,76 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+// For bug 1471989, test that an exception saved by chrome code can't leak the page.
+
+add_task(async function test() {
+ const url =
+ "http://mochi.test:8888/browser/js/xpconnect/tests/browser/browser_consoleStack.html";
+ let newTab = await BrowserTestUtils.openNewForegroundTab(gBrowser, url);
+ let browser = gBrowser.selectedBrowser;
+ let innerWindowId = browser.innerWindowID;
+
+ let stackTraceEmpty = await ContentTask.spawn(
+ browser,
+ { innerWindowId },
+ async function (args) {
+ let { TestUtils } = ChromeUtils.importESModule(
+ "resource://testing-common/TestUtils.sys.mjs"
+ );
+ let { Assert } = ChromeUtils.importESModule(
+ "resource://testing-common/Assert.sys.mjs"
+ );
+
+ const ConsoleAPIStorage = Cc[
+ "@mozilla.org/consoleAPI-storage;1"
+ ].getService(Ci.nsIConsoleAPIStorage);
+ let consoleEvents = ConsoleAPIStorage.getEvents(args.innerWindowId);
+ Assert.equal(
+ consoleEvents.length,
+ 1,
+ "Should only be one console event for the window"
+ );
+
+ // Intentionally hold a reference to the console event.
+ let leakedConsoleEvent = consoleEvents[0];
+
+ // XXX I think this is intentionally leaking |doc|.
+ // eslint-disable-next-line no-unused-vars
+ let doc = content.document;
+
+ let promise = TestUtils.topicObserved(
+ "inner-window-nuked",
+ (subject, data) => {
+ let id = subject.QueryInterface(Ci.nsISupportsPRUint64).data;
+ return id == args.innerWindowId;
+ }
+ );
+ content.location = "http://mochi.test:8888/";
+ await promise;
+
+ // This string should be empty. For that to happen, two things
+ // need to be true:
+ //
+ // a) ConsoleCallData::mStack is not null. This means that the
+ // stack trace was not reified before the page was nuked. If it
+ // was, then the correct |filename| value would be stored on the
+ // object. (This is not a problem, except that it stops us from
+ // testing the next condition.)
+ //
+ // b) ConsoleData::mStack.mStack is null. This means that the
+ // JSStackFrame is keeping alive the JS object in the page after
+ // the page was nuked, which leaks the page.
+ return leakedConsoleEvent.stacktrace[0].filename;
+ }
+ );
+
+ is(
+ stackTraceEmpty,
+ "",
+ "JSStackFrame shouldn't leak mStack after window nuking"
+ );
+
+ BrowserTestUtils.removeTab(newTab);
+});
diff --git a/js/xpconnect/tests/browser/browser_freeze_builtins.js b/js/xpconnect/tests/browser/browser_freeze_builtins.js
new file mode 100644
index 0000000000..905224094e
--- /dev/null
+++ b/js/xpconnect/tests/browser/browser_freeze_builtins.js
@@ -0,0 +1,27 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+function checkCtor(global, name, description) {
+ ok(Object.isFrozen(global[name]), `${description} ${name} is frozen`);
+ ok(
+ Object.isSealed(global[name].prototype),
+ `${description} ${name}.prototype is sealed`
+ );
+
+ let descr = Object.getOwnPropertyDescriptor(global, name);
+ ok(!descr.configurable, `${description} ${name} should be non-configurable`);
+ ok(!descr.writable, `${description} ${name} should not be writable`);
+}
+
+function checkGlobal(global, description) {
+ checkCtor(global, "Object", description);
+ checkCtor(global, "Array", description);
+ checkCtor(global, "Function", description);
+}
+
+add_task(async function () {
+ let systemGlobal = Cu.getGlobalForObject(Services);
+ checkGlobal(systemGlobal, "system global");
+});
diff --git a/js/xpconnect/tests/browser/browser_import_mapped_jsm.js b/js/xpconnect/tests/browser/browser_import_mapped_jsm.js
new file mode 100644
index 0000000000..ba65663b12
--- /dev/null
+++ b/js/xpconnect/tests/browser/browser_import_mapped_jsm.js
@@ -0,0 +1,61 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+"use strict";
+
+// Verify Cu.import and ChromeUtils.import works for JSM URL even after
+// ESM-ification, and any not-in-tree consumer doesn't break.
+//
+// This test modules that's commonly used by not-in-tree consumers, such as
+// privilege extensions and AutoConfigs.
+
+const JSMs = [
+ "resource:///modules/AboutNewTab.jsm",
+ "resource:///modules/CustomizableUI.jsm",
+ "resource:///modules/UITour.jsm",
+ "resource:///modules/distribution.js",
+ "resource://gre/modules/AddonManager.jsm",
+ "resource://gre/modules/AppConstants.jsm",
+ "resource://gre/modules/AsyncShutdown.jsm",
+ "resource://gre/modules/Console.jsm",
+ "resource://gre/modules/FileUtils.jsm",
+ "resource://gre/modules/LightweightThemeManager.jsm",
+ "resource://gre/modules/NetUtil.jsm",
+ "resource://gre/modules/PlacesUtils.jsm",
+ "resource://gre/modules/PrivateBrowsingUtils.jsm",
+ "resource://gre/modules/Timer.jsm",
+ "resource://gre/modules/XPCOMUtils.jsm",
+ "resource://gre/modules/addons/XPIDatabase.jsm",
+ "resource://gre/modules/addons/XPIProvider.jsm",
+ "resource://gre/modules/addons/XPIInstall.jsm",
+ "resource:///modules/BrowserWindowTracker.jsm",
+];
+
+if (AppConstants.platform === "win") {
+ JSMs.push("resource:///modules/WindowsJumpLists.jsm");
+}
+
+add_task(async function test_chrome_utils_import() {
+ for (const file of JSMs) {
+ try {
+ ChromeUtils.import(file);
+ ok(true, `Imported ${file}`);
+ } catch (e) {
+ ok(false, `Failed to import ${file}`);
+ }
+ }
+});
+
+add_task(async function test_cu_import() {
+ for (const file of JSMs) {
+ try {
+ // eslint-disable-next-line mozilla/use-chromeutils-import
+ Cu.import(file, {});
+ ok(true, `Imported ${file}`);
+ } catch (e) {
+ ok(false, `Failed to import ${file}`);
+ }
+ }
+});
diff --git a/js/xpconnect/tests/browser/browser_parent_process_hang_telemetry.js b/js/xpconnect/tests/browser/browser_parent_process_hang_telemetry.js
new file mode 100644
index 0000000000..f9b325c216
--- /dev/null
+++ b/js/xpconnect/tests/browser/browser_parent_process_hang_telemetry.js
@@ -0,0 +1,60 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+/**
+ * Check that we record hangs in the parent process in telemetry events.
+ * This test would be an xpcshell test except xpcshell does not think
+ * it is running e10s (see bug 1568333).
+ */
+add_task(async function test_browser_hang() {
+ // Trip some testing code to ensure we can test this. Sadly, this is a magic
+ // number corresponding to code in XPCJSContext.cpp
+ await SpecialPowers.pushPrefEnv({
+ set: [["dom.max_chrome_script_run_time", 2]],
+ });
+ await SpecialPowers.promiseTimeout(0);
+
+ // Hang for 1.2 seconds.
+ let now = Date.now();
+ let i = 0;
+ info("Start loop");
+ while (Date.now() - now < 2500) {
+ // The system clock can go backwards. Don't time out the test:
+ if (Date.now() - now < 0) {
+ info("Yikes, the system clock changed while running this test.");
+ now = Date.now();
+ }
+ i++;
+ }
+ let duration = (Date.now() - now) / 1000;
+ info("Looped " + i + " iterations.");
+
+ let events;
+ await TestUtils.waitForCondition(() => {
+ events = Services.telemetry.snapshotEvents(
+ Ci.nsITelemetry.DATASET_ALL_CHANNELS,
+ false
+ );
+ return events.parent?.some(e => e[1] == "slow_script_warning");
+ }, "Should find an event after doing this.").catch(e => ok(false, e));
+ events = events.parent || [];
+ let event = events.find(e => e[1] == "slow_script_warning");
+ ok(event, "Should have registered an event.");
+ if (event) {
+ is(event[3], "browser", "Should register as browser hang.");
+ let args = event[5];
+ is(args.uri_type, "browser", "Should register browser uri type.");
+ Assert.greater(
+ duration + 1,
+ parseFloat(args.hang_duration),
+ "hang duration should not exaggerate."
+ );
+ Assert.less(
+ duration - 1,
+ parseFloat(args.hang_duration),
+ "hang duration should not undersell."
+ );
+ }
+});
diff --git a/js/xpconnect/tests/browser/browser_promise_userInteractionHandling.html b/js/xpconnect/tests/browser/browser_promise_userInteractionHandling.html
new file mode 100644
index 0000000000..72f8ef3ee1
--- /dev/null
+++ b/js/xpconnect/tests/browser/browser_promise_userInteractionHandling.html
@@ -0,0 +1,10 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test UserInteractionHandling propagation</title>
+</head>
+<body>
+<button id="button">Meow</button>
+</body>
+</html>
diff --git a/js/xpconnect/tests/browser/browser_promise_userInteractionHandling.js b/js/xpconnect/tests/browser/browser_promise_userInteractionHandling.js
new file mode 100644
index 0000000000..612471be53
--- /dev/null
+++ b/js/xpconnect/tests/browser/browser_promise_userInteractionHandling.js
@@ -0,0 +1,50 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+"use strict";
+
+add_task(async function test_explicit_object_prototype() {
+ const url =
+ "http://mochi.test:8888/browser/js/xpconnect/tests/browser/browser_promise_userInteractionHandling.html";
+ await BrowserTestUtils.withNewTab(url, async browser => {
+ await SpecialPowers.spawn(browser, [], async () => {
+ const DOMWindowUtils = EventUtils._getDOMWindowUtils(content.window);
+ is(
+ DOMWindowUtils.isHandlingUserInput,
+ false,
+ "not yet handling user input"
+ );
+ const button = content.document.getElementById("button");
+
+ let resolve;
+ const p = new Promise(r => {
+ resolve = r;
+ });
+
+ button.addEventListener("click", () => {
+ is(DOMWindowUtils.isHandlingUserInput, true, "handling user input");
+ content.document.hasStorageAccess().then(() => {
+ is(
+ DOMWindowUtils.isHandlingUserInput,
+ true,
+ "still handling user input"
+ );
+ Promise.resolve().then(() => {
+ is(
+ DOMWindowUtils.isHandlingUserInput,
+ false,
+ "no more handling user input"
+ );
+ resolve();
+ });
+ });
+ });
+
+ EventUtils.synthesizeMouseAtCenter(button, {}, content.window);
+
+ await p;
+ });
+ });
+});
diff --git a/js/xpconnect/tests/browser/browser_realm_key_and_document_domain.js b/js/xpconnect/tests/browser/browser_realm_key_and_document_domain.js
new file mode 100644
index 0000000000..2f6910cd5d
--- /dev/null
+++ b/js/xpconnect/tests/browser/browser_realm_key_and_document_domain.js
@@ -0,0 +1,28 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+"use strict";
+
+async function test_document(url) {
+ await BrowserTestUtils.withNewTab(url, async function (browser) {
+ let result = await ContentTask.spawn(browser, {}, async function () {
+ let result = content.document.getElementById("result");
+ return result.innerText;
+ });
+ is(result, "OK", "test succeeds");
+ });
+}
+
+add_task(async function test_explicit_object_prototype() {
+ await test_document(
+ "http://mochi.test:8888/browser/js/xpconnect/tests/browser/browser_realm_key_object_prototype_top.html"
+ );
+});
+
+add_task(async function test_implicit_object_prototype() {
+ await test_document(
+ "http://mochi.test:8888/browser/js/xpconnect/tests/browser/browser_realm_key_promise_top.html"
+ );
+});
diff --git a/js/xpconnect/tests/browser/browser_realm_key_object_prototype_frame.html b/js/xpconnect/tests/browser/browser_realm_key_object_prototype_frame.html
new file mode 100644
index 0000000000..5f3c0b5c2c
--- /dev/null
+++ b/js/xpconnect/tests/browser/browser_realm_key_object_prototype_frame.html
@@ -0,0 +1,11 @@
+<script type="text/javascript">
+// Access to the top-level window property before getting access.
+// This will create an entry in cross-origin realm map.
+try {
+ window.top.Object;
+} catch (e) {}
+
+document.domain = "mochi.test";
+
+window.top.check();
+</script>
diff --git a/js/xpconnect/tests/browser/browser_realm_key_object_prototype_top.html b/js/xpconnect/tests/browser/browser_realm_key_object_prototype_top.html
new file mode 100644
index 0000000000..fdd342ff59
--- /dev/null
+++ b/js/xpconnect/tests/browser/browser_realm_key_object_prototype_top.html
@@ -0,0 +1,12 @@
+<script type="text/javascript">
+document.domain = "mochi.test";
+function check() {
+ // Ensure frame's Object.prototype is accessible.
+ if (document.getElementById("frame").contentWindow.Object.prototype.toString.call({}) == "[object Object]") {
+ document.getElementById("result").textContent = "OK";
+ }
+}
+</script>
+<iframe id="frame" src="http://test2.mochi.test:8888/browser/js/xpconnect/tests/browser/browser_realm_key_object_prototype_frame.html">
+</iframe>
+<span id="result"></span>
diff --git a/js/xpconnect/tests/browser/browser_realm_key_promise_frame.html b/js/xpconnect/tests/browser/browser_realm_key_promise_frame.html
new file mode 100644
index 0000000000..dfb08085cf
--- /dev/null
+++ b/js/xpconnect/tests/browser/browser_realm_key_promise_frame.html
@@ -0,0 +1,17 @@
+<script type="text/javascript">
+// Access to the top-level window property before getting access.
+// This will create an entry in cross-origin realm map.
+try {
+ window.top.P;
+} catch (e) {}
+
+document.domain = "mochi.test";
+
+// Ensure that frame's Object.prototype is accessible from top-level frame
+// when getting incumbent global object inside Promise handling.
+window.top.P.then(v => {
+ if (v == 10) {
+ window.top.document.getElementById("result").textContent = "OK";
+ }
+});
+</script>
diff --git a/js/xpconnect/tests/browser/browser_realm_key_promise_top.html b/js/xpconnect/tests/browser/browser_realm_key_promise_top.html
new file mode 100644
index 0000000000..fe31fb6182
--- /dev/null
+++ b/js/xpconnect/tests/browser/browser_realm_key_promise_top.html
@@ -0,0 +1,7 @@
+<script type="text/javascript">
+document.domain = "mochi.test";
+window.P = new Promise(r => r(10));
+</script>
+<iframe src="http://test2.mochi.test:8888/browser/js/xpconnect/tests/browser/browser_realm_key_promise_frame.html">
+</iframe>
+<span id="result"></span>
diff --git a/js/xpconnect/tests/browser/browser_weak_xpcwjs.js b/js/xpconnect/tests/browser/browser_weak_xpcwjs.js
new file mode 100644
index 0000000000..b8c8c2f85d
--- /dev/null
+++ b/js/xpconnect/tests/browser/browser_weak_xpcwjs.js
@@ -0,0 +1,238 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Some basic tests of the lifetime of an XPCWJS with a weak reference.
+
+// Create a weak reference, with a single-element weak map.
+let make_weak_ref = function (obj) {
+ let m = new WeakMap();
+ m.set(obj, {});
+ return m;
+};
+
+// Check to see if a weak reference is dead.
+let weak_ref_dead = function (r) {
+ return !SpecialPowers.nondeterministicGetWeakMapKeys(r).length;
+};
+
+add_task(async function gc_wwjs() {
+ // This subtest checks that a WJS with only a weak reference to it gets
+ // cleaned up, if its JS object is garbage, after just a GC.
+ // For the browser, this probably isn't important, but tests seem to rely
+ // on it.
+ const TEST_PREF = "wjs.pref1";
+ let wjs_weak_ref = null;
+ let observed_count = 0;
+
+ {
+ Services.prefs.clearUserPref(TEST_PREF);
+
+ // Create the observer object.
+ let observer1 = {
+ QueryInterface: ChromeUtils.generateQI(["nsISupportsWeakReference"]),
+ observe() {
+ observed_count += 1;
+ info(TEST_PREF + " pref observer.");
+ },
+ };
+
+ // Register the weak observer.
+ Services.prefs.addObserver(TEST_PREF, observer1, true);
+
+ // Invoke the observer to make sure it is doing something.
+ info("Flipping the pref " + TEST_PREF);
+ Services.prefs.setBoolPref(TEST_PREF, true);
+ is(observed_count, 1, "Ran observer1 once after first flip.");
+
+ wjs_weak_ref = make_weak_ref(observer1);
+
+ // Exit the scope, making observer1 garbage.
+ }
+
+ // Run the GC.
+ info("Running the GC.");
+ SpecialPowers.forceGC();
+
+ // Flip the pref again to make sure that the observer doesn't run.
+ info("Flipping the pref " + TEST_PREF);
+ Services.prefs.setBoolPref(TEST_PREF, false);
+
+ is(observed_count, 1, "After GC, don't run the observer.");
+ ok(weak_ref_dead(wjs_weak_ref), "WJS with weak ref should be freed.");
+
+ Services.prefs.clearUserPref(TEST_PREF);
+});
+
+add_task(async function alive_wwjs() {
+ // This subtest checks that a WJS with only a weak reference should not get
+ // cleaned up if the underlying JS object is held alive (here, via the
+ // variable |observer2|).
+ const TEST_PREF = "wjs.pref2";
+ let observed_count = 0;
+
+ Services.prefs.clearUserPref(TEST_PREF);
+ let observer2 = {
+ QueryInterface: ChromeUtils.generateQI(["nsISupportsWeakReference"]),
+ observe() {
+ observed_count += 1;
+ info(TEST_PREF + " pref observer");
+ },
+ };
+ Services.prefs.addObserver(TEST_PREF, observer2, true);
+
+ Services.prefs.setBoolPref(TEST_PREF, true);
+ is(observed_count, 1, "Run observer2 once after first flip.");
+
+ await new Promise(resolve =>
+ SpecialPowers.exactGC(() => {
+ SpecialPowers.forceCC();
+ SpecialPowers.forceGC();
+ SpecialPowers.forceCC();
+
+ Services.prefs.setBoolPref(TEST_PREF, false);
+
+ is(observed_count, 2, "Run observer2 again after second flip.");
+
+ Services.prefs.removeObserver(TEST_PREF, observer2);
+ Services.prefs.clearUserPref(TEST_PREF);
+
+ resolve();
+ })
+ );
+});
+
+add_task(async function cc_wwjs() {
+ // This subtest checks that a WJS with only a weak reference to it, where the
+ // underlying JS object is part of a garbage cycle, gets cleaned up after a
+ // cycle collection. It also checks that things held alive by the JS object
+ // don't end up in an unlinked state, although that's mostly for fun, because
+ // it is redundant with checking that the JS object gets cleaned up.
+ const TEST_PREF = "wjs.pref3";
+ let wjs_weak_ref = null;
+ let observed_count = 0;
+ let canary_count;
+
+ {
+ Services.prefs.clearUserPref(TEST_PREF);
+
+ // Set up a canary object that lets us detect unlinking.
+ // (When an nsArrayCC is unlinked, all of the elements are removed.)
+ // This is needed to distinguish the case where the observer was unlinked
+ // without removing the weak reference from the case where we did not
+ // collect the observer at all.
+ let canary = Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray);
+ let someString = Cc["@mozilla.org/supports-string;1"].createInstance(
+ Ci.nsISupportsString
+ );
+ someString.data = "canary";
+ canary.appendElement(someString);
+ canary.appendElement(someString);
+ is(canary.Count(), 2, "The canary array should have two elements");
+
+ // Create the observer object.
+ let observer3 = {
+ QueryInterface: ChromeUtils.generateQI(["nsISupportsWeakReference"]),
+ canary,
+ cycle: new DOMMatrix(),
+ observe() {
+ observed_count += 1;
+ canary_count = this.canary.Count();
+ info(TEST_PREF + " pref observer. Canary count: " + canary_count);
+ },
+ };
+
+ // Set up a cycle between C++ and JS that requires the CC to collect.
+ // |cycle| is a random WebIDL object that we can set an expando on to
+ // create a nice clean cycle that doesn't involve any weird XPConnect stuff.
+ observer3.cycle.backEdge = observer3;
+
+ // Register the weak observer.
+ Services.prefs.addObserver(TEST_PREF, observer3, true);
+
+ // Invoke the observer to make sure it is doing something.
+ info("Flipping the pref " + TEST_PREF);
+ canary_count = -1;
+ Services.prefs.setBoolPref(TEST_PREF, true);
+ is(
+ canary_count,
+ 2,
+ "Observer ran with expected value while observer3 is alive."
+ );
+ is(observed_count, 1, "Ran observer3 once after first flip.");
+
+ wjs_weak_ref = make_weak_ref(observer3);
+
+ // Exit the scope, making observer3 and canary garbage.
+ }
+
+ // Run the GC. This is necessary to mark observer3 gray so the CC
+ // might consider it to be garbage. This won't free it because it is held
+ // alive from C++ (namely the DOMMatrix via its expando).
+ info("Running the GC.");
+ SpecialPowers.forceGC();
+
+ // Note: Don't flip the pref here. Doing so will run the observer, which will
+ // cause it to get marked black again, preventing it from being freed.
+ // For the same reason, don't call weak_ref_dead(wjs_weak_ref) here.
+
+ // Run the CC. This should detect that the cycle between observer3 and the
+ // DOMMatrix is garbage, unlinking the DOMMatrix and the canary. Also, the
+ // weak reference for the WJS for observer3 should get cleared because the
+ // underlying JS object has been identifed as garbage. You can add logging to
+ // nsArrayCC's unlink method to see the canary getting unlinked.
+ info("Running the CC.");
+ SpecialPowers.forceCC();
+
+ // Flip the pref again to make sure that the observer doesn't run.
+ info("Flipping the pref " + TEST_PREF);
+ canary_count = -1;
+ Services.prefs.setBoolPref(TEST_PREF, false);
+
+ isnot(
+ canary_count,
+ 0,
+ "After CC, don't run the observer with an unlinked canary."
+ );
+ isnot(
+ canary_count,
+ 2,
+ "After CC, don't run the observer after it is garbage."
+ );
+ is(canary_count, -1, "After CC, don't run the observer.");
+ is(observed_count, 1, "After CC, don't run the observer.");
+
+ ok(
+ !weak_ref_dead(wjs_weak_ref),
+ "WJS with weak ref shouldn't be freed by the CC."
+ );
+
+ // Now that the CC has identified observer3 as garbage, running the GC again
+ // should free it.
+ info("Running the GC again.");
+ SpecialPowers.forceGC();
+
+ ok(weak_ref_dead(wjs_weak_ref), "WJS with weak ref should be freed.");
+
+ info("Flipping the pref " + TEST_PREF);
+ canary_count = -1;
+ Services.prefs.setBoolPref(TEST_PREF, true);
+
+ // Note: the original implementation of weak references for WJS fails most of
+ // the prior canary_count tests, but passes these.
+ isnot(
+ canary_count,
+ 0,
+ "After GC, don't run the observer with an unlinked canary."
+ );
+ isnot(
+ canary_count,
+ 2,
+ "After GC, don't run the observer after it is garbage."
+ );
+ is(canary_count, -1, "After GC, don't run the observer.");
+ is(observed_count, 1, "After GC, don't run the observer.");
+
+ Services.prefs.clearUserPref(TEST_PREF);
+});
diff --git a/js/xpconnect/tests/browser/browser_weak_xpcwn.js b/js/xpconnect/tests/browser/browser_weak_xpcwn.js
new file mode 100644
index 0000000000..fc0fb502e0
--- /dev/null
+++ b/js/xpconnect/tests/browser/browser_weak_xpcwn.js
@@ -0,0 +1,94 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Check to see if a weak reference is dead.
+let weak_ref_dead = function (r) {
+ return !SpecialPowers.nondeterministicGetWeakMapKeys(r).length;
+};
+
+add_task(async function cc_xpcwn_dead() {
+ // This test demonstrates that a JS reflector for an XPCOM object
+ // (implemented via XPCWrappedNative) can be used as a weak map key, but it
+ // won't persist across a GC/CC if there are no other references to the key in
+ // JS. It would be nice if it did work, in which case we could delete this
+ // test, but it would be difficult to implement.
+
+ let wnMap = new WeakMap();
+
+ // Create a new C++ XPCOM container.
+ let container = Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray);
+
+ {
+ // Create a new C++ XPCOM object, with a new JS reflector.
+ let str = Cc["@mozilla.org/supports-string;1"].createInstance(
+ Ci.nsISupportsString
+ );
+
+ // Set the string data so we can recognize it later.
+ str.data = "canary123";
+
+ // Store the C++ object in the C++ container.
+ container.appendElement(str);
+ is(container.Count(), 1, "The array should have one element");
+
+ // Use the JS reflector as a weak map key.
+ wnMap.set(str, {});
+ ok(!weak_ref_dead(wnMap), "weak map should have an entry");
+
+ // Make sure there are no references to the JS reflector.
+ str = null;
+ }
+
+ // Clean up the JS reflector.
+ SpecialPowers.forceGC();
+ SpecialPowers.forceCC();
+
+ ok(weak_ref_dead(wnMap), "The JS reflector has been freed.");
+
+ // Make a new JS reflector for the C++ XPCOM object.
+ let str2 = container.GetElementAt(0).QueryInterface(Ci.nsISupportsString);
+
+ is(str2.data, "canary123", "The C++ object we created still exists.");
+});
+
+add_task(async function cc_xpcwn_live() {
+ // This test is a slight variation of the previous one. It keeps a reference
+ // to the JS reflector for the C++ object, and shows that this keeps it from
+ // being removed from the weak map. This is mostly to show why it will work
+ // under some conditions.
+
+ let wnMap = new WeakMap();
+
+ // Create a new C++ XPCOM container.
+ let container = Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray);
+
+ // Create a new C++ XPCOM object, with a new JS reflector, and hold alive
+ // the reflector.
+ let str = Cc["@mozilla.org/supports-string;1"].createInstance(
+ Ci.nsISupportsString
+ );
+
+ // Set the string data so we can recognize it later.
+ str.data = "canary345";
+
+ // Store the C++ object in the C++ container.
+ container.appendElement(str);
+ is(container.Count(), 1, "The array should have one element");
+
+ // Use the JS reflector as a weak map key.
+ wnMap.set(str, {});
+ ok(!weak_ref_dead(wnMap), "weak map should have an entry");
+
+ // Clean up the JS reflector.
+ SpecialPowers.forceGC();
+ SpecialPowers.forceCC();
+
+ ok(!weak_ref_dead(wnMap), "The JS reflector hasn't been freed.");
+
+ // Get a JS reflector from scratch for the C++ XPCOM object.
+ let str2 = container.GetElementAt(0).QueryInterface(Ci.nsISupportsString);
+ is(str, str2, "The JS reflector is the same");
+ is(str2.data, "canary345", "The C++ object hasn't changed");
+});
diff --git a/js/xpconnect/tests/browser/moz.build b/js/xpconnect/tests/browser/moz.build
new file mode 100644
index 0000000000..cfd5452a0e
--- /dev/null
+++ b/js/xpconnect/tests/browser/moz.build
@@ -0,0 +1,7 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+BROWSER_CHROME_MANIFESTS += ["browser.toml"]
diff --git a/js/xpconnect/tests/chrome/bug503926.xhtml b/js/xpconnect/tests/chrome/bug503926.xhtml
new file mode 100644
index 0000000000..d62a6b575f
--- /dev/null
+++ b/js/xpconnect/tests/chrome/bug503926.xhtml
@@ -0,0 +1,30 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=503926
+-->
+<window title="Mozilla Bug 503926"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=503926"
+ target="_blank">Mozilla Bug 503926</a>
+ </body>
+
+ <!-- test code goes here -->
+ <script type="application/javascript">
+ <![CDATA[
+ var gWindowUtils = window.windowUtils;
+ var passed = false;
+ // eslint-disable-next-line mozilla/use-chromeutils-generateqi
+ var obj = { QueryInterface() { passed = true; } }
+ gWindowUtils.xpconnectArgument(obj);
+ var isDialog = location.hash != '#iframe';
+ var outer = Cu.waiveXrays(isDialog ? window.arguments[0] : parent);
+ outer.ok(passed, "chrome/chrome test passed: " + (isDialog ? "dialog" : "iframe"));
+ if (isDialog)
+ close();
+ ]]>
+ </script>
+</window>
diff --git a/js/xpconnect/tests/chrome/chrome.toml b/js/xpconnect/tests/chrome/chrome.toml
new file mode 100644
index 0000000000..6aec4b549b
--- /dev/null
+++ b/js/xpconnect/tests/chrome/chrome.toml
@@ -0,0 +1,220 @@
+[DEFAULT]
+skip-if = ["os == 'android'"]
+support-files = [
+ "bug503926.xhtml",
+ "file_bug484459.html",
+ "file_bug618176.xhtml",
+ "file_bug996069.html",
+ "file_bug1281071.html",
+ "file_discardSystemSource.html",
+ "file_empty.html",
+ "file_evalInSandbox.html",
+ "file_expandosharing.jsm",
+ "outoflinexulscript.js",
+ "subscript.js",
+ "utf8_subscript.js",
+ "worker_discardSystemSource.js",
+ "!/js/xpconnect/tests/mochitest/bug500931_helper.html",
+ "!/js/xpconnect/tests/mochitest/bug571849_helper.html",
+ "!/js/xpconnect/tests/mochitest/chrome_wrappers_helper.html",
+ "!/js/xpconnect/tests/mochitest/file_bug706301.html",
+ "!/js/xpconnect/tests/mochitest/file_bug738244.html",
+ "!/js/xpconnect/tests/mochitest/file_bug760131.html",
+ "!/js/xpconnect/tests/mochitest/file_bug795275.html",
+ "!/js/xpconnect/tests/mochitest/file_bug799348.html",
+ "!/js/xpconnect/tests/mochitest/file_bug860494.html",
+ "!/js/xpconnect/tests/mochitest/file_documentdomain.html",
+ "!/js/xpconnect/tests/mochitest/file_doublewrappedcompartments.html",
+ "!/js/xpconnect/tests/mochitest/file_empty.html",
+ "!/js/xpconnect/tests/mochitest/file_exnstack.html",
+ "!/js/xpconnect/tests/mochitest/file_expandosharing.html",
+ "!/js/xpconnect/tests/mochitest/file_nodelists.html",
+ "!/js/xpconnect/tests/mochitest/file_evalInSandbox.html",
+ "!/js/xpconnect/tests/mochitest/file_xrayic.html",
+]
+prefs = ["javascript.options.large_arraybuffers=true"]
+
+["test_APIExposer.xhtml"]
+
+["test_bug361111.xhtml"]
+
+["test_bug448587.xhtml"]
+
+["test_bug484459.xhtml"]
+skip-if = [
+ "os == 'win'",
+ "os == 'mac'",
+ "os == 'linux' && !debug",
+] # bug 1131110, 1255284
+
+["test_bug500931.xhtml"]
+
+["test_bug503926.xhtml"]
+
+["test_bug533596.xhtml"]
+
+["test_bug571849.xhtml"]
+
+["test_bug610390.xhtml"]
+
+["test_bug614757.xhtml"]
+
+["test_bug616992.xhtml"]
+
+["test_bug618176.xhtml"]
+
+["test_bug654370.xhtml"]
+
+["test_bug658560.xhtml"]
+
+["test_bug658909.xhtml"]
+
+["test_bug664689.xhtml"]
+
+["test_bug679861.xhtml"]
+
+["test_bug706301.xhtml"]
+
+["test_bug726949.xhtml"]
+
+["test_bug732665.xhtml"]
+
+["test_bug738244.xhtml"]
+
+["test_bug743843.xhtml"]
+
+["test_bug760076.xhtml"]
+
+["test_bug760131.html"]
+
+["test_bug763343.xhtml"]
+
+["test_bug771429.xhtml"]
+
+["test_bug773962.xhtml"]
+
+["test_bug792280.xhtml"]
+
+["test_bug793433.xhtml"]
+
+["test_bug795275.xhtml"]
+
+["test_bug799348.xhtml"]
+
+["test_bug801241.xhtml"]
+
+["test_bug812415.xhtml"]
+
+["test_bug853283.xhtml"]
+
+["test_bug853571.xhtml"]
+
+["test_bug858101.xhtml"]
+
+["test_bug860494.xhtml"]
+
+["test_bug865948.xhtml"]
+
+["test_bug866823.xhtml"]
+
+["test_bug895340.xhtml"]
+
+["test_bug932906.xhtml"]
+allow_xul_xbl = true
+
+["test_bug996069.xhtml"]
+
+["test_bug1041626.xhtml"]
+
+["test_bug1042436.xhtml"]
+
+["test_bug1065185.html"]
+
+["test_bug1074863.html"]
+
+["test_bug1092477.xhtml"]
+
+["test_bug1124898.html"]
+
+["test_bug1126911.html"]
+
+["test_bug1281071.xhtml"]
+
+["test_bug1390159.xhtml"]
+
+["test_bug1430164.html"]
+
+["test_bug1516237.html"]
+
+["test_bug1530146.html"]
+support-files = [
+ "file_bug1530146.html",
+ "file_bug1530146_inner.html",
+]
+
+["test_chrometoSource.xhtml"]
+
+["test_cloneInto.xhtml"]
+
+["test_cows.xhtml"]
+
+["test_discardSystemSource.xhtml"]
+
+["test_documentdomain.xhtml"]
+
+["test_doublewrappedcompartments.xhtml"]
+
+["test_envChain_event_handler.html"]
+
+["test_evalInSandbox.xhtml"]
+
+["test_evalInWindow.xhtml"]
+
+["test_exnstack.xhtml"]
+
+["test_expandosharing.xhtml"]
+
+["test_exposeInDerived.xhtml"]
+
+["test_inlineScripts.html"]
+
+["test_localstorage_with_nsEp.xhtml"]
+
+["test_matches.xhtml"]
+
+["test_nodelists.xhtml"]
+
+["test_nsScriptErrorWithStack.html"]
+
+["test_onGarbageCollection.html"]
+
+["test_precisegc.xhtml"]
+
+["test_private_field_cows.xhtml"]
+
+["test_sandboxImport.xhtml"]
+
+["test_scriptSettings.xhtml"]
+
+["test_scripterror.html"]
+
+["test_secureContexts.html"]
+
+["test_sharedChromeCompartment.html"]
+
+["test_weakmap_keys_preserved.xhtml"]
+
+["test_weakmap_keys_preserved2.xhtml"]
+
+["test_weakref.xhtml"]
+
+["test_windowProxyDeadWrapper.html"]
+
+["test_wrappers.xhtml"]
+
+["test_xrayLargeTypedArray.html"]
+skip-if = ["bits == 32"] # Large ArrayBuffers not supported on 32-bit.
+
+["test_xrayToJS.xhtml"]
+
+["test_xrayic.xhtml"]
diff --git a/js/xpconnect/tests/chrome/file_bug1281071.html b/js/xpconnect/tests/chrome/file_bug1281071.html
new file mode 100644
index 0000000000..2398ce4a57
--- /dev/null
+++ b/js/xpconnect/tests/chrome/file_bug1281071.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<script>
+ function tryLocationGet() {
+ var desc = Object.getOwnPropertyDescriptor(window, "location");
+ try {
+ desc.get.call(parent);
+ return "get succeeded";
+ } catch (e) {
+ return e.message;
+ }
+ }
+</script>
+
diff --git a/js/xpconnect/tests/chrome/file_bug1530146.html b/js/xpconnect/tests/chrome/file_bug1530146.html
new file mode 100644
index 0000000000..5414ea407d
--- /dev/null
+++ b/js/xpconnect/tests/chrome/file_bug1530146.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<script>
+ // eslint-disable-next-line no-self-assign
+ document.domain = document.domain;
+</script>
+<iframe></iframe>
diff --git a/js/xpconnect/tests/chrome/file_bug1530146_inner.html b/js/xpconnect/tests/chrome/file_bug1530146_inner.html
new file mode 100644
index 0000000000..db642cf81d
--- /dev/null
+++ b/js/xpconnect/tests/chrome/file_bug1530146_inner.html
@@ -0,0 +1,4 @@
+<!DOCTYPE html>
+<script>
+ var obj = { a: "hello" }
+</script>
diff --git a/js/xpconnect/tests/chrome/file_bug484459.html b/js/xpconnect/tests/chrome/file_bug484459.html
new file mode 100644
index 0000000000..27be5463a2
--- /dev/null
+++ b/js/xpconnect/tests/chrome/file_bug484459.html
@@ -0,0 +1,10 @@
+<html>
+<head>
+ <title>helper for bug 484459</title>
+</head>
+<body>
+ <script type="application/javascript">
+ var x=3;
+ </script>
+</body>
+</html>
diff --git a/js/xpconnect/tests/chrome/file_bug618176.xhtml b/js/xpconnect/tests/chrome/file_bug618176.xhtml
new file mode 100644
index 0000000000..3aea153159
--- /dev/null
+++ b/js/xpconnect/tests/chrome/file_bug618176.xhtml
@@ -0,0 +1,48 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=618176
+-->
+<window title="Mozilla Bug 618176"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="start()">
+ <label value="Mozilla Bug 618176"/>
+ <!-- test code goes here -->
+ <script type="application/javascript"><![CDATA[
+ /* global messageManager */
+ const TEST_PAGE = Services.io.newURI(
+ "data:text/html,<script>var a=[1,2,3];</script>Hi"
+ );
+
+ const FRAME_SCRIPT =
+"data:,addEventListener('pageshow', function() { sendAsyncMessage('test', content.wrappedJSObject.a) }, false);";
+ // s/content.wrappedJSObject.a/[ 1, 2, 3]/ and the test passes
+
+ function recvTest(m) {
+ var a = m.json;
+ window.arguments[0].is(a.length, 3, "array was serialized and deserialized");
+
+ messageManager.removeMessageListener("test", recvTest);
+ finish();
+ }
+
+ function start() {
+ messageManager.addMessageListener("test", recvTest);
+ messageManager.loadFrameScript(FRAME_SCRIPT, true);
+ let triggeringPrincipal = Services.scriptSecurityManager.getSystemPrincipal();
+ setTimeout(function () {
+ document.getElementById("browser").loadURI(TEST_PAGE, {triggeringPrincipal});
+ }, 0);
+ }
+
+ function finish() {
+ window.arguments[0].setTimeout(function() { this.done(); }, 0);
+ window.close();
+ }
+
+ ]]></script>
+
+ <browser id="browser" type="content" style="width: 200px; height: 200px;"/>
+</window>
diff --git a/js/xpconnect/tests/chrome/file_bug996069.html b/js/xpconnect/tests/chrome/file_bug996069.html
new file mode 100644
index 0000000000..e4bed07806
--- /dev/null
+++ b/js/xpconnect/tests/chrome/file_bug996069.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html>
+<head></head>
+<body>
+ <script>
+ if (window.opener && window.opener.finishTest) {
+ window.opener.finishTest();
+ }
+ </script>
+</body>
+</html>
diff --git a/js/xpconnect/tests/chrome/file_discardSystemSource.html b/js/xpconnect/tests/chrome/file_discardSystemSource.html
new file mode 100644
index 0000000000..5dc9e9e7ba
--- /dev/null
+++ b/js/xpconnect/tests/chrome/file_discardSystemSource.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script>
+ function canary() {
+ // eslint-disable-next-line no-unused-vars
+ var someBitOfSource = 42;
+ }
+ function inner() {
+ throw new Error("some error");
+ }
+ function throwSomething() {
+ inner();
+ }
+</script>
+</head>
+<body onload="someBitOfSource = 42">
+</body>
+</html>
diff --git a/js/xpconnect/tests/chrome/file_empty.html b/js/xpconnect/tests/chrome/file_empty.html
new file mode 100644
index 0000000000..b3bfe19c0b
--- /dev/null
+++ b/js/xpconnect/tests/chrome/file_empty.html
@@ -0,0 +1,2 @@
+<!DOCTYPE html>
+<html><head><title>test page</title></head><body>there is nothing to see</body></html>
diff --git a/js/xpconnect/tests/chrome/file_evalInSandbox.html b/js/xpconnect/tests/chrome/file_evalInSandbox.html
new file mode 100644
index 0000000000..fb58f2bb41
--- /dev/null
+++ b/js/xpconnect/tests/chrome/file_evalInSandbox.html
@@ -0,0 +1 @@
+<html><body><script>document.foo = 'bar';</script></body></html>
diff --git a/js/xpconnect/tests/chrome/file_expandosharing.jsm b/js/xpconnect/tests/chrome/file_expandosharing.jsm
new file mode 100644
index 0000000000..f680ae20a9
--- /dev/null
+++ b/js/xpconnect/tests/chrome/file_expandosharing.jsm
@@ -0,0 +1,12 @@
+var EXPORTED_SYMBOLS = ["checkFromJSM"];
+
+function checkFromJSM(target, is_op) {
+ is_op(target.numProp, 42, "Number expando works");
+ is_op(target.strProp, "foo", "String expando works");
+ // If is_op is todo_is, target.objProp will be undefined.
+ try {
+ is_op(target.objProp.bar, "baz", "Object expando works");
+ } catch (e) {
+ is_op(0, 1, "No object expando");
+ }
+}
diff --git a/js/xpconnect/tests/chrome/moz.build b/js/xpconnect/tests/chrome/moz.build
new file mode 100644
index 0000000000..51548541b7
--- /dev/null
+++ b/js/xpconnect/tests/chrome/moz.build
@@ -0,0 +1,12 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+MOCHITEST_CHROME_MANIFESTS += ["chrome.toml"]
+
+TEST_HARNESS_FILES.testing.mochitest.tests.js.xpconnect.tests.chrome += [
+ "file_discardSystemSource.html",
+ "worker_discardSystemSource.js",
+]
diff --git a/js/xpconnect/tests/chrome/outoflinexulscript.js b/js/xpconnect/tests/chrome/outoflinexulscript.js
new file mode 100644
index 0000000000..14b99048e9
--- /dev/null
+++ b/js/xpconnect/tests/chrome/outoflinexulscript.js
@@ -0,0 +1,5 @@
+// Some unicode characters that must be decoded:
+// ………………………………………………………………………………………………………………………………
+function outoflinefunction() {
+ return 42;
+}
diff --git a/js/xpconnect/tests/chrome/subscript.js b/js/xpconnect/tests/chrome/subscript.js
new file mode 100644
index 0000000000..c2708f6e9b
--- /dev/null
+++ b/js/xpconnect/tests/chrome/subscript.js
@@ -0,0 +1,4 @@
+/* global base */
+var ns = {};
+Services.scriptloader.loadSubScript(base + "file_expandosharing.jsm", ns);
+var checkFromJSM = ns.checkFromJSM;
diff --git a/js/xpconnect/tests/chrome/test_APIExposer.xhtml b/js/xpconnect/tests/chrome/test_APIExposer.xhtml
new file mode 100644
index 0000000000..b327abe44d
--- /dev/null
+++ b/js/xpconnect/tests/chrome/test_APIExposer.xhtml
@@ -0,0 +1,48 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=634156
+-->
+<window title="Testing API exposing capabilities"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=634156"
+ target="_blank">Mozilla Bug 634156</a>
+ </body>
+
+ <!-- test code goes here -->
+ <script type="application/javascript"><![CDATA[
+ var sandbox = new Cu.Sandbox("about:blank");
+ sandbox.ok = ok;
+ sandbox.is = is;
+ Cu.evalInSandbox("Object.defineProperty(Object.prototype, 'getProp', { get: function() { throw 'FAIL: called getter' }, set: function() { throw 'FAIL: called setter'; } })", sandbox);
+
+ var obj = Cu.createObjectIn(sandbox);
+ is(obj, Cu.waiveXrays(obj), "createObjectIn waives");
+ is(Object.getPrototypeOf(obj), Cu.waiveXrays(Cu.evalInSandbox("Object.prototype", sandbox)),
+ "Object is a sandbox object");
+
+ function genPropDesc(value) {
+ return { enumerable: true, configurable: true, writable: true,
+ value };
+ }
+ const props = {
+ 'getProp': genPropDesc(function() { ok(true, "called prop that shadowed a getter"); }),
+ 'argument': genPropDesc(function(arg) { is(arg, 42, "can pass arguments through"); }),
+ 'returnval': genPropDesc(function() { return 42; })
+ };
+ Object.defineProperties(obj, props);
+ Cu.makeObjectPropsNormal(obj);
+
+ sandbox.api = obj;
+ Cu.evalInSandbox("ok(Object.getPrototypeOf(api) === Object.prototype, 'we have the object we expected'); \
+ api.getProp(); api.argument(42); is(api.returnval(), 42, 'return value was correct');\
+ ok(typeof api.getProp === 'function', 'functions are functions');\
+ ok(Object.getPrototypeOf(api.getProp) === Function.prototype, 'functions come from our scope');", sandbox);
+ ]]></script>
+</window>
diff --git a/js/xpconnect/tests/chrome/test_bug1041626.xhtml b/js/xpconnect/tests/chrome/test_bug1041626.xhtml
new file mode 100644
index 0000000000..61d7630838
--- /dev/null
+++ b/js/xpconnect/tests/chrome/test_bug1041626.xhtml
@@ -0,0 +1,60 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1041626
+-->
+<window title="Mozilla Bug 1041626"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1041626"
+ target="_blank">Mozilla Bug 1041626</a>
+ </body>
+
+ <!-- test code goes here -->
+ <script type="application/javascript">
+ <![CDATA[
+ /** Test for Bug 1041626 **/
+ SimpleTest.waitForExplicitFinish();
+ function go() {
+
+ //
+ // Location
+ //
+
+ ok(Cu.isXrayWrapper(window[0].location), "Location is Xrayed");
+ let xrayOwnProperties = Object.getOwnPropertyNames(window[0].location);
+
+ let realOwnProperties = Object.getOwnPropertyNames(window[0].wrappedJSObject.location);
+ ok(realOwnProperties.length > 2);
+
+ is(xrayOwnProperties.sort().toSource(), realOwnProperties.sort().toSource(),
+ "Xray enumerates location properties properly");
+
+ //
+ // Document
+ //
+
+ ok(Cu.isXrayWrapper(window[0].document), "Document is Xrayed");
+ xrayOwnProperties = Object.getOwnPropertyNames(window[0].document);
+
+ realOwnProperties = Object.getOwnPropertyNames(window[0].wrappedJSObject.document);
+ ok(!!realOwnProperties.length);
+
+ is(xrayOwnProperties.sort().toSource(), realOwnProperties.sort().toSource(),
+ "Xray enumerates document properties properly");
+
+
+
+ SimpleTest.finish();
+ }
+
+
+
+ ]]>
+ </script>
+ <iframe onload="go();" src="http://example.org/tests/js/xpconnect/tests/mochitest/file_empty.html" />
+</window>
diff --git a/js/xpconnect/tests/chrome/test_bug1042436.xhtml b/js/xpconnect/tests/chrome/test_bug1042436.xhtml
new file mode 100644
index 0000000000..4c86839fd9
--- /dev/null
+++ b/js/xpconnect/tests/chrome/test_bug1042436.xhtml
@@ -0,0 +1,54 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1042436
+-->
+<window title="Mozilla Bug 1042436"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1042436"
+ target="_blank">Mozilla Bug 1042436</a>
+ </body>
+
+ <!-- test code goes here -->
+ <script type="application/javascript">
+ <![CDATA[
+ /** Test for Bug 1042436 **/
+ SimpleTest.waitForExplicitFinish();
+
+ var contentSb = Cu.Sandbox("https://www.example.com");
+ var nonXrayableObj = contentSb.eval("new Map()[Symbol.iterator]()");
+ nonXrayableObj.wrappedJSObject.someExpandoProperty = 42;
+ nonXrayableObj.wrappedJSObject.someOtherExpandoProperty = 52;
+
+
+ SimpleTest.expectConsoleMessages(function() {
+ // Create sandboxes in fresh compartments, because the warning state is
+ // stored per compartment.
+ var chromeSb1 = Cu.Sandbox(this, {freshCompartment: true});
+ chromeSb1.nonXrayableObj = nonXrayableObj;
+ Cu.evalInSandbox(`
+ nonXrayableObj.someExpandoProperty;
+ nonXrayableObj.someOtherExpandoProperty;
+ `, chromeSb1);
+
+ var chromeSb2 = Cu.Sandbox(this, {freshCompartment: true});
+ var contentObjWithGetter = contentSb.eval('({ get getterProp() {return 42;}, valueProp: 42 })');
+ is(contentObjWithGetter.wrappedJSObject.getterProp, 42, "Getter prop set up correctly");
+ is(contentObjWithGetter.getterProp, undefined, "Xrays work right");
+ is(contentObjWithGetter.valueProp, 42, "Getter prop set up correctly");
+ chromeSb2.contentObjWithGetter = contentObjWithGetter;
+ Cu.evalInSandbox('contentObjWithGetter.getterProp; contentObjWithGetter.valueProp; contentObjWithGetter.getterProp;',
+ chromeSb2, "1.7", "https://phony.example.com/file.js", 99);
+ },
+ [{ errorMessage: /property "someExpandoProperty" \(reason: object is not safely Xrayable/, sourceName: /test_bug1042436/, isWarning: true },
+ { errorMessage: /property "getterProp" \(reason: property has accessor/, sourceName: /phony/, lineNumber: 99, isWarning: true } ],
+ SimpleTest.finish.bind(SimpleTest));
+
+ ]]>
+ </script>
+</window>
diff --git a/js/xpconnect/tests/chrome/test_bug1065185.html b/js/xpconnect/tests/chrome/test_bug1065185.html
new file mode 100644
index 0000000000..bc5b6027f2
--- /dev/null
+++ b/js/xpconnect/tests/chrome/test_bug1065185.html
@@ -0,0 +1,64 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1065185
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1065185</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://global/skin"/>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+ /** Test for Bug 1065185 **/
+ SimpleTest.waitForExplicitFinish();
+
+ function doMonitor(rgxps) {
+ var messages = rgxps.map((x) => ({ errorMessage: x, isWarning: true }));
+ info("Expecting console messages: " + messages.toSource());
+ SimpleTest.monitorConsole(() => SimpleTest.executeSoon(() => window[0].location.reload()), messages, /* forbidUnexpected = */ true);
+ }
+ function endMonitor() {
+ SimpleTest.executeSoon(SimpleTest.endMonitorConsole.bind(SimpleTest));
+ }
+
+ var gLoadCount = 0;
+ function loaded() {
+ switch(gLoadCount++) {
+ case 0:
+ doMonitor([/access to property "a"/i]);
+ window[0].wrappedJSObject.probe = { a: 2 };
+ is(window[0].eval('probe.a'), undefined, "Accessed exposed prop");
+ endMonitor();
+ break;
+ case 1:
+ doMonitor([/access to property "a"/i]);
+ window[0].wrappedJSObject.probe = { a: 2 };
+ is(window[0].eval('probe.a'), undefined, "Non-exposed prop undefined");
+ is(window[0].eval('probe.a'), undefined, "Non-exposed prop undefined again");
+ endMonitor();
+ break;
+ case 2:
+ SimpleTest.finish();
+ break;
+ default:
+ ok(false, "Unexpected load");
+ SimpleTest.finish();
+ break;
+ }
+ }
+
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1065185">Mozilla Bug 1065185</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+<iframe onload="loaded();" src="http://example.org/tests/js/xpconnect/tests/mochitest/file_empty.html" />
+</body>
+</html>
diff --git a/js/xpconnect/tests/chrome/test_bug1074863.html b/js/xpconnect/tests/chrome/test_bug1074863.html
new file mode 100644
index 0000000000..2fbe69a547
--- /dev/null
+++ b/js/xpconnect/tests/chrome/test_bug1074863.html
@@ -0,0 +1,31 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1074863
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1074863</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://global/skin"/>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+ /** Test for Bug 1074863 **/
+ var sb = new Cu.Sandbox("https://www.example.com");
+ sb.namedCtor = Image;
+ ok(true, "Didn't assert");
+
+
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1074863">Mozilla Bug 1074863</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/js/xpconnect/tests/chrome/test_bug1092477.xhtml b/js/xpconnect/tests/chrome/test_bug1092477.xhtml
new file mode 100644
index 0000000000..d79cd6604d
--- /dev/null
+++ b/js/xpconnect/tests/chrome/test_bug1092477.xhtml
@@ -0,0 +1,33 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1092477
+-->
+<window title="Mozilla Bug 1092477"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1092477"
+ target="_blank">Mozilla Bug 1092477</a>
+ </body>
+
+ <!-- test code goes here -->
+ <script type="application/javascript"><![CDATA[
+
+var exn;
+var url = "resource://non-existent/script.js";
+try {
+ Services.scriptloader.loadSubScript(url);
+ ok(false, "This line should never be reached!");
+}
+catch (e) {
+ exn = String(e);
+}
+var msg = "loadSubScript should throw an exception for trying to load a non-existent script"
+is(exn, "Error opening input stream (invalid filename?): " + url, msg);
+ ]]></script>
+</window>
diff --git a/js/xpconnect/tests/chrome/test_bug1124898.html b/js/xpconnect/tests/chrome/test_bug1124898.html
new file mode 100644
index 0000000000..0b6c2b7451
--- /dev/null
+++ b/js/xpconnect/tests/chrome/test_bug1124898.html
@@ -0,0 +1,52 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1124898
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1124898</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://global/skin"/>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+ /** Test for Bug 1124898 **/
+ SimpleTest.waitForExplicitFinish();
+ (async () => {
+ await SpecialPowers.pushPrefEnv({"set": [["security.allow_eval_with_system_principal", true]]});
+
+ SimpleTest.expectAssertions(0, 1); // Dumb unrelated widget assertion - see bug 1126023.
+
+ var w = window.browsingContext.topChromeWindow.open("about:blank", "w", "chrome");
+ is(w.eval('typeof getAttention'), 'function', 'getAttention exists on regular chrome window');
+ is(w.eval('typeof messageManager'), 'object', 'messageManager exists on regular chrome window');
+ var contentURL = "https://example.org/tests/js/xpconnect/tests/mochitest/file_empty.html";
+ w.location = contentURL;
+ tryWindow();
+
+ function tryWindow() {
+ if (w.document.title != 'empty test page') {
+ info("Document not loaded yet - retrying");
+ SimpleTest.executeSoon(tryWindow);
+ return;
+ }
+ is(w.eval('typeof getAttention'), 'undefined', 'getAttention doesnt exist on content-in-chrome window');
+ is(w.eval('typeof messageManager'), 'undefined', 'messageManager doesnt exist on content-in-chrome window');
+ w.close();
+ SimpleTest.finish();
+ }
+ })();
+
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1124898">Mozilla Bug 1124898</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/js/xpconnect/tests/chrome/test_bug1126911.html b/js/xpconnect/tests/chrome/test_bug1126911.html
new file mode 100644
index 0000000000..bbb2a00f54
--- /dev/null
+++ b/js/xpconnect/tests/chrome/test_bug1126911.html
@@ -0,0 +1,40 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1126911
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1126911</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://global/skin"/>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+ /** Test for Bug 1126911 **/
+ var sb = new Cu.Sandbox(null);
+ sb.win = window;
+ sb.loc = location;
+ function checkThrows(expr) {
+ try {
+ Cu.evalInSandbox(expr, sb);
+ ok(false, "Should have thrown: " + expr);
+ } catch (e) {
+ ok(/denied|insecure/.test(e), "should get security exception: " + e);
+ }
+ }
+ checkThrows('win.top');
+ checkThrows('loc.replace');
+
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1126911">Mozilla Bug 1126911</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/js/xpconnect/tests/chrome/test_bug1281071.xhtml b/js/xpconnect/tests/chrome/test_bug1281071.xhtml
new file mode 100644
index 0000000000..e1ab036d55
--- /dev/null
+++ b/js/xpconnect/tests/chrome/test_bug1281071.xhtml
@@ -0,0 +1,32 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1281071
+-->
+<window title="Mozilla Bug 1281071"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <!-- test code goes here -->
+ <script type="application/javascript"><![CDATA[
+ SimpleTest.waitForExplicitFinish();
+ function go() {
+ var w = $('ifr').contentWindow.wrappedJSObject;
+ is(w.tryLocationGet(), "Permission to call 'get location' denied.",
+ "Trying to get our location object should throw");
+ SimpleTest.finish();
+ }
+ ]]></script>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <iframe type="content"
+ src="http://mochi.test:8888/chrome/js/xpconnect/tests/chrome/file_bug1281071.html"
+ onload="go()"
+ id="ifr">
+ </iframe>
+ </body>
+
+</window>
diff --git a/js/xpconnect/tests/chrome/test_bug1390159.xhtml b/js/xpconnect/tests/chrome/test_bug1390159.xhtml
new file mode 100644
index 0000000000..7d5b917a27
--- /dev/null
+++ b/js/xpconnect/tests/chrome/test_bug1390159.xhtml
@@ -0,0 +1,44 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1390159
+-->
+<window title="Mozilla Bug 1390159"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <!-- test code goes here -->
+ <script type="application/javascript"><![CDATA[
+ function test() {
+ var xulRuntime = Services.appinfo;
+
+ // Make sure it has an inSafeMode property. This is just an arbitrary
+ // readonly property.
+ var oldValue = xulRuntime.inSafeMode;
+ is(typeof oldValue, "boolean", "Expected an inSafeMode property");
+
+ // Changing a readonly property doesn't throw, but shouldn't change
+ // the value.
+ xulRuntime.inSafeMode = !oldValue;
+ is(xulRuntime.inSafeMode, oldValue, "Should not have modified prop");
+
+ // Adding a new property should throw.
+ let ex = null;
+ try {
+ xulRuntime.foobar = 1;
+ } catch (e) {
+ ex = e;
+ }
+ is(ex.toString().includes("Cannot modify"), true, "Expected an error");
+ is(xulRuntime.foobar, undefined, "Property not defined");
+ }
+ test();
+ ]]></script>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ </body>
+
+</window>
diff --git a/js/xpconnect/tests/chrome/test_bug1430164.html b/js/xpconnect/tests/chrome/test_bug1430164.html
new file mode 100644
index 0000000000..609e662ad5
--- /dev/null
+++ b/js/xpconnect/tests/chrome/test_bug1430164.html
@@ -0,0 +1,31 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1430164
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1430164</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://global/skin"/>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+ /** Test for Bug 1430164 **/
+ var s = new Cu.Sandbox(window, { sandboxPrototype: window } );
+ var t;
+ var desc;
+ try {
+ t = Cu.evalInSandbox('Node === Node', s);
+ ok(t, "Object identity should be sane");
+
+ t = Cu.evalInSandbox('Node === this.Node', s);
+ ok(t, "Node should match this.Node in toplevel");
+
+ t = Cu.evalInSandbox('Node === window.Node', s);
+ ok(t, "Node should match window.Node");
+ } catch (e) {
+ ok(false, "Should not get an exception: " + e);
+ }
+ </script>
+</head>
+</html>
diff --git a/js/xpconnect/tests/chrome/test_bug1516237.html b/js/xpconnect/tests/chrome/test_bug1516237.html
new file mode 100644
index 0000000000..5c640c2052
--- /dev/null
+++ b/js/xpconnect/tests/chrome/test_bug1516237.html
@@ -0,0 +1,48 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1516237
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1516237</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://global/skin"/>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+/** Test for Bug 1516237 **/
+function go() {
+ SimpleTest.waitForExplicitFinish();
+
+ var frame = $('subframe');
+ frame.onload = null;
+
+ // Get a CCW wrapping an Xray waiver wrapping a WindowProxy.
+ var w = frame.contentWindow;
+ var ccwToWaiver = w.wrappedJSObject;
+ ccwToWaiver.testProp = 1;
+ is(ccwToWaiver.testProp, 1, "Can set properties on content window");
+
+ // Load a chrome page in the content frame. This might create a realm in
+ // the current chrome compartment - in that case we nuke the CCW to the
+ // waiver.
+ frame.onload = function() {
+ is(Cu.getClassName(w.Math, /* unwrap = */ false), "Math",
+ "Window must be in same system compartment");
+ ok(Cu.isDeadWrapper(ccwToWaiver),
+ "Nuked CCW to same-compartment window");
+ SimpleTest.finish();
+ };
+ frame.src = "file_empty.html";
+}
+
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1516237">Mozilla Bug 1516237</a>
+
+<iframe id="subframe" src="http://example.org/tests/js/xpconnect/tests/mochitest/file_empty.html" onload="go()"></iframe>
+
+</body>
+</html>
diff --git a/js/xpconnect/tests/chrome/test_bug1530146.html b/js/xpconnect/tests/chrome/test_bug1530146.html
new file mode 100644
index 0000000000..59be912027
--- /dev/null
+++ b/js/xpconnect/tests/chrome/test_bug1530146.html
@@ -0,0 +1,58 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1530146
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1530146</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://global/skin"/>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+ /** Test for Bug 1530146 **/
+ SimpleTest.waitForExplicitFinish();
+
+ addLoadEvent(setupTest);
+
+ var sb;
+
+ function setupTest() {
+ // Create a sandbox with an expanded principal for our iframe.
+ sb = new Cu.Sandbox([frames[0].document.nodePrincipal],
+ {sandboxPrototype: frames[0]});
+ // Grab a waiver for the subframe in the sandbox to make sure the waiver
+ // stays alive. It would be nice if we could just use waiveXray in the
+ // sandbox: https://bugzilla.mozilla.org/show_bug.cgi?id=1531614
+ Cu.evalInSandbox('this.waiver = document.querySelector("iframe").contentWindow.wrappedJSObject',
+ sb);
+ var ifr = frames[0].document.querySelector("iframe");
+ ifr.onload = doTest;
+ ifr.src = "file_bug1530146_inner.html";
+ }
+
+function doTest() {
+ // Create a new sandbox for the iframe's subframe
+ var sb2 = new Cu.Sandbox([frames[0][0].document.nodePrincipal],
+ {sandboxPrototype: frames[0][0]});
+ // Reget the waiver; this is where things can go awry.
+ Cu.evalInSandbox('this.waiver = window.wrappedJSObject', sb2);
+ is(Cu.evalInSandbox("this.waiver.obj.a", sb2), "hello",
+ "Should get the right value and not crash");
+ is(Cu.evalInSandbox("(new this.waiver.Image()).localName", sb2), "img",
+ "Should create an image and not crash");
+ SimpleTest.finish();
+ }
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1530146">Mozilla Bug 1530146</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+<iframe src="http://mochi.test:8888/chrome/js/xpconnect/tests/chrome/file_bug1530146.html"></iframe>
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/js/xpconnect/tests/chrome/test_bug361111.xhtml b/js/xpconnect/tests/chrome/test_bug361111.xhtml
new file mode 100644
index 0000000000..4f1f89cf86
--- /dev/null
+++ b/js/xpconnect/tests/chrome/test_bug361111.xhtml
@@ -0,0 +1,33 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=361111
+-->
+<window title="Mozilla Bug 361111"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=361111"
+ target="_blank">Mozilla Bug 361111</a>
+ </body>
+
+ <!-- test code goes here -->
+ <script type="application/javascript"><![CDATA[
+
+ /** Test for Bug 361111 **/
+ window.onerror = null;
+ SimpleTest.waitForExplicitFinish();
+ document.documentElement.setAttribute("onclick", "%");
+ is(1, 1, "Good, setting a bogus onclick did not throw.");
+
+ // Bonus test - make sure that flushPrefEnv is appropriately
+ // called at the end of the test. It would be nice if there were
+ // somewhere in the harness that this could live, but there isn't.
+ SpecialPowers.pushPrefEnv({set: [['testing.some_arbitrary_pref', true]]},
+ function() { SimpleTest.finish(); });
+
+ ]]></script>
+</window>
diff --git a/js/xpconnect/tests/chrome/test_bug448587.xhtml b/js/xpconnect/tests/chrome/test_bug448587.xhtml
new file mode 100644
index 0000000000..cedab9ebbd
--- /dev/null
+++ b/js/xpconnect/tests/chrome/test_bug448587.xhtml
@@ -0,0 +1,35 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=448587.xul
+-->
+<window title="Mozilla Bug 503926"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=448587"
+ target="_blank">Mozilla Bug 448587</a>
+ </body>
+
+ <!-- test code goes here -->
+ <script type="application/javascript">
+ <![CDATA[
+
+ // Bonus test - collaborate with test_bug361111.xhtml to make sure that
+ // flushPrefEnv is appropriately called.
+ ok(!SpecialPowers.Services.prefs.prefHasUserValue('testing.some_arbitrary_pref'),
+ "Pref shouldn't carry over from previous test!");
+
+
+ /** Test for Bug 448587 **/
+ var sandbox = new Cu.Sandbox("about:blank");
+ var fwrapper = Cu.evalInSandbox("function f() {} f", sandbox);
+ is(Cu.unwaiveXrays(Cu.waiveXrays(fwrapper).prototype), Cu.evalInSandbox("f.prototype", sandbox),
+ ".prototype visible through .wrappedJSObject");
+ is(fwrapper.prototype, undefined, ".prototype invisible through Xrays");
+ ]]>
+ </script>
+</window>
diff --git a/js/xpconnect/tests/chrome/test_bug484459.xhtml b/js/xpconnect/tests/chrome/test_bug484459.xhtml
new file mode 100644
index 0000000000..59f2f4ea60
--- /dev/null
+++ b/js/xpconnect/tests/chrome/test_bug484459.xhtml
@@ -0,0 +1,37 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=484459
+-->
+<window title="Mozilla Bug 484459"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <iframe type="content"
+ src="./file_bug484459.html"
+ onload="go()"
+ id="ifr">
+ </iframe>
+ </body>
+
+ <!-- test code goes here -->
+ <script type="application/javascript"><![CDATA[
+ SimpleTest.waitForExplicitFinish();
+ var url = "chrome://mochitests/content/chrome/js/xpconnect/tests/chrome/test_bug484459.xhtml";
+ function go() {
+ var w = $('ifr').contentWindow.wrappedJSObject;
+ var sandbox = new Cu.Sandbox(w);
+ sandbox.__proto__ = w;
+ is(location.href, url, "location.href is set properly");
+ ok(w.location.href.endsWith("file_bug484459.html"),
+ "contents of w.location are correct");
+ is(Cu.evalInSandbox("x * 4", sandbox), 12,
+ "Unexpected return from the sandbox");
+ SimpleTest.finish();
+ }
+ ]]></script>
+</window>
diff --git a/js/xpconnect/tests/chrome/test_bug500931.xhtml b/js/xpconnect/tests/chrome/test_bug500931.xhtml
new file mode 100644
index 0000000000..68effb7342
--- /dev/null
+++ b/js/xpconnect/tests/chrome/test_bug500931.xhtml
@@ -0,0 +1,40 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=500931
+-->
+<window title="Mozilla Bug 500931"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=500931"
+ target="_blank">Mozilla Bug 500931</a>
+ </body>
+
+ <!-- test code goes here -->
+ <script type="application/javascript"><![CDATA[
+
+ /** Test for Bug 500931 **/
+
+ function go() {
+ var ifr = document.getElementById("ifr");
+ var doc = ifr.contentDocument;
+ ok(Cu.isXrayWrapper(doc), "doc is an XrayWrapper");
+ var weak = Cu.getWeakReference(doc);
+ ok(Cu.isXrayWrapper(weak.get()), "weak reference returns a wrapper");
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+
+ ]]></script>
+ <iframe type="content"
+ src="http://example.org/tests/js/xpconnect/tests/mochitest/bug500931_helper.html"
+ onload="go()"
+ id="ifr">
+ </iframe>
+</window>
diff --git a/js/xpconnect/tests/chrome/test_bug503926.xhtml b/js/xpconnect/tests/chrome/test_bug503926.xhtml
new file mode 100644
index 0000000000..cfe6315fd8
--- /dev/null
+++ b/js/xpconnect/tests/chrome/test_bug503926.xhtml
@@ -0,0 +1,58 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=503926
+-->
+<window title="Mozilla Bug 503926"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=503926"
+ target="_blank">Mozilla Bug 503926</a>
+
+ <iframe id="ifr" type="content" onload="loaded()" src="bug503926.xhtml#iframe"/>
+ <iframe id="ifrContent" type="content" onload="loaded()" src="http://example.org/tests/js/xpconnect/tests/mochitest/file_empty.html"/>
+ </body>
+
+ <!-- test code goes here -->
+ <script type="application/javascript">
+ <![CDATA[
+
+ SimpleTest.expectAssertions(0, 1);
+
+ var gLoadCount = 0;
+ function loaded() {
+ if (++gLoadCount == 2)
+ go();
+ }
+
+ /** Test for Bug 503926 **/
+ function go() {
+ var gWindowUtils = window.windowUtils;
+
+ // Try with a chrome object.
+ var passed = false;
+ // eslint-disable-next-line mozilla/use-chromeutils-generateqi
+ var obj = { QueryInterface() { passed = true; } };
+ gWindowUtils.xpconnectArgument(obj);
+ ok(passed, "trusted QIs should be called");
+
+ // Try with a content object.
+ var contentWin = $('ifrContent').contentWindow.wrappedJSObject;
+ contentWin.passed = false;
+ var contentObj = contentWin.eval('({ QueryInterface: function() { passed = true; } })');
+ gWindowUtils.xpconnectArgument(contentObj);
+ ok(!contentWin.passed, "untrusted QI should not be called");
+
+ // Try with a dialog.
+ window.browsingContext.topChromeWindow.openDialog("bug503926.xhtml", "chromeDialog", "modal", window);
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ ]]>
+ </script>
+</window>
diff --git a/js/xpconnect/tests/chrome/test_bug533596.xhtml b/js/xpconnect/tests/chrome/test_bug533596.xhtml
new file mode 100644
index 0000000000..3c3e610e81
--- /dev/null
+++ b/js/xpconnect/tests/chrome/test_bug533596.xhtml
@@ -0,0 +1,58 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=533596
+-->
+<window title="Mozilla Bug 533596"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=533596"
+ target="_blank">Mozilla Bug 533596</a>
+ </body>
+
+ <!-- test code goes here -->
+ <script type="application/javascript"><![CDATA[
+
+ // XPCNativeWrapper is not defined globally in ESLint as it may be going away.
+ // See bug 1481337.
+ /* global XPCNativeWrapper */
+
+ /** Test for Bug 533596 **/
+
+ function go() {
+ try { XPCNativeWrapper.unwrap(); } catch (e) {}
+ try { XPCNativeWrapper.unwrap(0); } catch (e) {}
+ try { XPCNativeWrapper.unwrap(null); } catch (e) {}
+
+ var o = {};
+ is(o, XPCNativeWrapper.unwrap(o), "unwrap on a random object returns it");
+
+ var win = $('ifr').contentWindow;
+ var utils = window.windowUtils;
+ is(utils.getClassName(win), "Proxy", "win is a Proxy");
+ ok("x" in XPCNativeWrapper.unwrap(win), "actually unwrapped");
+ is(utils.getClassName(XPCNativeWrapper.unwrap(win)), "Proxy",
+ "unwrap on an Proxy returns the same object");
+ is(utils.getClassName(XPCNativeWrapper.unwrap(new XPCNativeWrapper(win))), "Proxy",
+ "unwrap on an explicit NW works too");
+
+ ok(utils.getClassName(window) !== "XPCNativeWrapper", "window is not a native wrapper");
+ ok(utils.getClassName(XPCNativeWrapper.unwrap(new XPCNativeWrapper(window))) !== "XPCSafeJSObjectWrapper",
+ "unwrapping a chrome object returns the object itself");
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+
+ ]]></script>
+ <iframe type="content"
+ src="http://example.org/tests/js/xpconnect/tests/mochitest/bug500931_helper.html"
+ onload="go()"
+ id="ifr">
+ </iframe>
+</window>
diff --git a/js/xpconnect/tests/chrome/test_bug571849.xhtml b/js/xpconnect/tests/chrome/test_bug571849.xhtml
new file mode 100644
index 0000000000..3e4f6d6cc1
--- /dev/null
+++ b/js/xpconnect/tests/chrome/test_bug571849.xhtml
@@ -0,0 +1,44 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=500931
+-->
+<window title="Mozilla Bug 500931"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=500931"
+ target="_blank">Mozilla Bug 500931</a>
+
+ <!-- test code goes here -->
+ <script type="application/javascript"><![CDATA[
+
+ /** Test for Bug 500931 **/
+
+ function go() {
+ var ifr = document.getElementById('ifr');
+ var docnodes = ifr.contentDocument.body.childNodes;
+ var index, value;
+ for (let i of Object.entries(docnodes)) {
+ index = i[0];
+ value = i[1];
+ }
+ is(index, "0", "enumerated the 0th element");
+ ok(Text.isInstance(value), "the 0th element was a text node");
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+
+ ]]></script>
+ <iframe type="content"
+ src="http://example.org/tests/js/xpconnect/tests/mochitest/bug571849_helper.html"
+ onload="go()"
+ id="ifr">
+ </iframe>
+ </body>
+</window>
diff --git a/js/xpconnect/tests/chrome/test_bug610390.xhtml b/js/xpconnect/tests/chrome/test_bug610390.xhtml
new file mode 100644
index 0000000000..e4d8a48477
--- /dev/null
+++ b/js/xpconnect/tests/chrome/test_bug610390.xhtml
@@ -0,0 +1,32 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=610390
+-->
+<window title="Mozilla Bug 610390"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <iframe type="content"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ src="data:text/html,&lt;script&gt;var x=3&lt;/script&gt;"
+ onload="go()"
+ id="ifr">
+ </iframe>
+ </body>
+
+ <!-- test code goes here -->
+ <script type="application/javascript"><![CDATA[
+ SimpleTest.waitForExplicitFinish();
+
+ function go() {
+ var w = $('ifr').contentWindow;
+ is(w.wrappedJSObject, w.wrappedJSObject, "wrappedJSObject identity not maintained");
+ SimpleTest.finish();
+ }
+ ]]></script>
+</window>
diff --git a/js/xpconnect/tests/chrome/test_bug614757.xhtml b/js/xpconnect/tests/chrome/test_bug614757.xhtml
new file mode 100644
index 0000000000..34058e38a3
--- /dev/null
+++ b/js/xpconnect/tests/chrome/test_bug614757.xhtml
@@ -0,0 +1,33 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=614757
+-->
+<window title="Mozilla Bug 601803"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=614757"
+ target="_blank">Mozilla Bug 614757</a>
+
+ <!-- test code goes here -->
+ <script type="application/javascript"><![CDATA[
+
+ /** Test for Bug 614757 **/
+
+ function go() {
+ is($('ifr').contentDocument.wrappedJSObject.getElementsByTagName('body')[0].toString().indexOf('Xray'),
+ -1, "Properly deep wrap");
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+
+ ]]></script>
+ <iframe type="content" src="http://mochi.test:8888/tests/js/xpconnect/tests/mochitest/file_evalInSandbox.html" onload="go()" id="ifr" />
+ </body>
+</window>
diff --git a/js/xpconnect/tests/chrome/test_bug616992.xhtml b/js/xpconnect/tests/chrome/test_bug616992.xhtml
new file mode 100644
index 0000000000..52a97cee3d
--- /dev/null
+++ b/js/xpconnect/tests/chrome/test_bug616992.xhtml
@@ -0,0 +1,30 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=616992
+-->
+<window title="Mozilla Bug 601803"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=616992"
+ target="_blank">Mozilla Bug 616992</a>
+
+ <!-- test code goes here -->
+ <script type="application/javascript"><![CDATA[
+
+ /** Test for Bug 616992 **/
+
+ var sandbox = new Cu.Sandbox(window);
+ sandbox.w = window;
+ var actual = Cu.evalInSandbox("Object.keys(w.NodeFilter)", sandbox).sort().toString();
+ var expected = Object.keys(NodeFilter).sort().toString();
+ is(actual, expected, "interface constants are visible inside sandboxes");
+
+ ]]></script>
+ </body>
+</window>
diff --git a/js/xpconnect/tests/chrome/test_bug618176.xhtml b/js/xpconnect/tests/chrome/test_bug618176.xhtml
new file mode 100644
index 0000000000..63e93c9b56
--- /dev/null
+++ b/js/xpconnect/tests/chrome/test_bug618176.xhtml
@@ -0,0 +1,30 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=618176
+-->
+<window title="Mozilla Bug 618176"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=618176"
+ target="_blank">Mozilla Bug 618176</a>
+ </body>
+
+ <!-- test code goes here -->
+ <script type="application/javascript"><![CDATA[
+ SimpleTest.waitForExplicitFinish();
+
+ function done() {
+ SimpleTest.finish();
+ }
+
+ addLoadEvent(function() {
+ window.openDialog("file_bug618176.xhtml", "", "chrome,noopener", window);
+ });
+ ]]></script>
+</window>
diff --git a/js/xpconnect/tests/chrome/test_bug654370.xhtml b/js/xpconnect/tests/chrome/test_bug654370.xhtml
new file mode 100644
index 0000000000..e1afc32e38
--- /dev/null
+++ b/js/xpconnect/tests/chrome/test_bug654370.xhtml
@@ -0,0 +1,27 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=654370
+-->
+<window title="Mozilla Bug 654370"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=654370"
+ target="_blank">Mozilla Bug 654370</a>
+ </body>
+
+ <!-- test code goes here -->
+ <script type="application/javascript"><![CDATA[
+
+var sandbox = new Cu.Sandbox(window);
+var script = "(function (obj, type) { return obj instanceof type; })";
+var instanceOf = Cu.evalInSandbox(script, sandbox, "1.8", "Test", 1);
+ok(!instanceOf({}, Window), "instanceOf from the sandbox gets the right result");
+
+ ]]></script>
+</window>
diff --git a/js/xpconnect/tests/chrome/test_bug658560.xhtml b/js/xpconnect/tests/chrome/test_bug658560.xhtml
new file mode 100644
index 0000000000..78b623a828
--- /dev/null
+++ b/js/xpconnect/tests/chrome/test_bug658560.xhtml
@@ -0,0 +1,38 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=658560
+-->
+<window title="Mozilla Bug 658560"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=658560"
+ target="_blank">Mozilla Bug 658560</a>
+ </body>
+
+ <!-- test code goes here -->
+ <script type="application/javascript"><![CDATA[
+
+ SimpleTest.waitForExplicitFinish();
+ function go() {
+ var win = $('ifr').contentWindow.wrappedJSObject;
+ let o = { prop: true };
+ win.foo = o;
+
+ is(win.foo, o, "should have === identity through a CrossOriginWrapper");
+ SimpleTest.finish();
+ }
+
+ ]]></script>
+
+ <iframe
+ src="http://example.org/tests/js/xpconnect/tests/mochitest/file_bug658560.html"
+ id="ifr"
+ type="content"
+ onload="go()" />
+</window>
diff --git a/js/xpconnect/tests/chrome/test_bug658909.xhtml b/js/xpconnect/tests/chrome/test_bug658909.xhtml
new file mode 100644
index 0000000000..360b8045e2
--- /dev/null
+++ b/js/xpconnect/tests/chrome/test_bug658909.xhtml
@@ -0,0 +1,92 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=658909
+-->
+<window title="Mozilla Bug 658909"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=658909"
+ target="_blank">Mozilla Bug 658909</a>
+ </body>
+
+ <!-- test code goes here -->
+ <script type="application/javascript">
+ <![CDATA[
+ /** Test for call/apply-ing Xray methods.**/
+ SimpleTest.waitForExplicitFinish();
+
+ let gLoadCount = 0;
+ function frameLoaded() {
+ if (++gLoadCount == frames.length)
+ go();
+ }
+
+ function msg(a, b, testName) {
+ return "(" + a.name + ", " + b.name + "): " + testName;
+ }
+
+ var testFunctions = {
+ testDocumentElement(a, b, name) {
+ var getter = Object.prototype.__lookupGetter__.call(a.document, 'documentElement');
+ is(getter.call(b.document), b.document.documentElement, msg(a, b, name));
+ },
+
+ testInvalidCall(a, b, name) {
+ var getter = Object.prototype.__lookupGetter__.call(a.document, 'documentElement');
+ var threw = false;
+ try { getter.call(b.document.body); } catch (e) { threw = true; };
+ ok(threw, msg(a, b, name));
+ },
+
+ testStatus(a, b, name) {
+ var setter = Object.prototype.__lookupSetter__.call(a, 'status');
+ is(b.status, "", "Empty status");
+ setter.call(b, "foopy");
+ is(b.status, "foopy", msg(a, b, name));
+ b.status = "";
+ },
+
+ testCreateElement(a, b, name) {
+ is(a.document.createElement.call(b.document, 'div').ownerDocument, b.document, msg(a, b, name));
+ },
+
+ testWindowName(a, b, name) {
+ var getter = Object.prototype.__lookupGetter__.call(a, 'name');
+ is(getter.call(b), b.name, msg(a, b, name));
+ },
+
+ testCanvas(a, b, name) {
+ var canvasA = a.document.createElement('canvas');
+ var canvasB = b.document.createElement('canvas');
+ var contextA = canvasA.getContext('2d');
+ var contextB = canvasB.getContext('2d');
+ var getter = Object.prototype.__lookupGetter__.call(contextA, 'canvas');
+ is(getter.call(contextB), canvasB, msg(a, b, name));
+ }
+ };
+
+ function go() {
+ for (let i = 0; i < frames.length; ++i)
+ frames[i].name = 'frame' + i;
+ for (let i = 0; i < frames.length; ++i) {
+ for (let j = 0; j < frames.length; ++j) {
+ for (let k in testFunctions)
+ testFunctions[k](frames[i], frames[j], k);
+ }
+ }
+
+ SimpleTest.finish();
+ }
+
+
+ ]]>
+ </script>
+ <iframe id="frame1" onload="frameLoaded();" type="content" src="http://example.org/tests/js/xpconnect/tests/mochitest/file_empty.html" />
+ <iframe id="frame2" onload="frameLoaded();" type="content" src="http://example.org/tests/js/xpconnect/tests/mochitest/file_empty.html" />
+ <iframe id="frame3" onload="frameLoaded();" type="content" src="http://example.com/tests/js/xpconnect/tests/mochitest/file_empty.html" />
+</window>
diff --git a/js/xpconnect/tests/chrome/test_bug664689.xhtml b/js/xpconnect/tests/chrome/test_bug664689.xhtml
new file mode 100644
index 0000000000..45a39b1be9
--- /dev/null
+++ b/js/xpconnect/tests/chrome/test_bug664689.xhtml
@@ -0,0 +1,28 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=658560
+-->
+<window title="Mozilla Bug 658560"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=658560"
+ target="_blank">Mozilla Bug 658560</a>
+ </body>
+
+ <!-- test code goes here -->
+ <script type="application/javascript"><![CDATA[
+
+ var sandbox = new Cu.Sandbox("about:blank");
+ var contentObj = Cu.evalInSandbox("({ get foo() { return 42 } })", sandbox);
+ var propdesc = Object.getOwnPropertyDescriptor(Cu.waiveXrays(contentObj), "foo");
+ is(typeof propdesc, "object", "got a property descriptor back");
+ ok("get" in propdesc, "getter exists");
+
+ ]]></script>
+</window>
diff --git a/js/xpconnect/tests/chrome/test_bug679861.xhtml b/js/xpconnect/tests/chrome/test_bug679861.xhtml
new file mode 100644
index 0000000000..302b66c966
--- /dev/null
+++ b/js/xpconnect/tests/chrome/test_bug679861.xhtml
@@ -0,0 +1,38 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=500931
+-->
+<window title="Mozilla Bug 601803"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=601803"
+ target="_blank">Mozilla Bug 601803</a>
+
+ <!-- test code goes here -->
+ <script type="application/javascript"><![CDATA[
+
+ /** Test for Bug 601803 **/
+
+ function go() {
+ var doc = document.getElementById('ifr').contentDocument;
+ var list = doc.getElementsByTagName("body");
+ var zeroAsString = "0";
+ is(typeof list[zeroAsString], "object",
+ "can lookup nodelist items by string");
+ is(typeof list[0], "object",
+ "can lookup nodelist items by integer after the lookup by string");
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+
+ ]]></script>
+ <iframe type="content" src="about:blank" onload="go()" id="ifr" />
+ </body>
+</window>
diff --git a/js/xpconnect/tests/chrome/test_bug706301.xhtml b/js/xpconnect/tests/chrome/test_bug706301.xhtml
new file mode 100644
index 0000000000..867f9222d2
--- /dev/null
+++ b/js/xpconnect/tests/chrome/test_bug706301.xhtml
@@ -0,0 +1,52 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=706301
+-->
+<window title="Mozilla Bug 706301"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=706301"
+ target="_blank">Mozilla Bug 706301</a>
+ <iframe id="ifr" src="http://mochi.test:8888/tests/js/xpconnect/tests/mochitest/file_bug706301.html" onload="doTest();" />
+ </body>
+
+ <!-- test code goes here -->
+ <script type="application/javascript">
+ <![CDATA[
+ /** Test for Bug 706301 **/
+
+ SimpleTest.waitForExplicitFinish();
+
+ function getLengthInChrome(nodelist) {
+
+ // Make sure the object is Xray wrapped.
+ is(nodelist, Cu.unwaiveXrays(nodelist),
+ "object passed from content to chrome should be Xray-wrapped.");
+
+ // Perform the operation.
+ Object.getOwnPropertyDescriptor(nodelist, 'length');
+ return !nodelist.length;
+ }
+
+ function finishTestInChrome() {
+ SimpleTest.finish();
+ }
+
+ function doTest() {
+
+ // Set up the callbacks for content.
+ $('ifr').contentWindow.wrappedJSObject.getLengthInChrome = getLengthInChrome;
+ $('ifr').contentWindow.wrappedJSObject.finishTestInChrome = finishTestInChrome;
+ $('ifr').contentWindow.wrappedJSObject.ok = ok;
+
+ // Kick off the test.
+ $('ifr').contentWindow.postMessage({}, '*');
+ }
+ ]]>
+ </script>
+</window>
diff --git a/js/xpconnect/tests/chrome/test_bug720619.xhtml b/js/xpconnect/tests/chrome/test_bug720619.xhtml
new file mode 100644
index 0000000000..335218886b
--- /dev/null
+++ b/js/xpconnect/tests/chrome/test_bug720619.xhtml
@@ -0,0 +1,46 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=720619
+-->
+<window title="Mozilla Bug 720619"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=720619"
+ target="_blank">Mozilla Bug 720619</a>
+
+ <!-- test code goes here -->
+ <script type="application/javascript"><![CDATA[
+ /** Test for Bug 720619 **/
+ var obj = {
+ valueOf () {
+ return 42;
+ },
+ toString () {
+ return 'str';
+ }
+ };
+
+ var content = new Cu.Sandbox("about:blank");
+ content.obj = obj;
+
+ ok(Cu.evalInSandbox("obj + ''", content) == "[object Object]");
+ ok(Cu.evalInSandbox("'' + obj", content) == "[object Object]");
+ ok(isNaN(Cu.evalInSandbox("obj - 0", content)));
+ ok(Cu.evalInSandbox("String(obj)", content) == "[object Object]");
+
+ var chrome = new Cu.Sandbox(window);
+ chrome.obj = obj;
+
+ ok(Cu.evalInSandbox("obj + ''", chrome) == "42");
+ ok(Cu.evalInSandbox("'' + obj", chrome) == "42");
+ ok(Cu.evalInSandbox("obj - 0", chrome) == 42);
+ ok(Cu.evalInSandbox("String(obj)", chrome) == "str");
+ ]]></script>
+ </body>
+</window>
diff --git a/js/xpconnect/tests/chrome/test_bug726949.xhtml b/js/xpconnect/tests/chrome/test_bug726949.xhtml
new file mode 100644
index 0000000000..b02b200893
--- /dev/null
+++ b/js/xpconnect/tests/chrome/test_bug726949.xhtml
@@ -0,0 +1,41 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=726949
+-->
+<window title="Mozilla Bug 726949"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ style="display: block;">
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=726949"
+ target="_blank">Mozilla Bug 726949</a>
+ </body>
+
+ <!-- test code goes here -->
+ <script type="application/javascript">
+ <![CDATA[
+ /** Test for Bug 726949 **/
+ var s = new Cu.Sandbox(window, { sandboxPrototype: window } );
+ var t;
+ var desc;
+ try {
+ t = Cu.evalInSandbox('top', s);
+ is(t, window.top, "Should have gotten the right thing back");
+ desc = Cu.evalInSandbox('Object.getOwnPropertyDescriptor(Object.getPrototypeOf(this), "Cu")', s);
+ isnot(desc, undefined,
+ "Should have an own 'Cu' property");
+ is(desc.value, Cu, "Should have the right value");
+ var loc = Cu.evalInSandbox('location', s);
+ is(loc.href, location.href, "Should have the right location");
+ var display = Cu.evalInSandbox('getComputedStyle(document.documentElement, "").display', s);
+ is(display, "block", "Should be able to call getComputedStyle");
+ } catch (e) {
+ ok(false, "Should not get an exception: " + e);
+ }
+ ]]>
+ </script>
+</window>
diff --git a/js/xpconnect/tests/chrome/test_bug732665.xhtml b/js/xpconnect/tests/chrome/test_bug732665.xhtml
new file mode 100644
index 0000000000..0ffba66ebc
--- /dev/null
+++ b/js/xpconnect/tests/chrome/test_bug732665.xhtml
@@ -0,0 +1,92 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=732665
+-->
+<window title="Mozilla Bug 732665"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=732665"
+ target="_blank">Mozilla Bug 732665</a>
+ </body>
+
+ <!-- test code goes here -->
+ <script type="application/javascript">
+ <![CDATA[
+
+add_task(async () => {
+ await SpecialPowers.pushPrefEnv({"set": [["security.allow_eval_with_system_principal",
+ true]]});
+ //
+ // Important! If this test starts failing after a tricky platform-y change,
+ // the stack quota numbers in XPCJSContext probably need twiddling. We want
+ // to maintain the invariants in this test (at least to some approximation)
+ // for security reasons.
+ //
+
+ // Executes f() d steps from the probed native stack limit, and returns
+ // the number of steps to the recursion limit from the caller.
+ function nearNativeStackLimit(d, f) {
+ f = f || function() {};
+ let failed = null;
+ function inner() {
+ try {
+ // eslint-disable-next-line no-eval
+ var stepsFromLimit = eval("inner()"); // Use eval to force a number of native stackframes to be created.
+ if (stepsFromLimit == d) {
+ try {
+ f();
+ } catch(e) {
+ // If we didn't have enough stack space to call the (possibly
+ // trivial) test function above, we obviously can't expect to call
+ // into the test harness assertion code successfully.
+ failed = e;
+ }
+ }
+ return stepsFromLimit + 1;
+ } catch(e) {
+ // It would be nice to check here that the exception is actually an
+ // over-recursion here. But doing so would require toString()ing the
+ // exception, which we may not have the stack space to do.
+ return 0;
+ }
+ }
+ let result = inner();
+ ok(!failed, `nearNativeStackLimit callback threw: ${failed}`);
+ return result;
+ }
+
+ var contentSb = new Cu.Sandbox("https://www.example.com");
+ var chromeSb = new Cu.Sandbox(window);
+ chromeSb.ok = contentSb.ok = ok;
+ Cu.evalInSandbox(nearNativeStackLimit.toSource(), chromeSb);
+ Cu.evalInSandbox(nearNativeStackLimit.toSource(), contentSb);
+ var chromeLimit = Cu.evalInSandbox("nearNativeStackLimit(0);", chromeSb);
+ var contentLimit = Cu.evalInSandbox("nearNativeStackLimit(0)", contentSb);
+ ok(chromeLimit >= contentLimit + 10,
+ "Chrome should be able to have at least 10 heavy frames more stack than content: " + chromeLimit + ", " + contentLimit);
+
+ // Exhaust the stack space in content, and then make sure we can still get 10
+ // heavy frames in chrome.
+ //
+ // Note that sometimes, if we pass |0| to nearNativeStackLimit, we can end up
+ // so close to the border in content that we can't even get ourselves together
+ // enough to make the cross-compartment call. So rather than exhausting the
+ // stack entirely and then checking for 10 chrome frames, we leave ourselves
+ // one frame's worth, and check for 11.
+ //
+ // If this assertion fails, the current work-around so far is to measure
+ // again the worst frame size, by using the JS Shell to run
+ // test_bug732665_meta.js . This script will output numbers to update
+ // XPCJSContext.cpp comment, as well as the kTrustedScriptBuffer constant.
+ contentSb.nnslChrome = chromeSb.nearNativeStackLimit;
+ var nestedLimit = Cu.evalInSandbox("nearNativeStackLimit(1, function() { nestedLimit = nnslChrome(0);}); nestedLimit;", contentSb);
+ ok(nestedLimit >= 11, "Chrome should be invokable from content script with an exhausted stack: " + nestedLimit);
+});
+ ]]>
+ </script>
+</window>
diff --git a/js/xpconnect/tests/chrome/test_bug732665_meta.js b/js/xpconnect/tests/chrome/test_bug732665_meta.js
new file mode 100644
index 0000000000..92bf9066b3
--- /dev/null
+++ b/js/xpconnect/tests/chrome/test_bug732665_meta.js
@@ -0,0 +1,34 @@
+/* globals stackPointerInfo */
+
+var stackBottom = stackPointerInfo();
+var stackTop = stackBottom;
+
+function nearNativeStackLimit() {
+ function inner() {
+ try {
+ stackTop = stackPointerInfo();
+ // eslint-disable-next-line no-eval
+ var stepsFromLimit = eval("inner()"); // Use eval to force a number of native stackframes to be created.
+ return stepsFromLimit + 1;
+ } catch (e) {
+ // It would be nice to check here that the exception is actually an
+ // over-recursion here. But doing so would require toString()ing the
+ // exception, which we may not have the stack space to do.
+ return 1;
+ }
+ }
+ return inner();
+}
+
+var nbFrames = nearNativeStackLimit();
+var frameSize = stackBottom - stackTop;
+print(
+ "Max stack size:",
+ frameSize,
+ "bytes",
+ "\nMaximum number of frames:",
+ nbFrames,
+ "\nAverage frame size:",
+ Math.ceil(frameSize / nbFrames),
+ "bytes"
+);
diff --git a/js/xpconnect/tests/chrome/test_bug738244.xhtml b/js/xpconnect/tests/chrome/test_bug738244.xhtml
new file mode 100644
index 0000000000..96d0d880e6
--- /dev/null
+++ b/js/xpconnect/tests/chrome/test_bug738244.xhtml
@@ -0,0 +1,58 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=533596
+-->
+<window title="Mozilla Bug 533596"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+
+ <iframe src="http://example.org/tests/js/xpconnect/tests/mochitest/file_bug738244.html"
+ onload="xrayTest(this)">
+ </iframe>
+ </body>
+
+ <!-- test code goes here -->
+ <script type="application/javascript"><![CDATA[
+
+ SimpleTest.waitForExplicitFinish();
+
+ function xrayTest(ifr) {
+ var win = ifr.contentWindow;
+ var doc = ifr.contentDocument;
+
+ doc.getElementById = 42;
+ is(doc.getElementById, 42,
+ "Native property cannot be shadowed on the xray");
+
+ is(doc.form1.name, "form1",
+ "Form elements cannot be found by name on the document through xray");
+
+ is(doc.form1.input1.name, "input1",
+ "Input element cannot be found by name on a form element through xray");
+
+ is(typeof doc.form1.appendChild, "function",
+ "Input element shadows native property with its name through xray");
+
+ is(win.frame1, undefined,
+ "IFrames should not be found by name on the window through xray");
+
+ is(win[0].name, "frame1",
+ "IFrames should be found by index on the window through xray");
+
+ win["1000"] = "foopy";
+ ok(!("1000" in win), "Shouldn't be able to add indexed expandos to xray");
+
+ win["1000a"] = "foopy";
+ ok("1000a" in win, "Should be able to add named expandos to xray");
+
+ SimpleTest.finish();
+ }
+
+ ]]></script>
+</window>
diff --git a/js/xpconnect/tests/chrome/test_bug743843.xhtml b/js/xpconnect/tests/chrome/test_bug743843.xhtml
new file mode 100644
index 0000000000..a80134ff9e
--- /dev/null
+++ b/js/xpconnect/tests/chrome/test_bug743843.xhtml
@@ -0,0 +1,39 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=743843
+-->
+<window title="Mozilla Bug 743843"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=743843"
+ target="_blank">Mozilla Bug 743843</a>
+ </body>
+
+ <!-- test code goes here -->
+ <script type="application/javascript">
+ <![CDATA[
+ /** Test for Components.Exception options objects. **/
+
+ // Note: We pass |window| as the 'data' field here because Components.Exception
+ // doesn't handle JS Objects here all that nicely. See bug 743121.
+
+ // Test the old interface.
+ var e1 = Components.Exception('foo', Cr.NS_BINDING_ABORTED, null, window);
+ is(e1.result, Cr.NS_BINDING_ABORTED, "Result should get set properly");
+ is(e1.data, window, "User data should get set properly");
+
+ // Test the options object.
+ var e2 = Components.Exception('foo', { result: Cr.NS_BINDING_ABORTED,
+ data: window,
+ foobar: 2 });
+ is(e2.result, Cr.NS_BINDING_ABORTED, "Result should get set properly");
+ is(e2.data.window, window, "User data should get set properly");
+
+ ]]>
+ </script>
+</window>
diff --git a/js/xpconnect/tests/chrome/test_bug760076.xhtml b/js/xpconnect/tests/chrome/test_bug760076.xhtml
new file mode 100644
index 0000000000..9d56455024
--- /dev/null
+++ b/js/xpconnect/tests/chrome/test_bug760076.xhtml
@@ -0,0 +1,49 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=760076
+-->
+<window title="Mozilla Bug 760076"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=760076"
+ target="_blank">Mozilla Bug 760076</a>
+ </body>
+
+ <!-- test code goes here -->
+ <script type="application/javascript">
+ <![CDATA[
+ /** Test for constructing COW-ed functions in content. **/
+
+ // This gets decompiled and run in the sandbox.
+ function sandboxCode() {
+ /* globals chromeFunction */
+ try {
+ is(chromeFunction(), 42, "Should call successfully");
+ } catch(e) { ok(false, "Shouldn't throw when calling"); }
+
+ try {
+ ok(typeof (new chromeFunction()) !== 'undefined',
+ "Should construct successfully");
+ } catch(e) { ok(false, "Shouldn't throw when constructing"); }
+ }
+
+ // Set up the sandbox.
+ var sb = new Cu.Sandbox("https://www.example.com");
+
+ // Import the functions it needs.
+ sb.ok = ok;
+ sb.is = is;
+ sb.chromeFunction = function() { ok(true, "Called chrome function"); return 42; }
+ Cu.evalInSandbox(sandboxCode.toSource(), sb);
+
+ // Do the test.
+ Cu.evalInSandbox("sandboxCode();", sb);
+
+ ]]>
+ </script>
+</window>
diff --git a/js/xpconnect/tests/chrome/test_bug760131.html b/js/xpconnect/tests/chrome/test_bug760131.html
new file mode 100644
index 0000000000..eca4467c8d
--- /dev/null
+++ b/js/xpconnect/tests/chrome/test_bug760131.html
@@ -0,0 +1,48 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=760131
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 760131</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=760131">Mozilla Bug 760131</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+ <iframe id="frame"></iframe>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 760131 **/
+
+var frame = document.getElementById("frame");
+function runTest()
+{
+ var win = frame.contentWindow;
+ win.wrappedJSObject.ok = ok;
+ var doc = win.document;
+ var empty = doc.createTouchList();
+ var event = doc.createEvent("touchevent");
+ event.initTouchEvent("touchstart", true, true, win, 0, false, false, false, false, empty, empty, empty);
+ doc.getElementById("target").dispatchEvent(event);
+
+ SimpleTest.finish();
+}
+
+SimpleTest.waitForExplicitFinish();
+SpecialPowers.pushPrefEnv(
+ {"set": [["dom.w3c_touch_events.legacy_apis.enabled", true]]},
+ function() {
+ frame.onload = runTest;
+ frame.src = "http://mochi.test:8888/tests/js/xpconnect/tests/mochitest/file_bug760131.html";
+ }
+);
+</script>
+</pre>
+</body>
+</html>
diff --git a/js/xpconnect/tests/chrome/test_bug763343.xhtml b/js/xpconnect/tests/chrome/test_bug763343.xhtml
new file mode 100644
index 0000000000..db3acd37c0
--- /dev/null
+++ b/js/xpconnect/tests/chrome/test_bug763343.xhtml
@@ -0,0 +1,35 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=763343
+-->
+<window title="Mozilla Bug 763343"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=763343"
+ target="_blank">Mozilla Bug 763343</a>
+ </body>
+
+ <!-- test code goes here -->
+ <script type="application/javascript">
+ <![CDATA[
+
+ /** Test for Cross-compartment nsIClassInfo singleton wrapping. **/
+ // We need an object here that has singleton classinfo. For now, the console
+ // service works.
+ var singleton = Cc["@mozilla.org/consoleservice;1"].getService(Ci.nsIClassInfo);
+ ok(singleton.flags & Ci.nsIClassInfo.SINGLETON_CLASSINFO,
+ "Should have singleton classinfo");
+ var sb = new Cu.Sandbox(window);
+
+ // Don't crash.
+ sb.singleton = singleton;
+ ok(true, "didn't crash");
+
+ ]]>
+ </script>
+</window>
diff --git a/js/xpconnect/tests/chrome/test_bug771429.xhtml b/js/xpconnect/tests/chrome/test_bug771429.xhtml
new file mode 100644
index 0000000000..c5846dd6ac
--- /dev/null
+++ b/js/xpconnect/tests/chrome/test_bug771429.xhtml
@@ -0,0 +1,66 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=771429
+-->
+<window title="Mozilla Bug 771429"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=771429"
+ target="_blank">Mozilla Bug 771429</a>
+ </body>
+
+ <!-- test code goes here -->
+ <script type="application/javascript">
+ <![CDATA[
+ /** Test for Bug 771429 **/
+ function f() {}
+ function g() { return this; }
+ function h() { "use strict"; return this; }
+ function ctor() { this.x = 1; }
+ f.x = 2;
+ f.g = g;
+ function test(freshCompartment) {
+ var s = new Cu.Sandbox(window, {
+ sandboxPrototype: window,
+ freshCompartment,
+ });
+ try {
+ is(Cu.evalInSandbox('g()', s), window,
+ "Should get window as this object of non-strict global function");
+ is(Cu.evalInSandbox('h()', s), undefined,
+ "Should get undefined as this object of strict global function");
+ is(Cu.evalInSandbox('f.x', s), 2,
+ "Should have gotten the right thing back");
+ if (freshCompartment) {
+ is(Cu.evalInSandbox('f.g()', s), f,
+ "Should have the right function object");
+ } else {
+ // In the same-compartment case we don't go through a compartment
+ // boundary so when we call f.g() we don't unwrap the "callable
+ // proxy" around f and the test above fails. We accept this slight
+ // difference in behavior and assert a weaker invariant.
+ is(Cu.evalInSandbox('f.g()', s).toString(), f.toString(),
+ "Should have the right function object");
+ }
+ is(Cu.evalInSandbox('var x = { z: 7 }; g.call(x).z', s), 7,
+ "Should not rebind calls that are already bound");
+ // And test what happens when we use the normal Function.prototype.call
+ // on g instead of whatever our proxies happen to return.
+ is(Cu.evalInSandbox('var x = { z: 7 }; Function.prototype.call.call(g, x).z', s), 7,
+ "Should not rebind calls that are already bound");
+ is(Cu.evalInSandbox('new ctor();', s).x, 1,
+ "Should get a properly constructed object out of the sandbox");
+ } catch (e) {
+ ok(false, "Should not get an exception: " + e);
+ }
+ }
+ test(false);
+ test(true);
+ ]]>
+ </script>
+</window>
diff --git a/js/xpconnect/tests/chrome/test_bug773962.xhtml b/js/xpconnect/tests/chrome/test_bug773962.xhtml
new file mode 100644
index 0000000000..2bdb43f6fc
--- /dev/null
+++ b/js/xpconnect/tests/chrome/test_bug773962.xhtml
@@ -0,0 +1,88 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=773962
+-->
+<window title="Mozilla Bug 773962"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=773962"
+ target="_blank">Mozilla Bug 773962</a>
+ </body>
+
+ <!-- test code goes here -->
+ <script type="application/javascript">
+ <![CDATA[
+
+ /** Test for remapping Xray waivers during brain transplant. **/
+ SimpleTest.waitForExplicitFinish();
+
+ var gFramesLoaded = 0;
+ function frameLoaded() {
+ ++gFramesLoaded;
+ if (gFramesLoaded == 2)
+ startTest();
+ if (gFramesLoaded == 3)
+ finishTest();
+ }
+
+ var win1;
+ var win2;
+ var node1;
+ var loc1;
+ var win1Waiver;
+ var node1Waiver;
+ var loc1Waiver;
+
+ function startTest() {
+ // grab the windows and the node.
+ win1 = document.getElementById('frame1').contentWindow;
+ win2 = document.getElementById('frame2').contentWindow;
+ node1 = win1.document.getElementById('text');
+ loc1 = win1.location;
+
+ // Grab some Xray waivers.
+ win1Waiver = win1.wrappedJSObject;
+ node1Waiver = node1.wrappedJSObject;
+ loc1Waiver = win1Waiver.location;
+
+ // Adopt node1 into win2. This causes node1 to be transplanted.
+ win2.document.adoptNode(node1);
+
+ // Navigate win1. This causes win1 to be transplanted.
+ win1.location = "https://test2.example.org/tests/js/xpconnect/tests/mochitest/file_empty.html";
+
+ // The above happens async. Our onload handler will call finishTest() when we're ready.
+ }
+
+ function finishTest() {
+ // Now, recompute some wrappers.
+ Cu.recomputeWrappers();
+
+ // First, pat ourselves on the back for not asserting/crashing. That's most of
+ // what we're really testing here.
+ ok(true, "Didn't crash!");
+
+ // Now, make sure everything is set up how we expect.
+ ok(Cu.isDeadWrapper(win1Waiver), "window waiver should go away after navigation");
+ ok(node1Waiver === node1.wrappedJSObject, "waivers still work");
+ ok(Cu.unwaiveXrays(node1Waiver) === node1, "waivers still work");
+
+ // The semantics of location are tricky, because win1 now has a new location object.
+ // In fact, loc1 should be a dead object proxy. Let's make sure we get this right.
+ ok(loc1 !== win1.location, "navigation means different window.location");
+ ok(loc1Waiver !== win1.location.wrappedJSObject, "navigation means different window.location");
+
+ // Whew.
+ SimpleTest.finish();
+ }
+
+ ]]>
+ </script>
+ <iframe id="frame1" onload="frameLoaded();" type="content" src="https://test1.example.org/tests/js/xpconnect/tests/mochitest/file_empty.html" />
+ <iframe id="frame2" onload="frameLoaded();" type="content" src="https://test1.example.org/tests/js/xpconnect/tests/mochitest/file_empty.html" />
+</window>
diff --git a/js/xpconnect/tests/chrome/test_bug792280.xhtml b/js/xpconnect/tests/chrome/test_bug792280.xhtml
new file mode 100644
index 0000000000..1e1a197e57
--- /dev/null
+++ b/js/xpconnect/tests/chrome/test_bug792280.xhtml
@@ -0,0 +1,43 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=792280
+-->
+<window title="Mozilla Bug 792280"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=792280"
+ target="_blank">Mozilla Bug 792280</a>
+ </body>
+
+ <!-- test code goes here -->
+ <script type="application/javascript">
+ <![CDATA[
+ /** Test for Bug 792280 **/
+ function checkSb(sb, expect) {
+ var target = new Cu.Sandbox("https://www.example.com");
+ Cu.evalInSandbox('function fun() { return arguments.callee.caller; };', target);
+ sb.fun = target.fun;
+ let allowed = false;
+ try {
+ allowed = Cu.evalInSandbox('function doTest() { return fun() == doTest; }; doTest()', sb);
+ isnot(expect, "throw", "Should have thrown");
+ } catch (e) {
+ is(expect, "throw", "Should expect exception");
+ ok(/denied|insecure/.test(e), "Should be a security exception: " + e);
+ }
+ is(allowed, expect == "allow", "should censor appropriately");
+ }
+
+ // Note that COWs are callable, but XOWs are not.
+ checkSb(new Cu.Sandbox("https://www.example.com"), "allow");
+ checkSb(new Cu.Sandbox("https://www.example.org"), "throw");
+ checkSb(new Cu.Sandbox(window), "censor");
+
+ ]]>
+ </script>
+</window>
diff --git a/js/xpconnect/tests/chrome/test_bug793433.xhtml b/js/xpconnect/tests/chrome/test_bug793433.xhtml
new file mode 100644
index 0000000000..eed7166efc
--- /dev/null
+++ b/js/xpconnect/tests/chrome/test_bug793433.xhtml
@@ -0,0 +1,44 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=793433
+-->
+<window title="Mozilla Bug 793433"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=793433"
+ target="_blank">Mozilla Bug 793433</a>
+ <p id="display"></p>
+ <div id="content" style="display: none"/>
+ </body>
+
+ <!-- test code goes here -->
+ <script type="application/javascript"><![CDATA[
+
+function shouldThrow(fun, args, msg) {
+ try {
+ fun.apply(this, args);
+ ok(false, msg);
+ } catch (e) {
+ ok(true, msg+" -- "+e);
+ }
+}
+
+function do_registerTopLevelWindow(win) {
+ Services.appShell.registerTopLevelWindow(win);
+}
+
+shouldThrow(
+ do_registerTopLevelWindow, [null],
+ "registering null as a top-level window should throw");
+
+shouldThrow(
+ do_registerTopLevelWindow, [{}],
+ "registering a void object as a top-level window should throw");
+
+ ]]></script>
+</window>
diff --git a/js/xpconnect/tests/chrome/test_bug795275.xhtml b/js/xpconnect/tests/chrome/test_bug795275.xhtml
new file mode 100644
index 0000000000..a151a1a2b7
--- /dev/null
+++ b/js/xpconnect/tests/chrome/test_bug795275.xhtml
@@ -0,0 +1,80 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=795275
+-->
+<window title="Mozilla Bug 795275"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=795275"
+ target="_blank">Mozilla Bug 795275</a>
+ </body>
+
+ <!-- test code goes here -->
+ <script type="application/javascript">
+ <![CDATA[
+ /** Test for Warning in content scopes about Components. **/
+
+ SimpleTest.waitForExplicitFinish();
+ SimpleTest.executeSoon(function() {
+ SpecialPowers.pushPrefEnv({set: [["dom.use_components_shim", true]]},
+ startLoad)
+ });
+ function startLoad() {
+ for (var i = 1; i <= document.getElementsByTagName('iframe').length; ++i) {
+ var frame = document.getElementById('frame' + i);
+ frame.contentWindow.location = 'http://mochi.test:8888/tests/js/xpconnect/tests/mochitest/file_bug795275.html';
+ frame.onload = frameLoaded;
+ }
+ }
+
+ // Set up our console listener.
+ var gWarnings = 0;
+ function onWarning(consoleMessage) {
+ if (/soon be removed/.test(consoleMessage.message))
+ gWarnings++;
+ }
+ var gListener = {
+ observe: onWarning,
+ QueryInterface: ChromeUtils.generateQI(["nsIConsoleListener"])
+ };
+ Services.console.registerListener(gListener);
+
+ // Wait for all four child frame to load.
+ var gLoadCount = 0;
+ function frameLoaded() {
+ if (++gLoadCount == document.getElementsByTagName('iframe').length)
+ go();
+ }
+
+ function getWin(id) { return document.getElementById(id).contentWindow.wrappedJSObject; }
+ function go() {
+ getWin('frame1').touchComponents();
+ getWin('frame2').touchInterfaces();
+ getWin('frame4').touchComponents();
+ getWin('frame4').touchInterfaces();
+
+ // Warnings are dispatched async, so stick ourselves at the end of the event
+ // queue.
+ setTimeout(done, 0);
+ }
+
+ function done() {
+ Services.console.unregisterListener(gListener);
+ is(gWarnings, 3, "Got the right number of warnings");
+ SimpleTest.finish();
+ }
+
+ ]]>
+
+ </script>
+ <iframe id="frame1"/>
+ <iframe id="frame2"/>
+ <iframe id="frame3"/>
+ <iframe id="frame4"/>
+
+</window>
diff --git a/js/xpconnect/tests/chrome/test_bug799348.xhtml b/js/xpconnect/tests/chrome/test_bug799348.xhtml
new file mode 100644
index 0000000000..91de48164f
--- /dev/null
+++ b/js/xpconnect/tests/chrome/test_bug799348.xhtml
@@ -0,0 +1,47 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=799348
+-->
+<window title="Mozilla Bug 799348"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=799348"
+ target="_blank">Mozilla Bug 799348</a>
+ </body>
+
+ <!-- test code goes here -->
+ <script type="application/javascript">
+ <![CDATA[
+ /** Test for Bug 799348 **/
+ SimpleTest.waitForExplicitFinish();
+ var gCalledOnload = false;
+ var myObserver = {
+ QueryInterface: ChromeUtils.generateQI(["nsIObserver"]),
+ observe(win, topic, data) {
+ if (topic == "domwindowopened") {
+ ok(!gCalledOnload, "domwindowopened notification fired before onload");
+ win.addEventListener("load", function(evt) {
+ gCalledOnload = true;
+ win.close();
+ });
+ } else if (topic == "domwindowclosed") {
+ ok(gCalledOnload, "should have called onload");
+ Services.ww.unregisterNotification(myObserver);
+ SimpleTest.finish();
+ } else {
+ ok(false, "unknown topic");
+ }
+ }
+ };
+ Services.ww.registerNotification(myObserver);
+
+
+ ]]>
+ </script>
+ <iframe id="frame" type="content" src="http://test1.example.org/tests/js/xpconnect/tests/mochitest/file_bug799348.html" />
+</window>
diff --git a/js/xpconnect/tests/chrome/test_bug801241.xhtml b/js/xpconnect/tests/chrome/test_bug801241.xhtml
new file mode 100644
index 0000000000..5c83429afa
--- /dev/null
+++ b/js/xpconnect/tests/chrome/test_bug801241.xhtml
@@ -0,0 +1,48 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=801241
+-->
+<window title="Mozilla Bug 801241"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=801241"
+ target="_blank">Mozilla Bug 801241</a>
+ </body>
+
+ <!-- test code goes here -->
+ <script type="application/javascript">
+ <![CDATA[
+ /** Test for Bug 801241 **/
+ SimpleTest.waitForExplicitFinish();
+
+ /* globals win */
+
+ // This is decompiled and run inside the sandbox;
+ function sbCode() {
+ try {
+ // eslint-disable-next-line no-self-assign
+ win.location = win.location;
+ ok(true, "Didn't throw setting from location");
+ } catch (e) {
+ ok(false, "Threw setting location from sandbox");
+ }
+ }
+
+ function go() {
+ document.getElementById('ifr').onload = null;
+ var sb = new Cu.Sandbox(this);
+ sb.win = document.getElementById('ifr').contentWindow;
+ sb.ok = ok;
+ Cu.evalInSandbox('(' + sbCode.toSource() + ')()', sb);
+ SimpleTest.finish();
+ }
+
+ ]]>
+ </script>
+ <iframe id="ifr" onload="go();" type="content" src="http://example.org/tests/js/xpconnect/tests/mochitest/file_empty.html" />
+</window>
diff --git a/js/xpconnect/tests/chrome/test_bug812415.xhtml b/js/xpconnect/tests/chrome/test_bug812415.xhtml
new file mode 100644
index 0000000000..71dc23768e
--- /dev/null
+++ b/js/xpconnect/tests/chrome/test_bug812415.xhtml
@@ -0,0 +1,90 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=812415
+-->
+<window title="Mozilla Bug 812415"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=812415"
+ target="_blank">Mozilla Bug 812415</a>
+ </body>
+
+ <!-- test code goes here -->
+ <script type="application/javascript">
+ <![CDATA[
+ /** Test for Bug 812415 and Bug 823348 **/
+
+ SimpleTest.waitForExplicitFinish();
+
+ function testWaiving(iwin, sb) {
+ sb.win = iwin;
+ is(Cu.evalInSandbox('win', sb), iwin, "Basic identity works");
+ is(Cu.evalInSandbox('win.wrappedJSObject.expando', sb), 42, "Waivers work via .wrappedJSObject");
+ is(Cu.evalInSandbox('XPCNativeWrapper.unwrap(win).expando', sb), 42, "Waivers work via XPCNativeWrapper.unwrap");
+ is(Cu.evalInSandbox('win.wrappedJSObject.document.defaultView.expando', sb), 42, "Waivers are deep");
+ }
+
+ function checkThrows(expression, sb, msg) {
+ var result = Cu.evalInSandbox('(function() { try { ' + expression + '; return "allowed"; } catch (e) { return e.toString(); }})();', sb);
+ ok(!!/denied/.exec(result), msg);
+ }
+
+ function testAsymmetric(regular, expanded) {
+
+ // Set up objects.
+ expanded.regFun = Cu.evalInSandbox('function reg() { return 42; }; reg', regular);
+ expanded.regObj = Cu.evalInSandbox('new Object({foo: 2})', regular);
+ regular.expFun = Cu.evalInSandbox('function exp() { return 41; }; exp', expanded);
+ regular.expObj = Cu.evalInSandbox('new Object({bar: 3})', expanded);
+
+ // Check objects.
+ is(Cu.evalInSandbox('regObj.foo', expanded), 2, "Expanded can see regular object prop");
+ checkThrows('expObj.bar', regular, "Regular shouldn't read properties");
+ Cu.evalInSandbox('regObj.foo = 20', expanded);
+ is(expanded.regObj.foo, 20, "Expanded can set properties");
+ checkThrows('expFun.bar = 0', regular, "Regular shouldn't write properties");
+
+ // Check functions.
+ is(Cu.evalInSandbox('regFun()', expanded), 42, "Expanded can call regular function");
+ checkThrows('expFun()', regular, "Regular cannot call expanded function");
+ is(Cu.evalInSandbox('regFun.name', expanded), 'reg', "Expanded can see regular function's name");
+ checkThrows('expFun.name', regular, "Regular can't see expanded function's name");
+ Cu.evalInSandbox('regFun.expando = 30', expanded);
+ is(Cu.evalInSandbox('regFun.expando', expanded), 30, "Expanded can set expandos");
+ checkThrows('expFun.expando = 29', regular, "Regular can't set expandos");
+
+ // Check __proto__ stuff.
+ is(Cu.evalInSandbox('regFun.__proto__', expanded), regular.Function.prototype, "expanded can get __proto__");
+ checkThrows('expFun.__proto__', regular, "regular can't use __proto__");
+ checkThrows('expFun.__proto__ = {}', regular, "regular can't mutate __proto__");
+ }
+
+ function go() {
+ var iwin = document.getElementById('ifr').contentWindow;
+ iwin.wrappedJSObject.expando = 42;
+
+ // Make our sandboxes. We pass wantXrays=false for the nsEP to ensure that
+ // the Xrays we get are the result of being an nsEP, not from the wantXrays
+ // flag.
+ var regular = new Cu.Sandbox(iwin);
+ var expanded = new Cu.Sandbox([iwin], {wantXrays: false});
+
+ // Because of the crazy secret life of wantXrays, passing wantXrays=false
+ // has the side effect of waiving Xrays on the returned sandbox. Undo that.
+ expanded = Cu.unwaiveXrays(expanded);
+
+ testWaiving(iwin, regular);
+ testWaiving(iwin, expanded);
+ testAsymmetric(regular, expanded);
+ SimpleTest.finish();
+ }
+
+ ]]>
+ </script>
+ <iframe id="ifr" onload="go();" src="http://example.org/tests/js/xpconnect/tests/mochitest/file_empty.html" />
+</window>
diff --git a/js/xpconnect/tests/chrome/test_bug853283.xhtml b/js/xpconnect/tests/chrome/test_bug853283.xhtml
new file mode 100644
index 0000000000..6cdd5027b8
--- /dev/null
+++ b/js/xpconnect/tests/chrome/test_bug853283.xhtml
@@ -0,0 +1,40 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=853283
+-->
+<window title="Mozilla Bug 853283"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=853283"
+ target="_blank">Mozilla Bug 853283</a>
+ </body>
+
+ <!-- test code goes here -->
+ <script type="application/javascript">
+ <![CDATA[
+ /** Test nsNavigatorSH::Resolve in conjunction with Xrays.**/
+ SimpleTest.waitForExplicitFinish();
+
+ function go() {
+ // This chrome document already has Xrays to the content scope, but we use a
+ // a sandbox anyway to make sure that the global in play here isn't an
+ // nsIDOMWindow. Otherwise, the resolve hook might just end up squeaking by
+ // with the chrome window.
+ var iwin = $('ifr').contentWindow;
+ var sb = new Cu.Sandbox(window);
+ sb.iwin = iwin;
+ sb.ok = ok;
+ Cu.evalInSandbox('try {iwin.navigator.mozContacts; ok(true, "Didnt throw"); } catch (e) { ok(false, "Threw: " + e);}', sb);
+ SimpleTest.finish();
+ }
+
+
+ ]]>
+ </script>
+ <iframe id="ifr" onload="go();" src="http://example.org/tests/js/xpconnect/tests/mochitest/file_empty.html" />
+</window>
diff --git a/js/xpconnect/tests/chrome/test_bug853571.xhtml b/js/xpconnect/tests/chrome/test_bug853571.xhtml
new file mode 100644
index 0000000000..2f16915666
--- /dev/null
+++ b/js/xpconnect/tests/chrome/test_bug853571.xhtml
@@ -0,0 +1,62 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=853571
+-->
+<window title="Mozilla Bug 853571"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=853571"
+ target="_blank">Mozilla Bug 853571</a>
+ </body>
+
+ <!-- test code goes here -->
+ <script type="application/javascript">
+ <![CDATA[
+ /** Test for Bug 853571 **/
+ SimpleTest.waitForExplicitFinish();
+
+ function* mainTest() {
+ var iwin = $('ifr').contentWindow;
+
+ // Test with a simple sandbox with no prototype.
+ checkSource(iwin, new Cu.Sandbox(iwin), null, "should get null source with no sandboxPrototype");
+ yield;
+
+ // Test with a sandboxPrototype.
+ checkSource(iwin, new Cu.Sandbox(iwin, { sandboxPrototype: iwin }), iwin, "should be able to impersonate the prototype");
+ yield;
+
+ SimpleTest.finish();
+ yield; // Prevent StopIteration from being thrown.
+ }
+
+ var gen;
+ function runTest() {
+ gen = mainTest();
+ gen.next();
+ }
+
+ function checkSource(target, sb, expectedSource, message) {
+ target.addEventListener("message", function listener(event) {
+ is(event.source, expectedSource, message);
+ let {done} = gen.next();
+ if (done) {
+ // We're done.
+ }
+ },
+ { once: true});
+
+ sb.target = target;
+ Cu.evalInSandbox("target.postMessage('foo', '*');", sb);
+ }
+
+
+ ]]>
+ </script>
+ <iframe id="ifr" type="content" onload="runTest()" src="http://example.org/tests/js/xpconnect/tests/mochitest/file_empty.html" />
+</window>
diff --git a/js/xpconnect/tests/chrome/test_bug858101.xhtml b/js/xpconnect/tests/chrome/test_bug858101.xhtml
new file mode 100644
index 0000000000..5ff9f28dc7
--- /dev/null
+++ b/js/xpconnect/tests/chrome/test_bug858101.xhtml
@@ -0,0 +1,55 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=858101
+-->
+<window title="Mozilla Bug 858101"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=858101"
+ target="_blank">Mozilla Bug 858101</a>
+ </body>
+
+ <!-- test code goes here -->
+ <script type="application/javascript">
+ <![CDATA[
+
+ /** Test for [[DefaultValue]] on XrayWrappers. **/
+ SimpleTest.waitForExplicitFinish();
+
+ function muckWithToString() {
+ function tricky() { return "hah"; };
+
+ window.toString = document.toString = document.body.toString = tricky;
+ window.valueOf = document.valueOf = document.body.valueOf = tricky;
+
+ Window.prototype.toString = Window.prototype.valueOf = tricky;
+ Document.prototype.toString = Document.prototype.valueOf = tricky;
+ HTMLBodyElement.toString = HTMLBodyElement.valueOf = tricky;
+ }
+
+ function go() {
+ var iwin = $('ifr').contentWindow;
+ iwin.wrappedJSObject.eval('(' + muckWithToString.toSource() + ')()');
+
+ // Check behavior with vanilla CCWs.
+ ok(!!/hah/.exec(iwin.wrappedJSObject + ""), "Waivers should get content-defined window stringification");
+ ok(!!/hah/.exec(iwin.document.wrappedJSObject + ""), "Waivers should get content-defined document stringification");
+ ok(!!/hah/.exec(iwin.document.body.wrappedJSObject + ""), "Waivers should get content-defined body stringification");
+
+ // Check Xray behavior.
+ ok(!/hah/.exec(iwin + ""), "Xrays should not get content-defined window stringification");
+ ok(!/hah/.exec(iwin.document + ""), "Xrays should not get content-defined document stringification");
+ ok(!/hah/.exec(iwin.document.body + ""), "Xrays should not get content-defined body stringification");
+
+ SimpleTest.finish();
+ }
+
+ ]]>
+ </script>
+ <iframe id="ifr" onload="go();" type="content" src="http://example.org/tests/js/xpconnect/tests/mochitest/file_empty.html" />
+</window>
diff --git a/js/xpconnect/tests/chrome/test_bug860494.xhtml b/js/xpconnect/tests/chrome/test_bug860494.xhtml
new file mode 100644
index 0000000000..26bd1e13bd
--- /dev/null
+++ b/js/xpconnect/tests/chrome/test_bug860494.xhtml
@@ -0,0 +1,57 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=860494
+-->
+<window title="Mozilla Bug 860494"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=860494"
+ target="_blank">Mozilla Bug 860494</a>
+ </body>
+
+ <!-- test code goes here -->
+ <script type="application/javascript">
+ <![CDATA[
+ /** Test for Bug 860494 **/
+
+ SimpleTest.waitForExplicitFinish();
+
+ function go() {
+ var iwin = $('ifr').contentWindow;
+ // NB: mochitest-chrome actually runs the test as a content docshell.
+ is(iwin.top, window.top, "iframe names shouldn't shadow |top| via Xray");
+ is(iwin.parent, window, "iframe names shouldn't shadow |parent| via Xray");
+ ok(!!/http/.exec(iwin.location), "iframe names shouldn't shadow |location| via Xray");
+ is(iwin.length, 7, "iframe names shouldn't shadow |length| via Xray");
+ is(iwin.window, iwin, "iframe names shouldn't shadow |window| via Xray");
+ is(iwin.navigator, Cu.unwaiveXrays(iwin.wrappedJSObject.navigator),
+ "iframe names shouldn't shadow |navigator| via Xray");
+ ok(iwin.alert instanceof Function,
+ "iframe names shouldn't shadow |alert| via Xray");
+
+ // Now test XOWs.
+ var sb = new Cu.Sandbox("https://www.example.com");
+ sb.win = iwin;
+ sb.topWin = top;
+ sb.parentWin = window;
+ sb.is = is;
+ sb.ok = ok;
+ Cu.evalInSandbox('ok(win.top === topWin, "iframe names shouldnt shadow |top| via cross-origin Xray");', sb);
+ Cu.evalInSandbox('ok(win.parent === parentWin, "iframe names shouldnt shadow |parent| via cross-origin Xray");', sb);
+ Cu.evalInSandbox('is(win.length, 7, "iframe names shouldnt shadow |length| via cross-origin Xray");', sb);
+ Cu.evalInSandbox('ok(win.window === win, "iframe names shouldnt shadow |window| via cross-origin Xray");', sb);
+ Cu.evalInSandbox('ok(win.navigator === win[5], "iframe names that correspond to non-cross-origin-visible properties should expose the subframe: navigator");', sb);
+ Cu.evalInSandbox('ok(win.alert === win[6], "iframe names that correspond to non-cross-origin-visible properties should expose the subframe: alert");', sb);
+
+ SimpleTest.finish();
+ }
+
+ ]]>
+ </script>
+ <iframe id="ifr" type="content" onload="go();" src="https://example.org/tests/js/xpconnect/tests/mochitest/file_bug860494.html" />
+</window>
diff --git a/js/xpconnect/tests/chrome/test_bug865948.xhtml b/js/xpconnect/tests/chrome/test_bug865948.xhtml
new file mode 100644
index 0000000000..85fb86874d
--- /dev/null
+++ b/js/xpconnect/tests/chrome/test_bug865948.xhtml
@@ -0,0 +1,35 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=865948
+-->
+<window title="Mozilla Bug 865948"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=865948"
+ target="_blank">Mozilla Bug 865948</a>
+ </body>
+
+ <!-- test code goes here -->
+ <script type="application/javascript">
+ <![CDATA[
+
+ /** Test for Bug 865948 **/
+ SimpleTest.waitForExplicitFinish();
+ function go() {
+ $('ifr').onload = null;
+ var sb = Cu.Sandbox(["https://example.com", "https://example.org"]);
+ sb.iwin = $('ifr').contentWindow;
+ sb.ok = ok;
+ Cu.evalInSandbox('try { iwin.location = "about:mozilla"; ok(false, "didnt throw"); } catch (e) { ok(!!/denied/.exec(e), "threw: " + e); }', sb);
+ SimpleTest.finish();
+ }
+
+ ]]>
+ </script>
+<iframe id="ifr" type="content" onload="go();" src="https://example.org/tests/js/xpconnect/tests/mochitest/file_empty.html" />
+</window>
diff --git a/js/xpconnect/tests/chrome/test_bug866823.xhtml b/js/xpconnect/tests/chrome/test_bug866823.xhtml
new file mode 100644
index 0000000000..39a24fc14e
--- /dev/null
+++ b/js/xpconnect/tests/chrome/test_bug866823.xhtml
@@ -0,0 +1,49 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=866823
+-->
+<window title="Mozilla Bug 866823"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=866823"
+ target="_blank">Mozilla Bug 866823</a>
+ </body>
+
+ <!-- test code goes here -->
+ <script type="application/javascript">
+ <![CDATA[
+ /** Test for Bug 866823 **/
+ SimpleTest.waitForExplicitFinish();
+
+ function go() {
+ var iwin = $('ifr').contentWindow;
+
+ // Make sure that Xray waivers can't be transitively extended onto objects
+ // for whom the extender may not waive.
+ var sb = new Cu.Sandbox(iwin, { wantXrays: true });
+ sb.iwin = iwin;
+ ok(Cu.evalInSandbox('iwin.wrappedJSObject.top == iwin.top', sb), "Waiver does not propagate");
+ Cu.evalInSandbox('iwin.wrappedJSObject.waiver = iwin.wrappedJSObject', sb);
+ ok(iwin.wrappedJSObject.eval('waiver == window'), "Waivers disappear same-compartment");
+
+ // Make sure that standard prototypes don't get COWs.
+ sb.objProto = Object.prototype;
+ try {
+ sb.eval('objProto.toString;', sb);
+ ok(false, "Should have thrown");
+ } catch (e) {
+ ok(/denied|insecure/, "No silent failure when accessing properties on forbidden prototype");
+ }
+
+ SimpleTest.finish();
+ }
+
+ ]]>
+ </script>
+ <iframe id="ifr" onload="go();" type="content" src="http://example.org/tests/js/xpconnect/tests/mochitest/file_empty.html" />
+</window>
diff --git a/js/xpconnect/tests/chrome/test_bug895340.xhtml b/js/xpconnect/tests/chrome/test_bug895340.xhtml
new file mode 100644
index 0000000000..ded70e6dd4
--- /dev/null
+++ b/js/xpconnect/tests/chrome/test_bug895340.xhtml
@@ -0,0 +1,50 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=895340
+-->
+<window title="Mozilla Bug 895340"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=895340 "
+ target="_blank">Mozilla Bug 895340 </a>
+ </body>
+
+ <!-- test code goes here -->
+ <script type="application/javascript"><![CDATA[
+
+ SimpleTest.waitForExplicitFinish();
+ SimpleTest.expectUncaughtException();
+
+ var finished = false;
+
+ let listener = {
+ QueryInterface: ChromeUtils.generateQI(["nsIConsoleListener"])
+ };
+
+ listener.observe = function(aMessage) {
+ if (aMessage.message.includes("Will you report me?") && !finished) {
+ finished = true;
+ ok(true, "exception reported");
+ Services.console.unregisterListener(listener);
+ SimpleTest.finish();
+ }
+ };
+
+ Services.console.registerListener(listener);
+
+ /* Throw an exception and verify that it gets reported. */
+ let foo_obs = function() {
+ throw new Error("Will you report me?");
+ };
+
+ Services.obs.addObserver(foo_obs, "foo");
+ Services.obs.notifyObservers(null, "foo", "foo");
+ Services.obs.removeObserver(foo_obs, "foo");
+ ]]></script>
+</window>
diff --git a/js/xpconnect/tests/chrome/test_bug932906.xhtml b/js/xpconnect/tests/chrome/test_bug932906.xhtml
new file mode 100644
index 0000000000..f7e839d980
--- /dev/null
+++ b/js/xpconnect/tests/chrome/test_bug932906.xhtml
@@ -0,0 +1,69 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=932906
+-->
+<window title="Mozilla Bug 932906"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=932906"
+ target="_blank">Mozilla Bug 932906</a>
+ </body>
+
+ <!-- test code goes here -->
+ <script type="application/javascript">
+ <![CDATA[
+ /** Test for Bug 932906 **/
+ SimpleTest.waitForExplicitFinish();
+
+ function passToContent(shouldThrow) {
+ try {
+ $('ifr').contentWindow.obs = Services.obs;
+ ok(!shouldThrow, "Didn't throw when passing non-DOM XPCWN to content");
+ } catch (e) {
+ ok(shouldThrow, "Threw when passing non-DOM XPCWN to content");
+ ok(/denied/.test(e), "Threw correct exception: " + e);
+ }
+ }
+
+ var gLoadCount = 0;
+ function loaded() {
+ ++gLoadCount;
+ if (gLoadCount == 1)
+ part1();
+ else if (gLoadCount == 2)
+ part2();
+ else
+ ok(false, "Didn't expect three loads");
+ }
+
+ function part1() {
+
+ // Make sure that the pref is what we expect for mochitests.
+ is(Services.prefs.getBoolPref('dom.use_xbl_scopes_for_remote_xul'), true,
+ "Test harness set up like we expect");
+
+
+ // First, test that we can't normally pass non-DOM XPCWNs to content.
+ passToContent(/* shouldThrow = */ true);
+
+ // Now, make sure we _can_ for the remote xul case. We use SpecialPowers
+ // for the pref munging because it cleans up after us.
+ SpecialPowers.pushPrefEnv({set: [['dom.use_xbl_scopes_for_remote_xul', false]]}, function() {
+ $('ifr').contentWindow.location.reload();
+ });
+ }
+
+ function part2() {
+ passToContent(/* shouldThrow = */ false);
+ SimpleTest.finish();
+ }
+
+ ]]>
+ </script>
+ <iframe id="ifr" onload="loaded();" type="content" src="http://example.org/tests/js/xpconnect/tests/mochitest/file_empty.html" />
+</window>
diff --git a/js/xpconnect/tests/chrome/test_bug996069.xhtml b/js/xpconnect/tests/chrome/test_bug996069.xhtml
new file mode 100644
index 0000000000..5693fd3447
--- /dev/null
+++ b/js/xpconnect/tests/chrome/test_bug996069.xhtml
@@ -0,0 +1,52 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=996069
+-->
+<window title="Mozilla Bug 996069"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=996069"
+ target="_blank">Mozilla Bug 996069</a>
+ </body>
+
+ <!-- test code goes here -->
+ <script type="application/javascript">
+ <![CDATA[
+ /** Test for Bug 996069 **/
+ SimpleTest.waitForExplicitFinish();
+
+ function loaded() {
+ var ifr = document.getElementById("ifr").contentWindow;
+ var sb = new Cu.Sandbox([ifr],
+ { sandboxPrototype: ifr });
+
+ ifr.wrappedJSObject.finishTest = function() {
+ // If we got here we did not hit the NS_ReleaseAssert...
+ ok(true, "ExpandedPrincipal should not be inherited by content windows");
+
+ // But let's be sure that the new window does not have nsEP
+ newWin.wrappedJSObject.obj = Cu.evalInSandbox("var obj = { foo: 'bar' }; obj", sb);
+ try {
+ newWin.eval("obj.foo");
+ ok(false, "newWin should not have access to object from a scope with ExpandedPrincipal");
+ } catch (e) {
+ ok(/Permission denied/.exec(e.message), "newWin should not have access to object from a scope with ExpandedPrincipal");
+ }
+ newWin.close();
+ SimpleTest.finish();
+ };
+
+ var newWin = Cu.evalInSandbox(
+ "window.open('https://example.org/chrome/js/xpconnect/tests/chrome/file_bug996069.html');",
+ sb);
+ }
+
+ ]]>
+ </script>
+ <iframe id="ifr" onload="loaded();" type="content" src="https://example.org/chrome/js/xpconnect/tests/chrome/file_bug996069.html" />
+</window>
diff --git a/js/xpconnect/tests/chrome/test_chrometoSource.xhtml b/js/xpconnect/tests/chrome/test_chrometoSource.xhtml
new file mode 100644
index 0000000000..6710a59289
--- /dev/null
+++ b/js/xpconnect/tests/chrome/test_chrometoSource.xhtml
@@ -0,0 +1,70 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<window title="Mozilla Bug 761723"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=761723" target="_blank">Mozilla Bug 761723</a>
+ </body>
+
+ <!-- test code goes here -->
+ <script type="application/javascript" src="outoflinexulscript.js"></script>
+ <script type="application/javascript"><![CDATA[
+const {NetUtil} = ChromeUtils.importESModule(
+ "resource://gre/modules/NetUtil.sys.mjs"
+);
+let base = /.*\//.exec(window.location.href)[0];
+const {checkFromJSM} = ChromeUtils.import(base + "file_expandosharing.jsm");
+
+function inlinefunction() {
+ return 42;
+}
+
+var src;
+src = inlinefunction.toSource();
+isnot(src.indexOf("return 42"), -1, "inline XUL script should have source");
+is(src.charAt(src.length - 1), "}", "inline XUL source should end with '}'");
+src = outoflinefunction.toSource();
+isnot(src.indexOf("return 42"), -1, "out of line XUL script should have source")
+is(src.charAt(src.length - 1), "}", "out of line XUL source should end with '}'");
+src = checkFromJSM.toSource();
+isnot(src.indexOf("catch"), -1, "JSM should have source");
+var ns = {};
+Services.scriptloader.loadSubScript(base + "file_expandosharing.jsm", ns);
+src = ns.checkFromJSM.toSource();
+isnot(src.indexOf("catch"), -1, "subscript should have source");
+
+var reg = Cc["@mozilla.org/chrome/chrome-registry;1"].getService(Ci.nsIChromeRegistry);
+var resolvedBase = reg.convertChromeURL(NetUtil.newURI(base)).spec;
+
+ns = {
+ base,
+};
+Services.scriptloader.loadSubScript(resolvedBase + "subscript.js", ns);
+src = ns.checkFromJSM.toSource();
+isnot(src.indexOf("catch"), -1, "subscript of a subscript should have source");
+
+ns = {};
+Services.scriptloader.loadSubScript(resolvedBase + "utf8_subscript.js", ns);
+src = ns.f.toSource();
+isnot(src.indexOf("return 42;"), -1, "encoded subscript should have correct source");
+
+ns = {};
+Services.scriptloader.loadSubScriptWithOptions(resolvedBase + "utf8_subscript.js",
+ {target: ns, ignoreCache: true});
+src = ns.f.toSource();
+isnot(src.indexOf("return 42;"), -1, "encoded subscript should have correct source");
+
+ns = {};
+let b = new Blob([
+ "var Exported = 17;"
+]);
+var blobUrl = URL.createObjectURL(b);
+Services.scriptloader.loadSubScript(blobUrl, ns);
+is(ns.Exported, 17, "subscript from a blob URL should work");
+ ]]></script>
+</window>
diff --git a/js/xpconnect/tests/chrome/test_cloneInto.xhtml b/js/xpconnect/tests/chrome/test_cloneInto.xhtml
new file mode 100644
index 0000000000..aa874bda96
--- /dev/null
+++ b/js/xpconnect/tests/chrome/test_cloneInto.xhtml
@@ -0,0 +1,194 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
+<window title="Mozilla Bug 503926"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=964293"
+ target="_blank">Cu.cloneInto()</a>
+ </body>
+
+ <!-- test code goes here -->
+ <script type="application/javascript">
+ <![CDATA[
+
+ const TypedArrayThings = [
+ 'Int8Array',
+ 'Uint8Array',
+ 'Uint8ClampedArray',
+ 'Int16Array',
+ 'Uint16Array',
+ 'Int32Array',
+ 'Uint32Array',
+ 'Float32Array',
+ 'Float64Array',
+ ];
+
+ function checkThrows(f, msg, rgxp) {
+ try {
+ f();
+ ok(false, "Should have thrown: " + msg);
+ } catch (e) {
+ ok(true, "Threw correctly - " + msg + " - " + e);
+ if (rgxp)
+ ok(rgxp.test(e), "Should throw correct exception: " + e);
+ }
+ }
+
+ function getType(a) {
+ if (a === null || a === undefined)
+ return 'null';
+
+ if (Array.isArray(a))
+ return 'array';
+
+ if (File.isInstance(a))
+ return 'file';
+
+ if (Blob.isInstance(a))
+ return 'blob';
+
+ if (TypedArrayThings.includes(a.constructor.name))
+ return a.constructor.name;
+
+ if (typeof a == 'object')
+ return 'object';
+
+ if (typeof a == 'function')
+ return 'function';
+
+ return 'primitive';
+ }
+
+ function compare(a, b) {
+ is (getType(a), getType(b), 'Type matches');
+
+ var type = getType(a);
+ if (type == 'array') {
+ is (a.length, b.length, 'Array.length matches');
+ for (var i = 0; i < a.length; ++i) {
+ compare(a[i], b[i]);
+ }
+
+ return;
+ }
+
+ if (type == 'file' || type == 'blob') {
+ ok ( a === b, 'They should match');
+ return;
+ }
+
+ if (type == 'object') {
+ ok ( a !== b, 'They should not match');
+
+ var aProps = [];
+ for (let p in a) aProps.push(p);
+
+ var bProps = [];
+ for (let p in b) bProps.push(p);
+
+ is (aProps.length, bProps.length, 'Props match');
+ is (aProps.sort().toString(), bProps.sort().toString(), 'Props names match');
+
+ for (let p in a) {
+ compare(a[p], b[p]);
+ }
+
+ return;
+ }
+
+ if (type == 'function') {
+ ok ( a !== b, 'They should not match');
+ return;
+ }
+
+ if (type != 'null') {
+ is (a, b, 'Same value');
+ }
+ }
+
+ var sandboxOptions = {
+ wantXrays: true,
+ wantExportHelpers: true,
+ };
+ var sandbox = new Cu.Sandbox(window, sandboxOptions);
+ // The second sandbox is for testing the exportHelper version of the cloneInto
+ var sandbox2 = new Cu.Sandbox("https://example.com", sandboxOptions);
+ sandbox.sandbox2 = sandbox2;
+ sandbox2.sandbox = sandbox;
+
+ function cloneAndTest(test) {
+ var output = sandbox.test = Cu.cloneInto(test, sandbox);
+ compare(test, output);
+
+ output = Cu.evalInSandbox('cloneInto(test, sandbox2)', sandbox);
+ compare(test, output);
+ }
+
+ function cloneAndTestWithFunctions(test) {
+ var output = sandbox.test = Cu.cloneInto(test, sandbox, { cloneFunctions: true });
+ compare(test, output);
+
+ output = Cu.evalInSandbox('cloneInto(test, sandbox2, { cloneFunctions: true })', sandbox);
+ // Note - We need to waive here, because functions are filtered out by object Xrays.
+ compare(test, Cu.waiveXrays(output));
+ }
+
+ var tests = [
+ 1,
+ null,
+ true,
+ 'hello world',
+ [1, 2, 3],
+ { a: 1, b: 2 },
+ new Date(),
+ { a: 1, b: {}, c: [1, 2, 3, {} ], e: 'hello world' },
+ ];
+
+ for (var i = 0; i < tests.length; ++i) {
+ cloneAndTest(tests[i]);
+ }
+
+ checkThrows(function() { Cu.cloneInto({ a() {} }, sandbox); },
+ 'Function should not be cloned by default');
+
+ checkThrows(function() { Cu.cloneInto({ a: document }, sandbox); },
+ 'Reflectors should not be wrapped by default');
+
+ var withReflectors = Cu.cloneInto({ doc: document, win: window }, sandbox,
+ { wrapReflectors: true });
+ is(Cu.unwaiveXrays(Cu.waiveXrays(withReflectors).doc), document, "Document passes");
+ is(Cu.unwaiveXrays(Cu.waiveXrays(withReflectors).win), window, "Window passes");
+
+
+ checkThrows(function() { Cu.evalInSandbox('cloneInto({}, sandbox)', sandbox2); },
+ 'CloneInto should only work on less privileged target scopes.',
+ /denied|insecure/);
+
+ var cloneTarget = new Cu.Sandbox("https://example.com");
+ var sameOriginSB = new Cu.Sandbox("https://example.com", { wantGlobalProperties: ['XMLHttpRequest'] });
+ var crossOriginSB = new Cu.Sandbox("https://example.net", { wantGlobalProperties: ['XMLHttpRequest'] });
+ sandbox2.cloneTarget = cloneTarget;
+ sandbox2.soXHR = Cu.evalInSandbox('new XMLHttpRequest()', sameOriginSB);
+ sandbox2.xoXHR = Cu.evalInSandbox('new XMLHttpRequest()', crossOriginSB);
+ sandbox2.chromeDoc = document;
+ Cu.evalInSandbox('function tryToClone(x) { return cloneInto({val: x}, cloneTarget, { wrapReflectors: true }).val; }', sandbox2);
+ is(Cu.evalInSandbox('tryToClone(soXHR)', sandbox2), sandbox2.soXHR, 'Same-origin wrapReflectors works');
+ checkThrows(function() { Cu.evalInSandbox('tryToClone(chromeDoc)', sandbox2); },
+ 'wrapReflectors may not wrap cross-origin reflectors', /unsupported value type/);
+
+
+ var test = { a() { return 42; } };
+ cloneAndTestWithFunctions(test);
+
+ // Check that inputs are properly passed through cloned functions:
+ test = { a(obj) { return obj; } };
+ var clonedTest = Cu.cloneInto(test, sandbox, {cloneFunctions: true});
+ var testInput = {};
+ is(clonedTest.a(testInput), testInput, "Objects should be identical");
+ ]]>
+ </script>
+</window>
diff --git a/js/xpconnect/tests/chrome/test_cows.xhtml b/js/xpconnect/tests/chrome/test_cows.xhtml
new file mode 100644
index 0000000000..69d7d3e9e6
--- /dev/null
+++ b/js/xpconnect/tests/chrome/test_cows.xhtml
@@ -0,0 +1,207 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=500931
+-->
+<window title="Mozilla Bug 522764"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=522764 "
+ target="_blank">Mozilla Bug 522764 </a>
+ </body>
+
+ <!-- test code goes here -->
+ <script type="application/javascript"><![CDATA[
+add_task(async () => {
+var sandbox = new Cu.Sandbox("about:blank");
+
+await SpecialPowers.pushPrefEnv({"set": [["security.allow_eval_with_system_principal",
+ true]]});
+
+/* eslint-disable no-eval */
+
+function getCOW(x) {
+ if (typeof x != 'object' && typeof x != 'function')
+ return x;
+ x = Cu.waiveXrays(x);
+ var rval = {};
+ if (typeof x == "function")
+ rval = eval(`(${x.toString()})`);
+ for (var i in x) {
+ if (x.__lookupGetter__(i))
+ rval.__defineGetter__(i, eval(`(${x.__lookupGetter__(i).toString()})`))
+ else
+ rval[i] = getCOW(x[i]);
+ }
+ return rval;
+}
+
+// Give the sandbox a way to create ChromeObjectWrapped objects.
+sandbox.getCOW = getCOW;
+
+// Define test API functions in the sandbox.
+const TEST_API = ['is', 'isnot', 'ok', 'todo_is', 'todo_isnot', 'todo'];
+TEST_API.forEach(function(name) { sandbox[name] = window[name]; });
+
+sandbox.chromeGet = function (obj, prop) { return obj[prop]; };
+
+function COWTests() {
+ function getNames(cow) {
+ let names = [];
+ for (let name in cow) {
+ names.push(name);
+ }
+ return names;
+ }
+
+ // This function is actually stringified and run inside a
+ // sandbox with content privileges.
+
+ // TODO: This could use some refactoring; creating helper
+ // functions like assertIsWritable(myObj, 'someproperty') might
+ // be useful.
+
+ function isPropHidden(obj, propName, desc) {
+ try {
+ is(obj[propName], undefined,
+ "getting " + propName + " on " + desc + " should return undefined");
+ ok(!(propName in obj),
+ propName + " on " + desc + " should act as if it doesn't exist");
+ ok(!Object.hasOwnProperty.call(obj, propName),
+ propName + " on " + desc + " should act as if it doesn't exist");
+ } catch (e) {
+ ok(false, "getting " + propName + " on " + desc + " threw " + e);
+ }
+ }
+
+ const PROPS_TO_TEST = ['foo', 'bar', 'prototype'];
+
+ var empty = {};
+ var nonempty = {foo: 42, bar: 33};
+ is(getCOW(empty).foo, undefined,
+ "shouldn't throw when accessing exposed properties that don't exist");
+
+ PROPS_TO_TEST.forEach(function(name) {
+ isPropHidden(getCOW(nonempty), name, "object without exposedProps");
+ });
+
+ // Test function objects.
+ var func = function(x) { return 42; };
+ func.foo = "foo property";
+ var funcCOW = getCOW(func);
+ try {
+ funcCOW.foo;
+ ok(false, 'Functions are no longer COWable');
+ } catch (e) {
+ ok(/denied|insecure/.test(e), 'Functions are no longer COWable');
+ }
+ is(funcCOW(), 42, "Chrome functions should be callable");
+
+ // Test writable property
+ var writable = getCOW({});
+ try {
+ ok(!("foo" in writable),
+ "non-existing write-only property shouldn't exist");
+ writable.foo = 5;
+ ok(false, "writing to a write-only exposed prop should throw");
+ } catch (e) {
+ ok(/Permission denied/.test(e),
+ "writing to a write-only exposed prop should throw the right error");
+ }
+ is(writable.foo, undefined,
+ "reading from a write-only exposed prop should return undefined");
+ try {
+ delete writable.foo;
+ ok(false, "deleting a write-only exposed prop should throw");
+ } catch (e) {
+ ok(true, "deleting a write-only exposed prop should throw " + e);
+ }
+
+ // Test readable property
+ var readable = { foo: 5,
+ bar: 6 };
+ try {
+ isPropHidden(getCOW(readable), "foo", undefined,
+ "reading from a readable exposed prop shouldn't work");
+ } catch (e) {
+ ok(false, "reading from a readable exposed prop shouldn't throw " + e);
+ }
+ try {
+ getCOW(readable).foo = 1;
+ ok(false, "writing to a read-only exposed prop should fail");
+ } catch (e) {
+ ok(/Permission denied/.test(e),
+ "writing to a read-only exposed prop should fail");
+ }
+ try {
+ delete getCOW(readable).foo;
+ ok(false, "deleting a read-only exposed prop shouldn't work");
+ } catch (e) {
+ ok(/Permission denied/.test(e),
+ "deleting a read-only exposed prop should throw error");
+ }
+
+ try {
+ var props = getNames(getCOW(readable));
+ is(props.length, 0, "COW w/ one exposed prop should not enumerate");
+ } catch (e) {
+ ok(false, "COW w/ a readable prop should not raise exc " +
+ "on enumeration: " + e);
+ }
+
+ // Test read/write property
+ var readwrite = getCOW({});
+ try {
+ ok(!("foo" in readwrite),
+ "non-existing readwrite property shouldn't exist");
+ readwrite.foo = 5;
+ ok(false, "writing to a readwrite exposed prop should throw");
+ } catch (e) {
+ ok(/Permission denied/.test(e),
+ "writing to a readwrite exposed prop should throw the right error");
+ }
+ try {
+ delete readwrite.foo;
+ ok(false, "deleting a readwrite prop should throw");
+ } catch (e) {
+ ok(/Permission denied/.test(e),
+ "deleting a readwrite exposed prop should throw the right error");
+ }
+
+ // Readables and functions
+ try {
+ var COWFunc = getCOW((function() { return 5; }));
+ is(COWFunc(), 5, "COWed functions should be callable");
+ } catch (e) {
+ todo(false, "COWed functions should not raise " + e);
+ }
+}
+
+// Stringify the COW test suite and directly evaluate it in the sandbox.
+Cu.evalInSandbox('(' + COWTests.toString() + ')()', sandbox);
+
+// Test that COWed objects passing from content to chrome get unwrapped.
+function returnCOW() {
+ return getCOW({bar: 6});
+}
+
+var unwrapped = Cu.evalInSandbox(
+ '(' + returnCOW.toString() + ')()',
+ sandbox
+);
+
+try {
+ is(unwrapped.bar, 6,
+ "COWs should be unwrapped when entering chrome space");
+} catch (e) {
+ todo(false, "COWs should be unwrapped when entering chrome space, " +
+ "not raise " + e);
+}
+});
+ ]]></script>
+</window>
diff --git a/js/xpconnect/tests/chrome/test_discardSystemSource.xhtml b/js/xpconnect/tests/chrome/test_discardSystemSource.xhtml
new file mode 100644
index 0000000000..86a353e580
--- /dev/null
+++ b/js/xpconnect/tests/chrome/test_discardSystemSource.xhtml
@@ -0,0 +1,81 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=990353
+-->
+<window title="Mozilla Bug 990353"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=990353"
+ target="_blank">Mozilla Bug 990353</a>
+ </body>
+
+ <!-- test code goes here -->
+ <script type="application/javascript">
+ <![CDATA[
+ /** Test for Bug 990353 **/
+ SimpleTest.waitForExplicitFinish();
+
+ function canary() {
+ // eslint-disable-next-line no-unused-vars
+ var someBitOfSource = 42;
+ }
+
+ var gLoadCount = 0;
+ function frameLoaded() {
+ switch (++gLoadCount) {
+ case 1:
+ ok(/native code/.test(window[0].canary.toString()), "System function should be sourceless: " + window[0].canary.toString());
+ ok(/native code/.test(window[0].onload.toString()), "System event handler should be sourceless: " + window[0].onload.toString());
+ var sb = new Cu.Sandbox("https://www.example.com", { discardSource: true });
+ Cu.evalInSandbox('function canary() { var someBitOfSource = 42; }', sb);
+ ok(/native code/.test(sb.canary.toString()), "Function from sandbox with explicit discarding should be sourceless");
+ try {
+ window[0].throwSomething();
+ ok(false, "should have thrown");
+ } catch (e) {
+ ok(/some error/.test(e), "Threw exception as expected: " + e);
+ ok(/throwSomething/.test(e.stack), "Exception stack trace works: " + e.stack);
+ }
+ window[0].location = "https://example.org/tests/js/xpconnect/tests/chrome/file_discardSystemSource.html";
+ break;
+ case 2:
+ ok(/someBitOfSource/.test(Cu.waiveXrays(window[0]).canary.toString()), "Content function should have source");
+ ok(/someBitOfSource/.test(Cu.waiveXrays(window[0]).onload.toString()), "Content event handler should have source");
+ testWorker();
+ break;
+ }
+ }
+
+ function testWorker() {
+ var worker = new window[0].wrappedJSObject.Worker('worker_discardSystemSource.js');
+ worker.onmessage = function(evt) {
+ ok(/someBitOfSource/.test(evt.data), "Non-chrome worker should have source: " + evt.data);
+ var chromeWorker = new Worker('worker_discardSystemSource.js');
+ chromeWorker.onmessage = function(evt) {
+ ok(/native code/.test(evt.data), "Chrome worker should not have source: " + evt.data);
+ SimpleTest.finish();
+ }
+ }
+ }
+
+ function go() {
+ // We should have our own source, because the pref wasn't enabled when we
+ // were loaded.
+ ok(/someBitOfSource/.test(canary.toString()), "Should have own source");
+
+ window[0].frameElement.onload = frameLoaded;
+ window[0].location = "file_discardSystemSource.html";
+ }
+ addLoadEvent(function() {
+ SpecialPowers.pushPrefEnv({set: [['javascript.options.discardSystemSource', true]]}, go);
+ });
+
+ ]]>
+ </script>
+ <iframe></iframe>
+</window>
diff --git a/js/xpconnect/tests/chrome/test_documentdomain.xhtml b/js/xpconnect/tests/chrome/test_documentdomain.xhtml
new file mode 100644
index 0000000000..8cffdc8e46
--- /dev/null
+++ b/js/xpconnect/tests/chrome/test_documentdomain.xhtml
@@ -0,0 +1,100 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=601277
+-->
+<window title="Mozilla Bug 601277"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=601277"
+ target="_blank">Mozilla Bug 601277</a>
+ </body>
+
+ <!-- test code goes here -->
+ <script type="application/javascript">
+ <![CDATA[
+ /** Tests for document.domain. **/
+
+ SimpleTest.waitForExplicitFinish();
+
+ // Wait for the frames to load.
+ var gFramesLoaded = 0;
+ function frameLoaded() {
+ gFramesLoaded++;
+ if (gFramesLoaded == document.getElementsByTagName('iframe').length)
+ startTest();
+ }
+
+ function startTest() {
+
+ // Grab all the content windows and waive Xray. Xray waivers only apply to
+ // chrome, so we can pass these references directly to content.
+ var win1A = document.getElementById('test1A').contentWindow.wrappedJSObject;
+ var win1B = document.getElementById('test1B').contentWindow.wrappedJSObject;
+ var win2 = document.getElementById('test2').contentWindow.wrappedJSObject;
+ var winBase = document.getElementById('base').contentWindow.wrappedJSObject;
+
+ // Check the basics.
+ ok(win1A.tryToAccess(win1B),
+ "Same-origin windows should grant access");
+ ok(!win1A.tryToAccess(win2),
+ "Cross-origin windows should not grant access");
+ ok(!win1A.tryToAccess(winBase),
+ "Subdomain windows should not receive access");
+
+ // Store references now, while test1A and test1B are same-origin.
+ win1A.storeReference(win1B);
+ win1B.storeReference(win1A);
+ ok(win1A.tryToAccessStored(), "Stored references work when same-origin");
+ win1A.evalFromB = Cu.unwaiveXrays(win1B.eval); // Crashtest for bug 1040181.
+ win1B.functionFromA = Cu.unwaiveXrays(win1A.Function); // Crashtest for bug 1040181.
+ ok(!win1A.invokingFunctionThrowsSecurityException('evalFromB'), "Should allow before document.domain");
+ ok(!win1B.invokingFunctionThrowsSecurityException('functionFromA'), "Should allow before document.domain");
+
+ // Set document.domain on test1A. This should grant no access, since nobody
+ // else set it.
+ win1A.setDomain('example.org');
+ ok(!win1A.tryToAccess(winBase), "base must collaborate too");
+ ok(!winBase.tryToAccess(win1A), "base must collaborate too");
+ ok(!win1A.tryToAccess(win1B), "No longer same-origin");
+ ok(win1A.tryToAccessStored(), "We don't revoke access except through Window and Location");
+ ok(!win1B.tryToAccess(win1A), "No longer same-origin");
+ ok(win1B.tryToAccessStored(), "We don't revoke access except through Window and Location");
+ ok(!win1A.invokingFunctionThrowsSecurityException('evalFromB'), "We don't revoke access except through Window and Location");
+ ok(!win1B.invokingFunctionThrowsSecurityException('functionFromA'), "We don't revoke access except through Window and Location");
+
+ // Set document.domain on test1B. Now we're cooking with gas.
+ win1B.setDomain('example.org');
+ ok(!win1B.tryToAccess(winBase), "base must collaborate too");
+ ok(!winBase.tryToAccess(win1B), "base must collaborate too");
+ ok(win1A.tryToAccess(win1B), "same-origin");
+ ok(win1A.tryToAccessStored(), "same-origin");
+ ok(win1B.tryToAccess(win1A), "same-origin");
+ ok(win1B.tryToAccessStored(), "same-origin");
+
+ // Explicitly collaborate with base.
+ winBase.setDomain('example.org');
+ ok(winBase.tryToAccess(win1A), "base collaborates");
+ ok(win1A.tryToAccess(winBase), "base collaborates");
+
+ // All done.
+ SimpleTest.finish();
+ }
+
+
+ ]]>
+ </script>
+
+ <iframe id="test1A" onload="frameLoaded();" type="content"
+ src="http://test1.example.org/tests/js/xpconnect/tests/mochitest/file_documentdomain.html" />
+ <iframe id="test1B" onload="frameLoaded();" type="content"
+ src="http://test1.example.org/tests/js/xpconnect/tests/mochitest/file_documentdomain.html" />
+ <iframe id="test2" onload="frameLoaded();" type="content"
+ src="http://test2.example.org/tests/js/xpconnect/tests/mochitest/file_documentdomain.html" />
+ <iframe id="base" onload="frameLoaded();" type="content"
+ src="http://example.org/tests/js/xpconnect/tests/mochitest/file_documentdomain.html" />
+</window>
diff --git a/js/xpconnect/tests/chrome/test_doublewrappedcompartments.xhtml b/js/xpconnect/tests/chrome/test_doublewrappedcompartments.xhtml
new file mode 100644
index 0000000000..dd8ddd1bef
--- /dev/null
+++ b/js/xpconnect/tests/chrome/test_doublewrappedcompartments.xhtml
@@ -0,0 +1,41 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=533596
+-->
+<window title="Mozilla Bug 533596"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+
+ <iframe type="content"
+ src="http://example.org/tests/js/xpconnect/tests/mochitest/file_doublewrappedcompartments.html"
+ onload="go()"
+ id="ifr">
+ </iframe>
+ </body>
+
+ <!-- test code goes here -->
+ <script type="application/javascript"><![CDATA[
+ const utils = SpecialPowers.DOMWindowUtils;
+
+ function go() {
+ var wrappedWin = $('ifr').contentWindow;
+ is(typeof wrappedWin.expando, 'undefined', "Shouldn't be able to see the expando");
+
+ var unwrapped = wrappedWin.wrappedJSObject;
+
+ var expando = unwrapped.expando;
+ isnot(expando, 'undefined', 'properly wrapped');
+ is(typeof expando.querySelector, 'function', 'double wrapped');
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ ]]></script>
+</window>
diff --git a/js/xpconnect/tests/chrome/test_envChain_event_handler.html b/js/xpconnect/tests/chrome/test_envChain_event_handler.html
new file mode 100644
index 0000000000..f492e11512
--- /dev/null
+++ b/js/xpconnect/tests/chrome/test_envChain_event_handler.html
@@ -0,0 +1,137 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1782450
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1782450</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://global/skin"/>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+"use strict";
+
+SimpleTest.waitForExplicitFinish();
+
+// Verify the environment chain for DOM event handlers described in
+// js/src/vm/EnvironmentObject.h.
+
+let count = 0;
+function check(envs, hasEval) {
+ is(envs.length, hasEval ? 8 : 6);
+
+ let i = 0, env;
+
+ if (hasEval) {
+ env = envs[i]; i++;
+ is(env.type, "BlockLexicalEnvironmentObject");
+ is(env.qualified, false);
+ is(env.unqualified, false);
+ is(env.lexical, true, "lexical must live in the function lexical env");
+ is(env.prop, false);
+ is(env.form_prop, false);
+ is(env.document_prop, false);
+ is(env.button_prop, false);
+
+ env = envs[i]; i++;
+ is(env.type, "CallObject");
+ is(env.qualified, true, "qualified var live in the function call object");
+ is(env.unqualified, false);
+ is(env.lexical, false);
+ is(env.prop, false);
+ is(env.form_prop, false);
+ is(env.document_prop, false);
+ is(env.button_prop, false);
+ } else {
+ // qualified var and lexical live in function's frame.
+ }
+
+ env = envs[i]; i++;
+ is(env.type, "NonSyntacticLexicalEnvironmentObject");
+ is(env.qualified, false);
+ is(env.unqualified, false);
+ is(env.lexical, false);
+ is(env.prop, false);
+ is(env.form_prop, false);
+ is(env.document_prop, false);
+ is(env.button_prop, false);
+
+ env = envs[i]; i++;
+ is(env.type, "WithEnvironmentObject");
+ is(env.qualified, false);
+ is(env.unqualified, false);
+ is(env.lexical, false);
+ is(env.prop, true, "this property must live in the with env for button");
+ is(env.form_prop, false);
+ is(env.document_prop, false);
+ is(env.button_prop, true, "button property must live in the with env for button");
+
+ env = envs[i]; i++;
+ is(env.type, "WithEnvironmentObject");
+ is(env.qualified, false);
+ is(env.unqualified, false);
+ is(env.lexical, false);
+ is(env.prop, false);
+ is(env.form_prop, true, "form property must live in the with env for form");
+ is(env.document_prop, false);
+ is(env.button_prop, false);
+
+ env = envs[i]; i++;
+ is(env.type, "WithEnvironmentObject");
+ is(env.qualified, false);
+ is(env.unqualified, false);
+ is(env.lexical, false);
+ is(env.prop, false);
+ is(env.form_prop, false);
+ is(env.document_prop, true, "document property must live in the with env for document");
+ is(env.button_prop, false);
+
+ env = envs[i]; i++;
+ is(env.type, "GlobalLexicalEnvironmentObject");
+ is(env.qualified, false);
+ is(env.unqualified, false);
+ is(env.lexical, false);
+ is(env.prop, false);
+ is(env.form_prop, false);
+ is(env.document_prop, false);
+ is(env.button_prop, false);
+
+ env = envs[i]; i++;
+ is(env.type, "*global*");
+ is(env.qualified, false);
+ is(env.unqualified, true, "unqualified name must live in the global");
+ is(env.lexical, false);
+ is(env.prop, false);
+ is(env.form_prop, false);
+ is(env.document_prop, false);
+ is(env.button_prop, false);
+
+ count++;
+ if (count == 2) {
+ SimpleTest.finish();
+ }
+}
+ </script>
+</head>
+<body>
+<form id="form">
+<button id="button_optimized" onclick="event.preventDefault(); var qualified = 10; unqualified = 20; let lexical = 30; this.prop = 40; const funcs = Cu.getJSTestingFunctions(); const envs = []; let env = funcs.getInnerMostEnvironmentObject(); while (env) { envs.push({ type: funcs.getEnvironmentObjectType(env) || '*global*', qualified: !!Object.getOwnPropertyDescriptor(env, 'qualified'), unqualified: !!Object.getOwnPropertyDescriptor(env, 'unqualified'), lexical: !!Object.getOwnPropertyDescriptor(env, 'lexical'), prop: !!Object.getOwnPropertyDescriptor(env, 'prop'), document_prop: !!Object.getOwnPropertyDescriptor(env, 'document_prop'), form_prop: !!Object.getOwnPropertyDescriptor(env, 'form_prop'), button_prop: !!Object.getOwnPropertyDescriptor(env, 'button_prop'), }); env = funcs.getEnclosingEnvironmentObject(env); } check(envs, false); return false;">Click Me!</button>
+<!-- Put direct eval to de-optimize function scope -->
+<button id="button_unoptimized" onclick="event.preventDefault(); eval(''); var qualified = 10; unqualified = 20; let lexical = 30; this.prop = 40; const funcs = Cu.getJSTestingFunctions(); const envs = []; let env = funcs.getInnerMostEnvironmentObject(); while (env) { envs.push({ type: funcs.getEnvironmentObjectType(env) || '*global*', qualified: !!Object.getOwnPropertyDescriptor(env, 'qualified'), unqualified: !!Object.getOwnPropertyDescriptor(env, 'unqualified'), lexical: !!Object.getOwnPropertyDescriptor(env, 'lexical'), prop: !!Object.getOwnPropertyDescriptor(env, 'prop'), document_prop: !!Object.getOwnPropertyDescriptor(env, 'document_prop'), form_prop: !!Object.getOwnPropertyDescriptor(env, 'form_prop'), button_prop: !!Object.getOwnPropertyDescriptor(env, 'button_prop'), }); env = funcs.getEnclosingEnvironmentObject(env); } check(envs, true);">Click Me!</button>
+</form>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1782450">Mozilla Bug 1782450</a>
+<script type="application/javascript">
+"use strict";
+document.document_prop = 50;
+const form = document.getElementById("form");
+form.form_prop = 50;
+const button_unoptimized = document.getElementById("button_unoptimized");
+button_unoptimized.button_prop = 60;
+button_unoptimized.click();
+const button_optimized = document.getElementById("button_optimized");
+button_optimized.button_prop = 60;
+button_optimized.click();
+</script>
+</body>
+</html>
diff --git a/js/xpconnect/tests/chrome/test_evalInSandbox.xhtml b/js/xpconnect/tests/chrome/test_evalInSandbox.xhtml
new file mode 100644
index 0000000000..ac65151101
--- /dev/null
+++ b/js/xpconnect/tests/chrome/test_evalInSandbox.xhtml
@@ -0,0 +1,205 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=533596
+-->
+<window title="Mozilla Bug 533596"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+
+ <iframe src="http://example.org/tests/js/xpconnect/tests/mochitest/file_evalInSandbox.html"
+ onload="checkCrossOrigin(this)">
+ </iframe>
+ <iframe src="chrome://mochitests/content/chrome/js/xpconnect/tests/chrome/file_evalInSandbox.html"
+ onload="checkSameOrigin(this)">
+ </iframe>
+ </body>
+
+ <!-- test code goes here -->
+ <script type="application/javascript"><![CDATA[
+ const utils = window.windowUtils;
+
+ function checkCrossOriginSandbox(sandbox)
+ {
+ is(utils.getClassName(sandbox),
+ "Proxy",
+ "sandbox was wrapped correctly");
+
+ is(utils.getClassName(Cu.evalInSandbox("this.document", sandbox)),
+ "Proxy",
+ "return value was rewrapped correctly");
+ }
+
+ function checkCrossOriginXrayedSandbox(sandbox)
+ {
+ ok(Cu.evalInSandbox("!('windowfoo' in window);", sandbox),
+ "the window itself Xray is an XrayWrapper");
+ ok(Cu.evalInSandbox("('wrappedJSObject' in this.document);", sandbox),
+ "wrappers inside eIS are Xrays");
+ ok(Cu.evalInSandbox("!('foo' in this.document);", sandbox),
+ "must not see expandos");
+ ok('wrappedJSObject' in Cu.evalInSandbox("this.document", sandbox),
+ "wrappers returned from the sandbox are Xrays");
+ ok(!("foo" in Cu.evalInSandbox("this.document", sandbox)),
+ "must not see expandos in wrappers returned from the sandbox");
+
+ ok('wrappedJSObject' in sandbox.document,
+ "values obtained from the sandbox are Xrays");
+ ok(!("foo" in sandbox.document),
+ "must not see expandos in wrappers obtained from the sandbox");
+
+ }
+
+ function checkCrossOrigin(ifr) {
+ var win = ifr.contentWindow;
+ var sandbox =
+ new Cu.Sandbox(win, { sandboxPrototype: win, wantXrays: true } );
+
+ checkCrossOriginSandbox(sandbox);
+ checkCrossOriginXrayedSandbox(sandbox);
+
+ sandbox =
+ new Cu.Sandbox(win, { sandboxPrototype: win } );
+
+ checkCrossOriginSandbox(sandbox);
+ checkCrossOriginXrayedSandbox(sandbox);
+
+ sandbox =
+ new Cu.Sandbox(win, { sandboxPrototype: win, wantXrays: false } );
+
+ checkCrossOriginSandbox(sandbox);
+
+ ok(Cu.evalInSandbox("('foo' in this.document);", sandbox),
+ "can see expandos");
+ ok(!("foo" in Cu.evalInSandbox("this.document", sandbox)),
+ "must not see expandos in wrappers returned from the sandbox");
+ ok(("foo" in Cu.waiveXrays(Cu.evalInSandbox("this.document", sandbox))),
+ "must see expandos in waived wrappers returned from the sandbox");
+
+ ok(!("foo" in sandbox.document),
+ "must not see expandos in wrappers obtained from the sandbox");
+ ok("foo" in Cu.waiveXrays(sandbox.document),
+ "must see expandos in wrappers obtained from the sandbox");
+
+ testDone();
+ }
+
+ function checkSameOrigin(ifr) {
+ var win = ifr.contentWindow;
+ var sandbox =
+ new Cu.Sandbox(win, { sandboxPrototype: win, wantXrays: true } );
+
+ ok(Cu.evalInSandbox("('foo' in this.document);", sandbox),
+ "must see expandos for a chrome sandbox");
+
+ sandbox =
+ new Cu.Sandbox(win, { sandboxPrototype: win } );
+
+ ok(Cu.evalInSandbox("('foo' in this.document);", sandbox),
+ "must see expandos for a chrome sandbox");
+
+ sandbox =
+ new Cu.Sandbox(win, { sandboxPrototype: win, wantXrays: false } );
+
+ ok(Cu.evalInSandbox("('foo' in this.document);", sandbox),
+ "can see expandos for a chrome sandbox");
+
+ testDone();
+ }
+
+ var testsRun = 0;
+ function testDone() {
+ if (++testsRun == 2)
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+
+ try {
+ var sandbox1 = new Cu.Sandbox(this, { sandboxPrototype: undefined } );
+ ok(false, "undefined is not a valid prototype");
+ }
+ catch (e) {
+ ok(true, "undefined is not a valid prototype");
+ }
+
+ try {
+ var sandbox2 = new Cu.Sandbox(this, { wantXrays: undefined } );
+ ok(false, "undefined is not a valid value for wantXrays");
+ }
+ catch (e) {
+ ok(true, "undefined is not a valid value for wantXrays");
+ }
+
+ // Crash test for bug 601829.
+ try {
+ Cu.evalInSandbox('', null);
+ } catch (e) {
+ ok(true, "didn't crash on a null sandbox object");
+ }
+
+ try {
+ var sandbox3 = new Cu.Sandbox(this, { sameZoneAs: this } );
+ ok(true, "sameZoneAs works");
+ }
+ catch (e) {
+ ok(false, "sameZoneAs works");
+ }
+
+ // The 'let' keyword only appears with JS 1.7 and above. We use this fact
+ // to make sure that sandboxes get explict JS versions and don't inherit
+ // them from the most recent scripted frame.
+ function checkExplicitVersions() {
+ // eslint-disable-next-line no-undef
+ var sb = new Cu.Sandbox(sop);
+ Cu.evalInSandbox('let someVariable = 42', sb, '1.7');
+ ok(true, "Didn't throw with let");
+ try {
+ Cu.evalInSandbox('let someVariable = 42', sb);
+ ok(false, "Should have thrown with let");
+ } catch (e) {
+ ok(true, "Threw with let: " + e);
+ }
+ try {
+ Cu.evalInSandbox('let someVariable = 42', sb, '1.5');
+ ok(false, "Should have thrown with let");
+ } catch (e) {
+ ok(true, "Threw with let: " + e);
+ }
+ }
+ var outerSB = new Cu.Sandbox(this);
+ Cu.evalInSandbox(checkExplicitVersions.toSource(), outerSB, '1.7');
+ outerSB.ok = ok;
+ outerSB.sop = this;
+ Cu.evalInSandbox('checkExplicitVersions();', outerSB);
+
+ const {addDebuggerToGlobal} = ChromeUtils.importESModule("resource://gre/modules/jsdebugger.sys.mjs");
+ addDebuggerToGlobal(globalThis);
+
+ try {
+ let dbg = new Debugger();
+ let sandbox = new Cu.Sandbox(this, {
+ invisibleToDebugger: false,
+ freshCompartment: true,
+ });
+ dbg.addDebuggee(sandbox);
+ ok(true, "debugger added visible value");
+ } catch(e) {
+ ok(false, "debugger could not add visible value");
+ }
+
+ try {
+ let dbg = new Debugger();
+ let sandbox = new Cu.Sandbox(this, { invisibleToDebugger: true });
+ dbg.addDebuggee(sandbox);
+ ok(false, "debugger added invisible value");
+ } catch(e) {
+ ok(true, "debugger did not add invisible value");
+ }
+ ]]></script>
+</window>
diff --git a/js/xpconnect/tests/chrome/test_evalInWindow.xhtml b/js/xpconnect/tests/chrome/test_evalInWindow.xhtml
new file mode 100644
index 0000000000..00299266f2
--- /dev/null
+++ b/js/xpconnect/tests/chrome/test_evalInWindow.xhtml
@@ -0,0 +1,71 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=877673
+-->
+<window title="Mozilla Bug 877673"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ </body>
+
+ <!-- test code goes here -->
+ <script type="application/javascript"><![CDATA[
+ SimpleTest.waitForExplicitFinish();
+ var sb = new Cu.Sandbox("https://example.org", {wantExportHelpers: true});
+ sb.ok = ok;
+
+ function executeIn(frame, script, exceptionCb) {
+ sb.frame = frame;
+ sb.exceptionCb = exceptionCb;
+ if (exceptionCb) {
+ return Cu.evalInSandbox("try {frame.eval('" + script + "'); ok(false, 'Exception should have been thrown.')} catch(e) {exceptionCb(e)}", sb);
+ }
+
+ return Cu.evalInSandbox("frame.eval('" + script + "')", sb);
+ }
+
+ function testSameOrigin(frame) {
+ frame.contentWindow.document.wrappedJSObject.str = "foobar";
+ is(executeIn(frame.contentWindow, "document.str"), "foobar",
+ "Same origin string property access.");
+
+ executeIn(frame.contentWindow, 'document.obj = {prop: "foobar"}');
+ is((executeIn(frame.contentWindow, "document.obj")).prop, "foobar",
+ "Same origin object property access (cloning).");
+ isnot(executeIn(frame.contentWindow, "document.obj"), frame.contentWindow.document.wrappedJSObject.obj,
+ "Ensure cloning for js objects.");
+ is(executeIn(frame.contentWindow, "document"), frame.contentWindow.document,
+ "Xrayables should just pass without cloning.");
+ is( executeIn(frame.contentWindow, "({a:{doc: document}})").a.doc, frame.contentWindow.document,
+ "Deep cloning works.");
+
+ executeIn(frame.contentWindow, "throw 42", function(e){is(e, 42,
+ "Exception was thrown from script.")});
+
+ testDone();
+ }
+
+ function testCrossOrigin(frame) {
+ executeIn(frame.contentWindow, "var a = 42;", function(e){ok(e.toString().indexOf("Permission denied") > -1,
+ "Executing script in a window from cross origin should throw.");});
+ testDone();
+ }
+
+ var testsRun = 0;
+ function testDone() {
+ if (++testsRun == 2)
+ SimpleTest.finish();
+ }
+ ]]></script>
+ <iframe src="https://example.org/tests/js/xpconnect/tests/mochitest/file_empty.html"
+ onload="testSameOrigin(this)">
+ </iframe>
+ <iframe src="http://mochi.test:8888/tests/js/xpconnect/tests/mochitest/file_empty.html"
+ onload="testCrossOrigin(this)">
+ </iframe>
+</window>
diff --git a/js/xpconnect/tests/chrome/test_exnstack.xhtml b/js/xpconnect/tests/chrome/test_exnstack.xhtml
new file mode 100644
index 0000000000..48bcac5243
--- /dev/null
+++ b/js/xpconnect/tests/chrome/test_exnstack.xhtml
@@ -0,0 +1,68 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=735544
+-->
+<window title="Mozilla Bug 735544"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=735544"
+ target="_blank">Mozilla Bug 735544</a>
+ <iframe id='ifr0' onload="frameDone(0);" src="http://mochi.test:8888/tests/js/xpconnect/tests/mochitest/file_exnstack.html" />
+ <iframe id='ifr1' onload="frameDone(1);" src="http://mochi.test:8888/tests/js/xpconnect/tests/mochitest/file_exnstack.html" />
+ </body>
+
+ <!-- test code goes here -->
+ <script type="application/javascript">
+ <![CDATA[
+ /** Test for Bug 735544 - Allow exception stacks to cross compartment boundaries **/
+
+ SimpleTest.waitForExplicitFinish();
+
+ var gFramesDone = [false, false];
+ function frameDone(idx) {
+ gFramesDone[idx] = true;
+ if (gFramesDone[0] && gFramesDone[1])
+ startTest();
+ }
+
+ function throwAsChrome() {
+
+ // Grab the iframe content windows.
+ var cwin0 = document.getElementById('ifr0').contentWindow;
+ var cwin1 = document.getElementById('ifr1').contentWindow;
+
+ // Have cwin0 call a function on cwin1 that throws.
+ cwin0.wrappedJSObject.doThrow(cwin1);
+ }
+
+ function startTest() {
+
+ try {
+ throwAsChrome();
+ ok(false, "should throw");
+ } catch (e) {
+
+ let stackFrames = e.stack.split("\n");
+
+ ok(/throwAsInner/.exec(stackFrames[0]),
+ "The bottom frame should be thrown by the inner");
+
+ ok(/throwAsOuter/.exec(stackFrames[2]),
+ "The 3rd-from-bottom frame should be thrown by the other");
+
+ ok(!/throwAsChrome/.exec(e.stack),
+ "The entire stack should not cross into chrome.");
+ }
+
+ SimpleTest.finish();
+ }
+
+ ]]>
+ </script>
+
+</window>
diff --git a/js/xpconnect/tests/chrome/test_expandosharing.xhtml b/js/xpconnect/tests/chrome/test_expandosharing.xhtml
new file mode 100644
index 0000000000..1dc95239f4
--- /dev/null
+++ b/js/xpconnect/tests/chrome/test_expandosharing.xhtml
@@ -0,0 +1,147 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=758415
+-->
+<window title="Mozilla Bug 758415"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=758415"
+ target="_blank">Mozilla Bug 758415</a>
+ </body>
+
+ <!-- test code goes here -->
+ <script type="application/javascript">
+ <![CDATA[
+
+ /** Test for Cross-Origin Xray Expando Sharing. **/
+ SimpleTest.waitForExplicitFinish();
+
+ // Import our test JSM. We first strip the filename off
+ // the chrome url, then append the jsm filename.
+ var base = /.*\//.exec(window.location.href)[0];
+ const {checkFromJSM} = ChromeUtils.import(base + "file_expandosharing.jsm");
+
+ // Wait for all child frames to load.
+ var gLoadCount = 0;
+ function frameLoaded() {
+ if (++gLoadCount == window.frames.length)
+ go();
+ }
+
+ function go() {
+
+ // Empower the content windows with some functions.
+ var wins = document.getElementsByTagName('iframe');
+ for (var i = 0; i < wins.length; ++i) {
+ var win = wins[i].contentWindow.wrappedJSObject;
+ win.ok = ok;
+ win.is = is;
+ }
+
+ // Grab references to the content windows. We abbreviate the origins as follows:
+ // A: test1.example.org
+ // B: test2.example.org
+ // C: sub1.test1.example.org
+ window.gWinA1 = document.getElementById('frameA1').contentWindow;
+ window.gWinA2 = document.getElementById('frameA2').contentWindow;
+ window.gWinA3 = document.getElementById('frameA3').contentWindow;
+ window.gWinB = document.getElementById('frameB').contentWindow;
+ window.gWinC = document.getElementById('frameC').contentWindow;
+
+ /* globals gWinA1, gWinA2, gWinA3, gWinB, gWinC */
+
+ // Test expando sharing with a JSM for different types of Xrays.
+ testJSM(Cu.unwaiveXrays(gWinC.wrappedJSObject.targetWN));
+ testJSM(Cu.unwaiveXrays(gWinC.wrappedJSObject.targetDOM));
+ testJSM(Cu.unwaiveXrays(gWinC.wrappedJSObject.targetJS));
+
+ // Make sure sandboxes never share expandos with anyone else.
+ testSandbox(Cu.unwaiveXrays(gWinB.wrappedJSObject.targetWN));
+ testSandbox(Cu.unwaiveXrays(gWinB.wrappedJSObject.targetDOM));
+ testSandbox(Cu.unwaiveXrays(gWinB.wrappedJSObject.targetJS));
+
+ // Test Content Xrays.
+ testContentXrays();
+
+ SimpleTest.finish();
+ }
+
+ // Make sure that expandos are shared between us and a JSM.
+ function testJSM(target) {
+ target.numProp = 42;
+ target.strProp = "foo";
+ target.objProp = { bar: "baz" };
+ checkFromJSM(target, is);
+ }
+
+ function testSandbox(target) {
+
+ // This gets both run in this scope and the sandbox scope.
+ var name = "harness";
+ function placeExpando() {
+ target.prop = name;
+ }
+
+ // Set up the sandboxes. Use an expanded principal to get xrays with
+ // exclusive expandos.
+ let sb1 = Cu.Sandbox(["https://test1.example.org", "https://test2.example.org"]);
+ let sb2 = Cu.Sandbox(["https://test1.example.org", "https://test2.example.org"]);
+ sb1.target = target;
+ sb2.target = target;
+ sb1.name = "sandbox1";
+ sb2.name = "sandbox2";
+ placeExpando();
+ Cu.evalInSandbox(placeExpando.toSource() + "placeExpando();", sb1);
+ Cu.evalInSandbox(placeExpando.toSource() + "placeExpando();", sb2);
+
+ // Make sure everyone sees a different value.
+ is(target.prop, "harness", "Harness sees its own value");
+ is(Cu.evalInSandbox("target.prop", sb1), "sandbox1", "Sandbox 1 sees its own value");
+ is(Cu.evalInSandbox("target.prop", sb2), "sandbox2", "Sandbox 2 sees its own value");
+ }
+
+ // Make sure that the origin tagging machinery works correctly and that we don't
+ // mix up chrome and content expandos.
+ function testContentXrays() {
+
+ // Give A1 and A3 xrays to (same-origin) A2.
+ Cu.setWantXrays(gWinA1);
+ Cu.setWantXrays(gWinA3);
+
+ gWinA1.wrappedJSObject.placeExpando('A1_expando', 11, gWinA2.document);
+ gWinA3.wrappedJSObject.placeExpando('A3_expando', 33, gWinA2.document);
+ gWinA2.document.Chrome_expando = 33;
+
+ is(gWinA2.document.Chrome_expando, 33, "Read chrome expando properly");
+ is(typeof gWinA2.document.A1_expando, 'undefined', "Chrome doesn't see content expandos");
+ is(typeof gWinA2.document.A3_expando, 'undefined', "Chrome doesn't see content expandos");
+ gWinA1.wrappedJSObject.checkExpando('A1_expando', 11, gWinA2.document, "Content sees proper expandos");
+ gWinA3.wrappedJSObject.checkExpando('A1_expando', 11, gWinA2.document, "Content sees proper expandos");
+ gWinA1.wrappedJSObject.checkExpando('A3_expando', 33, gWinA2.document, "Content sees proper expandos");
+ gWinA3.wrappedJSObject.checkExpando('A3_expando', 33, gWinA2.document, "Content sees proper expandos");
+ gWinA1.wrappedJSObject.checkExpando('Chrome_expando', null, gWinA2.document, "Content doesn't see chrome expandos");
+ gWinA3.wrappedJSObject.checkExpando('Chrome_expando', null, gWinA2.document, "Content doesn't see chrome expandos");
+
+ // We very explicitly do not support expando sharing via document.domain.
+ // A comment in the implementation explains why.
+ gWinA1.document.domain = 'test1.example.org';
+ gWinA2.document.domain = 'test1.example.org';
+ gWinA3.document.domain = 'test1.example.org';
+ gWinC.document.domain = 'test1.example.org';
+ gWinC.wrappedJSObject.checkExpando('A1_expando', null, gWinA2.document, "document.domain should have no effect here");
+ gWinC.wrappedJSObject.checkExpando('A3_expando', null, gWinA2.document, "document.domain should have no effect here");
+ }
+
+ ]]>
+ </script>
+ <iframe id="frameA1" onload="frameLoaded();" type="content" src="https://test1.example.org/tests/js/xpconnect/tests/mochitest/file_expandosharing.html" />
+ <iframe id="frameA2" onload="frameLoaded();" type="content" src="https://test1.example.org/tests/js/xpconnect/tests/mochitest/file_expandosharing.html" />
+ <iframe id="frameA3" onload="frameLoaded();" type="content" src="https://test1.example.org/tests/js/xpconnect/tests/mochitest/file_expandosharing.html" />
+ <iframe id="frameB" onload="frameLoaded();" type="content" src="https://test2.example.org/tests/js/xpconnect/tests/mochitest/file_expandosharing.html" />
+ <iframe id="frameC" onload="frameLoaded();" type="content" src="https://sub1.test1.example.org/tests/js/xpconnect/tests/mochitest/file_expandosharing.html" />
+</window>
diff --git a/js/xpconnect/tests/chrome/test_exposeInDerived.xhtml b/js/xpconnect/tests/chrome/test_exposeInDerived.xhtml
new file mode 100644
index 0000000000..ee6ad2d229
--- /dev/null
+++ b/js/xpconnect/tests/chrome/test_exposeInDerived.xhtml
@@ -0,0 +1,45 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=804630
+-->
+<window title="Mozilla Bug 804630"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=804630"
+ target="_blank">Mozilla Bug 804630</a>
+ </body>
+
+ <!-- test code goes here -->
+ <script type="application/javascript">
+ <![CDATA[
+ /** Test to make sure that COWed objects can't expose properties from their prototypes. **/
+ // Set up the sandbox.
+ var sb = new Cu.Sandbox("https://www.example.com");
+ sb.ok = ok;
+ sb.is = is;
+
+ // Make a chrome object that tries to expose objects off its prototype.
+ sb.proto = { read: 42, readWrite: 32 };
+ sb.obj = {};
+ sb.obj.__proto__ = sb.proto;
+
+ // Make sure we can't access any of the properties on the prototype directly.
+ Cu.evalInSandbox('is(proto.read, undefined, "proto.read inaccessible");', sb);
+ Cu.evalInSandbox('var wrote = false; ' +
+ 'try { proto.readWrite = 12; wrote = true; } catch(e) {} ' +
+ ' ok(!wrote, "Should not write proto property");', sb);
+
+ // Make sure we can't access the exposed properties via the derived object.
+ Cu.evalInSandbox('is(obj.read, undefined, "obj.read inaccessible");', sb);
+ Cu.evalInSandbox('is(obj.readWrite, undefined, "obj.readWrite is not readable");', sb);
+ Cu.evalInSandbox('try { obj.readWrite = 8; ok(false, "obj.readWrite is not writable"); } catch (e) {};',
+ sb);
+
+ ]]>
+ </script>
+</window>
diff --git a/js/xpconnect/tests/chrome/test_inlineScripts.html b/js/xpconnect/tests/chrome/test_inlineScripts.html
new file mode 100644
index 0000000000..cb5f5ed530
--- /dev/null
+++ b/js/xpconnect/tests/chrome/test_inlineScripts.html
@@ -0,0 +1,53 @@
+<!DOCTYPE html>
+<html>
+ <meta charset=utf-8>
+ <title>Tests for nsIScriptError</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <div id="log"></div>
+
+ <!-- Verify that column is correct, even for inline scripts with HTML on the same line -->
+ <span>some html</span> <script>var inlineScriptStack = new Error().stack;</script>
+ <script>
+ function waitForError (expectedMessage){
+ return new Promise(resolve => {
+ const listener = {
+ QueryInterface: ChromeUtils.generateQI(["nsIConsoleListener"])
+ };
+
+ listener.observe = function(message) {
+ if (message.message.includes(expectedMessage)) {
+ message.QueryInterface(Ci.nsIScriptError);
+ resolve(message);
+ Services.console.unregisterListener(listener);
+ }
+ };
+
+ Services.console.registerListener(listener);
+ });
+ }
+
+ var onInlineScriptError = waitForError("doThrow");
+ var onModuleError = waitForError("doThrowInModule");
+ SimpleTest.expectUncaughtException();
+ </script>
+ <span>some more html</span><script>doThrow() // eslint-disable-line no-undef</script>
+ <script>var b;</script><hr><script type="module">SimpleTest.expectUncaughtException();doThrowInModule() // eslint-disable-line no-undef</script>
+ <script>
+ add_task(async () => {
+ info("Check line and column information in Error#stack");
+ const { groups } = inlineScriptStack.match(/(?<line>\d+):(?<column>\d+)/);
+ is(groups.line, "9", "line of Error#stack in inline script is correct");
+ is(groups.column, "58", "column of Error#stack in inline script is correct");
+
+ info("Check line and column information Error message in inline script");
+ const errorMessage = await onInlineScriptError;
+ is(errorMessage.lineNumber, 33, "The exception line is correct");
+ is(errorMessage.columnNumber, 38, "The exception column is correct");
+
+ info("Check line and column information Error message in inline module");
+ const errorMessageInModule = await onModuleError;
+ is(errorMessageInModule.lineNumber, 34, "The module exception line is correct");
+ is(errorMessageInModule.columnNumber, 89, "The module exception column is correct");
+ });
+ </script>
+</html> \ No newline at end of file
diff --git a/js/xpconnect/tests/chrome/test_localstorage_with_nsEp.xhtml b/js/xpconnect/tests/chrome/test_localstorage_with_nsEp.xhtml
new file mode 100644
index 0000000000..dbc0ea6a61
--- /dev/null
+++ b/js/xpconnect/tests/chrome/test_localstorage_with_nsEp.xhtml
@@ -0,0 +1,37 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=980023
+-->
+<window title="Mozilla Bug 980023 "
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=980023"
+ target="_blank">Mozilla Bug 980023 </a>
+ </body>
+
+ <!-- test code goes here -->
+ <script type="application/javascript">
+ <![CDATA[
+
+ /** Test for localstorage access with expanded principal. **/
+ SimpleTest.waitForExplicitFinish();
+
+ function go() {
+ var iwin = document.getElementById('ifr').contentWindow;
+ var sb = new Cu.Sandbox([iwin], {sandboxPrototype: iwin});
+ Cu.evalInSandbox("window.localStorage.test_localstorage_with_nsEp = 3",sb);
+ is(Cu.evalInSandbox("window.localStorage.test_localstorage_with_nsEp",sb), "3");
+ is(iwin.localStorage.test_localstorage_with_nsEp, "3");
+ iwin.localStorage.removeItem("test_localstorage_with_nsEp");
+ SimpleTest.finish();
+ }
+
+ ]]>
+ </script>
+ <iframe id="ifr" onload="go();" src="http://example.org/tests/js/xpconnect/tests/mochitest/file_empty.html" />
+</window>
diff --git a/js/xpconnect/tests/chrome/test_matches.xhtml b/js/xpconnect/tests/chrome/test_matches.xhtml
new file mode 100644
index 0000000000..96472bc8ac
--- /dev/null
+++ b/js/xpconnect/tests/chrome/test_matches.xhtml
@@ -0,0 +1,49 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=533596
+-->
+<window title="Mozilla Bug 533596"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+
+ <iframe src="http://example.org/tests/js/xpconnect/tests/mochitest/file_matches.html"
+ onload="runTest(this)">
+ </iframe>
+ </body>
+
+ <!-- test code goes here -->
+ <script type="application/javascript"><![CDATA[
+ SimpleTest.waitForExplicitFinish();
+ function runTest(ifr)
+ {
+ var doc = ifr.contentDocument;
+ var docElem = doc.documentElement;
+
+ var res = doc.createElement('div').matches('div');
+ is(res, true, "matches call through xray, regular case");
+
+ res = docElem.matches.call(
+ doc.createElement('div'), 'div');
+ is(res, true, "matches call through xray, with .call");
+
+ var sb = new Cu.Sandbox(ifr.contentWindow);
+ sb.doc = doc;
+ var str = "doc.documentElement.matches.call(doc.createElement( 'div' ),'div')";
+ res = Cu.evalInSandbox(str, sb);
+ is(res, true, "matches call through xray (same origin), with .call");
+
+ docElem.matches = function(){return false};
+ res = docElem.matches.call(doc.createElement( 'div' ),'div');
+ is(res, false, "shadowing matches with an expando on the xray wrapper");
+
+ SimpleTest.finish();
+ }
+
+ ]]></script>
+</window>
diff --git a/js/xpconnect/tests/chrome/test_nodelists.xhtml b/js/xpconnect/tests/chrome/test_nodelists.xhtml
new file mode 100644
index 0000000000..d3d4dfc34e
--- /dev/null
+++ b/js/xpconnect/tests/chrome/test_nodelists.xhtml
@@ -0,0 +1,49 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<window title="Test nodelists from chrome"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ </body>
+
+ <!-- test code goes here -->
+ <script type="application/javascript"><![CDATA[
+ SimpleTest.waitForExplicitFinish();
+
+ function go() {
+ var win = $('ifr').contentWindow;
+ var list = win.document.getElementsByTagName('p');
+ is(list.length, 3, "can get the length");
+ ok(HTMLParagraphElement.isInstance(list[0]), "can get list[0]");
+ is(list[0], list.item(0), "list.item works");
+ is(list.item, list.item, "don't recreate functions for each get");
+
+ var list2 = list[2];
+ ok(HTMLParagraphElement.isInstance(list[2]), "list[2] exists");
+ ok("2" in list, "in operator works");
+
+ is(win.document.body.removeChild(win.document.body.lastChild), list2, "remove last paragraph element");
+ ok(!("2" in list), "in operator doesn't see phantom element");
+ is(list[2], undefined, "no node there!");
+
+ var optionList = win.document.createElement("select").options;
+ var option = win.document.createElement("option");
+ optionList[0] = option;
+ is(optionList.item(0), option, "Creators work");
+
+ option = win.document.createElement("option");
+ optionList[0] = option;
+ is(optionList.item(0), option, "Setters work");
+
+ SimpleTest.finish();
+ }
+ ]]></script>
+
+ <iframe id="ifr"
+ src="http://mochi.test:8888/tests/js/xpconnect/tests/mochitest/file_nodelists.html"
+ onload="go()" />
+</window>
diff --git a/js/xpconnect/tests/chrome/test_nsScriptErrorWithStack.html b/js/xpconnect/tests/chrome/test_nsScriptErrorWithStack.html
new file mode 100644
index 0000000000..99a4727756
--- /dev/null
+++ b/js/xpconnect/tests/chrome/test_nsScriptErrorWithStack.html
@@ -0,0 +1,59 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Test for 814497</title>
+<script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+<div id="log"></div>
+<script>
+ var c = Cc;
+
+ SimpleTest.waitForExplicitFinish();
+ SimpleTest.expectUncaughtException();
+
+ // /!\ Line number is important in this test,
+ // we are asserting the following functions line #
+ function failingStack() {
+ nestedFunction();
+ }
+ function nestedFunction() {
+ // eslint-disable-next-line no-undef
+ doesntExistsAndThrow();
+ }
+
+ var TestObserver = {
+ QueryInterface: ChromeUtils.generateQI(["nsIObserver"]),
+
+ observe: function test_observe(aSubject)
+ {
+ if (!(aSubject instanceof Ci.nsIScriptError)) {
+ return;
+ }
+ dump("stack: "+aSubject.stack+"\n");
+
+ // Main assertions
+ var s = aSubject.stack;
+ ok(!!s, "has first frame");
+ ok(s.source.includes("test_nsScriptErrorWithStack.html"), "source is correct");
+ is(s.line, 19, "line is correct");
+ is(s.column, 5, "column is correct");
+ is(s.functionDisplayName, "nestedFunction");
+ s = s.parent;
+ ok(!!s, "has second frame");
+ ok(s.source.includes("test_nsScriptErrorWithStack.html"), "source is correct");
+ is(s.line, 15, "line is correct");
+ is(s.column, 5, "column is correct");
+ is(s.functionDisplayName, "failingStack");
+ // We shouldn't have any more frame as we used setTimeout
+ ok(!s.parent, "has no more frames");
+
+ // Cleanup
+ Services.console.unregisterListener(TestObserver);
+ SimpleTest.finish();
+ }
+ };
+
+ Services.console.registerListener(TestObserver);
+
+ // use setTimeout in order to prevent throwing from test frame
+ // and so have a clean stack frame with just our method calls
+ setTimeout(failingStack, 0);
+</script>
diff --git a/js/xpconnect/tests/chrome/test_onGarbageCollection.html b/js/xpconnect/tests/chrome/test_onGarbageCollection.html
new file mode 100644
index 0000000000..ce056ed354
--- /dev/null
+++ b/js/xpconnect/tests/chrome/test_onGarbageCollection.html
@@ -0,0 +1,48 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Bug 1150253 - Sanity test for the SpiderMonkey Debugger API's onGarbageCollection hook</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js">
+ </script>
+ </head>
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1150253"
+ target="_blank">Mozilla Bug 1150253</a>
+
+ <script type="application/javascript">
+ SimpleTest.waitForExplicitFinish();
+
+ const { gc } = Cu.getJSTestingFunctions();
+
+ // Instantiate `Debugger` in a sandbox as Debugger requires to be created
+ // in a compartment different than the debuggee.
+ let sandbox = Cu.Sandbox(
+ Components.Constructor("@mozilla.org/systemprincipal;1", "nsIPrincipal")(),
+ {
+ freshCompartment: true,
+ wantGlobalProperties: ["ChromeUtils"],
+ }
+ );
+ Cu.evalInSandbox(`
+const { addDebuggerToGlobal } = ChromeUtils.importESModule(
+ 'resource://gre/modules/jsdebugger.sys.mjs'
+);
+addDebuggerToGlobal(globalThis);
+`, sandbox
+ );
+
+ const dbg = new sandbox.Debugger(this);
+
+ dbg.memory.onGarbageCollection = function (data) {
+ // Don't keep calling this hook after we finish.
+ dbg.memory.onGarbageCollection = undefined;
+ dbg.removeAllDebuggees();
+
+ ok(data, "The onGarbageCollection hook was fired.");
+ SimpleTest.finish();
+ };
+
+ gc();
+ </script>
+ </body>
+</html>
diff --git a/js/xpconnect/tests/chrome/test_precisegc.xhtml b/js/xpconnect/tests/chrome/test_precisegc.xhtml
new file mode 100644
index 0000000000..56f047843e
--- /dev/null
+++ b/js/xpconnect/tests/chrome/test_precisegc.xhtml
@@ -0,0 +1,26 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=533596
+-->
+<window title="Mozilla Bug 661927"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+
+ </body>
+
+ <!-- test code goes here -->
+ <script type="application/javascript"><![CDATA[
+ Cu.schedulePreciseGC(
+ function() {
+ ok(true, "callback executed");
+ SimpleTest.finish();
+ });
+ SimpleTest.waitForExplicitFinish();
+ ]]></script>
+</window>
diff --git a/js/xpconnect/tests/chrome/test_private_field_cows.xhtml b/js/xpconnect/tests/chrome/test_private_field_cows.xhtml
new file mode 100644
index 0000000000..24fe9d5e0a
--- /dev/null
+++ b/js/xpconnect/tests/chrome/test_private_field_cows.xhtml
@@ -0,0 +1,131 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+
+<window title="Mozilla Bug ????" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <!-- test results are displayed in the html:body -->
+
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=>?????" target="_blank">Mozilla Bug ???? </a>
+ </body>
+
+ <!-- test code goes here -->
+ <script type="application/javascript"><![CDATA[
+ /* eslint-disable no-eval */
+
+ add_task(async () => {
+ var sandbox = new Cu.Sandbox("about:blank");
+
+ await SpecialPowers.pushPrefEnv({
+ "set": [["security.allow_eval_with_system_principal",
+ true]]
+ });
+
+ function getCOW(x) {
+ if (typeof x != 'object' && typeof x != 'function')
+ return x;
+ x = Cu.waiveXrays(x);
+ var rval = {};
+ if (typeof x == "function")
+ rval = eval(`(${x.toString()})`);
+ for (var i in x) {
+ if (x.__lookupGetter__(i))
+ rval.__defineGetter__(i, eval(`(${x.__lookupGetter__(i).toString()})`))
+ else
+ rval[i] = getCOW(x[i]);
+ }
+ return rval;
+ }
+
+ // Give the sandbox a way to create ChromeObjectWrapped objects.
+ sandbox.getCOW = getCOW;
+
+ // Define test API functions in the sandbox.
+ const TEST_API = ['is', 'ok', 'info'];
+ TEST_API.forEach(function (name) { sandbox[name] = window[name]; });
+
+
+ function COWTests() {
+
+ var empty = {};
+ var cow = getCOW(empty);
+
+ // Because private fields may not be enabled, we construct A via the below eval of an IFFE,
+ // and return early if it syntax errors.
+ var A;
+ try {
+ A = eval(`(function(){
+ class Base {
+ constructor(o) {
+ return o;
+ }
+ };
+
+ class A extends Base {
+ #x = 12;
+ static gx(o) {
+ return o.#x;
+ }
+
+ static sx(o, v) {
+ o.#x = v;
+ }
+ };
+ return A;
+ })();`);
+ } catch (e) {
+ is(e instanceof SyntaxError, true, "Syntax error: Private fields not enabled");
+ is(/private fields are not currently supported/.test(e.message), true, "correct message");
+ return;
+ }
+
+ new A(empty);
+ is(A.gx(empty), 12, "Correct value read");
+ A.sx(empty, 'wrapped');
+
+ function assertThrewInstance(f, error) {
+ var threw = true;
+ try {
+ f();
+ threw = false;
+ } catch (e) {
+ is(e instanceof error, true, "Correct Error");
+ }
+ is(threw, true, "Threw!");
+ }
+
+ // Note: These throw warnings:
+ //
+ // WARNING: Silently denied access to property ##x: Access to privileged JS object not permitted (@chrome://mochitests/content/chrome/js/xpconnect/tests/chrome/test_private_field_cows.xhtml:108:27): file /js/xpconnect/wrappers/XrayWrapper.cpp, line 226
+ //
+ // It's not clear to me if we ougth to wire this up to -not-? I suspect this is a result of invoking
+ // the has
+ assertThrewInstance(() => A.gx(cow), TypeError);
+ assertThrewInstance(() => A.sx(cow, 'unwrapped'), TypeError);
+ assertThrewInstance(() => new A(cow), Error);
+ assertThrewInstance(() => A.gx(cow), TypeError);
+ }
+
+ // Stringify the COW test suite and directly evaluate it in the sandbox.
+ Cu.evalInSandbox('(' + COWTests.toString() + ')()', sandbox);
+
+ // Test that COWed objects passing from content to chrome get unwrapped.
+ function returnCOW() {
+ return getCOW({
+ bar: 6
+ });
+ }
+
+ // eslint-disable-next-line no-unused-vars
+ var unwrapped = Cu.evalInSandbox(
+ '(' + returnCOW.toString() + ')()',
+ sandbox
+ );
+
+ ok(true, "passed");
+ });
+ ]]></script>
+</window>
diff --git a/js/xpconnect/tests/chrome/test_sandboxImport.xhtml b/js/xpconnect/tests/chrome/test_sandboxImport.xhtml
new file mode 100644
index 0000000000..1ae1f02ad7
--- /dev/null
+++ b/js/xpconnect/tests/chrome/test_sandboxImport.xhtml
@@ -0,0 +1,37 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=533596
+-->
+<window title="Mozilla Bug 533596"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ </body>
+
+ <!-- test code goes here -->
+ <script type="application/javascript"><![CDATA[
+
+ function checkWrapped(obj) {
+ var utils = window.windowUtils;
+ is(utils.getClassName(obj), "Proxy", "right type of wrapper");
+ }
+
+ var sandbox = new Cu.Sandbox("about:blank");
+ sandbox.importFunction(function() { return "PASS"; }, "foo");
+ sandbox.importFunction(function bar() { return "PASS"; });
+ sandbox.importFunction(checkWrapped);
+ is(Cu.evalInSandbox("foo()", sandbox), "PASS", "importFunction works");
+ is(Cu.evalInSandbox("bar()", sandbox), "PASS", "importFunction works");
+ Cu.evalInSandbox("checkWrapped({})", sandbox);
+
+ var importer = sandbox.importFunction;
+ importer(function() { return "PASS"; }, "bar");
+ is(Cu.evalInSandbox("bar()", sandbox), "PASS", "unbound importFunction works");
+ is(typeof this.bar, "undefined", "didn't import into our global");
+ ]]></script>
+</window>
diff --git a/js/xpconnect/tests/chrome/test_scriptSettings.xhtml b/js/xpconnect/tests/chrome/test_scriptSettings.xhtml
new file mode 100644
index 0000000000..3c41c0bfdc
--- /dev/null
+++ b/js/xpconnect/tests/chrome/test_scriptSettings.xhtml
@@ -0,0 +1,127 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=937317
+-->
+<window title="Mozilla Bug 937317"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=937317"
+ target="_blank">Mozilla Bug 937317</a>
+ </body>
+
+ <!-- test code goes here -->
+ <iframe src="./file_empty.html"></iframe>
+ <script type="application/javascript">
+ <![CDATA[
+
+ /** Test for the script settings stack. **/
+ SimpleTest.waitForExplicitFinish();
+ SpecialPowers.pushPrefEnv({"set": [["security.allow_eval_with_system_principal",
+ true]]});
+ addLoadEvent(function() {
+ let iwin = window[0];
+
+ // Smoketest.
+ is(Cu.getIncumbentGlobal(), window, "smoketest");
+
+ // Calling a cross-compartment non-scripted function changes the
+ // compartment, but not the incumbent script settings object.
+ var sb = new Cu.Sandbox(window, { wantComponents: true });
+ is(sb.Components.utils.getIncumbentGlobal(), window, "cross-compartment sb non-scripted");
+ is(iwin.Components.utils.getIncumbentGlobal(), window, "cross-compartment win non-scripted");
+
+ // If we call a scripted function in the other compartment, that becomes
+ // the incumbent script.
+ function gib() { return Cu.getIncumbentGlobal(); };
+ Cu.evalInSandbox(gib.toSource(), sb);
+ iwin.eval(gib.toSource());
+ is(sb.gib(), sb, "cross-compartment sb scripted");
+ is(iwin.gib(), iwin, "cross-compartment win scripted");
+
+ // Eval-ing top-level script in another component makes that compartment the
+ // incumbent script.
+ is(Cu.evalInSandbox('Components.utils.getIncumbentGlobal()', sb), sb, 'eval sb');
+ is(iwin.eval('Components.utils.getIncumbentGlobal()'), iwin, 'eval iwin');
+
+ // Make sure that the callback mechanism works.
+ function makeCallback(expectedGlobal, deferred, kind) {
+ function cb(incumbentGlobal) {
+ is(incumbentGlobal, expectedGlobal, "Callback got the right incumbent global: " + kind);
+ if (deferred)
+ deferred.resolve();
+ }
+ info("Generated callback: " + kind);
+ return cb;
+ }
+
+ var bound = Cu.getIncumbentGlobal.bind(Cu, makeCallback(window, undefined, "simple bound"));
+ is(bound(), window, "Bound method returns the right global");
+
+ // Callbacks grab the incumbent script at the time of invocation.
+ //
+ // Note - We avoid calling the initial defer |d| so that it's not in-scope for everything below.
+ let initialDefer = Promise.withResolvers();
+ setTimeout(Cu.getIncumbentGlobal.bind(Cu, makeCallback(window, initialDefer, "same-global setTimeout")), 0);
+ initialDefer.promise.then(function() {
+
+ // Try cross-global setTimeout where |window| is the incumbent script when the callback is created.
+ let d = Promise.withResolvers();
+ iwin.setTimeout(Cu.getIncumbentGlobal.bind(Cu, makeCallback(window, d, "cross-global setTimeout by |window|")), 0);
+ return d.promise;
+ }).then(function() {
+
+ // Try cross-global setTimeout where |iwin| is the incumbent script when the callback is created.
+ let d = Promise.withResolvers();
+ iwin.wrappedJSObject.timeoutFun = Cu.getIncumbentGlobal.bind(Cu, makeCallback(iwin, d, "cross-global setTimeout by |iwin|"));
+ iwin.eval('setTimeout(timeoutFun, 0);');
+ return d.promise;
+ }).then(function() {
+
+ // The incumbent script override doesn't take effect if the callback is scripted.
+ let d = Promise.withResolvers();
+ iwin.wrappedJSObject.timeoutFun2 = Cu.getIncumbentGlobal.bind(Cu, makeCallback(iwin, d, "cross-global setTimeout of scripted function"));
+ iwin.eval('var timeoutFun2Wrapper = function() { timeoutFun2(); }');
+ setTimeout(iwin.wrappedJSObject.timeoutFun2Wrapper, 0);
+ return d.promise;
+ }).then(function() {
+
+ // Try event listeners.
+ let d = Promise.withResolvers();
+ let body = iwin.document.body;
+ body.addEventListener('click', Cu.getIncumbentGlobal.bind(Cu, makeCallback(window, d, "cross-global event listener")));
+ body.dispatchEvent(new iwin.MouseEvent('click'));
+ return d.promise;
+
+ }).then(function() {
+
+ // Try an event handler set by |iwin|.
+ let d = Promise.withResolvers();
+ let body = iwin.document.body;
+ iwin.wrappedJSObject.handler = Cu.getIncumbentGlobal.bind(Cu, makeCallback(iwin, d, "cross-global event handler"));
+ iwin.eval('document.body.onmousemove = handler');
+ body.dispatchEvent(new iwin.MouseEvent('mousemove'));
+ return d.promise;
+
+ }).then(function() {
+
+ // Try an event handler compiled by a content attribute.
+ let d = Promise.withResolvers();
+ let body = iwin.document.body;
+ iwin.wrappedJSObject.innerHandler = Cu.getIncumbentGlobal.bind(Cu, makeCallback(iwin, d, "cross-global compiled event handler"));
+ iwin.eval("document.body.setAttribute('onmouseout', 'innerHandler()')");
+ body.dispatchEvent(new iwin.MouseEvent('mouseout'));
+ return d.promise;
+ }).then(function() {
+
+ SimpleTest.finish();
+ });
+ });
+
+ ]]>
+ </script>
+</window>
diff --git a/js/xpconnect/tests/chrome/test_scripterror.html b/js/xpconnect/tests/chrome/test_scripterror.html
new file mode 100644
index 0000000000..38cb316467
--- /dev/null
+++ b/js/xpconnect/tests/chrome/test_scripterror.html
@@ -0,0 +1,87 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Tests for nsIScriptError</title>
+<script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+<div id="log"></div>
+<script>
+function awaitScriptError(fun) {
+ // Use setTimeout in order to prevent throwing from test frame
+ // and to have a clean stack frame.
+ setTimeout(fun, 0)
+
+ return new Promise(resolve => {
+ const observer = {
+ QueryInterface: ChromeUtils.generateQI(["nsIObserver"]),
+ observe(message) {
+ if (!(message instanceof Ci.nsIScriptError)) {
+ return;
+ }
+
+ Services.console.unregisterListener(observer);
+ resolve(message);
+ }
+ };
+
+ Services.console.registerListener(observer);
+ });
+}
+
+function hasExpectedProperties(message, exception) {
+ is(message.hasException, true, "has exception");
+ is(message.exception, exception, "has correct exception");
+
+ ok(message.stack != null, "has stack");
+ is(message.stack?.source, location.href, "correct stack source")
+
+ is(message.sourceName, location.href, "has correct sourceName/filename");
+ ok(message.lineNumber > 0, "has lineNumber");
+
+ is(message.innerWindowID, window.windowGlobalChild.innerWindowId,
+ "correct innerWindowID");
+}
+
+add_task(async () => {
+ await SpecialPowers.pushPrefEnv({"set": [
+ ["javascript.options.asyncstack_capture_debuggee_only", false],
+ ]});
+
+ const TESTS = [
+ "abc",
+ new Error("foobar"),
+ {foo: "bar"}
+ ];
+
+ for (let test of TESTS) {
+ // First test as regular throw
+ SimpleTest.expectUncaughtException();
+ let message = await awaitScriptError(function testName() {
+ throw test;
+ });
+ hasExpectedProperties(message, test);
+ is(message.isPromiseRejection, false, "not a rejected promise");
+
+ // Now test as uncaught promise rejection
+ message = await awaitScriptError(function testName() {
+ Promise.reject(test);
+ });
+ hasExpectedProperties(message, test);
+ is(message.isPromiseRejection, true, "is a rejected promise");
+
+ // Uncaught rejection from async function
+ message = await awaitScriptError(async function testName() {
+ throw test;
+ });
+ hasExpectedProperties(message, test);
+ is(message.isPromiseRejection, true, "is a rejected promise");
+
+ // Uncaught rejection from then callback
+ message = await awaitScriptError(async function testName() {
+ Promise.resolve().then(() => {
+ throw test;
+ });
+ });
+ hasExpectedProperties(message, test);
+ is(message.isPromiseRejection, true, "is a rejected promise");
+ }
+});
+</script>
diff --git a/js/xpconnect/tests/chrome/test_secureContexts.html b/js/xpconnect/tests/chrome/test_secureContexts.html
new file mode 100644
index 0000000000..956e609d5f
--- /dev/null
+++ b/js/xpconnect/tests/chrome/test_secureContexts.html
@@ -0,0 +1,58 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1273687
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1430164</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://global/skin"/>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
+ <iframe id="t" src="https://example.com"></iframe>
+ <iframe id="i" src="http://example.com"></iframe>
+ <script type="application/javascript">
+ /** Test for Bug 1273687 **/
+ SimpleTest.waitForExplicitFinish();
+
+ window.onload = () => {
+ /* globals t, i */
+ runTest(t, true);
+ runTest(i, false);
+
+ // Check we can inherit the system principal
+ var s = new Cu.Sandbox(window, { wantGlobalProperties: ["isSecureContext"] } );
+ s.ok = ok;
+ Cu.evalInSandbox('ok(isSecureContext)', s);
+
+ // Check options insecure works
+ let sb = new Cu.Sandbox('https://www.example.com',
+ { forceSecureContext: false,
+ wantGlobalProperties:
+ ["isSecureContext"]
+ });
+ ok(!Cu.evalInSandbox('isSecureContext', sb));
+
+ // Check options secure works
+ let sb2 = new Cu.Sandbox('https://www.example.com',
+ { forceSecureContext: true,
+ wantGlobalProperties:
+ ["isSecureContext"]
+ });
+ ok(Cu.evalInSandbox('isSecureContext', sb2));
+ SimpleTest.finish();
+ };
+
+ // Check dom window inheritance works
+ function runTest(ifr, expectIsSecureContext) {
+ let frameWin = ifr.contentWindow;
+ let s = new Cu.Sandbox(frameWin, { wantGlobalProperties: ["isSecureContext"] });
+ is(expectIsSecureContext, Cu.evalInSandbox('isSecureContext', s));
+
+ // Ensure the implementation of 'wantGlobalProperties' matches the DOM window prototype
+ let s2 = new Cu.Sandbox(frameWin, { sandboxPrototype: frameWin });
+ is(expectIsSecureContext, Cu.evalInSandbox('isSecureContext', s2));
+ }
+ </script>
+</head>
+</html>
diff --git a/js/xpconnect/tests/chrome/test_sharedChromeCompartment.html b/js/xpconnect/tests/chrome/test_sharedChromeCompartment.html
new file mode 100644
index 0000000000..b653f19751
--- /dev/null
+++ b/js/xpconnect/tests/chrome/test_sharedChromeCompartment.html
@@ -0,0 +1,63 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1517694
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1517694</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://global/skin"/>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+"use strict";
+
+/** Test for Bug 1517694 **/
+const { XPCOMUtils } = ChromeUtils.importESModule(
+ "resource://gre/modules/XPCOMUtils.sys.mjs"
+);
+
+SimpleTest.waitForExplicitFinish();
+
+function go() {
+ var frame = $('subframe');
+ frame.onload = null;
+
+ var isSameCompartment = Cu.getJSTestingFunctions().isSameCompartment;
+
+ ok(isSameCompartment(window, frame.contentWindow),
+ "System window is in same compartment");
+
+ var sameCompSb = Cu.Sandbox(window);
+ ok(isSameCompartment(window, sameCompSb),
+ "System sandbox is in same compartment");
+
+ var ownCompSb = Cu.Sandbox(window, {freshCompartment: true});
+ ok(!isSameCompartment(window, ownCompSb),
+ "Sandbox created with freshCompartment is in separate compartment");
+
+ // Sandbox created in fresh-compartment sandbox must be in shared
+ // compartment.
+ var sb = Cu.evalInSandbox(`
+ let principal =
+ Cc["@mozilla.org/systemprincipal;1"].getService(Ci.nsIPrincipal);
+ Cu.Sandbox(principal)
+ `, ownCompSb);
+ ok(isSameCompartment(window, sb),
+ "System sandbox created in different compartment is in system compartment");
+
+ ok(isSameCompartment(window, XPCOMUtils),
+ "Object defined in JSM is in same compartment");
+
+ SimpleTest.finish();
+}
+
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1517694">Mozilla Bug 1517694</a>
+
+<iframe id="subframe" src="file_empty.html" onload="go()"></iframe>
+
+</body>
+</html>
diff --git a/js/xpconnect/tests/chrome/test_weakmap_keys_preserved.xhtml b/js/xpconnect/tests/chrome/test_weakmap_keys_preserved.xhtml
new file mode 100644
index 0000000000..c7c5d4369d
--- /dev/null
+++ b/js/xpconnect/tests/chrome/test_weakmap_keys_preserved.xhtml
@@ -0,0 +1,33 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=673468
+-->
+<window title="Mozilla Bug "
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a href="https://bugzilla.mozilla.org/show_bug.cgi?id="
+ target="_blank">Mozilla Bug 673468</a>
+ </body>
+
+ <!-- test code goes here -->
+ <script type="application/javascript">
+ <![CDATA[
+ /** Test for Bug 673468 **/
+
+ let system = Cc["@mozilla.org/systemprincipal;1"].createInstance();
+ let sandbox = Cu.Sandbox(system);
+ let map = new sandbox.WeakMap();
+ let obj = {};
+ map.set(obj, {});
+
+ Cu.forceGC();
+
+ ok(map.has(obj), "Weakmap still contains our wrapper!");
+ ]]>
+ </script>
+</window>
diff --git a/js/xpconnect/tests/chrome/test_weakmap_keys_preserved2.xhtml b/js/xpconnect/tests/chrome/test_weakmap_keys_preserved2.xhtml
new file mode 100644
index 0000000000..faaaa8b9ac
--- /dev/null
+++ b/js/xpconnect/tests/chrome/test_weakmap_keys_preserved2.xhtml
@@ -0,0 +1,80 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=673468
+-->
+<window title="Mozilla Bug "
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a id="testelem" href="https://bugzilla.mozilla.org/show_bug.cgi?id="
+ target="_blank">Mozilla Bug 673468</a>
+ </body>
+
+ <!-- test code goes here -->
+ <script type="application/javascript">
+ <![CDATA[
+ /** Test for Bug 673468 **/
+ SimpleTest.waitForExplicitFinish();
+ SpecialPowers.DOMWindowUtils.garbageCollect();
+
+ let get_live_dom = function () {
+ return document.getElementById("testelem");
+ };
+
+ let wrappers_as_keys_test = function () {
+ let e = new MessageEvent("foo", { bubbles: false, cancellable: false,
+ data: { dummy: document.createXULElement("foo") }});
+ window.eeeevent = e;
+
+ let live_dom = e.data.dummy;
+ let dead_dom = document.createElementNS("http://www.w3.org/1999/xhtml", "div");
+ let dead_child = document.createElementNS("http://www.w3.org/1999/xhtml", "div");
+ dead_dom.appendChild(dead_child);
+ is(dead_dom.children.length, 1, "children have wrong length");
+ let wrappee = {};
+
+ dead_dom.abcxyz = wrappee;
+
+ let system = Cc["@mozilla.org/systemprincipal;1"].createInstance();
+ let sandbox = Cu.Sandbox(system);
+
+ sandbox.wrapper = wrappee;
+ sandbox.value = dead_dom;
+ let map = Cu.evalInSandbox("wm = new WeakMap(); wm.set(wrapper, value); wm", sandbox);
+ sandbox.wrapper = null;
+ sandbox.value = null;
+
+ live_dom.xyzabc = {wrappee, m: map, sb: sandbox};
+
+ let key = ChromeUtils.nondeterministicGetWeakMapKeys(map)[0];
+ let value = map.get(key);
+ is(value.children.length, 1, "children have wrong length");
+ }
+
+ wrappers_as_keys_test();
+
+ let check_wrappers_as_keys = function () {
+ let live_dom = window.eeeevent.data.dummy;
+ let live_map = live_dom.xyzabc.m;
+ is(ChromeUtils.nondeterministicGetWeakMapKeys(live_map).length, 1,
+ "Map should not be empty.");
+ let key = ChromeUtils.nondeterministicGetWeakMapKeys(live_map)[0];
+ let value = live_map.get(key);
+ is(value.children.length, 1, "children have wrong length");
+ }
+
+ Cu.schedulePreciseGC(function () {
+ SpecialPowers.DOMWindowUtils.cycleCollect();
+ SpecialPowers.DOMWindowUtils.garbageCollect();
+
+ check_wrappers_as_keys();
+ SimpleTest.finish();
+ });
+
+ ]]>
+ </script>
+</window>
diff --git a/js/xpconnect/tests/chrome/test_weakref.xhtml b/js/xpconnect/tests/chrome/test_weakref.xhtml
new file mode 100644
index 0000000000..21986cbf34
--- /dev/null
+++ b/js/xpconnect/tests/chrome/test_weakref.xhtml
@@ -0,0 +1,32 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=484459
+-->
+<window title="Weakrefs"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <iframe type="content" id="ifr">
+ </iframe>
+ </body>
+
+ <!-- test code goes here -->
+ <script type="application/javascript"><![CDATA[
+ SimpleTest.waitForExplicitFinish();
+ var util = window.windowUtils;
+ var weakUtil = Cu.getWeakReference(util);
+ util = null;
+
+ function callback() {
+ ok(weakUtil.get(), "Should still be alive here");
+ SimpleTest.finish();
+ }
+
+ SpecialPowers.exactGC(callback);
+ ]]></script>
+</window>
diff --git a/js/xpconnect/tests/chrome/test_windowProxyDeadWrapper.html b/js/xpconnect/tests/chrome/test_windowProxyDeadWrapper.html
new file mode 100644
index 0000000000..a6f0ac95be
--- /dev/null
+++ b/js/xpconnect/tests/chrome/test_windowProxyDeadWrapper.html
@@ -0,0 +1,78 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1223372
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1223372</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://global/skin"/>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+/** Test for Bug 1223372 **/
+const {TestUtils} = ChromeUtils.importESModule(
+ "resource://testing-common/TestUtils.sys.mjs"
+);
+
+async function go() {
+ SimpleTest.waitForExplicitFinish();
+ await SpecialPowers.pushPrefEnv({"set": [["security.allow_eval_with_system_principal",
+ true]]});
+
+ var frame = $('subframe');
+ frame.onload = null;
+
+ var w = frame.contentWindow;
+
+ w.eval("checkDead = function() { return Components.utils.isDeadWrapper(this); };");
+ var checkDead = w.checkDead;
+
+ w.eval("getObject = function() { return {}; }");
+ var getObject = w.getObject;
+
+ ok(!checkDead(), "WindowProxy shouldn't be dead yet");
+
+ // Load a content page in the chrome frame.
+ w.location = "https://example.org/tests/js/xpconnect/tests/mochitest/file_empty.html";
+ tryWindow();
+
+ function tryWindow() {
+ if (w.document.title != 'empty test page') {
+ info("Document not loaded yet - retrying");
+ SimpleTest.executeSoon(tryWindow);
+ return;
+ }
+
+ let winID = w.docShell.outerWindowID;
+ // Remove the frame. This will nuke the WindowProxy wrapper from our chrome
+ // document's global, so evaluating 'this' in it will return a dead wrapper
+ // once the window is destroyed.
+ frame.remove();
+
+ TestUtils.topicObserved("outer-window-nuked", (subject, data) => {
+ let id = subject.QueryInterface(Ci.nsISupportsPRUint64).data;
+ return id == winID;
+ }).then(() => {
+ ok(checkDead(), "Expected a dead object wrapper");
+
+ // Wrapping the Window should return a dead wrapper now.
+ var global = Cu.getGlobalForObject(getObject());
+ ok(Cu.isDeadWrapper(global),
+ "Expected a dead wrapper when requesting the window's global");
+
+ SimpleTest.finish();
+ });
+ }
+}
+
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1223372">Mozilla Bug 1223372</a>
+
+<iframe id="subframe" src="./file_empty.html" onload="go()"></iframe>
+
+</body>
+</html>
diff --git a/js/xpconnect/tests/chrome/test_wrappers.xhtml b/js/xpconnect/tests/chrome/test_wrappers.xhtml
new file mode 100644
index 0000000000..73c808a9df
--- /dev/null
+++ b/js/xpconnect/tests/chrome/test_wrappers.xhtml
@@ -0,0 +1,85 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=500931
+-->
+<window title="Mozilla Bug 500931"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=533596"
+ target="_blank">Mozilla Bug 533596</a>
+ </body>
+
+ <!-- test code goes here -->
+ <script type="application/javascript"><![CDATA[
+
+ /** Test for Bug 533596 **/
+
+ function go() {
+ var win = $('ifr').contentWindow;
+ var utils = window.windowUtils;
+ is(utils.getClassName(window), "Proxy", "our window is wrapped correctly")
+ is(utils.getClassName(location), "Location", "chrome doesn't have location wrappers")
+ is(utils.getClassName(win), "Proxy", "win is an Proxy");
+ is(utils.getClassName(win.location), "Proxy", "deep wrapping works");
+ is(win.location.href, "https://example.org/tests/js/xpconnect/tests/mochitest/chrome_wrappers_helper.html",
+ "can still get strings out");
+
+ var unsafeWin = win.wrappedJSObject;
+ is(utils.getClassName(unsafeWin), "Proxy", "can get a Proxy");
+ is(utils.getClassName(unsafeWin.location), "Proxy", "deep wrapping works");
+
+ Object.defineProperty(unsafeWin, "defprop1", { value: 1, writable: true, enumerable: true, configurable: true });
+ /* TODO (bug 552854): the getter isn't visible in content.
+ function checkWrapper(val) {
+ ok(utils.getClassName(val) == "Proxy", "wrapped properly");
+ }
+ Object.defineProperty(unsafeWin, "defprop2", { set: checkWrapper, enumerable: true, configurable: true });
+ */
+ unsafeWin.run_test(ok, win, unsafeWin);
+
+ win.setTimeout(function() {
+ is(utils.getClassName(this), "Proxy",
+ "this is wrapped correctly");
+ SimpleTest.finish();
+ }, 0)
+
+ var saw0 = false;
+ for (let i in $('ifr').contentDocument.getElementsByTagName('body')) {
+ if (i === "0")
+ saw0 = true;
+ }
+ ok(saw0, "properly enumerated the 0 value");
+
+ ok(win.XPathEvaluator.toString().includes("XPathEvaluator"),
+ "Can access content window.XPathEvaluator");
+
+ var nativeToString =
+ ("" + Math.sin).replace("sin", "EventTarget");
+ var eventTargetToString = "" + win.EventTarget;
+ ok(eventTargetToString.indexOf(nativeToString) > -1,
+ "Stringifying a DOM interface object should return the same string as " +
+ "stringifying a native function. " + eventTargetToString + " " + nativeToString);
+
+ is(win.XPathResult.NUMBER_TYPE, 1, "can access constants on constructors");
+ is(typeof win.IDBKeyRange.bound, "function", "can access crazy IDBKeyRange static functions");
+
+ // Test getter/setter lookup on Xray wrappers.
+ ok(Object.prototype.__lookupGetter__.call(win.document, 'title'), 'found getter on document');
+ ok(Object.prototype.__lookupGetter__.call(win.document, 'title'), 'found getter on document');
+ }
+
+ SimpleTest.waitForExplicitFinish();
+
+ ]]></script>
+ <iframe type="content"
+ src="https://example.org/tests/js/xpconnect/tests/mochitest/chrome_wrappers_helper.html"
+ onload="go()"
+ id="ifr">
+ </iframe>
+</window>
diff --git a/js/xpconnect/tests/chrome/test_xrayLargeTypedArray.html b/js/xpconnect/tests/chrome/test_xrayLargeTypedArray.html
new file mode 100644
index 0000000000..dcb0b87380
--- /dev/null
+++ b/js/xpconnect/tests/chrome/test_xrayLargeTypedArray.html
@@ -0,0 +1,47 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1674777
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1674777</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://global/skin"/>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+/** Test for Bug 1674777 **/
+
+function go() {
+ SimpleTest.waitForExplicitFinish();
+
+ let win = document.getElementById('ifr').contentWindow;
+
+ const nbytes = 3 * 1024 * 1024 * 1024; // 3 GB.
+ let tarray = new win.Int8Array(nbytes);
+ ok(Cu.isXrayWrapper(tarray), "Should be Xray");
+ is(tarray.length, nbytes, "Length should match");
+ is(tarray.byteLength, nbytes, "byteLength should match");
+
+ // Expect OOM when getting all property names.
+ let ex;
+ try {
+ Object.getOwnPropertyNames(tarray);
+ } catch (e) {
+ ex = e;
+ }
+ is(ex, "out of memory", "Expected OOM");
+
+ SimpleTest.finish();
+}
+
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1674777">Mozilla Bug 1674777</a>
+
+<iframe id="ifr" onload="go();" src="http://example.org/tests/js/xpconnect/tests/mochitest/file_empty.html" />
+
+</body>
+</html>
diff --git a/js/xpconnect/tests/chrome/test_xrayToJS.xhtml b/js/xpconnect/tests/chrome/test_xrayToJS.xhtml
new file mode 100644
index 0000000000..cc009a2d55
--- /dev/null
+++ b/js/xpconnect/tests/chrome/test_xrayToJS.xhtml
@@ -0,0 +1,1200 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=933681
+-->
+<window title="Mozilla Bug 933681"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=933681"
+ target="_blank">Mozilla Bug 933681</a>
+ </body>
+
+ <!-- test code goes here -->
+ <script type="application/javascript">
+ <![CDATA[
+
+ /** Test for ES constructors on Xrayed globals. **/
+ SimpleTest.waitForExplicitFinish();
+ let global = Cu.getGlobalForObject.bind(Cu);
+
+ function checkThrows(f, rgxp, msg) {
+ try {
+ f();
+ ok(false, "Should have thrown: " + msg);
+ } catch (e) {
+ ok(true, "Threw as expected: " + msg);
+ ok(rgxp.test(e), "Message correct: " + e);
+ }
+ }
+
+ var { AppConstants } = SpecialPowers.ChromeUtils.importESModule(
+ "resource://gre/modules/AppConstants.sys.mjs"
+ );
+ var isNightlyBuild = AppConstants.NIGHTLY_BUILD;
+ var isReleaseOrBeta = AppConstants.RELEASE_OR_BETA;
+
+ let typedArrayClasses = ['Uint8Array', 'Int8Array', 'Uint16Array', 'Int16Array',
+ 'Uint32Array', 'Int32Array', 'Float32Array', 'Float64Array',
+ 'Uint8ClampedArray'];
+ let errorObjectClasses = ['Error', 'InternalError', 'EvalError', 'RangeError', 'ReferenceError',
+ 'SyntaxError', 'TypeError', 'URIError', 'AggregateError'];
+
+ // A simpleConstructors entry can either be the name of a constructor as a
+ // string, or an object containing the properties `name`, and `args`.
+ // In the former case, the constructor is invoked without any args; in the
+ // latter case, it is invoked with `args` as the arguments list.
+ let simpleConstructors = ['Object', 'Function', 'Array', 'Boolean', 'Date', 'Number',
+ 'String', 'RegExp', 'ArrayBuffer', 'WeakMap', 'WeakSet', 'Map', 'Set',
+ {name: 'Promise', args: [function(){}]}];
+
+ // All TypedArray constructors can be called with zero arguments.
+ simpleConstructors = simpleConstructors.concat(typedArrayClasses);
+
+ // All Error constructors except AggregateError can be called with zero arguments.
+ simpleConstructors = simpleConstructors.concat(errorObjectClasses.filter(name => {
+ return name !== 'AggregateError';
+ }));
+
+ function go() {
+ window.iwin = document.getElementById('ifr').contentWindow;
+ /* global iwin */
+
+ // Test constructors that can be instantiated with zero arguments, or with
+ // a fixed set of arguments provided using `...rest`.
+ for (let c of simpleConstructors) {
+ var args = [];
+ if (typeof c === 'object') {
+ args = c.args;
+ c = c.name;
+ }
+ ok(iwin[c], "Constructors appear: " + c);
+ is(iwin[c], Cu.unwaiveXrays(iwin.wrappedJSObject[c]),
+ "we end up with the appropriate constructor: " + c);
+ is(Cu.unwaiveXrays(Cu.waiveXrays(new iwin[c](...args)).constructor), iwin[c],
+ "constructor property is set up right: " + c);
+ let expectedProto = Cu.isOpaqueWrapper(new iwin[c](...args)) ?
+ iwin.Object.prototype : iwin[c].prototype;
+ is(Object.getPrototypeOf(new iwin[c](...args)), expectedProto,
+ "prototype is correct: " + c);
+ is(global(new iwin[c](...args)), iwin, "Got the right global: " + c);
+ }
+
+ // Test Object in more detail.
+ var num = new iwin.Object(4);
+ is(Cu.waiveXrays(num).valueOf(), 4, "primitive object construction works");
+ is(global(num), iwin, "correct global for num");
+ var obj = new iwin.Object();
+ obj.foo = 2;
+ var withProto = Cu.unwaiveXrays(Cu.waiveXrays(iwin).Object.create(obj));
+ is(global(withProto), iwin, "correct global for withProto");
+ is(Cu.waiveXrays(withProto).foo, 2, "Inherits properly");
+
+ // Test Function.
+ var primitiveFun = new iwin.Function('return 2');
+ is(global(primitiveFun), iwin, "function construction works");
+ is(primitiveFun(), 2, "basic function works");
+ var doSetFoo = new iwin.Function('arg', 'arg.foo = 2;');
+ is(global(doSetFoo), iwin, "function with args works");
+ try {
+ doSetFoo({});
+ ok(false, "should have thrown while setting property on object");
+ } catch (e) {
+ ok(!!/denied/.test(e), "Threw correctly: " + e);
+ }
+ var factoryFun = new iwin.Function('return {foo: 32}');
+ is(global(factoryFun), iwin, "proper global for factoryFun");
+ is(factoryFun().foo, 32, "factoryFun invokable");
+ is(global(factoryFun()), iwin, "minted objects live in the content scope");
+ testXray('Function', factoryFun, new iwin.Function(), ['length', 'name']);
+ var echoThis = new iwin.Function('return this;');
+ echoThis.wrappedJSObject.bind = 42;
+ var boundEchoThis = echoThis.bind(document);
+ is(boundEchoThis(), document, "bind() works correctly over Xrays");
+ is(global(boundEchoThis), window, "bound functions live in the caller's scope");
+ ok(/return this/.test(echoThis.toSource()), 'toSource works: ' + echoThis.toSource());
+ ok(/return this/.test(echoThis.toString()), 'toString works: ' + echoThis.toString());
+ is(iwin.Function.prototype, Object.getPrototypeOf(echoThis), "Function.prototype works for standard classes");
+ is(echoThis.prototype, undefined, "Function.prototype not visible for non standard constructors");
+ iwin.eval('var foopyFunction = function namedFoopyFunction(a, b, c) {}');
+ var foopyFunction = Cu.unwaiveXrays(Cu.waiveXrays(iwin).foopyFunction);
+ ok(Cu.isXrayWrapper(foopyFunction), "Should be Xrays");
+ is(foopyFunction.name, "namedFoopyFunction", ".name works over Xrays");
+ is(foopyFunction.length, 3, ".length works over Xrays");
+ ok(Object.getOwnPropertyNames(foopyFunction).includes('length'), "Should list length");
+ ok(Object.getOwnPropertyNames(foopyFunction).includes('name'), "Should list name");
+ ok(!Object.getOwnPropertyNames(foopyFunction).includes('prototype'), "Should not list prototype");
+ ok(Object.getOwnPropertyNames(iwin.Array).includes('prototype'), "Should list prototype for standard constructor");
+
+ // Test BoundFunction.
+ iwin.eval('var boundFoopyFunction = foopyFunction.bind(null, 1)');
+ var boundFoopyFunction = Cu.unwaiveXrays(Cu.waiveXrays(iwin).boundFoopyFunction);
+ is(boundFoopyFunction.name, "bound namedFoopyFunction", "bound .name works over Xrays");
+ is(boundFoopyFunction.length, 2, "bound .length works over Xrays");
+ is(JSON.stringify(Object.getOwnPropertyNames(boundFoopyFunction).sort()), '["length","name"]', "Should list length and name");
+ // Mutate .name, it's just a data property.
+ iwin.eval('Object.defineProperty(boundFoopyFunction, "name", {value: "foobar", configurable: true, writable: true});');
+ is(boundFoopyFunction.name, "foobar", "mutated .name works over Xrays");
+ iwin.eval('boundFoopyFunction.name = 123;');
+ is(boundFoopyFunction.name, undefined, "only support string for .name");
+ iwin.eval('delete boundFoopyFunction.name');
+ is(boundFoopyFunction.name, undefined, "deleted .name works over Xrays");
+ // Mutate .length.
+ iwin.eval('Object.defineProperty(boundFoopyFunction, "length", {value: 456, configurable: true, writable: true});');
+ is(boundFoopyFunction.length, 456, "mutated .length works over Xrays");
+ iwin.eval('boundFoopyFunction.length = "bar";');
+ is(boundFoopyFunction.length, undefined, "only support number for .length");
+
+ // Test proxies.
+ var targetObject = new iwin.Object();
+ targetObject.foo = 9;
+ var forwardingProxy = new iwin.Proxy(targetObject, new iwin.Object());
+ is(global(forwardingProxy), iwin, "proxy global correct");
+ is(Cu.waiveXrays(forwardingProxy).foo, 9, "forwards correctly");
+
+ // Test AggregateError.
+ {
+ // AggregateError throws when called without an iterable object as its first argument.
+ let args = [new iwin.Array()];
+
+ ok(iwin.AggregateError, "AggregateError constructor is present");
+ is(iwin.AggregateError, Cu.unwaiveXrays(iwin.wrappedJSObject.AggregateError),
+ "we end up with the appropriate AggregateError constructor");
+ is(Cu.unwaiveXrays(Cu.waiveXrays(new iwin.AggregateError(...args)).constructor), iwin.AggregateError,
+ "AggregateError constructor property is set up right");
+ let expectedProto = Cu.isOpaqueWrapper(new iwin.AggregateError(...args)) ?
+ iwin.Object.prototype : iwin.AggregateError.prototype;
+ is(Object.getPrototypeOf(new iwin.AggregateError(...args)), expectedProto,
+ "AggregateError prototype is correct");
+ is(global(new iwin.AggregateError(...args)), iwin, "Got the right global for AggregateError");
+ }
+
+ // Test eval.
+ var toEval = "({a: 2, b: {foo: 'bar'}, f: function() { return window; }})";
+ is(global(iwin.eval(toEval)), iwin, "eval creates objects in the correct global");
+ is(iwin.eval(toEval).b.foo, 'bar', "eval-ed object looks right");
+ is(Cu.waiveXrays(iwin.eval(toEval)).f(), Cu.waiveXrays(iwin), "evaled function works right");
+
+ testDate();
+
+ testObject();
+
+ testArray();
+
+ testTypedArrays();
+
+ testErrorObjects();
+
+ testRegExp();
+
+ testPromise();
+
+ testArrayBuffer();
+
+ testMap();
+
+ testSet();
+
+ testWeakMap();
+
+ testWeakSet();
+
+ testProxy();
+
+ testDataView();
+
+ testNumber();
+
+ SimpleTest.finish();
+ }
+
+ // Maintain a static list of the properties that are available on each standard
+ // prototype, so that we make sure to audit any new ones to make sure they're
+ // Xray-safe.
+ //
+ // DO NOT CHANGE WTIHOUT REVIEW FROM AN XPCONNECT PEER.
+ var gPrototypeProperties = {};
+ var gConstructorProperties = {};
+ // Properties which cannot be invoked if callable without potentially
+ // rendering the object useless.
+ var gStatefulProperties = {};
+ function constructorProps(arr) {
+ // Some props live on all constructors
+ return arr.concat(["prototype", "length", "name"]);
+ }
+ gPrototypeProperties.Date =
+ ["getTime", "getTimezoneOffset", "getYear", "getFullYear", "getUTCFullYear",
+ "getMonth", "getUTCMonth", "getDate", "getUTCDate", "getDay", "getUTCDay",
+ "getHours", "getUTCHours", "getMinutes", "getUTCMinutes", "getSeconds",
+ "getUTCSeconds", "getMilliseconds", "getUTCMilliseconds", "setTime",
+ "setYear", "setFullYear", "setUTCFullYear", "setMonth", "setUTCMonth",
+ "setDate", "setUTCDate", "setHours", "setUTCHours", "setMinutes",
+ "setUTCMinutes", "setSeconds", "setUTCSeconds", "setMilliseconds",
+ "setUTCMilliseconds", "toUTCString", "toLocaleString",
+ "toLocaleDateString", "toLocaleTimeString", "toDateString", "toTimeString",
+ "toISOString", "toJSON", "toSource", "toString", "valueOf", "constructor",
+ "toGMTString", Symbol.toPrimitive];
+ gConstructorProperties.Date = constructorProps(["UTC", "parse", "now"]);
+ gPrototypeProperties.Object =
+ ["constructor", "toSource", "toString", "toLocaleString", "valueOf",
+ "hasOwnProperty", "isPrototypeOf", "propertyIsEnumerable",
+ "__defineGetter__", "__defineSetter__", "__lookupGetter__", "__lookupSetter__",
+ "__proto__"];
+ gConstructorProperties.Object =
+ constructorProps(["setPrototypeOf", "getOwnPropertyDescriptor", "getOwnPropertyDescriptors",
+ "keys", "is", "defineProperty", "defineProperties", "create",
+ "getOwnPropertyNames", "getOwnPropertySymbols",
+ "preventExtensions", "freeze", "fromEntries", "isFrozen", "seal",
+ "isSealed", "assign", "getPrototypeOf", "values",
+ "entries", "isExtensible", "hasOwn", "groupBy"]);
+ gPrototypeProperties.Array =
+ ["length", "toSource", "toString", "toLocaleString", "join", "reverse", "sort", "push",
+ "pop", "shift", "unshift", "splice", "concat", "slice", "lastIndexOf", "indexOf",
+ "includes", "forEach", "map", "reduce", "reduceRight", "filter", "some", "every", "find",
+ "findIndex", "copyWithin", "fill", Symbol.iterator, Symbol.unscopables, "entries", "keys",
+ "values", "constructor", "flat", "flatMap", "at", "findLast", "findLastIndex",
+ "toReversed", "toSorted", "toSpliced", "with"];
+ gConstructorProperties.Array =
+ constructorProps(["isArray", "from", "fromAsync", "of", Symbol.species]);
+ for (let c of typedArrayClasses) {
+ gPrototypeProperties[c] = ["constructor", "BYTES_PER_ELEMENT"];
+ gConstructorProperties[c] = constructorProps(["BYTES_PER_ELEMENT"]);
+ }
+ // There is no TypedArray constructor, looks like.
+ is(window.TypedArray, undefined, "If this ever changes, add to this test!");
+ for (let c of errorObjectClasses) {
+ gPrototypeProperties[c] = ["constructor", "name", "message", "stack"];
+ gConstructorProperties[c] = constructorProps([]);
+ }
+ // toString and toSource only live on the parent proto (Error.prototype).
+ gPrototypeProperties.Error.push('toString');
+ gPrototypeProperties.Error.push('toSource');
+
+ gPrototypeProperties.Function =
+ ["constructor", "toSource", "toString", "apply", "call", "bind",
+ "length", "name", "arguments", "caller", Symbol.hasInstance];
+ gConstructorProperties.Function = constructorProps([])
+
+ gPrototypeProperties.RegExp =
+ ["constructor", "toSource", "toString", "compile", "exec", "test",
+ Symbol.match, Symbol.matchAll, Symbol.replace, Symbol.search, Symbol.split,
+ "flags", "dotAll", "global", "hasIndices", "ignoreCase", "multiline", "source", "sticky",
+ "unicode", "unicodeSets"];
+ gConstructorProperties.RegExp =
+ constructorProps(["input", "lastMatch", "lastParen",
+ "leftContext", "rightContext", "$1", "$2", "$3", "$4",
+ "$5", "$6", "$7", "$8", "$9", "$_", "$&", "$+",
+ "$`", "$'", Symbol.species])
+
+ gPrototypeProperties.Promise =
+ ["constructor", "catch", "then", "finally", Symbol.toStringTag];
+ gConstructorProperties.Promise =
+ constructorProps(["resolve", "reject", "all", "allSettled", "any", "race",
+ "withResolvers", Symbol.species]);
+
+ gPrototypeProperties.ArrayBuffer =
+ ["constructor", "byteLength", "detached", "slice", Symbol.toStringTag, "transfer", "transferToFixedLength"];
+ gConstructorProperties.ArrayBuffer =
+ constructorProps(["isView", Symbol.species]);
+ gStatefulProperties.ArrayBuffer = ["transfer", "transferToFixedLength"]
+
+ gPrototypeProperties.SharedArrayBuffer = ["constructor", "slice", "byteLength", "detached", Symbol.toStringTag, "transfer", "transferToFixedLength"];
+ gConstructorProperties.SharedArrayBuffer = constructorProps([Symbol.species]);
+ gStatefulProperties.SharedArrayBuffer = ["transfer", "transferToFixedLength"]
+
+ gPrototypeProperties.Map =
+ ["constructor", "size", Symbol.toStringTag, "get", "has", "set", "delete",
+ "keys", "values", "clear", "forEach", "entries", Symbol.iterator];
+ gConstructorProperties.Map =
+ constructorProps(["groupBy", Symbol.species]);
+
+ gPrototypeProperties.Set =
+ ["constructor", "size", Symbol.toStringTag, "has", "add", "delete",
+ "keys", "values", "clear", "forEach", "entries", Symbol.iterator];
+ gConstructorProperties.Set =
+ constructorProps([Symbol.species]);
+
+ gPrototypeProperties.WeakMap =
+ ["constructor", Symbol.toStringTag, "get", "has", "set", "delete"];
+ gConstructorProperties.WeakMap =
+ constructorProps([]);
+
+ gPrototypeProperties.WeakSet =
+ ["constructor", Symbol.toStringTag, "has", "add", "delete"];
+ gConstructorProperties.WeakSet =
+ constructorProps([]);
+
+ gPrototypeProperties.DataView =
+ ["constructor", "buffer", "byteLength", "byteOffset", Symbol.toStringTag,
+ "getInt8", "getUint8", "getInt16", "getUint16",
+ "getInt32", "getUint32", "getFloat32", "getFloat64",
+ "setInt8", "setUint8", "setInt16", "setUint16",
+ "setInt32", "setUint32", "setFloat32", "setFloat64",
+ "getBigInt64", "getBigUint64", "setBigInt64", "setBigUint64"]
+ gConstructorProperties.DataView = constructorProps([]);
+
+ // Sort an array that may contain symbols as well as strings.
+ function sortProperties(arr) {
+ function sortKey(prop) {
+ return typeof prop + ":" + prop.toString();
+ }
+ arr.sort((a, b) => sortKey(a) < sortKey(b) ? -1 : +1);
+ }
+
+ // Sort all the lists so we don't need to mutate them later (or copy them
+ // again to sort them).
+ for (let c of Object.keys(gPrototypeProperties))
+ sortProperties(gPrototypeProperties[c]);
+ for (let c of Object.keys(gConstructorProperties))
+ sortProperties(gConstructorProperties[c]);
+
+ function filterOut(array, props) {
+ return array.filter(p => !props.includes(p));
+ }
+
+ function isTypedArrayClass(classname) {
+ return typedArrayClasses.includes(classname);
+ }
+
+ function propertyIsGetter(obj, name, classname) {
+ return !!Object.getOwnPropertyDescriptor(obj, name).get;
+ }
+
+ function testProtoCallables(protoCallables, xray, xrayProto, localProto, callablesExcluded) {
+ // Handle undefined callablesExcluded.
+ let dontCall = callablesExcluded ?? [];
+ for (let name of protoCallables) {
+ info("Running tests for property: " + name);
+ // Test both methods and getter properties.
+ function lookupCallable(obj) {
+ let desc = null;
+ do {
+ desc = Object.getOwnPropertyDescriptor(obj, name);
+ if (desc) {
+ break;
+ }
+ obj = Object.getPrototypeOf(obj);
+ } while (obj);
+ return desc ? (desc.get || desc.value) : undefined;
+ };
+ ok(xrayProto.hasOwnProperty(name), `proto should have the property '${name}' as own`);
+ ok(!xray.hasOwnProperty(name), `instance should not have the property '${name}' as own`);
+ let method = lookupCallable(xrayProto);
+ is(typeof method, 'function', "Methods from Xrays are functions");
+ is(global(method), window, "Methods from Xrays are local");
+ ok(method instanceof Function, "instanceof works on methods from Xrays");
+ is(lookupCallable(xrayProto), method, "Holder caching works properly");
+ is(lookupCallable(xray), method, "Proto props resolve on the instance");
+ let local = lookupCallable(localProto);
+ is(method.length, local.length, "Function.length identical");
+ if (!method.length && !dontCall.includes(name)) {
+ is(method.call(xray) + "", local.call(xray) + "",
+ "Xray and local method results stringify identically");
+
+ // If invoking this method returns something non-Xrayable (opaque), the
+ // stringification is going to return [object Object].
+ // This happens for set[@@iterator] and other Iterator objects.
+ let callable = lookupCallable(xray.wrappedJSObject);
+ if (!Cu.isOpaqueWrapper(method.call(xray)) && callable) {
+ is(method.call(xray) + "",
+ callable.call(xray.wrappedJSObject) + "",
+ "Xray and waived method results stringify identically");
+ }
+ }
+ }
+ }
+
+ function testCtorCallables(ctorCallables, xrayCtor, localCtor) {
+ for (let name of ctorCallables) {
+ // Don't try to test Function.prototype, since that is in fact a callable
+ // but doesn't really do the things we expect callables to do here
+ // (e.g. it's in the wrong global, since it gets Xrayed itself).
+ if (name == "prototype" && localCtor.name == "Function") {
+ continue;
+ }
+ info(`Running tests for property: ${localCtor.name}.${name}`);
+ // Test both methods and getter properties.
+ function lookupCallable(obj) {
+ let desc = null;
+ do {
+ desc = Object.getOwnPropertyDescriptor(obj, name);
+ obj = Object.getPrototypeOf(obj);
+ } while (!desc);
+ return desc.get || desc.value;
+ };
+
+ ok(xrayCtor.hasOwnProperty(name), "ctor should have the property as own");
+ let method = lookupCallable(xrayCtor);
+ is(typeof method, 'function', "Methods from ctor Xrays are functions");
+ is(global(method), window, "Methods from ctor Xrays are local");
+ ok(method instanceof Function,
+ "instanceof works on methods from ctor Xrays");
+ is(lookupCallable(xrayCtor), method,
+ "Holder caching works properly on ctors");
+ let local = lookupCallable(localCtor);
+ is(method.length, local.length,
+ "Function.length identical for method from ctor");
+ // Don't try to do the return-value check on Date.now(), since there is
+ // absolutely no reason it should return the same value each time.
+ //
+ // Also don't try to do the return-value check on Regexp.lastMatch and
+ // Regexp["$&"] (which are aliases), because they get state off the global
+ // they live in, as far as I can tell, so testing them over Xrays will be
+ // wrong: on the Xray they will actaully get the lastMatch of _our_
+ // global, not the Xrayed one.
+ if (!method.length &&
+ !(localCtor.name == "Date" && name == "now") &&
+ !(localCtor.name == "RegExp" && (name == "lastMatch" || name == "$&"))) {
+ is(method.call(xrayCtor) + "", local.call(xrayCtor) + "",
+ "Xray and local method results stringify identically on constructors");
+ is(method.call(xrayCtor) + "",
+ lookupCallable(xrayCtor.wrappedJSObject).call(xrayCtor.wrappedJSObject) + "",
+ "Xray and waived method results stringify identically");
+ }
+ }
+ }
+
+ function testXray(classname, xray, xray2, propsToSkip, ctorPropsToSkip = []) {
+ propsToSkip = propsToSkip || [];
+ let xrayProto = Object.getPrototypeOf(xray);
+ let localProto = window[classname].prototype;
+ let desiredProtoProps = Object.getOwnPropertyNames(localProto).sort();
+
+ is(desiredProtoProps.toSource(),
+ gPrototypeProperties[classname].filter(id => typeof id === "string").toSource(),
+ "A property on the " + classname +
+ " prototype has changed! You need a security audit from an XPConnect peer");
+ is(Object.getOwnPropertySymbols(localProto).map(uneval).sort().toSource(),
+ gPrototypeProperties[classname].filter(id => typeof id !== "string").map(uneval).sort().toSource(),
+ "A symbol-keyed property on the " + classname +
+ " prototype has been changed! You need a security audit from an XPConnect peer");
+
+ let protoProps = filterOut(desiredProtoProps, propsToSkip);
+ let protoCallables = protoProps.filter(name => propertyIsGetter(localProto, name, classname) ||
+ typeof localProto[name] == 'function' &&
+ name != 'constructor');
+ let callablesExcluded = gStatefulProperties[classname];
+ ok(!!protoCallables.length, "Need something to test");
+ is(xrayProto, iwin[classname].prototype, "Xray proto is correct");
+ is(xrayProto, xray.__proto__, "Proto accessors agree");
+ var protoProto = classname == "Object" ? null : iwin.Object.prototype;
+ is(Object.getPrototypeOf(xrayProto), protoProto, "proto proto is correct");
+ testProtoCallables(protoCallables, xray, xrayProto, localProto, callablesExcluded);
+ is(Object.getOwnPropertyNames(xrayProto).sort().toSource(),
+ protoProps.toSource(), "getOwnPropertyNames works");
+ is(Object.getOwnPropertySymbols(xrayProto).map(uneval).sort().toSource(),
+ gPrototypeProperties[classname].filter(id => typeof id !== "string" && !propsToSkip.includes(id))
+ .map(uneval).sort().toSource(),
+ "getOwnPropertySymbols works");
+
+ is(xrayProto.constructor, iwin[classname], "constructor property works");
+
+ xrayProto.expando = 42;
+ is(xray.expando, 42, "Xrayed instances see proto expandos");
+ is(xray2.expando, 42, "Xrayed instances see proto expandos");
+
+ // Now test constructors
+ let localCtor = window[classname];
+ let xrayCtor = xrayProto.constructor;
+ // We already checked that this is the same as iwin[classname]
+
+ let desiredCtorProps =
+ Object.getOwnPropertyNames(localCtor).sort();
+ is(desiredCtorProps.toSource(),
+ gConstructorProperties[classname].filter(id => typeof id === "string").toSource(),
+ "A property on the " + classname +
+ " constructor has changed! You need a security audit from an XPConnect peer");
+ let desiredCtorSymbols =
+ Object.getOwnPropertySymbols(localCtor).map(uneval).sort()
+ is(desiredCtorSymbols.toSource(),
+ gConstructorProperties[classname].filter(id => typeof id !== "string").map(uneval).sort().toSource(),
+ "A symbol-keyed property on the " + classname +
+ " constructor has been changed! You need a security audit from an XPConnect peer");
+
+ let ctorProps = filterOut(desiredCtorProps, ctorPropsToSkip);
+ let ctorSymbols = filterOut(desiredCtorSymbols, ctorPropsToSkip.map(uneval));
+ let ctorCallables = ctorProps.filter(name => propertyIsGetter(localCtor, name, classname) ||
+ typeof localCtor[name] == 'function');
+ testCtorCallables(ctorCallables, xrayCtor, localCtor);
+ is(Object.getOwnPropertyNames(xrayCtor).sort().toSource(),
+ ctorProps.toSource(), "getOwnPropertyNames works on Xrayed ctors");
+ is(Object.getOwnPropertySymbols(xrayCtor).map(uneval).sort().toSource(),
+ ctorSymbols.toSource(), "getOwnPropertySymbols works on Xrayed ctors");
+ }
+
+ // We will need arraysEqual and testArrayIterators both in this global scope
+ // and in sandboxes.
+ function arraysEqual(arr1, arr2, reason) {
+ is(arr1.length, arr2.length, `${reason}; lengths should be equal`)
+ for (var i = 0; i < arr1.length; ++i) {
+ if (Array.isArray(arr2[i])) {
+ arraysEqual(arr1[i], arr2[i], `${reason}; item at index ${i}`);
+ } else {
+ is(arr1[i], arr2[i], `${reason}; item at index ${i} should be equal`);
+ }
+ }
+ }
+
+ function testArrayIterators(arrayLike, equivalentArray, reason) {
+ arraysEqual([...arrayLike], equivalentArray, `${reason}; spread operator`);
+ arraysEqual([...arrayLike.entries()], [...equivalentArray.entries()],
+ `${reason}; entries`);
+ arraysEqual([...arrayLike.keys()], [...equivalentArray.keys()],
+ `${reason}; keys`);
+ if (arrayLike.values) {
+ arraysEqual([...arrayLike.values()], equivalentArray,
+ `${reason}; values`);
+ }
+
+ var forEachCopy = [];
+ arrayLike.forEach(function(arg) { forEachCopy.push(arg); });
+ arraysEqual(forEachCopy, equivalentArray, `${reason}; forEach copy`);
+
+ var everyCopy = [];
+ arrayLike.every(function(arg) { everyCopy.push(arg); return true; });
+ arraysEqual(everyCopy, equivalentArray, `${reason}; every() copy`);
+
+ var filterCopy = [];
+ var filterResult = arrayLike.filter(function(arg) {
+ filterCopy.push(arg);
+ return true;
+ });
+ arraysEqual(filterCopy, equivalentArray, `${reason}; filter copy`);
+ arraysEqual([...filterResult], equivalentArray, `${reason}; filter result`);
+
+ var findCopy = [];
+ arrayLike.find(function(arg) { findCopy.push(arg); return false; });
+ arraysEqual(findCopy, equivalentArray, `${reason}; find() copy`);
+
+ var findIndexCopy = [];
+ arrayLike.findIndex(function(arg) { findIndexCopy.push(arg); return false; });
+ arraysEqual(findIndexCopy, equivalentArray, `${reason}; findIndex() copy`);
+
+ var mapCopy = [];
+ var mapResult = arrayLike.map(function(arg) { mapCopy.push(arg); return arg});
+ arraysEqual(mapCopy, equivalentArray, `${reason}; map() copy`);
+ arraysEqual([...mapResult], equivalentArray, `${reason}; map() result`);
+
+ var reduceCopy = [];
+ arrayLike.reduce(function(_, arg) { reduceCopy.push(arg); }, 0);
+ arraysEqual(reduceCopy, equivalentArray, `${reason}; reduce() copy`);
+
+ var reduceRightCopy = [];
+ arrayLike.reduceRight(function(_, arg) { reduceRightCopy.unshift(arg); }, 0);
+ arraysEqual(reduceRightCopy, equivalentArray, `${reason}; reduceRight() copy`);
+
+ var someCopy = [];
+ arrayLike.some(function(arg) { someCopy.push(arg); return false; });
+ arraysEqual(someCopy, equivalentArray, `${reason}; some() copy`);
+ }
+
+ function testDate() {
+ // toGMTString is handled oddly in the engine. We don't bother to support
+ // it over Xrays.
+ let propsToSkip = ['toGMTString'];
+
+ testXray('Date', new iwin.Date(), new iwin.Date(), propsToSkip);
+
+ // Test the self-hosted toLocaleString.
+ var d = new iwin.Date();
+ isnot(d.toLocaleString, Cu.unwaiveXrays(d.wrappedJSObject.toLocaleString), "Different function identities");
+ is(Cu.getGlobalForObject(d.toLocaleString), window, "Xray global is correct");
+ is(Cu.getGlobalForObject(d.wrappedJSObject.toLocaleString), iwin, "Underlying global is correct");
+ is(d.toLocaleString('de-DE'), d.wrappedJSObject.toLocaleString('de-DE'), "Results match");
+ }
+
+ var uniqueSymbol;
+
+ function testObject() {
+ testXray('Object', Cu.unwaiveXrays(Cu.waiveXrays(iwin).Object.create(new iwin.Object())),
+ new iwin.Object(), []);
+
+ // Construct an object full of tricky things.
+ let symbolProps = '';
+ uniqueSymbol = iwin.eval('var uniqueSymbol = Symbol("uniqueSymbol"); uniqueSymbol');
+ symbolProps = `, [uniqueSymbol]: 43,
+ [Symbol.for("registrySymbolProp")]: 44`;
+ var trickyObject =
+ iwin.eval(`(function() {
+ var o = new Object({
+ primitiveProp: 42, objectProp: { foo: 2 },
+ xoProp: top, hasOwnProperty: 10,
+ get getterProp() { return 2; },
+ set setterProp(x) { },
+ get getterSetterProp() { return 3; },
+ set getterSetterProp(x) { },
+ callableProp: function() { },
+ nonXrayableProp: new Map()[Symbol.iterator]()
+ ${symbolProps}
+ });
+ Object.defineProperty(o, "nonConfigurableGetterSetterProp",
+ { get: function() { return 5; }, set: function() {} });
+ return o;
+ })()`);
+ testTrickyObject(trickyObject);
+ }
+
+ function testArray() {
+ // The |length| property is generally very weird, especially with respect
+ // to its behavior on the prototype. Array.prototype is actually an Array
+ // instance, and therefore has a vestigial .length. But we don't want to
+ // show that over Xrays, and generally want .length to just appear as an
+ // |own| data property. So we add it to the ignore list here, and check it
+ // separately.
+ //
+ // |Symbol.unscopables| should in principle be exposed, but it is
+ // inconvenient (as it's a data property, unsupported by ClassSpec) and
+ // low value.
+ let propsToSkip = ['length', Symbol.unscopables];
+
+ testXray('Array', new iwin.Array(20), new iwin.Array(), propsToSkip);
+
+ let symbolProps = '';
+ uniqueSymbol = iwin.eval('var uniqueSymbol = Symbol("uniqueSymbol"); uniqueSymbol');
+ symbolProps = `trickyArray[uniqueSymbol] = 43;
+ trickyArray[Symbol.for("registrySymbolProp")] = 44;`;
+ var trickyArray =
+ iwin.eval(`var trickyArray = [];
+ trickyArray.primitiveProp = 42;
+ trickyArray.objectProp = { foo: 2 };
+ trickyArray.xoProp = top;
+ trickyArray.hasOwnProperty = 10;
+ Object.defineProperty(trickyArray, 'getterProp', { get: function() { return 2; }});
+ Object.defineProperty(trickyArray, 'setterProp', { set: function(x) {}});
+ Object.defineProperty(trickyArray, 'getterSetterProp', { get: function() { return 3; }, set: function(x) {}, configurable: true});
+ Object.defineProperty(trickyArray, 'nonConfigurableGetterSetterProp', { get: function() { return 5; }, set: function(x) {}});
+ trickyArray.callableProp = function() {};
+ trickyArray.nonXrayableProp = new Map()[Symbol.iterator]();
+ ${symbolProps}
+ trickyArray;`);
+
+ // Test indexed access.
+ trickyArray.wrappedJSObject[9] = "some indexed property";
+ is(trickyArray[9], "some indexed property", "indexed properties work correctly over Xrays");
+ is(trickyArray.length, 10, "Length works correctly over Xrays");
+ checkThrows(function() { "use strict"; delete trickyArray.length; }, /config/, "Can't delete non-configurable 'length' property");
+ delete trickyArray[9];
+ is(trickyArray[9], undefined, "Delete works correctly over Xrays");
+ is(trickyArray.wrappedJSObject[9], undefined, "Delete works correctly over Xrays (viewed via waiver)");
+ is(trickyArray.length, 10, "length doesn't change");
+ trickyArray[11] = "some other indexed property";
+ is(trickyArray.length, 12, "length now changes");
+ is(trickyArray.wrappedJSObject[11], "some other indexed property");
+ trickyArray.length = 0;
+ is(trickyArray.length, 0, "Setting length works over Xray");
+ is(trickyArray[11], undefined, "Setting length truncates over Xray");
+ Object.defineProperty(trickyArray, 'length', { configurable: false, enumerable: false, writable: false, value: 0 });
+ trickyArray[1] = "hi";
+ is(trickyArray.length, 0, "Length remains non-writable");
+ is(trickyArray[1], undefined, "Frozen length forbids new properties");
+ is(trickyArray instanceof iwin.Array, true, "instanceof should work across xray wrappers.");
+ testTrickyObject(trickyArray);
+
+ testArrayIterators(new iwin.Array(1, 1, 2, 3, 5), [1, 1, 2, 3, 5]);
+ }
+
+ // Parts of this function are kind of specific to testing Object, but we factor
+ // it out so that we can re-use the trickyObject stuff on Arrays.
+ function testTrickyObject(trickyObject) {
+
+ // Make sure it looks right under the hood.
+ is(trickyObject.wrappedJSObject.getterProp, 2, "Underlying object has getter");
+ is(Cu.unwaiveXrays(trickyObject.wrappedJSObject.xoProp), top, "Underlying object has xo property");
+
+ // Test getOwnPropertyNames.
+ var expectedNames = ['objectProp', 'primitiveProp'];
+ if (trickyObject instanceof iwin.Array)
+ expectedNames.push('length');
+ is(Object.getOwnPropertyNames(trickyObject).sort().toSource(),
+ expectedNames.sort().toSource(), "getOwnPropertyNames should be filtered correctly");
+ var expectedSymbols = [Symbol.for("registrySymbolProp"), uniqueSymbol];
+ is(Object.getOwnPropertySymbols(trickyObject).map(uneval).sort().toSource(),
+ expectedSymbols.map(uneval).sort().toSource(),
+ "getOwnPropertySymbols should be filtered correctly");
+
+ // Test that cloning uses the Xray view.
+ var cloned = Cu.cloneInto(trickyObject, this);
+ is(Object.getOwnPropertyNames(cloned).sort().toSource(),
+ expectedNames.sort().toSource(), "structured clone should use the Xray view");
+ is(Object.getOwnPropertySymbols(cloned).map(uneval).sort().toSource(),
+ "[]", "structured cloning doesn't clone symbol-keyed properties yet");
+
+ // Test iteration and in-place modification. Beware of 'expando', which is the property
+ // we placed on the xray proto.
+ var propCount = 0;
+ for (let prop in trickyObject) {
+ if (prop == 'primitiveProp')
+ trickyObject[prop] = trickyObject[prop] - 10;
+ if (prop != 'expando') {
+ // eslint-disable-next-line no-self-assign
+ trickyObject[prop] = trickyObject[prop];
+ }
+ ++propCount;
+ }
+ is(propCount, 3, "Should iterate the correct number of times");
+
+ // Test Object.keys.
+ is(Object.keys(trickyObject).sort().toSource(),
+ ['objectProp', 'primitiveProp'].toSource(), "Object.keys should be filtered correctly");
+
+ // Test getOwnPropertyDescriptor.
+ is(trickyObject.primitiveProp, 32, "primitive prop works");
+ is(trickyObject.objectProp.foo, 2, "object prop works");
+ is(typeof trickyObject.callableProp, 'undefined', "filtering works correctly");
+ is(Object.getOwnPropertyDescriptor(trickyObject, 'primitiveProp').value, 32, "getOwnPropertyDescriptor works");
+ is(Object.getOwnPropertyDescriptor(trickyObject, 'xoProp'), undefined, "filtering works with getOwnPropertyDescriptor");
+
+ // Test defineProperty.
+
+ trickyObject.primitiveSetByXray = 'fourty two';
+ is(trickyObject.primitiveSetByXray, 'fourty two', "Can set primitive correctly over Xray (ready via Xray)");
+ is(trickyObject.wrappedJSObject.primitiveSetByXray, 'fourty two', "Can set primitive correctly over Xray (ready via Waiver)");
+
+ var newContentObject = iwin.eval('new Object({prop: 99, get getterProp() { return 2; }})');
+ trickyObject.objectSetByXray = newContentObject;
+ is(trickyObject.objectSetByXray.prop, 99, "Can set object correctly over Xray (ready via Xray)");
+ is(trickyObject.wrappedJSObject.objectSetByXray.prop, 99, "Can set object correctly over Xray (ready via Waiver)");
+ checkThrows(function() { trickyObject.rejectedProp = {foo: 33}}, /cross-origin object/,
+ "Should reject privileged object property definition");
+
+ // Test JSON.stringify.
+ var jsonStr = JSON.stringify(newContentObject);
+ ok(/prop/.test(jsonStr), "JSON stringification should work: " + jsonStr);
+
+ // Test deletion.
+ delete newContentObject.prop;
+ ok(!newContentObject.hasOwnProperty('prop'), "Deletion should work");
+ ok(!newContentObject.wrappedJSObject.hasOwnProperty('prop'), "Deletion should forward");
+ delete newContentObject.getterProp;
+ ok(newContentObject.wrappedJSObject.hasOwnProperty('getterProp'), "Deletion be no-op for filtered property");
+
+ // We should be able to overwrite an existing accessor prop and convert it
+ // to a value prop.
+ is(trickyObject.wrappedJSObject.getterSetterProp, 3, "Underlying object has getter");
+ is(trickyObject.getterSetterProp, undefined, "Filtering properly over Xray");
+ trickyObject.getterSetterProp = 'redefined';
+ is(trickyObject.getterSetterProp, 'redefined', "Redefinition works");
+ is(trickyObject.wrappedJSObject.getterSetterProp, 'redefined', "Redefinition forwards");
+
+ // We should NOT be able to overwrite an existing non-configurable accessor
+ // prop, though.
+ is(trickyObject.wrappedJSObject.nonConfigurableGetterSetterProp, 5,
+ "Underlying object has getter");
+ is(trickyObject.nonConfigurableGetterSetterProp, undefined,
+ "Filtering properly over Xray here too");
+ is((trickyObject.nonConfigurableGetterSetterProp = 'redefined'), 'redefined',
+ "Assigning to non-configurable prop should fail silently in non-strict mode");
+ checkThrows(function() {
+ "use strict";
+ trickyObject.nonConfigurableGetterSetterProp = 'redefined';
+ }, /config/, "Should throw when redefining non-configurable prop in strict mode");
+ is(trickyObject.nonConfigurableGetterSetterProp, undefined,
+ "Redefinition should have failed");
+ is(trickyObject.wrappedJSObject.nonConfigurableGetterSetterProp, 5,
+ "Redefinition really should have failed");
+
+ checkThrows(function() { trickyObject.hasOwnProperty = 33; }, /shadow/,
+ "Should reject shadowing of pre-existing inherited properties over Xrays");
+
+ checkThrows(function() { Object.defineProperty(trickyObject, 'rejectedProp', { get() {}}); },
+ /accessor property/, "Should reject accessor property definition");
+ }
+
+ function testTypedArrays() {
+ // We don't invoke testXray with %TypedArray%, because that function isn't
+ // set up to deal with "anonymous" dependent classes (that is, classes not
+ // visible as a global property, which %TypedArray% is not), and fixing it
+ // up is more trouble than it's worth.
+
+ var typedArrayProto = Object.getPrototypeOf(Int8Array.prototype);
+
+ var desiredInheritedProps = Object.getOwnPropertyNames(typedArrayProto).sort();
+ var inheritedProps =
+ filterOut(desiredInheritedProps, ["BYTES_PER_ELEMENT", "constructor"]);
+
+ var inheritedCallables =
+ inheritedProps.filter(name => (propertyIsGetter(typedArrayProto, name) ||
+ typeof typedArrayProto[name] === "function") &&
+ name !== "constructor");
+
+ for (let c of typedArrayClasses) {
+ var t = new iwin[c](10);
+ checkThrows(function() { t[2]; }, /performant/, "direct property-wise reading of typed arrays forbidden over Xrays");
+ checkThrows(function() { t[2] = 3; }, /performant/, "direct property-wise writing of typed arrays forbidden over Xrays");
+ var wesb = new Cu.Sandbox([iwin], {isWebExtensionContentScript: true});
+ wesb.t = t;
+ wesb.eval('t[2] = 3');
+ is(wesb.eval('t.wrappedJSObject[2]'), 3, "direct property-wise writing of typed arrays allowed for WebExtension content scripts");
+ is(wesb.eval('t[2]'), 3, "direct property-wise reading and writing of typed arrays allowed for WebExtensions content scripts");
+
+ t.wrappedJSObject[2] = 3;
+ is(t.wrappedJSObject[2], 3, "accessing elements over waivers works");
+ t.wrappedJSObject.expando = 'hi';
+ is(t.wrappedJSObject.expando, 'hi', "access expandos over waivers works");
+ is(Cu.cloneInto(t, window)[2], 3, "cloneInto works");
+ is(Cu.cloneInto(t, window).expando, undefined, "cloneInto does not copy expandos");
+ is(Object.getOwnPropertyNames(t).sort().toSource(),
+ '["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]',
+ "Only indexed properties visible over Xrays");
+ Object.defineProperty(t.wrappedJSObject, 'length', {value: 42});
+ is(t.wrappedJSObject.length, 42, "Set tricky expando")
+ is(t.length, 10, "Length accessor works over Xrays")
+ is(t.byteLength, t.length * window[c].prototype.BYTES_PER_ELEMENT, "byteLength accessor works over Xrays")
+
+ // Can create TypedArray from content ArrayBuffer
+ var buffer = new iwin.ArrayBuffer(8);
+ new window[c](buffer);
+
+ var xray = new iwin[c](0);
+ var xrayTypedArrayProto = Object.getPrototypeOf(Object.getPrototypeOf(xray));
+ testProtoCallables(inheritedCallables, new iwin[c](0), xrayTypedArrayProto, typedArrayProto);
+
+ // When testing iterators, make sure to do so from inside our web
+ // extension sandbox, since from chrome we can't poke their indices. Note
+ // that we have to actually recreate our functions that touch typed array
+ // indices inside the sandbox, not just export them, because otherwise
+ // they'll just run with our principal anyway.
+ //
+ // But we do want to export is(), since we want ours called.
+ wesb.eval(String(arraysEqual));
+ wesb.eval(String(testArrayIterators));
+ Cu.exportFunction(is, wesb,
+ { defineAs: "is" });
+ wesb.eval('testArrayIterators(t, [0, 0, 3, 0, 0, 0, 0, 0, 0, 0])');
+ }
+ }
+
+ function testErrorObjects() {
+ // We only invoke testXray with Error, because that function isn't set up
+ // to deal with dependent classes and fixing it up is more trouble than
+ // it's worth.
+ testXray('Error', new iwin.Error('some error message'), new iwin.Error());
+
+ // Make sure that the dependent classes have their prototypes set up correctly.
+ for (let c of errorObjectClasses.filter(x => x != "Error")) {
+ var args = ['some message'];
+ if (c === 'AggregateError') {
+ // AggregateError's first argument is the list of aggregated errors.
+ args.unshift(new iwin.Array('error 1', 'error 2'));
+ }
+ var e = new iwin[c](...args);
+ is(Object.getPrototypeOf(e).name, c, "Prototype has correct name");
+ is(Object.getPrototypeOf(Object.getPrototypeOf(e)), iwin.Error.prototype, "Dependent prototype set up correctly");
+ is(e.name, c, "Exception name inherited correctly");
+
+ function testProperty(name, criterion, goodReplacement, faultyReplacement) {
+ ok(criterion(e[name]), name + " property is correct: " + e[name]);
+ e.wrappedJSObject[name] = goodReplacement;
+ is(e[name], goodReplacement, name + " property ok after replacement: " + goodReplacement);
+ e.wrappedJSObject[name] = faultyReplacement;
+ is(e[name], name == 'message' ? "" : undefined, name + " property skipped after suspicious replacement");
+ }
+ testProperty('message', x => x == 'some message', 'some other message', 42);
+ testProperty('fileName', x => x == '', 'otherFilename.html', new iwin.Object());
+ testProperty('columnNumber', x => x == 1, 99, 99.5);
+ testProperty('lineNumber', x => x == 0, 50, 'foo');
+
+ if (c === 'AggregateError') {
+ let {errors} = e;
+ is(errors.length, 2, "errors property has the correct length");
+ is(errors[0], 'error 1', "errors[0] has the correct value");
+ is(errors[1], 'error 2', "errors[1] has the correct value");
+
+ e.wrappedJSObject.errors = 42;
+ is(e.wrappedJSObject.errors, 42, "errors is a plain data property");
+ is(e.errors, 42, "visible over Xrays");
+ }
+
+ // Note - an Exception newed via Xrays is going to have an empty stack given the
+ // current semantics and implementation. This tests the current behavior, but that
+ // may change in bug 1036527 or similar.
+ //
+ // Furthermore, xrays should always return an error's original stack, and
+ // not overwrite it.
+ var stack = e.stack;
+ ok(/^\s*$/.test(stack), "stack property should be correct");
+ e.wrappedJSObject.stack = "not a stack";
+ is(e.stack, stack, "Xrays should never get an overwritten stack property.");
+
+ // Test the .cause property is correctly handled, too.
+ if (isNightlyBuild) {
+ let cause = 'error cause';
+ let options = new iwin.Object();
+ options.cause = cause;
+ args.push(options);
+
+ let e = new iwin[c](...args);
+ is(e.cause, cause);
+
+ e.wrappedJSObject.cause = 42;
+ is(e.wrappedJSObject.cause, 42, "cause is a plain data property");
+ is(e.cause, 42, "visible over Xrays");
+ }
+ }
+ }
+
+ function testRegExp() {
+ // RegExp statics are very weird, and in particular RegExp has static
+ // properties that have to do with the last regexp execution in the global.
+ // Xraying those makes no sense, so we just skip constructor properties for
+ // RegExp xrays.
+ // RegExp[@@species] is affected by above skip, but we don't fix it until
+ // compelling use-case appears, as supporting RegExp[@@species] while
+ // skipping other static properties makes things complicated.
+ let ctorPropsToSkip = ["input", "lastMatch", "lastParen",
+ "leftContext", "rightContext", "$1", "$2", "$3",
+ "$4", "$5", "$6", "$7", "$8", "$9", "$_", "$&",
+ "$+", "$`", "$'", Symbol.species];
+ testXray('RegExp', new iwin.RegExp('foo'), new iwin.RegExp(), [],
+ ctorPropsToSkip);
+
+ // Test the self-hosted |flags| property, toString, and toSource.
+ for (var flags of ["", "g", "i", "m", "y", "gimy"]) {
+ var re = new iwin.RegExp("foo", flags);
+ is(re.flags, re.wrappedJSObject.flags, "Results match");
+
+ isnot(re.toString, Cu.unwaiveXrays(re.wrappedJSObject.toString), "Different function identities");
+ is(Cu.getGlobalForObject(re.toString), window, "Xray global is correct");
+ is(Cu.getGlobalForObject(re.wrappedJSObject.toString), iwin, "Underlying global is correct");
+ is(re.toString(), re.wrappedJSObject.toString(), "Results match");
+
+ isnot(re.toSource, Cu.unwaiveXrays(re.wrappedJSObject.toSource), "Different function identities");
+ is(Cu.getGlobalForObject(re.toSource), window, "Xray global is correct");
+ if (re.wrappedJSObject.toSource) {
+ is(Cu.getGlobalForObject(re.wrappedJSObject.toSource), iwin, "Underlying global is correct");
+ is(re.toSource(), re.wrappedJSObject.toSource(), "Results match");
+ }
+
+ // Test with modified flags accessors
+ iwin.eval(`
+var props = ["global", "ignoreCase", "multiline", "sticky", "source", "unicode"];
+var origDescs = {};
+for (var prop of props) {
+ origDescs[prop] = Object.getOwnPropertyDescriptor(RegExp.prototype, prop);
+ Object.defineProperty(RegExp.prototype, prop, {
+ get: function() {
+ throw new Error("modified accessor is called");
+ }
+ });
+}
+`);
+ try {
+ is(re.flags, flags, "Unmodified flags accessors are called");
+ is(re.toString(), "/foo/" + flags, "Unmodified flags and source accessors are called");
+ is(re.toSource(), "/foo/" + flags, "Unmodified flags and source accessors are called");
+ } finally {
+ iwin.eval(`
+for (var prop of props) {
+ Object.defineProperty(RegExp.prototype, prop, origDescs[prop]);
+}
+`);
+ }
+ }
+ }
+
+ // Note: this is a small set of basic tests. More in-depth tests are located
+ // in test_promise_xrays.html.
+ function testPromise() {
+ testXray('Promise', new iwin.Promise(function(){}), new iwin.Promise(function(){}));
+
+ // Test catch and then.
+ var pr = new iwin.Promise(function(){});
+ isnot(pr.catch, Cu.unwaiveXrays(pr.wrappedJSObject.catch), "Different function identities");
+ is(Cu.getGlobalForObject(pr.catch), window, "Xray global is correct");
+ is(Cu.getGlobalForObject(pr.wrappedJSObject.catch), iwin, "Underlying global is correct");
+
+ isnot(pr.then, Cu.unwaiveXrays(pr.wrappedJSObject.then), "Different function identities");
+ is(Cu.getGlobalForObject(pr.then), window, "Xray global is correct");
+ is(Cu.getGlobalForObject(pr.wrappedJSObject.then), iwin, "Underlying global is correct");
+ }
+
+ function testArrayBuffer() {
+ let constructors = ['ArrayBuffer'];
+
+ for (const c of constructors) {
+ testXray(c, new iwin[c](0), new iwin[c](12));
+
+ var t = new iwin[c](12);
+ is(t.byteLength, 12, `${c} byteLength is correct`);
+
+ is(t.slice(4).byteLength, 8, `${c} byteLength is correct after slicing`);
+ is(Cu.getGlobalForObject(t.slice(4)), iwin, "Slice results lives in the target compartment");
+ is(Object.getPrototypeOf(t.slice(4)), iwin[c].prototype, "Slice results proto lives in target compartment")
+
+ var i32Array = new Int32Array(t);
+ // i32Array is going to be created in the buffer's target compartment,
+ // but usually this is unobservable, because the proto is set to
+ // the current compartment's prototype.
+ // However Xrays ignore the object's proto and claim its proto is
+ // the default proto for that class in the relevant compartment,
+ // so see through this proto hack.
+ todo_is(Object.getPrototypeOf(i32Array), Int32Array.prototype, "Int32Array has correct proto");
+ is(i32Array.length, 3, `Int32Array created from Xray ${c} has the correct length`);
+ is(i32Array.buffer, t, "Int32Array has the correct buffer that we passed in");
+
+ i32Array = new iwin.Int32Array(t);
+ is(Object.getPrototypeOf(i32Array), iwin.Int32Array.prototype, "Xray Int32Array has correct proto");
+ is(i32Array.length, 3, `Xray Int32Array created from Xray ${c} has the correct length`);
+ is(i32Array.buffer, t, "Xray Int32Array has the correct buffer that we passed in");
+
+ t = (new iwin.Int32Array(2)).buffer;
+ is(t.byteLength, 8, `Can access ${c} returned by buffer property`);
+ }
+ }
+
+ function testMap() {
+ testXray('Map', new iwin.Map(), new iwin.Map());
+
+ var t = iwin.eval(`new Map([[1, "a"], [null, "b"]])`);
+ is(t.size, 2, "Map size is correct");
+ is(t.get(1), "a", "Key 1 has the correct value");
+ is(t.get(null), "b", "Key null has the correct value");
+ is(t.has(1), true, "Has Key 1");
+ is(t.set(3, 5).get(3), 5, "Correctly sets key");
+ is(t.delete(null), true, "Key null can be deleted");
+
+ let values = [];
+ t.forEach((value, key) => values.push(value));
+ is(values.toString(), "a,5", "forEach enumerates values correctly");
+
+ t.clear();
+ is(t.size, 0, "Map is empty after calling clear");
+ }
+
+ function testSet() {
+ testXray('Set', new iwin.Set(), new iwin.Set());
+
+ var t = iwin.eval(`new Set([1, null])`);
+ is(t.size, 2, "Set size is correct");
+ is(t.has(1), true, "Contains 1");
+ is(t.has(null), true, "Contains null");
+ is(t.add(5).has(5), true, "Can add value to set");
+ is(t.delete(null), true, "Value null can be deleted");
+
+ let values = [];
+ t.forEach(value => values.push(value));
+ is(values.toString(), "1,5", "forEach enumerates values correctly");
+
+ t.clear();
+ is(t.size, 0, "Set is empty after calling clear");
+ }
+
+ function testWeakMap() {
+ testXray('WeakMap', new iwin.WeakMap(), new iwin.WeakMap());
+
+ var key1 = iwin.eval(`var key1 = {}; key1`);
+ var key2 = iwin.eval(`var key2 = []; key2`);
+ var key3 = iwin.eval(`var key3 = /a/; key3`);
+ var key4 = {};
+ var key5 = [];
+ var t = iwin.eval(`new WeakMap([[key1, "a"], [key2, "b"]])`);
+ is(t.get(key1), "a", "key1 has the correct value");
+ is(t.get(key2), "b", "key2 has the correct value");
+ is(t.has(key1), true, "Has key1");
+ is(t.has(key3), false, "Doesn't have key3");
+ is(t.has(key5), false, "Doesn't have key5");
+ is(t.set(key4, 5).get(key4), 5, "Correctly sets key");
+ is(t.get(key1), "a", "key1 has the correct value after modification");
+ is(t.get(key2), "b", "key2 has the correct value after modification");
+ is(t.delete(key1), true, "key1 can be deleted");
+ is(t.delete(key2), true, "key2 can be deleted");
+ is(t.delete(key3), false, "key3 cannot be deleted");
+ is(t.delete(key4), true, "key4 can be deleted");
+ is(t.delete(key5), false, "key5 cannot be deleted");
+ }
+
+ function testWeakSet() {
+ testXray('WeakSet', new iwin.WeakSet(), new iwin.WeakSet());
+
+ var key1 = iwin.eval(`var key1 = {}; key1`);
+ var key2 = iwin.eval(`var key2 = []; key2`);
+ var key3 = iwin.eval(`var key3 = /a/; key3`);
+ var key4 = {};
+ var key5 = [];
+ var t = iwin.eval(`new WeakSet([key1, key2])`);
+ is(t.has(key1), true, "Has key1");
+ is(t.has(key2), true, "Has key2");
+ is(t.has(key3), false, "Doesn't have key3");
+ is(t.has(key5), false, "Doesn't have key5");
+ is(t.add(key4, 5).has(key4), true, "Can add value to set");
+ is(t.delete(key1), true, "key1 can be deleted");
+ is(t.delete(key2), true, "key2 can be deleted");
+ is(t.delete(key3), false, "key3 cannot be deleted");
+ is(t.delete(key4), true, "key4 can be deleted");
+ is(t.delete(key5), false, "key5 cannot be deleted");
+ }
+
+ function testProxy() {
+ let ProxyCtor = iwin.Proxy;
+ is(Object.getOwnPropertyNames(ProxyCtor).sort().toSource(),
+ ["length", "name"].sort().toSource(),
+ "Xrayed Proxy constructor should not have any properties");
+ is(ProxyCtor.prototype, undefined, "Proxy.prototype should not be set");
+ // Proxy.revocable can safely be exposed, but it is not.
+ // Until it is supported, check that the property is not set.
+ is(ProxyCtor.revocable, undefined, "Proxy.reflect is not set");
+ }
+
+ function testDataView() {
+ testXray('DataView', new iwin.DataView(new iwin.ArrayBuffer(4)),
+ new iwin.DataView(new iwin.ArrayBuffer(8)));
+
+ const versions = [() => iwin.eval(`new DataView(new ArrayBuffer(8))`),
+ () => new DataView(new iwin.ArrayBuffer(8))];
+
+ for (const constructor of versions) {
+ let t = constructor();
+ is(t.byteLength, 8, `byteLength correct for "${constructor}"`);
+ is(t.byteOffset, 0, `byteOffset correct for "${constructor}"`);
+ is(t.buffer.byteLength, 8, `buffer works for "${constructor}"`);
+
+ const get = ["getInt8", "getUint8", "getInt16", "getUint16",
+ "getInt32", "getUint32", "getFloat32", "getFloat64"];
+
+ const set = ["setInt8", "setUint8", "setInt16", "setUint16",
+ "setInt32", "setUint32", "setFloat32", "setFloat64"];
+
+ for (const f of get) {
+ let x = t[f](0);
+ is(x, 0, `${f} is 0 for "${constructor}"`);
+ is(typeof x, 'number', `typeof ${f} is number for "${constructor}"`);
+ }
+
+ for (const f of ["getBigInt64", "getBigUint64"]) {
+ let x = t[f](0);
+ is(x, BigInt(0), `${f} is 0n for "${constructor}"`);
+ is(typeof x, 'bigint', `typeof ${f} is bigint for "${constructor}"`);
+ }
+
+ for (let i = 0; i < set.length; i++) {
+ t[set[i]](0, 13);
+ is(t[get[i]](0), 13, `${get[i]}(0) afer ${set[i]}(0, 13) is 13 for "${constructor}"`);
+ }
+
+ for (const k of ["BigInt64", "BigUint64"]) {
+ t["set" + k](0, BigInt(13));
+ is(t["get" + k](0), BigInt(13), `get${k}(0) afer set${k}(0, 13n) is 13n for "${constructor}"`);
+ }
+ }
+ }
+
+ function testNumber() {
+ // We don't actually support Xrays to Number yet. This is testing
+ // that case. If we add such support, we might have to start
+ // using a different non-Xrayed class here, if we can find one.
+ let xrayCtor = iwin.Number;
+ is(Object.getOwnPropertyNames(xrayCtor).sort().toSource(),
+ Object.getOwnPropertyNames(function() {}).sort().toSource(),
+ "We should not have any static properties on a non-Xrayable constructor");
+ is(xrayCtor.noSuchProperty, undefined,
+ "Where did our noSuchProperty property come from?");
+ }
+
+ ]]>
+ </script>
+ <iframe id="ifr" onload="go();" src="http://example.org/tests/js/xpconnect/tests/mochitest/file_empty.html" />
+</window>
diff --git a/js/xpconnect/tests/chrome/test_xrayic.xhtml b/js/xpconnect/tests/chrome/test_xrayic.xhtml
new file mode 100644
index 0000000000..fd9ab8f7b3
--- /dev/null
+++ b/js/xpconnect/tests/chrome/test_xrayic.xhtml
@@ -0,0 +1,81 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1355109
+-->
+<window title="Mozilla Bug 1355109"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=758415"
+ target="_blank">Mozilla Bug 758415</a>
+ </body>
+
+ <!-- test code goes here -->
+ <script type="application/javascript">
+ <![CDATA[
+
+ SimpleTest.waitForExplicitFinish();
+
+ // Import our test JSM. We first strip the filename off
+ // the chrome url, then append the jsm filename.
+ var base = /.*\//.exec(window.location.href)[0];
+ ChromeUtils.import(base + "file_expandosharing.jsm");
+
+ // Wait for all child frames to load.
+ var gLoadCount = 0;
+ function frameLoaded() {
+ if (++gLoadCount == window.frames.length)
+ go();
+ }
+
+ function go() {
+ testSandbox(1);
+ testSandbox(100);
+ testSandbox(1000);
+ SimpleTest.finish();
+ }
+
+ function testSandbox(iterations) {
+ // Create an expanded principal sandbox to get xrays with exclusive
+ // expandos.
+ var sandbox = new Cu.Sandbox(["https://test1.example.org",
+ "https://test2.example.org"]);
+ sandbox.iframeWindows = new sandbox.Array();
+ for (let iframe of document.getElementsByTagName('iframe')) {
+ sandbox.iframeWindows.push(iframe.contentWindow);
+ }
+ Cu.evalInSandbox(testClassName.toSource(), sandbox);
+ Cu.evalInSandbox(testIC.toSource(), sandbox);
+ is(Cu.evalInSandbox("testIC(" + iterations + ");", sandbox), true, "sandbox test");
+ }
+
+ // This is in a separate function to provide a common source location for ICs.
+ function testClassName(obj, expected) {
+ var className = obj.className;
+ if (className != expected)
+ throw new Error("Got " + className + ", expected " + expected);
+ }
+
+ function testIC(iterations) {
+ for (var i = 0; i < this.iframeWindows.length; i++) {
+ var win = this.iframeWindows[i];
+ var spans = win.document.getElementsByTagName('span');
+ for (var j = 0; j < spans.length; j++) {
+ var span = spans[j];
+ for (var k = 0; k < iterations; k++)
+ testClassName(span, "iamaspan");
+ Object.defineProperty(span, "className", { value: "what" });
+ testClassName(span, "what");
+ }
+ }
+ return true;
+ }
+ ]]>
+ </script>
+ <iframe id="inlineFrame1" onload="frameLoaded();" type="content" src="https://test1.example.org/tests/js/xpconnect/tests/mochitest/file_xrayic.html" />
+ <iframe id="inlineFrame2" onload="frameLoaded();" type="content" src="https://test1.example.org/tests/js/xpconnect/tests/mochitest/file_xrayic.html" />
+</window>
diff --git a/js/xpconnect/tests/chrome/utf8_subscript.js b/js/xpconnect/tests/chrome/utf8_subscript.js
new file mode 100644
index 0000000000..6444273c9d
--- /dev/null
+++ b/js/xpconnect/tests/chrome/utf8_subscript.js
@@ -0,0 +1,5 @@
+// -*- coding: utf-8; indent-tabs-mode: nil -*-
+var str = "𝔘𝔫𝔦𝔠𝔬𝔡𝔢";
+function f() {
+ return 42;
+}
diff --git a/js/xpconnect/tests/chrome/worker_discardSystemSource.js b/js/xpconnect/tests/chrome/worker_discardSystemSource.js
new file mode 100644
index 0000000000..5da32511fa
--- /dev/null
+++ b/js/xpconnect/tests/chrome/worker_discardSystemSource.js
@@ -0,0 +1,6 @@
+function canary() {
+ // eslint-disable-next-line no-unused-vars
+ var someBitOfSource = 42;
+}
+
+postMessage(canary.toString());
diff --git a/js/xpconnect/tests/components/native/moz.build b/js/xpconnect/tests/components/native/moz.build
new file mode 100644
index 0000000000..ba3d227c5b
--- /dev/null
+++ b/js/xpconnect/tests/components/native/moz.build
@@ -0,0 +1,24 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+EXPORTS += [
+ "xpctest_private.h",
+]
+
+UNIFIED_SOURCES += [
+ "xpctest_attributes.cpp",
+ "xpctest_cenums.cpp",
+ "xpctest_esmreturncode.cpp",
+ "xpctest_module.cpp",
+ "xpctest_params.cpp",
+ "xpctest_returncode.cpp",
+]
+
+LOCAL_INCLUDES += [
+ "/xpcom/components",
+]
+
+FINAL_LIBRARY = "xul"
diff --git a/js/xpconnect/tests/components/native/xpctest_attributes.cpp b/js/xpconnect/tests/components/native/xpctest_attributes.cpp
new file mode 100644
index 0000000000..180c1f7606
--- /dev/null
+++ b/js/xpconnect/tests/components/native/xpctest_attributes.cpp
@@ -0,0 +1,136 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "xpctest_private.h"
+
+NS_IMPL_ISUPPORTS(xpcTestObjectReadOnly, nsIXPCTestObjectReadOnly)
+
+xpcTestObjectReadOnly ::xpcTestObjectReadOnly() {
+ boolProperty = true;
+ shortProperty = 32767;
+ longProperty = 2147483647;
+ floatProperty = 5.5f;
+ charProperty = 'X';
+ // timeProperty is PRTime and signed type.
+ // So it has to allow negative value.
+ timeProperty = -1;
+}
+
+NS_IMETHODIMP xpcTestObjectReadOnly ::GetStrReadOnly(char** aStrReadOnly) {
+ if (!aStrReadOnly) return NS_ERROR_NULL_POINTER;
+ *aStrReadOnly = moz_xstrdup("XPConnect Read-Only String");
+ return NS_OK;
+}
+
+NS_IMETHODIMP xpcTestObjectReadOnly ::GetBoolReadOnly(bool* aBoolReadOnly) {
+ *aBoolReadOnly = boolProperty;
+ return NS_OK;
+}
+NS_IMETHODIMP xpcTestObjectReadOnly ::GetShortReadOnly(
+ int16_t* aShortReadOnly) {
+ *aShortReadOnly = shortProperty;
+ return NS_OK;
+}
+NS_IMETHODIMP xpcTestObjectReadOnly ::GetLongReadOnly(int32_t* aLongReadOnly) {
+ *aLongReadOnly = longProperty;
+ return NS_OK;
+}
+NS_IMETHODIMP xpcTestObjectReadOnly ::GetFloatReadOnly(float* aFloatReadOnly) {
+ *aFloatReadOnly = floatProperty;
+ return NS_OK;
+}
+NS_IMETHODIMP xpcTestObjectReadOnly ::GetCharReadOnly(char* aCharReadOnly) {
+ *aCharReadOnly = charProperty;
+ return NS_OK;
+}
+NS_IMETHODIMP xpcTestObjectReadOnly ::GetTimeReadOnly(PRTime* aTimeReadOnly) {
+ *aTimeReadOnly = timeProperty;
+ return NS_OK;
+}
+
+NS_IMPL_ISUPPORTS(xpcTestObjectReadWrite, nsIXPCTestObjectReadWrite)
+
+xpcTestObjectReadWrite ::xpcTestObjectReadWrite() {
+ stringProperty = moz_xstrdup("XPConnect Read-Writable String");
+ boolProperty = true;
+ shortProperty = 32767;
+ longProperty = 2147483647;
+ floatProperty = 5.5f;
+ charProperty = 'X';
+ // timeProperty is PRTime and signed type.
+ // So it has to allow negative value.
+ timeProperty = -1;
+}
+
+xpcTestObjectReadWrite ::~xpcTestObjectReadWrite() { free(stringProperty); }
+
+NS_IMETHODIMP xpcTestObjectReadWrite ::GetStringProperty(
+ char** aStringProperty) {
+ if (!aStringProperty) {
+ return NS_ERROR_NULL_POINTER;
+ }
+ *aStringProperty = moz_xstrdup(stringProperty);
+ return NS_OK;
+}
+NS_IMETHODIMP xpcTestObjectReadWrite ::SetStringProperty(
+ const char* aStringProperty) {
+ free(stringProperty);
+ stringProperty = moz_xstrdup(aStringProperty);
+ return NS_OK;
+}
+
+NS_IMETHODIMP xpcTestObjectReadWrite ::GetBooleanProperty(
+ bool* aBooleanProperty) {
+ *aBooleanProperty = boolProperty;
+ return NS_OK;
+}
+NS_IMETHODIMP xpcTestObjectReadWrite ::SetBooleanProperty(
+ bool aBooleanProperty) {
+ boolProperty = aBooleanProperty;
+ return NS_OK;
+}
+NS_IMETHODIMP xpcTestObjectReadWrite ::GetShortProperty(
+ int16_t* aShortProperty) {
+ *aShortProperty = shortProperty;
+ return NS_OK;
+}
+NS_IMETHODIMP xpcTestObjectReadWrite ::SetShortProperty(
+ int16_t aShortProperty) {
+ shortProperty = aShortProperty;
+ return NS_OK;
+}
+NS_IMETHODIMP xpcTestObjectReadWrite ::GetLongProperty(int32_t* aLongProperty) {
+ *aLongProperty = longProperty;
+ return NS_OK;
+}
+NS_IMETHODIMP xpcTestObjectReadWrite ::SetLongProperty(int32_t aLongProperty) {
+ longProperty = aLongProperty;
+ return NS_OK;
+}
+NS_IMETHODIMP xpcTestObjectReadWrite ::GetFloatProperty(float* aFloatProperty) {
+ *aFloatProperty = floatProperty;
+ return NS_OK;
+}
+NS_IMETHODIMP xpcTestObjectReadWrite ::SetFloatProperty(float aFloatProperty) {
+ floatProperty = aFloatProperty;
+ return NS_OK;
+}
+NS_IMETHODIMP xpcTestObjectReadWrite ::GetCharProperty(char* aCharProperty) {
+ *aCharProperty = charProperty;
+ return NS_OK;
+}
+NS_IMETHODIMP xpcTestObjectReadWrite ::SetCharProperty(char aCharProperty) {
+ charProperty = aCharProperty;
+ return NS_OK;
+}
+NS_IMETHODIMP xpcTestObjectReadWrite ::GetTimeProperty(PRTime* aTimeProperty) {
+ *aTimeProperty = timeProperty;
+ return NS_OK;
+}
+NS_IMETHODIMP xpcTestObjectReadWrite ::SetTimeProperty(PRTime aTimeProperty) {
+ timeProperty = aTimeProperty;
+ return NS_OK;
+}
diff --git a/js/xpconnect/tests/components/native/xpctest_cenums.cpp b/js/xpconnect/tests/components/native/xpctest_cenums.cpp
new file mode 100644
index 0000000000..ae72351b77
--- /dev/null
+++ b/js/xpconnect/tests/components/native/xpctest_cenums.cpp
@@ -0,0 +1,67 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* local header for xpconnect tests components */
+
+#include "xpctest_private.h"
+
+NS_IMPL_ISUPPORTS(xpcTestCEnums, nsIXPCTestCEnums)
+
+// If this compiles, we pass. Otherwise, this means that XPIDL bitflag
+// generation is broken.
+xpcTestCEnums::xpcTestCEnums() {
+ static_assert(
+ 0 == static_cast<uint32_t>(shouldBe0Implicit),
+ "XPIDL bitflag generation did not create correct shouldBe0Implicit flag");
+ static_assert(
+ 1 == static_cast<uint32_t>(shouldBe1Implicit),
+ "XPIDL bitflag generation did not create correct shouldBe1Implicit flag");
+ static_assert(
+ 2 == static_cast<uint32_t>(shouldBe2Implicit),
+ "XPIDL bitflag generation did not create correct shouldBe2Implicit flag");
+ static_assert(
+ 3 == static_cast<uint32_t>(shouldBe3Implicit),
+ "XPIDL bitflag generation did not create correct shouldBe3Implicit flag");
+ static_assert(
+ 5 == static_cast<uint32_t>(shouldBe5Implicit),
+ "XPIDL bitflag generation did not create correct shouldBe5Implicit flag");
+ static_assert(
+ 6 == static_cast<uint32_t>(shouldBe6Implicit),
+ "XPIDL bitflag generation did not create correct shouldBe6Implicit flag");
+ static_assert(2 == static_cast<uint32_t>(shouldBe2AgainImplicit),
+ "XPIDL bitflag generation did not create correct "
+ "shouldBe2AgainImplicit flag");
+ static_assert(3 == static_cast<uint32_t>(shouldBe3AgainImplicit),
+ "XPIDL bitflag generation did not create correct "
+ "shouldBe3AgainImplicit flag");
+ static_assert(
+ 1 == static_cast<uint32_t>(shouldBe1Explicit),
+ "XPIDL bitflag generation did not create correct shouldBe1Explicit flag");
+ static_assert(
+ 2 == static_cast<uint32_t>(shouldBe2Explicit),
+ "XPIDL bitflag generation did not create correct shouldBe2Explicit flag");
+ static_assert(
+ 4 == static_cast<uint32_t>(shouldBe4Explicit),
+ "XPIDL bitflag generation did not create correct shouldBe4Explicit flag");
+ static_assert(
+ 8 == static_cast<uint32_t>(shouldBe8Explicit),
+ "XPIDL bitflag generation did not create correct shouldBe8Explicit flag");
+ static_assert(12 == static_cast<uint32_t>(shouldBe12Explicit),
+ "XPIDL bitflag generation did not create correct "
+ "shouldBe12Explicit flag");
+}
+
+nsresult xpcTestCEnums::TestCEnumInput(testFlagsExplicit a) {
+ if (a != shouldBe12Explicit) {
+ return NS_ERROR_FAILURE;
+ }
+ return NS_OK;
+}
+
+nsresult xpcTestCEnums::TestCEnumOutput(testFlagsExplicit* a) {
+ *a = shouldBe8Explicit;
+ return NS_OK;
+}
diff --git a/js/xpconnect/tests/components/native/xpctest_esmreturncode.cpp b/js/xpconnect/tests/components/native/xpctest_esmreturncode.cpp
new file mode 100644
index 0000000000..758cab65da
--- /dev/null
+++ b/js/xpconnect/tests/components/native/xpctest_esmreturncode.cpp
@@ -0,0 +1,20 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "xpctest_private.h"
+#include "nsComponentManagerUtils.h"
+#include "nsImportModule.h"
+
+NS_IMPL_ISUPPORTS(nsXPCTestESMReturnCodeParent, nsIXPCTestReturnCodeParent)
+
+NS_IMETHODIMP nsXPCTestESMReturnCodeParent::CallChild(int32_t childBehavior,
+ nsresult* _retval) {
+ nsresult rv;
+ nsCOMPtr<nsIXPCTestReturnCodeChild> child(do_ImportESModule(
+ "resource://test/ReturnCodeChild.sys.mjs", "ReturnCodeChild", &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = child->DoIt(childBehavior);
+ *_retval = rv;
+ return NS_OK;
+}
diff --git a/js/xpconnect/tests/components/native/xpctest_module.cpp b/js/xpconnect/tests/components/native/xpctest_module.cpp
new file mode 100644
index 0000000000..42b4259d5b
--- /dev/null
+++ b/js/xpconnect/tests/components/native/xpctest_module.cpp
@@ -0,0 +1,46 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* module registration and factory code. */
+
+#include "mozilla/GenericFactory.h"
+#include "mozilla/ResultExtensions.h"
+#include "mozilla/Try.h"
+#include "nsComponentManager.h"
+#include "xpctest_private.h"
+
+template <typename T>
+nsresult RegisterFactory(const char* aContractID) {
+ auto constructor = [](REFNSIID aIID, void** aResult) {
+ RefPtr inst = new T();
+ return inst->QueryInterface(aIID, aResult);
+ };
+
+ nsCOMPtr<nsIFactory> factory = new mozilla::GenericFactory(constructor);
+
+ nsID cid;
+ MOZ_TRY(nsID::GenerateUUIDInPlace(cid));
+
+ return nsComponentManagerImpl::gComponentManager->RegisterFactory(
+ cid, aContractID, aContractID, factory);
+}
+
+nsresult xpcTestRegisterComponents() {
+ MOZ_TRY(RegisterFactory<xpcTestObjectReadOnly>(
+ "@mozilla.org/js/xpc/test/native/ObjectReadOnly;1"));
+ MOZ_TRY(RegisterFactory<xpcTestObjectReadWrite>(
+ "@mozilla.org/js/xpc/test/native/ObjectReadWrite;1"));
+ MOZ_TRY(RegisterFactory<nsXPCTestParams>(
+ "@mozilla.org/js/xpc/test/native/Params;1"));
+ MOZ_TRY(RegisterFactory<nsXPCTestReturnCodeParent>(
+ "@mozilla.org/js/xpc/test/native/ReturnCodeParent;1"));
+ MOZ_TRY(RegisterFactory<nsXPCTestESMReturnCodeParent>(
+ "@mozilla.org/js/xpc/test/native/ESMReturnCodeParent;1"));
+ MOZ_TRY(RegisterFactory<xpcTestCEnums>(
+ "@mozilla.org/js/xpc/test/native/CEnums;1"));
+
+ return NS_OK;
+}
diff --git a/js/xpconnect/tests/components/native/xpctest_params.cpp b/js/xpconnect/tests/components/native/xpctest_params.cpp
new file mode 100644
index 0000000000..4d1924de05
--- /dev/null
+++ b/js/xpconnect/tests/components/native/xpctest_params.cpp
@@ -0,0 +1,413 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "xpctest_private.h"
+#include "xpctest_interfaces.h"
+#include "mozilla/Casting.h"
+#include "js/Value.h"
+
+#include "nsCOMPtr.h"
+#include "nsComponentManagerUtils.h"
+#include "nsIURI.h"
+
+using namespace mozilla;
+
+NS_IMPL_ISUPPORTS(nsXPCTestParams, nsIXPCTestParams)
+
+#define GENERIC_METHOD_IMPL \
+ { \
+ *_retval = *b; \
+ *b = a; \
+ return NS_OK; \
+ }
+
+#define STRING_METHOD_IMPL \
+ { \
+ _retval.Assign(b); \
+ b.Assign(a); \
+ return NS_OK; \
+ }
+
+#define SEQUENCE_METHOD_IMPL(TAKE_OWNERSHIP) \
+ { \
+ _retval = std::move(b); \
+ b = a.Clone(); \
+ for (uint32_t i = 0; i < b.Length(); ++i) TAKE_OWNERSHIP(b[i]); \
+ return NS_OK; \
+ }
+
+#define TAKE_OWNERSHIP_NOOP(val) \
+ {}
+#define TAKE_OWNERSHIP_INTERFACE(val) \
+ { static_cast<nsISupports*>(val)->AddRef(); }
+#define TAKE_OWNERSHIP_STRING(val) \
+ { \
+ nsDependentCString vprime(val); \
+ val = ToNewCString(vprime); \
+ }
+#define TAKE_OWNERSHIP_WSTRING(val) \
+ { \
+ nsDependentString vprime(val); \
+ val = ToNewUnicode(vprime); \
+ }
+
+// Macro for our buffer-oriented types:
+// 'type' is the type of element that the buffer contains.
+// 'padding' is an offset added to length, allowing us to handle
+// null-terminated strings.
+// 'TAKE_OWNERSHIP' is one of the macros above.
+#define BUFFER_METHOD_IMPL(type, padding, TAKE_OWNERSHIP) \
+ { \
+ uint32_t elemSize = sizeof(type); \
+ \
+ /* Copy b into rv. */ \
+ *rvLength = *bLength; \
+ *rv = static_cast<type*>(moz_xmalloc(elemSize * (*bLength + padding))); \
+ memcpy(*rv, *b, elemSize*(*bLength + padding)); \
+ \
+ /* Copy a into b. */ \
+ *bLength = aLength; \
+ free(*b); \
+ *b = static_cast<type*>(moz_xmalloc(elemSize * (aLength + padding))); \
+ memcpy(*b, a, elemSize*(aLength + padding)); \
+ \
+ /* We need to take ownership of the data we got from a, \
+ since the caller owns it. */ \
+ for (unsigned i = 0; i < *bLength + padding; ++i) TAKE_OWNERSHIP((*b)[i]); \
+ \
+ return NS_OK; \
+ }
+
+NS_IMETHODIMP nsXPCTestParams::TestBoolean(bool a, bool* b, bool* _retval) {
+ GENERIC_METHOD_IMPL;
+}
+
+NS_IMETHODIMP nsXPCTestParams::TestOctet(uint8_t a, uint8_t* b,
+ uint8_t* _retval) {
+ GENERIC_METHOD_IMPL;
+}
+
+NS_IMETHODIMP nsXPCTestParams::TestShort(int16_t a, int16_t* b,
+ int16_t* _retval) {
+ GENERIC_METHOD_IMPL;
+}
+
+NS_IMETHODIMP nsXPCTestParams::TestLong(int32_t a, int32_t* b,
+ int32_t* _retval) {
+ GENERIC_METHOD_IMPL;
+}
+
+NS_IMETHODIMP nsXPCTestParams::TestLongLong(int64_t a, int64_t* b,
+ int64_t* _retval) {
+ GENERIC_METHOD_IMPL;
+}
+
+NS_IMETHODIMP nsXPCTestParams::TestUnsignedShort(uint16_t a, uint16_t* b,
+ uint16_t* _retval) {
+ GENERIC_METHOD_IMPL;
+}
+
+NS_IMETHODIMP nsXPCTestParams::TestUnsignedLong(uint32_t a, uint32_t* b,
+ uint32_t* _retval) {
+ GENERIC_METHOD_IMPL;
+}
+
+NS_IMETHODIMP nsXPCTestParams::TestUnsignedLongLong(uint64_t a, uint64_t* b,
+ uint64_t* _retval) {
+ GENERIC_METHOD_IMPL;
+}
+
+NS_IMETHODIMP nsXPCTestParams::TestFloat(float a, float* b, float* _retval) {
+ GENERIC_METHOD_IMPL;
+}
+
+NS_IMETHODIMP nsXPCTestParams::TestDouble(double a, float* b, double* _retval) {
+ GENERIC_METHOD_IMPL;
+}
+
+NS_IMETHODIMP nsXPCTestParams::TestChar(char a, char* b, char* _retval) {
+ GENERIC_METHOD_IMPL;
+}
+
+NS_IMETHODIMP nsXPCTestParams::TestString(const char* a, char** b,
+ char** _retval) {
+ nsDependentCString aprime(a);
+ nsDependentCString bprime(*b);
+ *_retval = ToNewCString(bprime);
+ *b = ToNewCString(aprime);
+
+ // XPCOM ownership rules dictate that overwritten inout params must be
+ // callee-freed. See https://developer.mozilla.org/en/XPIDL
+ free(const_cast<char*>(bprime.get()));
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsXPCTestParams::TestWchar(char16_t a, char16_t* b,
+ char16_t* _retval) {
+ GENERIC_METHOD_IMPL;
+}
+
+NS_IMETHODIMP nsXPCTestParams::TestWstring(const char16_t* a, char16_t** b,
+ char16_t** _retval) {
+ nsDependentString aprime(a);
+ nsDependentString bprime(*b);
+ *_retval = ToNewUnicode(bprime);
+ *b = ToNewUnicode(aprime);
+
+ // XPCOM ownership rules dictate that overwritten inout params must be
+ // callee-freed. See https://developer.mozilla.org/en/XPIDL
+ free((void*)bprime.get());
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsXPCTestParams::TestAString(const nsAString& a, nsAString& b,
+ nsAString& _retval) {
+ STRING_METHOD_IMPL;
+}
+
+NS_IMETHODIMP nsXPCTestParams::TestAUTF8String(const nsACString& a,
+ nsACString& b,
+ nsACString& _retval) {
+ STRING_METHOD_IMPL;
+}
+
+NS_IMETHODIMP nsXPCTestParams::TestACString(const nsACString& a, nsACString& b,
+ nsACString& _retval) {
+ STRING_METHOD_IMPL;
+}
+
+NS_IMETHODIMP nsXPCTestParams::TestJsval(JS::Handle<JS::Value> a,
+ JS::MutableHandle<JS::Value> b,
+ JS::MutableHandle<JS::Value> _retval) {
+ _retval.set(b);
+ b.set(a);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsXPCTestParams::TestShortArray(uint32_t aLength, int16_t* a,
+ uint32_t* bLength, int16_t** b,
+ uint32_t* rvLength,
+ int16_t** rv) {
+ BUFFER_METHOD_IMPL(int16_t, 0, TAKE_OWNERSHIP_NOOP);
+}
+
+NS_IMETHODIMP nsXPCTestParams::TestDoubleArray(uint32_t aLength, double* a,
+ uint32_t* bLength, double** b,
+ uint32_t* rvLength,
+ double** rv) {
+ BUFFER_METHOD_IMPL(double, 0, TAKE_OWNERSHIP_NOOP);
+}
+
+NS_IMETHODIMP nsXPCTestParams::TestByteArrayOptionalLength(uint8_t* a,
+ uint32_t aLength,
+ uint32_t* rv) {
+ *rv = aLength;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsXPCTestParams::TestStringArray(uint32_t aLength, const char** a,
+ uint32_t* bLength, char*** b,
+ uint32_t* rvLength, char*** rv) {
+ BUFFER_METHOD_IMPL(char*, 0, TAKE_OWNERSHIP_STRING);
+}
+
+NS_IMETHODIMP nsXPCTestParams::TestWstringArray(
+ uint32_t aLength, const char16_t** a, uint32_t* bLength, char16_t*** b,
+ uint32_t* rvLength, char16_t*** rv) {
+ BUFFER_METHOD_IMPL(char16_t*, 0, TAKE_OWNERSHIP_WSTRING);
+}
+
+NS_IMETHODIMP nsXPCTestParams::TestInterfaceArray(
+ uint32_t aLength, nsIXPCTestInterfaceA** a, uint32_t* bLength,
+ nsIXPCTestInterfaceA*** b, uint32_t* rvLength, nsIXPCTestInterfaceA*** rv) {
+ BUFFER_METHOD_IMPL(nsIXPCTestInterfaceA*, 0, TAKE_OWNERSHIP_INTERFACE);
+}
+
+NS_IMETHODIMP nsXPCTestParams::TestJsvalArray(uint32_t aLength, JS::Value* a,
+ uint32_t* bLength, JS::Value** b,
+ uint32_t* rvLength,
+ JS::Value** rv) {
+ BUFFER_METHOD_IMPL(JS::Value, 0, TAKE_OWNERSHIP_NOOP);
+}
+
+NS_IMETHODIMP nsXPCTestParams::TestSizedString(uint32_t aLength, const char* a,
+ uint32_t* bLength, char** b,
+ uint32_t* rvLength, char** rv) {
+ BUFFER_METHOD_IMPL(char, 1, TAKE_OWNERSHIP_NOOP);
+}
+
+NS_IMETHODIMP nsXPCTestParams::TestSizedWstring(uint32_t aLength,
+ const char16_t* a,
+ uint32_t* bLength, char16_t** b,
+ uint32_t* rvLength,
+ char16_t** rv) {
+ BUFFER_METHOD_IMPL(char16_t, 1, TAKE_OWNERSHIP_NOOP);
+}
+
+NS_IMETHODIMP nsXPCTestParams::TestInterfaceIs(const nsIID* aIID, void* a,
+ nsIID** bIID, void** b,
+ nsIID** rvIID, void** rv) {
+ //
+ // Getting the buffers and ownership right here can be a little tricky.
+ //
+
+ // The interface pointers are heap-allocated, and b has been AddRef'd
+ // by XPConnect for the duration of the call. If we snatch it away from b
+ // and leave no trace, XPConnect won't Release it. Since we also need to
+ // return an already-AddRef'd pointer in rv, we don't need to do anything
+ // special here.
+ *rv = *b;
+
+ // rvIID is out-only, so nobody allocated an IID buffer for us. Do that now,
+ // and store b's IID in the new buffer.
+ *rvIID = static_cast<nsIID*>(moz_xmalloc(sizeof(nsID)));
+ **rvIID = **bIID;
+
+ // Copy the interface pointer from a to b. Since a is in-only, XPConnect will
+ // release it upon completion of the call. AddRef it for b.
+ *b = a;
+ static_cast<nsISupports*>(*b)->AddRef();
+
+ // We already had a buffer allocated for b's IID, so we can re-use it.
+ **bIID = *aIID;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsXPCTestParams::TestInterfaceIsArray(
+ uint32_t aLength, const nsIID* aIID, void** a, uint32_t* bLength,
+ nsIID** bIID, void*** b, uint32_t* rvLength, nsIID** rvIID, void*** rv) {
+ // Transfer the IIDs. See the comments in TestInterfaceIs (above) for an
+ // explanation of what we're doing.
+ *rvIID = static_cast<nsIID*>(moz_xmalloc(sizeof(nsID)));
+ **rvIID = **bIID;
+ **bIID = *aIID;
+
+ // The macro is agnostic to the actual interface types, so we can re-use code
+ // here.
+ //
+ // Do this second, since the macro returns.
+ BUFFER_METHOD_IMPL(void*, 0, TAKE_OWNERSHIP_INTERFACE);
+}
+
+NS_IMETHODIMP nsXPCTestParams::TestOutAString(nsAString& o) {
+ o.AssignLiteral("out");
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsXPCTestParams::TestStringArrayOptionalSize(const char** a,
+ uint32_t length,
+ nsACString& out) {
+ out.Truncate();
+ for (uint32_t i = 0; i < length; ++i) {
+ out.Append(a[i]);
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXPCTestParams::TestShortSequence(const nsTArray<short>& a, nsTArray<short>& b,
+ nsTArray<short>& _retval) {
+ SEQUENCE_METHOD_IMPL(TAKE_OWNERSHIP_NOOP);
+}
+
+NS_IMETHODIMP
+nsXPCTestParams::TestDoubleSequence(const nsTArray<double>& a,
+ nsTArray<double>& b,
+ nsTArray<double>& _retval) {
+ SEQUENCE_METHOD_IMPL(TAKE_OWNERSHIP_NOOP);
+}
+
+NS_IMETHODIMP
+nsXPCTestParams::TestInterfaceSequence(
+ const nsTArray<RefPtr<nsIXPCTestInterfaceA>>& a,
+ nsTArray<RefPtr<nsIXPCTestInterfaceA>>& b,
+ nsTArray<RefPtr<nsIXPCTestInterfaceA>>& _retval) {
+ SEQUENCE_METHOD_IMPL(TAKE_OWNERSHIP_NOOP);
+}
+
+NS_IMETHODIMP
+nsXPCTestParams::TestAStringSequence(const nsTArray<nsString>& a,
+ nsTArray<nsString>& b,
+ nsTArray<nsString>& _retval) {
+ SEQUENCE_METHOD_IMPL(TAKE_OWNERSHIP_NOOP);
+}
+
+NS_IMETHODIMP
+nsXPCTestParams::TestACStringSequence(const nsTArray<nsCString>& a,
+ nsTArray<nsCString>& b,
+ nsTArray<nsCString>& _retval) {
+ SEQUENCE_METHOD_IMPL(TAKE_OWNERSHIP_NOOP);
+}
+
+NS_IMETHODIMP
+nsXPCTestParams::TestJsvalSequence(const nsTArray<JS::Value>& a,
+ nsTArray<JS::Value>& b,
+ nsTArray<JS::Value>& _retval) {
+ SEQUENCE_METHOD_IMPL(TAKE_OWNERSHIP_NOOP);
+}
+
+NS_IMETHODIMP
+nsXPCTestParams::TestSequenceSequence(const nsTArray<nsTArray<short>>& a,
+ nsTArray<nsTArray<short>>& b,
+ nsTArray<nsTArray<short>>& _retval) {
+ _retval = std::move(b);
+ for (const auto& element : a) {
+ b.AppendElement(element.Clone());
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXPCTestParams::TestInterfaceIsSequence(const nsIID* aIID,
+ const nsTArray<void*>& a, nsIID** bIID,
+ nsTArray<void*>& b, nsIID** rvIID,
+ nsTArray<void*>& _retval) {
+ // Shuffle around our nsIIDs
+ *rvIID = (*bIID)->Clone();
+ *bIID = aIID->Clone();
+
+ // Perform the generic sequence shuffle.
+ SEQUENCE_METHOD_IMPL(TAKE_OWNERSHIP_INTERFACE);
+}
+
+NS_IMETHODIMP
+nsXPCTestParams::TestOptionalSequence(const nsTArray<uint8_t>& aInArr,
+ nsTArray<uint8_t>& aReturnArr) {
+ aReturnArr = aInArr.Clone();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXPCTestParams::TestOmittedOptionalOut(nsIXPCTestParams* aJSObj,
+ nsIURI** aOut) {
+ MOZ_ASSERT(!(*aOut), "Unexpected value received");
+ // Call the js component, to check XPConnect won't crash when passing nullptr
+ // as the optional out parameter, and that the out object is built regardless.
+ nsresult rv;
+ // Invoke it directly passing nullptr.
+ rv = aJSObj->TestOmittedOptionalOut(nullptr, nullptr);
+ NS_ENSURE_SUCCESS(rv, rv);
+ // Also invoke it with a ref pointer.
+ nsCOMPtr<nsIURI> someURI;
+ rv = aJSObj->TestOmittedOptionalOut(nullptr, getter_AddRefs(someURI));
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsAutoCString spec;
+ rv = someURI->GetSpec(spec);
+ if (!spec.EqualsLiteral("http://example.com/")) {
+ return NS_ERROR_UNEXPECTED;
+ }
+ someURI.forget(aOut);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXPCTestParams::GetTestNaN(double* aResult) {
+ *aResult =
+ BitwiseCast<double>((uint64_t(JSVAL_TAG_OBJECT) << JSVAL_TAG_SHIFT) + 1);
+ return NS_OK;
+}
diff --git a/js/xpconnect/tests/components/native/xpctest_private.h b/js/xpconnect/tests/components/native/xpctest_private.h
new file mode 100644
index 0000000000..c5d7bc86cf
--- /dev/null
+++ b/js/xpconnect/tests/components/native/xpctest_private.h
@@ -0,0 +1,102 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* local header for xpconnect tests components */
+
+#ifndef xpctest_private_h___
+#define xpctest_private_h___
+
+#include "nsISupports.h"
+#include "nsString.h"
+#include "xpctest_attributes.h"
+#include "xpctest_params.h"
+#include "xpctest_returncode.h"
+#include "xpctest_cenums.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/ModuleUtils.h"
+
+nsresult xpcTestRegisterComponents();
+
+class xpcTestObjectReadOnly final : public nsIXPCTestObjectReadOnly {
+ public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIXPCTESTOBJECTREADONLY
+ xpcTestObjectReadOnly();
+
+ private:
+ ~xpcTestObjectReadOnly() = default;
+
+ bool boolProperty;
+ int16_t shortProperty;
+ int32_t longProperty;
+ float floatProperty;
+ char charProperty;
+ PRTime timeProperty;
+};
+
+class xpcTestObjectReadWrite final : public nsIXPCTestObjectReadWrite {
+ public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIXPCTESTOBJECTREADWRITE
+
+ xpcTestObjectReadWrite();
+
+ private:
+ ~xpcTestObjectReadWrite();
+
+ bool boolProperty;
+ int16_t shortProperty;
+ int32_t longProperty;
+ float floatProperty;
+ char charProperty;
+ char* stringProperty;
+ PRTime timeProperty;
+};
+
+class nsXPCTestParams final : public nsIXPCTestParams {
+ public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIXPCTESTPARAMS
+
+ nsXPCTestParams() = default;
+
+ private:
+ ~nsXPCTestParams() = default;
+};
+
+class nsXPCTestReturnCodeParent final : public nsIXPCTestReturnCodeParent {
+ public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIXPCTESTRETURNCODEPARENT
+
+ nsXPCTestReturnCodeParent() = default;
+
+ private:
+ ~nsXPCTestReturnCodeParent() = default;
+};
+
+class nsXPCTestESMReturnCodeParent final : public nsIXPCTestReturnCodeParent {
+ public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIXPCTESTRETURNCODEPARENT
+
+ nsXPCTestESMReturnCodeParent() = default;
+
+ private:
+ ~nsXPCTestESMReturnCodeParent() = default;
+};
+
+class xpcTestCEnums final : public nsIXPCTestCEnums {
+ public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIXPCTESTCENUMS
+
+ xpcTestCEnums();
+
+ private:
+ ~xpcTestCEnums() = default;
+};
+#endif /* xpctest_private_h___ */
diff --git a/js/xpconnect/tests/components/native/xpctest_returncode.cpp b/js/xpconnect/tests/components/native/xpctest_returncode.cpp
new file mode 100644
index 0000000000..3a52f616d9
--- /dev/null
+++ b/js/xpconnect/tests/components/native/xpctest_returncode.cpp
@@ -0,0 +1,20 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "xpctest_private.h"
+#include "nsComponentManagerUtils.h"
+#include "nsImportModule.h"
+
+NS_IMPL_ISUPPORTS(nsXPCTestReturnCodeParent, nsIXPCTestReturnCodeParent)
+
+NS_IMETHODIMP nsXPCTestReturnCodeParent::CallChild(int32_t childBehavior,
+ nsresult* _retval) {
+ nsresult rv;
+ nsCOMPtr<nsIXPCTestReturnCodeChild> child(do_ImportModule(
+ "resource://test/ReturnCodeChild.jsm", "ReturnCodeChild", &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = child->DoIt(childBehavior);
+ *_retval = rv;
+ return NS_OK;
+}
diff --git a/js/xpconnect/tests/idl/moz.build b/js/xpconnect/tests/idl/moz.build
new file mode 100644
index 0000000000..8b56f40c21
--- /dev/null
+++ b/js/xpconnect/tests/idl/moz.build
@@ -0,0 +1,17 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+XPIDL_SOURCES += [
+ "xpctest_attributes.idl",
+ "xpctest_bug809674.idl",
+ "xpctest_cenums.idl",
+ "xpctest_interfaces.idl",
+ "xpctest_params.idl",
+ "xpctest_returncode.idl",
+ "xpctest_utils.idl",
+]
+
+XPIDL_MODULE = "xpctest"
diff --git a/js/xpconnect/tests/idl/xpctest_attributes.idl b/js/xpconnect/tests/idl/xpctest_attributes.idl
new file mode 100644
index 0000000000..9822b24dfb
--- /dev/null
+++ b/js/xpconnect/tests/idl/xpctest_attributes.idl
@@ -0,0 +1,33 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+/*
+ * This defines the interface for a test object.
+ *
+ */
+
+[scriptable, uuid(42fbd9f6-b12d-47ef-b7a1-02d73c11fe53)]
+interface nsIXPCTestObjectReadOnly : nsISupports {
+ readonly attribute string strReadOnly;
+ readonly attribute boolean boolReadOnly;
+ readonly attribute short shortReadOnly;
+ readonly attribute long longReadOnly;
+ readonly attribute float floatReadOnly;
+ readonly attribute char charReadOnly;
+ readonly attribute PRTime timeReadOnly;
+};
+
+[scriptable, uuid(f07529b0-a479-4954-aba5-ab3142c6b1cb)]
+interface nsIXPCTestObjectReadWrite : nsISupports {
+ attribute string stringProperty;
+ attribute boolean booleanProperty;
+ attribute short shortProperty;
+ attribute long longProperty;
+ attribute float floatProperty;
+ attribute char charProperty;
+ attribute PRTime timeProperty;
+};
diff --git a/js/xpconnect/tests/idl/xpctest_bug809674.idl b/js/xpconnect/tests/idl/xpctest_bug809674.idl
new file mode 100644
index 0000000000..1e83e244ec
--- /dev/null
+++ b/js/xpconnect/tests/idl/xpctest_bug809674.idl
@@ -0,0 +1,47 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+/*
+ * Test interface for https://bugzilla.mozilla.org/show_bug.cgi?id=809674 .
+ *
+ * This test makes sure that accessing JS-implemented attributes or methods
+ * marked with [implicit_jscontext] works as expected.
+ *
+ * It also makes sure [optional_argc] is not supported on JS-implemented
+ * methods.
+ */
+
+[scriptable, uuid(2df46559-da21-49bf-b863-0d7b7bbcbc73)]
+interface nsIXPCTestBug809674 : nsISupports {
+ // Various interesting [implicit_jscontext] cases.
+ [implicit_jscontext] unsigned long addArgs(in unsigned long x, in unsigned long y);
+ [implicit_jscontext] unsigned long addSubMulArgs(in unsigned long x, in unsigned long y,
+ out unsigned long subOut,
+ out unsigned long mulOut);
+ [implicit_jscontext] jsval addVals(in jsval x, in jsval y);
+
+ [implicit_jscontext] unsigned long methodNoArgs();
+ [implicit_jscontext] void methodNoArgsNoRetVal();
+
+ // When there are many arguments, the context is passed on the stack on
+ // most platforms.
+ [implicit_jscontext] unsigned long addMany(in unsigned long x1,
+ in unsigned long x2,
+ in unsigned long x3,
+ in unsigned long x4,
+ in unsigned long x5,
+ in unsigned long x6,
+ in unsigned long x7,
+ in unsigned long x8);
+
+ // Attributes can use [implicit_jscontext], too.
+ [implicit_jscontext] attribute jsval valProperty;
+ [implicit_jscontext] attribute unsigned long uintProperty;
+
+ // [optional_argc] is not supported.
+ [optional_argc] void methodWithOptionalArgc();
+};
diff --git a/js/xpconnect/tests/idl/xpctest_cenums.idl b/js/xpconnect/tests/idl/xpctest_cenums.idl
new file mode 100644
index 0000000000..70b7f8fae7
--- /dev/null
+++ b/js/xpconnect/tests/idl/xpctest_cenums.idl
@@ -0,0 +1,39 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+/*
+ * This defines the interface for a test object.
+ *
+ */
+
+[scriptable, uuid(6a2f918e-cda2-11e8-bc9a-a34c716d1f2a)]
+interface nsIXPCTestCEnums : nsISupports {
+ const long testConst = 1;
+
+ cenum testFlagsExplicit: 8 {
+ shouldBe1Explicit = 1,
+ shouldBe2Explicit = 2,
+ shouldBe4Explicit = 4,
+ shouldBe8Explicit = 8,
+ shouldBe12Explicit = shouldBe4Explicit | shouldBe8Explicit,
+ };
+
+ cenum testFlagsImplicit: 8 {
+ shouldBe0Implicit,
+ shouldBe1Implicit,
+ shouldBe2Implicit,
+ shouldBe3Implicit,
+ shouldBe5Implicit = 5,
+ shouldBe6Implicit,
+ shouldBe2AgainImplicit = 2,
+ shouldBe3AgainImplicit,
+ };
+
+ void testCEnumInput(in nsIXPCTestCEnums_testFlagsExplicit abc);
+
+ nsIXPCTestCEnums_testFlagsExplicit testCEnumOutput();
+};
diff --git a/js/xpconnect/tests/idl/xpctest_esmreturncode.idl b/js/xpconnect/tests/idl/xpctest_esmreturncode.idl
new file mode 100644
index 0000000000..ac17feda3f
--- /dev/null
+++ b/js/xpconnect/tests/idl/xpctest_esmreturncode.idl
@@ -0,0 +1,45 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/**
+ * Test the use of Components.returnCode with system ESM
+ *
+ * This ("parent") interface defines a method that in-turn calls another
+ * ("child") interface implemented in JS, and returns the nsresult from that
+ * child interface. The child interface manages the return code by way of
+ * Components.returnCode.
+ */
+
+#include "nsISupports.idl"
+
+
+[scriptable, uuid(494f9336-ad06-46ad-bbb4-b0010e27e12d)]
+interface nsIXPCTestESMReturnCodeParent : nsISupports {
+ // Calls the "child" interface with the specified behavior flag. Returns
+ // the NSRESULT from the child interface.
+ nsresult callChild(in long childBehavior);
+};
+
+[scriptable, uuid(dee07408-75d8-4968-a37c-fe0d48ccd1ac)]
+interface nsIXPCTestESMReturnCodeChild : nsISupports {
+ void doIt(in long behavior);
+
+ // Flags to control that the child does.
+ // child will throw a JS exception
+ const long CHILD_SHOULD_THROW = 0;
+
+ // child will just return normally
+ const long CHILD_SHOULD_RETURN_SUCCESS = 1;
+
+ // child will return after setting Components.returnCode to NS_ERROR_FAILURE
+ const long CHILD_SHOULD_RETURN_RESULTCODE = 2;
+
+ // child will set Components.returnCode to NS_ERROR_UNEXPECTED, then create
+ // a new component that sets Components.returnCode to NS_ERROR_FAILURE.
+ // Our caller should see the NS_ERROR_UNEXPECTED we set rather than the
+ // value set later by the "inner" child.
+ const long CHILD_SHOULD_NEST_RESULTCODES = 3;
+};
diff --git a/js/xpconnect/tests/idl/xpctest_interfaces.idl b/js/xpconnect/tests/idl/xpctest_interfaces.idl
new file mode 100644
index 0000000000..2abc149623
--- /dev/null
+++ b/js/xpconnect/tests/idl/xpctest_interfaces.idl
@@ -0,0 +1,27 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/**
+ * Very simple test interfaces.
+ *
+ * This is used by the other test functionality when it needs to play around with
+ * interface pointers.
+ */
+
+#include "nsISupports.idl"
+
+[scriptable, uuid(3c8fd2f5-970c-42c6-b5dd-cda1c16dcfd8)]
+interface nsIXPCTestInterfaceA : nsISupports {
+ attribute string name;
+};
+
+[scriptable, uuid(ff528c3a-2410-46de-acaa-449aa6403a33)]
+interface nsIXPCTestInterfaceB : nsISupports {
+ attribute string name;
+};
+
+[scriptable, uuid(401cf1b4-355b-4cee-b7b3-c7973aee49bd)]
+interface nsIXPCTestInterfaceC : nsISupports {
+ attribute long someInteger;
+};
diff --git a/js/xpconnect/tests/idl/xpctest_params.idl b/js/xpconnect/tests/idl/xpctest_params.idl
new file mode 100644
index 0000000000..8bf224507c
--- /dev/null
+++ b/js/xpconnect/tests/idl/xpctest_params.idl
@@ -0,0 +1,120 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/**
+ * Test pararameter passing and argument conversion.
+ *
+ * Each test method returns the value in 'b', and copies 'a' into 'b'. This lets
+ * us test return values, in params, and inout params (out params should be
+ * covered by the intersection of return values and inout).
+ */
+
+#include "nsISupports.idl"
+
+interface nsIURI;
+interface nsIXPCTestInterfaceA;
+interface nsIXPCTestInterfaceB;
+
+[scriptable, uuid(812145c7-9fcc-425e-a878-36ad1b7730b7)]
+interface nsIXPCTestParams : nsISupports {
+
+ // These types correspond to the ones in typelib.py
+ boolean testBoolean(in boolean a, inout boolean b);
+ octet testOctet(in octet a, inout octet b);
+ short testShort(in short a, inout short b);
+ long testLong(in long a, inout long b);
+ long long testLongLong(in long long a, inout long long b);
+ unsigned short testUnsignedShort(in unsigned short a, inout unsigned short b);
+ unsigned long testUnsignedLong(in unsigned long a, inout unsigned long b);
+ unsigned long long testUnsignedLongLong(in unsigned long long a, inout unsigned long long b);
+ float testFloat(in float a, inout float b);
+ double testDouble(in double a, inout float b);
+ char testChar(in char a, inout char b);
+ string testString(in string a, inout string b);
+ wchar testWchar(in wchar a, inout wchar b);
+ wstring testWstring(in wstring a, inout wstring b);
+ AString testAString(in AString a, inout AString b);
+ AUTF8String testAUTF8String(in AUTF8String a, inout AUTF8String b);
+ ACString testACString(in ACString a, inout ACString b);
+ jsval testJsval(in jsval a, inout jsval b);
+
+ // Test various forms of the Array<T> type.
+ Array<short> testShortSequence(in Array<short> a, inout Array<short> b);
+ Array<double> testDoubleSequence(in Array<double> a, inout Array<double> b);
+ Array<nsIXPCTestInterfaceA> testInterfaceSequence(in Array<nsIXPCTestInterfaceA> a, inout Array<nsIXPCTestInterfaceA> b);
+ Array<AString> testAStringSequence(in Array<AString> a, inout Array<AString> b);
+ Array<ACString> testACStringSequence(in Array<ACString> a, inout Array<ACString> b);
+ Array<jsval> testJsvalSequence(in Array<jsval> a, inout Array<jsval> b);
+ Array<Array<short> > testSequenceSequence(in Array<Array<short> > a, inout Array<Array<short> > b);
+
+ void testInterfaceIsSequence(in nsIIDPtr aIID, [iid_is(aIID)] in Array<nsQIResult> a,
+ inout nsIIDPtr bIID, [iid_is(bIID)] inout Array<nsQIResult> b,
+ out nsIIDPtr rvIID, [retval, iid_is(rvIID)] out Array<nsQIResult> rv);
+
+ // Returns whatever was passed in.
+ Array<uint8_t> testOptionalSequence([optional] in Array<uint8_t> arr);
+
+ //
+ // Dependent parameters use the same types as above, but are handled much differently.
+ //
+
+ // Test arrays.
+ void testShortArray(in unsigned long aLength, [array, size_is(aLength)] in short a,
+ inout unsigned long bLength, [array, size_is(bLength)] inout short b,
+ out unsigned long rvLength, [retval, array, size_is(rvLength)] out short rv);
+ void testDoubleArray(in unsigned long aLength, [array, size_is(aLength)] in double a,
+ inout unsigned long bLength, [array, size_is(bLength)] inout double b,
+ out unsigned long rvLength, [retval, array, size_is(rvLength)] out double rv);
+ void testStringArray(in unsigned long aLength, [array, size_is(aLength)] in string a,
+ inout unsigned long bLength, [array, size_is(bLength)] inout string b,
+ out unsigned long rvLength, [retval, array, size_is(rvLength)] out string rv);
+ void testWstringArray(in unsigned long aLength, [array, size_is(aLength)] in wstring a,
+ inout unsigned long bLength, [array, size_is(bLength)] inout wstring b,
+ out unsigned long rvLength, [retval, array, size_is(rvLength)] out wstring rv);
+ void testInterfaceArray(in unsigned long aLength, [array, size_is(aLength)] in nsIXPCTestInterfaceA a,
+ inout unsigned long bLength, [array, size_is(bLength)] inout nsIXPCTestInterfaceA b,
+ out unsigned long rvLength, [retval, array, size_is(rvLength)] out nsIXPCTestInterfaceA rv);
+
+ // uint8 array with optional length. Returns array length.
+ unsigned long testByteArrayOptionalLength([array, size_is(aLength)] in uint8_t a, [optional] in unsigned long aLength);
+
+ // Test sized strings.
+ void testSizedString(in unsigned long aLength, [size_is(aLength)] in string a,
+ inout unsigned long bLength, [size_is(bLength)] inout string b,
+ out unsigned long rvLength, [retval, size_is(rvLength)] out string rv);
+ void testSizedWstring(in unsigned long aLength, [size_is(aLength)] in wstring a,
+ inout unsigned long bLength, [size_is(bLength)] inout wstring b,
+ out unsigned long rvLength, [retval, size_is(rvLength)] out wstring rv);
+
+ // Test iid_is.
+ void testInterfaceIs(in nsIIDPtr aIID, [iid_is(aIID)] in nsQIResult a,
+ inout nsIIDPtr bIID, [iid_is(bIID)] inout nsQIResult b,
+ out nsIIDPtr rvIID, [retval, iid_is(rvIID)] out nsQIResult rv);
+
+ // Test arrays of iid_is. According to khuey we don't use it for anything
+ // in mozilla-central, but calendar stuff depends on it.
+ void testInterfaceIsArray(in unsigned long aLength, in nsIIDPtr aIID,
+ [array, size_is(aLength), iid_is(aIID)] in nsQIResult a,
+ inout unsigned long bLength, inout nsIIDPtr bIID,
+ [array, size_is(bLength), iid_is(bIID)] inout nsQIResult b,
+ out unsigned long rvLength, out nsIIDPtr rvIID,
+ [retval, array, size_is(rvLength), iid_is(rvIID)] out nsQIResult rv);
+
+ // Test arrays of jsvals
+ void testJsvalArray(in unsigned long aLength, [array, size_is(aLength)] in jsval a,
+ inout unsigned long bLength, [array, size_is(bLength)] inout jsval b,
+ out unsigned long rvLength, [retval, array, size_is(rvLength)] out jsval rv);
+
+
+ // Test for out dipper parameters
+ void testOutAString(out AString o);
+
+ // Test for optional array size_is.
+ ACString testStringArrayOptionalSize([array, size_is(aLength)] in string a, [optional] in unsigned long aLength);
+
+ // Test for omitted optional out parameter.
+ void testOmittedOptionalOut(in nsIXPCTestParams aJSObj, [optional] out nsIURI aOut);
+
+ readonly attribute double testNaN;
+};
diff --git a/js/xpconnect/tests/idl/xpctest_returncode.idl b/js/xpconnect/tests/idl/xpctest_returncode.idl
new file mode 100644
index 0000000000..5ee6c55479
--- /dev/null
+++ b/js/xpconnect/tests/idl/xpctest_returncode.idl
@@ -0,0 +1,45 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/**
+ * Test the use of Components.returnCode
+ *
+ * This ("parent") interface defines a method that in-turn calls another
+ * ("child") interface implemented in JS, and returns the nsresult from that
+ * child interface. The child interface manages the return code by way of
+ * Components.returnCode.
+ */
+
+#include "nsISupports.idl"
+
+
+[scriptable, uuid(479e4532-95cf-48b8-a99b-8a5881e47138)]
+interface nsIXPCTestReturnCodeParent : nsISupports {
+ // Calls the "child" interface with the specified behavior flag. Returns
+ // the NSRESULT from the child interface.
+ nsresult callChild(in long childBehavior);
+};
+
+[scriptable, uuid(672cfd34-1fd1-455d-9901-d879fa6fdb95)]
+interface nsIXPCTestReturnCodeChild : nsISupports {
+ void doIt(in long behavior);
+
+ // Flags to control that the child does.
+ // child will throw a JS exception
+ const long CHILD_SHOULD_THROW = 0;
+
+ // child will just return normally
+ const long CHILD_SHOULD_RETURN_SUCCESS = 1;
+
+ // child will return after setting Components.returnCode to NS_ERROR_FAILURE
+ const long CHILD_SHOULD_RETURN_RESULTCODE = 2;
+
+ // child will set Components.returnCode to NS_ERROR_UNEXPECTED, then create
+ // a new component that sets Components.returnCode to NS_ERROR_FAILURE.
+ // Our caller should see the NS_ERROR_UNEXPECTED we set rather than the
+ // value set later by the "inner" child.
+ const long CHILD_SHOULD_NEST_RESULTCODES = 3;
+};
diff --git a/js/xpconnect/tests/idl/xpctest_utils.idl b/js/xpconnect/tests/idl/xpctest_utils.idl
new file mode 100644
index 0000000000..e59814272b
--- /dev/null
+++ b/js/xpconnect/tests/idl/xpctest_utils.idl
@@ -0,0 +1,19 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/**
+ * Utility interfaces for testing.
+ */
+
+#include "nsISupports.idl"
+
+[scriptable, function, uuid(d58a82ab-d8f7-4ca9-9273-b3290d42a0cf)]
+interface nsIXPCTestFunctionInterface : nsISupports {
+ string echo(in string arg);
+};
+
+[scriptable, uuid(1e9cddeb-510d-449a-b152-3c1b5b31d41d)]
+interface nsIXPCTestUtils : nsISupports {
+ nsIXPCTestFunctionInterface doubleWrapFunction(in nsIXPCTestFunctionInterface f);
+};
diff --git a/js/xpconnect/tests/marionette/manifest.toml b/js/xpconnect/tests/marionette/manifest.toml
new file mode 100644
index 0000000000..9b772c250a
--- /dev/null
+++ b/js/xpconnect/tests/marionette/manifest.toml
@@ -0,0 +1,3 @@
+[DEFAULT]
+
+["test_preloader_without_parallel_parsing.py"]
diff --git a/js/xpconnect/tests/marionette/test_preloader_without_parallel_parsing.py b/js/xpconnect/tests/marionette/test_preloader_without_parallel_parsing.py
new file mode 100644
index 0000000000..5d3a6b9856
--- /dev/null
+++ b/js/xpconnect/tests/marionette/test_preloader_without_parallel_parsing.py
@@ -0,0 +1,15 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+from marionette_harness import MarionetteTestCase
+
+
+class PreloaderWithoutParallelParsingTestCase(MarionetteTestCase):
+ def test_restart_with_preloader_cache_without_parallel_parsing(self):
+ self.marionette.set_pref("javascript.options.parallel_parsing", False)
+
+ self.marionette.restart()
+
+ # Make sure no crash happens until shutting down.
+ self.marionette.quit()
diff --git a/js/xpconnect/tests/mochitest/bug1681664_helper.js b/js/xpconnect/tests/mochitest/bug1681664_helper.js
new file mode 100644
index 0000000000..14f2289a19
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/bug1681664_helper.js
@@ -0,0 +1 @@
+while (true) {};
diff --git a/js/xpconnect/tests/mochitest/bug500931_helper.html b/js/xpconnect/tests/mochitest/bug500931_helper.html
new file mode 100644
index 0000000000..da268d99d8
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/bug500931_helper.html
@@ -0,0 +1,8 @@
+<html>
+ <head>
+ <title>Inner frame for bug 500931 mochitest</title>
+ <script>x = 42;</script>
+ </head>
+ <body>
+ </body>
+</html>
diff --git a/js/xpconnect/tests/mochitest/bug571849_helper.html b/js/xpconnect/tests/mochitest/bug571849_helper.html
new file mode 100644
index 0000000000..234cd57ccf
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/bug571849_helper.html
@@ -0,0 +1,7 @@
+<html>
+ <head>
+ </head>
+ <body>
+ TEXT NODE TEXT NODE
+ </body>
+</html>
diff --git a/js/xpconnect/tests/mochitest/bug589028_helper.html b/js/xpconnect/tests/mochitest/bug589028_helper.html
new file mode 100644
index 0000000000..dc56ecbc3c
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/bug589028_helper.html
@@ -0,0 +1,27 @@
+<html>
+ <head>
+ <script>
+ function getMyOption() {
+ return new Option();
+ }
+ function getCallersOption(caller) {
+ return new caller.Option();
+ }
+ function getMyAudio() {
+ return new Audio();
+ }
+ function getCallersAudio(caller) {
+ return new caller.Audio();
+ }
+ function getMyImage() {
+ return new Image();
+ }
+ function getCallersImage(caller) {
+ return new caller.Image();
+ }
+ </script>
+ </head>
+ <body>
+ the iframe
+ </body>
+</html>
diff --git a/js/xpconnect/tests/mochitest/bug92773_helper.html b/js/xpconnect/tests/mochitest/bug92773_helper.html
new file mode 100644
index 0000000000..be10dd54ae
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/bug92773_helper.html
@@ -0,0 +1,7 @@
+<html>
+ <head>
+ <script>
+ Object.defineProperty(window, "foo", { get() { alert("FAIL"); } });
+ </script>
+ </head>
+</html>
diff --git a/js/xpconnect/tests/mochitest/chrome_wrappers_helper.html b/js/xpconnect/tests/mochitest/chrome_wrappers_helper.html
new file mode 100644
index 0000000000..a0c1ec87e3
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/chrome_wrappers_helper.html
@@ -0,0 +1,29 @@
+<html>
+ <head>
+ <script>
+ function check_wrapper(ok, wrapper, expected, note) {
+ let { getClassName } = SpecialPowers.unwrap(
+ SpecialPowers.wrap(window).ChromeUtils
+ );
+ ok(getClassName(wrapper, false) === expected, note);
+ }
+ function run_test(ok, xpcnw, sjow) {
+ // both wrappers should point to our window: XOW
+ check_wrapper(ok, ok, "Proxy", "functions are wrapped properly");
+ check_wrapper(ok, xpcnw, "Proxy", "XPCNWs are transformed correctly");
+ check_wrapper(ok, sjow, "Proxy", "SJOWs are transformed correctly");
+
+ check_wrapper(ok, window.location, "Location",
+ "same-compartment security wrappers are gone");
+
+ ok(defprop1 === 1, "defprop1 exists");
+ window.defprop1 = 2;
+ ok(defprop1 === 2, "defprop1 is properly writable");
+
+ // defprop2 = {}; disabled because the test doesn't work
+ }
+ </script>
+ </head>
+ <body>
+ </body>
+</html>
diff --git a/js/xpconnect/tests/mochitest/class_static_worker.js b/js/xpconnect/tests/mochitest/class_static_worker.js
new file mode 100644
index 0000000000..eb949226ba
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/class_static_worker.js
@@ -0,0 +1,13 @@
+class A {
+ static { this.x = 12; }
+}
+
+
+self.onmessage = function (e) {
+ console.log(e)
+ if (e.data == 'get') {
+ postMessage(A.x);
+ return;
+ }
+ postMessage('Unknown message type.');
+} \ No newline at end of file
diff --git a/js/xpconnect/tests/mochitest/file1_bug629227.html b/js/xpconnect/tests/mochitest/file1_bug629227.html
new file mode 100644
index 0000000000..dd12484068
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/file1_bug629227.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <script>
+ function doIt() {
+ var doc = window.frames[0].document;
+ var ok = (doc.form1 == doc.getElementById("test1"));
+ window.parent.postMessage(
+ JSON.stringify({ ok: ok,
+ reason: "Should be able to get named items by name" }),
+ "*");
+ window.parent.postMessage("finish", "*");
+ }
+
+ window.onmessage = function(ev) {
+ if (ev.data == "start") {
+ doIt();
+ }
+ }
+
+ document.domain = "example.org";
+ </script>
+ </head>
+ <body>
+ <iframe id="subframe"></iframe>
+ <script>
+ document.getElementById("subframe").src =
+ "http://test2.example.org" +
+ location.pathname.replace(/file1_bug629227.html/, "file2_bug629227.html");
+ </script>
+ </body>
+</html>
diff --git a/js/xpconnect/tests/mochitest/file2_bug629227.html b/js/xpconnect/tests/mochitest/file2_bug629227.html
new file mode 100644
index 0000000000..02a0540865
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/file2_bug629227.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <script>
+ document.domain = "example.org";
+ </script>
+ </head>
+ <body>
+ <form name="form1" id="test1"></form>
+ </body>
+</html>
diff --git a/js/xpconnect/tests/mochitest/file_bug505915.html b/js/xpconnect/tests/mochitest/file_bug505915.html
new file mode 100644
index 0000000000..5129126914
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/file_bug505915.html
@@ -0,0 +1,10 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+Inner frame for testing bug 505915.
+https://bugzilla.mozilla.org/show_bug.cgi?id=505915
+-->
+<head>
+<body onload="parent.postMessage('', '*');">
+</body>
+</html>
diff --git a/js/xpconnect/tests/mochitest/file_bug605167.html b/js/xpconnect/tests/mochitest/file_bug605167.html
new file mode 100644
index 0000000000..d5253315b8
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/file_bug605167.html
@@ -0,0 +1,7 @@
+<html>
+<body>
+<script>
+ parent.f = function() { return this; };
+</script>
+</body>
+</html>
diff --git a/js/xpconnect/tests/mochitest/file_bug650273.html b/js/xpconnect/tests/mochitest/file_bug650273.html
new file mode 100644
index 0000000000..5fe33e9695
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/file_bug650273.html
@@ -0,0 +1,31 @@
+<!-- test by moz_bug_r_a4@yahoo.com -->
+<body onload="a()">
+<script>
+var targetUrl = "http://example.com/";
+var l;
+
+function a() {
+ var o = {};
+ o.toString = function() {
+ l();
+ return "a";
+ };
+ var f = Object.getOwnPropertyDescriptor(Document.prototype, "title").set;
+ setTimeout(f.bind(document), 0, o);
+}
+
+function l() {
+ var l = false;
+ onunload = function() {
+ l = true;
+ };
+ location = targetUrl;
+ do {
+ var r = new XMLHttpRequest();
+ r.open("GET", location.href, false);
+ r.overrideMimeType("text/plain");
+ try { r.send(null); }
+ catch (e) {}
+ } while (!l);
+}
+</script>
diff --git a/js/xpconnect/tests/mochitest/file_bug658560.html b/js/xpconnect/tests/mochitest/file_bug658560.html
new file mode 100644
index 0000000000..411d31ac73
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/file_bug658560.html
@@ -0,0 +1,4 @@
+<html>
+ <body>
+ </body>
+</html>
diff --git a/js/xpconnect/tests/mochitest/file_bug706301.html b/js/xpconnect/tests/mochitest/file_bug706301.html
new file mode 100644
index 0000000000..805449b4aa
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/file_bug706301.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <script type="application/javascript">
+ window.addEventListener('message', doContentTest);
+
+ function doContentTest() {
+
+ // This has always worked.
+ var nodelist1 = document.getElementsByTagName('details');
+ Object.getOwnPropertyDescriptor(nodelist1, 'length');
+ ok(nodelist1['length'] == 0, "Content should be able to get the length of " +
+ "its own nodelist after calling getOwnPropertyDescriptor.");
+
+ // This is bug 706301.
+ var nodelist2 = document.getElementsByTagName('section');
+ ok(getLengthInChrome(nodelist2), "Chrome should be able to get the length of " +
+ "content nodelist after calling getOwnPropertyDescriptor.");
+
+ // All done.
+ finishTestInChrome();
+ }
+ </script>
+ </head>
+ <body>
+ </body>
+</html>
diff --git a/js/xpconnect/tests/mochitest/file_bug720619.html b/js/xpconnect/tests/mochitest/file_bug720619.html
new file mode 100644
index 0000000000..d198ba1fa3
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/file_bug720619.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <script>
+ valueOf = function() { return "v"; }
+ toString = function() { return "s"; }
+ </script>
+ </head>
+ <body></body>
+</html>
diff --git a/js/xpconnect/tests/mochitest/file_bug731471.html b/js/xpconnect/tests/mochitest/file_bug731471.html
new file mode 100644
index 0000000000..fcfb194cb6
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/file_bug731471.html
@@ -0,0 +1,5 @@
+<html>
+<body>
+1
+</body>
+</html>
diff --git a/js/xpconnect/tests/mochitest/file_bug738244.html b/js/xpconnect/tests/mochitest/file_bug738244.html
new file mode 100644
index 0000000000..a399d9f0e0
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/file_bug738244.html
@@ -0,0 +1,10 @@
+<html>
+<body>
+<form name="form1">
+ <input name="input1" />
+ <input name="appendChild" />
+</form>
+<iframe name="frame1">
+</iframe>
+</body>
+</html>
diff --git a/js/xpconnect/tests/mochitest/file_bug760131.html b/js/xpconnect/tests/mochitest/file_bug760131.html
new file mode 100644
index 0000000000..736732a0a4
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/file_bug760131.html
@@ -0,0 +1,23 @@
+<html>
+<div id="target" ontouchstart="alert();"></div>
+<script type="application/javascript">
+
+/** Test for Bug 760131 **/
+
+function accessTouches(evt)
+{
+ var thrown = false;
+ try {
+ var a = evt.touches;
+ } catch (e) {
+ thrown = true;
+ }
+ ok(!thrown, "Unwrapping a TouchList shouldn't throw");
+}
+
+document.getElementById("target").ontouchstart = accessTouches;
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/js/xpconnect/tests/mochitest/file_bug781476.html b/js/xpconnect/tests/mochitest/file_bug781476.html
new file mode 100644
index 0000000000..745f8818e5
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/file_bug781476.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script type="application/javascript">
+function makeEvent() {
+ var evt = new Event("MouseEvents");
+ evt.expando = 42;
+ is(evt.expando, 42, "Expando properly visible in iframe");
+ return evt;
+}
+</script>
+</head>
+<body>
+</body>
+</html>
diff --git a/js/xpconnect/tests/mochitest/file_bug789713.html b/js/xpconnect/tests/mochitest/file_bug789713.html
new file mode 100644
index 0000000000..4c30fe8275
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/file_bug789713.html
@@ -0,0 +1,51 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=789713
+-->
+<head>
+ <meta charset="utf-8">
+</head>
+<body>
+
+<script type="application/javascript">
+
+/** Test for Bug 789713 **/
+
+function go() {
+ var ifr = document.getElementById('ifr');
+ var pass = true;
+ var doc = ifr.contentDocument;
+ var win = ifr.contentWindow;
+
+ var walker = doc.createTreeWalker(doc.body);
+ pass = pass && (walker.root === doc.body);
+ walker.foo = "expando";
+
+ win.bar = "another-expando";
+
+ // First, do the document.domain operation. This shouldn't crash.
+ document.domain = "example.org";
+
+ // Now make sure we can still access properties on "walker".
+ try {
+ walker.root;
+ pass = pass && walker.foo == "expando";
+ } catch (e) {
+ pass = false;
+ }
+
+ // And make sure we can't access properties on "win", because the
+ // document.domain change revoked the access.
+ try {
+ win.bar;
+ pass = false;
+ } catch (e) { pass = pass && /Permission denied/.exec(e.message); }
+ window.parent.postMessage(pass, '*');
+}
+
+</script>
+<iframe id="ifr" src="file_empty.html" onload="go()"></iframe>
+</pre>
+</body>
+</html>
diff --git a/js/xpconnect/tests/mochitest/file_bug795275.html b/js/xpconnect/tests/mochitest/file_bug795275.html
new file mode 100644
index 0000000000..c3886b8ba8
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/file_bug795275.html
@@ -0,0 +1,14 @@
+<html>
+<head>
+<script type="application/javascript">
+ function touchComponents() {
+ Components;
+ }
+ function touchInterfaces() {
+ Components.interfaces;
+ }
+</script>
+</head>
+<body>
+</body>
+</html>
diff --git a/js/xpconnect/tests/mochitest/file_bug799348.html b/js/xpconnect/tests/mochitest/file_bug799348.html
new file mode 100644
index 0000000000..5800868db0
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/file_bug799348.html
@@ -0,0 +1,11 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<script>
+ var foo = window.open('file_empty.html', '', 'width=550, height=420, status=no, resizable=yes, scrollbars=yes, toolbar=no, left=945, top=225');
+</script>
+</head>
+<body>
+</body>
+</html>
+
diff --git a/js/xpconnect/tests/mochitest/file_bug802557.html b/js/xpconnect/tests/mochitest/file_bug802557.html
new file mode 100644
index 0000000000..39f952bc5b
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/file_bug802557.html
@@ -0,0 +1,62 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script>
+var gTS = window.location.toString;
+var gGHR = Object.getOwnPropertyDescriptor(window.location, 'href').get;
+function getTests(fromOuter) {
+
+ function loc() {
+ return fromOuter ? window.location : location;
+ }
+ return {
+ getLocationImplicit: function() {
+ return loc() + "";
+ },
+ getLocationExplicit: function() {
+ return loc().toString();
+ },
+ getLocationApply1: function() {
+ return gTS.call(loc());
+ },
+ getLocationApply2: function() {
+ return gTS.apply(loc(), []);
+ },
+ getLocationApply3: function() {
+ return Function.call.apply(gTS, [loc()]);
+ },
+ getHref: function() {
+ return loc().href;
+ },
+ getHrefViaApply: function() {
+ return Function.call.apply(gGHR, [loc()]);
+ },
+ }
+};
+
+function mungeNames(obj, suffix) {
+ var rv = {};
+ Object.getOwnPropertyNames(obj)
+ .forEach(name => rv[name + suffix] = obj[name]);
+ return rv;
+}
+
+function mergeObjects(a, b) {
+ var rv = {};
+ Object.getOwnPropertyNames(a).forEach(name => rv[name] = a[name]);
+ Object.getOwnPropertyNames(b).forEach(name => rv[name] = b[name]);
+ return rv;
+}
+
+function getAllTests() {
+ var innerTests = getTests(false);
+ var outerTests = getTests(true);
+ return mergeObjects(mungeNames(innerTests, '_inner'),
+ mungeNames(outerTests, '_outer'));
+}
+
+</script>
+</head>
+<body>
+</body>
+</html>
diff --git a/js/xpconnect/tests/mochitest/file_bug860494.html b/js/xpconnect/tests/mochitest/file_bug860494.html
new file mode 100644
index 0000000000..63a7003796
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/file_bug860494.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="UTF-8">
+<title></title>
+</head>
+<body>
+<iframe name="top"></iframe>
+<iframe name="parent"></iframe>
+<iframe name="location"></iframe>
+<iframe name="length"></iframe>
+<iframe name="window"></iframe>
+<iframe name="navigator"></iframe>
+<iframe name="alert"></iframe>
+</body>
+</html>
diff --git a/js/xpconnect/tests/mochitest/file_crosscompartment_weakmap.html b/js/xpconnect/tests/mochitest/file_crosscompartment_weakmap.html
new file mode 100644
index 0000000000..127c479ebe
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/file_crosscompartment_weakmap.html
@@ -0,0 +1,8 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test Cross-Compartment DOM WeakMaps</title>
+</head>
+<body>
+</body>
+</html>
diff --git a/js/xpconnect/tests/mochitest/file_documentdomain.html b/js/xpconnect/tests/mochitest/file_documentdomain.html
new file mode 100644
index 0000000000..784ed269d0
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/file_documentdomain.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script type="application/javascript">
+
+ function setDomain(domain) {
+ document.domain = domain;
+ }
+
+ function tryToAccess(otherWin) {
+ try {
+ var text = otherWin.document.getElementById('foo').innerHTML;
+ return /Better Late/.exec(text);
+ } catch (e) { return false; }
+ }
+
+ var gRef = null;
+ function storeReference(otherWin) {
+ gRef = otherWin.document.getElementById('foo');
+ }
+
+ function tryToAccessStored() {
+ try {
+ return /Better Late/.exec(gRef.innerHTML);
+ } catch (e) { return false; }
+ }
+
+ function invokingFunctionThrowsSecurityException(name) {
+ try {
+ window[name]();
+ return false;
+ } catch (e) { return /insecure|denied/.test(e); }
+ }
+
+
+</script>
+</head>
+<body>
+<span id="foo">Better Late than Never</span>
+</body>
+</html>
diff --git a/js/xpconnect/tests/mochitest/file_doublewrappedcompartments.html b/js/xpconnect/tests/mochitest/file_doublewrappedcompartments.html
new file mode 100644
index 0000000000..f789a33d76
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/file_doublewrappedcompartments.html
@@ -0,0 +1,10 @@
+<html>
+ <head>
+ <script>
+ // We want to put an expando on the object, but we want this object
+ // to be wrapped in other compartments. This means that the expando
+ // must implement precreate, which happens (in general) for nodes.
+ // So we just do a cyclic reference to the document body.
+ window.expando = document.documentElement;
+ </script>
+ </head>
diff --git a/js/xpconnect/tests/mochitest/file_empty.html b/js/xpconnect/tests/mochitest/file_empty.html
new file mode 100644
index 0000000000..ebe8e56a68
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/file_empty.html
@@ -0,0 +1,3 @@
+<!DOCTYPE html>
+<!-- Note: other tests throughout the tree depend on the layout of this, including the title. Don't make big changes without a try run. -->
+<html><head><title>empty test page</title></head><body><span id="text">Nothing to see here</span><iframe name="subframe"></iframe></body></html>
diff --git a/js/xpconnect/tests/mochitest/file_evalInSandbox.html b/js/xpconnect/tests/mochitest/file_evalInSandbox.html
new file mode 100644
index 0000000000..f53aa1166c
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/file_evalInSandbox.html
@@ -0,0 +1,8 @@
+<html>
+ <body>
+ <script>
+ document.foo = "bar";
+ windowfoo = "windowbar";
+ </script>
+ </body>
+</html>
diff --git a/js/xpconnect/tests/mochitest/file_exnstack.html b/js/xpconnect/tests/mochitest/file_exnstack.html
new file mode 100644
index 0000000000..448e3c0a70
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/file_exnstack.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <script type="application/javascript">
+ window.doThrow = function(other) {
+ if (other)
+ throwAsOuter(other);
+ else
+ throwAsInner();
+ }
+
+ function throwAsInner() {
+ throw Error('look at me go!');
+ }
+
+ function throwAsOuter(other) {
+ other.doThrow(null);
+ }
+ </script>
+</head>
+<body>
+</body>
+</html>
diff --git a/js/xpconnect/tests/mochitest/file_expandosharing.html b/js/xpconnect/tests/mochitest/file_expandosharing.html
new file mode 100644
index 0000000000..ceb4131bb8
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/file_expandosharing.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script type="application/javascript">
+ function setup() {
+ // Set up different target objects for expandos, one for each binding type.
+ window.targetWN = window;
+ window.targetDOM = new XMLHttpRequest();
+ window.targetJS = new Date();
+ }
+
+ function placeExpando(name, val, target) {
+ target[name] = val;
+ }
+
+ // If val === null, then we shouldn't have access.
+ function checkExpando(name, val, target, msg) {
+ if (val !== null) {
+ ok(name in target, msg);
+ try {
+ is(target[name], val, "Got the right expando value");
+ } catch(e) { ok(false, "Threw when accessing same-origin expando"); }
+ }
+ else {
+ ok(!(name in target), msg);
+ }
+ }
+
+</script>
+</head>
+<body onload="setup();">
+ <span>Salut, Ma Cherise. ;-)</span>
+</body>
+</html>
diff --git a/js/xpconnect/tests/mochitest/file_matches.html b/js/xpconnect/tests/mochitest/file_matches.html
new file mode 100644
index 0000000000..0dc101b533
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/file_matches.html
@@ -0,0 +1 @@
+<html><body></body></html>
diff --git a/js/xpconnect/tests/mochitest/file_nodelists.html b/js/xpconnect/tests/mochitest/file_nodelists.html
new file mode 100644
index 0000000000..2195c62ccf
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/file_nodelists.html
@@ -0,0 +1,7 @@
+<html>
+ <body>
+ <p>
+ <p>
+ <p>
+ </body>
+</html>
diff --git a/js/xpconnect/tests/mochitest/file_wrappers-2.html b/js/xpconnect/tests/mochitest/file_wrappers-2.html
new file mode 100644
index 0000000000..e27b07ed6a
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/file_wrappers-2.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html>
+ <body>
+ <script>
+ var obj = {a: 3, next: 1};
+ var to_iterate = Object.create(obj);
+ var enumerate = { 0: 0, "hi": "there" };
+ function func () {};
+ var o = {};
+ var a = [1];
+ </script>
+ </body>
+</html>
diff --git a/js/xpconnect/tests/mochitest/file_xrayic.html b/js/xpconnect/tests/mochitest/file_xrayic.html
new file mode 100644
index 0000000000..ad06a3118b
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/file_xrayic.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script type="application/javascript">
+ function setup() {
+ // Set up targets for sandbox expandos.
+ window.targetDOM = [document.getElementById("hello"), document.getElementById("there")];
+ }
+</script>
+</head>
+<body onload="setup();">
+<span id="hello" class="iamaspan">Hello</span>
+<span id="there" class="iamaspan">There</span>
+</body>
+</html>
diff --git a/js/xpconnect/tests/mochitest/finalizationRegistry_worker.js b/js/xpconnect/tests/mochitest/finalizationRegistry_worker.js
new file mode 100644
index 0000000000..d603cdab38
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/finalizationRegistry_worker.js
@@ -0,0 +1,96 @@
+let holdings1 = [];
+let holdings2 = [];
+let holdings3 = [];
+let holdings4 = [];
+let holdings5 = [];
+
+onmessage = (event) => {
+ switch (event.data) {
+ case 'startTest':
+ startTest();
+ break;
+ case 'checkResults':
+ checkResults();
+ break;
+ default:
+ throw "Unknown message";
+ }
+};
+
+function startTest() {
+ // Registry with no registered objects.
+ let registry1 = new FinalizationRegistry(v => { holdings1.push(v); });
+
+ // Registry with three registered objects.
+ let registry2 = new FinalizationRegistry(v => { holdings2.push(v); });
+ registry2.register({}, 1);
+ registry2.register({}, 2);
+ registry2.register({}, 3);
+
+ // Registry with registered object that is then unregistered.
+ let registry3 = new FinalizationRegistry(v => { holdings3.push(v); });
+ let token3 = {}
+ registry3.register({}, 1, token3);
+ registry3.unregister(token3);
+
+ // Registry with registered object that doesn't die.
+ let registry4 = new FinalizationRegistry(v => { holdings4.push(v); });
+ let object4 = {};
+ registry4.register(object4, 1);
+
+ // Registry observing cyclic JS data structure.
+ let registry5 = new FinalizationRegistry(v => { holdings5.push(v); });
+ registry5.register(makeJSCycle(4), 5);
+
+ const { gc } = getJSTestingFunctions();
+ gc();
+
+ Promise.resolve().then(() => {
+ checkNoCallbacks();
+ });
+
+ postMessage('started');
+}
+
+function checkNoCallbacks() {
+ is(holdings1.length, 0);
+ is(holdings2.length, 0);
+ is(holdings3.length, 0);
+ is(holdings4.length, 0);
+ is(holdings5.length, 0);
+}
+
+function checkResults() {
+ is(holdings1.length, 0);
+
+ let result = holdings2.sort((a, b) => a - b);
+ is(result.length, 3);
+ is(result[0], 1);
+ is(result[1], 2);
+ is(result[2], 3);
+
+ is(holdings3.length, 0);
+ is(holdings4.length, 0);
+
+ is(holdings5.length, 1);
+ is(holdings5[0], 5);
+
+ postMessage('passed');
+}
+
+function is(a, b) {
+ if (a !== b) {
+ throw `Expected ${b} but got ${a}`;
+ }
+}
+
+function makeJSCycle(size) {
+ let first = {};
+ let current = first;
+ for (let i = 0; i < size; i++) {
+ current.next = {};
+ current = current.next;
+ }
+ current.next = first;
+ return first;
+}
diff --git a/js/xpconnect/tests/mochitest/inner.html b/js/xpconnect/tests/mochitest/inner.html
new file mode 100644
index 0000000000..8021a55539
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/inner.html
@@ -0,0 +1,7 @@
+<html>
+ <head>
+ <title>Inner frame for bug 39685 mochitest</title>
+ </head>
+ <body onload="x = 4">
+ </body>
+</html>
diff --git a/js/xpconnect/tests/mochitest/mochitest.toml b/js/xpconnect/tests/mochitest/mochitest.toml
new file mode 100644
index 0000000000..c57cb26890
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/mochitest.toml
@@ -0,0 +1,297 @@
+[DEFAULT]
+support-files = [
+ "bug500931_helper.html",
+ "bug571849_helper.html",
+ "bug589028_helper.html",
+ "bug92773_helper.html",
+ "chrome_wrappers_helper.html",
+ "file1_bug629227.html",
+ "file2_bug629227.html",
+ "file_bug505915.html",
+ "file_bug605167.html",
+ "file_bug650273.html",
+ "file_bug658560.html",
+ "file_bug706301.html",
+ "file_bug720619.html",
+ "file_bug731471.html",
+ "file_bug738244.html",
+ "file_bug760131.html",
+ "file_bug781476.html",
+ "file_bug789713.html",
+ "file_bug795275.html",
+ "file_bug799348.html",
+ "file_bug802557.html",
+ "file_bug860494.html",
+ "file_crosscompartment_weakmap.html",
+ "file_documentdomain.html",
+ "file_doublewrappedcompartments.html",
+ "file_empty.html",
+ "file_evalInSandbox.html",
+ "file_exnstack.html",
+ "file_expandosharing.html",
+ "file_matches.html",
+ "file_nodelists.html",
+ "file_wrappers-2.html",
+ "file_xrayic.html",
+ "inner.html",
+ "test1_bug629331.html",
+ "test2_bug629331.html",
+ "finalizationRegistry_worker.js",
+ "private_field_worker.js",
+ "class_static_worker.js",
+ "bug1681664_helper.js",
+ "shadow_realm_worker.js",
+ "shadow_realm_module.js",
+]
+prefs = [
+ "javascript.options.weakrefs=true",
+ "javascript.options.spectre.disable_for_isolated_content=true",
+ "javascript.options.experimental.shadow_realms=true",
+]
+
+["test_bug92773.html"]
+skip-if = [
+ "http3",
+ "http2",
+]
+
+["test_bug384632.html"]
+
+["test_bug390488.html"]
+
+["test_bug393269.html"]
+
+["test_bug396851.html"]
+skip-if = [
+ "http3",
+ "http2",
+]
+
+["test_bug428021.html"]
+
+["test_bug446584.html"]
+
+["test_bug462428.html"]
+
+["test_bug478438.html"]
+skip-if = [
+ "http3",
+ "http2",
+]
+
+["test_bug500691.html"]
+
+["test_bug505915.html"]
+skip-if = [
+ "http3",
+ "http2",
+]
+
+["test_bug560351.html"]
+
+["test_bug585745.html"]
+
+["test_bug589028.html"]
+
+["test_bug601299.html"]
+
+["test_bug605167.html"]
+skip-if = [
+ "http3",
+ "http2",
+]
+
+["test_bug618017.html"]
+
+["test_bug623437.html"]
+
+["test_bug628410.html"]
+
+["test_bug628794.html"]
+
+["test_bug629227.html"]
+skip-if = [
+ "http3",
+ "http2",
+]
+
+["test_bug629331.html"]
+skip-if = [
+ "http3",
+ "http2",
+]
+
+["test_bug636097.html"]
+skip-if = [
+ "http3",
+ "http2",
+]
+
+["test_bug650273.html"]
+skip-if = [
+ "http3",
+ "http2",
+]
+
+["test_bug655297-1.html"]
+
+["test_bug655297-2.html"]
+
+["test_bug661980.html"]
+
+["test_bug691059.html"]
+
+["test_bug720619.html"]
+skip-if = [
+ "http3",
+ "http2",
+]
+
+["test_bug731471.html"]
+skip-if = ["os == 'android' && debug"]
+
+["test_bug764389.html"]
+
+["test_bug772288.html"]
+
+["test_bug781476.html"]
+
+["test_bug789713.html"]
+skip-if = [
+ "http3",
+ "http2",
+]
+
+["test_bug790732.html"]
+
+["test_bug793969.html"]
+
+["test_bug800864.html"]
+skip-if = [
+ "http3",
+ "http2",
+]
+
+["test_bug802557.html"]
+skip-if = [
+ "http3",
+ "http2",
+]
+
+["test_bug803730.html"]
+
+["test_bug809547.html"]
+
+["test_bug829872.html"]
+skip-if = [
+ "http3",
+ "http2",
+]
+
+["test_bug862380.html"]
+skip-if = [
+ "http3",
+ "http2",
+]
+
+["test_bug865260.html"]
+skip-if = [
+ "http3",
+ "http2",
+]
+
+["test_bug871887.html"]
+
+["test_bug912322.html"]
+
+["test_bug916945.html"]
+skip-if = [
+ "http3",
+ "http2",
+]
+
+["test_bug940783.html"]
+skip-if = [
+ "http3",
+ "http2",
+]
+
+["test_bug960820.html"]
+
+["test_bug965082.html"]
+skip-if = [
+ "http3",
+ "http2",
+]
+
+["test_bug993423.html"]
+
+["test_bug1005806.html"]
+
+["test_bug1094930.html"]
+
+["test_bug1158558.html"]
+
+["test_bug1448048.html"]
+
+["test_bug1681664.html"]
+
+["test_class_static_block_worker.html"]
+skip-if = ["!nightly_build"]
+
+["test_crosscompartment_weakmap.html"]
+
+["test_enable_privilege.html"]
+
+["test_finalizationRegistry.html"]
+
+["test_finalizationRegistryInWorker.html"]
+
+["test_finalizationRegistry_cleanupSome.html"]
+
+["test_finalizationRegistry_incumbent.html"]
+
+["test_frameWrapping.html"]
+# The JS test component we use below is only available in debug builds.
+
+["test_getWebIDLCaller.html"]
+skip-if = ["!debug"]
+
+["test_getweakmapkeys.html"]
+
+["test_isRemoteProxy.html"]
+
+["test_nukeContentWindow.html"]
+
+["test_paris_weakmap_keys.html"]
+skip-if = ["!debug"]
+
+["test_private_field_dom.html"]
+
+["test_private_field_worker.html"]
+
+["test_sameOriginPolicy.html"]
+skip-if = [
+ "http3",
+ "http2",
+]
+
+["test_sandbox_fetch.html"]
+support-files = ["../../../../dom/tests/mochitest/fetch/test_fetch_basic.js"]
+
+["test_shadowRealm.html"]
+# This test has been updated to work with worker modules
+
+["test_shadowRealm_worker.html"]
+skip-if = ["!nightly_build"]
+
+["test_spectre_mitigations.html"]
+skip-if = ["os == 'android'"] # Fission situation on Android is more complicated.
+
+["test_weakRefs.html"]
+
+["test_weakRefs_collected_wrapper.html"]
+
+["test_weakRefs_cross_compartment.html"]
+
+["test_weakmaps.html"]
diff --git a/js/xpconnect/tests/mochitest/moz.build b/js/xpconnect/tests/mochitest/moz.build
new file mode 100644
index 0000000000..2f41008128
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/moz.build
@@ -0,0 +1,7 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+MOCHITEST_MANIFESTS += ["mochitest.toml"]
diff --git a/js/xpconnect/tests/mochitest/private_field_worker.js b/js/xpconnect/tests/mochitest/private_field_worker.js
new file mode 100644
index 0000000000..b822c16248
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/private_field_worker.js
@@ -0,0 +1,21 @@
+class A {
+ #x;
+
+ g(o) {
+ return #x in o;
+ }
+}
+
+let objects = [];
+
+self.onmessage = function (e) {
+ if (e.data === 'allocate') {
+ objects.push(new A);
+ return;
+ }
+ if (e.data == 'count') {
+ postMessage(objects.length);
+ return;
+ }
+ postMessage('Unknown message type.');
+} \ No newline at end of file
diff --git a/js/xpconnect/tests/mochitest/shadow_realm_module.js b/js/xpconnect/tests/mochitest/shadow_realm_module.js
new file mode 100644
index 0000000000..35ba80666b
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/shadow_realm_module.js
@@ -0,0 +1 @@
+export var x = 1;
diff --git a/js/xpconnect/tests/mochitest/shadow_realm_worker.js b/js/xpconnect/tests/mochitest/shadow_realm_worker.js
new file mode 100644
index 0000000000..c91c9bc30c
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/shadow_realm_worker.js
@@ -0,0 +1,81 @@
+
+var sr = new ShadowRealm();
+var resolve;
+
+var allSettled = new Promise((resolved) => { resolve = resolved });
+
+self.onmessage = async function (e) {
+ try {
+ // Test evaluate
+ if (e.data === 'evaluate') {
+ sr.evaluate("var s = 'PASS set string in realm';")
+ var res = sr.evaluate('s');
+ postMessage(res);
+ return;
+ }
+
+ // If Import works in a worker, then it ought to work in a shadow realm
+ //
+ // See https://bugzilla.mozilla.org/show_bug.cgi?id=1247687 and
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=1772162
+ if (e.data == 'import') {
+ var import_worked = false;
+ var importValue_worked = false;
+ var importNested_worked = false;
+ try {
+ var module = await import("./shadow_realm_module.js");
+ if (module.x != 1) {
+ throw "mismatch";
+ }
+ import_worked = true;
+ } catch (e) { }
+
+ try {
+ await sr.importValue("./shadow_realm_module.js", 'x').then((x) => {
+ if (x == 1) { importValue_worked = true; }
+ });
+ } catch (e) { }
+
+ try {
+ sr.evaluate(`
+ var imported = false;
+ import("./shadow_realm_module.js").then((module) => {
+ if (module.x == 1) {
+ imported = true;
+ }
+ });
+ true;
+ `);
+
+ importNested_worked = sr.evaluate("imported");
+ } catch (e) {
+
+ }
+
+ if (importValue_worked == importNested_worked) {
+ postMessage(`PASS: import in workers ${
+ import_worked ? "worked" : "failed"
+ }. importValue, and nested import all ${
+ importValue_worked ? "worked" : "failed"
+ } `);
+ resolve();
+ return;
+ }
+
+ postMessage(`FAIL: importValue ${importValue_worked}, import ${import_worked}, importNested ${importNested_worked}`);
+ resolve();
+ return;
+ }
+
+
+ // Reply back with finish
+ if (e.data == 'finish') {
+ await allSettled;
+ postMessage("finish");
+ return;
+ }
+ } catch (e) {
+ postMessage("FAIL: " + e.message);
+ }
+ postMessage('Unknown message type.');
+}
diff --git a/js/xpconnect/tests/mochitest/test1_bug629331.html b/js/xpconnect/tests/mochitest/test1_bug629331.html
new file mode 100644
index 0000000000..18843e08da
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/test1_bug629331.html
@@ -0,0 +1,19 @@
+<body>
+<iframe src="about:blank" id="ifr"></iframe>
+<script>
+/** Test for Bug 629331 **/
+function finish() {
+ parent.postMessage(JSON.stringify({fun: "finish"}), "*");
+}
+
+function is(a, b, description) {
+ parent.postMessage(JSON.stringify({ fun: "is", a: a, b: b, description: description }), "*");
+}
+
+document.domain = "example.org";
+var i = 0;
+is(i, 0, 'i meets starting conditions');
+document.getElementById('ifr').src = 'http://test2.example.org/tests/js/xpconnect/tests/mochitest/test2_bug629331.html';
+</script>
+
+
diff --git a/js/xpconnect/tests/mochitest/test2_bug629331.html b/js/xpconnect/tests/mochitest/test2_bug629331.html
new file mode 100644
index 0000000000..1bcf037398
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/test2_bug629331.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ </head>
+ <body>
+ <script>
+ document.domain = "example.org";
+
+ for (var j = 1; j <= 9; j++) {
+ parent.i = j;
+ var locali = parent.i;
+ parent.is(locali, j, 'step ' + j + ' worked');
+ }
+
+ parent.finish();
+ </script>
+ </body>
+</html>
diff --git a/js/xpconnect/tests/mochitest/test_bug1005806.html b/js/xpconnect/tests/mochitest/test_bug1005806.html
new file mode 100644
index 0000000000..41fe6ee1e6
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/test_bug1005806.html
@@ -0,0 +1,27 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1005806
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1005806</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1005806">Mozilla Bug 1005806</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+ <input id="ipt"></input>
+</div>
+<pre id="test">
+</pre>
+<script type="application/javascript">
+
+/** Test for Bug 1005806 **/
+is(typeof document.getElementById('ipt').controllers, 'undefined', "Controllers property should not appear for content");
+
+</script>
+</body>
+</html>
diff --git a/js/xpconnect/tests/mochitest/test_bug1094930.html b/js/xpconnect/tests/mochitest/test_bug1094930.html
new file mode 100644
index 0000000000..d303bf2495
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/test_bug1094930.html
@@ -0,0 +1,29 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1094930
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1094930</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <iframe id="ifr"></iframe>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1094930">Mozilla Bug 1094930</a>
+<p id="display"></p>
+<script type="application/javascript">
+ SimpleTest.waitForExplicitFinish();
+ class XFoo extends frames[0].HTMLElement {
+ connectedCallback() {
+ ok(true, "connectedCallback was called");
+ SimpleTest.finish();
+ }
+ };
+
+ customElements.define.call(frames[0].customElements, "x-foo", XFoo);
+ frames[0].document.firstChild.appendChild(new XFoo());
+</script>
+</body>
+</html>
diff --git a/js/xpconnect/tests/mochitest/test_bug1158558.html b/js/xpconnect/tests/mochitest/test_bug1158558.html
new file mode 100644
index 0000000000..f5c50d640e
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/test_bug1158558.html
@@ -0,0 +1,47 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1158558
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1158558</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1158558">Mozilla Bug 1158558</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+ <input id="ipt"></input>
+</div>
+<pre id="test">
+</pre>
+<script type="application/javascript">
+
+/** Test for Bug 1158558 **/
+
+// Observers of cycle-collector-begin can be implemented in JS, and
+// thus can end up starting an incremental GC while we're in the middle
+// of a CC slice.
+
+SimpleTest.waitForExplicitFinish();
+
+var observer = {
+ observe: function(subject, topic, data) {
+ SpecialPowers.removeObserver(observer, "cycle-collector-begin");
+ SpecialPowers.Cu.getJSTestingFunctions().startgc(1);
+
+ ok(true, "Do something so the test harness doesn't get angry");
+
+ SimpleTest.finish();
+ }
+};
+
+SpecialPowers.addObserver(observer, "cycle-collector-begin");
+
+SpecialPowers.Cu.forceCC();
+
+</script>
+</body>
+</html>
diff --git a/js/xpconnect/tests/mochitest/test_bug1448048.html b/js/xpconnect/tests/mochitest/test_bug1448048.html
new file mode 100644
index 0000000000..7cd747b5c7
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/test_bug1448048.html
@@ -0,0 +1,33 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1448048
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1448048</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+ /** Test for Bug 1448048 **/
+ var { AppConstants } = SpecialPowers.ChromeUtils.importESModule(
+ "resource://gre/modules/AppConstants.sys.mjs"
+ );
+ if (AppConstants.NIGHTLY_BUILD) {
+ is(typeof Components, "undefined", "Should be no Components shim on Nightly");
+ } else {
+ is(typeof Components, "object", "Should have a components shim on non-Nightly");
+ }
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1448048">Mozilla Bug 1448048</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/js/xpconnect/tests/mochitest/test_bug1681664.html b/js/xpconnect/tests/mochitest/test_bug1681664.html
new file mode 100644
index 0000000000..685d4aa669
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/test_bug1681664.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<html lang="en" dir="ltr">
+ <head>
+ <title>Test page for bug 1681664</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script>
+ SimpleTest.waitForExplicitFinish()
+ async function init() {
+ var Services = SpecialPowers.Services;
+ var observer = {
+ observe(subject, topic, data) {
+ if (topic === "process-hang-report") {
+ var report = subject.QueryInterface(Ci.nsIHangReport);
+ report.terminateScript();
+ Services.obs.removeObserver(observer, "process-hang-report");
+ }
+ }
+ }
+
+ Services.obs.addObserver(observer, "process-hang-report");
+ try {
+ await import("test_bug1681664_helper.js");
+ result.textContent = "FAIL";
+ } catch (ex) {
+ result.textContent = "PASS";
+ }
+ }
+ </script>
+ </head>
+ <body>
+ <p id="result"></p>
+ <script>
+ (async function() {
+ await init();
+ is(result.textContent, "PASS", "Infinite loop script should not cause browser crash");
+ SimpleTest.finish()
+ })();
+ </script>
+ </body>
+</html>
+
diff --git a/js/xpconnect/tests/mochitest/test_bug384632.html b/js/xpconnect/tests/mochitest/test_bug384632.html
new file mode 100644
index 0000000000..251ec9b153
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/test_bug384632.html
@@ -0,0 +1,34 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=384632
+-->
+<head>
+ <title>Test for Bug 384632</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=384632">Mozilla Bug 384632</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 384632 **/
+var Cc = SpecialPowers.Cc, Ci = SpecialPowers.Ci;
+var propBag = Cc["@mozilla.org/hash-property-bag;1"].createInstance(Ci.nsIWritablePropertyBag);
+var obj = {};
+propBag.setProperty("foopy", obj);
+ok(SpecialPowers.unwrap(propBag.getProperty("foopy")) === obj,
+ "nsIVariant works with regular objects");
+propBag.setProperty("foopy1", external);
+ok(SpecialPowers.unwrap(propBag.getProperty("foopy1")) === external,
+ "nsIVariant works with bizarre objects");
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/js/xpconnect/tests/mochitest/test_bug390488.html b/js/xpconnect/tests/mochitest/test_bug390488.html
new file mode 100644
index 0000000000..ca4bc4024b
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/test_bug390488.html
@@ -0,0 +1,64 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=390488
+-->
+<head>
+ <title>Test for Bug 390488</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=390488">Mozilla Bug 390488</a>
+<p id="display">
+ <div id="testdiv" onclick="checkForStacks();" style="visibility:hidden">
+ </div>
+</p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 390488 **/
+ function getStack1() {
+ var func = arguments.callee.caller;
+ var stack = "";
+ for (var i = 1; func && i < 8; i++) {
+ stack += " " + i + ". " + func.name;
+ func = func.caller;
+ }
+ return stack;
+ }
+
+ function getStack2() {
+ var stack = new Error().stack;
+ // Remove the two lines due to calling this
+ return stack.substring(stack.indexOf("\n", stack.indexOf("\n")+1)+1);
+ }
+
+ function simulateClick() {
+ var evt = document.createEvent("MouseEvents");
+ evt.initMouseEvent("click", true, true, window,
+ 0, 0, 0, 0, 0, false, false, false, false, 0, null);
+ $("testdiv").dispatchEvent(evt);
+ }
+
+ function matches(s, p, name) {
+ ok(s.match(p) != null,
+ name + " - got " + s + ", expected a string matching " + p);
+ }
+
+ function checkForStacks() {
+ matches(getStack1(), /checkForStacks .* onclick .* simulateClick/,
+ "Stack from walking caller chain should be correct");
+ isnot(getStack2().indexOf("simulateClick@"), -1,
+ "Stack from |new Error().stack| should include simulateClick");
+ }
+
+ simulateClick();
+</script>
+</pre>
+</body>
+</html>
+
diff --git a/js/xpconnect/tests/mochitest/test_bug393269.html b/js/xpconnect/tests/mochitest/test_bug393269.html
new file mode 100644
index 0000000000..d69e9ef2d1
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/test_bug393269.html
@@ -0,0 +1,46 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=393269
+-->
+<head>
+ <title>Test for Bug 393269</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=393269">Mozilla Bug 393269</a>
+<iframe id="ifr"></iframe>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+(function () {
+ /** Test for Bug 393269 **/
+ var doc = $("ifr").contentDocument;
+ is("UTF-8", doc.characterSet, "control, getting a property");
+ doc.open();
+ try {
+ is("UTF-8", doc.characterSet,
+ "can get a property after 1 document.open")
+ } catch (e) {
+ fail("Shouldn't have thrown: " + e);
+ return;
+ } finally {
+ doc.close();
+ }
+
+ doc.open();
+ try {
+ is("UTF-8", doc.characterSet,
+ "can get a property after 2 document.opens")
+ } catch (e) {
+ fail("Shouldn't have thrown: " + e);
+ } finally {
+ doc.close();
+ }
+})();
+</script>
+</pre>
+</body>
+</html>
+
diff --git a/js/xpconnect/tests/mochitest/test_bug396851.html b/js/xpconnect/tests/mochitest/test_bug396851.html
new file mode 100644
index 0000000000..dc6bd25d52
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/test_bug396851.html
@@ -0,0 +1,53 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=396851
+-->
+<head>
+ <title>Test for Bug 396851</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+
+ <script type="text/javascript">
+ function throws(func, pattern, msg) {
+ try {
+ func();
+ ok(false, msg);
+ } catch (e) {
+ ok(pattern.test(e), `${msg}: Expect exception mathing ${pattern}`);
+ }
+ }
+
+ function go() {
+ var iframe = $("ifr");
+ var win = iframe.contentWindow;
+ throws(() => win.document,
+ /Permission denied/,
+ "Unprivileged code should not be able to access cross-origin document");
+
+ if (SpecialPowers.useRemoteSubframes) {
+ throws(() => win.document,
+ /Permission denied/,
+ "Privileged code should not be able to access cross-process document");
+ } else {
+ ok(SpecialPowers.wrap(win).document != null,
+ "Able to access the cross-origin document");
+ }
+ SimpleTest.finish();
+ }
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=396851">Mozilla Bug 396851</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<script type="text/javascript">
+ SimpleTest.waitForExplicitFinish();
+</script>
+<iframe id="ifr"
+ src="http://example.org/tests/js/xpconnect/tests/mochitest/inner.html"
+ onload="go()">
+</iframe>
+</body>
+</html>
diff --git a/js/xpconnect/tests/mochitest/test_bug428021.html b/js/xpconnect/tests/mochitest/test_bug428021.html
new file mode 100644
index 0000000000..932dcf7428
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/test_bug428021.html
@@ -0,0 +1,40 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=428021
+-->
+<head>
+ <title>Test for Bug 428021</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=428021">Mozilla Bug 428021</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+ /** Test for Bug 428021 **/
+ var rangetter, ransetter;
+ this.__defineGetter__('x', function() { rangetter = true; });
+ this.__defineSetter__('x', function(val) { ransetter = true; });
+
+ var exn;
+ try {
+ e = x;
+ x = false;
+ } catch (e) {
+ exn = e;
+ }
+ ok(!exn, "Exception caught: " + exn);
+ ok(rangetter, "Failed to run getter");
+ ok(ransetter, "Failed to run setter");
+
+</script>
+</pre>
+</body>
+</html>
+
diff --git a/js/xpconnect/tests/mochitest/test_bug446584.html b/js/xpconnect/tests/mochitest/test_bug446584.html
new file mode 100644
index 0000000000..09fef92867
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/test_bug446584.html
@@ -0,0 +1,47 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=446584
+-->
+<head>
+ <title>Test for Bug 446584</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=446584">Mozilla Bug 446584</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 446584 **/
+
+function test(val) {
+ try {
+ document.createNodeIterator(document.body,
+ NodeFilter.SHOW_ALL,
+ function() { throw val }).nextNode();
+ ok(false, "NodeIterator::nextNode() should have thrown an exception.");
+ } catch (ex) {
+ ok(val === ex, "NodeIterator did not properly forward exception " +
+ val + " of type " + typeof val + ". Thrown value was " + ex + ".");
+ }
+}
+
+test(0);
+test(1);
+test(3.14);
+test('roses');
+test({});
+test(false);
+test(true);
+test([1,2,3]);
+test(function(){});
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/js/xpconnect/tests/mochitest/test_bug462428.html b/js/xpconnect/tests/mochitest/test_bug462428.html
new file mode 100644
index 0000000000..a3f7b7c39a
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/test_bug462428.html
@@ -0,0 +1,51 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=462428
+-->
+<head>
+ <title>Test for Bug 462428</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=462428">Mozilla Bug 462428</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 462428 **/
+var getter = document.__lookupGetter__('documentElement');
+ok(getter !== undefined, "But able to look it up the normal way");
+ok(!document.hasOwnProperty('documentElement'), "property should still be on the prototype");
+
+var sawProp = false;
+for (var i in document) {
+ if (i === "documentElement") {
+ sawProp = true;
+ }
+}
+
+ok(sawProp, "property should be enumerable");
+
+is(getter.call(document), document.documentElement, "the getter actually works");
+
+Document.prototype.__defineSetter__('documentElement', function() {});
+is(getter.call(document), document.documentElement, "the getter works after defineSetter");
+
+var oldTitle = document.title;
+try {
+ var setter = document.__lookupSetter__('title');
+ setter.call(document, "title 1");
+ is(document.title, "title 1", "the setter is bound correctly");
+} finally {
+ document.title = oldTitle
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/js/xpconnect/tests/mochitest/test_bug478438.html b/js/xpconnect/tests/mochitest/test_bug478438.html
new file mode 100644
index 0000000000..e64a1e89fb
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/test_bug478438.html
@@ -0,0 +1,65 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=478438
+-->
+<head>
+ <title>Test for Bug 478438</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+ function fail(s, e) { ok(false, s + e) }
+ function pass(s, e) { ok(true, s) }
+ (pass.opposite = fail).opposite = pass;
+
+ function test() {
+ if (test.calledAlready)
+ return;
+ test.calledAlready = true;
+
+ var iwin = document.getElementById("f").contentWindow;
+
+ function testOne(fn, onAllow, infinitive) {
+ try { fn(); onAllow("able " + infinitive, "") }
+ catch (e) { onAllow.opposite("unable " + infinitive, ": " + e) }
+ }
+
+ testOne(() => iwin.focus, pass,
+ "to resolve/get allAccess property iwin.focus");
+
+ testOne(() => iwin.focus(), pass,
+ "to call allAccess method iwin.focus");
+
+ testOne(() => iwin.alert, fail,
+ "to resolve/get restricted property iwin.alert");
+
+ testOne(() => iwin.alert(), fail,
+ "to call restricted method iwin.alert");
+
+ testOne(() => iwin.location.toString(), fail,
+ "to call restricted method iwin.location.toString");
+
+ testOne(function() { iwin.location = "http://example.org" }, pass,
+ "to set writable property iwin.location");
+
+ SimpleTest.finish();
+ }
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=478438">Mozilla Bug 478438</a>
+<p id="display"></p>
+<div id="content">
+ <iframe id="f" src="http://example.com" onload="test()"></iframe>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 478438 **/
+
+SimpleTest.waitForExplicitFinish();
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/js/xpconnect/tests/mochitest/test_bug484107.html b/js/xpconnect/tests/mochitest/test_bug484107.html
new file mode 100644
index 0000000000..38ca7b207b
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/test_bug484107.html
@@ -0,0 +1,99 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=484107
+-->
+<head>
+ <title>Test for Bug 484107</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=484107">Mozilla Bug 484107</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 484107 **/
+
+ var text = "first group",
+ xpcWin = new XPCSafeJSObjectWrapper(window);
+ function get$1() { return RegExp.$1 };
+
+ function reset() {
+ var match = /(.*)/.exec(text);
+ if (!reset.skipStupidTests) {
+ reset.skipStupidTests = true;
+ ok(match, "No match?");
+ is(match[1], text, "Bad match?");
+ is(text, RegExp.$1, "RegExp.$1 missing?");
+ is(text, get$1(), "RegExp.$1 inaccessible?");
+ }
+ }
+
+ function test_XPC_SJOW_Call() {
+ isnot(text, xpcWin.get$1(), "Able to see RegExp.$1 from wrapped method.");
+ is("", xpcWin.get$1(), "Saw something other than an empty string for " +
+ "RegExp.$1 from wrapped method.");
+ is(text, window.get$1(), "Unable to see RegExp.$1 from non-wrapped method.");
+ }
+
+ function test_XPC_SJOW_Call_foreign_obj() {
+ var obj = {
+ xpcGet: xpcWin.get$1,
+ rawGet: window.get$1
+ };
+ isnot(text, obj.xpcGet(), "obj.xpcGet() returned matched text.");
+ is("", obj.xpcGet(), "obj.xpcGet() returned something other than the empty string.");
+ is(text, obj.rawGet(), "obj.rawGet() did not return matched text.");
+ }
+
+ function test_XPC_SJOW_toString() {
+ var str = new XPCSafeJSObjectWrapper({
+ toString: function() { return RegExp.$1 }
+ }) + "";
+ isnot(text, str, "toString() returned the matched text.");
+ is("", str, "toString() returned something other than the empty string.");
+ }
+
+ function test_XPC_SJOW_GetOrSetProperty() {
+ window.__defineGetter__("firstMatch", function() { return RegExp.$1 });
+ isnot(text, xpcWin.firstMatch, "Getter xpcWin.firstMatch returned matched text.");
+ is("", xpcWin.firstMatch,
+ "Getter xpcWin.firstMatch returned something other than the empty string.");
+ is(text, window.firstMatch, "Getter window.firstMatch did not return matched text.");
+ }
+
+ function test_XPC_SJOW_Create() {
+ function ctor() {
+ this.match = RegExp.$1;
+ return this; // XXX Why is this necessary?
+ }
+ ctor.prototype.getMatch = function() { return this.match };
+ var xpcCtor = new XPCSafeJSObjectWrapper(ctor),
+ match = (new xpcCtor).getMatch();
+ isnot(text, match, "(new xpcCtor).getMatch() was the matched text.");
+ is("", match, "(new xpcCtor).getMatch() was not the empty string.");
+ }
+
+ var tests = [
+ test_XPC_SJOW_Call,
+ test_XPC_SJOW_Call_foreign_obj,
+ test_XPC_SJOW_toString,
+ test_XPC_SJOW_GetOrSetProperty,
+ test_XPC_SJOW_Create
+ ];
+
+ for (var i = 0; i < tests.length; i++) {
+ reset();
+ tests[i]();
+ is(text, RegExp.$1, "RegExp.$1 was clobbered.");
+ }
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/js/xpconnect/tests/mochitest/test_bug500691.html b/js/xpconnect/tests/mochitest/test_bug500691.html
new file mode 100644
index 0000000000..2ed127c099
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/test_bug500691.html
@@ -0,0 +1,27 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=500691
+-->
+<head>
+ <title>Test for Bug 500691</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=500691">Mozilla Bug 500691</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 500691 **/
+ok(Function === alert.constructor, "alert's constructor is our Function");
+ok(window.Function === alert.constructor, "window.Function is also correct");
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/js/xpconnect/tests/mochitest/test_bug505915.html b/js/xpconnect/tests/mochitest/test_bug505915.html
new file mode 100644
index 0000000000..d5898bc1ed
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/test_bug505915.html
@@ -0,0 +1,49 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=505915
+-->
+<head>
+ <title>Test for Bug 505915</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=505915">Mozilla Bug 505915</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 505915 **/
+window.addEventListener("message", function () {
+ ok(false, "should not receive message");
+});
+
+function go() {
+ var ifr = $('ifr');
+ try {
+ // NB: the contentDocument getter now returns null for cross-origin
+ // frames, so use SpecialPowers to get a security wrapper to the document.
+ var xdoc = SpecialPowers.unwrap(SpecialPowers.wrap(ifr).contentDocument)
+ document.createTreeWalker(xdoc, 0, null);
+ ok(false, "should have thrown a security exception");
+ } catch (e) {
+ ok(/TypeError: Document.createTreeWalker: Argument 1 does not implement interface Node/.test(e),
+ "threw a binding exception instead of an invalid child exception");
+ }
+
+ SimpleTest.finish();
+}
+
+SimpleTest.waitForExplicitFinish();
+
+</script>
+</pre>
+
+<iframe id="ifr" onload="go();" src="http://test1.mochi.test:8888/"></iframe>
+
+</body>
+</html>
diff --git a/js/xpconnect/tests/mochitest/test_bug560351.html b/js/xpconnect/tests/mochitest/test_bug560351.html
new file mode 100644
index 0000000000..263e6850d7
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/test_bug560351.html
@@ -0,0 +1,36 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=560351
+-->
+<head>
+ <title>Test for Bug 560351</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=560351">Mozilla Bug 560351</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 560351 **/
+var pass = false;
+try {
+ document.body.__lookupGetter__("lastChild")();
+} catch (e) {
+ pass = true;
+}
+
+</script>
+<script>
+
+ok(pass, "pass was set to true");
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/js/xpconnect/tests/mochitest/test_bug585745.html b/js/xpconnect/tests/mochitest/test_bug585745.html
new file mode 100644
index 0000000000..758aeed30d
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/test_bug585745.html
@@ -0,0 +1,43 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=585745
+-->
+<head>
+ <title>Test for Bug 585745</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=585745">Mozilla Bug 585745</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 585745 **/
+
+ var a = document.createElementNS("http://www.w3.org/1998/Math/MathML", 'mrow');
+ var b = document.createElementNS("http://www.w3.org/1999/xhtml", 'span');
+ var htmlProto = Object.getPrototypeOf(b);
+ var mathMLProto = Object.getPrototypeOf(a);
+ // XXXbz once bug 560072 is fixed, we should be able to use
+ // getOwnPropertyDescriptor here.
+ Object.defineProperty(mathMLProto, "style", {
+ get: htmlProto.__lookupGetter__("style"),
+ });
+
+ var threw = false;
+ try {
+ a.style;
+ } catch(e) {
+ threw = true;
+ }
+ is(threw, true,
+ "Getting .style off a mathml element should throw in this case");
+</script>
+</pre>
+</body>
+</html>
diff --git a/js/xpconnect/tests/mochitest/test_bug589028.html b/js/xpconnect/tests/mochitest/test_bug589028.html
new file mode 100644
index 0000000000..2cd0d15ebb
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/test_bug589028.html
@@ -0,0 +1,62 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=589028
+-->
+<head>
+ <title>Test for Bug 589028</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=589028">Mozilla Bug 589028</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+<script>
+
+/** Test for Bug 589028 **/
+SimpleTest.waitForExplicitFinish();
+var p = 0;
+function go() {
+ var ifr = $('ifr');
+ var ifrwin = ifr.contentWindow;
+ var ifrdoc = ifr.contentDocument;
+
+ o1 = new ifrwin.Option();
+ is(o1.ownerDocument, ifrdoc, "ownerDocument doesn't match iframe");
+
+ o2 = ifrwin.getMyOption();
+ is(o2.ownerDocument, ifrdoc, "ownerDocument doesn't match iframe");
+
+ o3 = ifrwin.getCallersOption(this);
+ is(o3.ownerDocument, document);
+
+ a1 = new ifrwin.Audio();
+ is(a1.ownerDocument, ifrdoc, "ownerDocument doesn't match iframe");
+
+ a2 = ifrwin.getMyAudio();
+ is(a2.ownerDocument, ifrdoc, "ownerDocument doesn't match iframe");
+
+ a3 = ifrwin.getCallersAudio(this);
+ is(a3.ownerDocument, document);
+
+ i1 = new ifrwin.Image();
+ is(i1.ownerDocument, ifrdoc, "ownerDocument doesn't match iframe");
+
+ i2 = ifrwin.getMyImage();
+ is(i2.ownerDocument, ifrdoc, "ownerDocument doesn't match iframe");
+
+ i3 = ifrwin.getCallersImage(this);
+ is(i3.ownerDocument, document);
+
+ SimpleTest.finish();
+}
+
+
+</script>
+</pre>
+<iframe src="bug589028_helper.html" id="ifr" onload="go()"></iframe>
+</body>
+</html>
diff --git a/js/xpconnect/tests/mochitest/test_bug601299.html b/js/xpconnect/tests/mochitest/test_bug601299.html
new file mode 100644
index 0000000000..cd726d7974
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/test_bug601299.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=601299
+
+Trigger a fastnative from a frameless context.
+-->
+<head>
+ <title>Test for Bug 601299</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body onload="window.setTimeout(RegExp, 0); window.setTimeout(function() { ok(true); SimpleTest.finish(); }, 0);">
+<script>
+SimpleTest.waitForExplicitFinish();
+</script>
+</body>
+</html>
diff --git a/js/xpconnect/tests/mochitest/test_bug605167.html b/js/xpconnect/tests/mochitest/test_bug605167.html
new file mode 100644
index 0000000000..ba625aa805
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/test_bug605167.html
@@ -0,0 +1,56 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=505915
+-->
+<head>
+ <title>Test for Bug 505915</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=505915">Mozilla Bug 505915</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 505915 **/
+var url = "file_bug605167.html";
+var targetUrl = "http://example.com";
+var f;
+
+var p = 0;
+function go() {
+ switch (++p) {
+ case 1:
+ frames[0].location = url;
+ break;
+ case 2:
+ frames[0].location = targetUrl;
+ break;
+ case 3:
+ try {
+ f().cross_origin_property;
+ ok(false, "should have thrown an exception");
+ } catch (e) {
+ ok(/Permission denied/.test(e) || /attempt to run compile-and-go script/.test(e),
+ "threw the correct exception");
+ }
+ SimpleTest.finish();
+ break;
+ }
+}
+
+
+SimpleTest.waitForExplicitFinish();
+
+</script>
+</pre>
+
+<iframe id="ifr" onload="go();"></iframe>
+
+</body>
+</html>
diff --git a/js/xpconnect/tests/mochitest/test_bug618017.html b/js/xpconnect/tests/mochitest/test_bug618017.html
new file mode 100644
index 0000000000..8bfb428870
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/test_bug618017.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=618017
+
+Parsing XML must not override the version.
+-->
+<head>
+ <title>Test for Bug 618017</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+
+<script type='application/javascript'>
+let x = 12;
+function doLetEval() {
+ ok(eval('let x = 13; x') === 13, 'let statement is valid syntax in version 1.7');
+}
+</script>
+
+<script type='application/javascript'>
+doLetEval(); // Call to a function with a different version.
+</script>
+
+</body>
+</html>
+
diff --git a/js/xpconnect/tests/mochitest/test_bug623437.html b/js/xpconnect/tests/mochitest/test_bug623437.html
new file mode 100644
index 0000000000..0cea6f330b
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/test_bug623437.html
@@ -0,0 +1,43 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=623437
+-->
+<head>
+ <title>Test for Bug 623437</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=623437">Mozilla Bug 623437</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 623437 **/
+ var c = document.createElement("canvas").getContext("2d");
+ var seenArcToFirst;
+ var seenArc = false;
+ var seenArcTo = false;
+ for (var i in c) {
+ if (i == "arc") {
+ seenArc = true;
+ if (!seenArcTo)
+ seenArcToFirst = false;
+ }
+ if (i == "arcTo") {
+ seenArcTo = true;
+ if (!seenArc)
+ seenArcToFirst = true;
+ }
+ }
+ is(seenArc, true, "Should see arc");
+ is(seenArcTo, true, "Should see arcTo");
+ is(seenArcToFirst, true, "Should see arcTo before arc");
+</script>
+</pre>
+</body>
+</html>
diff --git a/js/xpconnect/tests/mochitest/test_bug628410.html b/js/xpconnect/tests/mochitest/test_bug628410.html
new file mode 100644
index 0000000000..2aec713793
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/test_bug628410.html
@@ -0,0 +1,34 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=628410
+-->
+<head>
+ <title>Test for Bug 628410</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=628410">Mozilla Bug 628410</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+
+<pre id="test">
+<script type="application/javascript">
+
+window.toString();
+
+if (SpecialPowers.Services.prefs.getBoolPref("extensions.InstallTrigger.enabled") &&
+ SpecialPowers.Services.prefs.getBoolPref("extensions.InstallTriggerImpl.enabled")) {
+ InstallTrigger + "";
+}
+
+console + "";
+ok(true, "Things didn't throw");
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/js/xpconnect/tests/mochitest/test_bug628794.html b/js/xpconnect/tests/mochitest/test_bug628794.html
new file mode 100644
index 0000000000..9cbfe0f436
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/test_bug628794.html
@@ -0,0 +1,43 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=585745
+-->
+<head>
+ <title>Test for Bug 585745</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=585745">Mozilla Bug 585745</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 585745 **/
+
+ var a = document.createElementNS("http://www.w3.org/2000/svg", 'svg');
+ var b = document.createElementNS("http://www.w3.org/1999/xhtml", 'span');
+ var htmlProto = Object.getPrototypeOf(b);
+ var svgProto = Object.getPrototypeOf(a);
+ // XXXbz once bug 560072 is fixed, we should be able to use
+ // getOwnPropertyDescriptor here.
+ Object.defineProperty(svgProto, "style", {
+ get: htmlProto.__lookupGetter__("style"),
+ });
+
+ var threw = false;
+ try {
+ a.style;
+ } catch(e) {
+ threw = true;
+ }
+ is(threw, true,
+ "Getting .style off an svg element should throw in this case");
+</script>
+</pre>
+</body>
+</html>
diff --git a/js/xpconnect/tests/mochitest/test_bug629227.html b/js/xpconnect/tests/mochitest/test_bug629227.html
new file mode 100644
index 0000000000..6d01c37ec0
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/test_bug629227.html
@@ -0,0 +1,47 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=629227
+-->
+<head>
+ <title>Test for Bug 629227</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=629227">Mozilla Bug 629227</a>
+<p id="display">
+ <iframe id="testTarget"></iframe>
+</p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 629227 **/
+SimpleTest.waitForExplicitFinish();
+
+$("testTarget").src =
+ "http://test1.example.org" +
+ location.pathname.replace(/test_bug629227.html/, "file1_bug629227.html");
+
+window.onmessage = function(ev) {
+ if (ev.data == "finish") {
+ SimpleTest.finish();
+ } else {
+ var data = JSON.parse(ev.data);
+ if ("ok" in data) {
+ ok(data.ok, data.reason);
+ }
+ }
+}
+
+addLoadEvent(function() {
+ $("testTarget").contentWindow.postMessage("start", "*");
+});
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/js/xpconnect/tests/mochitest/test_bug629331.html b/js/xpconnect/tests/mochitest/test_bug629331.html
new file mode 100644
index 0000000000..17520b187f
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/test_bug629331.html
@@ -0,0 +1,37 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=629331
+-->
+<head>
+ <title>Test for Bug 629331</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=629331">Mozilla Bug 629331</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+SimpleTest.waitForExplicitFinish();
+
+function handler(event) {
+ var obj = JSON.parse(event.data);
+ if (obj.fun == "finish") {
+ SimpleTest.finish();
+ } else {
+ is(obj.a, obj.b, obj.description);
+ }
+}
+
+window.addEventListener('message', handler);
+
+</script>
+<iframe src="http://test1.example.org/tests/js/xpconnect/tests/mochitest/test1_bug629331.html">
+</iframe>
+</pre>
+</body>
+</html>
diff --git a/js/xpconnect/tests/mochitest/test_bug636097.html b/js/xpconnect/tests/mochitest/test_bug636097.html
new file mode 100644
index 0000000000..8ae8e4822e
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/test_bug636097.html
@@ -0,0 +1,62 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=504877
+test by moz_bug_r_a4@yahoo.com
+-->
+<head>
+ <title>Test for Bug 504877</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=504877">Mozilla Bug 504877</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 504877 **/
+SimpleTest.waitForExplicitFinish();
+
+var targetUrl = "http://example.com/";
+var l;
+
+function a() {
+ var r = "FAIL", s;
+ try {
+ s = l.toString();
+ }
+ catch (e) {
+ if (/denied|insecure/.test(e))
+ r = "PASS";
+ s = e;
+ }
+
+ is(r, "PASS", "should have thrown an exception");
+ SimpleTest.finish();
+}
+
+var p = 0;
+function b() {
+ switch (++p) {
+ case 1:
+ frames[0].location = "about:blank";
+ break;
+ case 2:
+ l = frames[0].location;
+ frames[0].location = targetUrl;
+ break;
+ case 3:
+ a();
+ break;
+ }
+}
+</script>
+
+</pre>
+<iframe onload="b()"></iframe>
+</body>
+</html>
diff --git a/js/xpconnect/tests/mochitest/test_bug650273.html b/js/xpconnect/tests/mochitest/test_bug650273.html
new file mode 100644
index 0000000000..18e029982a
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/test_bug650273.html
@@ -0,0 +1,42 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=650273
+test by moz_bug_r_a4@yahoo.com
+-->
+<head>
+ <title>Test for Bug 650273</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank"
+href="https://bugzilla.mozilla.org/show_bug.cgi?id=650273">Mozilla Bug 650273</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 504877 **/
+SimpleTest.waitForExplicitFinish();
+var count = 0;
+function done() {
+ if (++count == 2) {
+ try {
+ ok($('ifr').location.host === 'example.com', "shouldn't see this");
+ } catch (e) {
+ ok(true, "navigation successfully happened");
+ }
+ SimpleTest.finish();
+ }
+}
+
+</script>
+
+<iframe id="ifr" src="file_bug650273.html" onload="done()"></iframe>
+
+</pre>
+</body>
+</html>
diff --git a/js/xpconnect/tests/mochitest/test_bug655297-1.html b/js/xpconnect/tests/mochitest/test_bug655297-1.html
new file mode 100644
index 0000000000..38ff528c5b
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/test_bug655297-1.html
@@ -0,0 +1,49 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=655297
+-->
+<head>
+ <title>Test for Bug 655297</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=655297">Mozilla Bug 655297</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+ <form>0</form> <form>1</form> <form>2</form> <form>3</form> <form>4</form>
+ <form>5</form> <form>6</form> <form>7</form> <form>8</form> <form>9</form>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 655297 **/
+
+var map = new WeakMap();
+function f() {
+ var paras = document.getElementsByTagName("form");
+ for (var i = 0; i < paras.length; i++)
+ map.set(paras[i], "ok");
+}
+function g() {
+ var paras = document.getElementsByTagName("form");
+ for (var i = 0; i < paras.length; i++) {
+ if (map.get(paras[i]) != "ok") {
+ return false;
+ }
+ }
+ return true;
+}
+
+f();
+SpecialPowers.forceGC();
+ok(g(), "Failed to keep XPCWrappedNative used as WeakMap key alive.");
+
+</script>
+
+
+</pre>
+</body>
+</html>
diff --git a/js/xpconnect/tests/mochitest/test_bug655297-2.html b/js/xpconnect/tests/mochitest/test_bug655297-2.html
new file mode 100644
index 0000000000..67da7964bd
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/test_bug655297-2.html
@@ -0,0 +1,49 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=655297
+-->
+<head>
+ <title>Test for Bug 655297</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=655297">Mozilla Bug 655297</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+ <p>0</p> <p>1</p> <p>2</p> <p>3</p> <p>4</p>
+ <p>5</p> <p>6</p> <p>7</p> <p>8</p> <p>9</p>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 655297 **/
+
+var map = new WeakMap();
+function f() {
+ var paras = document.getElementsByTagName("p");
+ for (var i = 0; i < paras.length; i++)
+ map.set(paras[i], "ok");
+}
+function g() {
+ var paras = document.getElementsByTagName("p");
+ for (var i = 0; i < paras.length; i++) {
+ if (map.get(paras[i]) != "ok") {
+ return false;
+ }
+ }
+ return true;
+}
+
+f();
+SpecialPowers.forceGC();
+ok(g(), "Failed to keep XPCWrappedNative used as WeakMap key alive.");
+
+</script>
+
+
+</pre>
+</body>
+</html>
diff --git a/js/xpconnect/tests/mochitest/test_bug661980.html b/js/xpconnect/tests/mochitest/test_bug661980.html
new file mode 100644
index 0000000000..afe62559a5
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/test_bug661980.html
@@ -0,0 +1,61 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=661980
+-->
+<head>
+ <title>Test for Bug 661980</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=661980">Mozilla Bug 661980</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 661980 **/
+
+// While not currently needed, make this as similar as possible to a real
+// EventTarget just to make sure that we're tripping on the wrapping and
+// nothing else.
+var fakeTarget = {
+ addEventListener: function() {},
+ removeEventListener: function() {},
+ dispatchEvent: function() {}
+}
+
+var mouseevent = document.createEvent("MouseEvent");
+var didThrow = false;
+dump("hello nurse\n");
+try {
+ mouseevent.initMouseEvent("mouseover",
+ false, false,
+ window,
+ 1, 2, 3, 4, 5,
+ false, false, false, false,
+ 0,
+ fakeTarget);
+}
+catch (ex) {
+ didThrow = true;
+}
+ok(didThrow, "should not be able to implement EventTarget using script");
+
+mouseevent.initMouseEvent("mouseout",
+ false, false,
+ window,
+ 1, 2, 3, 4, 5,
+ false, false, false, false,
+ 0,
+ document.body);
+is(mouseevent.type, "mouseout",
+ "should able to implement EventTarget using Element");
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/js/xpconnect/tests/mochitest/test_bug691059.html b/js/xpconnect/tests/mochitest/test_bug691059.html
new file mode 100644
index 0000000000..b00da02b0d
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/test_bug691059.html
@@ -0,0 +1,59 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=691059
+-->
+<head>
+ <title>Test for Bug 691059</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=691059">Mozilla Bug 691059</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+
+/** Test for Bug 691059 **/
+
+function f() {}
+
+function testEventTarget(obj) {
+ obj.onmouseenter = f;
+ is(obj.onmouseenter, f,
+ "onmouseenter should be settable");
+ obj.onmouseleave = f;
+ is(obj.onmouseleave, f,
+ "onmouseenter should be settable");
+}
+
+function testInterface(obj) {
+ try {
+ obj.prototype.onmouseenter = f;
+ is("onmouseenter" in obj, false,
+ "setting <Interface>.prototype.onmouseenter has no effect on the " +
+ "non-existent <Interface>.onmouseenter");
+ obj.prototype.onmouseleave = f;
+ is("onmouseleave" in obj, false,
+ "setting <Interface>.prototype.onmouseleave has no effect on the " +
+ "non-existent <Interface>.onmouseleave");
+ } catch(ex) {
+ ok(false, ex);
+ }
+}
+
+testEventTarget(window);
+testEventTarget(document);
+testEventTarget(document.documentElement);
+
+testInterface(Document);
+testInterface(HTMLElement);
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/js/xpconnect/tests/mochitest/test_bug720619.html b/js/xpconnect/tests/mochitest/test_bug720619.html
new file mode 100644
index 0000000000..804ec96d75
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/test_bug720619.html
@@ -0,0 +1,55 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=720619
+-->
+<head>
+ <title>Test for Bug 629227</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=720619">Mozilla Bug 720619</a>
+<p id="display">
+ <iframe id="testTarget"></iframe>
+</p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 720619 **/
+SimpleTest.waitForExplicitFinish();
+
+function checkThrows(f, exception) {
+ try {
+ f();
+ ok(false, "should have thrown: " + f);
+ } catch (e) {
+ ok(exception.test(e.toString()), "correctly threw");
+ }
+}
+
+function go() {
+ var loc = $('ifr').contentWindow.location;
+ checkThrows(function() {loc + '';}, /Permission denied/);
+ checkThrows(function() {'' + loc;}, /Permission denied/);
+ checkThrows(function() {String(loc);}, /Permission denied/);
+
+ var win = $('ifr').contentWindow;
+ checkThrows(function() {win + '';}, /Permission denied/);
+ checkThrows(function() {'' + win;}, /Permission denied/);
+ checkThrows(function() {String(win);}, /Permission denied/);
+
+ SimpleTest.finish();
+}
+
+</script>
+
+<iframe id="ifr" onload="go()"
+ src="http://example.org/tests/js/xpconnect/tests/mochitest/file_bug720619.html">
+</iframe>
+</pre>
+</body>
+</html>
diff --git a/js/xpconnect/tests/mochitest/test_bug731471.html b/js/xpconnect/tests/mochitest/test_bug731471.html
new file mode 100644
index 0000000000..e74b07734a
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/test_bug731471.html
@@ -0,0 +1,42 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=731471
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 731471</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body onload="setTimeout(boom, 0);">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=731471">Mozilla Bug 731471</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 731471. This is effectively a crashtest, but it uses window.open, which
+ doesn't work in the crashtest harness. **/
+SimpleTest.waitForExplicitFinish();
+SimpleTest.requestFlakyTimeout("untriaged");
+function boom()
+{
+ w = window.open("file_bug731471.html");
+ setTimeout(function() {
+ w.document.write("2");
+ w.document.close();
+ w.document.write("3 - Done");
+ w.document.close();
+ w.close();
+ ok(true, "Didn't assert!");
+ SimpleTest.finish();
+ }, 400);
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/js/xpconnect/tests/mochitest/test_bug764389.html b/js/xpconnect/tests/mochitest/test_bug764389.html
new file mode 100644
index 0000000000..6e90dd448b
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/test_bug764389.html
@@ -0,0 +1,40 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=764389
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 764389</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=764389">Mozilla Bug 764389</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 764389 **/
+
+// This is basically a crash test, but we need to write a mochitest so that it
+// runs with http:// urls instead of file:// urls.
+SimpleTest.waitForExplicitFinish();
+
+function go() {
+ var ifr = document.getElementById('ifr');
+ ifr.contentDocument.open();
+ ok(true, "Didn't crash");
+ ifr.contentDocument.close();
+ SimpleTest.finish();
+}
+
+
+
+</script>
+<iframe id="ifr" onload="go();" src="file_empty.html">
+</pre>
+</body>
+</html>
diff --git a/js/xpconnect/tests/mochitest/test_bug772288.html b/js/xpconnect/tests/mochitest/test_bug772288.html
new file mode 100644
index 0000000000..5af0f7d2d6
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/test_bug772288.html
@@ -0,0 +1,50 @@
+<!DOCTYPE html>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=772288
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 772288</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body onload="doTest()">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=772288">Mozilla Bug 772288</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 772288 **/
+SimpleTest.waitForExplicitFinish();
+
+const Cu = SpecialPowers.Cu;
+
+function doTest() {
+ msg = "AppConstants should be imported on window";
+ try {
+ Cu.import("resource://gre/modules/AppConstants.jsm", window);
+ ok(AppConstants, msg);
+ } catch (ex) {
+ ok(false, msg + " : " + ex);
+ }
+
+ msg = "AppConstants should be imported on myObj";
+ try {
+ var myObj = {};
+ Cu.import("resource://gre/modules/AppConstants.jsm", myObj);
+ ok(myObj.AppConstants, msg);
+ } catch (ex) {
+ ok(false, msg + " : " + ex);
+ }
+
+ SimpleTest.finish();
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/js/xpconnect/tests/mochitest/test_bug781476.html b/js/xpconnect/tests/mochitest/test_bug781476.html
new file mode 100644
index 0000000000..f9ab40d4d9
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/test_bug781476.html
@@ -0,0 +1,36 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=781476
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 781476</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=781476">Mozilla Bug 781476</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 781476 **/
+SimpleTest.waitForExplicitFinish();
+
+function go() {
+ var iwin = document.getElementById('ifr').contentWindow;
+ iwin.is = is;
+ var evt = iwin.makeEvent();
+ is(evt.expando, 42, "Expando properly visible in caller frame");
+ SimpleTest.finish();
+}
+
+
+</script>
+</pre>
+<iframe onload="go();" id="ifr" src="file_bug781476.html"></iframe>
+</body>
+</html>
diff --git a/js/xpconnect/tests/mochitest/test_bug789713.html b/js/xpconnect/tests/mochitest/test_bug789713.html
new file mode 100644
index 0000000000..251ecf22c2
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/test_bug789713.html
@@ -0,0 +1,39 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=789713
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 789713</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=789713">Mozilla Bug 789713</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+<iframe id="ifr"></iframe>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 789713 **/
+
+// We can't set document.domain on mochi.test, because it's forbidden to set
+// document.domain to a TLD.
+var ifr = document.getElementById('ifr');
+
+SimpleTest.waitForExplicitFinish();
+ifr.src = window.location.toString().replace("mochi.test:8888", "test1.example.org")
+ .replace("test_bug789713", "file_bug789713")
+ .split('?')[0];
+window.onmessage = function(message) {
+ ok(message.data, "Test succeeded and didn't crash");
+ SimpleTest.finish();
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/js/xpconnect/tests/mochitest/test_bug790732.html b/js/xpconnect/tests/mochitest/test_bug790732.html
new file mode 100644
index 0000000000..c702273900
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/test_bug790732.html
@@ -0,0 +1,55 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=790732
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 790732</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+SimpleTest.waitForExplicitFinish();
+
+async function doTest() {
+ await SpecialPowers.pushPrefEnv({set: [["dom.use_components_shim", true]]})
+
+ // Basic stuff
+ ok(Components, "Components shim exists!");
+ var Ci = Components.interfaces;
+ ok(Ci, "interfaces shim exists!");
+ is(typeof Components.classes, 'undefined', "Shouldn't have a Cc");
+
+ // Check each interface that we shim. We start by checking specific
+ // constants for a couple of interfaces, and then once it's pretty clear that
+ // it's working as intended we just check that the objects themselves are the
+ // same.
+ is(Ci.nsIXMLHttpRequest.HEADERS_RECEIVED, XMLHttpRequest.HEADERS_RECEIVED);
+ is(Ci.nsIDOMNode.DOCUMENT_NODE, Node.DOCUMENT_NODE);
+ is(Ci.nsIDOMKeyEvent, KeyEvent);
+ is(Ci.nsIDOMMouseEvent, MouseEvent);
+ is(Ci.nsIDOMMouseScrollEvent, MouseScrollEvent);
+ is(Ci.nsIDOMMutationEvent, MutationEvent);
+ is(Ci.nsIDOMUIEvent, UIEvent);
+ is(Ci.nsIDOMHTMLMediaElement, HTMLMediaElement);
+ is(Ci.nsIDOMRange, Range);
+ is(Ci.nsIDOMNodeFilter, NodeFilter);
+ is(Ci.nsIDOMXPathResult, XPathResult);
+
+ SimpleTest.finish();
+}
+
+doTest();
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=790732">Mozilla Bug 790732</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+<iframe id="ifr"></iframe>
+</body>
+</html>
diff --git a/js/xpconnect/tests/mochitest/test_bug793969.html b/js/xpconnect/tests/mochitest/test_bug793969.html
new file mode 100644
index 0000000000..1d06d65904
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/test_bug793969.html
@@ -0,0 +1,53 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=793969
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 793969</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=793969">Mozilla Bug 793969</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 793969 **/
+function checkThrows(f, desc, skipMessageCheck) {
+ try {
+ f();
+ ok(false, "Should have thrown for " + desc);
+ } catch (e) {
+ ok(true, "threw correctly");
+ if (!skipMessageCheck)
+ ok(/denied/.exec(e) ||
+ /can't redefine non-configurable property/.exec(e),
+ "Correctly threw a security exception: " + e);
+ }
+}
+
+// NB: These sets will be no-ops (throw in strict mode) because setting an inherited readonly value prop has those semantics.
+checkThrows(function() { "use strict"; location.valueOf = 'hah'; }, 'Shadow with string', /* skipMessageCheck = */ true);
+checkThrows(function() { "use strict"; location.valueOf = function() { return {a: 'hah'};} }, 'Shadow with function', /* skipMessageCheck = */ true);
+checkThrows(function() { Object.defineProperty(location, 'valueOf', { value: function() { return 'hah'; } }); }, 'defineProperty with value');
+checkThrows(function() { delete location.valueOf; Object.defineProperty(location, 'valueOf', { value: function() { return 'hah'; } }); }, 'delete + defineProperty with value');
+checkThrows(function() { Object.defineProperty(location, 'valueOf', { get: function() { return 'hah'; } }); }, 'defineProperty with getter');
+checkThrows(function() { delete location.valueOf; Object.defineProperty(location, 'valueOf', { get: function() { return 'hah'; } }); }, 'delete + defineProperty with getter');
+
+Object.prototype.valueOf = function() { return 'hah'; };
+is(({}).valueOf(), 'hah', "Shadowing on Object.prototype works for vanilla objects");
+is(location.valueOf(), location, "Shadowing on Object.prototype and Location.prototype doesn't for location objects");
+
+location[Symbol.toPrimitive] = function() { return 'hah'; }
+is(location + "", location.toString(), "Should't be able to shadow with toPrimitive");
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/js/xpconnect/tests/mochitest/test_bug800864.html b/js/xpconnect/tests/mochitest/test_bug800864.html
new file mode 100644
index 0000000000..4acee755f5
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/test_bug800864.html
@@ -0,0 +1,51 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=800864
+-->
+<head>
+ <title>Test for Bug 800864</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=800864">Mozilla Bug 800864</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+SimpleTest.waitForExplicitFinish();
+
+function checkThrows(f) {
+ try {
+ f();
+ ok(false, "Didn't throw a security exception like we should");
+ } catch(e) {
+ ok(/denied|insecure/.exec(e), "Should throw security exception. Got: " + e);
+ }
+}
+
+function go() {
+ ifr = document.getElementById('ifr');
+ win = ifr.contentWindow;
+ loc = win.location;
+ ifr.onload = check;
+ win.location = 'http://test1.example.com';
+}
+
+function check() {
+ checkThrows(function() { loc.toString(); });
+ checkThrows(function() { loc.valueOf().toString(); });
+ checkThrows(function() { loc.href; });
+ checkThrows(function() { loc + ''; });
+ SimpleTest.finish();
+}
+
+</script>
+</pre>
+</body>
+<iframe id="ifr" onload="go()" src="file_empty.html"></iframe>
+</html>
diff --git a/js/xpconnect/tests/mochitest/test_bug802557.html b/js/xpconnect/tests/mochitest/test_bug802557.html
new file mode 100644
index 0000000000..4479986b8e
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/test_bug802557.html
@@ -0,0 +1,116 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=802557
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 802557</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+ /** Test for Bug 802557 **/
+ SimpleTest.waitForExplicitFinish();
+
+ function checkThrows(fun, desc) {
+ try {
+ fun();
+ ok(false, "Didn't throw when " + desc);
+ } catch(e) {
+ ok(true, "Threw when " + desc + " " + e);
+ ok(/denied|insecure/.exec(e), "Should be security exception");
+ }
+ }
+
+ var loadCount = 0;
+ function go() {
+ ++loadCount;
+ window.ifr = document.getElementById('ifr');
+ window.iWin = ifr.contentWindow;
+
+ if (loadCount == 1) {
+ gLoc = iWin.location;
+ // Note that accessors pulled off Xrays are currently bound. This is bug 658909.
+ // [getter, description, locationObj, bound]
+ gGetters = [[ location.toString, 'toString from LW' ],
+ [ gLoc.toString, 'toString from XLW' ],
+ [ Object.__lookupGetter__.call(location, 'href'), 'href getter from LW' ],
+ [ Object.__lookupGetter__.call(gLoc, 'href'), 'href getter from XLW' ],
+ [ Object.getOwnPropertyDescriptor(location, 'href').get, 'href getter from location' ],
+ [ Object.getOwnPropertyDescriptor(gLoc, 'href').get, 'href getter from iWin.location' ],
+ [ function() { return this + ''; }, 'implicit conversion via [[DefaultValue]]', /* doMessageCheck = */ true ]];
+ gGetters.forEach(function(item) {
+ try {
+ is(item[0].call(location), location.toString(), 'Same-origin LW: ' + item[1]);
+ is(item[0].call(gLoc), gLoc.toString(), 'Same-origin XLW: ' + item[1]);
+ } catch (e) {
+ ok(false, "Threw while applying " + item[1] + " to same-origin location object: " + e);
+ }
+ });
+ ifr.src = "http://example.org/tests/js/xpconnect/tests/mochitest/file_empty.html";
+ }
+ else if (loadCount == 2) {
+ gGetters.forEach(function(item) {
+ checkThrows(function() { item[0].call(gLoc); },
+ 'call()ing ' + item[1] + ' after navigation cross-origin');
+ });
+ ifr.src = 'http://mochi.test:8888/tests/js/xpconnect/tests/mochitest/file_bug802557.html';
+ }
+ else if (loadCount == 3) {
+ gTestFunctions = ifr.contentWindow.getAllTests();
+ var win = ifr.contentWindow;
+ for (fun in gTestFunctions)
+ is(gTestFunctions[fun](), win.location.toString(), "allowed via " + fun);
+ win.location = 'http://example.org/tests/js/xpconnect/tests/mochitest/file_bug802557.html';
+ }
+ else if (loadCount == 4) {
+ for (fun in gTestFunctions) {
+ var f = gTestFunctions[fun];
+ checkThrows(f, "calling " + fun);
+ }
+
+ // Verify that URL.prototype.toString can't be applied to Location
+ var threw = false;
+ try {
+ URL.prototype.toString.call(location);
+ } catch (e) {
+ threw = true;
+ }
+ ok(threw,
+ "Should not be able to use URL.prototype.toString on a Location instance");
+
+ // Verify that URL.prototype.href getter can't be applied to Location
+ threw = false;
+ var reachedTest = false;
+ try {
+ var get = Object.getOwnPropertyDescriptor(URL.prototype, "href").get;
+ is(typeof(get), "function", "Should have an href getter on URL.prototype");
+ var reachedTest = true;
+ get.call(location);
+ } catch (e) {
+ threw = true;
+ }
+ ok(reachedTest,
+ "Should not be able to find URL.prototype.href getter");
+ ok(threw,
+ "Should not be able to use URL.prototype.href getter on a Location instance");
+ SimpleTest.finish();
+ }
+ }
+
+
+
+</script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=802557">Mozilla Bug 802557</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<iframe id="ifr" onload="go();" src="file_empty.html"></iframe>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/js/xpconnect/tests/mochitest/test_bug803730.html b/js/xpconnect/tests/mochitest/test_bug803730.html
new file mode 100644
index 0000000000..88df8d5477
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/test_bug803730.html
@@ -0,0 +1,41 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=803730
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 803730</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=803730">Mozilla Bug 803730</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 803730 **/
+var foo = {
+ isNode: function(obj) {
+ return !!(obj instanceof Node);
+ }
+};
+
+var elem = document.createElement("span");
+var trueCount = 0,
+ falseCount = 0;
+for (var x = 0; x < 100000; x++) {
+ if (foo.isNode(elem))
+ trueCount++;
+ else
+ falseCount++;
+}
+is(falseCount, 0, "elem instanceof Node working correctly.");
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/js/xpconnect/tests/mochitest/test_bug809547.html b/js/xpconnect/tests/mochitest/test_bug809547.html
new file mode 100644
index 0000000000..17808867d6
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/test_bug809547.html
@@ -0,0 +1,42 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=809547
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 809547</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body onload="go()">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=809547">Mozilla Bug 809547</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 809547 **/
+SimpleTest.waitForExplicitFinish();
+var gObj = {};
+function go() {
+ window.location.expando = gObj;
+ is(window.location.expando, gObj, "Expando appears");
+ SimpleTest.executeSoon(finish);
+}
+
+function finish() {
+ SpecialPowers.forceGC();
+ SpecialPowers.forceCC();
+ SpecialPowers.forceGC();
+ SpecialPowers.forceCC();
+ is(window.location.expando, gObj, "Expando preserved");
+ SimpleTest.finish();
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/js/xpconnect/tests/mochitest/test_bug829872.html b/js/xpconnect/tests/mochitest/test_bug829872.html
new file mode 100644
index 0000000000..07e2a4ca77
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/test_bug829872.html
@@ -0,0 +1,52 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=829872
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 829872</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+ /** Test for Bug 829872 and Bug 968003 **/
+ SimpleTest.waitForExplicitFinish();
+
+ var gLoadCount = 0;
+ function loaded() {
+ if (++gLoadCount == 3)
+ go();
+ }
+
+ async function check(elem, desc) {
+ is(elem.contentDocument, null, "null cross-origin contentDocument for " + desc);
+ ok(await SpecialPowers.spawn(elem, [], () => this.content.eval('frameElement === null;')),
+ "null cross-origin frameElement for " + desc);
+ if (!(elem instanceof elem.ownerDocument.defaultView.HTMLFrameElement))
+ is(elem.getSVGDocument(), null, "null cross-origin getSVGDocument() for " + desc);
+ }
+
+ async function go() {
+ ok(true, "Starting test");
+ await check($('ifr'), "iframe element");
+ await check($('obj'), "object element");
+ await check($('framesetholder').contentDocument.getElementById('fr'), "frameset frame");
+ SimpleTest.finish();
+ }
+
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=829872">Mozilla Bug 829872</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+<iframe id="ifr" onload="loaded();" src="http://example.org/tests/js/xpconnect/tests/mochitest/file_empty.html"></iframe>
+<object id="obj" onload="loaded();" data="http://example.org/tests/js/xpconnect/tests/mochitest/file_empty.html"></object>
+<iframe id="framesetholder" srcdoc="<html><head></head><frameset cols='100%'><frame id='fr' onload='parent.loaded();' src='http://example.org/tests/js/xpconnect/tests/mochitest/file_empty.html' /></frameset></html>"></iframe>
+</body>
+</html>
diff --git a/js/xpconnect/tests/mochitest/test_bug862380.html b/js/xpconnect/tests/mochitest/test_bug862380.html
new file mode 100644
index 0000000000..d604e3d05f
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/test_bug862380.html
@@ -0,0 +1,54 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=862380
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 862380</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+ /** Test for Bug 862380 **/
+ SimpleTest.waitForExplicitFinish();
+ function go() {
+ checkNotEnumerable($('ifr').contentWindow, true);
+ checkNotEnumerable($('ifr').contentWindow.location, false);
+ SimpleTest.finish();
+ }
+
+function checkNotEnumerable(obj, isWindow) {
+ try {
+ const expectedWindow = ["0"];
+ const expectedLocation = [];
+ const expected = isWindow ? expectedWindow : expectedLocation;
+ is(Object.keys(obj).length, expected.length,
+ "Object.keys gives right array length");
+ var actual = [];
+ for (var i in obj)
+ actual.push(i);
+ is(actual.length, expected.length,
+ "Enumeration sees the right number of props");
+ actual.sort();
+ expected.sort();
+ for (var i = 0; i < actual.length; ++i)
+ is(actual[i], expected[i], "Arrays should be the same " + i);
+ } catch (e) {
+ ok(false, "threw: " + e);
+ }
+ }
+
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=862380">Mozilla Bug 862380</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<iframe id="ifr" onload="go();" src="http://example.org/tests/js/xpconnect/tests/mochitest/file_empty.html"></iframe>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/js/xpconnect/tests/mochitest/test_bug865260.html b/js/xpconnect/tests/mochitest/test_bug865260.html
new file mode 100644
index 0000000000..b09b488dcd
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/test_bug865260.html
@@ -0,0 +1,33 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=865260
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 865260</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+ /** Test for Bug 865260 **/
+ SimpleTest.waitForExplicitFinish();
+ function go() {
+ var exn = "nothrow";
+ try { $('ifr').contentWindow['Date']; } catch (e) { exn = e; };
+ ok(!!/denied/.exec(exn), "Threw instead of crashing");
+ SimpleTest.finish();
+ }
+
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=865260">Mozilla Bug 865260</a>
+<p id="display"></p>
+<div id="content">
+<iframe id="ifr" onload="go();" src="http://example.org/tests/js/xpconnect/tests/mochitest/file_empty.html"></iframe>
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/js/xpconnect/tests/mochitest/test_bug871887.html b/js/xpconnect/tests/mochitest/test_bug871887.html
new file mode 100644
index 0000000000..082b2ae746
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/test_bug871887.html
@@ -0,0 +1,43 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=871887
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 871887</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+ /** Test for Bug 871887 **/
+ SimpleTest.waitForExplicitFinish();
+
+ // NB: onstart ends up getting invoked twice, for mysterious and potentially-
+ // IE6-related reasons.
+ function checkpoint(invocant) {
+ ok(true, "onstart called");
+ is(invocant, $('llama'), "this-binding is correct");
+ $('llama').loop = 1;
+ $('llama').scrollDelay = 1;
+ $('llama').scrollAmount = 500;
+ }
+
+ function done(invocant) {
+ is(invocant, $('llama'), "this-binding is correct");
+ ok(true, "onfinish called");
+ SimpleTest.finish();
+ }
+
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=871887">Mozilla Bug 871887</a>
+<p id="display"></p>
+<div id="content">
+<marquee id="llama" onstart="checkpoint(this);" onfinish="done(this);">Watch the Llama</marquee>
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/js/xpconnect/tests/mochitest/test_bug912322.html b/js/xpconnect/tests/mochitest/test_bug912322.html
new file mode 100644
index 0000000000..84ec5f7bbc
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/test_bug912322.html
@@ -0,0 +1,35 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=912322
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 912322</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+ var { AppConstants } = SpecialPowers.ChromeUtils.importESModule(
+ "resource://gre/modules/AppConstants.sys.mjs"
+ );
+ // Test window.controllers.
+ if (AppConstants.RELEASE_OR_BETA) {
+ is(typeof window.controllers, 'object', "shimmed controllers should be available to content in beta and release");
+ } else {
+ is(typeof window.controllers, 'undefined', "controllers should not be available to content in Nightly");
+ }
+ is(typeof SpecialPowers.wrap(window).controllers, 'object', "controllers should be available over Xray");
+
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=912322">Mozilla Bug 912322</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/js/xpconnect/tests/mochitest/test_bug916945.html b/js/xpconnect/tests/mochitest/test_bug916945.html
new file mode 100644
index 0000000000..01c342eea1
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/test_bug916945.html
@@ -0,0 +1,78 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=916945
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 916945</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+ /** Test for Bug 916945 **/
+ SimpleTest.waitForExplicitFinish();
+
+ var gLoadCount = 0;
+ function loaded() {
+ if (++gLoadCount == 2)
+ go();
+ }
+ async function go() {
+ // Both same-origin and cross-origin names should be visible if they're set
+ // on the iframe element.
+ ok('winA' in window, "same-origin named access works");
+ ok(winA instanceof winA.Window, "same-origin named access works");
+ ok('winB' in window, "cross-origin named access works when iframe name matches");
+ is(winB.parent, window, "cross-origin named access works when iframe name matches");
+
+ // Setting the 'name' attribute should propagate to the docshell.
+ var ifrB = document.getElementById('ifrB');
+ await SpecialPowers.spawn(ifrB, [], () => {
+ Assert.equal(this.content.name, 'winB',
+ 'initial attribute value propagates to the docshell');
+ });
+
+ ifrB.setAttribute('name', 'foo');
+ await SpecialPowers.spawn(ifrB, [], () => {
+ Assert.equal(this.content.name, 'foo',
+ 'attribute sets propagate to the docshell');
+ });
+ ok('foo' in window, "names are dynamic if updated via setAttribute");
+ is(foo.parent, window, "names are dynamic if updated via setAttribute");
+
+ // Setting window.name on the subframe should not propagate to the attribute.
+ await SpecialPowers.spawn(ifrB, [], () => {
+ this.content.name = "bar";
+ });
+ is(ifrB.getAttribute('name'), 'foo', 'docshell updates dont propagate to the attribute');
+
+ // When the frame element attribute and docshell name don't match, nothing is returned.
+ ok(!('foo' in window), "frame element name not resolved if it doesn't match the docshell");
+ ok(!('bar' in window), "docshell name not resolved if it doesn't match the frame element");
+
+ // This shouldn't assert.
+ function listener() {
+ document.body.appendChild(ifrA);
+ ifrA.name = "";
+ }
+ ifrA.addEventListener("DOMNodeInserted", listener, {once: true});
+ document.body.appendChild(ifrA);
+
+ SimpleTest.finish();
+ }
+
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=916945">Mozilla Bug 916945</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<iframe id="ifrA" name="winA" onload="loaded();" src="file_empty.html"></iframe>
+<iframe id="ifrB" name="winB" onload="loaded();" src="http://example.org/tests/js/xpconnect/tests/mochitest/file_empty.html"></iframe>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/js/xpconnect/tests/mochitest/test_bug92773.html b/js/xpconnect/tests/mochitest/test_bug92773.html
new file mode 100644
index 0000000000..b1172f377e
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/test_bug92773.html
@@ -0,0 +1,43 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=92773
+-->
+<head>
+ <title>Test for Bug 92773</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=92773">Mozilla Bug 92773</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 92773 **/
+function go() {
+ try {
+ $('ifr').contentWindow.foo;
+ ok(false, "able to access cross-origin getter");
+ } catch (e) {
+ ok(/Permission denied/.exec(e), "unable to access cross-origin getter");
+ }
+
+ SimpleTest.finish();
+}
+SimpleTest.waitForExplicitFinish();
+
+</script>
+</pre>
+
+<iframe id='ifr'
+ src='http://example.com/tests/js/xpconnect/tests/mochitest/bug92773_helper.html'
+ onload="go()">
+</iframe>
+
+</body>
+</html>
diff --git a/js/xpconnect/tests/mochitest/test_bug940783.html b/js/xpconnect/tests/mochitest/test_bug940783.html
new file mode 100644
index 0000000000..0c87e710c1
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/test_bug940783.html
@@ -0,0 +1,62 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=940783
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 940783</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+ /** Test for Bug 940783 **/
+ SimpleTest.waitForExplicitFinish();
+
+ function checkHistoryThrows(hist) {
+ checkThrows(function() { hist.length; });
+ checkThrows(function() { hist.state; });
+ checkThrows(function() { hist.go(); });
+ checkThrows(function() { hist.back(); });
+ checkThrows(function() { hist.forward(); });
+ checkThrows(function() { hist.pushState({}, "foo"); });
+ checkThrows(function() { hist.replaceState({}, "foo"); });
+
+ }
+
+ window.gLoads = 0;
+ function load() {
+ var iwin = $('ifr').contentWindow;
+ ++gLoads;
+ if (gLoads == 1) {
+ window.gHist = iwin.history;
+ iwin.location = "file_empty.html";
+ } else if (gLoads == 2) {
+ checkHistoryThrows(gHist);
+ window.gHist = iwin.history;
+ iwin.location = "http://example.org/tests/js/xpconnect/tests/mochitest/file_empty.html";
+ } else {
+ checkHistoryThrows(gHist);
+ $('ifr').setAttribute('onload', null);
+ SimpleTest.finish();
+ }
+ }
+
+ function checkThrows(fn) {
+ try { fn(); ok(false, "Should have thrown: " + fn.toSource()); }
+ catch (e) { ok(!!/denied|insecure/.exec(e), "Threw correctly: " + e); }
+ }
+
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=940783">Mozilla Bug 940783</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+<iframe id="ifr" onload="load();" src="file_empty.html"></iframe>
+
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/js/xpconnect/tests/mochitest/test_bug960820.html b/js/xpconnect/tests/mochitest/test_bug960820.html
new file mode 100644
index 0000000000..7667fde624
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/test_bug960820.html
@@ -0,0 +1,56 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=960820
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 960820</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+ /** Test for exception stacks crossing **/
+
+ // Synchronous event dispatch creates a new script entry point. At the time
+ // of this writing, an event listener defined in a Sandbox will cause the
+ // SafeJSContext to be pushed to the cx stack, which differs from the JSContext
+ // associated with this DOM window. So we test both kinds of boundaries.
+ var sb = new SpecialPowers.Cu.Sandbox(SpecialPowers.Services.scriptSecurityManager.getSystemPrincipal());
+ sb.win = window;
+ SpecialPowers.Cu.evalInSandbox("win.document.addEventListener('click', " +
+ "function clickHandler() { win.wrappedJSObject.clickCallback(); });", sb);
+ function clickCallback() {
+ var stack = (new Error()).stack;
+ ok(true, "Invoked clickCallback. Stack: " + stack);
+ ok(/clickCallback/.test(stack), "clickCallback should be in the stack");
+ ok(!/clickHandler/.test(stack), "clickHandler should not be in the stack");
+ ok(/dispatchClick/.test(stack), "dispatchClick should be in the stack");
+
+ // Check Components.stack, but first filter through the SpecialPowers junk.
+ var stack = SpecialPowers.wrap(SpecialPowers.Components).stack;
+ while (/testing-common/.test(stack)) {
+ stack = stack.caller;
+ }
+ ok(/clickCallback/.test(stack), "clickCallback should be reachable via Components.stack");
+ ok(/clickHandler/.test(stack.caller), "clickHandler should be reachable via Components.stack");
+ ok(/dispatchClick/.test(stack.caller.caller), "dispatchClick hould be reachable via Components.stack");
+ }
+ function dispatchClick() {
+ document.dispatchEvent(new MouseEvent('click'));
+ }
+ dispatchClick();
+
+
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=960820">Mozilla Bug 960820</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/js/xpconnect/tests/mochitest/test_bug965082.html b/js/xpconnect/tests/mochitest/test_bug965082.html
new file mode 100644
index 0000000000..b2320b52c7
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/test_bug965082.html
@@ -0,0 +1,39 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=965082
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 965082</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+ /** Test for Bug 965082 **/
+ SimpleTest.waitForExplicitFinish();
+
+ function checkThrows(f, msg) {
+ try { f(); ok(false, "Should have thrown: " + msg); }
+ catch (e) { ok(/denied|insecure|can't set prototype/.test(e), "Should throw security exception: " + e + " (" + msg + ")"); }
+ }
+
+ function go() {
+ var protoSetter = Object.getOwnPropertyDescriptor(Object.prototype, '__proto__').set;
+ checkThrows(function() { protoSetter.call(window[0], new Object()); }, "Setting cross-origin Window prototype");
+ checkThrows(function() { protoSetter.call(window[0].location, new Object()); }, "Setting cross-origin Location prototype");
+ SimpleTest.finish();
+ }
+
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=965082">Mozilla Bug 965082</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<iframe id="ifr" onload="go();" src="http://example.org/tests/js/xpconnect/tests/mochitest/file_empty.html"></iframe>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/js/xpconnect/tests/mochitest/test_bug993423.html b/js/xpconnect/tests/mochitest/test_bug993423.html
new file mode 100644
index 0000000000..dbe4cef3cc
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/test_bug993423.html
@@ -0,0 +1,47 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=993423
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 993423</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+ /** Test for Bug 993423 **/
+ SimpleTest.waitForExplicitFinish();
+
+ var sCallbackInvocations = 0;
+ function callback(handlerIsInXBLScope) {
+ ok(!handlerIsInXBLScope, "Event handler should not be in XBL scope");
+ if (++sCallbackInvocations == 2)
+ SimpleTest.finish();
+ }
+
+ function go() {
+ document.querySelector('use').setAttributeNS('http://www.w3.org/1999/xlink',
+ 'href', location.href + '#a');
+ }
+
+ </script>
+</head>
+<body onload="go()";>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=993423">Mozilla Bug 993423</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+<svg>
+ <symbol id="a">
+ <foreignObject>
+ <img src="about:logo" onload="var isInXBL = (function() { return this; })() != window; if (isInXBL) callback = window.wrappedJSObject.callback; callback(isInXBL);">
+ </foreignObject>
+ </symbol>
+ <use />
+</svg>
+</body>
+</html>
diff --git a/js/xpconnect/tests/mochitest/test_class_static_block_worker.html b/js/xpconnect/tests/mochitest/test_class_static_block_worker.html
new file mode 100644
index 0000000000..e3ce063fe9
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/test_class_static_block_worker.html
@@ -0,0 +1,32 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <title>Test class static fields</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript">
+ // Make sure static classes parse in regular context too:
+ class B {
+ static { this.x = 12; }
+ }
+ is(B.x, 12, "static block set class value");
+ console.log("script");
+ function go() {
+ SimpleTest.waitForExplicitFinish();
+
+ let worker = new Worker('class_static_worker.js');
+
+ console.log("message")
+ worker.onmessage = function(e) {
+
+ is(e.data, 12, "correctly allocated class-static containing-class in worker");
+ SimpleTest.finish();
+ }
+ worker.postMessage("get");
+ info("Messages posted");
+ }
+ go();
+ </script>
+ </head>
+
+</html> \ No newline at end of file
diff --git a/js/xpconnect/tests/mochitest/test_crosscompartment_weakmap.html b/js/xpconnect/tests/mochitest/test_crosscompartment_weakmap.html
new file mode 100644
index 0000000000..d7d364fdeb
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/test_crosscompartment_weakmap.html
@@ -0,0 +1,36 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test Cross-Compartment DOM WeakMaps</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<p id="display"></p>
+<script type="application/javascript">
+
+var my_map = new WeakMap();
+
+function setup() {
+ var item = window.frames[0].document.querySelector("body");
+
+ my_map.set(item, "success_string");
+}
+
+function runTest() {
+ setup();
+ SpecialPowers.forceGC();
+ SpecialPowers.forceCC();
+ SpecialPowers.forceGC();
+ SpecialPowers.forceCC();
+ var item = window.frames[0].document.querySelector("body");
+ is(my_map.get(item), "success_string", "Preserve reflectors used cross-compartment as weak map keys.");
+}
+
+</script>
+<iframe src="file_crosscompartment_weakmap.html" onload="runTest()"></iframe>
+
+
+</pre>
+</body>
+</html>
diff --git a/js/xpconnect/tests/mochitest/test_enable_privilege.html b/js/xpconnect/tests/mochitest/test_enable_privilege.html
new file mode 100644
index 0000000000..03043e9ce6
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/test_enable_privilege.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<html lang="en" dir="ltr">
+ <head>
+ <title>Test page for enablePrivilege</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script>
+ function init() {
+ var result = document.getElementById("result");
+ try {
+ /* globals netscape */
+ netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
+ result.textContent = "FAIL";
+ } catch (ex) {
+ result.textContent = "PASS";
+ }
+ }
+ </script>
+ </head>
+ <body>
+ <p id="result"></p>
+ <script>
+ init();
+ is(result.textContent, "PASS", "enablePrivilege should be unavaillable");
+ </script>
+ </body>
+</html>
diff --git a/js/xpconnect/tests/mochitest/test_finalizationRegistry.html b/js/xpconnect/tests/mochitest/test_finalizationRegistry.html
new file mode 100644
index 0000000000..9d9a798cdf
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/test_finalizationRegistry.html
@@ -0,0 +1,168 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <title>Test FinalizationRegistry works in the browser</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript">
+ let registry1, holdings1;
+ let registry2, holdings2;
+ let registry3, holdings3;
+ let registry4, holdings4;
+ let registry5, holdings5;
+ let registry6, holdings6;
+ let registry7, holdings7;
+ let registry8, holdings8;
+ let registry9, holdings9;
+
+ let object4 = {};
+
+ function go() {
+ SimpleTest.waitForExplicitFinish();
+
+ // Registry with no registered objects.
+ holdings1 = [];
+ registry1 = new FinalizationRegistry(v => { holdings1.push(v); });
+
+ // Registry with three registered objects.
+ holdings2 = [];
+ registry2 = new FinalizationRegistry(v => { holdings2.push(v); });
+ registry2.register({}, 1);
+ registry2.register({}, 2);
+ registry2.register({}, 3);
+
+ // Registry with registered object that is then unregistered.
+ holdings3 = [];
+ registry3 = new FinalizationRegistry(v => { holdings3.push(v); });
+ let token3 = {}
+ registry3.register({}, 1, token3);
+ registry3.unregister(token3);
+
+ // Registry with registered object that doesn't die.
+ holdings4 = [];
+ registry4 = new FinalizationRegistry(v => { holdings4.push(v); });
+ registry4.register(object4, 1);
+
+ // Registry observing cyclic JS data structure.
+ holdings5 = [];
+ registry5 = new FinalizationRegistry(v => { holdings5.push(v); });
+ registry5.register(makeJSCycle(4), 5);
+
+ // Registry observing DOM object without preserved wrappers.
+ holdings6 = [];
+ registry6 = new FinalizationRegistry(v => { holdings6.push(v); });
+ registry6.register(document.createElement("div"), 6);
+
+ // Registry observing DOM object with preserved wrappers.
+ holdings7 = [];
+ registry7 = new FinalizationRegistry(v => { holdings7.push(v); });
+ let object = document.createElement("div");
+ object.someProperty = true;
+ registry7.register(object, 7);
+ object = null;
+
+ // Registry observing reachable DOM object without preserved wrappers.
+ holdings8 = [];
+ registry8 = new FinalizationRegistry(v => { holdings8.push(v); });
+ document.body.appendChild(document.createElement("div"));
+ registry8.register(document.body.lastChild, 8);
+
+ // Registry observing cyclic DOM/JS data structure.
+ holdings9 = [];
+ registry9 = new FinalizationRegistry(v => { holdings9.push(v); });
+ registry9.register(makeDOMCycle(4), 9);
+
+ // Need to run full GC/CC/GC cycle to collect cyclic garbage through DOM
+ // and JS heaps.
+ SpecialPowers.DOMWindowUtils.garbageCollect();
+ SpecialPowers.DOMWindowUtils.cycleCollect();
+ SpecialPowers.DOMWindowUtils.garbageCollect();
+
+ // Microtasks are run before cleanup callbacks.
+ Promise.resolve().then(() => {
+ is(holdings1.length, 0);
+ is(holdings2.length, 0);
+ is(holdings3.length, 0);
+ is(holdings4.length, 0);
+ is(holdings5.length, 0);
+ is(holdings6.length, 0);
+ is(holdings7.length, 0);
+ is(holdings8.length, 0);
+ is(holdings9.length, 0);
+ });
+
+ // setTimeout queues a task which will run after cleanup callbacks.
+ setTimeout(task2, 0);
+ }
+
+ function task2() {
+ is(holdings1.length, 0);
+
+ let result = holdings2.sort((a, b) => a - b);
+ is(result.length, 3);
+ is(result[0], 1);
+ is(result[1], 2);
+ is(result[2], 3);
+
+ is(holdings3.length, 0);
+ is(holdings4.length, 0);
+
+ is(holdings5.length, 1);
+ is(holdings5[0], 5);
+
+ is(holdings6.length, 1);
+ is(holdings6[0], 6);
+
+ is(holdings7.length, 1);
+ is(holdings7[0], 7);
+
+ is(holdings8.length, 0);
+
+ is(holdings9.length, 1);
+ is(holdings9[0], 9);
+
+ document.body.removeChild(document.body.lastChild);
+
+ SpecialPowers.DOMWindowUtils.garbageCollect();
+ SpecialPowers.DOMWindowUtils.cycleCollect();
+ SpecialPowers.DOMWindowUtils.garbageCollect();
+
+ setTimeout(task3, 0);
+ }
+
+ function task3() {
+ is(holdings8.length, 1);
+ is(holdings8[0], 8);
+
+ SimpleTest.finish();
+ }
+
+ function makeJSCycle(size) {
+ let first = {};
+ let current = first;
+ for (let i = 0; i < size; i++) {
+ current.next = {};
+ current = current.next;
+ }
+ current.next = first;
+ return first;
+ }
+
+ function makeDOMCycle(size) {
+ let first = {};
+ let current = first;
+ for (let i = 0; i < size; i++) {
+ if (i % 2 === 0) {
+ current.next = document.createElement("div");
+ } else {
+ current.next = {};
+ }
+ current = current.next;
+ }
+ current.next = first;
+ return first;
+ }
+ </script>
+ </head>
+ <body onload="go()"></body>
+</html>
diff --git a/js/xpconnect/tests/mochitest/test_finalizationRegistryInWorker.html b/js/xpconnect/tests/mochitest/test_finalizationRegistryInWorker.html
new file mode 100644
index 0000000000..8393781ce1
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/test_finalizationRegistryInWorker.html
@@ -0,0 +1,40 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <title>Test FinalizationRegistry works in workers</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript">
+ function go() {
+ SimpleTest.waitForExplicitFinish();
+
+ let worker = new Worker('finalizationRegistry_worker.js');
+
+ worker.onevent = (event) => {
+ console.log(event.message);
+ throw event.error;
+ };
+
+ worker.onmessage = (event) => {
+ switch (event.data) {
+ case 'started':
+ worker.postMessage('checkResults');
+ break;
+
+ case 'passed':
+ ok(true, "Tests passed");
+ SimpleTest.finish();
+ break;
+
+ default:
+ console.log(event.data);
+ break;
+ }
+ };
+
+ worker.postMessage('startTest');
+ }
+ </script>
+ </head>
+ <body onload="go()"></body>
+</html>
diff --git a/js/xpconnect/tests/mochitest/test_finalizationRegistry_cleanupSome.html b/js/xpconnect/tests/mochitest/test_finalizationRegistry_cleanupSome.html
new file mode 100644
index 0000000000..d70cfa7172
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/test_finalizationRegistry_cleanupSome.html
@@ -0,0 +1,13 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <title>Test FinalizationRegistry cleanupSome method is not exposed by default</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript">
+ let registry = new FinalizationRegistry(x => 1);
+ is(registry.cleanupSome, undefined,
+ "The cleanupSome method should not be exposed by default");
+ </script>
+ </head>
+</html>
diff --git a/js/xpconnect/tests/mochitest/test_finalizationRegistry_incumbent.html b/js/xpconnect/tests/mochitest/test_finalizationRegistry_incumbent.html
new file mode 100644
index 0000000000..8b6c71c9cf
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/test_finalizationRegistry_incumbent.html
@@ -0,0 +1,62 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <title>Test FinalizationRegistry tracks its incumbent global</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript">
+ let resolvePromise, rejectPromise;
+
+ async function runTest(global, callback) {
+ let fr = new global.FinalizationRegistry(callback);
+ fr.register({}, undefined);
+
+ SpecialPowers.DOMWindowUtils.garbageCollect();
+
+ let promise = new Promise((resolve, reject) => {
+ resolvePromise = resolve;
+ rejectPromise = reject;
+ });
+
+ return promise;
+ }
+
+ function receiveMessage(event) {
+ resolvePromise(event.source.sourceName);
+ }
+
+ async function go() {
+ // This test uses FinalizationRegistry to trigger a callback and reports
+ // the incumbent global in the callback using postMessage. In all cases
+ // the author function that scheduled the callback is runTest(), so the
+ // incumbent global should be the main window.
+
+ SimpleTest.waitForExplicitFinish();
+
+ window.sourceName = "main";
+ window.addEventListener("message", receiveMessage, false);
+
+ let other = window.frames[0];
+ other.sourceName = "other";
+ other.addEventListener("message", receiveMessage, false);
+
+ is(await runTest(window, v => window.postMessage(v)), "main");
+ is(await runTest(window, window.postMessage.bind(window)), "main");
+ is(await runTest(other, v => other.postMessage(v)), "main");
+ is(await runTest(other, other.postMessage.bind(other)), "main");
+ is(await runTest(window, v => other.postMessage(v)), "main");
+ is(await runTest(window, other.postMessage.bind(other)), "main");
+ is(await runTest(other, v => window.postMessage(v)), "main");
+ is(await runTest(other, window.postMessage.bind(window)), "main");
+
+ SimpleTest.finish();
+ }
+ </script>
+ </head>
+ <body onload="go()">
+ <div style="display: none">
+ <!-- A subframe so we have another global to work with -->
+ <iframe></iframe>
+ </div>
+ </body>
+</html>
diff --git a/js/xpconnect/tests/mochitest/test_frameWrapping.html b/js/xpconnect/tests/mochitest/test_frameWrapping.html
new file mode 100644
index 0000000000..4c8a6c428c
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/test_frameWrapping.html
@@ -0,0 +1,37 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+No bug.
+-->
+<head>
+ <title>Test for Bug </title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=">Mozilla Bug </a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** No bug for this test **/
+
+function go() {
+ var win = frames[0];
+ (function() {
+ var utils = SpecialPowers.getDOMWindowUtils(window);
+ is(utils.getClassName(win), "Proxy", "correctly wrap frame elements");
+ })()
+ SimpleTest.finish();
+}
+
+SimpleTest.waitForExplicitFinish();
+
+</script>
+</pre>
+<iframe id="ifr" src="inner.html" onload="go()"></iframe>
+</body>
+</html>
diff --git a/js/xpconnect/tests/mochitest/test_getWebIDLCaller.html b/js/xpconnect/tests/mochitest/test_getWebIDLCaller.html
new file mode 100644
index 0000000000..22a4ae1c3b
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/test_getWebIDLCaller.html
@@ -0,0 +1,49 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=968335
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 968335</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+ /** Test for Cu.getCallerPrincipal (within JS-implemented WebIDL). **/
+
+ SimpleTest.waitForExplicitFinish();
+ SpecialPowers.pushPrefEnv({set: [['dom.expose_test_interfaces', true]]}, go);
+
+
+ function go() {
+ var t = new TestInterfaceJS();
+ is(t.getCallerPrincipal(), location.origin,
+ "Cu.getCallerPrincipal works right within JS-implemented WebIDL");
+
+ try {
+ SpecialPowers.Cu.getWebIDLCallerPrincipal();
+ ok(false, "Should have thrown");
+ } catch (e) {
+ ok(/NOT_AVAILABLE/.test(SpecialPowers.wrap(e)),
+ "API should throw when invoked outside of JS-implemented WebIDL");
+ }
+
+ SimpleTest.finish();
+ }
+
+
+
+
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=968335">Mozilla Bug 968335</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/js/xpconnect/tests/mochitest/test_getweakmapkeys.html b/js/xpconnect/tests/mochitest/test_getweakmapkeys.html
new file mode 100644
index 0000000000..7942d9b945
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/test_getweakmapkeys.html
@@ -0,0 +1,59 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=688277
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Tests for nondeterministicGetWeakMapKeys</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+ /** Test for Bug 688277 **/
+
+ /* Fail gracefully if junk is passed in. */
+ is(SpecialPowers.nondeterministicGetWeakMapKeys(11), undefined,
+ "nondeterministicGetWeakMapKeys should return undefined for non-objects");
+ is(SpecialPowers.nondeterministicGetWeakMapKeys({}), undefined,
+ "nondeterministicGetWeakMapKeys should return undefined for non-weakmap objects");
+ is(SpecialPowers.nondeterministicGetWeakMapKeys(null), undefined,
+ "nondeterministicGetWeakMapKeys should return undefined for null");
+
+ /* return an empty array for an empty WeakMap */
+ let mempty = new WeakMap();
+ is(SpecialPowers.nondeterministicGetWeakMapKeys(mempty).length, 0,
+ "nondeterministicGetWeakMapKeys should return empty array for empty weakmap");
+
+ /* Test freeing/nonfreeing. */
+ let m = new WeakMap();
+ let liveKeys = new Array();
+
+ let add_elements = function () {
+ let k1 = {};
+ m.set(k1, "live1");
+ liveKeys.push(k1);
+
+ let k2 = {};
+ m.set(k2, "dead1");
+
+ let k = {};
+ m.set(k, k); /* simple cycle */
+ };
+
+ add_elements();
+
+ SpecialPowers.exactGC(function () {
+ let keys = SpecialPowers.nondeterministicGetWeakMapKeys(m);
+ is(liveKeys.length, 1, "Wrong number of live keys.");
+ is(keys.length, 1, "Should have one weak map key.");
+ is(m.get(keys[0]), "live1", "live1 should be live");
+ SimpleTest.finish();
+ });
+
+ SimpleTest.waitForExplicitFinish();
+ </script>
+</head>
+<body>
+<p id="display"></p>
+</body>
+</html>
diff --git a/js/xpconnect/tests/mochitest/test_isRemoteProxy.html b/js/xpconnect/tests/mochitest/test_isRemoteProxy.html
new file mode 100644
index 0000000000..864407af9d
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/test_isRemoteProxy.html
@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Tests for Cu.isRemoteProxy</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<script>
+
+async function addFrame(url) {
+ let frame = document.createElement("iframe");
+ frame.src = url;
+ document.body.appendChild(frame);
+
+ await new Promise(resolve => {
+ frame.addEventListener("load", resolve, { once: true });
+ });
+
+ return frame;
+}
+
+add_task(async function() {
+ const { Cu } = SpecialPowers;
+
+ let localFrame = await addFrame("file_empty.html");
+ let remoteFrame = await addFrame(
+ SimpleTest.getTestFileURL("file_empty.html")
+ .replace("mochi.test:8888", "example.com"));
+
+ ok(frames[0] === localFrame.contentWindow, "frames[0] is localFrame");
+ ok(frames[1] === remoteFrame.contentWindow, "frames[1] is remoteFrame");
+
+ ok(!Cu.isRemoteProxy(window), "window is not a remote proxy");
+ ok(!Cu.isRemoteProxy(location), "location is not a remote proxy");
+
+ ok(!Cu.isRemoteProxy(frames[0]), "frames[0] is not a remote proxy");
+ ok(
+ !Cu.isRemoteProxy(frames[0].location),
+ "frames[0].location is not a remote proxy"
+ );
+
+ // The processes will be remote only with isolateEverything strategy
+ const expected = SpecialPowers.effectiveIsolationStrategy() == SpecialPowers.ISOLATION_STRATEGY.IsolateEverything;
+
+ is(Cu.isRemoteProxy(frames[1]), expected,
+ "frames[1] is a remote proxy if Fission is enabled and strategy is isolateEverything");
+ is(Cu.isRemoteProxy(frames[1].location), expected,
+ "frames[1].location is a remote proxy if Fission is enabled and strategy is isolateEverything");
+});
+
+</script>
+</body>
+</html>
diff --git a/js/xpconnect/tests/mochitest/test_nukeContentWindow.html b/js/xpconnect/tests/mochitest/test_nukeContentWindow.html
new file mode 100644
index 0000000000..0db8749b59
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/test_nukeContentWindow.html
@@ -0,0 +1,75 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1322273
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1322273</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1322273">Mozilla Bug 1322273</a>
+
+<iframe id="subframe"></iframe>
+
+<script type="application/javascript">
+"use strict";
+
+function waitForWindowDestroyed(winID, callback) {
+ let observer = {
+ observe: function(subject, topic, data) {
+ let id = subject.QueryInterface(SpecialPowers.Ci.nsISupportsPRUint64).data;
+ if (id != winID) {
+ return;
+ }
+ SpecialPowers.removeObserver(observer, "outer-window-nuked");
+ SpecialPowers.executeSoon(callback);
+ }
+ };
+ SpecialPowers.addObserver(observer, "outer-window-nuked");
+}
+
+add_task(async function() {
+ let frame = $('subframe');
+ frame.srcdoc = "foo";
+ await new Promise(resolve => frame.addEventListener("load", resolve, {once: true}));
+
+ let win = frame.contentWindow;
+ let winID = SpecialPowers.wrap(win).docShell.outerWindowID;
+
+ win.eval("obj = {}");
+ win.obj.foo = {bar: "baz"};
+
+ let obj = win.obj;
+
+ let system = SpecialPowers.Services.scriptSecurityManager.getSystemPrincipal()
+ let sandbox = SpecialPowers.Cu.Sandbox(system);
+
+ sandbox.obj = obj;
+
+ let isWrapperDead = SpecialPowers.Cu.evalInSandbox(`(${
+ function isWrapperDead() {
+ return Cu.isDeadWrapper(obj);
+ }
+ })`,
+ sandbox);
+
+ is(isWrapperDead(), false, "Sandbox wrapper for content window should not be dead");
+ is(obj.foo.bar, "baz", "Content wrappers into and out of content window should be alive");
+
+ // Remove the frame, which should nuke the content window.
+ info("Remove the content frame");
+ frame.remove();
+
+ // Give the nuke wrappers task a chance to run.
+ await new Promise(resolve => waitForWindowDestroyed(winID, resolve));
+
+ is(isWrapperDead(), true, "Sandbox wrapper for content window should be dead");
+ is(obj.foo.bar, "baz", "Content wrappers into and out of content window should be alive");
+});
+</script>
+
+</body>
+</html>
diff --git a/js/xpconnect/tests/mochitest/test_paris_weakmap_keys.html b/js/xpconnect/tests/mochitest/test_paris_weakmap_keys.html
new file mode 100644
index 0000000000..1b786138d4
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/test_paris_weakmap_keys.html
@@ -0,0 +1,94 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=777385
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Tests for WebIDL objects as weak map keys</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+ /** Test for Bug 777385 **/
+
+ SimpleTest.waitForExplicitFinish();
+
+ // We wait to run this until the load event because it needs to access an element.
+ function go() {
+
+ let live_map = new WeakMap;
+
+ let get_div_style = function () {
+ return document.getElementById("mydivname").style;
+ }
+
+ let make_live_map = function () {
+ let my_div_style = get_div_style();
+ let div_fail = false;
+ try {
+ live_map.set(my_div_style, 12345);
+ } catch (e) {
+ div_fail = true;
+ }
+ ok(!div_fail, "Using elem.style as a weak map key should not produce an exception.");
+
+ is(live_map.get(get_div_style()), 12345, "Live map should have live style with right value before GC.");
+
+ }
+
+ make_live_map();
+
+ let tf = new TestFunctions;
+
+ let add_non_isupports2 = function () {
+ let testKey = tf.wrapperCachedNonISupportsObject;
+
+ let testFail = false;
+ try {
+ live_map.set(testKey, 23456);
+ } catch (e) {
+ testFail = true;
+ }
+
+ ok(!testFail, "Using a wrapper cached non-nsISupports class as a weak map key should not produce an exception.");
+
+ is(live_map.get(testKey), 23456, "Live map should have wrapper cached non-nsISupports class right value before GC.");
+ }
+
+ add_non_isupports2();
+
+
+ /* Set up for running precise GC/CC then check the results. */
+
+ SpecialPowers.exactGC(function () {
+ SpecialPowers.forceCC();
+ SpecialPowers.forceGC();
+ SpecialPowers.forceGC();
+
+ is(SpecialPowers.nondeterministicGetWeakMapKeys(live_map).length, 2,
+ "Live WebIDL bindings keys should not be removed from a weak map.");
+
+ is(live_map.get(get_div_style()), 12345, "Live weak map should have live style with right value after GC.");
+ is(live_map.get(tf.wrapperCachedNonISupportsObject), 23456,
+ "Live weak map should have live wrapper cached non-nsISupports class with right value after GC.");
+
+ SimpleTest.finish();
+ });
+
+ }
+
+ SimpleTest.waitForExplicitFinish();
+
+ addLoadEvent(function() {
+ SpecialPowers.pushPrefEnv({set: [['dom.expose_test_interfaces', true]]},
+ go);
+ });
+ </script>
+</head>
+<div></div>
+<div id="mydivname"></div>
+<body>
+<p id="display"></p>
+</body>
+</html>
diff --git a/js/xpconnect/tests/mochitest/test_private_field_dom.html b/js/xpconnect/tests/mochitest/test_private_field_dom.html
new file mode 100644
index 0000000000..4a50c7ca95
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/test_private_field_dom.html
@@ -0,0 +1,221 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=????
+-->
+
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug ????</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ <iframe id="ifr"></iframe>
+</head>
+
+<body>
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1094930">Mozilla Bug 1094930</a>
+ <p id="display"></p>
+ <div id="test_contents">
+ <!-- Extracted from nsHTMLTagList.h -->
+ <applet></applet>
+ <area></area>
+ <audio></audio>
+ <base>
+ </base>
+ <bgsound></bgsound>
+ <blockquote></blockquote>
+
+ <body></body>
+ <br></br>
+ <button></button>
+ <canvas></canvas>
+ <caption></caption>
+ <col>
+ </col>
+ <colgroup></colgroup>
+ <data></data>
+ <datalist></datalist>
+ <del></del>
+ <details></details>
+ <dialog></dialog>
+ <dir></dir>
+ <div></div>
+ <dl></dl>
+ <embed></embed>
+ <fieldset></fieldset>
+ <font></font>
+ <form></form>
+ <frame></frame>
+ <frameset></frameset>
+ <h1></h1>
+ <h2></h2>
+ <h3></h3>
+ <h4></h4>
+ <h5></h5>
+ <h6></h6>
+
+ <head></head>
+ <hr>
+ </hr>
+ <html>
+
+ </html>
+ <iframe></iframe>
+ <img></img>
+ <input></input>
+ <ins></ins>
+ <keygen></keygen>
+ <label></label>
+ <legend></legend>
+ <li></li>
+ <link>
+ </link>
+ <listing></listing>
+ <map></map>
+ <marquee></marquee>
+ <menu></menu>
+ <menuitem>
+ </menuitem>
+ <meta>
+ </meta>
+ <meter></meter>
+ <multicol></multicol>
+ <object></object>
+ <ol></ol>
+ <optgroup></optgroup>
+ <option></option>
+ <output></output>
+ <p></p>
+ <param>
+ </param>
+ <picture></picture>
+ <pre></pre>
+ <progress></progress>
+ <q></q>
+ <script></script>
+ <select></select>
+ <slot></slot>
+ <source>
+ </source>
+ <span></span>
+ <style></style>
+ <summary></summary>
+ <table></table>
+ <tbody></tbody>
+ <td></td>
+ <textarea></textarea>
+ <tfoot></tfoot>
+ <th></th>
+ <thead></thead>
+ <template></template>
+ <time></time>
+ <title></title>
+ <tr></tr>
+ <track>
+ </track>
+ <ul></ul>
+ <video></video>
+ <xmp></xmp>
+ </div>
+ <script type="application/javascript">
+ SimpleTest.waitForExplicitFinish();
+ info("running")
+
+
+ // Because private fields may not be enabled, we construct A via the below eval of an IFFE,
+ // and return early if it syntax errors.
+ var A = undefined;
+ try {
+ A = eval(`(function(){
+ class Base {
+ constructor(o) {
+ return o;
+ }
+ }
+
+ class A extends Base {
+ #x = 1;
+ static g(o) {
+ return o.#x;
+ }
+ static s(o, v) {
+ o.#x = v;
+ }
+ }
+
+ return A;
+ })();`);
+ } catch (e) {
+ is(e instanceof SyntaxError, true, "Threw Syntax Error, Private Fields Not Enabled");
+ is(/private fields are not currently supported/.test(e.message), true, "correct message");
+ }
+
+ if (A instanceof Function) {
+ function assertThrewInstance(f, error) {
+ var threw = true;
+ try {
+ f();
+ threw = false;
+ } catch (e) {
+ // info("Caught " + e.name);
+ is(e instanceof error, true, "Correct Error thrown");
+ }
+ is(threw, true, "Error was thrown");
+ }
+
+ function testNode(node) {
+ info("Testing node " + node.nodeName);
+
+ assertThrewInstance(() => A.g(node), TypeError);
+ assertThrewInstance(() => A.s(node, 'node'), TypeError);
+ // info("Stamping Node");
+ new A(node);
+ // info("Asserting read");
+ is(A.g(node), 1, "correct value read");
+ // info("Setting");
+ A.s(node, 'node');
+ // info("Verifing setter set the value");
+ is(A.g(node), 'node', "updated value read");
+ // info("Verifying we cannot double-init");
+ assertThrewInstance(() => new A(node), TypeError);
+ }
+
+ function testNodeRecursive(node) {
+ testNode(node);
+ for (c of node.children) {
+ testNodeRecursive(c)
+ }
+ }
+
+ const test_contents = document.getElementById('test_contents');
+ testNodeRecursive(test_contents);
+
+ info("Checking Window");
+ // Window itself isn't allowed to host private fields, because it's
+ // a cross-origin object
+ assertThrewInstance(() => A.g(window), TypeError)
+
+ info("Checking Window Prototype Chain")
+ // However, it's prototype chain can.
+ w = Object.getPrototypeOf(window);
+ while (w) {
+ testNode(w);
+ w = Object.getPrototypeOf(w);
+ }
+
+ info("Test Document")
+ testNode(document);
+
+
+ info("Test CSSRuleList")
+ testNode(document.styleSheets[0].cssRules)
+
+ info("Test DOMTokenList")
+ const div = document.createElement('div');
+ testNode(div.classList);
+ }
+ SimpleTest.finish();
+ </script>
+</body>
+
+</html> \ No newline at end of file
diff --git a/js/xpconnect/tests/mochitest/test_private_field_worker.html b/js/xpconnect/tests/mochitest/test_private_field_worker.html
new file mode 100644
index 0000000000..68351000c5
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/test_private_field_worker.html
@@ -0,0 +1,27 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <title>Test Private Fields</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript">
+ function go() {
+ SimpleTest.waitForExplicitFinish();
+
+ let worker = new Worker('private_field_worker.js');
+
+ var allocated = 0;
+ worker.onmessage = function(e) {
+ is(e.data, allocated, "correctly allocated private field-containing-class");
+ SimpleTest.finish();
+ }
+
+ worker.postMessage("allocate"); allocated++;
+ worker.postMessage("count");
+ info("Messages posted");
+ }
+ go();
+ </script>
+ </head>
+
+</html> \ No newline at end of file
diff --git a/js/xpconnect/tests/mochitest/test_sameOriginPolicy.html b/js/xpconnect/tests/mochitest/test_sameOriginPolicy.html
new file mode 100644
index 0000000000..2393e3c24f
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/test_sameOriginPolicy.html
@@ -0,0 +1,109 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=801576
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 801576</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=801576">Mozilla Bug 801576</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for the same-origin policy. **/
+SimpleTest.waitForExplicitFinish();
+
+function check(obj, prop, allowed, write) {
+ var accessed = false;
+ try {
+ if (write) {
+ try {
+ obj[prop] = 2;
+ accessed = true;
+ } catch (e) {}
+ Object.defineProperty(obj, 'prop', {getter: function() {}, setter: null});
+ }
+ else
+ obj[prop];
+ accessed = true;
+ } catch (e) {}
+ is(accessed, allowed, prop + " is correctly (in)accessible for " + (write ? 'write' : 'read'));
+}
+
+var crossOriginReadableWindowProps = ['blur', 'close', 'closed', 'focus',
+ 'frames', 'location', 'length',
+ 'opener', 'parent', 'postMessage',
+ 'self', 'top', 'window',
+ /* indexed and named accessors */
+ '0', 'subframe'];
+
+function isCrossOriginReadable(obj, prop) {
+ if (obj == "Window")
+ return crossOriginReadableWindowProps.includes(prop);
+ if (obj == "Location")
+ return prop == 'replace';
+ return false;
+}
+
+function isCrossOriginWritable(obj, prop) {
+ if (obj == "Window")
+ return prop == 'location';
+ if (obj == "Location")
+ return prop == 'href';
+}
+
+// NB: we don't want to succeed with writes, so we only check them when it should be denied.
+function testAll(sameOrigin) {
+ var win = document.getElementById('ifr').contentWindow;
+
+ // Build a list of properties to check from the properties available on our
+ // window.
+ var props = [];
+ for (var prop in window) { props.push(prop); }
+
+ // On android, this appears to be on the window but not on the iframe. It's
+ // not really relevant to this test, so just skip it.
+ if (props.includes('crypto'))
+ props.splice(props.indexOf('crypto'), 1);
+
+ // Add the named grand-child, since that won't appear on our window.
+ props.push('subframe');
+
+ for (var prop of props) {
+ check(win, prop, sameOrigin || isCrossOriginReadable('Window', prop), /* write = */ false);
+ if (!sameOrigin && !isCrossOriginWritable('Window', prop))
+ check(win, prop, false, /* write = */ true);
+ }
+ for (var prop in window.location) {
+ check(win.location, prop, sameOrigin || isCrossOriginReadable('Location', prop));
+ if (!sameOrigin && !isCrossOriginWritable('Location', prop))
+ check(win.location, prop, false, /* write = */ true);
+ }
+}
+
+var loadCount = 0;
+function go() {
+ ++loadCount;
+ if (loadCount == 1) {
+ testAll(true);
+ document.getElementById('ifr').contentWindow.location = 'http://example.org/tests/js/xpconnect/tests/mochitest/file_empty.html';
+ }
+ else {
+ is(loadCount, 2);
+ testAll(false);
+ SimpleTest.finish();
+ }
+}
+
+</script>
+</pre>
+<iframe id="ifr" onload="go();" src="file_empty.html"></iframe>
+</body>
+</html>
diff --git a/js/xpconnect/tests/mochitest/test_sandbox_fetch.html b/js/xpconnect/tests/mochitest/test_sandbox_fetch.html
new file mode 100644
index 0000000000..3b6cffed4e
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/test_sandbox_fetch.html
@@ -0,0 +1,54 @@
+<!doctype html>
+<html>
+<head>
+ <title>Fetch in JS Sandbox</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"></link>
+ <script src="test_fetch_basic.js"></script>
+</head>
+<body>
+<script type="application/javascript">
+
+SimpleTest.waitForExplicitFinish();
+
+function testHttpFetch(url) {
+ info('fetch: ' + url);
+ return fetch(new Request(url, { method: 'GET' }))
+ .then(response => {
+ is(response.status, 200, 'Response is 200');
+ is(response.url, url, 'Response URL matches');
+ });
+}
+
+function runSandboxTest(testFunc, argString) {
+ is(typeof testFunc, 'function');
+ var resolvePromise;
+ var testPromise = new Promise(r => resolvePromise = r);
+ var finishFuncName = 'finish_' + testFunc.name;
+ SpecialPowers.Cu.exportFunction(_ => resolvePromise(), sb,
+ { defineAs: finishFuncName });
+ SpecialPowers.Cu.evalInSandbox('(' + testFunc.toString() + ')' +
+ '(' + argString + ')' +
+ '.then(' + finishFuncName + ');', sb);
+ return testPromise;
+}
+
+var origin = document.location.origin;
+var properties = ['fetch', 'Blob', 'URL'];
+var sb = new SpecialPowers.Cu.Sandbox(origin,
+ { wantGlobalProperties: properties });
+
+sb.ok = SpecialPowers.Cu.exportFunction(ok, sb);
+sb.is = SpecialPowers.Cu.exportFunction(is, sb);
+sb.info = SpecialPowers.Cu.exportFunction(info, sb);
+
+Promise.resolve()
+ .then(_ => runSandboxTest(testHttpFetch, '"' + origin + window.location.pathname + '"'))
+ .then(_ => runSandboxTest(testAboutURL))
+ .then(_ => runSandboxTest(testDataURL))
+ .then(_ => runSandboxTest(testSameOriginBlobURL))
+ .then(_ => SimpleTest.finish());
+
+</script>
+</body>
+</html>
diff --git a/js/xpconnect/tests/mochitest/test_shadowRealm.html b/js/xpconnect/tests/mochitest/test_shadowRealm.html
new file mode 100644
index 0000000000..311ce68107
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/test_shadowRealm.html
@@ -0,0 +1,34 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test for ShadowRealms</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ <iframe id="ifr"></iframe>
+</head>
+
+<body>
+ <p id="display"></p>
+ <script type="application/javascript">
+ SimpleTest.waitForExplicitFinish();
+ info("running")
+
+ let realm = new ShadowRealm();
+
+ let install = (fun, internal_name) => {
+ let installer = realm.evaluate(`var ${internal_name}; (x) => { ${internal_name} = x}`);
+ installer(fun);
+ }
+
+ install(info, "log");
+ install(is, "is");
+ realm.evaluate(`is(true, true, 'inside realm')`);
+
+ is(realm.evaluate("10"), 10, "ten is ten");
+
+ SimpleTest.finish();
+ </script>
+</body>
+
+</html>
diff --git a/js/xpconnect/tests/mochitest/test_shadowRealm_worker.html b/js/xpconnect/tests/mochitest/test_shadowRealm_worker.html
new file mode 100644
index 0000000000..d26e665f8f
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/test_shadowRealm_worker.html
@@ -0,0 +1,63 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test for ShadowRealms</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ <iframe id="ifr"></iframe>
+</head>
+
+<body>
+ <p id="display"></p>
+ <script type="application/javascript">
+ SimpleTest.waitForExplicitFinish();
+
+ var promise = (async ()=> {
+ var module = await import("./shadow_realm_module.js");
+ is(module.x, 1, "import works outside worker");
+
+ var sr = new ShadowRealm();
+ await sr.importValue("./shadow_realm_module.js", 'x').then((x) => is(x,1, "imported x and got 1"));
+ })();
+
+ promise.then(() => {
+ var worker = new Worker("shadow_realm_worker.js");
+
+ var expected = 0;
+ var recieved = 0;
+
+ function test(str) {
+ worker.postMessage(str);
+ expected++;
+ }
+
+ worker.onmessage = function(e) {
+ console.log("Received Message: "+e.data);
+ recieved++;
+
+ if (e.data == "finish") {
+ is(expected, recieved, "Got the appropriate Number of messages");
+ SimpleTest.finish();
+ return;
+ }
+
+ if (e.data.startsWith("PASS")) {
+ ok(true, e.data);
+ return;
+ }
+
+ ok(false, e.data);
+ };
+
+
+ test("evaluate");
+ test("import");
+
+
+ test("finish");
+ });
+ </script>
+</body>
+
+</html>
diff --git a/js/xpconnect/tests/mochitest/test_spectre_mitigations.html b/js/xpconnect/tests/mochitest/test_spectre_mitigations.html
new file mode 100644
index 0000000000..0340873243
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/test_spectre_mitigations.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Tests for Spectre mitigations</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<script>
+add_task(async function() {
+ const { Cu } = SpecialPowers;
+ const options = Cu.getJSTestingFunctions().getJitCompilerOptions();
+
+ const testMitigation = function(name) {
+ let val = options[name];
+ ok(val === 0 || val === 1, "must be valid JitOption");
+ is(Boolean(val), !SpecialPowers.useRemoteSubframes, "must be enabled if Fission is disabled");
+ };
+
+ testMitigation("spectre.index-masking");
+ testMitigation("spectre.object-mitigations");
+ testMitigation("spectre.string-mitigations");
+ testMitigation("spectre.value-masking");
+});
+</script>
+</body>
+</html>
diff --git a/js/xpconnect/tests/mochitest/test_weakRefs.html b/js/xpconnect/tests/mochitest/test_weakRefs.html
new file mode 100644
index 0000000000..331bb9bb69
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/test_weakRefs.html
@@ -0,0 +1,80 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <title>Test WeakRef works in the browser</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript">
+ let wr1, wr2, wr3, wr4;
+
+ function go() {
+ SimpleTest.waitForExplicitFinish();
+
+ // 1. WeakRef with JS target.
+ wr1 = new WeakRef({});
+
+ // 2. WeakRef with DOM object target (without preserved wrapper).
+ wr2 = new WeakRef(document.createElement("div"));
+
+ // 3. WeakRef with DOM object target (with preserved wrapper).
+ let object = document.createElement("div");
+ object.someProperty = true;
+ wr3 = new WeakRef(object);
+ object = null
+
+ // 4. WeakRef with reachable DOM object target without preserved wrapper.
+ document.body.appendChild(document.createElement("div"));
+ wr4 = new WeakRef(document.body.lastChild);
+
+ // WeakRef should keep the target in the current task.
+ isnot(wr1.deref(), undefined, "deref() should return its target.");
+ isnot(wr2.deref(), undefined, "deref() should return its target.");
+ isnot(wr3.deref(), undefined, "deref() should return its target.");
+ isnot(wr4.deref(), undefined, "deref() should return its target.");
+
+ // WeakRef should keep the target until the end of current task, which
+ // includes promise microtasks.
+ Promise.resolve().then(() => {
+ isnot(wr1.deref(), undefined, "deref() should return its target.");
+ isnot(wr2.deref(), undefined, "deref() should return its target.");
+ isnot(wr3.deref(), undefined, "deref() should return its target.");
+ isnot(wr4.deref(), undefined, "deref() should return its target.");
+ });
+
+ // setTimeout will call its callback in a new task.
+ setTimeout(task2, 0);
+ }
+
+ function task2() {
+ // Trigger a full GC/CC/GC cycle to collect WeakRef targets.
+ SpecialPowers.DOMWindowUtils.garbageCollect();
+ SpecialPowers.DOMWindowUtils.cycleCollect();
+ SpecialPowers.DOMWindowUtils.garbageCollect();
+
+ is(wr1.deref(), undefined, "deref() should return undefined.");
+ is(wr2.deref(), undefined, "deref() should return undefined.");
+ is(wr3.deref(), undefined, "deref() should return undefined.");
+ isnot(wr4.deref(), undefined, "deref() should return its target.");
+
+ // setTimeout will call its callback in a new task.
+ setTimeout(task3, 0);
+ }
+
+ function task3() {
+ document.body.removeChild(document.body.lastChild);
+
+ SpecialPowers.DOMWindowUtils.garbageCollect();
+ SpecialPowers.DOMWindowUtils.cycleCollect();
+ SpecialPowers.DOMWindowUtils.garbageCollect();
+
+ is(wr1.deref(), undefined, "deref() should return undefined.");
+ is(wr2.deref(), undefined, "deref() should return undefined.");
+ is(wr3.deref(), undefined, "deref() should return undefined.");
+ is(wr4.deref(), undefined, "deref() should return undefined.");
+
+ SimpleTest.finish();
+ }
+ </script>
+ </head>
+ <body onload="go()"></body>
+</html>
diff --git a/js/xpconnect/tests/mochitest/test_weakRefs_collected_wrapper.html b/js/xpconnect/tests/mochitest/test_weakRefs_collected_wrapper.html
new file mode 100644
index 0000000000..c7af313d96
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/test_weakRefs_collected_wrapper.html
@@ -0,0 +1,52 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <title>Test WeakRefs with DOM wrappers that get cycle collected</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript">
+ let weakrefs = [];
+
+ function go() {
+ SimpleTest.waitForExplicitFinish();
+
+ // 1. nsISupports-derived object.
+ let doc = document.implementation.createHTMLDocument();
+ weakrefs.push(new WeakRef(doc));
+
+ // 2. non-nsISupports-derived object.
+ let buffer = new AudioBuffer({length: 1, sampleRate: 8000});
+ weakrefs.push(new WeakRef(buffer));
+
+ // 3. nsISupports non-wrapper-cached object.
+ let image = new ImageData(1, 1);
+ weakrefs.push(new WeakRef(image));
+
+ // 4. non-nsISupports non-wrapper-cached object.
+ let iterator = document.fonts.values();
+ weakrefs.push(new WeakRef(iterator));
+
+ for (let wr of weakrefs) {
+ isnot(wr.deref(), undefined, "Check that live wrapper is returned");
+ }
+
+ // setTimeout will call its callback in a new task.
+ setTimeout(task2, 0);
+ }
+
+ function task2() {
+ // Trigger a GC and CC to collect the DOM objects, but no GC to
+ // collect the wrappers.
+ SpecialPowers.DOMWindowUtils.garbageCollect();
+ SpecialPowers.DOMWindowUtils.cycleCollect();
+
+ for (let wr of weakrefs) {
+ is(wr.deref(), undefined, "Check that stale wrapper is not exposed");
+ }
+
+ SimpleTest.finish();
+ }
+ </script>
+ </head>
+ <body onload="go()"></body>
+</html>
diff --git a/js/xpconnect/tests/mochitest/test_weakRefs_cross_compartment.html b/js/xpconnect/tests/mochitest/test_weakRefs_cross_compartment.html
new file mode 100644
index 0000000000..87e509b535
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/test_weakRefs_cross_compartment.html
@@ -0,0 +1,68 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <title>Test WeakRef works when target is in different compartment in the browser</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript">
+ function go() {
+ SimpleTest.waitForExplicitFinish();
+
+ let Cu = SpecialPowers.Cu;
+ let isSameCompartment = Cu.getJSTestingFunctions().isSameCompartment;
+
+ // Open a new window, which will be from different compartment.
+ let win = window.open();
+ is(isSameCompartment(win, window), false,
+ "Test for opeing a window from a different compartment.");
+
+ let wr1, wr2, wr3;
+ {
+ let obj = {};
+
+ // WeakRef and target are both from different compartment.
+ wr1 = new win.WeakRef(new win.Object());
+
+ // WeakRef is same compartment, but target isn't.
+ wr2 = new WeakRef(new win.Object());
+
+ // WeakRef is in different compartment, but target is.
+ wr3 = new win.WeakRef(obj);
+
+ obj = null;
+ }
+
+ // WeakRef should keep the target in the current task.
+ isnot(wr1.deref(), undefined, "wr1.deref() should return its target.");
+ isnot(wr2.deref(), undefined, "wr2.deref() should return its target.");
+ isnot(wr3.deref(), undefined, "we3.deref() should return its target.");
+
+ // Weakref should keep the target until the end of current Job, that
+ // includes microtask(Promise).
+ Promise.resolve().then(() => {
+ isnot(wr1.deref(), undefined,
+ "wr1.deref() should return its target in promise");
+ isnot(wr2.deref(), undefined,
+ "wr2.deref() should return its target in promise");
+ isnot(wr3.deref(), undefined,
+ "wr3.deref() should return its target in promise");
+ });
+
+ // setTimeout will launch a new job and call ClearKeptObjects().
+ setTimeout(() => {
+ // Call gc() forcibly to clear the target of wr.
+ SpecialPowers.DOMWindowUtils.garbageCollect();
+
+ is(wr1.deref(), undefined, "wr1.deref() should return undefined in the new job.");
+ is(wr2.deref(), undefined, "wr2.deref() should return undefined in the new job.");
+ is(wr3.deref(), undefined, "wr3.deref() should return undefined in the new job.");
+
+ win.close();
+ SimpleTest.finish();
+ }, 0);
+ }
+
+ </script>
+ </head>
+ <body onload="go()"></body>
+</html>
diff --git a/js/xpconnect/tests/mochitest/test_weakmaps.html b/js/xpconnect/tests/mochitest/test_weakmaps.html
new file mode 100644
index 0000000000..5e00106fed
--- /dev/null
+++ b/js/xpconnect/tests/mochitest/test_weakmaps.html
@@ -0,0 +1,264 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=668855
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test Cross-Compartment DOM WeakMaps</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+<script type="application/javascript">
+ /** Test for Bug 668855 **/
+
+ SimpleTest.waitForExplicitFinish();
+
+// We wait to run this until the load event because it needs to access an element.
+function go() {
+
+ /* Create a weak reference, with a single-element weak map. */
+ let make_weak_ref = function (obj) {
+ let m = new WeakMap;
+ m.set(obj, {});
+ return m;
+ };
+
+ /* Check to see if a weak reference is dead. */
+ let weak_ref_dead = function (r) {
+ return SpecialPowers.nondeterministicGetWeakMapKeys(r).length == 0;
+ }
+
+ /* Deterministically grab an arbitrary DOM element. */
+ let get_live_dom = function () {
+ let elems = document.getElementsByTagName("a");
+ return elems[0];
+ };
+
+
+ /* Test case from bug 653248, adapted into a standard test.
+
+ This is a dead cycle involving a DOM edge, so the cycle collector can free it. Keys and
+ values reachable only from XPConnect must be marked gray for this to work, and the cycle collector
+ must know the proper structure of the heap.
+
+ */
+ let make_gray_loop = function () {
+ let map = new WeakMap;
+ let div = document.createElement("div");
+ let key = {};
+ let obj = {m:map, k:key};
+ div.addEventListener("foo", function() {
+ // The code below doesn't matter (it won't run). Just pull a
+ // reference to obj.
+ obj.k = 1;
+ obj.m = "bar";
+ });
+ //div.entrain = {m:map, k:key}; This is not sufficient to cause a leak in Fx9
+ map.set(key, div);
+ return make_weak_ref(map);
+ };
+
+ let weakref = make_gray_loop();
+
+
+ /* Combinations of live and dead gray maps/keys. */
+ let basic_weak_ref = null;
+ let basic_map_weak_ref = null;
+ let black_map = new WeakMap;
+ let black_key = {};
+
+ let basic_unit_tests = function () {
+ let live_dom = get_live_dom();
+ let dead_dom = document.createElement("div");
+ let live_map = new WeakMap;
+ let dead_map = new WeakMap;
+ let live_key = {};
+ let dead_key = {};
+
+ // put the live/dead maps/keys into the appropriate DOM elements
+ live_dom.basic_unit_tests = {m:live_map, k:live_key};
+
+ let obj = {m:dead_map, k:dead_key};
+ // dead_dom.hook = {m:dead_map, k:dead_key};
+ dead_dom.addEventListener("foo", function() {
+ // The code below doesn't matter (it won't run). Just pull a
+ // reference to obj.
+ obj.m = 1;
+ obj.k = "2";
+ });
+
+ // Create a dead value, and a weak ref to it.
+ // The loop keeps dead_dom alive unless the CC is smart enough to kill it.
+ let dead_val = {loop:dead_dom};
+ basic_weak_ref = make_weak_ref(dead_val);
+ basic_map_weak_ref = make_weak_ref(dead_map);
+
+ // set up the actual entries. most will die.
+ live_map.set(live_key, {my_key:'live_live'});
+ live_map.set(dead_key, dead_val);
+ live_map.set(black_key, {my_key:'live_black'});
+
+ dead_map.set(live_key, dead_val);
+ dead_map.set(dead_key, dead_val);
+ dead_map.set(black_key, dead_val);
+
+ black_map.set(live_key, {my_key:'black_live'});
+ black_map.set(dead_key, dead_val);
+ black_map.set(black_key, {my_key:'black_black'});
+
+ };
+
+ basic_unit_tests();
+
+
+ let check_basic_unit = function () {
+ let live_dom = get_live_dom();
+ let live_map = live_dom.basic_unit_tests.m;
+ let live_key = live_dom.basic_unit_tests.k;
+
+ // check the dead elements
+ ok(weak_ref_dead(basic_weak_ref), "Dead value was kept alive.");
+ ok(weak_ref_dead(basic_map_weak_ref), "Dead map was kept alive.");
+
+ // check the live gray map
+ is(live_map.get(live_key).my_key, 'live_live',
+ "Live key should have the same value in live map.");
+ is(live_map.get(black_key).my_key, 'live_black',
+ "Black key should have the same value in live map.");
+ is(SpecialPowers.nondeterministicGetWeakMapKeys(live_map).length, 2,
+ "Live map should have two entries.");
+
+ // check the live black map
+ is(black_map.get(live_key).my_key, 'black_live',
+ "Live key should have the same value in black map.");
+ is(black_map.get(black_key).my_key, 'black_black',
+ "Black key should have the same value in black map.");
+ is(SpecialPowers.nondeterministicGetWeakMapKeys(black_map).length, 2,
+ "Black map should have two entries.");
+
+ };
+
+
+ /* live gray chained weak map entries, involving the cycle collector. */
+ let chainm = new WeakMap;
+ let num_chains = 5;
+
+ let nested_cc_maps = function () {
+ let dom = get_live_dom();
+ for(let i = 0; i < num_chains; i++) {
+ let k = {count:i};
+ dom.key = k;
+ dom0 = document.createElement("div");
+ chainm.set(k, {d:dom0});
+ dom = document.createElement("div");
+ dom0.appendChild(dom);
+ };
+ };
+
+ let check_nested_cc_maps = function () {
+ let dom = get_live_dom();
+ let all_ok = true;
+ for(let i = 0; i < num_chains; i++) {
+ let k = dom.key;
+ all_ok = all_ok && k.count == i;
+ dom = chainm.get(k).d.firstChild;
+ };
+ ok(all_ok, "Count was invalid on a key in chained weak map entries.");
+ };
+
+ nested_cc_maps();
+
+
+ /* black weak map, chained garbage cycle involving DOM */
+ let garbage_map = new WeakMap;
+
+ let chained_garbage_maps = function () {
+ let dom0 = document.createElement("div");
+ let dom = dom0;
+ for(let i = 0; i < num_chains; i++) {
+ let k = {};
+ dom.key = k;
+ let new_dom = document.createElement("div");
+ garbage_map.set(k, {val_child:new_dom});
+ dom = document.createElement("div");
+ new_dom.appendChild(dom);
+ };
+ // tie the knot
+ dom.appendChild(dom0);
+ };
+
+ chained_garbage_maps();
+
+
+ /* black weak map, chained garbage cycle involving DOM, XPCWN keys */
+ let wn_garbage_map = new WeakMap;
+
+ let wn_chained_garbage_maps = function () {
+ let dom0 = document.createElement("div");
+ let dom = dom0;
+ for(let i = 0; i < num_chains; i++) {
+ let new_dom = document.createElement("div");
+ wn_garbage_map.set(dom, {wn_val_child:new_dom});
+ dom = document.createElement("div");
+ new_dom.appendChild(dom);
+ };
+ // tie the knot
+ dom.appendChild(dom0);
+ };
+
+ wn_chained_garbage_maps();
+
+
+ /* The cycle collector shouldn't remove a live wrapped native key. */
+
+ let wn_live_map = new WeakMap;
+
+ let make_live_map = function () {
+ let live = get_live_dom();
+ wn_live_map.set(live, {});
+ ok(wn_live_map.has(get_live_dom()), "Live map should have live DOM node before GC.");
+ }
+
+ make_live_map();
+
+ // We're out of ideas for unpreservable natives, now that just about
+ // everything is on webidl, so just don't test those.
+
+ /* set up for running precise GC/CC then checking the results */
+
+ SpecialPowers.exactGC(function () {
+ SpecialPowers.forceCC();
+ SpecialPowers.forceGC();
+ SpecialPowers.forceGC();
+
+ ok(weak_ref_dead(weakref), "Garbage gray cycle should be collected.");
+
+ check_nested_cc_maps();
+
+ is(SpecialPowers.nondeterministicGetWeakMapKeys(garbage_map).length, 0, "Chained garbage weak map entries should not leak.");
+
+ check_basic_unit();
+
+ // fixed by Bug 680937
+ is(SpecialPowers.nondeterministicGetWeakMapKeys(wn_garbage_map).length, 0,
+ "Chained garbage WN weak map entries should not leak.");
+
+ // fixed by Bug 680937
+ is(SpecialPowers.nondeterministicGetWeakMapKeys(wn_live_map).length, 1,
+ "Live weak map wrapped native key should not be removed.");
+
+ ok(wn_live_map.has(get_live_dom()), "Live map should have live dom.");
+
+ SimpleTest.finish();
+ });
+
+}
+ </script>
+</head>
+<div></div>
+<div id="mydivname"></div>
+<body onload="go()";>
+<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=668855" target="_blank">Mozilla Bug 668855</a>
+<p id="display"></p>
+</body>
+</html>
diff --git a/js/xpconnect/tests/moz.build b/js/xpconnect/tests/moz.build
new file mode 100644
index 0000000000..46fab92636
--- /dev/null
+++ b/js/xpconnect/tests/moz.build
@@ -0,0 +1,23 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+TEST_DIRS += [
+ "mochitest",
+ "chrome",
+ "browser",
+ "components/native",
+]
+
+if CONFIG["COMPILE_ENVIRONMENT"]:
+ TEST_DIRS += [
+ "idl",
+ ]
+
+XPCSHELL_TESTS_MANIFESTS += [
+ "unit/xpcshell.toml",
+]
+
+MARIONETTE_MANIFESTS += ["marionette/manifest.toml"]
diff --git a/js/xpconnect/tests/unit/CatBackgroundTaskRegistrationComponents.manifest b/js/xpconnect/tests/unit/CatBackgroundTaskRegistrationComponents.manifest
new file mode 100644
index 0000000000..7bc3763da9
--- /dev/null
+++ b/js/xpconnect/tests/unit/CatBackgroundTaskRegistrationComponents.manifest
@@ -0,0 +1,4 @@
+category test-cat1 Cat1RegisteredComponent @unit.test.com/cat1-registered-component;1
+category test-cat1 Cat1BackgroundTaskRegisteredComponent @unit.test.com/cat1-backgroundtask-registered-component;1 backgroundtask
+category test-cat1 Cat1BackgroundTaskAlwaysRegisteredComponent @unit.test.com/cat1-backgroundtask-alwaysregistered-component;1 backgroundtask=1
+category test-cat1 Cat1BackgroundTaskNotRegisteredComponent @unit.test.com/cat1-backgroundtask-notregistered-component;1 backgroundtask=0
diff --git a/js/xpconnect/tests/unit/CatRegistrationComponents.manifest b/js/xpconnect/tests/unit/CatRegistrationComponents.manifest
new file mode 100644
index 0000000000..11646b0282
--- /dev/null
+++ b/js/xpconnect/tests/unit/CatRegistrationComponents.manifest
@@ -0,0 +1,2 @@
+category test-cat CatRegisteredComponent @unit.test.com/cat-registered-component;1
+category test-cat CatAppRegisteredComponent @unit.test.com/cat-app-registered-component;1 application={adb42a9a-0d19-4849-bf4d-627614ca19be}
diff --git a/js/xpconnect/tests/unit/ReturnCodeChild.jsm b/js/xpconnect/tests/unit/ReturnCodeChild.jsm
new file mode 100644
index 0000000000..bf74453969
--- /dev/null
+++ b/js/xpconnect/tests/unit/ReturnCodeChild.jsm
@@ -0,0 +1,51 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+var EXPORTED_SYMBOLS = ["ReturnCodeChild"];
+
+function xpcWrap(obj, iface) {
+ let ifacePointer = Cc[
+ "@mozilla.org/supports-interface-pointer;1"
+ ].createInstance(Ci.nsISupportsInterfacePointer);
+
+ ifacePointer.data = obj;
+ return ifacePointer.data.QueryInterface(iface);
+}
+
+var ReturnCodeChild = {
+ QueryInterface: ChromeUtils.generateQI(["nsIXPCTestReturnCodeChild"]),
+
+ doIt(behaviour) {
+ switch (behaviour) {
+ case Ci.nsIXPCTestReturnCodeChild.CHILD_SHOULD_THROW:
+ throw(new Error("a requested error"));
+ case Ci.nsIXPCTestReturnCodeChild.CHILD_SHOULD_RETURN_SUCCESS:
+ return;
+ case Ci.nsIXPCTestReturnCodeChild.CHILD_SHOULD_RETURN_RESULTCODE:
+ Components.returnCode = Cr.NS_ERROR_FAILURE;
+ return;
+ case Ci.nsIXPCTestReturnCodeChild.CHILD_SHOULD_NEST_RESULTCODES:
+ // Use xpconnect to create another instance of *this* component and
+ // call that. This way we have crossed the xpconnect bridge twice.
+
+ // We set *our* return code early - this should be what is returned
+ // to our caller, even though our "inner" component will set it to
+ // a different value that we will see (but our caller should not)
+ Components.returnCode = Cr.NS_ERROR_UNEXPECTED;
+ // call the child asking it to do the .returnCode set.
+ let sub = xpcWrap(ReturnCodeChild, Ci.nsIXPCTestReturnCodeChild);
+ let childResult = Cr.NS_OK;
+ try {
+ sub.doIt(Ci.nsIXPCTestReturnCodeChild.CHILD_SHOULD_RETURN_RESULTCODE);
+ } catch (ex) {
+ childResult = ex.result;
+ }
+ // write it to the console so the test can check it.
+ let consoleService = Cc["@mozilla.org/consoleservice;1"]
+ .getService(Ci.nsIConsoleService);
+ consoleService.logStringMessage("nested child returned " + childResult);
+ return;
+ }
+ }
+};
diff --git a/js/xpconnect/tests/unit/ReturnCodeChild.sys.mjs b/js/xpconnect/tests/unit/ReturnCodeChild.sys.mjs
new file mode 100644
index 0000000000..4d3120da33
--- /dev/null
+++ b/js/xpconnect/tests/unit/ReturnCodeChild.sys.mjs
@@ -0,0 +1,49 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+function xpcWrap(obj, iface) {
+ let ifacePointer = Cc[
+ "@mozilla.org/supports-interface-pointer;1"
+ ].createInstance(Ci.nsISupportsInterfacePointer);
+
+ ifacePointer.data = obj;
+ return ifacePointer.data.QueryInterface(iface);
+}
+
+export var ReturnCodeChild = {
+ QueryInterface: ChromeUtils.generateQI(["nsIXPCTestReturnCodeChild"]),
+
+ doIt(behaviour) {
+ switch (behaviour) {
+ case Ci.nsIXPCTestReturnCodeChild.CHILD_SHOULD_THROW:
+ throw(new Error("a requested error"));
+ case Ci.nsIXPCTestReturnCodeChild.CHILD_SHOULD_RETURN_SUCCESS:
+ return;
+ case Ci.nsIXPCTestReturnCodeChild.CHILD_SHOULD_RETURN_RESULTCODE:
+ Components.returnCode = Cr.NS_ERROR_FAILURE;
+ return;
+ case Ci.nsIXPCTestReturnCodeChild.CHILD_SHOULD_NEST_RESULTCODES:
+ // Use xpconnect to create another instance of *this* component and
+ // call that. This way we have crossed the xpconnect bridge twice.
+
+ // We set *our* return code early - this should be what is returned
+ // to our caller, even though our "inner" component will set it to
+ // a different value that we will see (but our caller should not)
+ Components.returnCode = Cr.NS_ERROR_UNEXPECTED;
+ // call the child asking it to do the .returnCode set.
+ let sub = xpcWrap(ReturnCodeChild, Ci.nsIXPCTestReturnCodeChild);
+ let childResult = Cr.NS_OK;
+ try {
+ sub.doIt(Ci.nsIXPCTestReturnCodeChild.CHILD_SHOULD_RETURN_RESULTCODE);
+ } catch (ex) {
+ childResult = ex.result;
+ }
+ // write it to the console so the test can check it.
+ let consoleService = Cc["@mozilla.org/consoleservice;1"]
+ .getService(Ci.nsIConsoleService);
+ consoleService.logStringMessage("nested child returned " + childResult);
+ return;
+ }
+ }
+};
diff --git a/js/xpconnect/tests/unit/TestBlob.jsm b/js/xpconnect/tests/unit/TestBlob.jsm
new file mode 100644
index 0000000000..7d67963dd6
--- /dev/null
+++ b/js/xpconnect/tests/unit/TestBlob.jsm
@@ -0,0 +1,48 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+var EXPORTED_SYMBOLS = ["TestBlob"];
+
+const Assert = {
+ ok(cond, text) {
+ // we don't have the test harness' utilities in this scope, so we need this
+ // little helper. In the failure case, the exception is propagated to the
+ // caller in the main run_test() function, and the test fails.
+ if (!cond)
+ throw "Failed check: " + text;
+ }
+};
+
+var TestBlob = {
+ doTest: function() {
+ // throw if anything goes wrong
+ let testContent = "<a id=\"a\"><b id=\"b\">hey!<\/b><\/a>";
+ // should be able to construct a file
+ var f1 = new Blob([testContent], {"type" : "text/xml"});
+
+ // do some tests
+ Assert.ok(f1 instanceof Blob, "Should be a DOM Blob");
+
+ Assert.ok(!(f1 instanceof File), "Should not be a DOM File");
+
+ Assert.ok(f1.type == "text/xml", "Wrong type");
+
+ Assert.ok(f1.size == testContent.length, "Wrong content size");
+
+ var f2 = new Blob();
+ Assert.ok(f2.size == 0, "Wrong size");
+ Assert.ok(f2.type == "", "Wrong type");
+
+ var threw = false;
+ try {
+ // Needs a valid ctor argument
+ var f2 = new Blob(Date(132131532));
+ } catch (e) {
+ threw = true;
+ }
+ Assert.ok(threw, "Passing a random object should fail");
+
+ return true;
+ },
+};
diff --git a/js/xpconnect/tests/unit/TestFile.jsm b/js/xpconnect/tests/unit/TestFile.jsm
new file mode 100644
index 0000000000..4e9162d495
--- /dev/null
+++ b/js/xpconnect/tests/unit/TestFile.jsm
@@ -0,0 +1,78 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+var EXPORTED_SYMBOLS = ["TestFile"];
+
+const Assert = {
+ ok(cond, text) {
+ // we don't have the test harness' utilities in this scope, so we need this
+ // little helper. In the failure case, the exception is propagated to the
+ // caller in the main run_test() function, and the test fails.
+ if (!cond)
+ throw "Failed check: " + text;
+ }
+};
+
+var TestFile = {
+ doTest: function(cb) {
+ // throw if anything goes wrong
+
+ // find the current directory path
+ var file = Cc["@mozilla.org/file/directory_service;1"]
+ .getService(Ci.nsIProperties)
+ .get("CurWorkD", Ci.nsIFile);
+ file.append("xpcshell.toml");
+
+ // should be able to construct a file
+ var f1, f2;
+ Promise.all([
+ File.createFromFileName(file.path).then(f => { f1 = f; }),
+ File.createFromNsIFile(file).then(f => { f2 = f; }),
+ ])
+ .then(() => {
+ // do some tests
+ Assert.ok(f1 instanceof File, "Should be a DOM File");
+ Assert.ok(f2 instanceof File, "Should be a DOM File");
+
+ Assert.ok(f1.name == "xpcshell.toml", "Should be the right file");
+ Assert.ok(f2.name == "xpcshell.toml", "Should be the right file");
+
+ Assert.ok(f1.type == "", "Should be the right type");
+ Assert.ok(f2.type == "", "Should be the right type");
+ })
+ .then(() => {
+ var threw = false;
+ try {
+ // Needs a ctor argument
+ var f7 = new File();
+ } catch (e) {
+ threw = true;
+ }
+ Assert.ok(threw, "No ctor arguments should throw");
+
+ var threw = false;
+ try {
+ // Needs a valid ctor argument
+ var f7 = new File(Date(132131532));
+ } catch (e) {
+ threw = true;
+ }
+ Assert.ok(threw, "Passing a random object should fail");
+
+ // Directories fail
+ var dir = Cc["@mozilla.org/file/directory_service;1"]
+ .getService(Ci.nsIProperties)
+ .get("CurWorkD", Ci.nsIFile);
+ return File.createFromNsIFile(dir)
+ })
+ .then(() => {
+ Assert.ok(false, "Can't create a File object for a directory");
+ }, () => {
+ Assert.ok(true, "Can't create a File object for a directory");
+ })
+ .then(() => {
+ cb(true);
+ });
+ },
+};
diff --git a/js/xpconnect/tests/unit/api_script.js b/js/xpconnect/tests/unit/api_script.js
new file mode 100644
index 0000000000..de4a0a6b59
--- /dev/null
+++ b/js/xpconnect/tests/unit/api_script.js
@@ -0,0 +1,26 @@
+"use strict";
+
+// This is a test script similar to those used by ExtensionAPIs.
+// https://searchfox.org/mozilla-central/source/toolkit/components/extensions/parent
+
+let module3, module4;
+
+// This should work across ESR 102 and Firefox 103+.
+if (ChromeUtils.importESModule) {
+ module3 = ChromeUtils.importESModule("resource://test/esmified-3.sys.mjs");
+ module4 = ChromeUtils.importESModule("resource://test/esmified-4.sys.mjs");
+} else {
+ module3 = ChromeUtils.import("resource://test/esmified-3.jsm");
+ module4 = ChromeUtils.import("resource://test/esmified-4.jsm");
+}
+
+injected3.obj.value += 3;
+module3.obj.value += 3;
+module4.obj.value += 4;
+
+this.testResults = {
+ injected3: injected3.obj.value,
+ module3: module3.obj.value,
+ sameInstance3: injected3 === module3,
+ module4: module4.obj.value,
+};
diff --git a/js/xpconnect/tests/unit/bogus_element_type.jsm b/js/xpconnect/tests/unit/bogus_element_type.jsm
new file mode 100644
index 0000000000..882ca56809
--- /dev/null
+++ b/js/xpconnect/tests/unit/bogus_element_type.jsm
@@ -0,0 +1 @@
+var EXPORTED_SYMBOLS = [{}];
diff --git a/js/xpconnect/tests/unit/bogus_exports_type.jsm b/js/xpconnect/tests/unit/bogus_exports_type.jsm
new file mode 100644
index 0000000000..4b306e4e89
--- /dev/null
+++ b/js/xpconnect/tests/unit/bogus_exports_type.jsm
@@ -0,0 +1 @@
+var EXPORTED_SYMBOLS = "not an array";
diff --git a/js/xpconnect/tests/unit/bug451678_subscript.js b/js/xpconnect/tests/unit/bug451678_subscript.js
new file mode 100644
index 0000000000..72ff49a2d6
--- /dev/null
+++ b/js/xpconnect/tests/unit/bug451678_subscript.js
@@ -0,0 +1,5 @@
+var tags = [];
+function makeTags() {}
+
+// This will be the return value of the script.
+42
diff --git a/js/xpconnect/tests/unit/contextual.sys.mjs b/js/xpconnect/tests/unit/contextual.sys.mjs
new file mode 100644
index 0000000000..beb90123d5
--- /dev/null
+++ b/js/xpconnect/tests/unit/contextual.sys.mjs
@@ -0,0 +1,3 @@
+export const ns = ChromeUtils.importESModule("resource://test/esmified-1.sys.mjs", {
+ global: "contextual",
+});
diff --git a/js/xpconnect/tests/unit/contextual_worker.js b/js/xpconnect/tests/unit/contextual_worker.js
new file mode 100644
index 0000000000..a6344daf1e
--- /dev/null
+++ b/js/xpconnect/tests/unit/contextual_worker.js
@@ -0,0 +1,14 @@
+onmessage = event => {
+ const ns1 = ChromeUtils.importESModule("resource://test/esmified-1.sys.mjs", {
+ global: "current",
+ });
+
+ const ns2 = ChromeUtils.importESModule("resource://test/esmified-1.sys.mjs", {
+ global: "contextual",
+ });
+
+ const equal1 = ns1 == ns2;
+ const equal2 = ns1.obj == ns2.obj;
+
+ postMessage({ equal1, equal2 });
+};
diff --git a/js/xpconnect/tests/unit/envChain.jsm b/js/xpconnect/tests/unit/envChain.jsm
new file mode 100644
index 0000000000..c60b032fcc
--- /dev/null
+++ b/js/xpconnect/tests/unit/envChain.jsm
@@ -0,0 +1,20 @@
+var qualified = 10;
+// NOTE: JSM cannot have unqualified name.
+let lexical = 30;
+this.prop = 40;
+
+const funcs = Cu.getJSTestingFunctions();
+const envs = [];
+let env = funcs.getInnerMostEnvironmentObject();
+while (env) {
+ envs.push({
+ type: funcs.getEnvironmentObjectType(env) || "*BackstagePass*",
+ qualified: !!Object.getOwnPropertyDescriptor(env, "qualified"),
+ prop: !!Object.getOwnPropertyDescriptor(env, "prop"),
+ lexical: !!Object.getOwnPropertyDescriptor(env, "lexical"),
+ });
+
+ env = funcs.getEnclosingEnvironmentObject(env);
+}
+
+const EXPORTED_SYMBOLS = ["envs"];
diff --git a/js/xpconnect/tests/unit/envChain_subscript.jsm b/js/xpconnect/tests/unit/envChain_subscript.jsm
new file mode 100644
index 0000000000..473f6eb2d9
--- /dev/null
+++ b/js/xpconnect/tests/unit/envChain_subscript.jsm
@@ -0,0 +1,27 @@
+const target = {};
+Services.scriptloader.loadSubScript(`data:,
+var qualified = 10;
+unqualified = 20;
+let lexical = 30;
+this.prop = 40;
+
+const funcs = Cu.getJSTestingFunctions();
+const envs = [];
+let env = funcs.getInnerMostEnvironmentObject();
+while (env) {
+ envs.push({
+ type: funcs.getEnvironmentObjectType(env) || "*BackstagePass*",
+ qualified: !!Object.getOwnPropertyDescriptor(env, "qualified"),
+ unqualified: !!Object.getOwnPropertyDescriptor(env, "unqualified"),
+ lexical: !!Object.getOwnPropertyDescriptor(env, "lexical"),
+ prop: !!Object.getOwnPropertyDescriptor(env, "prop"),
+ });
+
+ env = funcs.getEnclosingEnvironmentObject(env);
+}
+
+this.ENVS = envs;
+`, target);
+
+const envs = target.ENVS;
+const EXPORTED_SYMBOLS = ["envs"];
diff --git a/js/xpconnect/tests/unit/environment_checkscript.jsm b/js/xpconnect/tests/unit/environment_checkscript.jsm
new file mode 100644
index 0000000000..b4dc452b8e
--- /dev/null
+++ b/js/xpconnect/tests/unit/environment_checkscript.jsm
@@ -0,0 +1,13 @@
+var EXPORTED_SYMBOLS = ["bound"];
+
+var bound = "";
+
+try { void vu; bound += "vu,"; } catch (e) {}
+try { void vq; bound += "vq,"; } catch (e) {}
+try { void vl; bound += "vl,"; } catch (e) {}
+try { void gt; bound += "gt,"; } catch (e) {}
+try { void ed; bound += "ed,"; } catch (e) {}
+try { void ei; bound += "ei,"; } catch (e) {}
+try { void fo; bound += "fo,"; } catch (e) {}
+try { void fi; bound += "fi,"; } catch (e) {}
+try { void fd; bound += "fd,"; } catch (e) {}
diff --git a/js/xpconnect/tests/unit/environment_loadscript.jsm b/js/xpconnect/tests/unit/environment_loadscript.jsm
new file mode 100644
index 0000000000..0e5a0208ae
--- /dev/null
+++ b/js/xpconnect/tests/unit/environment_loadscript.jsm
@@ -0,0 +1,16 @@
+var EXPORTED_SYMBOLS = ["target", "bound"];
+
+var bound = "";
+var target = {};
+Services.scriptloader.loadSubScript("resource://test/environment_script.js", target);
+
+// Check global bindings
+try { void vu; bound += "vu,"; } catch (e) {}
+try { void vq; bound += "vq,"; } catch (e) {}
+try { void vl; bound += "vl,"; } catch (e) {}
+try { void gt; bound += "gt,"; } catch (e) {}
+try { void ed; bound += "ed,"; } catch (e) {}
+try { void ei; bound += "ei,"; } catch (e) {}
+try { void fo; bound += "fo,"; } catch (e) {}
+try { void fi; bound += "fi,"; } catch (e) {}
+try { void fd; bound += "fd,"; } catch (e) {}
diff --git a/js/xpconnect/tests/unit/environment_script.js b/js/xpconnect/tests/unit/environment_script.js
new file mode 100644
index 0000000000..18490541ad
--- /dev/null
+++ b/js/xpconnect/tests/unit/environment_script.js
@@ -0,0 +1,14 @@
+let strict = (function() { return this; })() === undefined;
+
+// Allow this to be used as a JSM
+var EXPORTED_SYMBOLS = [];
+
+if (!strict) vu = 1; // Unqualified Variable
+var vq = 2; // Qualified Variable
+let vl = 3; // Lexical
+this.gt = 4; // Global This
+eval("this.ed = 5"); // Direct Eval
+(1,eval)("this.ei = 6"); // Indirect Eval
+(new Function("this.fo = 7"))(); // Dynamic Function Object
+if (!strict) (function() { this.fi = 8; })(); // Indirect Function This
+function fd_() { this.fd = 9; }; if (!strict) fd_(); // Direct Function Implicit
diff --git a/js/xpconnect/tests/unit/error_export.sys.mjs b/js/xpconnect/tests/unit/error_export.sys.mjs
new file mode 100644
index 0000000000..7f0f1ec979
--- /dev/null
+++ b/js/xpconnect/tests/unit/error_export.sys.mjs
@@ -0,0 +1,2 @@
+export function something() {
+}
diff --git a/js/xpconnect/tests/unit/error_import.sys.mjs b/js/xpconnect/tests/unit/error_import.sys.mjs
new file mode 100644
index 0000000000..2bbeef5da2
--- /dev/null
+++ b/js/xpconnect/tests/unit/error_import.sys.mjs
@@ -0,0 +1 @@
+import { something } from "./something.sys.mjs";
diff --git a/js/xpconnect/tests/unit/error_other.sys.mjs b/js/xpconnect/tests/unit/error_other.sys.mjs
new file mode 100644
index 0000000000..f6d220f17e
--- /dev/null
+++ b/js/xpconnect/tests/unit/error_other.sys.mjs
@@ -0,0 +1 @@
+a =
diff --git a/js/xpconnect/tests/unit/es6import.js b/js/xpconnect/tests/unit/es6import.js
new file mode 100644
index 0000000000..79d76849fd
--- /dev/null
+++ b/js/xpconnect/tests/unit/es6import.js
@@ -0,0 +1 @@
+export let value = 1;
diff --git a/js/xpconnect/tests/unit/es6module.js b/js/xpconnect/tests/unit/es6module.js
new file mode 100644
index 0000000000..a160895a01
--- /dev/null
+++ b/js/xpconnect/tests/unit/es6module.js
@@ -0,0 +1,6 @@
+export let loadCount = 0;
+loadCount++;
+
+export let value = 0;
+import {value as importedValue} from "./es6import.js";
+value = importedValue + 1;
diff --git a/js/xpconnect/tests/unit/es6module_absolute.js b/js/xpconnect/tests/unit/es6module_absolute.js
new file mode 100644
index 0000000000..d74732d296
--- /dev/null
+++ b/js/xpconnect/tests/unit/es6module_absolute.js
@@ -0,0 +1,4 @@
+import { x as x1 } from "resource://test/es6module_absolute2.js";
+import { x as x2 } from "./es6module_absolute2.js";
+export const absoluteX = x1;
+export const relativeX = x2;
diff --git a/js/xpconnect/tests/unit/es6module_absolute2.js b/js/xpconnect/tests/unit/es6module_absolute2.js
new file mode 100644
index 0000000000..d9d1342a3f
--- /dev/null
+++ b/js/xpconnect/tests/unit/es6module_absolute2.js
@@ -0,0 +1 @@
+export const x = { value: 10 };
diff --git a/js/xpconnect/tests/unit/es6module_cycle_a.js b/js/xpconnect/tests/unit/es6module_cycle_a.js
new file mode 100644
index 0000000000..62e88d17e2
--- /dev/null
+++ b/js/xpconnect/tests/unit/es6module_cycle_a.js
@@ -0,0 +1,9 @@
+export const name = "a";
+
+import { name as bName } from "./es6module_cycle_b.js";
+
+export let loaded = true;
+
+export function getValueFromB() {
+ return bName;
+}
diff --git a/js/xpconnect/tests/unit/es6module_cycle_b.js b/js/xpconnect/tests/unit/es6module_cycle_b.js
new file mode 100644
index 0000000000..32725f0f0a
--- /dev/null
+++ b/js/xpconnect/tests/unit/es6module_cycle_b.js
@@ -0,0 +1,9 @@
+export const name = "b";
+
+import { name as cName } from "./es6module_cycle_c.js";
+
+export let loaded = true;
+
+export function getValueFromC() {
+ return cName;
+}
diff --git a/js/xpconnect/tests/unit/es6module_cycle_c.js b/js/xpconnect/tests/unit/es6module_cycle_c.js
new file mode 100644
index 0000000000..2fd2f6e3eb
--- /dev/null
+++ b/js/xpconnect/tests/unit/es6module_cycle_c.js
@@ -0,0 +1,9 @@
+export const name = "c";
+
+import { name as aName } from "./es6module_cycle_a.js";
+
+export let loaded = true;
+
+export function getValueFromA() {
+ return aName;
+}
diff --git a/js/xpconnect/tests/unit/es6module_devtoolsLoader.js b/js/xpconnect/tests/unit/es6module_devtoolsLoader.js
new file mode 100644
index 0000000000..30e4f13863
--- /dev/null
+++ b/js/xpconnect/tests/unit/es6module_devtoolsLoader.js
@@ -0,0 +1 @@
+export const object = { uniqueObjectPerLoader: true };
diff --git a/js/xpconnect/tests/unit/es6module_devtoolsLoader.sys.mjs b/js/xpconnect/tests/unit/es6module_devtoolsLoader.sys.mjs
new file mode 100644
index 0000000000..c7de54c82f
--- /dev/null
+++ b/js/xpconnect/tests/unit/es6module_devtoolsLoader.sys.mjs
@@ -0,0 +1,29 @@
+export let x = 0;
+
+export function increment() {
+ x++;
+};
+
+import { object } from "resource://test/es6module_devtoolsLoader.js";
+export const importedObject = object;
+
+const importTrue = ChromeUtils.importESModule("resource://test/es6module_devtoolsLoader.js", { loadInDevToolsLoader : true });
+export const importESModuleTrue = importTrue.object;
+
+const importFalse = ChromeUtils.importESModule("resource://test/es6module_devtoolsLoader.js", { loadInDevToolsLoader : false });
+export const importESModuleFalse = importFalse.object;
+
+const importNull = ChromeUtils.importESModule("resource://test/es6module_devtoolsLoader.js", {});
+export const importESModuleNull = importNull.object;
+
+const importNull2 = ChromeUtils.importESModule("resource://test/es6module_devtoolsLoader.js");
+export const importESModuleNull2 = importNull2.object;
+
+const lazy = {};
+ChromeUtils.defineESModuleGetters(lazy, {
+ object: "resource://test/es6module_devtoolsLoader.js",
+});
+
+export function importLazy() {
+ return lazy.object;
+}
diff --git a/js/xpconnect/tests/unit/es6module_devtoolsLoader_only.js b/js/xpconnect/tests/unit/es6module_devtoolsLoader_only.js
new file mode 100644
index 0000000000..4d995c5cfb
--- /dev/null
+++ b/js/xpconnect/tests/unit/es6module_devtoolsLoader_only.js
@@ -0,0 +1 @@
+export const object = { onlyLoadedFromDevToolsModule: true };
diff --git a/js/xpconnect/tests/unit/es6module_dynamic_import.js b/js/xpconnect/tests/unit/es6module_dynamic_import.js
new file mode 100644
index 0000000000..0b73a3daf4
--- /dev/null
+++ b/js/xpconnect/tests/unit/es6module_dynamic_import.js
@@ -0,0 +1,25 @@
+import { getCounter, setCounter } from "./es6module_dynamic_import_static.js";
+
+let resolve;
+
+export const result = new Promise(r => { resolve = r; });
+
+import("./es6module_dynamic_import2.js").then(ns => {
+ resolve(ns);
+});
+
+export function doImport() {
+ return import("./es6module_dynamic_import3.js");
+}
+
+export function callGetCounter() {
+ return getCounter();
+}
+
+export function callSetCounter(v) {
+ setCounter(v);
+}
+
+export function doImportStatic() {
+ return import("./es6module_dynamic_import_static.js");
+}
diff --git a/js/xpconnect/tests/unit/es6module_dynamic_import2.js b/js/xpconnect/tests/unit/es6module_dynamic_import2.js
new file mode 100644
index 0000000000..abc62eff40
--- /dev/null
+++ b/js/xpconnect/tests/unit/es6module_dynamic_import2.js
@@ -0,0 +1 @@
+export const x = 10;
diff --git a/js/xpconnect/tests/unit/es6module_dynamic_import3.js b/js/xpconnect/tests/unit/es6module_dynamic_import3.js
new file mode 100644
index 0000000000..d9aad86216
--- /dev/null
+++ b/js/xpconnect/tests/unit/es6module_dynamic_import3.js
@@ -0,0 +1 @@
+export const y = 20;
diff --git a/js/xpconnect/tests/unit/es6module_dynamic_import_missing.js b/js/xpconnect/tests/unit/es6module_dynamic_import_missing.js
new file mode 100644
index 0000000000..c64e212460
--- /dev/null
+++ b/js/xpconnect/tests/unit/es6module_dynamic_import_missing.js
@@ -0,0 +1,17 @@
+let resolve;
+
+export const result = new Promise(r => { resolve = r; });
+
+import("./es6module_dynamic_import_missing2.js").then(ns => {}, e => {
+ resolve(e);
+});
+
+export async function doImport() {
+ try {
+ await import("./es6module_dynamic_import_missing3.js");
+ } catch (e) {
+ return e;
+ }
+
+ return null;
+}
diff --git a/js/xpconnect/tests/unit/es6module_dynamic_import_runtime_error.js b/js/xpconnect/tests/unit/es6module_dynamic_import_runtime_error.js
new file mode 100644
index 0000000000..cee6ba366f
--- /dev/null
+++ b/js/xpconnect/tests/unit/es6module_dynamic_import_runtime_error.js
@@ -0,0 +1,17 @@
+let resolve;
+
+export const result = new Promise(r => { resolve = r; });
+
+import("./es6module_dynamic_import_runtime_error2.js").then(ns => {}, e => {
+ resolve(e);
+});
+
+export async function doImport() {
+ try {
+ await import("./es6module_dynamic_import_runtime_error3.js");
+ } catch (e) {
+ return e;
+ }
+
+ return null;
+}
diff --git a/js/xpconnect/tests/unit/es6module_dynamic_import_runtime_error2.js b/js/xpconnect/tests/unit/es6module_dynamic_import_runtime_error2.js
new file mode 100644
index 0000000000..86c450c3bc
--- /dev/null
+++ b/js/xpconnect/tests/unit/es6module_dynamic_import_runtime_error2.js
@@ -0,0 +1,2 @@
+
+foo();
diff --git a/js/xpconnect/tests/unit/es6module_dynamic_import_runtime_error3.js b/js/xpconnect/tests/unit/es6module_dynamic_import_runtime_error3.js
new file mode 100644
index 0000000000..f392f4ffc1
--- /dev/null
+++ b/js/xpconnect/tests/unit/es6module_dynamic_import_runtime_error3.js
@@ -0,0 +1,2 @@
+
+bar();
diff --git a/js/xpconnect/tests/unit/es6module_dynamic_import_static.js b/js/xpconnect/tests/unit/es6module_dynamic_import_static.js
new file mode 100644
index 0000000000..9cebb1e194
--- /dev/null
+++ b/js/xpconnect/tests/unit/es6module_dynamic_import_static.js
@@ -0,0 +1,9 @@
+let counter = 0;
+counter++;
+
+export function getCounter() {
+ return counter;
+}
+export function setCounter(v) {
+ counter = v;
+}
diff --git a/js/xpconnect/tests/unit/es6module_dynamic_import_syntax_error.js b/js/xpconnect/tests/unit/es6module_dynamic_import_syntax_error.js
new file mode 100644
index 0000000000..039bb41785
--- /dev/null
+++ b/js/xpconnect/tests/unit/es6module_dynamic_import_syntax_error.js
@@ -0,0 +1,17 @@
+let resolve;
+
+export const result = new Promise(r => { resolve = r; });
+
+import("./es6module_dynamic_import_syntax_error2.js").then(ns => {}, e => {
+ resolve(e);
+});
+
+export async function doImport() {
+ try {
+ await import("./es6module_dynamic_import_syntax_error3.js");
+ } catch (e) {
+ return e;
+ }
+
+ return null;
+}
diff --git a/js/xpconnect/tests/unit/es6module_dynamic_import_syntax_error2.js b/js/xpconnect/tests/unit/es6module_dynamic_import_syntax_error2.js
new file mode 100644
index 0000000000..b2901ea97c
--- /dev/null
+++ b/js/xpconnect/tests/unit/es6module_dynamic_import_syntax_error2.js
@@ -0,0 +1 @@
+a b
diff --git a/js/xpconnect/tests/unit/es6module_dynamic_import_syntax_error3.js b/js/xpconnect/tests/unit/es6module_dynamic_import_syntax_error3.js
new file mode 100644
index 0000000000..3c2d87cae3
--- /dev/null
+++ b/js/xpconnect/tests/unit/es6module_dynamic_import_syntax_error3.js
@@ -0,0 +1 @@
+aa bb
diff --git a/js/xpconnect/tests/unit/es6module_import_error.js b/js/xpconnect/tests/unit/es6module_import_error.js
new file mode 100644
index 0000000000..e590d0a450
--- /dev/null
+++ b/js/xpconnect/tests/unit/es6module_import_error.js
@@ -0,0 +1 @@
+import { y } from "./es6module_import_error2.js";
diff --git a/js/xpconnect/tests/unit/es6module_import_error2.js b/js/xpconnect/tests/unit/es6module_import_error2.js
new file mode 100644
index 0000000000..abc62eff40
--- /dev/null
+++ b/js/xpconnect/tests/unit/es6module_import_error2.js
@@ -0,0 +1 @@
+export const x = 10;
diff --git a/js/xpconnect/tests/unit/es6module_loaded-1.sys.mjs b/js/xpconnect/tests/unit/es6module_loaded-1.sys.mjs
new file mode 100644
index 0000000000..e405565d6f
--- /dev/null
+++ b/js/xpconnect/tests/unit/es6module_loaded-1.sys.mjs
@@ -0,0 +1 @@
+export function test() {}
diff --git a/js/xpconnect/tests/unit/es6module_loaded-2.sys.mjs b/js/xpconnect/tests/unit/es6module_loaded-2.sys.mjs
new file mode 100644
index 0000000000..e405565d6f
--- /dev/null
+++ b/js/xpconnect/tests/unit/es6module_loaded-2.sys.mjs
@@ -0,0 +1 @@
+export function test() {}
diff --git a/js/xpconnect/tests/unit/es6module_loaded-3.sys.mjs b/js/xpconnect/tests/unit/es6module_loaded-3.sys.mjs
new file mode 100644
index 0000000000..e405565d6f
--- /dev/null
+++ b/js/xpconnect/tests/unit/es6module_loaded-3.sys.mjs
@@ -0,0 +1 @@
+export function test() {}
diff --git a/js/xpconnect/tests/unit/es6module_missing_import.js b/js/xpconnect/tests/unit/es6module_missing_import.js
new file mode 100644
index 0000000000..df79b6a0b7
--- /dev/null
+++ b/js/xpconnect/tests/unit/es6module_missing_import.js
@@ -0,0 +1 @@
+import { name } from "./es6module_not_found2.js";
diff --git a/js/xpconnect/tests/unit/es6module_parse_error.js b/js/xpconnect/tests/unit/es6module_parse_error.js
new file mode 100644
index 0000000000..3128787ba4
--- /dev/null
+++ b/js/xpconnect/tests/unit/es6module_parse_error.js
@@ -0,0 +1 @@
+this is not valid JS
diff --git a/js/xpconnect/tests/unit/es6module_parse_error_in_import.js b/js/xpconnect/tests/unit/es6module_parse_error_in_import.js
new file mode 100644
index 0000000000..14f057ebe1
--- /dev/null
+++ b/js/xpconnect/tests/unit/es6module_parse_error_in_import.js
@@ -0,0 +1 @@
+import { name } from "./es6module_parse_error.js";
diff --git a/js/xpconnect/tests/unit/es6module_throws.js b/js/xpconnect/tests/unit/es6module_throws.js
new file mode 100644
index 0000000000..c3ca94b6eb
--- /dev/null
+++ b/js/xpconnect/tests/unit/es6module_throws.js
@@ -0,0 +1,4 @@
+function throwFunction() {
+ throw new Error("Failing with error foobar");
+}
+throwFunction();
diff --git a/js/xpconnect/tests/unit/es6module_top_level_await.js b/js/xpconnect/tests/unit/es6module_top_level_await.js
new file mode 100644
index 0000000000..9148868fa0
--- /dev/null
+++ b/js/xpconnect/tests/unit/es6module_top_level_await.js
@@ -0,0 +1,5 @@
+await 1;
+
+export function foo() {
+ return 10;
+}
diff --git a/js/xpconnect/tests/unit/esm_lazy-1.sys.mjs b/js/xpconnect/tests/unit/esm_lazy-1.sys.mjs
new file mode 100644
index 0000000000..a995420a56
--- /dev/null
+++ b/js/xpconnect/tests/unit/esm_lazy-1.sys.mjs
@@ -0,0 +1,4 @@
+export let X = 10;
+function GetX() {
+ return X;
+}
diff --git a/js/xpconnect/tests/unit/esm_lazy-2.sys.mjs b/js/xpconnect/tests/unit/esm_lazy-2.sys.mjs
new file mode 100644
index 0000000000..49410b6187
--- /dev/null
+++ b/js/xpconnect/tests/unit/esm_lazy-2.sys.mjs
@@ -0,0 +1,4 @@
+export let Y = 20;
+export function AddY(n) {
+ Y += n;
+};
diff --git a/js/xpconnect/tests/unit/esmified-1.sys.mjs b/js/xpconnect/tests/unit/esmified-1.sys.mjs
new file mode 100644
index 0000000000..0f7a1da661
--- /dev/null
+++ b/js/xpconnect/tests/unit/esmified-1.sys.mjs
@@ -0,0 +1,4 @@
+export let loadCount = 0;
+loadCount++;
+
+export const obj = { value: 10 };
diff --git a/js/xpconnect/tests/unit/esmified-2.sys.mjs b/js/xpconnect/tests/unit/esmified-2.sys.mjs
new file mode 100644
index 0000000000..0f7a1da661
--- /dev/null
+++ b/js/xpconnect/tests/unit/esmified-2.sys.mjs
@@ -0,0 +1,4 @@
+export let loadCount = 0;
+loadCount++;
+
+export const obj = { value: 10 };
diff --git a/js/xpconnect/tests/unit/esmified-3.sys.mjs b/js/xpconnect/tests/unit/esmified-3.sys.mjs
new file mode 100644
index 0000000000..0f7a1da661
--- /dev/null
+++ b/js/xpconnect/tests/unit/esmified-3.sys.mjs
@@ -0,0 +1,4 @@
+export let loadCount = 0;
+loadCount++;
+
+export const obj = { value: 10 };
diff --git a/js/xpconnect/tests/unit/esmified-4.sys.mjs b/js/xpconnect/tests/unit/esmified-4.sys.mjs
new file mode 100644
index 0000000000..0f7a1da661
--- /dev/null
+++ b/js/xpconnect/tests/unit/esmified-4.sys.mjs
@@ -0,0 +1,4 @@
+export let loadCount = 0;
+loadCount++;
+
+export const obj = { value: 10 };
diff --git a/js/xpconnect/tests/unit/esmified-5.sys.mjs b/js/xpconnect/tests/unit/esmified-5.sys.mjs
new file mode 100644
index 0000000000..0f7a1da661
--- /dev/null
+++ b/js/xpconnect/tests/unit/esmified-5.sys.mjs
@@ -0,0 +1,4 @@
+export let loadCount = 0;
+loadCount++;
+
+export const obj = { value: 10 };
diff --git a/js/xpconnect/tests/unit/esmified-6.sys.mjs b/js/xpconnect/tests/unit/esmified-6.sys.mjs
new file mode 100644
index 0000000000..0f7a1da661
--- /dev/null
+++ b/js/xpconnect/tests/unit/esmified-6.sys.mjs
@@ -0,0 +1,4 @@
+export let loadCount = 0;
+loadCount++;
+
+export const obj = { value: 10 };
diff --git a/js/xpconnect/tests/unit/esmified-not-exported.sys.mjs b/js/xpconnect/tests/unit/esmified-not-exported.sys.mjs
new file mode 100644
index 0000000000..e4ae8c0815
--- /dev/null
+++ b/js/xpconnect/tests/unit/esmified-not-exported.sys.mjs
@@ -0,0 +1,13 @@
+export var exportedVar = "exported var";
+export function exportedFunction() {
+ return "exported function";
+}
+export let exportedLet = "exported let";
+export const exportedConst = "exported const";
+
+var notExportedVar = "not exported var";
+function notExportedFunction() {
+ return "not exported function";
+}
+let notExportedLet = "not exported let";
+const notExportedConst = "not exported const";
diff --git a/js/xpconnect/tests/unit/file_simple_script.js b/js/xpconnect/tests/unit/file_simple_script.js
new file mode 100644
index 0000000000..af20291400
--- /dev/null
+++ b/js/xpconnect/tests/unit/file_simple_script.js
@@ -0,0 +1 @@
+this.bar = ({foo: "®"});
diff --git a/js/xpconnect/tests/unit/frame.js b/js/xpconnect/tests/unit/frame.js
new file mode 100644
index 0000000000..8b13789179
--- /dev/null
+++ b/js/xpconnect/tests/unit/frame.js
@@ -0,0 +1 @@
+
diff --git a/js/xpconnect/tests/unit/head.js b/js/xpconnect/tests/unit/head.js
new file mode 100644
index 0000000000..be3d74401a
--- /dev/null
+++ b/js/xpconnect/tests/unit/head.js
@@ -0,0 +1,32 @@
+"use strict";
+
+// Wraps the given object in an XPConnect wrapper and, if an interface
+// is passed, queries the result to that interface.
+function xpcWrap(obj, iface) {
+ let ifacePointer = Cc[
+ "@mozilla.org/supports-interface-pointer;1"
+ ].createInstance(Ci.nsISupportsInterfacePointer);
+
+ ifacePointer.data = obj;
+ if (iface) {
+ return ifacePointer.data.QueryInterface(iface);
+ }
+ return ifacePointer.data;
+}
+
+function createContentWindow(uri) {
+ const principal = Services.scriptSecurityManager
+ .createContentPrincipalFromOrigin(uri);
+ const webnav = Services.appShell.createWindowlessBrowser(false);
+ const docShell = webnav.docShell;
+ docShell.createAboutBlankDocumentViewer(principal, principal);
+ return webnav.document.defaultView;
+}
+
+function createChromeWindow() {
+ const principal = Services.scriptSecurityManager.getSystemPrincipal();
+ const webnav = Services.appShell.createWindowlessBrowser(true);
+ const docShell = webnav.docShell;
+ docShell.createAboutBlankDocumentViewer(principal, principal);
+ return webnav.document.defaultView;
+}
diff --git a/js/xpconnect/tests/unit/head_ongc.js b/js/xpconnect/tests/unit/head_ongc.js
new file mode 100644
index 0000000000..146a15eb4f
--- /dev/null
+++ b/js/xpconnect/tests/unit/head_ongc.js
@@ -0,0 +1,35 @@
+var {addDebuggerToGlobal, addSandboxedDebuggerToGlobal} = ChromeUtils.importESModule("resource://gre/modules/jsdebugger.sys.mjs");
+
+const testingFunctions = Cu.getJSTestingFunctions();
+const systemPrincipal = Cc["@mozilla.org/systemprincipal;1"].createInstance(Ci.nsIPrincipal);
+
+function addTestingFunctionsToGlobal(global) {
+ for (let k in testingFunctions) {
+ global[k] = testingFunctions[k];
+ }
+ global.print = info;
+ global.newGlobal = newGlobal;
+ addDebuggerToGlobal(global);
+}
+
+function newGlobal() {
+ const global = new Cu.Sandbox(systemPrincipal, { freshZone: true });
+ addTestingFunctionsToGlobal(global);
+ return global;
+}
+
+addTestingFunctionsToGlobal(this);
+
+function executeSoon(f) {
+ Services.tm.dispatchToMainThread({ run: f });
+}
+
+// The onGarbageCollection tests don't play well gczeal settings and lead to
+// intermittents.
+if (typeof gczeal == "function") {
+ gczeal(0);
+}
+
+// Make sure to GC before we start the test, so that no zones are scheduled for
+// GC before we start testing onGarbageCollection hooks.
+gc();
diff --git a/js/xpconnect/tests/unit/head_watchdog.js b/js/xpconnect/tests/unit/head_watchdog.js
new file mode 100644
index 0000000000..f977c1f129
--- /dev/null
+++ b/js/xpconnect/tests/unit/head_watchdog.js
@@ -0,0 +1,116 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+//
+// Pref management.
+//
+
+var {PromiseTestUtils} = ChromeUtils.importESModule("resource://testing-common/PromiseTestUtils.sys.mjs");
+
+///////////////////
+//
+// Whitelisting these tests.
+// As part of bug 1077403, the shutdown crash should be fixed.
+//
+// These tests may crash intermittently on shutdown if the DOM Promise uncaught
+// rejection observers are still registered when the watchdog operates.
+PromiseTestUtils.thisTestLeaksUncaughtRejectionsAndShouldBeFixed();
+
+var gPrefs = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch);
+
+function setWatchdogEnabled(enabled) {
+ gPrefs.setBoolPref("dom.use_watchdog", enabled);
+}
+
+function isWatchdogEnabled() {
+ return gPrefs.getBoolPref("dom.use_watchdog");
+}
+
+function setScriptTimeout(seconds) {
+ var oldTimeout = gPrefs.getIntPref("dom.max_script_run_time");
+ gPrefs.setIntPref("dom.max_script_run_time", seconds);
+ return oldTimeout;
+}
+
+//
+// Utilities.
+//
+
+function busyWait(ms) {
+ var start = new Date();
+ while ((new Date()) - start < ms) {}
+}
+
+function do_log_info(aMessage)
+{
+ print("TEST-INFO | " + _TEST_FILE + " | " + aMessage);
+}
+
+// We don't use do_execute_soon, because that inserts a
+// do_test_{pending,finished} pair that gets screwed up when we terminate scripts
+// from the operation callback.
+function executeSoon(fn) {
+ var tm = Cc["@mozilla.org/thread-manager;1"].getService(Ci.nsIThreadManager);
+ tm.dispatchToMainThread({run: fn});
+}
+
+//
+// Asynchronous watchdog diagnostics.
+//
+// When running, the watchdog wakes up every second, and fires the operation
+// callback if the script has been running for >= the minimum script timeout.
+// As such, if the script timeout is 1 second, a script should never be able to
+// run for two seconds or longer without servicing the operation callback.
+// We wait 3 seconds, just to be safe.
+//
+
+function checkWatchdog(expectInterrupt) {
+ var oldTimeout = setScriptTimeout(1);
+ var lastWatchdogWakeup = Cu.getWatchdogTimestamp("WatchdogWakeup");
+
+ return new Promise(resolve => {
+ let inBusyWait = false;
+ setInterruptCallback(function() {
+ // If the watchdog didn't actually trigger the operation callback, ignore
+ // this call. This allows us to test the actual watchdog behavior without
+ // interference from other sites where we trigger the operation callback.
+ if (lastWatchdogWakeup == Cu.getWatchdogTimestamp("WatchdogWakeup")) {
+ return true;
+ }
+ if (!inBusyWait) {
+ Assert.ok(true, "Not in busy wait, ignoring interrupt callback");
+ return true;
+ }
+
+ Assert.ok(expectInterrupt, "Interrupt callback fired");
+ setInterruptCallback(undefined);
+ setScriptTimeout(oldTimeout);
+ // Schedule the promise for resolution before we kill this script.
+ executeSoon(resolve);
+ return false;
+ });
+
+ executeSoon(function() {
+ inBusyWait = true;
+ busyWait(3000);
+ inBusyWait = false;
+ Assert.ok(!expectInterrupt, "Interrupt callback didn't fire");
+ setInterruptCallback(undefined);
+ setScriptTimeout(oldTimeout);
+ resolve();
+ });
+ });
+}
+
+function run_test() {
+
+ // Run async.
+ do_test_pending();
+
+ // Run the async function.
+ testBody().then(() => {
+ do_test_finished();
+ });
+}
+
diff --git a/js/xpconnect/tests/unit/import_non_shared_1.mjs b/js/xpconnect/tests/unit/import_non_shared_1.mjs
new file mode 100644
index 0000000000..89c05e0c10
--- /dev/null
+++ b/js/xpconnect/tests/unit/import_non_shared_1.mjs
@@ -0,0 +1 @@
+export { getCounter, incCounter } from "./non_shared_1.mjs";
diff --git a/js/xpconnect/tests/unit/import_shared_in_worker.js b/js/xpconnect/tests/unit/import_shared_in_worker.js
new file mode 100644
index 0000000000..bc92fe26a6
--- /dev/null
+++ b/js/xpconnect/tests/unit/import_shared_in_worker.js
@@ -0,0 +1,36 @@
+onmessage = event => {
+ let caught1 = false;
+ try {
+ ChromeUtils.importESModule("resource://test/esmified-1.sys.mjs");
+ } catch (e) {
+ caught1 = true;
+ }
+
+ let caught2 = false;
+ try {
+ ChromeUtils.importESModule("resource://test/esmified-1.sys.mjs", {
+ global: "shared",
+ });
+ } catch (e) {
+ caught2 = true;
+ }
+
+ let caught3 = false;
+ try {
+ ChromeUtils.importESModule("resource://test/esmified-1.sys.mjs", {
+ global: "devtools",
+ });
+ } catch (e) {
+ caught3 = true;
+ }
+
+ let caught4 = false;
+ try {
+ ChromeUtils.importESModule("resource://test/esmified-1.sys.mjs", {
+ loadInDevToolsLoader: true,
+ });
+ } catch (e) {
+ caught4 = true;
+ }
+ postMessage({ caught1, caught2, caught3, caught4 });
+};
diff --git a/js/xpconnect/tests/unit/import_stack.jsm b/js/xpconnect/tests/unit/import_stack.jsm
new file mode 100644
index 0000000000..9f12c25566
--- /dev/null
+++ b/js/xpconnect/tests/unit/import_stack.jsm
@@ -0,0 +1,2 @@
+function test() {}
+var EXPORTED_SYMBOLS = ["test"];
diff --git a/js/xpconnect/tests/unit/import_stack.sys.mjs b/js/xpconnect/tests/unit/import_stack.sys.mjs
new file mode 100644
index 0000000000..e405565d6f
--- /dev/null
+++ b/js/xpconnect/tests/unit/import_stack.sys.mjs
@@ -0,0 +1 @@
+export function test() {}
diff --git a/js/xpconnect/tests/unit/import_stack_static_1.sys.mjs b/js/xpconnect/tests/unit/import_stack_static_1.sys.mjs
new file mode 100644
index 0000000000..1d2e0452c5
--- /dev/null
+++ b/js/xpconnect/tests/unit/import_stack_static_1.sys.mjs
@@ -0,0 +1 @@
+import { f2 } from './import_stack_static_2.sys.mjs';
diff --git a/js/xpconnect/tests/unit/import_stack_static_2.sys.mjs b/js/xpconnect/tests/unit/import_stack_static_2.sys.mjs
new file mode 100644
index 0000000000..d6e332e68c
--- /dev/null
+++ b/js/xpconnect/tests/unit/import_stack_static_2.sys.mjs
@@ -0,0 +1,2 @@
+import { f3 } from './import_stack_static_3.sys.mjs';
+export function f2() {}
diff --git a/js/xpconnect/tests/unit/import_stack_static_3.sys.mjs b/js/xpconnect/tests/unit/import_stack_static_3.sys.mjs
new file mode 100644
index 0000000000..d40df510bf
--- /dev/null
+++ b/js/xpconnect/tests/unit/import_stack_static_3.sys.mjs
@@ -0,0 +1,2 @@
+import { f4 } from './import_stack_static_4.sys.mjs';
+export function f3() {}
diff --git a/js/xpconnect/tests/unit/import_stack_static_4.sys.mjs b/js/xpconnect/tests/unit/import_stack_static_4.sys.mjs
new file mode 100644
index 0000000000..1bf71d53e9
--- /dev/null
+++ b/js/xpconnect/tests/unit/import_stack_static_4.sys.mjs
@@ -0,0 +1 @@
+export function f4() {}
diff --git a/js/xpconnect/tests/unit/importer.jsm b/js/xpconnect/tests/unit/importer.jsm
new file mode 100644
index 0000000000..e6d2f184e6
--- /dev/null
+++ b/js/xpconnect/tests/unit/importer.jsm
@@ -0,0 +1 @@
+ChromeUtils.import("resource://test/syntax_error.jsm"); \ No newline at end of file
diff --git a/js/xpconnect/tests/unit/jsm_loaded-1.jsm b/js/xpconnect/tests/unit/jsm_loaded-1.jsm
new file mode 100644
index 0000000000..9f12c25566
--- /dev/null
+++ b/js/xpconnect/tests/unit/jsm_loaded-1.jsm
@@ -0,0 +1,2 @@
+function test() {}
+var EXPORTED_SYMBOLS = ["test"];
diff --git a/js/xpconnect/tests/unit/jsm_loaded-2.jsm b/js/xpconnect/tests/unit/jsm_loaded-2.jsm
new file mode 100644
index 0000000000..9f12c25566
--- /dev/null
+++ b/js/xpconnect/tests/unit/jsm_loaded-2.jsm
@@ -0,0 +1,2 @@
+function test() {}
+var EXPORTED_SYMBOLS = ["test"];
diff --git a/js/xpconnect/tests/unit/jsm_loaded-3.jsm b/js/xpconnect/tests/unit/jsm_loaded-3.jsm
new file mode 100644
index 0000000000..9f12c25566
--- /dev/null
+++ b/js/xpconnect/tests/unit/jsm_loaded-3.jsm
@@ -0,0 +1,2 @@
+function test() {}
+var EXPORTED_SYMBOLS = ["test"];
diff --git a/js/xpconnect/tests/unit/lazy_non_shared_in_worker.js b/js/xpconnect/tests/unit/lazy_non_shared_in_worker.js
new file mode 100644
index 0000000000..47e1dede92
--- /dev/null
+++ b/js/xpconnect/tests/unit/lazy_non_shared_in_worker.js
@@ -0,0 +1,28 @@
+onmessage = event => {
+ const lazy1 = {};
+ const lazy2 = {};
+
+ ChromeUtils.defineESModuleGetters(lazy1, {
+ GetX: "resource://test/esm_lazy-1.sys.mjs",
+ }, {
+ global: "current",
+ });
+
+ ChromeUtils.defineESModuleGetters(lazy2, {
+ GetX: "resource://test/esm_lazy-1.sys.mjs",
+ }, {
+ global: "contextual",
+ });
+
+ lazy1.GetX; // delazify before import.
+ lazy2.GetX; // delazify before import.
+
+ const ns = ChromeUtils.importESModule("resource://test/esm_lazy-1.sys.mjs", {
+ global: "current",
+ });
+
+ const equal1 = ns.GetX == lazy1.GetX;
+ const equal2 = ns.GetX == lazy2.GetX;
+
+ postMessage({ equal1, equal2 });
+};
diff --git a/js/xpconnect/tests/unit/lazy_shared_in_worker.js b/js/xpconnect/tests/unit/lazy_shared_in_worker.js
new file mode 100644
index 0000000000..148cdefb3e
--- /dev/null
+++ b/js/xpconnect/tests/unit/lazy_shared_in_worker.js
@@ -0,0 +1,52 @@
+onmessage = event => {
+ let caught1 = false;
+ try {
+ const lazy = {};
+ ChromeUtils.defineESModuleGetters(lazy, {
+ obj: "resource://test/esmified-1.sys.mjs"
+ });
+ lazy.obj;
+ } catch (e) {
+ caught1 = true;
+ }
+
+ let caught2 = false;
+ try {
+ const lazy = {};
+ ChromeUtils.defineESModuleGetters(lazy, {
+ obj: "resource://test/esmified-1.sys.mjs"
+ }, {
+ global: "shared",
+ });
+ lazy.obj;
+ } catch (e) {
+ caught2 = true;
+ }
+
+ let caught3 = false;
+ try {
+ const lazy = {};
+ ChromeUtils.defineESModuleGetters(lazy, {
+ obj: "resource://test/esmified-1.sys.mjs"
+ }, {
+ global: "devtools",
+ });
+ lazy.obj;
+ } catch (e) {
+ caught3 = true;
+ }
+
+ let caught4 = false;
+ try {
+ const lazy = {};
+ ChromeUtils.defineESModuleGetters(lazy, {
+ obj: "resource://test/esmified-1.sys.mjs"
+ }, {
+ loadInDevToolsLoader: true,
+ });
+ lazy.obj;
+ } catch (e) {
+ caught4 = true;
+ }
+ postMessage({ caught1, caught2, caught3, caught4 });
+};
diff --git a/js/xpconnect/tests/unit/non_shared_1.mjs b/js/xpconnect/tests/unit/non_shared_1.mjs
new file mode 100644
index 0000000000..9b909c134a
--- /dev/null
+++ b/js/xpconnect/tests/unit/non_shared_1.mjs
@@ -0,0 +1,19 @@
+import { setGlobal } from "./non_shared_2.mjs";
+
+globalThis["loaded"].push(1);
+
+globalThis["counter"] = 0;
+
+let counter = 0;
+
+export function getCounter() {
+ return counter;
+}
+
+export function incCounter() {
+ counter++;
+}
+
+export function putCounter() {
+ setGlobal("counter", counter);
+}
diff --git a/js/xpconnect/tests/unit/non_shared_2.mjs b/js/xpconnect/tests/unit/non_shared_2.mjs
new file mode 100644
index 0000000000..ffa542b445
--- /dev/null
+++ b/js/xpconnect/tests/unit/non_shared_2.mjs
@@ -0,0 +1,5 @@
+globalThis["loaded"].push(2);
+
+export function setGlobal(name, value) {
+ globalThis[name] = value;
+}
diff --git a/js/xpconnect/tests/unit/non_shared_nest_import_non_shared_1.mjs b/js/xpconnect/tests/unit/non_shared_nest_import_non_shared_1.mjs
new file mode 100644
index 0000000000..ea78e9f881
--- /dev/null
+++ b/js/xpconnect/tests/unit/non_shared_nest_import_non_shared_1.mjs
@@ -0,0 +1,7 @@
+const { func2 } = ChromeUtils.importESModule("resource://test/non_shared_nest_import_non_shared_target_1.mjs", {
+ global: "current",
+});
+
+export function func() {
+ return func2();
+}
diff --git a/js/xpconnect/tests/unit/non_shared_nest_import_non_shared_2.mjs b/js/xpconnect/tests/unit/non_shared_nest_import_non_shared_2.mjs
new file mode 100644
index 0000000000..a4c636ab56
--- /dev/null
+++ b/js/xpconnect/tests/unit/non_shared_nest_import_non_shared_2.mjs
@@ -0,0 +1,5 @@
+Cu.evalInSandbox(`
+ChromeUtils.importESModule("resource://test/non_shared_nest_import_non_shared_target_2.mjs", {
+ global: "current",
+});
+`, globalThis["sb"]);
diff --git a/js/xpconnect/tests/unit/non_shared_nest_import_non_shared_3.mjs b/js/xpconnect/tests/unit/non_shared_nest_import_non_shared_3.mjs
new file mode 100644
index 0000000000..f5a0731fa8
--- /dev/null
+++ b/js/xpconnect/tests/unit/non_shared_nest_import_non_shared_3.mjs
@@ -0,0 +1,14 @@
+export function func3() {
+ const { func3 } = ChromeUtils.importESModule("resource://test/non_shared_nest_import_non_shared_target_3.mjs", {
+ global: "current",
+ });
+
+ const result = Cu.evalInSandbox(`
+ const { func3 } = ChromeUtils.importESModule("resource://test/non_shared_nest_import_non_shared_target_3.mjs", {
+ global: "current",
+ });
+ func3();
+`, globalThis["sb"]);
+
+ return func3() + result;
+}
diff --git a/js/xpconnect/tests/unit/non_shared_nest_import_non_shared_target_1.mjs b/js/xpconnect/tests/unit/non_shared_nest_import_non_shared_target_1.mjs
new file mode 100644
index 0000000000..4f7a654988
--- /dev/null
+++ b/js/xpconnect/tests/unit/non_shared_nest_import_non_shared_target_1.mjs
@@ -0,0 +1,3 @@
+export function func2() {
+ return 10;
+}
diff --git a/js/xpconnect/tests/unit/non_shared_nest_import_non_shared_target_2.mjs b/js/xpconnect/tests/unit/non_shared_nest_import_non_shared_target_2.mjs
new file mode 100644
index 0000000000..78c9141ba1
--- /dev/null
+++ b/js/xpconnect/tests/unit/non_shared_nest_import_non_shared_target_2.mjs
@@ -0,0 +1,3 @@
+export function func2() {
+ return 11;
+}
diff --git a/js/xpconnect/tests/unit/non_shared_nest_import_non_shared_target_3.mjs b/js/xpconnect/tests/unit/non_shared_nest_import_non_shared_target_3.mjs
new file mode 100644
index 0000000000..f5bbcb4560
--- /dev/null
+++ b/js/xpconnect/tests/unit/non_shared_nest_import_non_shared_target_3.mjs
@@ -0,0 +1,3 @@
+export function func3() {
+ return 11;
+}
diff --git a/js/xpconnect/tests/unit/non_shared_nest_import_shared_1.mjs b/js/xpconnect/tests/unit/non_shared_nest_import_shared_1.mjs
new file mode 100644
index 0000000000..5aed011ffd
--- /dev/null
+++ b/js/xpconnect/tests/unit/non_shared_nest_import_shared_1.mjs
@@ -0,0 +1,7 @@
+const { sys1 } = ChromeUtils.importESModule("resource://test/non_shared_nest_import_shared_target_1.sys.mjs");
+
+export function func1() {
+ const { sys2 } = ChromeUtils.importESModule("resource://test/non_shared_nest_import_shared_target_2.sys.mjs");
+
+ return sys1() + sys2();
+}
diff --git a/js/xpconnect/tests/unit/non_shared_nest_import_shared_target_1.sys.mjs b/js/xpconnect/tests/unit/non_shared_nest_import_shared_target_1.sys.mjs
new file mode 100644
index 0000000000..17d5b453f2
--- /dev/null
+++ b/js/xpconnect/tests/unit/non_shared_nest_import_shared_target_1.sys.mjs
@@ -0,0 +1,3 @@
+export function sys1() {
+ return 20;
+}
diff --git a/js/xpconnect/tests/unit/non_shared_nest_import_shared_target_2.sys.mjs b/js/xpconnect/tests/unit/non_shared_nest_import_shared_target_2.sys.mjs
new file mode 100644
index 0000000000..d894bb4ce5
--- /dev/null
+++ b/js/xpconnect/tests/unit/non_shared_nest_import_shared_target_2.sys.mjs
@@ -0,0 +1,3 @@
+export function sys2() {
+ return 7;
+}
diff --git a/js/xpconnect/tests/unit/non_shared_worker_1.js b/js/xpconnect/tests/unit/non_shared_worker_1.js
new file mode 100644
index 0000000000..219759b057
--- /dev/null
+++ b/js/xpconnect/tests/unit/non_shared_worker_1.js
@@ -0,0 +1,11 @@
+onmessage = event => {
+ globalThis["loaded"] = [];
+
+ var ns = ChromeUtils.importESModule("resource://test/non_shared_1.mjs", {
+ global: "current",
+ });
+ const c1 = ns.getCounter();
+ ns.incCounter();
+ const c2 = ns.getCounter();
+ postMessage({ c1, c2, loaded: globalThis["loaded"] });
+};
diff --git a/js/xpconnect/tests/unit/not-esmified-not-exported.jsm b/js/xpconnect/tests/unit/not-esmified-not-exported.jsm
new file mode 100644
index 0000000000..094eab7f92
--- /dev/null
+++ b/js/xpconnect/tests/unit/not-esmified-not-exported.jsm
@@ -0,0 +1,20 @@
+var exportedVar = "exported var";
+function exportedFunction() {
+ return "exported function";
+}
+let exportedLet = "exported let";
+const exportedConst = "exported const";
+
+var notExportedVar = "not exported var";
+function notExportedFunction() {
+ return "not exported function";
+}
+let notExportedLet = "not exported let";
+const notExportedConst = "not exported const";
+
+const EXPORTED_SYMBOLS = [
+ "exportedVar",
+ "exportedFunction",
+ "exportedLet",
+ "exportedConst",
+];
diff --git a/js/xpconnect/tests/unit/recursive_importA.jsm b/js/xpconnect/tests/unit/recursive_importA.jsm
new file mode 100644
index 0000000000..ac763354c4
--- /dev/null
+++ b/js/xpconnect/tests/unit/recursive_importA.jsm
@@ -0,0 +1,12 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+var EXPORTED_SYMBOLS = ["foo", "bar"];
+
+function foo() {
+ return "foo";
+}
+
+var bar = {}
+ChromeUtils.import("resource://test/recursive_importB.jsm", bar);
diff --git a/js/xpconnect/tests/unit/recursive_importB.jsm b/js/xpconnect/tests/unit/recursive_importB.jsm
new file mode 100644
index 0000000000..1bf84971b6
--- /dev/null
+++ b/js/xpconnect/tests/unit/recursive_importB.jsm
@@ -0,0 +1,13 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+var EXPORTED_SYMBOLS = ["baz", "qux"];
+
+function baz() {
+ return "baz";
+}
+
+var qux = {}
+ChromeUtils.import("resource://test/recursive_importA.jsm", qux);
+
diff --git a/js/xpconnect/tests/unit/sync_and_async_in_worker.js b/js/xpconnect/tests/unit/sync_and_async_in_worker.js
new file mode 100644
index 0000000000..e21c5cca67
--- /dev/null
+++ b/js/xpconnect/tests/unit/sync_and_async_in_worker.js
@@ -0,0 +1,124 @@
+onmessage = async event => {
+ if (event.data.order === "test") {
+ globalThis["loaded"] = [];
+ const ns = await import("resource://test/non_shared_1.mjs");
+ postMessage({});
+ return;
+ }
+
+ if (event.data.order === "sync-before-async") {
+ globalThis["loaded"] = [];
+ const ns = ChromeUtils.importESModule("resource://test/non_shared_1.mjs", {
+ global: "current",
+ });
+
+ const sync_beforeInc = ns.getCounter();
+ ns.incCounter();
+ const sync_afterInc = ns.getCounter();
+
+ const loaded1 = globalThis["loaded"].join(",");
+
+ let nsPromise;
+ if (event.data.target === "top-level") {
+ nsPromise = import("resource://test/non_shared_1.mjs");
+ } else {
+ nsPromise = import("resource://test/import_non_shared_1.mjs");
+ }
+
+ const ns2 = await nsPromise;
+
+ const async_beforeInc = ns2.getCounter();
+ ns2.incCounter();
+ const async_afterInc = ns2.getCounter();
+ const sync_afterIncInc = ns.getCounter();
+
+ const loaded2 = globalThis["loaded"].join(",");
+
+ postMessage({
+ sync_beforeInc,
+ sync_afterInc,
+ sync_afterIncInc,
+ async_beforeInc,
+ async_afterInc,
+ loaded1,
+ loaded2,
+ });
+ return;
+ }
+
+ if (event.data.order === "sync-after-async") {
+ globalThis["loaded"] = [];
+ const ns = await import("resource://test/non_shared_1.mjs");
+
+ const async_beforeInc = ns.getCounter();
+ ns.incCounter();
+ const async_afterInc = ns.getCounter();
+
+ const loaded1 = globalThis["loaded"].join(",");
+
+ let ns2;
+ if (event.data.target === "top-level") {
+ ns2 = ChromeUtils.importESModule("resource://test/non_shared_1.mjs", {
+ global: "current",
+ });
+ } else {
+ ns2 = ChromeUtils.importESModule("resource://test/import_non_shared_1.mjs", {
+ global: "current",
+ });
+ }
+
+ const sync_beforeInc = ns2.getCounter();
+ ns2.incCounter();
+ const sync_afterInc = ns2.getCounter();
+ const async_afterIncInc = ns.getCounter();
+
+ const loaded2 = globalThis["loaded"].join(",");
+
+ postMessage({
+ sync_beforeInc,
+ sync_afterInc,
+ async_beforeInc,
+ async_afterInc,
+ async_afterIncInc,
+ loaded1,
+ loaded2,
+ });
+ return;
+ }
+
+ if (event.data.order === "sync-while-async") {
+ globalThis["loaded"] = [];
+ const nsPromise = import("resource://test/non_shared_1.mjs");
+
+ let errorMessage = "";
+ try {
+ if (event.data.target === "top-level") {
+ ChromeUtils.importESModule("resource://test/non_shared_1.mjs", {
+ global: "current",
+ });
+ } else {
+ ChromeUtils.importESModule("resource://test/import_non_shared_1.mjs", {
+ global: "current",
+ });
+ }
+ } catch (e) {
+ errorMessage = e.message;
+ }
+
+ const ns = await nsPromise;
+
+ const async_beforeInc = ns.getCounter();
+ ns.incCounter();
+ const async_afterInc = ns.getCounter();
+
+ const loaded = globalThis["loaded"].join(",");
+
+ postMessage({
+ sync_error: errorMessage,
+ async_beforeInc,
+ async_afterInc,
+ loaded,
+ });
+ return;
+ }
+};
diff --git a/js/xpconnect/tests/unit/syntax_error.jsm b/js/xpconnect/tests/unit/syntax_error.jsm
new file mode 100644
index 0000000000..fca785bcdd
--- /dev/null
+++ b/js/xpconnect/tests/unit/syntax_error.jsm
@@ -0,0 +1 @@
+bogusjs)(
diff --git a/js/xpconnect/tests/unit/test_ComponentEnvironment.js b/js/xpconnect/tests/unit/test_ComponentEnvironment.js
new file mode 100644
index 0000000000..1d2c474ffd
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_ComponentEnvironment.js
@@ -0,0 +1,20 @@
+let tgt = {};
+
+Services.prefs.setBoolPref("security.allow_eval_with_system_principal", true);
+registerCleanupFunction(() => {
+ Services.prefs.clearUserPref("security.allow_eval_with_system_principal");
+});
+
+const a = ChromeUtils.import("resource://test/environment_script.js", tgt);
+const b = ChromeUtils.import("resource://test/environment_checkscript.jsm", tgt);
+
+const isShared = Cu.getGlobalForObject(a) === Cu.getGlobalForObject(b);
+
+
+// Components should not share namespace
+if (isShared) {
+ todo_check_eq(tgt.bound, "");
+ Assert.equal(tgt.bound, "ei,fo,", "Modules should have no shared non-eval bindings");
+} else {
+ Assert.equal(tgt.bound, "", "Modules should have no shared bindings");
+}
diff --git a/js/xpconnect/tests/unit/test_Cu_reportError_column.js b/js/xpconnect/tests/unit/test_Cu_reportError_column.js
new file mode 100644
index 0000000000..15176082e7
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_Cu_reportError_column.js
@@ -0,0 +1,57 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+add_task(async function() {
+ // Passing an Error with JSErrorReport to Cu.reportError should preserve
+ // the columnNumber.
+
+ const tests = [
+ // Parser error.
+ {
+ throwError() {
+ eval("a b");
+ },
+ messagePattern: /unexpected token/,
+ lineNumber: 1,
+ columnNumber: 3,
+ },
+ // Runtime error.
+ {
+ throwError() { // line = 21
+ not_found();
+ },
+ messagePattern: /is not defined/,
+ lineNumber: 22,
+ columnNumber: 9,
+ },
+ ];
+
+ for (const test of tests) {
+ const { promise, resolve } = Promise.withResolvers();
+ const listener = {
+ observe(msg) {
+ if (msg instanceof Ci.nsIScriptError) {
+ resolve(msg);
+ }
+ }
+ };
+
+ try {
+ Services.console.registerListener(listener);
+
+ try {
+ test.throwError();
+ } catch (e) {
+ Cu.reportError(e);
+ }
+
+ const msg = await promise;
+ Assert.stringMatches(msg.errorMessage, test.messagePattern);
+ Assert.equal(msg.lineNumber, test.lineNumber);
+ Assert.equal(msg.columnNumber, test.columnNumber);
+ } finally {
+ Services.console.unregisterListener(listener);
+ }
+ }
+});
diff --git a/js/xpconnect/tests/unit/test_FrameScriptEnvironment.js b/js/xpconnect/tests/unit/test_FrameScriptEnvironment.js
new file mode 100644
index 0000000000..d02c9900e1
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_FrameScriptEnvironment.js
@@ -0,0 +1,46 @@
+let ppmm = Services.ppmm.getChildAt(0);
+
+Services.prefs.setBoolPref("security.allow_eval_with_system_principal", true);
+registerCleanupFunction(() => {
+ Services.prefs.clearUserPref("security.allow_eval_with_system_principal");
+});
+
+add_task(async function test_bindings() {
+ let {strict, bound} = await new Promise(function(resolve) {
+ // Use a listener to get results from child
+ ppmm.addMessageListener("results", function listener(msg) {
+ ppmm.removeMessageListener("results", listener);
+ resolve(msg.data);
+ });
+
+ // Bind vars in first process script
+ ppmm.loadProcessScript("resource://test/environment_script.js", false);
+
+ // Check visibility in second process script
+ ppmm.loadProcessScript(`data:,
+ let strict = (function() { return this; })() === undefined;
+ var bound = "";
+
+ try { void vu; bound += "vu,"; } catch (e) {}
+ try { void vq; bound += "vq,"; } catch (e) {}
+ try { void vl; bound += "vl,"; } catch (e) {}
+ try { void gt; bound += "gt,"; } catch (e) {}
+ try { void ed; bound += "ed,"; } catch (e) {}
+ try { void ei; bound += "ei,"; } catch (e) {}
+ try { void fo; bound += "fo,"; } catch (e) {}
+ try { void fi; bound += "fi,"; } catch (e) {}
+ try { void fd; bound += "fd,"; } catch (e) {}
+
+ sendAsyncMessage("results", { strict, bound });
+ `, false);
+ });
+
+ // FrameScript loader should share |this| access
+ if (strict) {
+ if (bound != "gt,ed,ei,fo,")
+ throw new Error("Unexpected global binding set - " + bound);
+ } else {
+ if (bound != "gt,ed,ei,fo,fi,fd,")
+ throw new Error("Unexpected global binding set - " + bound);
+ }
+});
diff --git a/js/xpconnect/tests/unit/test_ReadableStream_from.js b/js/xpconnect/tests/unit/test_ReadableStream_from.js
new file mode 100644
index 0000000000..b4ecf25123
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_ReadableStream_from.js
@@ -0,0 +1,28 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+add_task(async function run_test() {
+ let sb = new Cu.Sandbox('http://www.example.com');
+
+ let done = false;
+ let iterator = {
+ [Symbol.asyncIterator]() {
+ return this;
+ },
+
+ next() {
+ let promise = Cu.evalInSandbox(`Promise.resolve({done: ${done}, value: {hello: "world"}})`, sb);
+ done = true;
+ return promise;
+ }
+ }
+
+ let stream = ReadableStream.from(iterator);
+ let reader = stream.getReader();
+ let result = await reader.read();
+ Assert.equal(result.done, false);
+ Assert.equal(result.value?.hello, "world");
+ result = await reader.read();
+ Assert.equal(result.done, true);
+});
diff --git a/js/xpconnect/tests/unit/test_SubscriptLoaderEnvironment.js b/js/xpconnect/tests/unit/test_SubscriptLoaderEnvironment.js
new file mode 100644
index 0000000000..c0a5cf202c
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_SubscriptLoaderEnvironment.js
@@ -0,0 +1,38 @@
+let tgt = {};
+
+Services.prefs.setBoolPref("security.allow_eval_with_system_principal", true);
+
+registerCleanupFunction(() => {
+ Services.prefs.clearUserPref("security.allow_eval_with_system_principal");
+});
+
+Services.scriptloader.loadSubScript("resource://test/environment_script.js", tgt);
+
+var bound = "";
+var tgt_bound = "";
+
+// Check global bindings
+try { void vu; bound += "vu,"; } catch (e) {}
+try { void vq; bound += "vq,"; } catch (e) {}
+try { void vl; bound += "vl,"; } catch (e) {}
+try { void gt; bound += "gt,"; } catch (e) {}
+try { void ed; bound += "ed,"; } catch (e) {}
+try { void ei; bound += "ei,"; } catch (e) {}
+try { void fo; bound += "fo,"; } catch (e) {}
+try { void fi; bound += "fi,"; } catch (e) {}
+try { void fd; bound += "fd,"; } catch (e) {}
+
+// Check target bindings
+for (var name of ["vu", "vq", "vl", "gt", "ed", "ei", "fo", "fi", "fd"])
+ if (tgt.hasOwnProperty(name))
+ tgt_bound += name + ",";
+
+
+// Expected subscript loader behavior is as follows:
+// - Qualified vars and |this| access occur on target object
+// - Lexical vars occur on ExtensibleLexicalEnvironment of target object
+// - Bareword assignments and global |this| access occur on caller's global
+if (bound != "vu,ei,fo,fi,")
+ throw new Error("Unexpected global binding set - " + bound);
+if (tgt_bound != "vq,gt,ed,fd,")
+ throw new Error("Unexpected target binding set - " + tgt_bound);
diff --git a/js/xpconnect/tests/unit/test_SubscriptLoaderJSMEnvironment.js b/js/xpconnect/tests/unit/test_SubscriptLoaderJSMEnvironment.js
new file mode 100644
index 0000000000..1ae9bc3b74
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_SubscriptLoaderJSMEnvironment.js
@@ -0,0 +1,32 @@
+let tgt_load = {};
+let tgt_check = {};
+
+Services.prefs.setBoolPref("security.allow_eval_with_system_principal", true);
+registerCleanupFunction(() => {
+ Services.prefs.clearUserPref("security.allow_eval_with_system_principal");
+});
+const a = ChromeUtils.import("resource://test/environment_loadscript.jsm", tgt_load);
+const b = ChromeUtils.import("resource://test/environment_checkscript.jsm", tgt_check);
+
+const isShared = Cu.getGlobalForObject(a) === Cu.getGlobalForObject(b);
+
+// Check target bindings
+var tgt_subscript_bound = "";
+for (var name of ["vu", "vq", "vl", "gt", "ed", "ei", "fo", "fi", "fd"])
+ if (tgt_load.target.hasOwnProperty(name))
+ tgt_subscript_bound += name + ",";
+
+// Expected subscript loader behavior is as follows:
+// - Qualified vars and |this| access occur on target object
+// - Lexical vars occur on ExtensibleLexicalEnvironment of target object
+// - Bareword assignments and global |this| access occur on caller's global
+Assert.equal(tgt_load.bound, "vu,ei,fo,fi,", "Should have expected module binding set");
+Assert.equal(tgt_subscript_bound, "vq,gt,ed,fd,", "Should have expected subscript binding set");
+
+// Components should not share namespace
+if (isShared) {
+ todo_check_eq(tgt_check.bound, "");
+ Assert.equal(tgt_check.bound, "ei,fo,", "Modules should have no shared non-eval bindings");
+} else {
+ Assert.equal(tgt_check.bound, "", "Modules should have no shared bindings");
+}
diff --git a/js/xpconnect/tests/unit/test_SubscriptLoaderSandboxEnvironment.js b/js/xpconnect/tests/unit/test_SubscriptLoaderSandboxEnvironment.js
new file mode 100644
index 0000000000..3f4a10a14f
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_SubscriptLoaderSandboxEnvironment.js
@@ -0,0 +1,35 @@
+let tgt = Cu.Sandbox(Services.scriptSecurityManager.getSystemPrincipal());
+
+Services.prefs.setBoolPref("security.allow_eval_with_system_principal", true);
+registerCleanupFunction(() => {
+ Services.prefs.clearUserPref("security.allow_eval_with_system_principal");
+});
+Services.scriptloader.loadSubScript("resource://test/environment_script.js", tgt);
+
+var bound = "";
+var tgt_bound = "";
+
+// Check global bindings
+try { void vu; bound += "vu,"; } catch (e) {}
+try { void vq; bound += "vq,"; } catch (e) {}
+try { void vl; bound += "vl,"; } catch (e) {}
+try { void gt; bound += "gt,"; } catch (e) {}
+try { void ed; bound += "ed,"; } catch (e) {}
+try { void ei; bound += "ei,"; } catch (e) {}
+try { void fo; bound += "fo,"; } catch (e) {}
+try { void fi; bound += "fi,"; } catch (e) {}
+try { void fd; bound += "fd,"; } catch (e) {}
+
+// Check target bindings
+for (var name of ["vu", "vq", "vl", "gt", "ed", "ei", "fo", "fi", "fd"])
+ if (tgt.hasOwnProperty(name))
+ tgt_bound += name + ",";
+
+
+// Expected subscript loader behavior with a Sandbox is as follows:
+// - Lexicals occur on ExtensibleLexicalEnvironment of target
+// - Everything else occurs on Sandbox global
+if (bound != "")
+ throw new Error("Unexpected global binding set - " + bound);
+if (tgt_bound != "vu,vq,gt,ed,ei,fo,fi,fd,")
+ throw new Error("Unexpected target binding set - " + tgt_bound);
diff --git a/js/xpconnect/tests/unit/test_URLSearchParams.js b/js/xpconnect/tests/unit/test_URLSearchParams.js
new file mode 100644
index 0000000000..fb2d203187
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_URLSearchParams.js
@@ -0,0 +1,12 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+function run_test() {
+ var sb = new Cu.Sandbox('http://www.example.com',
+ { wantGlobalProperties: ["URLSearchParams"] });
+ sb.equal = equal;
+ Cu.evalInSandbox('equal(new URLSearchParams("one=1&two=2").get("one"), "1");',
+ sb);
+ Cu.importGlobalProperties(["URLSearchParams"]);
+ Assert.equal(new URLSearchParams("one=1&two=2").get("one"), "1");
+}
diff --git a/js/xpconnect/tests/unit/test_allowWaivers.js b/js/xpconnect/tests/unit/test_allowWaivers.js
new file mode 100644
index 0000000000..b5a764e352
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_allowWaivers.js
@@ -0,0 +1,29 @@
+function checkWaivers(from, allowed) {
+ var sb = new Cu.Sandbox('http://example.com');
+ from.test = sb.eval('var o = {prop: 2, f: function() {return 42;}}; o');
+
+ // Make sure that |from| has Xrays to sb.
+ Assert.equal(from.eval('test.prop'), 2);
+ Assert.equal(from.eval('test.f'), undefined);
+
+ // Make sure that waivability works as expected.
+ Assert.equal(from.eval('!!test.wrappedJSObject'), allowed);
+ Assert.equal(from.eval('XPCNativeWrapper.unwrap(test) !== test'), allowed);
+
+ // Make a sandbox with the same principal as |from|, but without any waiver
+ // restrictions, and make sure that the waiver does not transfer.
+ var friend = new Cu.Sandbox(Cu.getObjectPrincipal(from));
+ friend.test = from.test;
+ friend.eval('var waived = test.wrappedJSObject;');
+ Assert.equal(friend.eval('waived.f()'), 42);
+ friend.from = from;
+ friend.eval('from.waived = waived');
+ Assert.equal(from.eval('!!waived.f'), allowed);
+}
+
+function run_test() {
+ checkWaivers(new Cu.Sandbox('http://example.com'), true);
+ checkWaivers(new Cu.Sandbox('http://example.com', {allowWaivers: false}), false);
+ checkWaivers(new Cu.Sandbox(['http://example.com']), true);
+ checkWaivers(new Cu.Sandbox(['http://example.com'], {allowWaivers: false}), false);
+}
diff --git a/js/xpconnect/tests/unit/test_allowedDomains.js b/js/xpconnect/tests/unit/test_allowedDomains.js
new file mode 100644
index 0000000000..bc703a9f6d
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_allowedDomains.js
@@ -0,0 +1,41 @@
+function run_test() {
+ var sbMaster = Cu.Sandbox(["http://www.a.com",
+ "http://www.b.com",
+ "http://www.d.com"]);
+ var sbSubset = Cu.Sandbox(["http://www.d.com",
+ "http://www.a.com"]);
+
+ var sbA = Cu.Sandbox("http://www.a.com");
+ var sbB = Cu.Sandbox("http://www.b.com");
+ var sbC = Cu.Sandbox("http://www.c.com");
+
+ sbMaster.objA = Cu.evalInSandbox("var obj = {prop1:200}; obj", sbA);
+ sbMaster.objB = Cu.evalInSandbox("var obj = {prop1:200}; obj", sbB);
+ sbMaster.objC = Cu.evalInSandbox("var obj = {prop1:200}; obj", sbC);
+ sbMaster.objOwn = Cu.evalInSandbox("var obj = {prop1:200}; obj", sbMaster);
+
+ sbMaster.objSubset = Cu.evalInSandbox("var obj = {prop1:200}; obj", sbSubset);
+ sbA.objMaster = Cu.evalInSandbox("var obj = {prop1:200}; obj", sbMaster);
+ sbSubset.objMaster = Cu.evalInSandbox("var obj = {prop1:200}; obj", sbMaster);
+
+ var ret;
+ ret = Cu.evalInSandbox("objA.prop1", sbMaster);
+ Assert.equal(ret, 200);
+ ret = Cu.evalInSandbox("objB.prop1", sbMaster);
+ Assert.equal(ret, 200);
+ ret = Cu.evalInSandbox("objSubset.prop1", sbMaster);
+ Assert.equal(ret, 200);
+
+ function evalAndCatch(str, sb) {
+ try {
+ ret = Cu.evalInSandbox(str, sb);
+ Assert.ok(false, "unexpected pass")
+ } catch (e) {
+ Assert.ok(e.message && e.message.includes("Permission denied to access property"));
+ }
+ }
+
+ evalAndCatch("objC.prop1", sbMaster);
+ evalAndCatch("objMaster.prop1", sbA);
+ evalAndCatch("objMaster.prop1", sbSubset);
+}
diff --git a/js/xpconnect/tests/unit/test_allowedDomainsXHR.js b/js/xpconnect/tests/unit/test_allowedDomainsXHR.js
new file mode 100644
index 0000000000..f1e9cb2892
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_allowedDomainsXHR.js
@@ -0,0 +1,135 @@
+const { HttpServer } = ChromeUtils.importESModule("resource://testing-common/httpd.sys.mjs");
+
+var httpserver = new HttpServer();
+var httpserver2 = new HttpServer();
+var httpserver3 = new HttpServer();
+var testpath = "/simple";
+var redirectpath = "/redirect";
+var negativetestpath = "/negative";
+var httpbody = "<?xml version='1.0' ?><root>0123456789</root>";
+
+var sb = Cu.Sandbox(["http://www.example.com",
+ "http://localhost:4444/redirect",
+ "http://localhost:4444/simple",
+ "http://localhost:4446/redirect"],
+ { wantGlobalProperties: ["XMLHttpRequest"] });
+
+function createXHR(loc, async)
+{
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", "http://localhost:" + loc, async);
+ return xhr;
+}
+
+function checkResults(xhr)
+{
+ if (xhr.readyState != 4)
+ return false;
+
+ equal(xhr.status, 200);
+ equal(xhr.responseText, httpbody);
+
+ var root_node = xhr.responseXML.getElementsByTagName('root').item(0);
+ equal(root_node.firstChild.data, "0123456789");
+ return true;
+}
+
+var httpServersClosed = 0;
+function finishIfDone()
+{
+ if (++httpServersClosed == 3)
+ do_test_finished();
+}
+
+function run_test()
+{
+ do_get_profile();
+ do_test_pending();
+
+ httpserver.registerPathHandler(testpath, serverHandler);
+ httpserver.registerPathHandler(redirectpath, redirectHandler1);
+ httpserver.start(4444);
+
+ httpserver2.registerPathHandler(negativetestpath, serverHandler);
+ httpserver2.start(4445);
+
+ httpserver3.registerPathHandler(redirectpath, redirectHandler2);
+ httpserver3.start(4446);
+
+ // Test sync XHR sending
+ Cu.evalInSandbox('var createXHR = ' + createXHR.toString(), sb);
+ var res = Cu.evalInSandbox('var sync = createXHR("4444/simple"); sync.send(null); sync', sb);
+ Assert.ok(checkResults(res));
+
+ var principal = res.responseXML.nodePrincipal;
+ Assert.ok(principal.isContentPrincipal);
+ var requestURL = "http://localhost:4444/redirect";
+ Assert.equal(principal.spec, requestURL);
+
+ // negative test sync XHR sending (to ensure that the xhr do not have chrome caps, see bug 779821)
+ try {
+ Cu.evalInSandbox('var createXHR = ' + createXHR.toString(), sb);
+ var res = Cu.evalInSandbox('var sync = createXHR("4445/negative"); sync.send(null); sync', sb);
+ Assert.equal(false, true, "XHR created from sandbox should not have chrome caps");
+ } catch (e) {
+ Assert.ok(true);
+ }
+
+ // Test redirect handling.
+ // This request bounces to server 2 and then back to server 1. Neither of
+ // these servers support CORS, but if the expanded principal is used as the
+ // triggering principal, this should work.
+ Cu.evalInSandbox('var createXHR = ' + createXHR.toString(), sb);
+ var res = Cu.evalInSandbox('var sync = createXHR("4444/redirect"); sync.send(null); sync', sb);
+ Assert.ok(checkResults(res));
+
+ var principal = res.responseXML.nodePrincipal;
+ Assert.ok(principal.isContentPrincipal);
+ var requestURL = "http://localhost:4444/redirect";
+ Assert.equal(principal.spec, requestURL);
+
+ httpserver2.stop(finishIfDone);
+ httpserver3.stop(finishIfDone);
+
+ // Test async XHR sending
+ sb.finish = function(){
+ httpserver.stop(finishIfDone);
+ }
+
+ // We want to execute checkResults from the scope of the sandbox as well to
+ // make sure that there are no permission errors related to nsEP. For that
+ // we need to clone the function into the sandbox and make a few things
+ // available for it.
+ Cu.evalInSandbox('var checkResults = ' + checkResults.toSource(), sb);
+ sb.equal = equal;
+ sb.httpbody = httpbody;
+
+ function changeListener(event) {
+ if (checkResults(async))
+ finish();
+ }
+
+ var async = Cu.evalInSandbox('var async = createXHR("4444/simple", true);' +
+ 'async.addEventListener("readystatechange", ' +
+ changeListener.toString() + ', false);' +
+ 'async', sb);
+ async.send(null);
+}
+
+function serverHandler(request, response)
+{
+ response.setHeader("Content-Type", "text/xml", false);
+ response.bodyOutputStream.write(httpbody, httpbody.length);
+}
+
+function redirectHandler1(request, response)
+{
+ response.setStatusLine(request.httpVersion, 302, "Found");
+ response.setHeader("Location", "http://localhost:4446/redirect", false);
+}
+
+function redirectHandler2(request, response)
+{
+ response.setStatusLine(request.httpVersion, 302, "Found");
+ response.setHeader("Location", "http://localhost:4444/simple", false);
+}
diff --git a/js/xpconnect/tests/unit/test_attributes.js b/js/xpconnect/tests/unit/test_attributes.js
new file mode 100644
index 0000000000..4fc0acaa91
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_attributes.js
@@ -0,0 +1,103 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+var ObjectReadWrite = {
+ QueryInterface: ChromeUtils.generateQI(["nsIXPCTestObjectReadWrite"]),
+
+ /* nsIXPCTestObjectReadWrite */
+ stringProperty: "XPConnect Read-Writable String",
+ booleanProperty: true,
+ shortProperty: 32767,
+ longProperty: 2147483647,
+ floatProperty: 5.5,
+ charProperty: "X",
+ // timeProperty is PRTime and signed type.
+ // So it has to allow negative value.
+ timeProperty: -1,
+};
+
+var ObjectReadOnly = {
+ QueryInterface: ChromeUtils.generateQI(["nsIXPCTestObjectReadOnly"]),
+
+ /* nsIXPCTestObjectReadOnly */
+ strReadOnly: "XPConnect Read-Only String",
+ boolReadOnly: true,
+ shortReadOnly: 32767,
+ longReadOnly: 2147483647,
+ floatReadOnly: 5.5,
+ charReadOnly: "X",
+ // timeProperty is PRTime and signed type.
+ // So it has to allow negative value.
+ timeReadOnly: -1,
+};
+
+function run_test() {
+ // Load the component manifests.
+ registerXPCTestComponents();
+
+ // Test for each component.
+ test_component_readwrite(Cc["@mozilla.org/js/xpc/test/native/ObjectReadWrite;1"].createInstance());
+ test_component_readwrite(xpcWrap(ObjectReadWrite));
+ test_component_readonly(Cc["@mozilla.org/js/xpc/test/native/ObjectReadOnly;1"].createInstance());
+ test_component_readonly(xpcWrap(ObjectReadOnly));
+}
+
+function test_component_readwrite(obj) {
+ // Instantiate the object.
+ var o = obj.QueryInterface(Ci.nsIXPCTestObjectReadWrite);
+
+ // Test the initial values.
+ Assert.equal("XPConnect Read-Writable String", o.stringProperty);
+ Assert.equal(true, o.booleanProperty);
+ Assert.equal(32767, o.shortProperty);
+ Assert.equal(2147483647, o.longProperty);
+ Assert.ok(5.25 < o.floatProperty && 5.75 > o.floatProperty);
+ Assert.equal("X", o.charProperty);
+ Assert.equal(-1, o.timeProperty);
+
+ // Write new values.
+ o.stringProperty = "another string";
+ o.booleanProperty = false;
+ o.shortProperty = -12345;
+ o.longProperty = 1234567890;
+ o.floatProperty = 10.2;
+ o.charProperty = "Z";
+ o.timeProperty = 1;
+
+ // Test the new values.
+ Assert.equal("another string", o.stringProperty);
+ Assert.equal(false, o.booleanProperty);
+ Assert.equal(-12345, o.shortProperty);
+ Assert.equal(1234567890, o.longProperty);
+ Assert.ok(10.15 < o.floatProperty && 10.25 > o.floatProperty);
+ Assert.equal("Z", o.charProperty);
+ Assert.equal(1, o.timeProperty);
+
+ // Assign values that differ from the expected type to verify conversion.
+
+ function SetAndTestBooleanProperty(newValue, expectedValue) {
+ o.booleanProperty = newValue;
+ Assert.equal(expectedValue, o.booleanProperty);
+ };
+ SetAndTestBooleanProperty(false, false);
+ SetAndTestBooleanProperty(1, true);
+ SetAndTestBooleanProperty(null, false);
+ SetAndTestBooleanProperty("A", true);
+ SetAndTestBooleanProperty(undefined, false);
+ SetAndTestBooleanProperty([], true);
+ SetAndTestBooleanProperty({}, true);
+}
+
+function test_component_readonly(obj) {
+ var o = obj.QueryInterface(Ci.nsIXPCTestObjectReadOnly);
+
+ // Test the initial values.
+ Assert.equal("XPConnect Read-Only String", o.strReadOnly);
+ Assert.equal(true, o.boolReadOnly);
+ Assert.equal(32767, o.shortReadOnly);
+ Assert.equal(2147483647, o.longReadOnly);
+ Assert.ok(5.25 < o.floatReadOnly && 5.75 > o.floatReadOnly);
+ Assert.equal("X", o.charReadOnly);
+ Assert.equal(-1, o.timeReadOnly);
+}
diff --git a/js/xpconnect/tests/unit/test_blob.js b/js/xpconnect/tests/unit/test_blob.js
new file mode 100644
index 0000000000..42f6cf9be8
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_blob.js
@@ -0,0 +1,8 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+function run_test() {
+ let { TestBlob } = ChromeUtils.import("resource://test/TestBlob.jsm");
+ Assert.ok(TestBlob.doTest());
+}
diff --git a/js/xpconnect/tests/unit/test_blob2.js b/js/xpconnect/tests/unit/test_blob2.js
new file mode 100644
index 0000000000..90d4bdc1c6
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_blob2.js
@@ -0,0 +1,34 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+Cu.importGlobalProperties(['Blob', 'File']);
+
+function run_test() {
+ // throw if anything goes wrong
+ let testContent = "<a id=\"a\"><b id=\"b\">hey!<\/b><\/a>";
+ // should be able to construct a file
+ var f1 = new Blob([testContent], {"type" : "text/xml"});
+
+ // do some tests
+ Assert.ok(f1 instanceof Blob, "Should be a DOM Blob");
+
+ Assert.ok(!(f1 instanceof File), "Should not be a DOM File");
+
+ Assert.ok(f1.type == "text/xml", "Wrong type");
+
+ Assert.ok(f1.size == testContent.length, "Wrong content size");
+
+ var f2 = new Blob();
+ Assert.ok(f2.size == 0, "Wrong size");
+ Assert.ok(f2.type == "", "Wrong type");
+
+ var threw = false;
+ try {
+ // Needs a valid ctor argument
+ var f2 = new Blob(Date(132131532));
+ } catch (e) {
+ threw = true;
+ }
+ Assert.ok(threw, "Passing a random object should fail");
+}
diff --git a/js/xpconnect/tests/unit/test_bogus_files.js b/js/xpconnect/tests/unit/test_bogus_files.js
new file mode 100644
index 0000000000..1e8b9f0f2a
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_bogus_files.js
@@ -0,0 +1,32 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+function test_BrokenFile(path, shouldThrow, expectedName) {
+ var didThrow = false;
+ try {
+ ChromeUtils.import(path);
+ } catch (ex) {
+ var exceptionName = ex.name;
+ print("ex: " + ex + "; name = " + ex.name);
+ didThrow = true;
+ }
+
+ Assert.equal(didThrow, shouldThrow);
+ if (didThrow)
+ Assert.equal(exceptionName, expectedName);
+}
+
+function run_test() {
+ test_BrokenFile("resource://test/bogus_exports_type.jsm", true, "Error");
+
+ test_BrokenFile("resource://test/bogus_element_type.jsm", true, "Error");
+
+ test_BrokenFile("resource://test/non_existing.jsm",
+ true,
+ "NS_ERROR_FILE_NOT_FOUND");
+
+ test_BrokenFile("chrome://test/content/test.jsm",
+ true,
+ "NS_ERROR_FILE_NOT_FOUND");
+}
diff --git a/js/xpconnect/tests/unit/test_bug1001094.js b/js/xpconnect/tests/unit/test_bug1001094.js
new file mode 100644
index 0000000000..ac06e4c0f3
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_bug1001094.js
@@ -0,0 +1,4 @@
+function run_test() {
+ // Make sure nsJSID implements classinfo.
+ Assert.equal(Components.ID("{a6e2a27f-5521-4b35-8b52-99799a744aee}").equals, Components.ID("{daa47351-7d2e-44a7-b8e3-281802a1eab7}").equals);
+}
diff --git a/js/xpconnect/tests/unit/test_bug1021312.js b/js/xpconnect/tests/unit/test_bug1021312.js
new file mode 100644
index 0000000000..ccb9981b43
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_bug1021312.js
@@ -0,0 +1,15 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+function run_test() {
+ let sb = new Cu.Sandbox(this);
+ var called = false;
+
+ Cu.exportFunction(function(str) { Assert.ok(/someString/.test(str)); called = true; },
+ sb, { defineAs: "func" });
+ // Do something weird with the string to make sure that it doesn't get interned.
+ Cu.evalInSandbox("var str = 'someString'; for (var i = 0; i < 10; ++i) str += i;", sb);
+ Cu.evalInSandbox("func(str);", sb);
+ Assert.ok(called);
+}
diff --git a/js/xpconnect/tests/unit/test_bug1033253.js b/js/xpconnect/tests/unit/test_bug1033253.js
new file mode 100644
index 0000000000..e5860833b2
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_bug1033253.js
@@ -0,0 +1,5 @@
+function run_test() {
+ var sb = Cu.Sandbox('http://www.example.com');
+ var f = Cu.evalInSandbox('var f = function() {}; f;', sb);
+ Assert.equal(f.name, "");
+}
diff --git a/js/xpconnect/tests/unit/test_bug1033920.js b/js/xpconnect/tests/unit/test_bug1033920.js
new file mode 100644
index 0000000000..6e85ec4f1d
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_bug1033920.js
@@ -0,0 +1,6 @@
+function run_test() {
+ var sb = Cu.Sandbox('http://www.example.com');
+ var o = new sb.Object();
+ o.__proto__ = null;
+ Assert.equal(Object.getPrototypeOf(o), null);
+}
diff --git a/js/xpconnect/tests/unit/test_bug1033927.js b/js/xpconnect/tests/unit/test_bug1033927.js
new file mode 100644
index 0000000000..cd2bb210e7
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_bug1033927.js
@@ -0,0 +1,8 @@
+function run_test() {
+ var sb = Cu.Sandbox('http://www.example.com', { wantGlobalProperties: ['XMLHttpRequest']});
+ var xhr = Cu.evalInSandbox('new XMLHttpRequest()', sb);
+ Assert.equal(xhr[Symbol.toStringTag], "XMLHttpRequest");
+ Assert.equal(xhr.toString(), '[object XMLHttpRequest]');
+ Assert.equal((new sb.Object()).toString(), '[object Object]');
+ Assert.equal(sb.Object.prototype.toString.call(new sb.Uint16Array()), '[object Uint16Array]');
+}
diff --git a/js/xpconnect/tests/unit/test_bug1034262.js b/js/xpconnect/tests/unit/test_bug1034262.js
new file mode 100644
index 0000000000..6bd598bd53
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_bug1034262.js
@@ -0,0 +1,8 @@
+function run_test() {
+ var sb1 = Cu.Sandbox('http://www.example.com', { wantXrays: true });
+ var sb2 = Cu.Sandbox('http://www.example.com', { wantXrays: false });
+ sb2.f = Cu.evalInSandbox('x => typeof x', sb1);
+ Assert.equal(Cu.evalInSandbox('f(dump)', sb2), 'function');
+ Assert.equal(Cu.evalInSandbox('f.call(null, dump)', sb2), 'function');
+ Assert.equal(Cu.evalInSandbox('f.apply(null, [dump])', sb2), 'function');
+}
diff --git a/js/xpconnect/tests/unit/test_bug1081990.js b/js/xpconnect/tests/unit/test_bug1081990.js
new file mode 100644
index 0000000000..80e37ac282
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_bug1081990.js
@@ -0,0 +1,9 @@
+function run_test() {
+ var sb = new Cu.Sandbox('http://www.example.com');
+ sb.obj = {};
+ sb.arr = [];
+ sb.fun = function() {};
+ Assert.ok(sb.eval('Object.getPrototypeOf(obj) == null'));
+ Assert.ok(sb.eval('Object.getPrototypeOf(arr) == null'));
+ Assert.ok(sb.eval('Object.getPrototypeOf(fun) == null'));
+}
diff --git a/js/xpconnect/tests/unit/test_bug1110546.js b/js/xpconnect/tests/unit/test_bug1110546.js
new file mode 100644
index 0000000000..04e1add915
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_bug1110546.js
@@ -0,0 +1,4 @@
+function run_test() {
+ var sb = new Cu.Sandbox(null);
+ Assert.ok(Cu.getObjectPrincipal(sb).isNullPrincipal);
+}
diff --git a/js/xpconnect/tests/unit/test_bug1131707.js b/js/xpconnect/tests/unit/test_bug1131707.js
new file mode 100644
index 0000000000..57ade9f8c8
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_bug1131707.js
@@ -0,0 +1,20 @@
+function testStrict(sb) {
+ "use strict";
+ Assert.equal(sb.eval("typeof wrappedCtor()"), "string");
+ Assert.equal(sb.eval("typeof new wrappedCtor()"), "object");
+}
+
+function run_test() {
+ var sb = new Cu.Sandbox(null);
+ var dateCtor = sb.Date;
+ sb.wrappedCtor = Cu.exportFunction(function wrapper(val) {
+ "use strict";
+ var constructing = this.constructor == wrapper;
+ return constructing ? new dateCtor(val) : dateCtor(val);
+ }, sb);
+ Assert.equal(typeof Date(), "string");
+ Assert.equal(typeof new Date(), "object");
+ Assert.equal(sb.eval("typeof wrappedCtor()"), "string");
+ Assert.equal(sb.eval("typeof new wrappedCtor()"), "object");
+ testStrict(sb);
+}
diff --git a/js/xpconnect/tests/unit/test_bug1150771.js b/js/xpconnect/tests/unit/test_bug1150771.js
new file mode 100644
index 0000000000..433156d6f5
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_bug1150771.js
@@ -0,0 +1,12 @@
+function run_test() {
+let sandbox1 = new Cu.Sandbox(null);
+let sandbox2 = new Cu.Sandbox(null);
+let arg = Cu.evalInSandbox('({ buf: new ArrayBuffer(2) })', sandbox1);
+
+let clonedArg = Cu.cloneInto(Cu.waiveXrays(arg), sandbox2);
+Assert.equal(typeof Cu.waiveXrays(clonedArg).buf, "object");
+
+clonedArg = Cu.cloneInto(arg, sandbox2);
+Assert.equal(typeof Cu.waiveXrays(clonedArg).buf, "object");
+Assert.equal(Cu.waiveXrays(clonedArg).buf.constructor.name, "ArrayBuffer");
+}
diff --git a/js/xpconnect/tests/unit/test_bug1151385.js b/js/xpconnect/tests/unit/test_bug1151385.js
new file mode 100644
index 0000000000..913050248f
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_bug1151385.js
@@ -0,0 +1,9 @@
+function run_test()
+{
+ try {
+ var sandbox = new Cu.Sandbox(null, {"sandboxPrototype" : {}});
+ Assert.ok(false);
+ } catch (e) {
+ Assert.ok(/must subsume sandboxPrototype/.test(e));
+ }
+}
diff --git a/js/xpconnect/tests/unit/test_bug1170311.js b/js/xpconnect/tests/unit/test_bug1170311.js
new file mode 100644
index 0000000000..cdbe62407a
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_bug1170311.js
@@ -0,0 +1,4 @@
+function run_test() {
+ do_check_throws_nsIException(() => Cu.getObjectPrincipal({}).equals(null), "NS_ERROR_ILLEGAL_VALUE");
+ do_check_throws_nsIException(() => Cu.getObjectPrincipal({}).subsumes(null), "NS_ERROR_ILLEGAL_VALUE");
+}
diff --git a/js/xpconnect/tests/unit/test_bug1244222.js b/js/xpconnect/tests/unit/test_bug1244222.js
new file mode 100644
index 0000000000..b907c72033
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_bug1244222.js
@@ -0,0 +1,31 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+Services.prefs.setBoolPref("security.allow_eval_with_system_principal", true);
+registerCleanupFunction(() => {
+ Services.prefs.clearUserPref("security.allow_eval_with_system_principal");
+});
+
+var TestUtils = {
+ QueryInterface: ChromeUtils.generateQI(["nsIXPCTestUtils"]),
+ doubleWrapFunction(fun) { return fun }
+};
+
+function run_test() {
+ // Generate a CCW to a function.
+ var sb = new Cu.Sandbox(this);
+ sb.eval('function fun(x) { return x; }');
+ Assert.equal(sb.fun("foo"), "foo");
+
+ // Double-wrap the CCW.
+ var utils = xpcWrap(TestUtils, Ci.nsIXPCTestUtils);
+ var doubleWrapped = utils.doubleWrapFunction(sb.fun);
+ Assert.equal(doubleWrapped.echo("foo"), "foo");
+
+ // GC.
+ Cu.forceGC();
+
+ // Make sure it still works.
+ Assert.equal(doubleWrapped.echo("foo"), "foo");
+}
diff --git a/js/xpconnect/tests/unit/test_bug1617527.js b/js/xpconnect/tests/unit/test_bug1617527.js
new file mode 100644
index 0000000000..3db33e60d9
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_bug1617527.js
@@ -0,0 +1,17 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+function run_test() {
+ let sb1 = new Cu.Sandbox("https://example.org");
+ let throwingFunc = Cu.evalInSandbox("new Function('throw new Error')", sb1);
+ // NOTE: Different origin from the other sandbox.
+ let sb2 = new Cu.Sandbox("https://example.com");
+ Cu.exportFunction(function() {
+ // Call a different-compartment throwing function.
+ throwingFunc();
+ }, sb2, { defineAs: "func" });
+ let threw = Cu.evalInSandbox("var threw; try { func(); threw = false; } catch (e) { threw = true } threw",
+ sb2);
+ Assert.ok(threw);
+}
diff --git a/js/xpconnect/tests/unit/test_bug267645.js b/js/xpconnect/tests/unit/test_bug267645.js
new file mode 100644
index 0000000000..6196a2165c
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_bug267645.js
@@ -0,0 +1,62 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+function run_test() {
+ let sb = new Cu.Sandbox("https://example.com",
+ { wantGlobalProperties: ["DOMException"] });
+ Cu.exportFunction(function() {
+ undefined.foo();
+ }, sb, { defineAs: "func" });
+ // By default, the stacks of things running in a sandbox will contain the
+ // actual evalInSandbox() call location. To override that, we have to pass an
+ // explicit filename.
+ let threw = Cu.evalInSandbox("var threw; try { func(); threw = false; } catch (e) { globalThis.exn = e; threw = true } threw",
+ sb, "", "FakeFile");
+ Assert.ok(threw);
+
+ // Check what the sandbox could see from this exception.
+ Assert.ok(!Cu.evalInSandbox("exn.filename", sb).includes("/unit/"));
+ Assert.equal(Cu.evalInSandbox("exn.fileName", sb), undefined);
+ Assert.ok(!Cu.evalInSandbox("exn.stack", sb).includes("/unit/"));
+ Assert.equal(Cu.evalInSandbox("exn.message", sb), "An exception was thrown");
+ Assert.equal(Cu.evalInSandbox("exn.name", sb), "InvalidStateError");
+
+ Cu.exportFunction(function() {
+ throw new Error("Hello");
+ }, sb, { defineAs: "func2" });
+ threw = Cu.evalInSandbox("var threw; try { func2(); threw = false; } catch (e) { globalThis.exn = e; threw = true } threw",
+ sb, "", "FakeFile");
+ Assert.ok(threw);
+ Assert.ok(!Cu.evalInSandbox("exn.filename", sb).includes("/unit/"));
+ Assert.equal(Cu.evalInSandbox("exn.fileName", sb), undefined);
+ Assert.ok(!Cu.evalInSandbox("exn.stack", sb).includes("/unit/"));
+ Assert.equal(Cu.evalInSandbox("exn.message", sb), "An exception was thrown");
+ Assert.equal(Cu.evalInSandbox("exn.name", sb), "InvalidStateError");
+
+ let ctor = Cu.evalInSandbox("TypeError", sb);
+ Cu.exportFunction(function() {
+ throw new ctor("Hello");
+ }, sb, { defineAs: "func3" });
+ threw = Cu.evalInSandbox("var threw; try { func3(); threw = false; } catch (e) { globalThis.exn = e; threw = true } threw",
+ sb, "", "FakeFile");
+ Assert.ok(threw);
+ Assert.ok(!Cu.evalInSandbox("exn.fileName", sb).includes("/unit/"));
+ Assert.equal(Cu.evalInSandbox("exn.filename", sb), undefined);
+ Assert.ok(!Cu.evalInSandbox("exn.stack", sb).includes("/unit/"));
+ Assert.equal(Cu.evalInSandbox("exn.message", sb), "Hello");
+ Assert.equal(Cu.evalInSandbox("exn.name", sb), "TypeError");
+
+ ctor = Cu.evalInSandbox("DOMException", sb);
+ Cu.exportFunction(function() {
+ throw new ctor("Goodbye", "InvalidAccessError");
+ }, sb, { defineAs: "func4" });
+ threw = Cu.evalInSandbox("var threw; try { func4(); threw = false; } catch (e) { globalThis.exn = e; threw = true } threw",
+ sb, "", "FakeFile");
+ Assert.ok(threw);
+ Assert.ok(!Cu.evalInSandbox("exn.filename", sb).includes("/unit/"));
+ Assert.equal(Cu.evalInSandbox("exn.fileName", sb), undefined);
+ Assert.ok(!Cu.evalInSandbox("exn.stack", sb).includes("/unit/"));
+ Assert.equal(Cu.evalInSandbox("exn.message", sb), "Goodbye");
+ Assert.equal(Cu.evalInSandbox("exn.name", sb), "InvalidAccessError");
+}
diff --git a/js/xpconnect/tests/unit/test_bug408412.js b/js/xpconnect/tests/unit/test_bug408412.js
new file mode 100644
index 0000000000..06321a6f87
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_bug408412.js
@@ -0,0 +1,12 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+function run_test() {
+ try {
+ ChromeUtils.import("resource://test/syntax_error.jsm");
+ do_throw("Failed to report any error at all");
+ } catch (e) {
+ Assert.notEqual(/^SyntaxError:/.exec(e + ''), null);
+ }
+}
diff --git a/js/xpconnect/tests/unit/test_bug451678.js b/js/xpconnect/tests/unit/test_bug451678.js
new file mode 100644
index 0000000000..90c18a614c
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_bug451678.js
@@ -0,0 +1,15 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+function run_test() {
+ var file = do_get_file("bug451678_subscript.js");
+ var ios = Cc["@mozilla.org/network/io-service;1"]
+ .getService(Ci.nsIIOService);
+ var uri = ios.newFileURI(file);
+ var scriptLoader = Cc["@mozilla.org/moz/jssubscript-loader;1"]
+ .getService(Ci.mozIJSSubScriptLoader);
+ var srvScope = {};
+ scriptLoader.loadSubScript(uri.spec, srvScope);
+ Assert.ok('makeTags' in srvScope && srvScope.makeTags instanceof Function);
+}
diff --git a/js/xpconnect/tests/unit/test_bug604362.js b/js/xpconnect/tests/unit/test_bug604362.js
new file mode 100644
index 0000000000..7adcfab96c
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_bug604362.js
@@ -0,0 +1,10 @@
+function run_test() {
+ var sp = Cc["@mozilla.org/systemprincipal;1"].
+ createInstance(Ci.nsIPrincipal);
+ var s = Cu.Sandbox(sp);
+ s.a = [];
+ s.Cu = Cu;
+ s.C = Components;
+ s.notEqual = notEqual;
+ Cu.evalInSandbox("notEqual(Cu.import, undefined);", s);
+}
diff --git a/js/xpconnect/tests/unit/test_bug677864.js b/js/xpconnect/tests/unit/test_bug677864.js
new file mode 100644
index 0000000000..f92d15fe66
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_bug677864.js
@@ -0,0 +1,9 @@
+function check_cl(iface, desc) {
+ Assert.equal(iface.QueryInterface(Ci.nsIClassInfo).classDescription, desc);
+}
+
+function run_test() {
+ check_cl(Ci, 'XPCComponents_Interfaces');
+ check_cl(Cc, 'XPCComponents_Classes');
+ check_cl(Cr, 'XPCComponents_Results');
+}
diff --git a/js/xpconnect/tests/unit/test_bug711404.js b/js/xpconnect/tests/unit/test_bug711404.js
new file mode 100644
index 0000000000..f74b43316c
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_bug711404.js
@@ -0,0 +1,7 @@
+function run_test()
+{
+ var p = Cc["@mozilla.org/hash-property-bag;1"].
+ createInstance(Ci.nsIWritablePropertyBag2);
+ p.setPropertyAsInt64("a", -4000);
+ Assert.notEqual(p.getPropertyAsUint64("a"), -4000);
+}
diff --git a/js/xpconnect/tests/unit/test_bug742444.js b/js/xpconnect/tests/unit/test_bug742444.js
new file mode 100644
index 0000000000..3b8262834f
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_bug742444.js
@@ -0,0 +1,16 @@
+function run_test() {
+ let sb1A = Cu.Sandbox('http://www.example.com');
+ let sb1B = Cu.Sandbox('http://www.example.com');
+ let sb2 = Cu.Sandbox('http://www.example.org');
+ let sbChrome = Cu.Sandbox(this);
+ let obj = new sb1A.Object();
+ sb1B.obj = obj;
+ sb1B.waived = Cu.waiveXrays(obj);
+ sb2.obj = obj;
+ sb2.waived = Cu.waiveXrays(obj);
+ sbChrome.obj = obj;
+ sbChrome.waived = Cu.waiveXrays(obj);
+ Assert.ok(Cu.evalInSandbox('obj === waived', sb1B));
+ Assert.ok(Cu.evalInSandbox('obj === waived', sb2));
+ Assert.ok(Cu.evalInSandbox('obj !== waived', sbChrome));
+}
diff --git a/js/xpconnect/tests/unit/test_bug778409.js b/js/xpconnect/tests/unit/test_bug778409.js
new file mode 100644
index 0000000000..4ca2ea6767
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_bug778409.js
@@ -0,0 +1,10 @@
+function run_test() {
+ var sb1 = Cu.Sandbox('http://example.com');
+ var sb2 = Cu.Sandbox('http://example.org');
+ var chromeObj = {foo: 2};
+ var sb1obj = Cu.evalInSandbox('new Object()', sb1);
+ chromeObj.__proto__ = sb1obj;
+ sb2.wrapMe = chromeObj;
+ Assert.ok(true, "Didn't crash");
+ Assert.equal(sb2.wrapMe.__proto__, sb1obj, 'proto set correctly');
+}
diff --git a/js/xpconnect/tests/unit/test_bug780370.js b/js/xpconnect/tests/unit/test_bug780370.js
new file mode 100644
index 0000000000..e0da551201
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_bug780370.js
@@ -0,0 +1,16 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* See https://bugzilla.mozilla.org/show_bug.cgi?id=780370 */
+
+// Use a COW to expose a function from a standard prototype, and make we deny
+// access to it.
+
+function run_test()
+{
+ var sb = Cu.Sandbox("http://www.example.com");
+ sb.obj = { foo: 42 };
+ Assert.equal(Cu.evalInSandbox('typeof obj.foo', sb), 'undefined', "COW works as expected");
+ Assert.equal(Cu.evalInSandbox('obj.hasOwnProperty', sb), undefined);
+}
diff --git a/js/xpconnect/tests/unit/test_bug809652.js b/js/xpconnect/tests/unit/test_bug809652.js
new file mode 100644
index 0000000000..6d63c6531f
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_bug809652.js
@@ -0,0 +1,62 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* See https://bugzilla.mozilla.org/show_bug.cgi?id=813901 */
+
+const TypedArrays = [ Int8Array, Uint8Array, Int16Array, Uint16Array,
+ Int32Array, Uint32Array, Float32Array, Float64Array,
+ Uint8ClampedArray ];
+
+// Make sure that the correct nativecall-y stuff is denied on security wrappers.
+
+function run_test() {
+
+ var sb = new Cu.Sandbox('http://www.example.org');
+ sb.obj = {foo: 2};
+
+ /* Set up some typed arrays. */
+ sb.ab = new ArrayBuffer(8);
+ for (var i = 0; i < 8; ++i)
+ new Uint8Array(sb.ab)[i] = i * 10;
+ sb.ta = [];
+ TypedArrays.forEach(f => sb.ta.push(new f(sb.ab)));
+ sb.dv = new DataView(sb.ab);
+
+ /* Things that should throw. */
+ checkThrows("Object.prototype.__lookupSetter__('__proto__').call(obj, {});", sb);
+ sb.re = /f/;
+ checkThrows("RegExp.prototype.exec.call(re, 'abcdefg').index", sb);
+ sb.d = new Date();
+ checkThrows("Date.prototype.setYear.call(d, 2011)", sb);
+ sb.m = new Map();
+ checkThrows("(new Map()).clear.call(m)", sb);
+ checkThrows("ArrayBuffer.prototype.__lookupGetter__('byteLength').call(ab);", sb);
+ checkThrows("ArrayBuffer.prototype.slice.call(ab, 0);", sb);
+ checkThrows("DataView.prototype.getInt8.call(dv, 0);", sb);
+
+ /* Now that Date is on Xrays, these should all throw. */
+ checkThrows("Date.prototype.getYear.call(d)", sb);
+ checkThrows("Date.prototype.valueOf.call(d)", sb);
+ checkThrows("d.valueOf()", sb);
+ checkThrows("d.toString()", sb);
+
+ /* Typed arrays. */
+ function testForTypedArray(t) {
+ sb.curr = t;
+ sb.currName = t.constructor.name;
+ checkThrows("this[currName].prototype.subarray.call(curr, 0)[0]", sb);
+ checkThrows("(new this[currName]).__lookupGetter__('length').call(curr)", sb);
+ checkThrows("(new this[currName]).__lookupGetter__('buffer').call(curr)", sb);
+ checkThrows("(new this[currName]).__lookupGetter__('byteOffset').call(curr)", sb);
+ checkThrows("(new this[currName]).__lookupGetter__('byteLength').call(curr)", sb);
+ }
+ sb.ta.forEach(testForTypedArray);
+}
+
+function checkThrows(expression, sb) {
+ var result = Cu.evalInSandbox('(function() { try { ' + expression + '; return "allowed"; } catch (e) { return e.toString(); }})();', sb);
+ dump('result: ' + result + '\n\n\n');
+ Assert.ok(!!/denied/.exec(result));
+}
+
diff --git a/js/xpconnect/tests/unit/test_bug809674.js b/js/xpconnect/tests/unit/test_bug809674.js
new file mode 100644
index 0000000000..dee089e759
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_bug809674.js
@@ -0,0 +1,76 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+
+var Bug809674 = {
+ QueryInterface: ChromeUtils.generateQI(["nsIXPCTestBug809674"]),
+
+ /* nsIXPCTestBug809674 */
+ methodWithOptionalArgc() {},
+
+ addArgs(x, y) {
+ return x + y;
+ },
+ addSubMulArgs(x, y, subOut, mulOut) {
+ subOut.value = x - y;
+ mulOut.value = x * y;
+ return x + y;
+ },
+ addVals(x, y) {
+ return x + y;
+ },
+ addMany(x1, x2, x3, x4, x5, x6, x7, x8) {
+ return x1 + x2 + x3 + x4 + x5 + x6 + x7 + x8;
+ },
+
+ methodNoArgs() {
+ return 7;
+ },
+ methodNoArgsNoRetVal() {},
+
+ valProperty: {value: 42},
+ uintProperty: 123,
+};
+
+function run_test() {
+ // XPConnect wrap the object
+ var o = xpcWrap(Bug809674, Ci.nsIXPCTestBug809674);
+
+ // Methods marked [implicit_jscontext].
+
+ Assert.equal(o.addArgs(12, 34), 46);
+
+ var subRes = {}, mulRes = {};
+ Assert.equal(o.addSubMulArgs(9, 7, subRes, mulRes), 16);
+ Assert.equal(subRes.value, 2);
+ Assert.equal(mulRes.value, 63);
+
+ Assert.equal(o.addVals("foo", "x"), "foox");
+ Assert.equal(o.addVals("foo", 1.2), "foo1.2");
+ Assert.equal(o.addVals(1234, "foo"), "1234foo");
+
+ Assert.equal(o.addMany(1, 2, 4, 8, 16, 32, 64, 128), 255);
+
+ Assert.equal(o.methodNoArgs(), 7);
+ Assert.equal(o.methodNoArgsNoRetVal(), undefined);
+
+ // Attributes marked [implicit_jscontext].
+
+ Assert.equal(o.valProperty.value, 42);
+ o.valProperty = o;
+ Assert.equal(o.valProperty, o);
+
+ Assert.equal(o.uintProperty, 123);
+ o.uintProperty++;
+ Assert.equal(o.uintProperty, 124);
+
+ // [optional_argc] is not supported.
+ try {
+ o.methodWithOptionalArgc();
+ Assert.ok(false);
+ } catch (e) {
+ Assert.ok(true);
+ Assert.ok(/optional_argc/.test(e))
+ }
+}
diff --git a/js/xpconnect/tests/unit/test_bug813901.js b/js/xpconnect/tests/unit/test_bug813901.js
new file mode 100644
index 0000000000..433c7872ef
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_bug813901.js
@@ -0,0 +1,23 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* See https://bugzilla.mozilla.org/show_bug.cgi?id=813901 */
+
+// Make sure that we can't inject __exposedProps__ via the proto of a COW-ed object.
+
+function checkThrows(expression, sb, regexp) {
+ var result = Cu.evalInSandbox('(function() { try { ' + expression + '; return "allowed"; } catch (e) { return e.toString(); }})();', sb);
+ dump('result: ' + result + '\n\n\n');
+ Assert.ok(!!regexp.exec(result));
+}
+
+function run_test() {
+
+ var sb = new Cu.Sandbox('http://www.example.org');
+ sb.obj = {foo: 2};
+ checkThrows('obj.foo = 3;', sb, /denied/);
+ Cu.evalInSandbox("var p = {};", sb);
+ sb.obj.__proto__ = sb.p;
+ checkThrows('obj.foo = 4;', sb, /denied/);
+}
diff --git a/js/xpconnect/tests/unit/test_bug845201.js b/js/xpconnect/tests/unit/test_bug845201.js
new file mode 100644
index 0000000000..74253ccaed
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_bug845201.js
@@ -0,0 +1,18 @@
+function sbTest() {
+ var threw = false;
+ try {
+ for (var x in Components) { }
+ ok(false, "Shouldn't be able to enumerate Components");
+ } catch(e) {
+ ok(true, "Threw appropriately");
+ threw = true;
+ }
+ ok(threw, "Shouldn't have thrown uncatchable exception");
+}
+
+function run_test() {
+ var sb = Cu.Sandbox('http://www.example.com', { wantComponents: true });
+ sb.ok = ok;
+ Cu.evalInSandbox(sbTest.toSource(), sb);
+ Cu.evalInSandbox('sbTest();', sb);
+}
diff --git a/js/xpconnect/tests/unit/test_bug845862.js b/js/xpconnect/tests/unit/test_bug845862.js
new file mode 100644
index 0000000000..41d799803f
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_bug845862.js
@@ -0,0 +1,7 @@
+function run_test() {
+ var sb = new Cu.Sandbox('http://www.example.com');
+ Cu.evalInSandbox("this.foo = {}; Object.defineProperty(foo, 'bar', {get: function() {return {};}});", sb);
+ var desc = Object.getOwnPropertyDescriptor(Cu.waiveXrays(sb.foo), 'bar');
+ var b = desc.get();
+ Assert.ok(b != XPCNativeWrapper(b), "results from accessor descriptors are waived");
+}
diff --git a/js/xpconnect/tests/unit/test_bug849730.js b/js/xpconnect/tests/unit/test_bug849730.js
new file mode 100644
index 0000000000..9be55457bf
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_bug849730.js
@@ -0,0 +1,5 @@
+function run_test() {
+ var sb = new Cu.Sandbox('http://www.example.com');
+ sb.arr = [3, 4];
+ Assert.ok(Cu.evalInSandbox('!Array.isArray(arr);', sb));
+}
diff --git a/js/xpconnect/tests/unit/test_bug851895.js b/js/xpconnect/tests/unit/test_bug851895.js
new file mode 100644
index 0000000000..1c3d0f461f
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_bug851895.js
@@ -0,0 +1,9 @@
+function run_test() {
+ // Make sure Components.utils gets its |this| fixed up.
+ var isXrayWrapper = Cu.isXrayWrapper;
+ Assert.ok(!isXrayWrapper({}), "Didn't throw");
+
+ // Even for classes without |this| fixup, make sure that we don't crash.
+ var isSuccessCode = Components.isSuccessCode;
+ try { isSuccessCode(Cr.NS_OK); } catch (e) {};
+}
diff --git a/js/xpconnect/tests/unit/test_bug853709.js b/js/xpconnect/tests/unit/test_bug853709.js
new file mode 100644
index 0000000000..a59d4707bf
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_bug853709.js
@@ -0,0 +1,30 @@
+function setupChromeSandbox() {
+ this.chromeObj = {a: 2 };
+ this.chromeArr = [4, 2, 1];
+}
+
+function checkDefineThrows(sb, obj, prop, desc) {
+ var result = Cu.evalInSandbox('(function() { try { Object.defineProperty(' + obj + ', "' + prop + '", ' + desc.toSource() + '); return "nothrow"; } catch (e) { return e.toString(); }})();', sb);
+ Assert.notEqual(result, 'nothrow');
+ Assert.ok(!!/denied|prohibited/.exec(result));
+ Assert.ok(result.includes(prop)); // Make sure the prop name is in the error message.
+}
+
+function run_test() {
+ var chromeSB = new Cu.Sandbox(this);
+ var contentSB = new Cu.Sandbox('http://www.example.org');
+ Cu.evalInSandbox('(' + setupChromeSandbox.toSource() + ')()', chromeSB);
+ contentSB.chromeObj = chromeSB.chromeObj;
+ contentSB.chromeArr = chromeSB.chromeArr;
+
+ Assert.equal(Cu.evalInSandbox('chromeObj.a', contentSB), undefined);
+ try {
+ Cu.evalInSandbox('chromeArr[1]', contentSB);
+ Assert.ok(false);
+ } catch (e) { Assert.ok(/denied|insecure/.test(e)); }
+
+ checkDefineThrows(contentSB, 'chromeObj', 'a', {get: function() { return 2; }});
+ checkDefineThrows(contentSB, 'chromeObj', 'a', {configurable: true, get: function() { return 2; }});
+ checkDefineThrows(contentSB, 'chromeObj', 'b', {configurable: true, get: function() { return 2; }, set: function() {}});
+ checkDefineThrows(contentSB, 'chromeArr', '1', {configurable: true, get: function() { return 2; }});
+}
diff --git a/js/xpconnect/tests/unit/test_bug856067.js b/js/xpconnect/tests/unit/test_bug856067.js
new file mode 100644
index 0000000000..b724ba4b18
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_bug856067.js
@@ -0,0 +1,8 @@
+function run_test() {
+ var sb = new Cu.Sandbox('http://www.example.com');
+ let w = Cu.evalInSandbox('var w = new Map()[Symbol.iterator](); w.__proto__ = new Set(); w.foopy = 12; w', sb);
+ Assert.equal(Object.getPrototypeOf(w), sb.Object.prototype);
+ Assert.equal(Object.getOwnPropertyNames(w).length, 0);
+ Assert.equal(w.wrappedJSObject.foopy, 12);
+ Assert.equal(w.foopy, undefined);
+}
diff --git a/js/xpconnect/tests/unit/test_bug867486.js b/js/xpconnect/tests/unit/test_bug867486.js
new file mode 100644
index 0000000000..c053ec27e1
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_bug867486.js
@@ -0,0 +1,8 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+function run_test() {
+ var sb = new Cu.Sandbox('http://www.example.com', { wantComponents: true } );
+ Assert.ok(!Cu.evalInSandbox('"Components" in this', sb));
+}
diff --git a/js/xpconnect/tests/unit/test_bug868675.js b/js/xpconnect/tests/unit/test_bug868675.js
new file mode 100644
index 0000000000..7f5e94f83b
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_bug868675.js
@@ -0,0 +1,29 @@
+function run_test() {
+
+ // Make sure we don't throw for primitive values.
+ var result = "threw";
+ try { result = XPCNativeWrapper.unwrap(2); } catch (e) {}
+ Assert.equal(result, 2);
+ result = "threw";
+ try { result = XPCNativeWrapper(2); } catch (e) {}
+ Assert.equal(result, 2);
+
+ // Make sure we throw when using `new` with primitives.
+ result = null;
+ try { result = new XPCNativeWrapper(2); } catch (e) { result = "catch"; }
+ Assert.equal(result, "catch");
+
+ // Make sure that we can waive on a non-Xrayable object, and that we preserve
+ // transitive waiving behavior.
+ var sb = new Cu.Sandbox('http://www.example.com', { wantGlobalProperties: ["XMLHttpRequest"] });
+ Cu.evalInSandbox('this.xhr = new XMLHttpRequest();', sb);
+ Cu.evalInSandbox('this.jsobj = {mynative: xhr};', sb);
+ Assert.ok(!Cu.isXrayWrapper(XPCNativeWrapper.unwrap(sb.xhr)));
+ Assert.ok(Cu.isXrayWrapper(sb.jsobj.mynative));
+ Assert.ok(!Cu.isXrayWrapper(XPCNativeWrapper.unwrap(sb.jsobj).mynative));
+
+ // Test the new Cu API.
+ var waived = Cu.waiveXrays(sb.xhr);
+ Assert.ok(!Cu.isXrayWrapper(waived));
+ Assert.ok(Cu.isXrayWrapper(Cu.unwaiveXrays(waived)));
+}
diff --git a/js/xpconnect/tests/unit/test_bug872772.js b/js/xpconnect/tests/unit/test_bug872772.js
new file mode 100644
index 0000000000..bfb0d7f4f8
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_bug872772.js
@@ -0,0 +1,33 @@
+function run_test() {
+
+ // Make a content sandbox with an Xrayable object.
+ // NB: We use an nsEP here so that we can have access to Components, but still
+ // have Xray behavior from this scope.
+ var contentSB = new Cu.Sandbox(['http://www.google.com'],
+ { wantGlobalProperties: ["XMLHttpRequest"] });
+
+ // Make an XHR in the content sandbox.
+ Cu.evalInSandbox('xhr = new XMLHttpRequest();', contentSB);
+
+ // Make sure that waivers can be set as Xray expandos.
+ var xhr = contentSB.xhr;
+ Assert.ok(Cu.isXrayWrapper(xhr));
+ xhr.unwaivedExpando = xhr;
+ Assert.ok(Cu.isXrayWrapper(xhr.unwaivedExpando));
+ var waived = xhr.wrappedJSObject;
+ Assert.ok(!Cu.isXrayWrapper(waived));
+ xhr.waivedExpando = waived;
+ Assert.ok(!Cu.isXrayWrapper(xhr.waivedExpando));
+
+ // Try the same thing for getters/setters, even though that's kind of
+ // contrived.
+ Cu.evalInSandbox('function f() {}', contentSB);
+ var f = contentSB.f;
+ var fWaiver = Cu.waiveXrays(f);
+ Assert.ok(f != fWaiver);
+ Assert.ok(Cu.unwaiveXrays(fWaiver) === f);
+ Object.defineProperty(xhr, 'waivedAccessors', {get: fWaiver, set: fWaiver});
+ var desc = Object.getOwnPropertyDescriptor(xhr, 'waivedAccessors');
+ Assert.ok(desc.get === fWaiver);
+ Assert.ok(desc.set === fWaiver);
+}
diff --git a/js/xpconnect/tests/unit/test_bug885800.js b/js/xpconnect/tests/unit/test_bug885800.js
new file mode 100644
index 0000000000..8e00b997b1
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_bug885800.js
@@ -0,0 +1,11 @@
+function run_test() {
+ var sb = new Cu.Sandbox('http://www.example.com');
+ var obj = Cu.evalInSandbox('this.obj = {foo: 2}; obj', sb);
+ var chromeSb = new Cu.Sandbox(this);
+ chromeSb.objRef = obj;
+ Assert.equal(Cu.evalInSandbox('objRef.foo', chromeSb), 2);
+ Cu.nukeSandbox(sb);
+ Assert.ok(Cu.isDeadWrapper(obj));
+ // CCWs to nuked wrappers should be considered dead.
+ Assert.ok(Cu.isDeadWrapper(chromeSb.objRef));
+}
diff --git a/js/xpconnect/tests/unit/test_bug930091.js b/js/xpconnect/tests/unit/test_bug930091.js
new file mode 100644
index 0000000000..ef2b7ae253
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_bug930091.js
@@ -0,0 +1,27 @@
+function checkThrows(fn) {
+ try {
+ fn();
+ ok(false, "Should have thrown");
+ } catch (e) {
+ ok(/denied|insecure|prohibited/.test(e));
+ }
+}
+
+function run_test() {
+ var xosb = new Cu.Sandbox('http://www.example.org');
+ var sb = new Cu.Sandbox('http://www.example.com');
+ sb.ok = ok;
+ sb.fun = function() { ok(false, "Shouldn't ever reach me"); };
+ sb.cow = { foopy: 2 };
+ sb.payload = Cu.evalInSandbox('new Object()', xosb);
+ Cu.evalInSandbox(checkThrows.toSource(), sb);
+ Cu.evalInSandbox('checkThrows(function() { fun(payload); });', sb);
+ Cu.evalInSandbox('checkThrows(function() { Function.prototype.call.call(fun, payload); });', sb);
+ Cu.evalInSandbox('checkThrows(function() { Function.prototype.call.call(fun, null, payload); });', sb);
+ Cu.evalInSandbox('checkThrows(function() { new fun(payload); });', sb);
+ Cu.evalInSandbox('checkThrows(function() { cow.foopy = payload; });', sb);
+ Cu.evalInSandbox('checkThrows(function() { Object.defineProperty(cow, "foopy", { value: payload }); });', sb);
+ // These fail for a different reason, .bind can't access the length/name property on the function.
+ Cu.evalInSandbox('checkThrows(function() { Function.bind.call(fun, null, payload); });', sb);
+ Cu.evalInSandbox('checkThrows(function() { Function.bind.call(fun, payload); });', sb);
+}
diff --git a/js/xpconnect/tests/unit/test_bug976151.js b/js/xpconnect/tests/unit/test_bug976151.js
new file mode 100644
index 0000000000..2e02c3c541
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_bug976151.js
@@ -0,0 +1,23 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+function run_test() {
+ let unprivilegedSb = new Cu.Sandbox('http://www.example.com');
+ function checkOpaqueWrapper(val) {
+ unprivilegedSb.prop = val;
+ try {
+ Cu.evalInSandbox('prop();', sb);
+ } catch (e) {
+ Assert.ok(/denied|insecure|/.test(e));
+ }
+ }
+ let xoSb = new Cu.Sandbox('http://www.example.net');
+ let epSb = new Cu.Sandbox(['http://www.example.com']);
+ checkOpaqueWrapper(eval);
+ checkOpaqueWrapper(xoSb.eval);
+ checkOpaqueWrapper(epSb.eval);
+ checkOpaqueWrapper(Function);
+ checkOpaqueWrapper(xoSb.Function);
+ checkOpaqueWrapper(epSb.Function);
+}
diff --git a/js/xpconnect/tests/unit/test_bug_442086.js b/js/xpconnect/tests/unit/test_bug_442086.js
new file mode 100644
index 0000000000..ad1d8aabaa
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_bug_442086.js
@@ -0,0 +1,36 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// Bug 442086 - XPConnect creates doubles without checking for
+// the INT_FITS_IN_JSVAL case
+
+var types = [
+ 'PRUint8',
+ 'PRUint16',
+ 'PRUint32',
+ 'PRUint64',
+ 'PRInt16',
+ 'PRInt32',
+ 'PRInt64',
+ 'float',
+ 'double'
+];
+
+function run_test()
+{
+ var i;
+ for (i = 0; i < types.length; i++) {
+ var name = types[i];
+ var cls = Cc["@mozilla.org/supports-" + name + ";1"];
+ var ifname = ("nsISupports" + name.charAt(0).toUpperCase() +
+ name.substring(1));
+ var f = cls.createInstance(Ci[ifname]);
+
+ f.data = 0;
+ switch (f.data) {
+ case 0: /*ok*/ break;
+ default: do_throw("FAILED - bug 442086 (type=" + name + ")");
+ }
+ }
+}
diff --git a/js/xpconnect/tests/unit/test_callFunctionWithAsyncStack.js b/js/xpconnect/tests/unit/test_callFunctionWithAsyncStack.js
new file mode 100644
index 0000000000..75c3fa013e
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_callFunctionWithAsyncStack.js
@@ -0,0 +1,28 @@
+function run_test() {
+ if (!Services.prefs.getBoolPref("javascript.options.asyncstack")) {
+ info("Async stacks are disabled.");
+ return;
+ }
+
+ function getAsyncStack() {
+ return Components.stack;
+ }
+
+ // asyncCause may contain non-ASCII characters.
+ let testAsyncCause = "Tes" + String.fromCharCode(355) + "String";
+
+ Cu.callFunctionWithAsyncStack(function asyncCallback() {
+ let stack = Components.stack;
+
+ Assert.equal(stack.name, "asyncCallback");
+ Assert.equal(stack.caller, null);
+ Assert.equal(stack.asyncCause, null);
+
+ Assert.equal(stack.asyncCaller.name, "getAsyncStack");
+ Assert.equal(stack.asyncCaller.asyncCause, testAsyncCause);
+ Assert.equal(stack.asyncCaller.asyncCaller, null);
+
+ Assert.equal(stack.asyncCaller.caller.name, "run_test");
+ Assert.equal(stack.asyncCaller.caller.asyncCause, null);
+ }, getAsyncStack(), testAsyncCause);
+}
diff --git a/js/xpconnect/tests/unit/test_cenums.js b/js/xpconnect/tests/unit/test_cenums.js
new file mode 100644
index 0000000000..6efa8912b1
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_cenums.js
@@ -0,0 +1,58 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+function TestCEnums() {
+}
+
+TestCEnums.prototype = {
+ /* Boilerplate */
+ QueryInterface: ChromeUtils.generateQI(["nsIXPCTestCEnums"]),
+
+ testCEnumInput: function(input) {
+ if (input != Ci.nsIXPCTestCEnums.shouldBe12Explicit)
+ {
+ throw new Error("Enum values do not match expected value");
+ }
+ },
+
+ testCEnumOutput: function() {
+ return Ci.nsIXPCTestCEnums.shouldBe8Explicit;
+ },
+};
+
+
+function run_test() {
+ // Load the component manifests.
+ registerXPCTestComponents();
+
+ // Test for each component.
+ test_interface_consts();
+ test_component(Cc["@mozilla.org/js/xpc/test/native/CEnums;1"].createInstance());
+ test_component(xpcWrap(new TestCEnums()));
+}
+
+function test_interface_consts() {
+ Assert.equal(Ci.nsIXPCTestCEnums.testConst, 1);
+ Assert.equal(Ci.nsIXPCTestCEnums.shouldBe1Explicit, 1);
+ Assert.equal(Ci.nsIXPCTestCEnums.shouldBe2Explicit, 2);
+ Assert.equal(Ci.nsIXPCTestCEnums.shouldBe4Explicit, 4);
+ Assert.equal(Ci.nsIXPCTestCEnums.shouldBe8Explicit, 8);
+ Assert.equal(Ci.nsIXPCTestCEnums.shouldBe12Explicit, 12);
+ Assert.equal(Ci.nsIXPCTestCEnums.shouldBe1Implicit, 1);
+ Assert.equal(Ci.nsIXPCTestCEnums.shouldBe2Implicit, 2);
+ Assert.equal(Ci.nsIXPCTestCEnums.shouldBe3Implicit, 3);
+ Assert.equal(Ci.nsIXPCTestCEnums.shouldBe5Implicit, 5);
+ Assert.equal(Ci.nsIXPCTestCEnums.shouldBe6Implicit, 6);
+ Assert.equal(Ci.nsIXPCTestCEnums.shouldBe2AgainImplicit, 2);
+ Assert.equal(Ci.nsIXPCTestCEnums.shouldBe3AgainImplicit, 3);
+}
+
+function test_component(obj) {
+ var o = obj.QueryInterface(Ci.nsIXPCTestCEnums);
+ o.testCEnumInput(Ci.nsIXPCTestCEnums.shouldBe12Explicit);
+ o.testCEnumInput(Ci.nsIXPCTestCEnums.shouldBe8Explicit | Ci.nsIXPCTestCEnums.shouldBe4Explicit);
+ var a = o.testCEnumOutput();
+ Assert.equal(a, Ci.nsIXPCTestCEnums.shouldBe8Explicit);
+}
+
diff --git a/js/xpconnect/tests/unit/test_compileScript.js b/js/xpconnect/tests/unit/test_compileScript.js
new file mode 100644
index 0000000000..1baf7ab56e
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_compileScript.js
@@ -0,0 +1,99 @@
+"use strict";
+
+const { AddonTestUtils } = ChromeUtils.importESModule(
+ "resource://testing-common/AddonTestUtils.sys.mjs"
+);
+
+AddonTestUtils.init(this);
+
+add_task(async function() {
+ let scriptUrl = Services.io.newFileURI(do_get_file("file_simple_script.js")).spec;
+
+
+ let script1 = await ChromeUtils.compileScript(scriptUrl, {hasReturnValue: true});
+ let script2 = await ChromeUtils.compileScript(scriptUrl, {hasReturnValue: false});
+
+ equal(script1.url, scriptUrl, "Script URL is correct")
+ equal(script2.url, scriptUrl, "Script URL is correct")
+
+ equal(script1.hasReturnValue, true, "Script hasReturnValue property is correct")
+ equal(script2.hasReturnValue, false, "Script hasReturnValue property is correct")
+
+
+ // Test return-value version.
+
+ let sandbox1 = Cu.Sandbox("http://example.com");
+ let sandbox2 = Cu.Sandbox("http://example.org");
+
+ let obj = script1.executeInGlobal(sandbox1);
+ equal(Cu.getObjectPrincipal(obj).origin, "http://example.com", "Return value origin is correct");
+ equal(obj.foo, "\u00ae", "Return value has the correct charset");
+
+ obj = script1.executeInGlobal(sandbox2);
+ equal(Cu.getObjectPrincipal(obj).origin, "http://example.org", "Return value origin is correct");
+ equal(obj.foo, "\u00ae", "Return value has the correct charset");
+
+
+ // Test no-return-value version.
+
+ sandbox1.bar = null;
+ equal(sandbox1.bar, null);
+
+ obj = script2.executeInGlobal(sandbox1);
+ equal(obj, undefined, "No-return script has no return value");
+
+ equal(Cu.getObjectPrincipal(sandbox1.bar).origin, "http://example.com", "Object value origin is correct");
+ equal(sandbox1.bar.foo, "\u00ae", "Object value has the correct charset");
+
+
+ sandbox2.bar = null;
+ equal(sandbox2.bar, null);
+
+ obj = script2.executeInGlobal(sandbox2);
+ equal(obj, undefined, "No-return script has no return value");
+
+ equal(Cu.getObjectPrincipal(sandbox2.bar).origin, "http://example.org", "Object value origin is correct");
+ equal(sandbox2.bar.foo, "\u00ae", "Object value has the correct charset");
+});
+
+add_task(async function test_syntaxError() {
+ // Generate an artificially large script to force off-main-thread
+ // compilation.
+ let scriptUrl = `data:,${";".repeat(1024 * 1024)}(`;
+
+ await Assert.rejects(
+ ChromeUtils.compileScript(scriptUrl),
+ SyntaxError);
+
+ // Generate a small script to force main thread compilation.
+ scriptUrl = `data:,;(`;
+
+ await Assert.rejects(
+ ChromeUtils.compileScript(scriptUrl),
+ SyntaxError);
+});
+
+/**
+ * Assert that executeInGlobal throws a special exception when the content script throws.
+ * And the content script exception is notified to the console.
+ */
+add_task(async function test_exceptions_in_webconsole() {
+ const scriptUrl = `data:,throw new Error("foo")`;
+ const script = await ChromeUtils.compileScript(scriptUrl);
+ const sandbox = Cu.Sandbox("http://example.com");
+
+ Assert.throws(() => script.executeInGlobal(sandbox),
+ /Error: foo/,
+ "Without reportException set to true, executeInGlobal throws an exception");
+
+ info("With reportException, executeInGlobal doesn't throw, but notifies the console");
+ const { messages } = await AddonTestUtils.promiseConsoleOutput(() => {
+ script.executeInGlobal(sandbox, { reportExceptions: true });
+ });
+
+ info("Wait for the console message related to the content script exception");
+ equal(messages.length, 1, "Got one console message");
+ messages[0].QueryInterface(Ci.nsIScriptError);
+ equal(messages[0].errorMessage, "Error: foo", "We are notified about the plain content script exception via the console");
+ ok(messages[0].stack, "The message has a stack");
+});
diff --git a/js/xpconnect/tests/unit/test_components.js b/js/xpconnect/tests/unit/test_components.js
new file mode 100644
index 0000000000..e019b78f8f
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_components.js
@@ -0,0 +1,24 @@
+function run_test() {
+ var sb1 = Cu.Sandbox("http://www.blah.com");
+ var sb2 = Cu.Sandbox(this);
+ var rv;
+
+ // non-chrome accessing chrome Components
+ sb1.C = Components;
+ checkThrows("C.interfaces", sb1);
+ checkThrows("C.utils", sb1);
+ checkThrows("C.classes", sb1);
+
+ // non-chrome accessing own Components: shouldn't exist.
+ Assert.equal(Cu.evalInSandbox("typeof Components", sb1), 'undefined');
+
+ // chrome accessing chrome
+ sb2.C = Components;
+ rv = Cu.evalInSandbox("C.utils", sb2);
+ Assert.equal(rv, Cu);
+}
+
+function checkThrows(expression, sb) {
+ var result = Cu.evalInSandbox('(function() { try { ' + expression + '; return "allowed"; } catch (e) { return e.toString(); }})();', sb);
+ Assert.ok(!!/denied/.exec(result));
+}
diff --git a/js/xpconnect/tests/unit/test_crypto.js b/js/xpconnect/tests/unit/test_crypto.js
new file mode 100644
index 0000000000..228701d182
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_crypto.js
@@ -0,0 +1,28 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+function run_test() {
+ let sb = new Cu.Sandbox('https://www.example.com',
+ { wantGlobalProperties:
+ ["crypto", "TextEncoder", "TextDecoder", "isSecureContext"],
+ forceSecureContext: true,
+ });
+ sb.ok = ok;
+ Cu.evalInSandbox('ok(this.crypto);', sb);
+ Cu.evalInSandbox('ok(this.crypto.subtle);', sb);
+ sb.equal = equal;
+ let innerPromise = new Promise(r => (sb.test_done = r));
+ Cu.evalInSandbox('crypto.subtle.digest("SHA-256", ' +
+ ' new TextEncoder().encode("abc"))' +
+ ' .then(h => equal(new Uint16Array(h)[0], 30906))' +
+ ' .then(test_done);', sb);
+
+ Cu.importGlobalProperties(["crypto"]);
+ ok(crypto);
+ ok(crypto.subtle);
+ let outerPromise = crypto.subtle.digest("SHA-256", new TextEncoder().encode("abc"))
+ .then(h => Assert.equal(new Uint16Array(h)[0], 30906));
+
+ do_test_pending();
+ Promise.all([innerPromise, outerPromise]).then(() => do_test_finished());
+}
diff --git a/js/xpconnect/tests/unit/test_css.js b/js/xpconnect/tests/unit/test_css.js
new file mode 100644
index 0000000000..e6635d5293
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_css.js
@@ -0,0 +1,9 @@
+function run_test() {
+ var sb = new Cu.Sandbox('http://www.example.com',
+ { wantGlobalProperties: ["CSS"] });
+ sb.equal = equal;
+ Cu.evalInSandbox('equal(CSS.escape("$"), "\\\\$");',
+ sb);
+ Cu.importGlobalProperties(["CSS"]);
+ Assert.equal(CSS.escape("$"), "\\$");
+}
diff --git a/js/xpconnect/tests/unit/test_deepFreezeClone.js b/js/xpconnect/tests/unit/test_deepFreezeClone.js
new file mode 100644
index 0000000000..949b85f551
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_deepFreezeClone.js
@@ -0,0 +1,31 @@
+function checkThrows(f, rgxp) { try { f(); do_check_false(); } catch (e) { Assert.ok(rgxp.test(e)); } }
+
+var o = { foo: 42, bar : { tick: 'tock' } };
+function checkClone(clone, frozen) {
+ var waived = Cu.waiveXrays(clone);
+ function touchFoo() { "use strict"; waived.foo = 12; Assert.equal(waived.foo, 12); }
+ function touchBar() { "use strict"; waived.bar.tick = 'tack'; Assert.equal(waived.bar.tick, 'tack'); }
+ function addProp() { "use strict"; waived.newProp = 100; Assert.equal(waived.newProp, 100); }
+ if (!frozen) {
+ touchFoo();
+ touchBar();
+ addProp();
+ } else {
+ checkThrows(touchFoo, /read-only/);
+ checkThrows(touchBar, /read-only/);
+ checkThrows(addProp, /extensible/);
+ }
+
+ var desc = Object.getOwnPropertyDescriptor(waived, 'foo');
+ Assert.equal(desc.writable, !frozen);
+ Assert.equal(desc.configurable, !frozen);
+ desc = Object.getOwnPropertyDescriptor(waived.bar, 'tick');
+ Assert.equal(desc.writable, !frozen);
+ Assert.equal(desc.configurable, !frozen);
+}
+
+function run_test() {
+ var sb = new Cu.Sandbox(null);
+ checkClone(Cu.waiveXrays(Cu.cloneInto(o, sb)), false);
+ checkClone(Cu.cloneInto(o, sb, { deepFreeze: true }), true);
+}
diff --git a/js/xpconnect/tests/unit/test_defineESModuleGetters.js b/js/xpconnect/tests/unit/test_defineESModuleGetters.js
new file mode 100644
index 0000000000..f7f12de1d9
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_defineESModuleGetters.js
@@ -0,0 +1,76 @@
+function assertAccessor(lazy, name) {
+ let desc = Object.getOwnPropertyDescriptor(lazy, name);
+ Assert.equal(typeof desc.get, "function");
+ Assert.equal(desc.get.name, name);
+ Assert.equal(typeof desc.set, "function");
+ Assert.equal(desc.set.name, name);
+ Assert.equal(desc.enumerable, true);
+ Assert.equal(desc.configurable, true);
+}
+
+function assertDataProperty(lazy, name, value) {
+ let desc = Object.getOwnPropertyDescriptor(lazy, name);
+ Assert.equal(desc.value, value);
+ Assert.equal(desc.writable, true);
+ Assert.equal(desc.enumerable, true);
+ Assert.equal(desc.configurable, true);
+}
+
+add_task(function test_getter() {
+ // The property should be defined as getter, and getting it should make it
+ // a data property.
+
+ const lazy = {};
+ ChromeUtils.defineESModuleGetters(lazy, {
+ X: "resource://test/esm_lazy-1.sys.mjs",
+ });
+
+ assertAccessor(lazy, "X");
+
+ Assert.equal(lazy.X, 10);
+ assertDataProperty(lazy, "X", 10);
+});
+
+add_task(function test_setter() {
+ // Setting the value before the first get should result in a data property.
+ const lazy = {};
+ ChromeUtils.defineESModuleGetters(lazy, {
+ X: "resource://test/esm_lazy-1.sys.mjs",
+ });
+
+ assertAccessor(lazy, "X");
+ lazy.X = 20;
+ Assert.equal(lazy.X, 20);
+ assertDataProperty(lazy, "X", 20);
+
+ // The above set shouldn't affect the module's value.
+ const lazy2 = {};
+ ChromeUtils.defineESModuleGetters(lazy2, {
+ X: "resource://test/esm_lazy-1.sys.mjs",
+ });
+
+ Assert.equal(lazy2.X, 10);
+});
+
+add_task(function test_order() {
+ // The change to the exported value should be reflected until it's accessed.
+
+ const lazy = {};
+ ChromeUtils.defineESModuleGetters(lazy, {
+ Y: "resource://test/esm_lazy-2.sys.mjs",
+ AddY: "resource://test/esm_lazy-2.sys.mjs",
+ });
+
+ assertAccessor(lazy, "Y");
+ assertAccessor(lazy, "AddY");
+
+ // The change before getting the value should be reflected.
+ lazy.AddY(2);
+ Assert.equal(lazy.Y, 22);
+ assertDataProperty(lazy, "Y", 22);
+
+ // Change after getting the value shouldn't be reflected.
+ lazy.AddY(2);
+ Assert.equal(lazy.Y, 22);
+ assertDataProperty(lazy, "Y", 22);
+});
diff --git a/js/xpconnect/tests/unit/test_defineESModuleGetters_options.js b/js/xpconnect/tests/unit/test_defineESModuleGetters_options.js
new file mode 100644
index 0000000000..11d282e511
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_defineESModuleGetters_options.js
@@ -0,0 +1,105 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+add_task(async function testShared() {
+ const lazy1 = {};
+ const lazy2 = {};
+
+ ChromeUtils.defineESModuleGetters(lazy1, {
+ GetX: "resource://test/esm_lazy-1.sys.mjs",
+ });
+
+ ChromeUtils.defineESModuleGetters(lazy2, {
+ GetX: "resource://test/esm_lazy-1.sys.mjs",
+ }, {
+ global: "shared",
+ });
+
+ Assert.equal(lazy1.GetX, lazy2.GetX);
+
+ const ns = ChromeUtils.importESModule("resource://test/esm_lazy-1.sys.mjs");
+
+ Assert.equal(ns.GetX, lazy1.GetX);
+ Assert.equal(ns.GetX, lazy2.GetX);
+});
+
+add_task(async function testDevTools() {
+ const lazy1 = {};
+ const lazy2 = {};
+
+ ChromeUtils.defineESModuleGetters(lazy1, {
+ GetX: "resource://test/esm_lazy-1.sys.mjs",
+ }, {
+ loadInDevToolsLoader: true,
+ });
+
+ ChromeUtils.defineESModuleGetters(lazy2, {
+ GetX: "resource://test/esm_lazy-1.sys.mjs",
+ }, {
+ global: "devtools",
+ });
+
+ Assert.equal(lazy1.GetX, lazy2.GetX);
+
+ const ns = ChromeUtils.importESModule("resource://test/esm_lazy-1.sys.mjs", {
+ loadInDevToolsLoader: true,
+ });
+
+ Assert.equal(ns.GetX, lazy1.GetX);
+ Assert.equal(ns.GetX, lazy2.GetX);
+});
+
+add_task(async function testSandbox() {
+ const uri = "http://example.com/";
+ const window = createContentWindow(uri);
+ const sandboxOpts = {
+ sandboxPrototype: window,
+ wantGlobalProperties: ["ChromeUtils"],
+ };
+ const sb = new Cu.Sandbox(uri, sandboxOpts);
+
+ const result = Cu.evalInSandbox(`
+ const lazy = {};
+
+ ChromeUtils.defineESModuleGetters(lazy, {
+ GetX: "resource://test/esm_lazy-1.sys.mjs",
+ }, {
+ global: "current",
+ });
+
+ lazy.GetX; // delazify before import.
+
+ const ns = ChromeUtils.importESModule("resource://test/esm_lazy-1.sys.mjs", {
+ global: "current",
+ });
+
+ ns.GetX == lazy.GetX;
+`, sb);
+
+ Assert.ok(result);
+});
+
+add_task(async function testWindow() {
+ const win1 = createChromeWindow();
+
+ const result = win1.eval(`
+ const lazy = {};
+
+ ChromeUtils.defineESModuleGetters(lazy, {
+ GetX: "resource://test/esm_lazy-1.sys.mjs",
+ }, {
+ global: "current",
+ });
+
+ lazy.GetX; // delazify before import.
+
+ const ns = ChromeUtils.importESModule("resource://test/esm_lazy-1.sys.mjs", {
+ global: "current",
+ });
+
+ ns.GetX == lazy.GetX;
+`);
+
+ Assert.ok(result);
+});
diff --git a/js/xpconnect/tests/unit/test_defineESModuleGetters_options_worker.js b/js/xpconnect/tests/unit/test_defineESModuleGetters_options_worker.js
new file mode 100644
index 0000000000..f1eab22d2b
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_defineESModuleGetters_options_worker.js
@@ -0,0 +1,33 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+add_task(async function testWorker() {
+ let worker = new ChromeWorker("resource://test/lazy_non_shared_in_worker.js");
+ let { promise, resolve } = Promise.withResolvers();
+ worker.onmessage = event => {
+ resolve(event.data);
+ };
+ worker.postMessage("");
+
+ const result = await promise;
+
+ Assert.ok(result.equal1);
+ Assert.ok(result.equal2);
+});
+
+add_task(async function testSharedInWorker() {
+ let worker = new ChromeWorker("resource://test/lazy_shared_in_worker.js");
+ let { promise, resolve } = Promise.withResolvers();
+ worker.onmessage = event => {
+ resolve(event.data);
+ };
+ worker.postMessage("");
+
+ const result = await promise;
+
+ Assert.equal(result.caught1, true);
+ Assert.equal(result.caught2, true);
+ Assert.equal(result.caught3, true);
+ Assert.equal(result.caught4, true);
+});
diff --git a/js/xpconnect/tests/unit/test_defineModuleGetter.js b/js/xpconnect/tests/unit/test_defineModuleGetter.js
new file mode 100644
index 0000000000..a3a24874d0
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_defineModuleGetter.js
@@ -0,0 +1,115 @@
+"use strict";
+
+function assertIsGetter(obj, prop) {
+ let desc = Object.getOwnPropertyDescriptor(obj, prop);
+
+ ok(desc, `Property ${prop} exists on object`);
+ equal(typeof desc.get, "function", `Getter function exists for property ${prop}`);
+ equal(typeof desc.set, "function", `Setter function exists for property ${prop}`);
+ equal(desc.enumerable, true, `Property ${prop} is enumerable`);
+ equal(desc.configurable, true, `Property ${prop} is configurable`);
+}
+
+function assertIsValue(obj, prop, value) {
+ let desc = Object.getOwnPropertyDescriptor(obj, prop);
+
+ ok(desc, `Property ${prop} exists on object`);
+
+ ok("value" in desc, `${prop} is a data property`);
+ equal(desc.value, value, `${prop} has the expected value`);
+
+ equal(desc.enumerable, true, `Property ${prop} is enumerable`);
+ equal(desc.configurable, true, `Property ${prop} is configurable`);
+ equal(desc.writable, true, `Property ${prop} is writable`);
+}
+
+add_task(async function() {
+ let temp = {};
+ ChromeUtils.import("resource://test/TestFile.jsm", temp);
+
+ let obj = {};
+ let child = Object.create(obj);
+ let sealed = Object.seal(Object.create(obj));
+
+
+ // Test valid import
+
+ ChromeUtils.defineModuleGetter(obj, "TestFile",
+ "resource://test/TestFile.jsm");
+
+ assertIsGetter(obj, "TestFile");
+ equal(child.TestFile, temp.TestFile, "Getter works on descendent object");
+ assertIsValue(child, "TestFile", temp.TestFile);
+ assertIsGetter(obj, "TestFile");
+
+ Assert.throws(() => sealed.TestFile, /Object is not extensible/,
+ "Cannot access lazy getter from sealed object");
+ Assert.throws(() => sealed.TestFile = null, /Object is not extensible/,
+ "Cannot access lazy setter from sealed object");
+ assertIsGetter(obj, "TestFile");
+
+ equal(obj.TestFile, temp.TestFile, "Getter works on object");
+ assertIsValue(obj, "TestFile", temp.TestFile);
+
+
+ // Test overwriting via setter
+
+ child = Object.create(obj);
+
+ ChromeUtils.defineModuleGetter(obj, "TestFile",
+ "resource://test/TestFile.jsm");
+
+ assertIsGetter(obj, "TestFile");
+
+ child.TestFile = "foo";
+ assertIsValue(child, "TestFile", "foo");
+ assertIsGetter(obj, "TestFile");
+
+ obj.TestFile = "foo";
+ assertIsValue(obj, "TestFile", "foo");
+
+
+ // Test import missing property
+
+ ChromeUtils.defineModuleGetter(obj, "meh",
+ "resource://test/TestFile.jsm");
+ assertIsGetter(obj, "meh");
+ equal(obj.meh, undefined, "Missing property returns undefined");
+ assertIsValue(obj, "meh", undefined);
+
+
+ // Test import broken module
+
+ ChromeUtils.defineModuleGetter(obj, "broken",
+ "resource://test/bogus_exports_type.jsm");
+ assertIsGetter(obj, "broken");
+
+ let errorPattern = /EXPORTED_SYMBOLS is not an array/;
+ Assert.throws(() => child.broken, errorPattern,
+ "Broken import throws on child");
+ Assert.throws(() => child.broken, errorPattern,
+ "Broken import throws on child again");
+ Assert.throws(() => sealed.broken, errorPattern,
+ "Broken import throws on sealed child");
+ Assert.throws(() => obj.broken, errorPattern,
+ "Broken import throws on object");
+ assertIsGetter(obj, "broken");
+
+
+ // Test import missing module
+
+ ChromeUtils.defineModuleGetter(obj, "missing",
+ "resource://test/does_not_exist.jsm");
+ assertIsGetter(obj, "missing");
+
+ Assert.throws(() => obj.missing, /NS_ERROR_FILE_NOT_FOUND/,
+ "missing import throws on object");
+ assertIsGetter(obj, "missing");
+
+
+ // Test overwriting broken import via setter
+
+ assertIsGetter(obj, "broken");
+ obj.broken = "foo";
+ assertIsValue(obj, "broken", "foo");
+});
diff --git a/js/xpconnect/tests/unit/test_envChain_JSM.js b/js/xpconnect/tests/unit/test_envChain_JSM.js
new file mode 100644
index 0000000000..0c5b2e1b80
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_envChain_JSM.js
@@ -0,0 +1,40 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+// Verify the environment chain for JSM described in
+// js/src/vm/EnvironmentObject.h.
+
+add_task(async function() {
+ const { envs } = ChromeUtils.import("resource://test/envChain.jsm");
+
+ Assert.equal(envs.length, 4);
+
+ let i = 0, env;
+
+ env = envs[i]; i++;
+ Assert.equal(env.type, "NonSyntacticLexicalEnvironmentObject");
+ Assert.equal(env.qualified, false);
+ Assert.equal(env.lexical, true, "lexical must live in the NSLEO");
+ Assert.equal(env.prop, false);
+
+ env = envs[i]; i++;
+ Assert.equal(env.type, "NonSyntacticVariablesObject");
+ Assert.equal(env.qualified, true, "qualified var must live in the NSVO");
+ Assert.equal(env.lexical, false);
+ Assert.equal(env.prop, true, "prop var must live in the NSVO");
+
+ env = envs[i]; i++;
+ Assert.equal(env.type, "GlobalLexicalEnvironmentObject");
+ Assert.equal(env.qualified, false);
+ Assert.equal(env.lexical, false);
+ Assert.equal(env.prop, false);
+
+ env = envs[i]; i++;
+ Assert.equal(env.type, "*BackstagePass*");
+ Assert.equal(env.qualified, false);
+ Assert.equal(env.lexical, false);
+ Assert.equal(env.prop, false);
+});
diff --git a/js/xpconnect/tests/unit/test_envChain_frameScript.js b/js/xpconnect/tests/unit/test_envChain_frameScript.js
new file mode 100644
index 0000000000..2d877d822f
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_envChain_frameScript.js
@@ -0,0 +1,211 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+// Verify the environment chain for frame scripts described in
+// js/src/vm/EnvironmentObject.h.
+
+const { XPCShellContentUtils } = ChromeUtils.importESModule(
+ "resource://testing-common/XPCShellContentUtils.sys.mjs"
+);
+
+XPCShellContentUtils.init(this);
+
+add_task(async function unique_scope() {
+ const page = await XPCShellContentUtils.loadContentPage("about:blank", {
+ remote: true,
+ });
+
+ const envsPromise = new Promise(resolve => {
+ Services.mm.addMessageListener("unique-envs-result", msg => {
+ resolve(msg.data);
+ });
+ });
+ const sharePromise = new Promise(resolve => {
+ Services.mm.addMessageListener("unique-share-result", msg => {
+ resolve(msg.data);
+ });
+ });
+
+ const runInUniqueScope = true;
+ const runInGlobalScope = !runInUniqueScope;
+
+ Services.mm.loadFrameScript(`data:,
+var unique_qualified = 10;
+unique_unqualified = 20;
+let unique_lexical = 30;
+this.unique_prop = 40;
+
+const funcs = Cu.getJSTestingFunctions();
+const envs = [];
+let env = funcs.getInnerMostEnvironmentObject();
+while (env) {
+ envs.push({
+ type: funcs.getEnvironmentObjectType(env) || "*BackstagePass*",
+ qualified: !!Object.getOwnPropertyDescriptor(env, "unique_qualified"),
+ unqualified: !!Object.getOwnPropertyDescriptor(env, "unique_unqualified"),
+ lexical: !!Object.getOwnPropertyDescriptor(env, "unique_lexical"),
+ prop: !!Object.getOwnPropertyDescriptor(env, "unique_prop"),
+ });
+
+ env = funcs.getEnclosingEnvironmentObject(env);
+}
+
+sendSyncMessage("unique-envs-result", envs);
+`, false, runInGlobalScope);
+
+ Services.mm.loadFrameScript(`data:,
+sendSyncMessage("unique-share-result", {
+ unique_qualified: typeof unique_qualified,
+ unique_unqualified: typeof unique_unqualified,
+ unique_lexical: typeof unique_lexical,
+ unique_prop: this.unique_prop,
+});
+`, false, runInGlobalScope);
+
+ const envs = await envsPromise;
+ const share = await sharePromise;
+
+ Assert.equal(envs.length, 5);
+
+ let i = 0, env;
+
+ env = envs[i]; i++;
+ Assert.equal(env.type, "NonSyntacticLexicalEnvironmentObject");
+ Assert.equal(env.qualified, false);
+ Assert.equal(env.unqualified, false);
+ Assert.equal(env.lexical, true, "lexical must live in the NSLEO");
+ Assert.equal(env.prop, false);
+
+ env = envs[i]; i++;
+ Assert.equal(env.type, "WithEnvironmentObject");
+ Assert.equal(env.qualified, false);
+ Assert.equal(env.unqualified, false);
+ Assert.equal(env.lexical, false);
+ Assert.equal(env.prop, true, "this property must live in the with env");
+
+ env = envs[i]; i++;
+ Assert.equal(env.type, "NonSyntacticVariablesObject");
+ Assert.equal(env.qualified, true, "qualified var must live in the NSVO");
+ Assert.equal(env.unqualified, true, "unqualified var must live in the NSVO");
+ Assert.equal(env.lexical, false);
+ Assert.equal(env.prop, false);
+
+ env = envs[i]; i++;
+ Assert.equal(env.type, "GlobalLexicalEnvironmentObject");
+ Assert.equal(env.qualified, false);
+ Assert.equal(env.unqualified, false);
+ Assert.equal(env.lexical, false);
+ Assert.equal(env.prop, false);
+
+ env = envs[i]; i++;
+ Assert.equal(env.type, "*BackstagePass*");
+ Assert.equal(env.qualified, false);
+ Assert.equal(env.unqualified, false);
+ Assert.equal(env.lexical, false);
+ Assert.equal(env.prop, false);
+
+ Assert.equal(share.unique_qualified, "undefined", "qualified var must not be shared");
+ Assert.equal(share.unique_unqualified, "undefined", "unqualified name must not be shared");
+ Assert.equal(share.unique_lexical, "undefined", "lexical must not be shared");
+ Assert.equal(share.unique_prop, 40, "this property must be shared");
+
+ await page.close();
+});
+
+add_task(async function non_unique_scope() {
+ const page = await XPCShellContentUtils.loadContentPage("about:blank", {
+ remote: true,
+ });
+
+ const envsPromise = new Promise(resolve => {
+ Services.mm.addMessageListener("non-unique-envs-result", msg => {
+ resolve(msg.data);
+ });
+ });
+ const sharePromise = new Promise(resolve => {
+ Services.mm.addMessageListener("non-unique-share-result", msg => {
+ resolve(msg.data);
+ });
+ });
+
+ const runInUniqueScope = false;
+ const runInGlobalScope = !runInUniqueScope;
+
+ Services.mm.loadFrameScript(`data:,
+var non_unique_qualified = 10;
+non_unique_unqualified = 20;
+let non_unique_lexical = 30;
+this.non_unique_prop = 40;
+
+const funcs = Cu.getJSTestingFunctions();
+const envs = [];
+let env = funcs.getInnerMostEnvironmentObject();
+while (env) {
+ envs.push({
+ type: funcs.getEnvironmentObjectType(env) || "*BackstagePass*",
+ qualified: !!Object.getOwnPropertyDescriptor(env, "non_unique_qualified"),
+ unqualified: !!Object.getOwnPropertyDescriptor(env, "non_unique_unqualified"),
+ lexical: !!Object.getOwnPropertyDescriptor(env, "non_unique_lexical"),
+ prop: !!Object.getOwnPropertyDescriptor(env, "non_unique_prop"),
+ });
+
+ env = funcs.getEnclosingEnvironmentObject(env);
+}
+
+sendSyncMessage("non-unique-envs-result", envs);
+`, false, runInGlobalScope);
+
+ Services.mm.loadFrameScript(`data:,
+sendSyncMessage("non-unique-share-result", {
+ non_unique_qualified,
+ non_unique_unqualified,
+ non_unique_lexical,
+ non_unique_prop,
+});
+`, false, runInGlobalScope);
+
+ const envs = await envsPromise;
+ const share = await sharePromise;
+
+ Assert.equal(envs.length, 4);
+
+ let i = 0, env;
+
+ env = envs[i]; i++;
+ Assert.equal(env.type, "NonSyntacticLexicalEnvironmentObject");
+ Assert.equal(env.qualified, false);
+ Assert.equal(env.unqualified, false);
+ Assert.equal(env.lexical, true, "lexical must live in the NSLEO");
+ Assert.equal(env.prop, false);
+
+ env = envs[i]; i++;
+ Assert.equal(env.type, "WithEnvironmentObject");
+ Assert.equal(env.qualified, true, "qualified var must live in the with env");
+ Assert.equal(env.unqualified, false);
+ Assert.equal(env.lexical, false);
+ Assert.equal(env.prop, true, "this property must live in the with env");
+
+ env = envs[i]; i++;
+ Assert.equal(env.type, "GlobalLexicalEnvironmentObject");
+ Assert.equal(env.qualified, false);
+ Assert.equal(env.unqualified, false);
+ Assert.equal(env.lexical, false);
+ Assert.equal(env.prop, false);
+
+ env = envs[i]; i++;
+ Assert.equal(env.type, "*BackstagePass*");
+ Assert.equal(env.qualified, false);
+ Assert.equal(env.unqualified, true, "unqualified name must live in the backstage pass");
+ Assert.equal(env.lexical, false);
+ Assert.equal(env.prop, false);
+
+ Assert.equal(share.non_unique_qualified, 10, "qualified var must be shared");
+ Assert.equal(share.non_unique_unqualified, 20, "unqualified name must be shared");
+ Assert.equal(share.non_unique_lexical, 30, "lexical must be shared");
+ Assert.equal(share.non_unique_prop, 40, "this property must be shared");
+
+ await page.close();
+});
diff --git a/js/xpconnect/tests/unit/test_envChain_subscript.js b/js/xpconnect/tests/unit/test_envChain_subscript.js
new file mode 100644
index 0000000000..e7774d0d0d
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_envChain_subscript.js
@@ -0,0 +1,72 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+// Verify the environment chain for subscripts described in
+// js/src/vm/EnvironmentObject.h.
+
+add_task(async function() {
+ const target = {};
+ Services.scriptloader.loadSubScript(`data:,
+var qualified = 10;
+unqualified = 20;
+let lexical = 30;
+this.prop = 40;
+
+const funcs = Cu.getJSTestingFunctions();
+const envs = [];
+let env = funcs.getInnerMostEnvironmentObject();
+while (env) {
+ envs.push({
+ type: funcs.getEnvironmentObjectType(env) || "*global*",
+ qualified: !!Object.getOwnPropertyDescriptor(env, "qualified"),
+ unqualified: !!Object.getOwnPropertyDescriptor(env, "unqualified"),
+ lexical: !!Object.getOwnPropertyDescriptor(env, "lexical"),
+ prop: !!Object.getOwnPropertyDescriptor(env, "prop"),
+ });
+
+ env = funcs.getEnclosingEnvironmentObject(env);
+}
+
+this.ENVS = envs;
+`, target);
+
+ const envs = target.ENVS;
+
+ Assert.equal(envs.length, 4);
+
+ let i = 0, env;
+
+ env = envs[i]; i++;
+ Assert.equal(env.type, "NonSyntacticLexicalEnvironmentObject");
+ Assert.equal(env.qualified, false);
+ Assert.equal(env.unqualified, false);
+ Assert.equal(env.lexical, true, "lexical must live in the NSLEO");
+ Assert.equal(env.prop, false);
+
+ env = envs[i]; i++;
+ Assert.equal(env.type, "WithEnvironmentObject");
+ Assert.equal(env.qualified, true, "qualified var must live in the with env");
+ Assert.equal(env.unqualified, false);
+ Assert.equal(env.lexical, false);
+ Assert.equal(env.prop, true, "this property must live in the with env");
+
+ env = envs[i]; i++;
+ Assert.equal(env.type, "GlobalLexicalEnvironmentObject");
+ Assert.equal(env.qualified, false);
+ Assert.equal(env.unqualified, false);
+ Assert.equal(env.lexical, false);
+ Assert.equal(env.prop, false);
+
+ env = envs[i]; i++;
+ Assert.equal(env.type, "*global*");
+ Assert.equal(env.qualified, false);
+ Assert.equal(env.unqualified, true, "unqualified var must live in the global");
+ Assert.equal(env.lexical, false);
+ Assert.equal(env.prop, false);
+
+ Assert.equal(target.qualified, 10, "qualified var must be reflected to the target object");
+ Assert.equal(target.prop, 40, "this property must be reflected to the target object");
+});
diff --git a/js/xpconnect/tests/unit/test_envChain_subscript_in_JSM.js b/js/xpconnect/tests/unit/test_envChain_subscript_in_JSM.js
new file mode 100644
index 0000000000..a95806177e
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_envChain_subscript_in_JSM.js
@@ -0,0 +1,58 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+// Verify the environment chain for subscripts in JSM described in
+// js/src/vm/EnvironmentObject.h.
+
+add_task(async function() {
+ const { envs } = ChromeUtils.import("resource://test/envChain_subscript.jsm");
+
+ Assert.equal(envs.length, 6);
+
+ let i = 0, env;
+
+ env = envs[i]; i++;
+ Assert.equal(env.type, "NonSyntacticLexicalEnvironmentObject");
+ Assert.equal(env.qualified, false);
+ Assert.equal(env.unqualified, false);
+ Assert.equal(env.lexical, true, "lexical must live in the NSLEO");
+ Assert.equal(env.prop, false);
+
+ env = envs[i]; i++;
+ Assert.equal(env.type, "WithEnvironmentObject");
+ Assert.equal(env.qualified, true, "qualified var must live in the with env");
+ Assert.equal(env.unqualified, false);
+ Assert.equal(env.lexical, false);
+ Assert.equal(env.prop, true, "this property must live in the with env");
+
+ env = envs[i]; i++;
+ Assert.equal(env.type, "NonSyntacticLexicalEnvironmentObject");
+ Assert.equal(env.qualified, false);
+ Assert.equal(env.unqualified, false);
+ Assert.equal(env.lexical, false);
+ Assert.equal(env.prop, false);
+
+ env = envs[i]; i++;
+ Assert.equal(env.type, "NonSyntacticVariablesObject");
+ Assert.equal(env.qualified, false);
+ Assert.equal(env.unqualified, true, "unqualified var must live in the global");
+ Assert.equal(env.lexical, false);
+ Assert.equal(env.prop, false);
+
+ env = envs[i]; i++;
+ Assert.equal(env.type, "GlobalLexicalEnvironmentObject");
+ Assert.equal(env.qualified, false);
+ Assert.equal(env.unqualified, false);
+ Assert.equal(env.lexical, false);
+ Assert.equal(env.prop, false);
+
+ env = envs[i]; i++;
+ Assert.equal(env.type, "*BackstagePass*");
+ Assert.equal(env.qualified, false);
+ Assert.equal(env.unqualified, false);
+ Assert.equal(env.lexical, false);
+ Assert.equal(env.prop, false);
+});
diff --git a/js/xpconnect/tests/unit/test_error_to_exception.js b/js/xpconnect/tests/unit/test_error_to_exception.js
new file mode 100644
index 0000000000..1330ecd0ae
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_error_to_exception.js
@@ -0,0 +1,58 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+add_task(async function() {
+ // Throwing an error inside a JS callback in xpconnect should preserve
+ // the columnNumber.
+
+ const tests = [
+ // Parser error.
+ {
+ throwError() {
+ eval("a b");
+ },
+ messagePattern: /unexpected token/,
+ lineNumber: 1,
+ columnNumber: 3,
+ },
+ // Runtime error.
+ {
+ throwError() { // line = 21
+ not_found();
+ },
+ messagePattern: /is not defined/,
+ lineNumber: 22,
+ columnNumber: 9,
+ },
+ ];
+
+ for (const test of tests) {
+ const { promise, resolve } = Promise.withResolvers();
+ const listener = {
+ observe(msg) {
+ if (msg instanceof Ci.nsIScriptError) {
+ resolve(msg);
+ }
+ }
+ };
+
+ try {
+ Services.console.registerListener(listener);
+
+ try {
+ const obs = Cc["@mozilla.org/observer-service;1"]
+ .getService(Ci.nsIObserverService);
+ obs.addObserver(test.throwError, "test-obs", false);
+ obs.notifyObservers(null, "test-obs");
+ } catch {}
+
+ const msg = await promise;
+ Assert.stringMatches(msg.errorMessage, test.messagePattern);
+ Assert.equal(msg.lineNumber, test.lineNumber);
+ Assert.equal(msg.columnNumber, test.columnNumber);
+ } finally {
+ Services.console.unregisterListener(listener);
+ }
+ }
+});
diff --git a/js/xpconnect/tests/unit/test_eventSource.js b/js/xpconnect/tests/unit/test_eventSource.js
new file mode 100644
index 0000000000..8983d852bd
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_eventSource.js
@@ -0,0 +1,6 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+Assert.throws(() => new EventSource('a'),
+ /NS_ERROR_FAILURE/,
+ "This should fail, but not crash, in xpcshell");
diff --git a/js/xpconnect/tests/unit/test_exportFunction.js b/js/xpconnect/tests/unit/test_exportFunction.js
new file mode 100644
index 0000000000..a7b2ca8056
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_exportFunction.js
@@ -0,0 +1,152 @@
+function run_test() {
+ var epsb = new Cu.Sandbox(["http://example.com", "http://example.org"], { wantExportHelpers: true });
+ var subsb = new Cu.Sandbox("http://example.com", { wantGlobalProperties: ["XMLHttpRequest"] });
+ var subsb2 = new Cu.Sandbox("http://example.com", { wantGlobalProperties: ["XMLHttpRequest"] });
+ var xorigsb = new Cu.Sandbox("http://test.com", { wantGlobalProperties: ["XMLHttpRequest"] });
+
+ epsb.subsb = subsb;
+ epsb.xorigsb = xorigsb;
+ epsb.ok = ok;
+ epsb.equal = equal;
+ subsb.ok = ok;
+ subsb.equal = equal;
+
+ // Exporting should work if prinicipal of the source sandbox
+ // subsumes the principal of the target sandbox.
+ Cu.evalInSandbox("(" + function() {
+ var wasCalled = false;
+ this.funToExport = function(expectedThis, a, obj, native, mixed, callback) {
+ equal(arguments.callee.length, 6);
+ equal(a, 42);
+ equal(obj, subsb.tobecloned);
+ equal(obj.cloned, "cloned");
+ equal(native, subsb.native);
+ equal(expectedThis, this);
+ equal(mixed.xrayed, subsb.xrayed);
+ equal(mixed.xrayed2, subsb.xrayed2);
+ if (typeof callback == 'function') {
+ equal(typeof subsb.callback, 'function');
+ equal(callback, subsb.callback);
+ callback();
+ }
+ wasCalled = true;
+ };
+ this.checkIfCalled = function() {
+ ok(wasCalled);
+ wasCalled = false;
+ }
+ exportFunction(funToExport, subsb, { defineAs: "imported", allowCallbacks: true });
+ exportFunction((x) => x, subsb, { defineAs: "echoAllowXO", allowCallbacks: true, allowCrossOriginArguments: true });
+ }.toSource() + ")()", epsb);
+
+ subsb.xrayed = Cu.evalInSandbox("(" + function () {
+ return new XMLHttpRequest();
+ }.toSource() + ")()", subsb2);
+
+ // Exported function should be able to be call from the
+ // target sandbox. Native arguments should be just wrapped
+ // every other argument should be cloned.
+ Cu.evalInSandbox("(" + function () {
+ native = new XMLHttpRequest();
+ xrayed2 = XPCNativeWrapper(new XMLHttpRequest());
+ mixed = { xrayed: xrayed, xrayed2: xrayed2 };
+ tobecloned = { cloned: "cloned" };
+ invokedCallback = false;
+ callback = function() { invokedCallback = true; };
+ imported(this, 42, tobecloned, native, mixed, callback);
+ equal(imported.length, 6);
+ ok(invokedCallback);
+ }.toSource() + ")()", subsb);
+
+ // Invoking an exported function with cross-origin arguments should throw.
+ subsb.xoNative = Cu.evalInSandbox('new XMLHttpRequest()', xorigsb);
+ try {
+ Cu.evalInSandbox('imported(this, xoNative)', subsb);
+ Assert.ok(false);
+ } catch (e) {
+ Assert.ok(/denied|insecure/.test(e));
+ }
+
+ // Callers can opt-out of the above.
+ subsb.xoNative = Cu.evalInSandbox('new XMLHttpRequest()', xorigsb);
+ try {
+ Assert.equal(Cu.evalInSandbox('echoAllowXO(xoNative)', subsb), subsb.xoNative);
+ Assert.ok(true);
+ } catch (e) {
+ Assert.ok(false);
+ }
+
+ // Apply should work and |this| should carry over appropriately.
+ Cu.evalInSandbox("(" + function() {
+ var someThis = {};
+ imported.apply(someThis, [someThis, 42, tobecloned, native, mixed]);
+ }.toSource() + ")()", subsb);
+
+ Cu.evalInSandbox("(" + function() {
+ checkIfCalled();
+ }.toSource() + ")()", epsb);
+
+ // Exporting should throw if principal of the source sandbox does
+ // not subsume the principal of the target.
+ Cu.evalInSandbox("(" + function() {
+ try{
+ exportFunction(function() {}, this.xorigsb, { defineAs: "denied" });
+ ok(false);
+ } catch (e) {
+ ok(e.toString().indexOf('Permission denied') > -1);
+ }
+ }.toSource() + ")()", epsb);
+
+ // Exporting should throw if the principal of the source sandbox does
+ // not subsume the principal of the function.
+ epsb.xo_function = new xorigsb.Function();
+ Cu.evalInSandbox("(" + function() {
+ try{
+ exportFunction(xo_function, this.subsb, { defineAs: "denied" });
+ ok(false);
+ } catch (e) {
+ dump('Exception: ' + e);
+ ok(e.toString().indexOf('Permission denied') > -1);
+ }
+ }.toSource() + ")()", epsb);
+
+ // Let's create an object in the target scope and add privileged
+ // function to it as a property.
+ Cu.evalInSandbox("(" + function() {
+ var newContentObject = createObjectIn(subsb, { defineAs: "importedObject" });
+ exportFunction(funToExport, newContentObject, { defineAs: "privMethod" });
+ }.toSource() + ")()", epsb);
+
+ Cu.evalInSandbox("(" + function () {
+ importedObject.privMethod(importedObject, 42, tobecloned, native, mixed);
+ }.toSource() + ")()", subsb);
+
+ Cu.evalInSandbox("(" + function() {
+ checkIfCalled();
+ }.toSource() + ")()", epsb);
+
+ // exportFunction and createObjectIn should be available from Cu too.
+ var newContentObject = Cu.createObjectIn(subsb, { defineAs: "importedObject2" });
+ var wasCalled = false;
+ Cu.exportFunction(function(arg) { wasCalled = arg.wasCalled; },
+ newContentObject, { defineAs: "privMethod" });
+
+ Cu.evalInSandbox("(" + function () {
+ importedObject2.privMethod({wasCalled: true});
+ }.toSource() + ")()", subsb);
+
+ // 3rd argument of exportFunction should be optional.
+ Cu.evalInSandbox("(" + function() {
+ subsb.imported2 = exportFunction(funToExport, subsb);
+ }.toSource() + ")()", epsb);
+
+ Cu.evalInSandbox("(" + function () {
+ imported2(this, 42, tobecloned, native, mixed);
+ }.toSource() + ")()", subsb);
+
+ Cu.evalInSandbox("(" + function() {
+ checkIfCalled();
+ }.toSource() + ")()", epsb);
+
+ Assert.ok(wasCalled);
+}
diff --git a/js/xpconnect/tests/unit/test_file.js b/js/xpconnect/tests/unit/test_file.js
new file mode 100644
index 0000000000..ff4589c3f5
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_file.js
@@ -0,0 +1,11 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+add_task(function() {
+ let { TestFile } = ChromeUtils.import("resource://test/TestFile.jsm");
+ TestFile.doTest(result => {
+ Assert.ok(result);
+ run_next_test();
+ });
+});
diff --git a/js/xpconnect/tests/unit/test_file2.js b/js/xpconnect/tests/unit/test_file2.js
new file mode 100644
index 0000000000..4f3149b691
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_file2.js
@@ -0,0 +1,60 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+Cu.importGlobalProperties(['File']);
+
+add_task(async function() {
+ // throw if anything goes wrong
+
+ // find the current directory path
+ var file = Cc["@mozilla.org/file/directory_service;1"]
+ .getService(Ci.nsIProperties)
+ .get("CurWorkD", Ci.nsIFile);
+ file.append("xpcshell.toml");
+
+ // should be able to construct a file
+ var f1 = await File.createFromFileName(file.path);
+ // and with nsIFiles
+ var f2 = await File.createFromNsIFile(file);
+
+ // do some tests
+ Assert.ok(f1 instanceof File, "Should be a DOM File");
+ Assert.ok(f2 instanceof File, "Should be a DOM File");
+
+ Assert.ok(f1.name == "xpcshell.toml", "Should be the right file");
+ Assert.ok(f2.name == "xpcshell.toml", "Should be the right file");
+
+ Assert.ok(f1.type == "", "Should be the right type");
+ Assert.ok(f2.type == "", "Should be the right type");
+
+ var threw = false;
+ try {
+ // Needs a ctor argument
+ var f7 = File();
+ } catch (e) {
+ threw = true;
+ }
+ Assert.ok(threw, "No ctor arguments should throw");
+
+ var threw = false;
+ try {
+ // Needs a valid ctor argument
+ var f7 = File(Date(132131532));
+ } catch (e) {
+ threw = true;
+ }
+ Assert.ok(threw, "Passing a random object should fail");
+
+ var threw = false
+ try {
+ // Directories fail
+ var dir = Cc["@mozilla.org/file/directory_service;1"]
+ .getService(Ci.nsIProperties)
+ .get("CurWorkD", Ci.nsIFile);
+ var f7 = await File.createFromNsIFile(dir)
+ } catch (e) {
+ threw = true;
+ }
+ Assert.ok(threw, "Can't create a File object for a directory");
+});
diff --git a/js/xpconnect/tests/unit/test_fileReader.js b/js/xpconnect/tests/unit/test_fileReader.js
new file mode 100644
index 0000000000..ea86096319
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_fileReader.js
@@ -0,0 +1,12 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+function run_test() {
+ var sb = new Cu.Sandbox('http://www.example.com',
+ { wantGlobalProperties: ["FileReader"] });
+ sb.ok = ok;
+ Cu.evalInSandbox('ok((new FileReader()) instanceof FileReader);',
+ sb);
+ Cu.importGlobalProperties(["FileReader"]);
+ Assert.ok((new FileReader()) instanceof FileReader);
+}
diff --git a/js/xpconnect/tests/unit/test_function_names.js b/js/xpconnect/tests/unit/test_function_names.js
new file mode 100644
index 0000000000..6eadc1fce7
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_function_names.js
@@ -0,0 +1,37 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+function callback() {}
+
+let sandbox = Cu.Sandbox(this);
+let callbackWrapped = Cu.evalInSandbox("(function wrapped() {})", sandbox);
+
+function run_test() {
+ let functions = [
+ [{ notify: callback }, "callback[test_function_names.js]:JS"],
+ [{ notify: { notify: callback } }, "callback[test_function_names.js]:JS"],
+ [callback, "callback[test_function_names.js]:JS"],
+ [function() {}, "run_test/functions<[test_function_names.js]:JS"],
+ [function foobar() {}, "foobar[test_function_names.js]:JS"],
+ [function Δ() {}, "Δ[test_function_names.js]:JS"],
+ [{ notify1: callback, notify2: callback }, "nonfunction:JS"],
+ [{ notify: 10 }, "nonfunction:JS"],
+ [{}, "nonfunction:JS"],
+ [{ notify: callbackWrapped }, "wrapped[test_function_names.js]:JS"],
+ ];
+
+ // Use the observer service so we can get double-wrapped functions.
+ var obs = Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService);
+
+ function observer(subject, topic, data)
+ {
+ let named = subject.QueryInterface(Ci.nsINamed);
+ Assert.equal(named.name, data);
+ dump(`name: ${named.name}\n`);
+ }
+ obs.addObserver(observer, "test-obs-fun", false);
+
+ for (let [f, requiredName] of functions) {
+ obs.notifyObservers(f, "test-obs-fun", requiredName);
+ }
+}
diff --git a/js/xpconnect/tests/unit/test_generateQI.js b/js/xpconnect/tests/unit/test_generateQI.js
new file mode 100644
index 0000000000..d54ed53212
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_generateQI.js
@@ -0,0 +1,29 @@
+"use strict";
+
+add_task(async function test_generateQI() {
+ function checkQI(interfaces, iface) {
+ let obj = {
+ QueryInterface: ChromeUtils.generateQI(interfaces),
+ };
+ equal(obj.QueryInterface(iface), obj,
+ `Correct return value for query to ${iface}`);
+ }
+
+ // Test success scenarios.
+ checkQI([], Ci.nsISupports);
+
+ checkQI([Ci.nsIPropertyBag, "nsIPropertyBag2"], Ci.nsIPropertyBag);
+ checkQI([Ci.nsIPropertyBag, "nsIPropertyBag2"], Ci.nsIPropertyBag2);
+
+ checkQI([Ci.nsIPropertyBag, "nsIPropertyBag2", "nsINotARealInterface"], Ci.nsIPropertyBag2);
+
+ // Non-IID values get stringified, and don't cause any errors as long
+ // as there isn't a non-IID property with the same name on Ci.
+ checkQI([Ci.nsIPropertyBag, "nsIPropertyBag2", null, Object], Ci.nsIPropertyBag2);
+
+ ChromeUtils.generateQI([])(Ci.nsISupports);
+
+ // Test failure scenarios.
+ Assert.throws(() => checkQI([], Ci.nsIPropertyBag),
+ e => e.result == Cr.NS_ERROR_NO_INTERFACE);
+});
diff --git a/js/xpconnect/tests/unit/test_getCallerLocation.js b/js/xpconnect/tests/unit/test_getCallerLocation.js
new file mode 100644
index 0000000000..569d76f8eb
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_getCallerLocation.js
@@ -0,0 +1,86 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+"use strict";
+
+Cu.importGlobalProperties(["ChromeUtils"]);
+
+const {AddonTestUtils} = ChromeUtils.importESModule("resource://testing-common/AddonTestUtils.sys.mjs");
+
+add_task(async function() {
+ const sandbox = Cu.Sandbox("http://example.com/");
+
+ function foo() {
+ return bar();
+ }
+
+ function bar() {
+ return baz();
+ }
+
+ function baz() {
+ return ChromeUtils.getCallerLocation(Cu.getObjectPrincipal(sandbox));
+ }
+
+ Cu.evalInSandbox(`
+ function it() {
+ // Use map() to throw a self-hosted frame on the stack, which we
+ // should filter out.
+ return [0].map(foo)[0];
+ }
+ function thing() {
+ return it();
+ }
+ `, sandbox, undefined, "thing.js");
+
+ Cu.exportFunction(foo, sandbox, {defineAs: "foo"});
+
+ let frame = sandbox.thing();
+
+ equal(frame.source, "thing.js", "Frame source");
+ equal(frame.line, 5, "Frame line");
+ equal(frame.column, 18, "Frame column");
+ equal(frame.functionDisplayName, "it", "Frame function name");
+ equal(frame.parent, null, "Frame parent");
+
+ equal(String(frame), "it@thing.js:5:18\n", "Stringified frame");
+
+
+ // reportError
+
+ let {messages} = await AddonTestUtils.promiseConsoleOutput(() => {
+ Cu.reportError("Meh", frame);
+ });
+
+ let [msg] = messages.filter(m => m.message.includes("Meh"));
+
+ equal(msg.stack, frame, "reportError stack frame");
+ equal(msg.message, '[JavaScript Error: "Meh" {file: "thing.js" line: 5}]\nit@thing.js:5:18\n');
+
+ Assert.throws(() => { Cu.reportError("Meh", {}); },
+ err => err.result == Cr.NS_ERROR_INVALID_ARG,
+ "reportError should throw when passed a non-SavedFrame object");
+
+
+ // createError
+
+ Assert.throws(() => { ChromeUtils.createError("Meh", {}); },
+ err => err.result == Cr.NS_ERROR_INVALID_ARG,
+ "createError should throw when passed a non-SavedFrame object");
+
+ let cloned = Cu.cloneInto(frame, sandbox);
+ let error = ChromeUtils.createError("Meh", cloned);
+
+ equal(String(cloned), String(frame),
+ "Cloning a SavedStack preserves its stringification");
+
+ equal(Cu.getGlobalForObject(error), sandbox,
+ "createError creates errors in the global of the SavedFrame");
+ equal(error.stack, String(cloned),
+ "createError creates errors with the correct stack");
+
+ equal(error.message, "Meh", "Error message");
+ equal(error.fileName, "thing.js", "Error filename");
+ equal(error.lineNumber, 5, "Error line");
+ equal(error.columnNumber, 18, "Error column");
+});
diff --git a/js/xpconnect/tests/unit/test_getObjectPrincipal.js b/js/xpconnect/tests/unit/test_getObjectPrincipal.js
new file mode 100644
index 0000000000..03c6ffce3d
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_getObjectPrincipal.js
@@ -0,0 +1,6 @@
+function run_test() {
+ Assert.ok(Cu.getObjectPrincipal({}).isSystemPrincipal);
+ var sb = new Cu.Sandbox('http://www.example.com');
+ Cu.evalInSandbox('var obj = { foo: 42 };', sb);
+ Assert.equal(Cu.getObjectPrincipal(sb.obj).origin, 'http://www.example.com');
+}
diff --git a/js/xpconnect/tests/unit/test_import.js b/js/xpconnect/tests/unit/test_import.js
new file mode 100644
index 0000000000..eef474cf3e
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_import.js
@@ -0,0 +1,72 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+var TestFile;
+function run_test() {
+ var scope = {};
+ var exports = ChromeUtils.import("resource://test/TestFile.jsm", scope);
+ Assert.equal(typeof(scope.TestFile), "object");
+ Assert.equal(typeof(scope.TestFile.doTest), "function");
+
+ equal(scope.TestFile, exports.TestFile);
+ deepEqual(Object.keys(scope), ["TestFile"]);
+ deepEqual(Object.keys(exports), ["TestFile"]);
+
+ exports = ChromeUtils.import("resource://test/TestFile.jsm");
+ equal(scope.TestFile, exports.TestFile);
+ deepEqual(Object.keys(exports), ["TestFile"]);
+
+ // access module's global object directly without importing any
+ // symbols
+ Assert.throws(
+ () => ChromeUtils.import("resource://test/TestFile.jsm", null),
+ TypeError
+ );
+
+ // import symbols to our global object
+ Assert.equal(typeof(Cu.import), "function");
+ ({TestFile} = ChromeUtils.import("resource://test/TestFile.jsm"));
+ Assert.equal(typeof(TestFile), "object");
+ Assert.equal(typeof(TestFile.doTest), "function");
+
+ // try on a new object
+ var scope2 = {};
+ ChromeUtils.import("resource://test/TestFile.jsm", scope2);
+ Assert.equal(typeof(scope2.TestFile), "object");
+ Assert.equal(typeof(scope2.TestFile.doTest), "function");
+
+ Assert.ok(scope2.TestFile == scope.TestFile);
+
+ // try on a new object using the resolved URL
+ var res = Cc["@mozilla.org/network/protocol;1?name=resource"]
+ .getService(Ci.nsIResProtocolHandler);
+ var resURI = Cc["@mozilla.org/network/io-service;1"]
+ .getService(Ci.nsIIOService)
+ .newURI("resource://test/TestFile.jsm");
+ dump("resURI: " + resURI + "\n");
+ var filePath = res.resolveURI(resURI);
+ var scope3 = {};
+ Assert.throws(
+ () => ChromeUtils.import(filePath, scope3),
+ /SecurityError/, "Expecting file URI not to be imported"
+ );
+
+ // make sure we throw when the second arg is bogus
+ var didThrow = false;
+ try {
+ ChromeUtils.import("resource://test/TestFile.jsm", "wrong");
+ } catch (ex) {
+ print("exception (expected): " + ex);
+ didThrow = true;
+ }
+ Assert.ok(didThrow);
+
+ // make sure we throw when the URL scheme is not known
+ var scope4 = {};
+ const wrongScheme = "data:text/javascript,var a = {a:1}";
+ Assert.throws(
+ () => ChromeUtils.import(wrongScheme, scope4),
+ /SecurityError/, "Expecting data URI not to be imported"
+ );
+}
diff --git a/js/xpconnect/tests/unit/test_import_devtools_loader.js b/js/xpconnect/tests/unit/test_import_devtools_loader.js
new file mode 100644
index 0000000000..d7e6fe42f6
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_import_devtools_loader.js
@@ -0,0 +1,85 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+const { AppConstants } = ChromeUtils.importESModule(
+ "resource://gre/modules/AppConstants.sys.mjs"
+);
+const { addDebuggerToGlobal } = ChromeUtils.importESModule(
+ "resource://gre/modules/jsdebugger.sys.mjs"
+);
+addDebuggerToGlobal(this);
+
+const ESM_URL = "resource://test/es6module_devtoolsLoader.sys.mjs";
+
+// Toggle the following pref to enable Cu.getModuleImportStack()
+if (AppConstants.NIGHTLY_BUILD) {
+ Services.prefs.setBoolPref("browser.startup.record", true);
+ registerCleanupFunction(() => {
+ Services.prefs.clearUserPref("browser.startup.record");
+ });
+}
+
+add_task(async function testDevToolsModuleLoader() {
+ const dbg = new Debugger();
+
+ const sharedGlobal = Cu.getGlobalForObject(Services);
+ const sharedPrincipal = Cu.getObjectPrincipal(sharedGlobal);
+
+ info("Test importing in the regular shared loader");
+ const ns = ChromeUtils.importESModule(ESM_URL);
+ Assert.equal(ns.x, 0);
+ ns.increment();
+ Assert.equal(ns.x, 1);
+ const nsGlobal = Cu.getGlobalForObject(ns);
+ const nsPrincipal = Cu.getObjectPrincipal(nsGlobal);
+ Assert.equal(nsGlobal, sharedGlobal, "Without any parameter, importESModule load in the shared JSM global");
+ Assert.equal(nsPrincipal, sharedPrincipal);
+ Assert.ok(nsPrincipal.isSystemPrincipal);
+ info("Global of ESM loaded in the shared loader can be inspected by the Debugger");
+ dbg.addDebuggee(nsGlobal);
+ Assert.ok(true, "The global is accepted by the Debugger API");
+
+ const ns1 = ChromeUtils.importESModule(ESM_URL, { loadInDevToolsLoader : false });
+ Assert.equal(ns1, ns, "Passing loadInDevToolsLoader=false from the shared JSM global is equivalent to regular importESModule");
+
+ info("Test importing in the devtools loader");
+ const ns2 = ChromeUtils.importESModule(ESM_URL, { loadInDevToolsLoader: true });
+ Assert.equal(ns2.x, 0, "We get a new module instance with a new incremented number");
+ Assert.notEqual(ns2, ns, "We imported a new instance of the module");
+ Assert.notEqual(ns2.importedObject, ns.importedObject, "The two module instances expose distinct objects");
+ Assert.equal(ns2.importESModuleTrue, ns2.importedObject, "When using loadInDevToolsLoader:true from a devtools global, we keep loading in the same loader");
+ Assert.equal(ns2.importESModuleNull, ns2.importedObject, "When having an undefined loadInDevToolsLoader from a devtools global, we keep loading in the same loader");
+ Assert.equal(ns2.importESModuleNull2, ns2.importedObject, "When having no optional argument at all, we keep loading in the same loader");
+ Assert.equal(ns2.importESModuleFalse, ns.importedObject, "When passing an explicit loadInDevToolsLoader:false, we load in the shared global, even from a devtools global");
+ Assert.equal(ns2.importLazy(), ns2.importedObject, "ChromeUtils.defineESModuleGetters imports will follow the contextual loader");
+
+ info("When using the devtools loader, we load in a distinct global, but the same compartment");
+ const ns2Global = Cu.getGlobalForObject(ns2);
+ const ns2Principal = Cu.getObjectPrincipal(ns2Global);
+ Assert.notEqual(ns2Global, sharedGlobal, "The module is loaded in a distinct global");
+ Assert.equal(ns2Principal, sharedPrincipal, "The principal is still the shared system principal");
+ Assert.equal(Cu.getGlobalForObject(ns2.importedObject), ns2Global, "Nested dependencies are also loaded in the same devtools global");
+ Assert.throws(() => dbg.addDebuggee(ns2Global), /TypeError: passing non-debuggable global to addDebuggee/,
+ "Global os ESM loaded in the devtools loader can't be inspected by the Debugee");
+
+ info("Re-import the same module in the devtools loader");
+ const ns3 = ChromeUtils.importESModule(ESM_URL, { loadInDevToolsLoader: true });
+ Assert.equal(ns3, ns2, "We import the exact same module");
+ Assert.equal(ns3.importedObject, ns2.importedObject, "The two module expose the same objects");
+
+ info("Import a module only from the devtools loader");
+ const ns4 = ChromeUtils.importESModule("resource://test/es6module_devtoolsLoader_only.js", { loadInDevToolsLoader: true });
+ const ns4Global = Cu.getGlobalForObject(ns4);
+ Assert.equal(ns4Global, ns2Global, "The module is loaded in the same devtools global");
+
+ // getModuleImportStack only works on nightly builds
+ if (AppConstants.NIGHTLY_BUILD) {
+ info("Assert the behavior of getModuleImportStack on modules loaded in the devtools loader");
+ Assert.ok(Cu.getModuleImportStack(ESM_URL).includes("testDevToolsModuleLoader"));
+ Assert.ok(Cu.getModuleImportStack("resource://test/es6module_devtoolsLoader.js").includes("testDevToolsModuleLoader"));
+ Assert.ok(Cu.getModuleImportStack("resource://test/es6module_devtoolsLoader.js").includes(ESM_URL));
+ // Previous import stack were for module loaded via the shared jsm loader.
+ // Let's also assert that we get stack traces for modules loaded via the devtools loader.
+ Assert.ok(Cu.getModuleImportStack("resource://test/es6module_devtoolsLoader_only.js").includes("testDevToolsModuleLoader"));
+ }
+});
diff --git a/js/xpconnect/tests/unit/test_import_es6_modules.js b/js/xpconnect/tests/unit/test_import_es6_modules.js
new file mode 100644
index 0000000000..30b4edab9f
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_import_es6_modules.js
@@ -0,0 +1,250 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+add_task(async function() {
+ // Test basic import.
+ let ns = ChromeUtils.importESModule("resource://test/es6module.js");
+ Assert.equal(ns.loadCount, 1);
+ Assert.equal(ns.value, 2);
+
+ // Test re-import of the same module.
+ let ns2 = ChromeUtils.importESModule("resource://test/es6module.js");
+ Assert.equal(ns.loadCount, 1);
+ Assert.equal(ns, ns2);
+
+ // Test imports with absolute and relative URIs return the same thing.
+ let ns3 = ChromeUtils.importESModule("resource://test/es6module_absolute.js");
+ let ns4 = ChromeUtils.importESModule("resource://test/es6module_absolute2.js");
+ Assert.ok(ns3.absoluteX === ns3.relativeX);
+ Assert.ok(ns3.absoluteX === ns4.x);
+
+ // Test load failure.
+ testFailure("resource://test/es6module_not_found.js", {
+ type: "Error",
+ message: "Failed to load resource://test/es6module_not_found.js",
+ fileName: "test_import_es6_modules.js",
+ stack: "testFailure",
+ lineNumber: "*",
+ columnNumber: "*",
+ result: Cr.NS_ERROR_FILE_NOT_FOUND,
+ });
+
+ // Test load failure in import.
+ testFailure("resource://test/es6module_missing_import.js", {
+ type: "Error",
+ message: "Failed to load resource://test/es6module_not_found2.js",
+ fileName: "test_import_es6_modules.js",
+ stack: "testFailure",
+ lineNumber: "*",
+ columnNumber: "*",
+ result: Cr.NS_ERROR_FILE_NOT_FOUND,
+ });
+
+ // Test parse error.
+ testFailure("resource://test/es6module_parse_error.js", {
+ type: "SyntaxError",
+ fileName: "resource://test/es6module_parse_error.js",
+ stack: "testFailure",
+ lineNumber: 1,
+ columnNumber: 6,
+ });
+
+ // Test parse error in import.
+ testFailure("resource://test/es6module_parse_error_in_import.js", {
+ type: "SyntaxError",
+ fileName: "resource://test/es6module_parse_error.js",
+ stack: "testFailure",
+ lineNumber: 1,
+ columnNumber: 6,
+ });
+
+ // Test import error.
+ testFailure("resource://test/es6module_import_error.js", {
+ type: "SyntaxError",
+ fileName: "resource://test/es6module_import_error.js",
+ lineNumber: 1,
+ columnNumber: 10,
+ });
+
+ // Test execution failure.
+ let exception1 = testFailure("resource://test/es6module_throws.js", {
+ type: "Error",
+ message: "foobar",
+ stack: "throwFunction",
+ fileName: "resource://test/es6module_throws.js",
+ lineNumber: 2,
+ columnNumber: 9,
+ });
+
+ // Test re-import throws the same exception.
+ let exception2 = testFailure("resource://test/es6module_throws.js", {
+ type: "Error",
+ message: "foobar",
+ stack: "throwFunction",
+ fileName: "resource://test/es6module_throws.js",
+ lineNumber: 2,
+ columnNumber: 9,
+ });
+ Assert.ok(exception1 === exception2);
+
+ // Test loading cyclic module graph.
+ ns = ChromeUtils.importESModule("resource://test/es6module_cycle_a.js");
+ Assert.ok(ns.loaded);
+ Assert.equal(ns.getValueFromB(), "b");
+ ns = ChromeUtils.importESModule("resource://test/es6module_cycle_b.js");
+ Assert.ok(ns.loaded);
+ Assert.equal(ns.getValueFromC(), "c");
+ ns = ChromeUtils.importESModule("resource://test/es6module_cycle_c.js");
+ Assert.ok(ns.loaded);
+ Assert.equal(ns.getValueFromA(), "a");
+
+ // Test top-level await is not supported.
+ testFailure("resource://test/es6module_top_level_await.js", {
+ type: "SyntaxError",
+ message: "not supported",
+ stack: "testFailure",
+ fileName: "resource://test/es6module_top_level_await.js",
+ lineNumber: 1,
+ columnNumber: 1,
+ });
+});
+
+add_task(async function testDynamicImport() {
+ // Dynamic import while and after evaluating top-level script.
+ let ns = ChromeUtils.importESModule("resource://test/es6module_dynamic_import.js");
+ let ns2 = await ns.result;
+ Assert.equal(ns2.x, 10);
+
+ ns2 = await ns.doImport();
+ Assert.equal(ns2.y, 20);
+
+ // Dynamic import for statically imported module.
+ Assert.equal(ns.callGetCounter(), 1);
+ ns.callSetCounter(5);
+ Assert.equal(ns.callGetCounter(), 5);
+
+ const { getCounter, setCounter } = await ns.doImportStatic();
+ Assert.equal(getCounter(), 5);
+ setCounter(8);
+ Assert.equal(getCounter(), 8);
+ Assert.equal(ns.callGetCounter(), 8);
+
+ // Dynamic import for missing file.
+ ns = ChromeUtils.importESModule("resource://test/es6module_dynamic_import_missing.js");
+ let e = await ns.result;
+ checkException(e, {
+ type: "TypeError",
+ message: "error loading dynamically imported",
+ fileName: "resource://test/es6module_dynamic_import_missing.js",
+ lineNumber: 5,
+ columnNumber: 1,
+ });
+
+ e = await ns.doImport();
+ checkException(e, {
+ type: "TypeError",
+ message: "error loading dynamically imported",
+ fileName: "resource://test/es6module_dynamic_import_missing.js",
+ lineNumber: 11,
+ columnNumber: 5,
+ });
+
+ // Syntax error in dynamic import.
+ ns = ChromeUtils.importESModule("resource://test/es6module_dynamic_import_syntax_error.js");
+ e = await ns.result;
+ checkException(e, {
+ type: "SyntaxError",
+ message: "unexpected token",
+ fileName: "resource://test/es6module_dynamic_import_syntax_error2.js",
+ lineNumber: 1,
+ columnNumber: 3,
+ });
+
+ e = await ns.doImport();
+ checkException(e, {
+ type: "SyntaxError",
+ message: "unexpected token",
+ fileName: "resource://test/es6module_dynamic_import_syntax_error3.js",
+ lineNumber: 1,
+ columnNumber: 4,
+ });
+
+ // Runtime error in dynamic import.
+ ns = ChromeUtils.importESModule("resource://test/es6module_dynamic_import_runtime_error.js");
+ e = await ns.result;
+ checkException(e, {
+ type: "ReferenceError",
+ message: "foo is not defined",
+ fileName: "resource://test/es6module_dynamic_import_runtime_error2.js",
+ lineNumber: 2,
+ columnNumber: 1,
+ });
+
+ e = await ns.doImport();
+ checkException(e, {
+ type: "ReferenceError",
+ message: "bar is not defined",
+ fileName: "resource://test/es6module_dynamic_import_runtime_error3.js",
+ lineNumber: 2,
+ columnNumber: 1,
+ });
+});
+
+function testFailure(url, expected) {
+ let threw = false;
+ let exception;
+ let importLine, importColumn;
+ try {
+ // Get the line/column for ChromeUtils.importESModule.
+ // lineNumber/columnNumber value with "*" in `expected` points the
+ // line/column.
+ let e = new Error();
+ importLine = e.lineNumber + 3;
+ importColumn = 17;
+ ChromeUtils.importESModule(url);
+ } catch (e) {
+ threw = true;
+ exception = e;
+ }
+
+ Assert.ok(threw, "Error should be thrown");
+
+ checkException(exception, expected, importLine, importColumn);
+
+ return exception;
+}
+
+function checkException(exception, expected, importLine, importColumn) {
+ if ("type" in expected) {
+ Assert.equal(exception.constructor.name, expected.type, "error type");
+ }
+ if ("message" in expected) {
+ Assert.ok(exception.message.includes(expected.message),
+ `Message "${exception.message}" should contain "${expected.message}"`);
+ }
+ if ("stack" in expected) {
+ Assert.ok(exception.stack.includes(expected.stack),
+ `Stack "${exception.stack}" should contain "${expected.stack}"`);
+ }
+ if ("fileName" in expected) {
+ Assert.ok(exception.fileName.includes(expected.fileName),
+ `fileName "${exception.fileName}" should contain "${expected.fileName}"`);
+ }
+ if ("lineNumber" in expected) {
+ let expectedLine = expected.lineNumber;
+ if (expectedLine === "*") {
+ expectedLine = importLine;
+ }
+ Assert.equal(exception.lineNumber, expectedLine, "lineNumber");
+ }
+ if ("columnNumber" in expected) {
+ let expectedColumn = expected.columnNumber;
+ if (expectedColumn === "*") {
+ expectedColumn = importColumn;
+ }
+ Assert.equal(exception.columnNumber, expectedColumn, "columnNumber");
+ }
+ if ("result" in expected) {
+ Assert.equal(exception.result, expected.result, "result");
+ }
+}
diff --git a/js/xpconnect/tests/unit/test_import_fail.js b/js/xpconnect/tests/unit/test_import_fail.js
new file mode 100644
index 0000000000..9ad7fcb072
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_import_fail.js
@@ -0,0 +1,10 @@
+function run_test()
+{
+ try {
+ ChromeUtils.import("resource://test/importer.jsm");
+ Assert.ok(false, "import should not succeed.");
+ } catch (x) {
+ Assert.notEqual(x.fileName.indexOf("syntax_error.jsm"), -1);
+ Assert.equal(x.lineNumber, 1);
+ }
+} \ No newline at end of file
diff --git a/js/xpconnect/tests/unit/test_import_from_sandbox.js b/js/xpconnect/tests/unit/test_import_from_sandbox.js
new file mode 100644
index 0000000000..ddd384a77b
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_import_from_sandbox.js
@@ -0,0 +1,82 @@
+"use strict";
+
+function makeSandbox() {
+ return Cu.Sandbox(
+ Services.scriptSecurityManager.getSystemPrincipal(),
+ {
+ wantXrays: false,
+ wantGlobalProperties: ["ChromeUtils"],
+ sandboxName: `Sandbox type used for ext-*.js ExtensionAPI subscripts`,
+ }
+ );
+}
+
+// This test will fail (and should be removed) once the JSM shim is dropped.
+add_task(function test_import_from_sandbox_using_shim() {
+ let sandbox = makeSandbox();
+ Object.assign(sandbox, {
+ injected1: ChromeUtils.import("resource://test/esmified-1.jsm"),
+ });
+
+ Assert.equal(Cu.isModuleLoaded("resource://test/esmified-2.jsm"), false);
+
+ Services.scriptloader.loadSubScript(
+ `data:,
+ "use strict";
+
+ const shimmed1 = ChromeUtils.import("resource://test/esmified-1.jsm");
+ const shimmed2 = ChromeUtils.import("resource://test/esmified-2.jsm");
+
+ this.testResults = {
+ shimmed1: shimmed1.obj.value,
+ injected1: injected1.obj.value,
+ sameInstance1: injected1 === shimmed1,
+ shimmed2: shimmed2.obj.value,
+ };
+ `,
+ sandbox
+ );
+ let tr = sandbox.testResults;
+
+ Assert.equal(tr.injected1, 10, "Injected esmified-1.mjs has correct value.");
+ Assert.equal(tr.shimmed1, 10, "Shim-imported esmified-1.jsm correct value.");
+ Assert.ok(tr.sameInstance1, "Injected and imported are the same instance.");
+ Assert.equal(tr.shimmed2, 10, "Shim-imported esmified-2.jsm correct value.");
+
+ Assert.equal(Cu.isModuleLoaded("resource://test/esmified-2.jsm"), true);
+});
+
+// This tests the ESMification transition for extension API scripts.
+add_task(function test_import_from_sandbox_transition() {
+ let sandbox = makeSandbox();
+
+ Object.assign(sandbox, {
+ injected3: ChromeUtils.importESModule("resource://test/esmified-3.sys.mjs"),
+ });
+
+ Services.scriptloader.loadSubScript("resource://test/api_script.js", sandbox);
+ let tr = sandbox.testResults;
+
+ Assert.equal(tr.injected3, 16, "Injected esmified-3.mjs has correct value.");
+ Assert.equal(tr.module3, 16, "Iimported esmified-3.mjs has correct value.");
+ Assert.ok(tr.sameInstance3, "Injected and imported are the same instance.");
+ Assert.equal(tr.module4, 14, "Iimported esmified-4.mjs has correct value.");
+});
+
+// Same as above, just using a PrecompiledScript.
+add_task(async function test_import_from_sandbox_transition() {
+ let sandbox = makeSandbox();
+
+ Object.assign(sandbox, {
+ injected3: ChromeUtils.importESModule("resource://test/esmified-3.sys.mjs"),
+ });
+
+ let script = await ChromeUtils.compileScript("resource://test/api_script.js");
+ script.executeInGlobal(sandbox);
+ let tr = sandbox.testResults;
+
+ Assert.equal(tr.injected3, 22, "Injected esmified-3.mjs has correct value.");
+ Assert.equal(tr.module3, 22, "Iimported esmified-3.mjs has correct value.");
+ Assert.ok(tr.sameInstance3, "Injected and imported are the same instance.");
+ Assert.equal(tr.module4, 18, "Iimported esmified-4.mjs has correct value.");
+});
diff --git a/js/xpconnect/tests/unit/test_import_global.js b/js/xpconnect/tests/unit/test_import_global.js
new file mode 100644
index 0000000000..9ad4522854
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_import_global.js
@@ -0,0 +1,47 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+add_task(async function testShared() {
+ const ns1 = ChromeUtils.importESModule("resource://test/esmified-1.sys.mjs");
+
+ const ns2 = ChromeUtils.importESModule("resource://test/esmified-1.sys.mjs", {
+ global: "shared",
+ });
+
+ Assert.equal(ns1, ns2);
+ Assert.equal(ns1.obj, ns2.obj);
+});
+
+add_task(async function testDevTools() {
+ const ns1 = ChromeUtils.importESModule("resource://test/esmified-1.sys.mjs", {
+ loadInDevToolsLoader: true,
+ });
+
+ const ns2 = ChromeUtils.importESModule("resource://test/esmified-1.sys.mjs", {
+ global: "devtools",
+ });
+
+ Assert.equal(ns1, ns2);
+ Assert.equal(ns1.obj, ns2.obj);
+});
+
+add_task(async function testInvalidOptions() {
+ // Unknown value is rejected.
+ Assert.throws(() => {
+ ChromeUtils.importESModule("resource://test/esmified-1.sys.mjs", {
+ global: "invalid",
+ });
+ }, Error);
+
+ Assert.throws(() => {
+ ChromeUtils.importESModule("resource://test/esmified-1.sys.mjs", {
+ global: globalThis,
+ });
+ }, Error);
+
+ // Unknown name is ignored.
+ ChromeUtils.importESModule("resource://test/esmified-1.sys.mjs", {
+ global2: "shared",
+ });
+});
diff --git a/js/xpconnect/tests/unit/test_import_global_contextual.js b/js/xpconnect/tests/unit/test_import_global_contextual.js
new file mode 100644
index 0000000000..817f00fc45
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_import_global_contextual.js
@@ -0,0 +1,38 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+add_task(async function testInNonShared() {
+ const ns1 = ChromeUtils.importESModule("resource://test/esmified-1.sys.mjs");
+
+ const ns2 = ChromeUtils.importESModule("resource://test/esmified-1.sys.mjs", {
+ global: "contextual",
+ });
+
+ Assert.equal(ns1, ns2);
+ Assert.equal(ns1.obj, ns2.obj);
+});
+
+add_task(async function testInShared() {
+ const { ns: ns1 } = ChromeUtils.importESModule("resource://test/contextual.sys.mjs");
+
+ const ns2 = ChromeUtils.importESModule("resource://test/esmified-1.sys.mjs", {
+ global: "shared",
+ });
+
+ Assert.equal(ns1, ns2);
+ Assert.equal(ns1.obj, ns2.obj);
+});
+
+add_task(async function testInShared() {
+ const { ns: ns1 } = ChromeUtils.importESModule("resource://test/contextual.sys.mjs", {
+ global: "devtools",
+ });
+
+ const ns2 = ChromeUtils.importESModule("resource://test/esmified-1.sys.mjs", {
+ global: "devtools",
+ });
+
+ Assert.equal(ns1, ns2);
+ Assert.equal(ns1.obj, ns2.obj);
+});
diff --git a/js/xpconnect/tests/unit/test_import_global_contextual_worker.js b/js/xpconnect/tests/unit/test_import_global_contextual_worker.js
new file mode 100644
index 0000000000..f64317c552
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_import_global_contextual_worker.js
@@ -0,0 +1,17 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+add_task(async function testInWorker() {
+ const worker = new ChromeWorker("resource://test/contextual_worker.js");
+ const { promise, resolve } = Promise.withResolvers();
+ worker.onmessage = event => {
+ resolve(event.data);
+ };
+ worker.postMessage("");
+
+ const result = await promise;
+
+ Assert.ok(result.equal1);
+ Assert.ok(result.equal2);
+});
diff --git a/js/xpconnect/tests/unit/test_import_global_current.js b/js/xpconnect/tests/unit/test_import_global_current.js
new file mode 100644
index 0000000000..cf466a7391
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_import_global_current.js
@@ -0,0 +1,796 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+add_task(async function testSandbox() {
+ const uri = "http://example.com/";
+ const window = createContentWindow(uri);
+ const sandboxOpts = {
+ sandboxPrototype: window,
+ wantGlobalProperties: ["ChromeUtils"],
+ };
+ const sb = new Cu.Sandbox(uri, sandboxOpts);
+
+ Cu.evalInSandbox(`
+globalThis["loaded"] = [];
+var ns = ChromeUtils.importESModule("resource://test/non_shared_1.mjs", {
+ global: "current",
+});
+`, sb);
+
+ Assert.equal(Cu.evalInSandbox(`ns.getCounter();`, sb), 0);
+ Cu.evalInSandbox(`ns.incCounter();`, sb);
+ Assert.equal(Cu.evalInSandbox(`ns.getCounter();`, sb), 1);
+
+ Assert.equal(Cu.evalInSandbox(`globalThis["loaded"].join(",")`, sb), "2,1");
+});
+
+add_task(async function testNoWindowSandbox() {
+ // Sandbox without window doesn't have ScriptLoader, and Sandbox's
+ // ModuleLoader cannot be created.
+ const systemPrincipal = Components.Constructor(
+ "@mozilla.org/systemprincipal;1",
+ "nsIPrincipal"
+ )();
+ const sandboxOpts = {
+ wantGlobalProperties: ["ChromeUtils"],
+ };
+
+ const sb = new Cu.Sandbox(systemPrincipal, sandboxOpts);
+
+ let caught = false;
+ try {
+ Cu.evalInSandbox(`
+ChromeUtils.importESModule("resource://test/non_shared_1.mjs", {
+ global: "current",
+});
+`, sb);
+ } catch (e) {
+ caught = true;
+ Assert.stringMatches(e.message, /No ModuleLoader found/);
+ }
+ Assert.ok(caught);
+});
+
+add_task(async function testWindow() {
+ const win1 = createChromeWindow();
+
+ win1.eval(`
+globalThis["loaded"] = [];
+var ns = ChromeUtils.importESModule("resource://test/non_shared_1.mjs", {
+ global: "current",
+});
+`);
+
+ Assert.equal(win1.eval(`ns.getCounter();`), 0);
+ win1.eval(`ns.incCounter();`);
+ Assert.equal(win1.eval(`ns.getCounter();`), 1);
+
+ Assert.equal(win1.eval(`globalThis["loaded"].join(",")`), "2,1");
+});
+
+add_task(async function testReImport() {
+ // Re-importing the same module should return the same thing.
+
+ const uri = "http://example.com/";
+ const window = createContentWindow(uri);
+ const sandboxOpts = {
+ sandboxPrototype: window,
+ wantGlobalProperties: ["ChromeUtils"],
+ };
+ const sb = new Cu.Sandbox(uri, sandboxOpts);
+
+ Cu.evalInSandbox(`
+globalThis["loaded"] = [];
+var ns = ChromeUtils.importESModule("resource://test/non_shared_1.mjs", {
+ global: "current",
+});
+`, sb);
+
+ Assert.equal(Cu.evalInSandbox(`ns.getCounter();`, sb), 0);
+ Cu.evalInSandbox(`ns.incCounter();`, sb);
+ Assert.equal(Cu.evalInSandbox(`ns.getCounter();`, sb), 1);
+
+ Assert.equal(Cu.evalInSandbox(`globalThis["loaded"].join(",")`, sb), "2,1");
+
+ Cu.evalInSandbox(`
+var ns2 = ChromeUtils.importESModule("resource://test/non_shared_1.mjs", {
+ global: "current",
+});
+`, sb);
+
+ // The counter should be shared, and also not reset.
+ Assert.equal(Cu.evalInSandbox(`ns2.getCounter();`, sb), 1);
+ Cu.evalInSandbox(`ns2.incCounter();`, sb);
+ Assert.equal(Cu.evalInSandbox(`ns.getCounter();`, sb), 2);
+ Assert.equal(Cu.evalInSandbox(`ns2.getCounter();`, sb), 2);
+
+ // The top-level script shouldn't be executed twice.
+ Assert.equal(Cu.evalInSandbox(`globalThis["loaded"].join(",")`, sb), "2,1");
+});
+
+add_task(async function testNotFound() {
+ // Importing non-existent file should throw error.
+
+ const uri = "http://example.com/";
+ const window = createContentWindow(uri);
+ const sandboxOpts = {
+ sandboxPrototype: window,
+ wantGlobalProperties: ["ChromeUtils"],
+ };
+ const sb = new Cu.Sandbox(uri, sandboxOpts);
+
+ let caught = false;
+ try {
+ Cu.evalInSandbox(`
+ChromeUtils.importESModule("resource://test/not_found.mjs", {
+ global: "current",
+});
+`, sb);
+ } catch (e) {
+ caught = true;
+ Assert.stringMatches(e.message, /Failed to load/);
+ }
+ Assert.ok(caught);
+});
+
+add_task(async function testParseError() {
+ // Parse error should be thrown.
+
+ const uri = "http://example.com/";
+ const window = createContentWindow(uri);
+ const sandboxOpts = {
+ sandboxPrototype: window,
+ wantGlobalProperties: ["ChromeUtils"],
+ };
+ const sb = new Cu.Sandbox(uri, sandboxOpts);
+
+ let caught = false;
+ try {
+ Cu.evalInSandbox(`
+ChromeUtils.importESModule("resource://test/es6module_parse_error.js", {
+ global: "current",
+});
+`, sb);
+ } catch (e) {
+ caught = true;
+ Assert.stringMatches(e.message, /unexpected token/);
+ }
+ Assert.ok(caught);
+});
+
+add_task(async function testParseErrorInImport() {
+ // Parse error in imported module should be thrown.
+
+ const uri = "http://example.com/";
+ const window = createContentWindow(uri);
+ const sandboxOpts = {
+ sandboxPrototype: window,
+ wantGlobalProperties: ["ChromeUtils"],
+ };
+ const sb = new Cu.Sandbox(uri, sandboxOpts);
+
+ let caught = false;
+ try {
+ Cu.evalInSandbox(`
+ChromeUtils.importESModule("resource://test/es6module_parse_error_in_import.js", {
+ global: "current",
+});
+`, sb);
+ } catch (e) {
+ caught = true;
+ Assert.stringMatches(e.message, /unexpected token/);
+ }
+ Assert.ok(caught);
+});
+
+add_task(async function testImportError() {
+ // Error for nested import should be thrown.
+
+ const uri = "http://example.com/";
+ const window = createContentWindow(uri);
+ const sandboxOpts = {
+ sandboxPrototype: window,
+ wantGlobalProperties: ["ChromeUtils"],
+ };
+ const sb = new Cu.Sandbox(uri, sandboxOpts);
+
+ let caught = false;
+ try {
+ Cu.evalInSandbox(`
+ChromeUtils.importESModule("resource://test/es6module_import_error.js", {
+ global: "current",
+});
+`, sb);
+ } catch (e) {
+ caught = true;
+ Assert.stringMatches(e.message, /import not found/);
+ }
+ Assert.ok(caught);
+});
+
+add_task(async function testExecutionError() {
+ // Error while execution the top-level script should be thrown.
+
+ const uri = "http://example.com/";
+ const window = createContentWindow(uri);
+ const sandboxOpts = {
+ sandboxPrototype: window,
+ wantGlobalProperties: ["ChromeUtils"],
+ };
+ const sb = new Cu.Sandbox(uri, sandboxOpts);
+
+ let caught = false;
+ try {
+ Cu.evalInSandbox(`
+ChromeUtils.importESModule("resource://test/es6module_throws.js", {
+ global: "current",
+});
+`, sb);
+ } catch (e) {
+ caught = true;
+ Assert.stringMatches(e.message, /foobar/);
+ }
+ Assert.ok(caught);
+
+ // Re-import should throw the same error.
+
+ caught = false;
+ try {
+ Cu.evalInSandbox(`
+ChromeUtils.importESModule("resource://test/es6module_throws.js", {
+ global: "current",
+});
+`, sb);
+ } catch (e) {
+ caught = true;
+ Assert.stringMatches(e.message, /foobar/);
+ }
+ Assert.ok(caught);
+});
+
+add_task(async function testImportNestShared() {
+ // Importing system ESM should work.
+
+ const win1 = createChromeWindow();
+
+ const result = win1.eval(`
+const { func1 } = ChromeUtils.importESModule("resource://test/non_shared_nest_import_shared_1.mjs", {
+ global: "current",
+});
+func1();
+`);
+
+ Assert.equal(result, 27);
+});
+
+add_task(async function testImportNestNonSharedSame() {
+ // For the same global, nested import for non-shared global is allowed while
+ // executing top-level script.
+
+ const win1 = createChromeWindow();
+
+ const result = win1.eval(`
+const { func } = ChromeUtils.importESModule("resource://test/non_shared_nest_import_non_shared_1.mjs", {
+ global: "current",
+});
+func();
+`);
+ Assert.equal(result, 10);
+});
+
+add_task(async function testImportNestNonSharedDifferent() {
+ // For the different globals, nested import for non-shared global isn't
+ // allowed while executing top-level script.
+
+ const win1 = createChromeWindow();
+
+ const uri = "http://example.com/";
+ const window = createContentWindow(uri);
+ const sandboxOpts = {
+ sandboxPrototype: window,
+ wantGlobalProperties: ["ChromeUtils"],
+ };
+ win1.sb = new Cu.Sandbox(uri, sandboxOpts);
+
+ let caught = false;
+ try {
+ win1.eval(`
+ChromeUtils.importESModule("resource://test/non_shared_nest_import_non_shared_2.mjs", {
+ global: "current",
+});
+`);
+ } catch (e) {
+ caught = true;
+ Assert.stringMatches(e.message, /cannot be used for different global/);
+ }
+ Assert.ok(caught);
+});
+
+add_task(async function testImportNestNonSharedAfterImport() {
+ // Nested import for non-shared global is allowed after the import, both for
+ // the same and different globals.
+
+ const win1 = createChromeWindow();
+
+ const uri = "http://example.com/";
+ const window = createContentWindow(uri);
+ const sandboxOpts = {
+ sandboxPrototype: window,
+ wantGlobalProperties: ["ChromeUtils"],
+ };
+ win1.sb = new Cu.Sandbox(uri, sandboxOpts);
+
+ const result = win1.eval(`
+const { func3 } = ChromeUtils.importESModule("resource://test/non_shared_nest_import_non_shared_3.mjs", {
+ global: "current",
+});
+
+// Nested import happens here.
+func3();
+`);
+ Assert.equal(result, 22);
+});
+
+add_task(async function testIsolationWithSandbox() {
+ // Modules should be isolated for each sandbox.
+
+ const uri = "http://example.com/";
+ const window = createContentWindow(uri);
+ const sandboxOpts = {
+ sandboxPrototype: window,
+ wantGlobalProperties: ["ChromeUtils"],
+ };
+ const sb1 = new Cu.Sandbox(uri, sandboxOpts);
+ const sb2 = new Cu.Sandbox(uri, sandboxOpts);
+ const sb3 = new Cu.Sandbox(uri, sandboxOpts);
+
+ // Verify modules in 2 sandboxes are isolated.
+
+ Cu.evalInSandbox(`
+globalThis["loaded"] = [];
+var ns = ChromeUtils.importESModule("resource://test/non_shared_1.mjs", {
+ global: "current",
+});
+`, sb1);
+ Cu.evalInSandbox(`
+globalThis["loaded"] = [];
+var ns = ChromeUtils.importESModule("resource://test/non_shared_1.mjs", {
+ global: "current",
+});
+`, sb2);
+
+ Assert.equal(Cu.evalInSandbox(`ns.getCounter();`, sb1), 0);
+ Cu.evalInSandbox(`ns.incCounter();`, sb1);
+ Assert.equal(Cu.evalInSandbox(`ns.getCounter();`, sb1), 1);
+
+ Assert.equal(Cu.evalInSandbox(`globalThis["loaded"].join(",")`, sb1), "2,1");
+
+ Assert.equal(Cu.evalInSandbox(`ns.getCounter();`, sb2), 0);
+ Cu.evalInSandbox(`ns.incCounter();`, sb2);
+ Assert.equal(Cu.evalInSandbox(`ns.getCounter();`, sb2), 1);
+
+ Assert.equal(Cu.evalInSandbox(`globalThis["loaded"].join(",")`, sb2), "2,1");
+
+ // Verify importing after any modification to different global doesn't affect.
+
+ const ns3 = Cu.evalInSandbox(`
+globalThis["loaded"] = [];
+var ns = ChromeUtils.importESModule("resource://test/non_shared_1.mjs", {
+ global: "current",
+});
+`, sb3);
+
+ Assert.equal(Cu.evalInSandbox(`ns.getCounter();`, sb3), 0);
+ Cu.evalInSandbox(`ns.incCounter();`, sb3);
+ Assert.equal(Cu.evalInSandbox(`ns.getCounter();`, sb3), 1);
+
+ Assert.equal(Cu.evalInSandbox(`globalThis["loaded"].join(",")`, sb3), "2,1");
+
+ // Verify yet another modification are still isolated.
+
+ Assert.equal(Cu.evalInSandbox(`ns.getCounter();`, sb1), 1);
+ Cu.evalInSandbox(`ns.incCounter();`, sb1);
+ Assert.equal(Cu.evalInSandbox(`ns.getCounter();`, sb1), 2);
+
+ Assert.equal(Cu.evalInSandbox(`ns.getCounter();`, sb2), 1);
+ Cu.evalInSandbox(`ns.incCounter();`, sb2);
+ Cu.evalInSandbox(`ns.incCounter();`, sb2);
+ Assert.equal(Cu.evalInSandbox(`ns.getCounter();`, sb2), 3);
+
+ Assert.equal(Cu.evalInSandbox(`ns.getCounter();`, sb3), 1);
+ Cu.evalInSandbox(`ns.incCounter();`, sb3);
+ Cu.evalInSandbox(`ns.incCounter();`, sb3);
+ Cu.evalInSandbox(`ns.incCounter();`, sb3);
+ Assert.equal(Cu.evalInSandbox(`ns.getCounter();`, sb3), 4);
+
+ // Verify the module's `globalThis` points the target global.
+
+ Cu.evalInSandbox(`ns.putCounter();`, sb1);
+ Cu.evalInSandbox(`ns.putCounter();`, sb2);
+ Cu.evalInSandbox(`ns.putCounter();`, sb3);
+
+ const counter1 = Cu.evalInSandbox(`globalThis["counter"]`, sb1);
+ Assert.equal(counter1, 2);
+ const counter2 = Cu.evalInSandbox(`globalThis["counter"]`, sb2);
+ Assert.equal(counter2, 3);
+ const counter3 = Cu.evalInSandbox(`globalThis["counter"]`, sb3);
+ Assert.equal(counter3, 4);
+});
+
+add_task(async function testIsolationWithWindow() {
+ // Modules should be isolated for each window.
+
+ const win1 = createChromeWindow();
+ const win2 = createChromeWindow();
+ const win3 = createChromeWindow();
+
+ // Verify modules in 2 sandboxes are isolated.
+
+ win1.eval(`
+globalThis["loaded"] = [];
+var ns = ChromeUtils.importESModule("resource://test/non_shared_1.mjs", {
+ global: "current",
+});
+`);
+ win2.eval(`
+globalThis["loaded"] = [];
+var ns = ChromeUtils.importESModule("resource://test/non_shared_1.mjs", {
+ global: "current",
+});
+`);
+
+ Assert.equal(win1.eval(`ns.getCounter();`), 0);
+ win1.eval(`ns.incCounter();`);
+ Assert.equal(win1.eval(`ns.getCounter();`), 1);
+
+ Assert.equal(win1.eval(`globalThis["loaded"].join(",")`), "2,1");
+
+ Assert.equal(win2.eval(`ns.getCounter();`), 0);
+ win2.eval(`ns.incCounter();`);
+ Assert.equal(win2.eval(`ns.getCounter();`), 1);
+
+ Assert.equal(win2.eval(`globalThis["loaded"].join(",")`), "2,1");
+
+ // Verify importing after any modification to different global doesn't affect.
+
+ const ns3 = win3.eval(`
+globalThis["loaded"] = [];
+var ns = ChromeUtils.importESModule("resource://test/non_shared_1.mjs", {
+ global: "current",
+});
+`);
+
+ Assert.equal(win3.eval(`ns.getCounter();`), 0);
+ win3.eval(`ns.incCounter();`);
+ Assert.equal(win3.eval(`ns.getCounter();`), 1);
+
+ Assert.equal(win3.eval(`globalThis["loaded"].join(",")`), "2,1");
+
+ // Verify yet another modification are still isolated.
+
+ Assert.equal(win1.eval(`ns.getCounter();`), 1);
+ win1.eval(`ns.incCounter();`);
+ Assert.equal(win1.eval(`ns.getCounter();`), 2);
+
+ Assert.equal(win2.eval(`ns.getCounter();`), 1);
+ win2.eval(`ns.incCounter();`);
+ win2.eval(`ns.incCounter();`);
+ Assert.equal(win2.eval(`ns.getCounter();`), 3);
+
+ Assert.equal(win3.eval(`ns.getCounter();`), 1);
+ win3.eval(`ns.incCounter();`);
+ win3.eval(`ns.incCounter();`);
+ win3.eval(`ns.incCounter();`);
+ Assert.equal(win3.eval(`ns.getCounter();`), 4);
+
+ // Verify the module's `globalThis` points the target global.
+
+ win1.eval(`ns.putCounter();`);
+ win2.eval(`ns.putCounter();`);
+ win3.eval(`ns.putCounter();`);
+
+ const counter1 = win1.eval(`globalThis["counter"]`);
+ Assert.equal(counter1, 2);
+ const counter2 = win2.eval(`globalThis["counter"]`);
+ Assert.equal(counter2, 3);
+ const counter3 = win3.eval(`globalThis["counter"]`);
+ Assert.equal(counter3, 4);
+});
+
+add_task(async function testSyncImportBeforeAsyncImportTopLevel() {
+ const window = createChromeWindow();
+
+ window.eval(`
+globalThis["loaded"] = [];
+var ns = ChromeUtils.importESModule("resource://test/non_shared_1.mjs", {
+ global: "current",
+});
+`);
+
+ Assert.equal(window.eval(`ns.getCounter();`), 0);
+ window.eval(`ns.incCounter();`);
+ Assert.equal(window.eval(`ns.getCounter();`), 1);
+
+ Assert.equal(window.eval(`globalThis["loaded"].join(",")`), "2,1");
+
+ window.eval(`
+var ns2 = null;
+const nsPromise = import("resource://test/non_shared_1.mjs");
+nsPromise.then(v => { ns2 = v; });
+`);
+
+ Services.tm.spinEventLoopUntil(
+ "Wait until dynamic import finishes",
+ () => window.eval(`ns2 !== null`)
+ );
+
+ Assert.equal(window.eval(`ns2.getCounter();`), 1);
+ window.eval(`ns2.incCounter();`);
+ Assert.equal(window.eval(`ns2.getCounter();`), 2);
+ Assert.equal(window.eval(`ns.getCounter();`), 2);
+
+ Assert.equal(window.eval(`globalThis["loaded"].join(",")`), "2,1");
+});
+
+add_task(async function testSyncImportBeforeAsyncImportDependency() {
+ const window = createChromeWindow();
+
+ window.eval(`
+globalThis["loaded"] = [];
+var ns = ChromeUtils.importESModule("resource://test/non_shared_1.mjs", {
+ global: "current",
+});
+`);
+
+ Assert.equal(window.eval(`ns.getCounter();`), 0);
+ window.eval(`ns.incCounter();`);
+ Assert.equal(window.eval(`ns.getCounter();`), 1);
+
+ Assert.equal(window.eval(`globalThis["loaded"].join(",")`), "2,1");
+
+ window.eval(`
+var ns2 = null;
+const nsPromise = import("resource://test/import_non_shared_1.mjs");
+nsPromise.then(v => { ns2 = v; });
+`);
+
+ Services.tm.spinEventLoopUntil(
+ "Wait until dynamic import finishes",
+ () => window.eval(`ns2 !== null`)
+ );
+
+ Assert.equal(window.eval(`ns2.getCounter();`), 1);
+ window.eval(`ns2.incCounter();`);
+ Assert.equal(window.eval(`ns2.getCounter();`), 2);
+ Assert.equal(window.eval(`ns.getCounter();`), 2);
+
+ Assert.equal(window.eval(`globalThis["loaded"].join(",")`), "2,1");
+});
+
+add_task(async function testSyncImportAfterAsyncImportTopLevel() {
+ const window = createChromeWindow();
+
+ window.eval(`
+var ns = null;
+globalThis["loaded"] = [];
+const nsPromise = import("resource://test/non_shared_1.mjs");
+nsPromise.then(v => { ns = v; });
+`);
+
+ Services.tm.spinEventLoopUntil(
+ "Wait until dynamic import finishes",
+ () => window.eval(`ns !== null`)
+ );
+
+ Assert.equal(window.eval(`ns.getCounter();`), 0);
+ window.eval(`ns.incCounter();`);
+ Assert.equal(window.eval(`ns.getCounter();`), 1);
+
+ Assert.equal(window.eval(`globalThis["loaded"].join(",")`), "2,1");
+
+ window.eval(`
+var ns2 = ChromeUtils.importESModule("resource://test/non_shared_1.mjs", {
+ global: "current",
+});
+`);
+
+ Assert.equal(window.eval(`ns2.getCounter();`), 1);
+ window.eval(`ns2.incCounter();`);
+ Assert.equal(window.eval(`ns2.getCounter();`), 2);
+ Assert.equal(window.eval(`ns.getCounter();`), 2);
+
+ Assert.equal(window.eval(`globalThis["loaded"].join(",")`), "2,1");
+});
+
+add_task(async function testSyncImportAfterAsyncImportDependency() {
+ const window = createChromeWindow();
+
+ window.eval(`
+var ns = null;
+globalThis["loaded"] = [];
+const nsPromise = import("resource://test/non_shared_1.mjs");
+nsPromise.then(v => { ns = v; });
+`);
+
+ Services.tm.spinEventLoopUntil(
+ "Wait until dynamic import finishes",
+ () => window.eval(`ns !== null`)
+ );
+
+ Assert.equal(window.eval(`ns.getCounter();`), 0);
+ window.eval(`ns.incCounter();`);
+ Assert.equal(window.eval(`ns.getCounter();`), 1);
+
+ Assert.equal(window.eval(`globalThis["loaded"].join(",")`), "2,1");
+
+ window.eval(`
+var ns2 = ChromeUtils.importESModule("resource://test/import_non_shared_1.mjs", {
+ global: "current",
+});
+`);
+
+ Assert.equal(window.eval(`ns2.getCounter();`), 1);
+ window.eval(`ns2.incCounter();`);
+ Assert.equal(window.eval(`ns2.getCounter();`), 2);
+ Assert.equal(window.eval(`ns.getCounter();`), 2);
+
+ Assert.equal(window.eval(`globalThis["loaded"].join(",")`), "2,1");
+});
+
+add_task(async function testSyncImportWhileAsyncImportTopLevel() {
+ const window = createChromeWindow();
+
+ window.eval(`
+var ns = null;
+globalThis["loaded"] = [];
+const nsPromise = import("resource://test/non_shared_1.mjs");
+nsPromise.then(v => { ns = v; });
+`);
+
+ window.eval(`
+var ns2 = ChromeUtils.importESModule("resource://test/non_shared_1.mjs", {
+ global: "current",
+});
+`);
+
+ Assert.equal(window.eval(`ns2.getCounter();`), 0);
+ window.eval(`ns2.incCounter();`);
+ Assert.equal(window.eval(`ns2.getCounter();`), 1);
+
+ Services.tm.spinEventLoopUntil(
+ "Wait until dynamic import finishes",
+ () => window.eval(`ns !== null`)
+ );
+
+ Assert.equal(window.eval(`ns.getCounter();`), 1);
+ window.eval(`ns.incCounter();`);
+ Assert.equal(window.eval(`ns.getCounter();`), 2);
+ Assert.equal(window.eval(`ns2.getCounter();`), 2);
+
+ Assert.equal(window.eval(`globalThis["loaded"].join(",")`), "2,1");
+});
+
+add_task(async function testSyncImportWhileAsyncImportDependency() {
+ const window = createChromeWindow();
+
+ window.eval(`
+var ns = null;
+globalThis["loaded"] = [];
+const nsPromise = import("resource://test/non_shared_1.mjs");
+nsPromise.then(v => { ns = v; });
+`);
+
+ window.eval(`
+var ns2 = ChromeUtils.importESModule("resource://test/import_non_shared_1.mjs", {
+ global: "current",
+});
+`);
+
+ Assert.equal(window.eval(`ns2.getCounter();`), 0);
+ window.eval(`ns2.incCounter();`);
+ Assert.equal(window.eval(`ns2.getCounter();`), 1);
+
+ Services.tm.spinEventLoopUntil(
+ "Wait until dynamic import finishes",
+ () => window.eval(`ns !== null`)
+ );
+
+ Assert.equal(window.eval(`ns.getCounter();`), 1);
+ window.eval(`ns.incCounter();`);
+ Assert.equal(window.eval(`ns.getCounter();`), 2);
+ Assert.equal(window.eval(`ns2.getCounter();`), 2);
+
+ Assert.equal(window.eval(`globalThis["loaded"].join(",")`), "2,1");
+});
+
+add_task(async function testSyncImportBeforeAsyncImportTLA() {
+ // Top-level-await is not supported by sync import.
+
+ const window = createChromeWindow();
+
+ let caught = false;
+
+ try {
+ window.eval(`
+ChromeUtils.importESModule("resource://test/es6module_top_level_await.js", {
+ global: "current",
+});
+`);
+ } catch (e) {
+ caught = true;
+ Assert.stringMatches(e.message, /top level await is not supported/);
+ }
+ Assert.ok(caught);
+
+ window.eval(`
+var ns2 = null;
+const nsPromise = import("resource://test/es6module_top_level_await.js");
+nsPromise.then(v => { ns2 = v; });
+`);
+
+ Services.tm.spinEventLoopUntil(
+ "Wait until dynamic import finishes",
+ () => window.eval(`ns2 !== null`)
+ );
+
+ Assert.equal(window.eval(`ns2.foo();`), 10);
+});
+
+add_task(async function testSyncImportAfterAsyncImportTLA() {
+ // Top-level-await is not supported by sync import, but if the module is
+ // already imported, the existing module namespace is returned.
+
+ const window = createChromeWindow();
+
+ window.eval(`
+var ns2 = null;
+const nsPromise = import("resource://test/es6module_top_level_await.js");
+nsPromise.then(v => { ns2 = v; });
+`);
+
+ Services.tm.spinEventLoopUntil(
+ "Wait until dynamic import finishes",
+ () => window.eval(`ns2 !== null`)
+ );
+
+ Assert.equal(window.eval(`ns2.foo();`), 10);
+
+ window.eval(`
+var ns = ChromeUtils.importESModule("resource://test/es6module_top_level_await.js", {
+ global: "current",
+});
+`);
+
+ Assert.equal(window.eval(`ns.foo();`), 10);
+ Assert.equal(window.eval(`ns2.foo == ns.foo;`), true);
+});
+
+add_task(async function testSyncImportWhileAsyncImportTLA() {
+ // Top-level-await is not supported by sync import, but if the module is
+ // already fetching, ChromeUtils.importESModule waits for it and, the
+ // async-imported module namespace is returned.
+
+ const window = createChromeWindow();
+
+ window.eval(`
+var ns2 = null;
+const nsPromise = import("resource://test/es6module_top_level_await.js");
+nsPromise.then(v => { ns2 = v; });
+`);
+
+ window.eval(`
+var ns = ChromeUtils.importESModule("resource://test/es6module_top_level_await.js", {
+ global: "current",
+});
+`);
+
+ Services.tm.spinEventLoopUntil(
+ "Wait until dynamic import finishes",
+ () => window.eval(`ns2 !== null`)
+ );
+
+ Assert.equal(window.eval(`ns2.foo();`), 10);
+ Assert.equal(window.eval(`ns.foo();`), 10);
+ Assert.equal(window.eval(`ns2.foo == ns.foo;`), true);
+});
diff --git a/js/xpconnect/tests/unit/test_import_global_current_worker.js b/js/xpconnect/tests/unit/test_import_global_current_worker.js
new file mode 100644
index 0000000000..1eb28e8325
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_import_global_current_worker.js
@@ -0,0 +1,196 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+add_task(async function testWorker() {
+ const win1 = createChromeWindow();
+
+ const worker = new ChromeWorker("resource://test/non_shared_worker_1.js");
+ const { promise, resolve } = Promise.withResolvers();
+ worker.onmessage = event => {
+ resolve(event.data);
+ };
+ worker.postMessage("");
+
+ const result = await promise;
+
+ Assert.equal(result.c1, 0);
+ Assert.equal(result.c2, 1);
+ Assert.equal(result.loaded, "2,1");
+});
+
+add_task(async function testSyncImportBeforeAsyncImportTopLevelInWorker() {
+ const window = createChromeWindow();
+
+ let worker = new ChromeWorker("resource://test/sync_and_async_in_worker.js");
+ let { promise, resolve } = Promise.withResolvers();
+ worker.onmessage = event => {
+ resolve(event.data);
+ };
+ worker.postMessage({ order: "sync-before-async", target: "top-level" });
+
+ const {
+ sync_beforeInc,
+ sync_afterInc,
+ sync_afterIncInc,
+ async_beforeInc,
+ async_afterInc,
+ loaded1,
+ loaded2,
+ } = await promise;
+
+ Assert.equal(sync_beforeInc, 0);
+ Assert.equal(sync_afterInc, 1);
+
+ Assert.equal(loaded1, "2,1");
+
+ Assert.equal(async_beforeInc, 1);
+ Assert.equal(async_afterInc, 2);
+ Assert.equal(sync_afterIncInc, 2);
+
+ Assert.equal(loaded2, "2,1");
+});
+
+add_task(async function testSyncImportBeforeAsyncImportDependencyInWorker() {
+ let worker = new ChromeWorker("resource://test/sync_and_async_in_worker.js");
+ let { promise, resolve } = Promise.withResolvers();
+ worker.onmessage = event => {
+ resolve(event.data);
+ };
+ worker.postMessage({ order: "sync-before-async", target: "dependency" });
+
+ const {
+ sync_beforeInc,
+ sync_afterInc,
+ sync_afterIncInc,
+ async_beforeInc,
+ async_afterInc,
+ loaded1,
+ loaded2,
+ } = await promise;
+
+ Assert.equal(sync_beforeInc, 0);
+ Assert.equal(sync_afterInc, 1);
+
+ Assert.equal(loaded1, "2,1");
+
+ Assert.equal(async_beforeInc, 1);
+ Assert.equal(async_afterInc, 2);
+ Assert.equal(sync_afterIncInc, 2);
+
+ Assert.equal(loaded2, "2,1");
+});
+
+add_task(async function testSyncImportAfterAsyncImportTopLevelInWorker() {
+ const window = createChromeWindow();
+
+ let worker = new ChromeWorker("resource://test/sync_and_async_in_worker.js");
+ let { promise, resolve } = Promise.withResolvers();
+ worker.onmessage = event => {
+ resolve(event.data);
+ };
+ worker.postMessage({ order: "sync-after-async", target: "top-level" });
+
+ const {
+ sync_beforeInc,
+ sync_afterInc,
+ async_beforeInc,
+ async_afterInc,
+ async_afterIncInc,
+ loaded1,
+ loaded2,
+ } = await promise;
+
+ Assert.equal(async_beforeInc, 0);
+ Assert.equal(async_afterInc, 1);
+
+ Assert.equal(loaded1, "2,1");
+
+ Assert.equal(sync_beforeInc, 1);
+ Assert.equal(sync_afterInc, 2);
+ Assert.equal(async_afterIncInc, 2);
+
+ Assert.equal(loaded2, "2,1");
+});
+
+add_task(async function testSyncImportAfterAsyncImportDependencyInWorker() {
+ const window = createChromeWindow();
+
+ let worker = new ChromeWorker("resource://test/sync_and_async_in_worker.js");
+ let { promise, resolve } = Promise.withResolvers();
+ worker.onmessage = event => {
+ resolve(event.data);
+ };
+ worker.postMessage({ order: "sync-after-async", target: "dependency" });
+
+ const {
+ sync_beforeInc,
+ sync_afterInc,
+ async_beforeInc,
+ async_afterInc,
+ async_afterIncInc,
+ loaded1,
+ loaded2,
+ } = await promise;
+
+ Assert.equal(async_beforeInc, 0);
+ Assert.equal(async_afterInc, 1);
+
+ Assert.equal(loaded1, "2,1");
+
+ Assert.equal(sync_beforeInc, 1);
+ Assert.equal(sync_afterInc, 2);
+ Assert.equal(async_afterIncInc, 2);
+
+ Assert.equal(loaded2, "2,1");
+});
+
+add_task(async function testSyncImportWhileAsyncImportTopLevelInWorker() {
+ const window = createChromeWindow();
+
+ let worker = new ChromeWorker("resource://test/sync_and_async_in_worker.js");
+ let { promise, resolve } = Promise.withResolvers();
+ worker.onmessage = event => {
+ resolve(event.data);
+ };
+ worker.postMessage({ order: "sync-while-async", target: "top-level" });
+
+ const {
+ sync_error,
+ async_beforeInc,
+ async_afterInc,
+ loaded,
+ } = await promise;
+
+ Assert.stringMatches(sync_error, /ChromeUtils.importESModule cannot be used/);
+
+ Assert.equal(async_beforeInc, 0);
+ Assert.equal(async_afterInc, 1);
+
+ Assert.equal(loaded, "2,1");
+});
+
+add_task(async function testSyncImportWhileAsyncImportDependencyInWorker() {
+ const window = createChromeWindow();
+
+ let worker = new ChromeWorker("resource://test/sync_and_async_in_worker.js");
+ let { promise, resolve } = Promise.withResolvers();
+ worker.onmessage = event => {
+ resolve(event.data);
+ };
+ worker.postMessage({ order: "sync-while-async", target: "dependency" });
+
+ const {
+ sync_error,
+ async_beforeInc,
+ async_afterInc,
+ loaded,
+ } = await promise;
+
+ Assert.stringMatches(sync_error, /ChromeUtils.importESModule cannot be used/);
+
+ Assert.equal(async_beforeInc, 0);
+ Assert.equal(async_afterInc, 1);
+
+ Assert.equal(loaded, "2,1");
+});
diff --git a/js/xpconnect/tests/unit/test_import_global_worker.js b/js/xpconnect/tests/unit/test_import_global_worker.js
new file mode 100644
index 0000000000..16359a4da4
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_import_global_worker.js
@@ -0,0 +1,21 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+add_task(async function testSharedInWorker() {
+ // Loading into shared global isn't allowed in worker.
+
+ let worker = new ChromeWorker("resource://test/import_shared_in_worker.js");
+ let { promise, resolve } = Promise.withResolvers();
+ worker.onmessage = event => {
+ resolve(event.data);
+ };
+ worker.postMessage("");
+
+ const result = await promise;
+
+ Assert.equal(result.caught1, true);
+ Assert.equal(result.caught2, true);
+ Assert.equal(result.caught3, true);
+ Assert.equal(result.caught4, true);
+});
diff --git a/js/xpconnect/tests/unit/test_import_shim.js b/js/xpconnect/tests/unit/test_import_shim.js
new file mode 100644
index 0000000000..3e265414f8
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_import_shim.js
@@ -0,0 +1,377 @@
+add_task(function test_Cu_import_shim_first() {
+ // Load and cache with shim.
+
+ const exports = {};
+ const global = Components.utils.import(
+ "resource://test/esmified-1.jsm",
+ exports
+ );
+ Assert.equal(global.loadCount, 1);
+ Assert.equal(global.obj.value, 10);
+ Assert.equal(exports.loadCount, 1);
+ Assert.equal(exports.obj.value, 10);
+ Assert.ok(exports.obj === global.obj);
+
+ const ns = ChromeUtils.importESModule("resource://test/esmified-1.sys.mjs");
+ Assert.equal(ns.loadCount, 1);
+ Assert.equal(ns.obj.value, 10);
+ Assert.ok(ns.obj === global.obj);
+
+ const exports2 = {};
+ const global2 = Components.utils.import(
+ "resource://test/esmified-1.jsm", exports2
+ );
+ Assert.equal(global2.loadCount, 1);
+ Assert.equal(global2.obj.value, 10);
+ Assert.equal(exports2.loadCount, 1);
+ Assert.equal(exports2.obj.value, 10);
+ Assert.ok(exports2.obj === global2.obj);
+ Assert.ok(exports2.obj === global.obj);
+
+ // Also test with *.js extension.
+ const exports3 = {};
+ const global3 = Components.utils.import(
+ "resource://test/esmified-1.js", exports3
+ );
+ Assert.equal(global3.loadCount, 1);
+ Assert.equal(global3.obj.value, 10);
+ Assert.equal(exports3.loadCount, 1);
+ Assert.equal(exports3.obj.value, 10);
+ Assert.ok(exports3.obj === global3.obj);
+ Assert.ok(exports3.obj === global.obj);
+
+ // Also test with *.jsm.js extension.
+ const exports4 = {};
+ const global4 = Components.utils.import(
+ "resource://test/esmified-1.js", exports4
+ );
+ Assert.equal(global4.loadCount, 1);
+ Assert.equal(global4.obj.value, 10);
+ Assert.equal(exports4.loadCount, 1);
+ Assert.equal(exports4.obj.value, 10);
+ Assert.ok(exports4.obj === global4.obj);
+ Assert.ok(exports4.obj === global.obj);
+});
+
+add_task(function test_Cu_import_no_shim_first() {
+ // Load and cache with importESModule.
+
+ const ns = ChromeUtils.importESModule("resource://test/esmified-2.sys.mjs");
+ Assert.equal(ns.loadCount, 1);
+ Assert.equal(ns.obj.value, 10);
+
+ const exports = {};
+ const global = Components.utils.import(
+ "resource://test/esmified-2.jsm", exports
+ );
+ Assert.equal(global.loadCount, 1);
+ Assert.equal(global.obj.value, 10);
+ Assert.equal(exports.loadCount, 1);
+ Assert.equal(exports.obj.value, 10);
+ Assert.ok(exports.obj === global.obj);
+ Assert.ok(ns.obj === global.obj);
+
+ const ns2 = ChromeUtils.importESModule("resource://test/esmified-2.sys.mjs");
+ Assert.equal(ns2.loadCount, 1);
+ Assert.equal(ns2.obj.value, 10);
+});
+
+add_task(function test_ChromeUtils_import_shim_first() {
+ // Load and cache with shim.
+
+ const exports = {};
+ const global = ChromeUtils.import(
+ "resource://test/esmified-3.jsm", exports
+ );
+ Assert.equal(global.loadCount, 1);
+ Assert.equal(global.obj.value, 10);
+ Assert.equal(exports.loadCount, 1);
+ Assert.equal(exports.obj.value, 10);
+ Assert.ok(exports.obj === global.obj);
+
+ const ns = ChromeUtils.importESModule("resource://test/esmified-3.sys.mjs");
+ Assert.equal(ns.loadCount, 1);
+ Assert.equal(ns.obj.value, 10);
+ Assert.ok(ns.obj === global.obj);
+
+ const exports2 = {};
+ const global2 = ChromeUtils.import(
+ "resource://test/esmified-3.jsm", exports2
+ );
+ Assert.equal(global2.loadCount, 1);
+ Assert.equal(global2.obj.value, 10);
+ Assert.equal(exports2.loadCount, 1);
+ Assert.equal(exports2.obj.value, 10);
+ Assert.ok(exports2.obj === global2.obj);
+ Assert.ok(exports2.obj === global.obj);
+});
+
+add_task(function test_ChromeUtils_import_no_shim_first() {
+ // Load and cache with importESModule.
+
+ const ns = ChromeUtils.importESModule("resource://test/esmified-4.sys.mjs");
+ Assert.equal(ns.loadCount, 1);
+ Assert.equal(ns.obj.value, 10);
+
+ const exports = {};
+ const global = ChromeUtils.import(
+ "resource://test/esmified-4.jsm", exports
+ );
+ Assert.equal(global.loadCount, 1);
+ Assert.equal(global.obj.value, 10);
+ Assert.equal(exports.loadCount, 1);
+ Assert.equal(exports.obj.value, 10);
+ Assert.ok(exports.obj === global.obj);
+ Assert.ok(ns.obj === global.obj);
+
+ const ns2 = ChromeUtils.importESModule("resource://test/esmified-4.sys.mjs");
+ Assert.equal(ns2.loadCount, 1);
+ Assert.equal(ns2.obj.value, 10);
+});
+
+add_task(function test_ChromeUtils_import_not_exported_no_shim_JSM() {
+ // `exports` properties for not-ESM-ified case.
+
+ const exports = ChromeUtils.import(
+ "resource://test/not-esmified-not-exported.jsm"
+ );
+
+ Assert.equal(exports.exportedVar, "exported var");
+ Assert.equal(exports.exportedFunction(), "exported function");
+ Assert.equal(exports.exportedLet, "exported let");
+ Assert.equal(exports.exportedConst, "exported const");
+ Assert.equal(exports.notExportedVar, undefined);
+ Assert.equal(exports.notExportedFunction, undefined);
+ Assert.equal(exports.notExportedLet, undefined);
+ Assert.equal(exports.notExportedConst, undefined);
+});
+
+add_task(function test_ChromeUtils_import_not_exported_shim() {
+ // `exports` properties for shim case.
+
+ const exports = ChromeUtils.import(
+ "resource://test/esmified-not-exported.jsm"
+ );
+
+ Assert.equal(exports.exportedVar, "exported var");
+ Assert.equal(exports.exportedFunction(), "exported function");
+ Assert.equal(exports.exportedLet, "exported let");
+ Assert.equal(exports.exportedConst, "exported const");
+ Assert.equal(exports.notExportedVar, undefined);
+ Assert.equal(exports.notExportedFunction, undefined);
+ Assert.equal(exports.notExportedLet, undefined);
+ Assert.equal(exports.notExportedConst, undefined);
+});
+
+add_task(function test_ChromeUtils_import_not_exported_no_shim_ESM() {
+ // `exports` properties for ESM-ified case.
+
+ const exports = ChromeUtils.importESModule(
+ "resource://test/esmified-not-exported.sys.mjs"
+ );
+
+ Assert.equal(exports.exportedVar, "exported var");
+ Assert.equal(exports.exportedFunction(), "exported function");
+ Assert.equal(exports.exportedLet, "exported let");
+ Assert.equal(exports.exportedConst, "exported const");
+ Assert.equal(exports.notExportedVar, undefined);
+ Assert.equal(exports.notExportedFunction, undefined);
+ Assert.equal(exports.notExportedLet, undefined);
+ Assert.equal(exports.notExportedConst, undefined);
+});
+
+function testReadProxyOps(global, expectedNames, expectedDesc) {
+ expectedNames.sort();
+
+ // enumerate
+ const names = Object.keys(global).sort();
+ Assert.equal(JSON.stringify(names), JSON.stringify(expectedNames),
+ `enumerate`);
+
+ // has
+ for (const name of expectedNames) {
+ Assert.ok(name in global, `has for ${name}`);
+ }
+
+ // getOwnPropertyDescriptor
+ for (const name of expectedNames) {
+ const desc = Object.getOwnPropertyDescriptor(global, name);
+ Assert.equal(desc.value, global[name]);
+ Assert.equal(desc.writable, expectedDesc.writable,
+ `writable for ${name}`);
+ Assert.equal(desc.enumerable, expectedDesc.enumerable,
+ `enumerable for ${name}`);
+ Assert.equal(desc.configurable, expectedDesc.configurable,
+ `configurable for ${name}`);
+ }
+}
+
+function testWriteProxyOps(global, expectedNames) {
+ // set: no-op
+ for (const name of expectedNames) {
+ const before = global[name];
+ global[name] = -1;
+ Assert.equal(global[name], before, `value after set for ${name}`);
+ }
+
+ // delete: no-op
+ for (const name of expectedNames) {
+ const before = global[name];
+ Assert.ok(!(delete global[name]), `delete result for ${name}`);
+ Assert.equal(global[name], before, `value after delete for ${name}`);
+ }
+}
+
+add_task(function test_Cu_import_not_exported_no_shim_JSM() {
+ // `exports` and `global` properties for not-ESM-ified case.
+ // Not-exported variables should be visible in `global`.
+
+ const exports = {};
+ const global = Components.utils.import(
+ "resource://test/not-esmified-not-exported.jsm",
+ exports
+ );
+
+ Assert.equal(global.exportedVar, "exported var");
+ Assert.equal(global.exportedFunction(), "exported function");
+ Assert.equal(global.exportedLet, "exported let");
+ Assert.equal(global.exportedConst, "exported const");
+ Assert.equal(global.notExportedVar, "not exported var");
+ Assert.equal(global.notExportedFunction(), "not exported function");
+ Assert.equal(global.notExportedLet, "not exported let");
+ Assert.equal(global.notExportedConst, "not exported const");
+
+ const expectedNames = [
+ "EXPORTED_SYMBOLS",
+ "exportedVar",
+ "exportedFunction",
+ "exportedLet",
+ "exportedConst",
+ "notExportedVar",
+ "notExportedFunction",
+ "notExportedLet",
+ "notExportedConst",
+ ];
+
+ testReadProxyOps(global, expectedNames, {
+ writable: false,
+ enumerable: true,
+ configurable: false,
+ });
+ testWriteProxyOps(global, expectedNames);
+
+ Assert.equal(exports.exportedVar, "exported var");
+ Assert.equal(exports.exportedFunction(), "exported function");
+ Assert.equal(exports.exportedLet, "exported let");
+ Assert.equal(exports.exportedConst, "exported const");
+ Assert.equal(exports.notExportedVar, undefined);
+ Assert.equal(exports.notExportedFunction, undefined);
+ Assert.equal(exports.notExportedLet, undefined);
+ Assert.equal(exports.notExportedConst, undefined);
+});
+
+add_task(function test_Cu_import_not_exported_shim() {
+ // `exports` and `global` properties for shim case.
+ // Not-exported variables should be visible in global.
+
+ const exports = {};
+ const global = Components.utils.import(
+ "resource://test/esmified-not-exported.jsm",
+ exports
+ );
+
+ Assert.equal(global.exportedVar, "exported var");
+ Assert.equal(global.exportedFunction(), "exported function");
+ Assert.equal(global.exportedLet, "exported let");
+ Assert.equal(global.exportedConst, "exported const");
+
+ Assert.equal(global.notExportedVar, "not exported var");
+ Assert.equal(global.notExportedFunction(), "not exported function");
+ Assert.equal(global.notExportedLet, "not exported let");
+ Assert.equal(global.notExportedConst, "not exported const");
+
+ const expectedNames = [
+ "exportedVar",
+ "exportedFunction",
+ "exportedLet",
+ "exportedConst",
+ "notExportedVar",
+ "notExportedFunction",
+ "notExportedLet",
+ "notExportedConst",
+ ];
+
+ testReadProxyOps(global, expectedNames, {
+ writable: false,
+ enumerable: true,
+ configurable: false,
+ });
+ testWriteProxyOps(global, expectedNames);
+
+ Assert.equal(exports.exportedVar, "exported var");
+ Assert.equal(exports.exportedFunction(), "exported function");
+ Assert.equal(exports.exportedLet, "exported let");
+ Assert.equal(exports.exportedConst, "exported const");
+ Assert.equal(exports.notExportedVar, undefined);
+ Assert.equal(exports.notExportedFunction, undefined);
+ Assert.equal(exports.notExportedLet, undefined);
+ Assert.equal(exports.notExportedConst, undefined);
+
+ const desc = Object.getOwnPropertyDescriptor(global, "*namespace*");
+ Assert.ok(!desc, `*namespace* special binding should not be exposed`);
+ Assert.equal("*namespace*" in global, false,
+ `*namespace* special binding should not be exposed`);
+ Assert.equal(global["*namespace*"], undefined,
+ `*namespace* special binding should not be exposed`);
+});
+
+add_task(function test_Cu_isModuleLoaded_shim() {
+ Assert.equal(Cu.isModuleLoaded("resource://test/esmified-5.jsm"), false);
+ Assert.equal(Cu.isModuleLoaded("resource://test/esmified-5.js"), false);
+ Assert.equal(Cu.isModuleLoaded("resource://test/esmified-5.jsm.js"), false);
+ Assert.equal(Cu.loadedModules.includes("resource://test/esmified-5.jsm"), false);
+ Assert.equal(Cu.isModuleLoaded("resource://test/esmified-5.sys.mjs"), false);
+ Assert.equal(Cu.loadedModules.includes("resource://test/esmified-5.sys.mjs"), false);
+
+ Cu.import("resource://test/esmified-5.jsm", {});
+
+ Assert.equal(Cu.isModuleLoaded("resource://test/esmified-5.jsm"), true);
+ Assert.equal(Cu.isModuleLoaded("resource://test/esmified-5.js"), true);
+ Assert.equal(Cu.isModuleLoaded("resource://test/esmified-5.jsm.js"), true);
+ Assert.equal(Cu.loadedModules.includes("resource://test/esmified-5.jsm"), true);
+
+ // This is false because Cu.isModuleLoaded does not support ESM directly
+ // (bug 1768819)
+ Assert.equal(Cu.isModuleLoaded("resource://test/esmified-5.sys.mjs"), false);
+ Assert.equal(Cu.loadedModules.includes("resource://test/esmified-5.sys.mjs"), false);
+});
+
+add_task(function test_Cu_isModuleLoaded_no_shim() {
+ Assert.equal(Cu.isModuleLoaded("resource://test/esmified-6.jsm"), false);
+ Assert.equal(Cu.isModuleLoaded("resource://test/esmified-6.js"), false);
+ Assert.equal(Cu.isModuleLoaded("resource://test/esmified-6.jsm.js"), false);
+ Assert.equal(Cu.loadedModules.includes("resource://test/esmified-6.jsm"), false);
+ Assert.equal(Cu.loadedModules.includes("resource://test/esmified-6.js"), false);
+ Assert.equal(Cu.loadedModules.includes("resource://test/esmified-6.jsm.js"), false);
+ Assert.equal(Cu.isModuleLoaded("resource://test/esmified-6.sys.mjs"), false);
+ Assert.equal(Cu.loadedModules.includes("resource://test/esmified-6.sys.mjs"), false);
+
+ ChromeUtils.importESModule("resource://test/esmified-6.sys.mjs");
+
+ // Regardless of whether the ESM is loaded by shim or not,
+ // query that accesses the ESM-ified module returns the existence of
+ // ESM.
+ Assert.equal(Cu.isModuleLoaded("resource://test/esmified-6.jsm"), true);
+ Assert.equal(Cu.isModuleLoaded("resource://test/esmified-6.js"), true);
+ Assert.equal(Cu.isModuleLoaded("resource://test/esmified-6.jsm.js"), true);
+ Assert.equal(Cu.loadedModules.includes("resource://test/esmified-6.jsm"), true);
+
+ // This is false because shim always use *.jsm.
+ Assert.equal(Cu.loadedModules.includes("resource://test/esmified-6.js"), false);
+ Assert.equal(Cu.loadedModules.includes("resource://test/esmified-6.jsm.js"), false);
+
+ // This is false because Cu.isModuleLoaded does not support ESM directly
+ // (bug 1768819)
+ Assert.equal(Cu.isModuleLoaded("resource://test/esmified-6.sys.mjs"), false);
+ Assert.equal(Cu.loadedModules.includes("resource://test/esmified-6.sys.mjs"), false);
+});
diff --git a/js/xpconnect/tests/unit/test_import_stack.js b/js/xpconnect/tests/unit/test_import_stack.js
new file mode 100644
index 0000000000..2fa35a7502
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_import_stack.js
@@ -0,0 +1,39 @@
+Services.prefs.setBoolPref("browser.startup.record", true);
+registerCleanupFunction(() => {
+ Services.prefs.clearUserPref("browser.startup.record");
+});
+
+add_task(function test_JSModule() {
+ const URL = "resource://test/import_stack.jsm";
+ ChromeUtils.import(URL);
+ Assert.ok(Cu.getModuleImportStack(URL).includes("test_JSModule"));
+});
+
+add_task(function test_ESModule() {
+ const URL = "resource://test/import_stack.sys.mjs";
+ ChromeUtils.importESModule(URL);
+ Assert.ok(Cu.getModuleImportStack(URL).includes("test_ESModule"));
+});
+
+add_task(function test_ESModule_static_import() {
+ const URL1 = "resource://test/import_stack_static_1.sys.mjs";
+ const URL2 = "resource://test/import_stack_static_2.sys.mjs";
+ const URL3 = "resource://test/import_stack_static_3.sys.mjs";
+ const URL4 = "resource://test/import_stack_static_4.sys.mjs";
+
+ ChromeUtils.importESModule(URL1);
+
+ Assert.ok(Cu.getModuleImportStack(URL1).includes("test_ESModule_static"));
+
+ Assert.ok(Cu.getModuleImportStack(URL2).includes("test_ESModule_static"));
+ Assert.ok(Cu.getModuleImportStack(URL2).includes(URL1));
+
+ Assert.ok(Cu.getModuleImportStack(URL3).includes("test_ESModule_static"));
+ Assert.ok(Cu.getModuleImportStack(URL3).includes(URL1));
+ Assert.ok(Cu.getModuleImportStack(URL3).includes(URL2));
+
+ Assert.ok(Cu.getModuleImportStack(URL4).includes("test_ESModule_static"));
+ Assert.ok(Cu.getModuleImportStack(URL4).includes(URL1));
+ Assert.ok(Cu.getModuleImportStack(URL4).includes(URL2));
+ Assert.ok(Cu.getModuleImportStack(URL4).includes(URL3));
+});
diff --git a/js/xpconnect/tests/unit/test_import_syntax_error.js b/js/xpconnect/tests/unit/test_import_syntax_error.js
new file mode 100644
index 0000000000..bfdcaf9e04
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_import_syntax_error.js
@@ -0,0 +1,23 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+
+add_task(async function() {
+ Assert.throws(
+ () => ChromeUtils.import("resource://test/error_import.sys.mjs"),
+ /use ChromeUtils.importESModule instead/,
+ "Error should be caught and suggest ChromeUtils.importESModule"
+ );
+
+ Assert.throws(
+ () => ChromeUtils.import("resource://test/error_export.sys.mjs"),
+ /use ChromeUtils.importESModule instead/,
+ "Error should be caught and suggest ChromeUtils.importESModule"
+ );
+
+ Assert.throws(
+ () => ChromeUtils.import("resource://test/error_other.sys.mjs"),
+ /expected expression, got end of script/,
+ "Error should be caught but should not suggest ChromeUtils.importESModule"
+ );
+});
diff --git a/js/xpconnect/tests/unit/test_isModuleLoaded.js b/js/xpconnect/tests/unit/test_isModuleLoaded.js
new file mode 100644
index 0000000000..aaf67c13c7
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_isModuleLoaded.js
@@ -0,0 +1,20 @@
+function run_test() {
+ // Existing module.
+ Assert.ok(!Cu.isModuleLoaded("resource://test/jsm_loaded-1.jsm"),
+ "isModuleLoaded returned correct value for non-loaded module");
+ ChromeUtils.import("resource://test/jsm_loaded-1.jsm");
+ Assert.ok(Cu.isModuleLoaded("resource://test/jsm_loaded-1.jsm"),
+ "isModuleLoaded returned true after loading that module");
+ Cu.unload("resource://test/jsm_loaded-1.jsm");
+ Assert.ok(!Cu.isModuleLoaded("resource://test/jsm_loaded-1.jsm"),
+ "isModuleLoaded returned false after unloading that module");
+
+ // Non-existing module
+ Assert.ok(!Cu.isModuleLoaded("resource://gre/modules/non-existing-module.jsm"),
+ "isModuleLoaded returned correct value for non-loaded module");
+ Assert.throws(
+ () => ChromeUtils.import("resource://gre/modules/non-existing-module.jsm"),
+ /NS_ERROR_FILE_NOT_FOUND/,
+ "Should have thrown while trying to load a non existing file"
+ );
+}
diff --git a/js/xpconnect/tests/unit/test_isProxy.js b/js/xpconnect/tests/unit/test_isProxy.js
new file mode 100644
index 0000000000..996aa320b9
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_isProxy.js
@@ -0,0 +1,26 @@
+function run_test() {
+ var handler = {
+ get: function(target, name){
+ return name in target?
+ target[name] :
+ 37;
+ }
+ };
+
+ var p = new Proxy({}, handler);
+ Assert.ok(Cu.isProxy(p));
+ Assert.ok(!Cu.isProxy({}));
+ Assert.ok(!Cu.isProxy(42));
+
+ sb = new Cu.Sandbox(this,
+ { wantExportHelpers: true });
+
+ Assert.ok(!Cu.isProxy(sb));
+
+ sb.ok = ok;
+ sb.p = p;
+ Cu.evalInSandbox('ok(isProxy(p));' +
+ 'ok(!isProxy({}));' +
+ 'ok(!isProxy(42));',
+ sb);
+}
diff --git a/js/xpconnect/tests/unit/test_js_memory_telemetry.js b/js/xpconnect/tests/unit/test_js_memory_telemetry.js
new file mode 100644
index 0000000000..4c44f2e48c
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_js_memory_telemetry.js
@@ -0,0 +1,53 @@
+"use strict";
+
+add_task(function test_compartment_realm_counts() {
+ const compsSystem = "MEMORY_JS_COMPARTMENTS_SYSTEM";
+ const compsUser = "MEMORY_JS_COMPARTMENTS_USER";
+ const realmsSystem = "MEMORY_JS_REALMS_SYSTEM";
+ const realmsUser = "MEMORY_JS_REALMS_USER";
+
+ Cu.forceShrinkingGC();
+
+ Services.telemetry.gatherMemory();
+ let snapshot1 = Services.telemetry.getSnapshotForHistograms("main", true).parent;
+
+ // We can't hard code exact counts, but we can check some basic invariants:
+ //
+ // * Compartments must contain at least one realm, so there must be more
+ // realms than compartments.
+ // * There must be at least one system realm.
+
+ Assert.ok(snapshot1[realmsSystem].sum <= snapshot1[compsSystem].sum,
+ "Number of system compartments can't exceed number of system realms");
+ Assert.ok(snapshot1[realmsUser].sum <= snapshot1[compsUser].sum,
+ "Number of user compartments can't exceed number of user realms");
+ Assert.ok(snapshot1[realmsSystem].sum > 0,
+ "There must be at least one system realm");
+
+ // Now we create a bunch of sandboxes (more than one to be more resilient
+ // against GCs happening in the meantime), so we can check:
+ //
+ // * There are now more realms and user compartments than before. Not system
+ // compartments, because system realms share a compartment.
+ // * The system compartment contains multiple realms.
+
+ let systemPrincipal = Services.scriptSecurityManager.getSystemPrincipal();
+ let arr = [];
+ for (let i = 0; i < 5; i++) {
+ arr.push(Cu.Sandbox(null));
+ arr.push(Cu.Sandbox(systemPrincipal));
+ }
+
+ Services.telemetry.gatherMemory();
+ let snapshot2 = Services.telemetry.getSnapshotForHistograms("main", true).parent;
+
+ for (let k of [realmsSystem, realmsUser, compsUser]) {
+ Assert.ok(snapshot2[k].sum > snapshot1[k].sum,
+ "There must be more compartments/realms now: " + k);
+ }
+
+ Assert.ok(snapshot2[realmsSystem].sum > snapshot2[compsSystem].sum,
+ "There must be more system realms than system compartments now");
+
+ arr[0].x = 10; // Ensure the JS engine keeps |arr| alive until this point.
+});
diff --git a/js/xpconnect/tests/unit/test_js_weak_references.js b/js/xpconnect/tests/unit/test_js_weak_references.js
new file mode 100644
index 0000000000..2603f24ee2
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_js_weak_references.js
@@ -0,0 +1,45 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* See https://bugzilla.mozilla.org/show_bug.cgi?id=317304 */
+
+function run_test()
+{
+ // Bug 712649: Calling getWeakReference(null) should work.
+ try {
+ var nullWeak = Cu.getWeakReference(null);
+ Assert.ok(nullWeak.get() === null);
+ } catch (e) {
+ Assert.ok(false);
+ }
+
+ var obj = { num: 5, str: 'foo' };
+ var weak = Cu.getWeakReference(obj);
+
+ Assert.ok(weak.get() === obj);
+ Assert.ok(weak.get().num == 5);
+ Assert.ok(weak.get().str == 'foo');
+
+ // Force garbage collection
+ Cu.forceGC();
+
+ // obj still references the object, so it should still be accessible via weak
+ Assert.ok(weak.get() === obj);
+ Assert.ok(weak.get().num == 5);
+ Assert.ok(weak.get().str == 'foo');
+
+ // Clear obj's reference to the object and force garbage collection. To make
+ // sure that there are no instances of obj stored in the registers or on the
+ // native stack and the conservative GC would not find it we force the same
+ // code paths that we used for the initial allocation.
+ obj = { num: 6, str: 'foo2' };
+ var weak2 = Cu.getWeakReference(obj);
+ Assert.ok(weak2.get() === obj);
+
+ Cu.forceGC();
+
+ // The object should have been garbage collected and so should no longer be
+ // accessible via weak
+ Assert.ok(weak.get() === null);
+}
diff --git a/js/xpconnect/tests/unit/test_loadedESModules.js b/js/xpconnect/tests/unit/test_loadedESModules.js
new file mode 100644
index 0000000000..00e1059037
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_loadedESModules.js
@@ -0,0 +1,127 @@
+add_task(function test_JSModule() {
+ const URL1 = "resource://test/jsm_loaded-1.jsm";
+ const URL2 = "resource://test/jsm_loaded-2.jsm";
+ const URL3 = "resource://test/jsm_loaded-3.jsm";
+
+ Assert.ok(!Cu.loadedJSModules.includes(URL1));
+ Assert.ok(!Cu.isJSModuleLoaded(URL1));
+ Assert.ok(!Cu.loadedJSModules.includes(URL2));
+ Assert.ok(!Cu.isJSModuleLoaded(URL2));
+ Assert.ok(!Cu.loadedJSModules.includes(URL3));
+ Assert.ok(!Cu.isJSModuleLoaded(URL3));
+ Assert.ok(!Cu.loadedESModules.includes(URL1));
+ Assert.ok(!Cu.isESModuleLoaded(URL1));
+ Assert.ok(!Cu.loadedESModules.includes(URL2));
+ Assert.ok(!Cu.isESModuleLoaded(URL2));
+ Assert.ok(!Cu.loadedESModules.includes(URL3));
+ Assert.ok(!Cu.isESModuleLoaded(URL3));
+
+ ChromeUtils.import(URL1);
+
+ Assert.ok(Cu.loadedJSModules.includes(URL1));
+ Assert.ok(Cu.isJSModuleLoaded(URL1));
+ Assert.ok(!Cu.loadedJSModules.includes(URL2));
+ Assert.ok(!Cu.isJSModuleLoaded(URL2));
+ Assert.ok(!Cu.loadedJSModules.includes(URL3));
+ Assert.ok(!Cu.isJSModuleLoaded(URL3));
+ Assert.ok(!Cu.loadedESModules.includes(URL1));
+ Assert.ok(!Cu.isESModuleLoaded(URL1));
+ Assert.ok(!Cu.loadedESModules.includes(URL2));
+ Assert.ok(!Cu.isESModuleLoaded(URL2));
+ Assert.ok(!Cu.loadedESModules.includes(URL3));
+ Assert.ok(!Cu.isESModuleLoaded(URL3));
+
+ ChromeUtils.import(URL2);
+
+ Assert.ok(Cu.loadedJSModules.includes(URL1));
+ Assert.ok(Cu.isJSModuleLoaded(URL1));
+ Assert.ok(Cu.loadedJSModules.includes(URL2));
+ Assert.ok(Cu.isJSModuleLoaded(URL2));
+ Assert.ok(!Cu.loadedJSModules.includes(URL3));
+ Assert.ok(!Cu.isJSModuleLoaded(URL3));
+ Assert.ok(!Cu.loadedESModules.includes(URL1));
+ Assert.ok(!Cu.isESModuleLoaded(URL1));
+ Assert.ok(!Cu.loadedESModules.includes(URL2));
+ Assert.ok(!Cu.isESModuleLoaded(URL2));
+ Assert.ok(!Cu.loadedESModules.includes(URL3));
+ Assert.ok(!Cu.isESModuleLoaded(URL3));
+
+ ChromeUtils.import(URL3);
+
+ Assert.ok(Cu.loadedJSModules.includes(URL1));
+ Assert.ok(Cu.isJSModuleLoaded(URL1));
+ Assert.ok(Cu.loadedJSModules.includes(URL2));
+ Assert.ok(Cu.isJSModuleLoaded(URL2));
+ Assert.ok(Cu.loadedJSModules.includes(URL3));
+ Assert.ok(Cu.isJSModuleLoaded(URL3));
+ Assert.ok(!Cu.loadedESModules.includes(URL1));
+ Assert.ok(!Cu.isESModuleLoaded(URL1));
+ Assert.ok(!Cu.loadedESModules.includes(URL2));
+ Assert.ok(!Cu.isESModuleLoaded(URL2));
+ Assert.ok(!Cu.loadedESModules.includes(URL3));
+ Assert.ok(!Cu.isESModuleLoaded(URL3));
+});
+
+add_task(function test_ESModule() {
+ const URL1 = "resource://test/es6module_loaded-1.sys.mjs";
+ const URL2 = "resource://test/es6module_loaded-2.sys.mjs";
+ const URL3 = "resource://test/es6module_loaded-3.sys.mjs";
+
+ Assert.ok(!Cu.loadedJSModules.includes(URL1));
+ Assert.ok(!Cu.isJSModuleLoaded(URL1));
+ Assert.ok(!Cu.loadedJSModules.includes(URL2));
+ Assert.ok(!Cu.isJSModuleLoaded(URL2));
+ Assert.ok(!Cu.loadedJSModules.includes(URL3));
+ Assert.ok(!Cu.isJSModuleLoaded(URL3));
+ Assert.ok(!Cu.loadedESModules.includes(URL1));
+ Assert.ok(!Cu.isESModuleLoaded(URL1));
+ Assert.ok(!Cu.loadedESModules.includes(URL2));
+ Assert.ok(!Cu.isESModuleLoaded(URL2));
+ Assert.ok(!Cu.loadedESModules.includes(URL3));
+ Assert.ok(!Cu.isESModuleLoaded(URL3));
+
+ ChromeUtils.importESModule(URL1);
+
+ Assert.ok(!Cu.loadedJSModules.includes(URL1));
+ Assert.ok(!Cu.isJSModuleLoaded(URL1));
+ Assert.ok(!Cu.loadedJSModules.includes(URL2));
+ Assert.ok(!Cu.isJSModuleLoaded(URL2));
+ Assert.ok(!Cu.loadedJSModules.includes(URL3));
+ Assert.ok(!Cu.isJSModuleLoaded(URL3));
+ Assert.ok(Cu.loadedESModules.includes(URL1));
+ Assert.ok(Cu.isESModuleLoaded(URL1));
+ Assert.ok(!Cu.loadedESModules.includes(URL2));
+ Assert.ok(!Cu.isESModuleLoaded(URL2));
+ Assert.ok(!Cu.loadedESModules.includes(URL3));
+ Assert.ok(!Cu.isESModuleLoaded(URL3));
+
+ ChromeUtils.importESModule(URL2);
+
+ Assert.ok(!Cu.loadedJSModules.includes(URL1));
+ Assert.ok(!Cu.isJSModuleLoaded(URL1));
+ Assert.ok(!Cu.loadedJSModules.includes(URL2));
+ Assert.ok(!Cu.isJSModuleLoaded(URL2));
+ Assert.ok(!Cu.loadedJSModules.includes(URL3));
+ Assert.ok(!Cu.isJSModuleLoaded(URL3));
+ Assert.ok(Cu.loadedESModules.includes(URL1));
+ Assert.ok(Cu.isESModuleLoaded(URL1));
+ Assert.ok(Cu.loadedESModules.includes(URL2));
+ Assert.ok(Cu.isESModuleLoaded(URL2));
+ Assert.ok(!Cu.loadedESModules.includes(URL3));
+ Assert.ok(!Cu.isESModuleLoaded(URL3));
+
+ ChromeUtils.importESModule(URL3);
+
+ Assert.ok(!Cu.loadedJSModules.includes(URL1));
+ Assert.ok(!Cu.isJSModuleLoaded(URL1));
+ Assert.ok(!Cu.loadedJSModules.includes(URL2));
+ Assert.ok(!Cu.isJSModuleLoaded(URL2));
+ Assert.ok(!Cu.loadedJSModules.includes(URL3));
+ Assert.ok(!Cu.isJSModuleLoaded(URL3));
+ Assert.ok(Cu.loadedESModules.includes(URL1));
+ Assert.ok(Cu.isESModuleLoaded(URL1));
+ Assert.ok(Cu.loadedESModules.includes(URL2));
+ Assert.ok(Cu.isESModuleLoaded(URL2));
+ Assert.ok(Cu.loadedESModules.includes(URL3));
+ Assert.ok(Cu.isESModuleLoaded(URL3));
+});
diff --git a/js/xpconnect/tests/unit/test_localeCompare.js b/js/xpconnect/tests/unit/test_localeCompare.js
new file mode 100644
index 0000000000..fa98d865e4
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_localeCompare.js
@@ -0,0 +1,6 @@
+function run_test() {
+ Assert.ok("C".localeCompare("D") < 0);
+ Assert.ok("D".localeCompare("C") > 0);
+ Assert.ok("\u010C".localeCompare("D") < 0);
+ Assert.ok("D".localeCompare("\u010C") > 0);
+}
diff --git a/js/xpconnect/tests/unit/test_malformed_utf8.js b/js/xpconnect/tests/unit/test_malformed_utf8.js
new file mode 100644
index 0000000000..8ab4592321
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_malformed_utf8.js
@@ -0,0 +1,74 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// nsIPrefBranch.{getCharPref,setCharPref} uses Latin-1 string, and
+// nsIPrefBranch.{getStringPref,setStringPref} uses UTF-8 string.
+//
+// Mixing them results in unexpected string, but it should perform lossy
+// conversion, and not throw.
+
+const gPrefs = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch);
+
+const tests = [
+ // Latin-1 to Latin-1 and UTF-8 to UTF-8 should preserve the string.
+ // Latin-1 to UTF-8 should replace invalid character with REPLACEMENT
+ // CHARACTER.
+ // UTF-8 to Latin1 should return the raw UTF-8 code units.
+
+ // UTF-8 code units sequence without the last unit.
+ //
+ // input, Latin-1 to UTF-8, UTF-8 to Latin-1
+ ["\xC2", "\uFFFD", "\xC3\x82"],
+ ["\xDF", "\uFFFD", "\xC3\x9F"],
+ ["\xE0\xA0", "\uFFFD", "\xC3\xA0\xC2\xA0"],
+ ["\xF0\x90\x80", "\uFFFD", "\xC3\xB0\xC2\x90\xC2\x80"],
+
+ // UTF-8 code units sequence with malformed last unit.
+ //
+ // input, Latin-1 to UTF-8, UTF-8 to Latin-1
+ ["\xC2 ", "\uFFFD ", "\xC3\x82 "],
+ ["\xDF ", "\uFFFD ", "\xC3\x9F "],
+ ["\xE0\xA0 ", "\uFFFD ", "\xC3\xA0\xC2\xA0 "],
+ ["\xF0\x90\x80 ", "\uFFFD ", "\xC3\xB0\xC2\x90\xC2\x80 "],
+
+ // UTF-8 code units without the first unit.
+ //
+ // input, Latin-1 to UTF-8, UTF-8 to Latin-1
+ ["\x80", "\uFFFD", "\xC2\x80"],
+ ["\xBF", "\uFFFD", "\xC2\xBF"],
+ ["\xA0\x80", "\uFFFD\uFFFD", "\xC2\xA0\xC2\x80"],
+ ["\x8F\x80\x80", "\uFFFD\uFFFD\uFFFD", "\xC2\x8F\xC2\x80\xC2\x80"],
+];
+
+add_task(function testLatin1ToLatin1() {
+ for (const [input, ] of tests) {
+ gPrefs.setCharPref("test.malformed_utf8_data", input);
+ const result = gPrefs.getCharPref("test.malformed_utf8_data");
+ Assert.equal(result, input);
+ }
+});
+
+add_task(function testLatin1ToUTF8() {
+ for (const [input, expected] of tests) {
+ gPrefs.setCharPref("test.malformed_utf8_data", input);
+ const result = gPrefs.getStringPref("test.malformed_utf8_data");
+ Assert.equal(result, expected);
+ }
+});
+
+add_task(function testUTF8ToLatin1() {
+ for (const [input, , expected] of tests) {
+ gPrefs.setStringPref("test.malformed_utf8_data", input);
+ const result = gPrefs.getCharPref("test.malformed_utf8_data");
+ Assert.equal(result, expected);
+ }
+});
+
+add_task(function testUTF8ToUTF8() {
+ for (const [input, ] of tests) {
+ gPrefs.setStringPref("test.malformed_utf8_data", input);
+ const result = gPrefs.getStringPref("test.malformed_utf8_data");
+ Assert.equal(result, input);
+ }
+});
diff --git a/js/xpconnect/tests/unit/test_messageChannel.js b/js/xpconnect/tests/unit/test_messageChannel.js
new file mode 100644
index 0000000000..685aa10e43
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_messageChannel.js
@@ -0,0 +1,29 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+add_task(async function() {
+ let sb = new Cu.Sandbox('http://www.example.com',
+ { wantGlobalProperties: ["MessageChannel"] });
+ sb.ok = ok;
+ Cu.evalInSandbox('ok((new MessageChannel()) instanceof MessageChannel);',
+ sb);
+ Cu.evalInSandbox('ok((new MessageChannel()).port1 instanceof MessagePort);',
+ sb);
+
+ Cu.importGlobalProperties(["MessageChannel"]);
+
+ let mc = new MessageChannel();
+ Assert.ok(mc instanceof MessageChannel);
+ Assert.ok(mc.port1 instanceof MessagePort);
+ Assert.ok(mc.port2 instanceof MessagePort);
+
+ mc.port1.postMessage(42);
+
+ let result = await new Promise(resolve => {
+ mc.port2.onmessage = e => {
+ resolve(e.data);
+ }
+ });
+
+ Assert.equal(result, 42);
+});
diff --git a/js/xpconnect/tests/unit/test_nuke_sandbox.js b/js/xpconnect/tests/unit/test_nuke_sandbox.js
new file mode 100644
index 0000000000..c555121306
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_nuke_sandbox.js
@@ -0,0 +1,50 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* See https://bugzilla.mozilla.org/show_bug.cgi?id=769273 */
+
+const global = this;
+
+function run_test()
+{
+ var ifacePointer = Cc["@mozilla.org/supports-interface-pointer;1"]
+ .createInstance(Ci.nsISupportsInterfacePointer);
+
+ var sb = Cu.Sandbox(global, {wantGlobalProperties: ["ChromeUtils"]});
+ sb.prop = "prop"
+ sb.ifacePointer = ifacePointer
+
+ var refToObjFromSb = Cu.evalInSandbox(`
+ ifacePointer.data = {
+ QueryInterface: ChromeUtils.generateQI([]),
+ wrappedJSObject: {foo: "bar"},
+ };
+
+ var a = {prop2:'prop2'};
+ a
+ `, sb);
+
+ equal(ifacePointer.data.wrappedJSObject.foo, "bar",
+ "Got expected wrapper into sandbox")
+
+ Cu.nukeSandbox(sb);
+ ok(Cu.isDeadWrapper(sb), "sb should be dead");
+ ok(Cu.isDeadWrapper(ifacePointer.data.wrappedJSObject),
+ "Wrapper retrieved via XPConnect should be dead");
+
+ try{
+ sb.prop;
+ Assert.ok(false);
+ } catch (e) {
+ Assert.ok(e.toString().indexOf("can't access dead object") > -1);
+ }
+
+ Cu.isDeadWrapper(refToObjFromSb, "ref to object from sb should be dead");
+ try{
+ refToObjFromSb.prop2;
+ Assert.ok(false);
+ } catch (e) {
+ Assert.ok(e.toString().indexOf("can't access dead object") > -1);
+ }
+}
diff --git a/js/xpconnect/tests/unit/test_nuke_sandbox_event_listeners.js b/js/xpconnect/tests/unit/test_nuke_sandbox_event_listeners.js
new file mode 100644
index 0000000000..5724e83b12
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_nuke_sandbox_event_listeners.js
@@ -0,0 +1,89 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// See https://bugzilla.mozilla.org/show_bug.cgi?id=1273251
+
+function promiseEvent(target, event) {
+ return new Promise(resolve => {
+ target.addEventListener(event, resolve, {capture: true, once: true});
+ });
+}
+
+add_task(async function() {
+ let principal = Services.scriptSecurityManager
+ .createContentPrincipalFromOrigin("http://example.com/");
+
+ let webnav = Services.appShell.createWindowlessBrowser(false);
+
+ let docShell = webnav.docShell;
+
+ docShell.createAboutBlankDocumentViewer(principal, principal);
+
+ let window = webnav.document.defaultView;
+ let sandbox = Cu.Sandbox(window, {sandboxPrototype: window});
+
+ function sandboxContent() {
+ window.onload = function SandboxOnLoad() {};
+
+ window.addEventListener("FromTest", () => {
+ window.dispatchEvent(new CustomEvent("FromSandbox"));
+ }, true);
+ }
+
+ Cu.evalInSandbox(`(${sandboxContent})()`, sandbox);
+
+
+ let fromTestPromise = promiseEvent(window, "FromTest");
+ let fromSandboxPromise = promiseEvent(window, "FromSandbox");
+
+ equal(typeof window.onload, "function",
+ "window.onload should contain sandbox event listener");
+ equal(window.onload.name, "SandboxOnLoad",
+ "window.onload have the correct function name");
+
+ info("Dispatch FromTest event");
+ window.dispatchEvent(new window.CustomEvent("FromTest"));
+
+ await fromTestPromise;
+ info("Got event from test");
+
+ await fromSandboxPromise;
+ info("Got response from sandbox");
+
+
+ window.addEventListener("FromSandbox", () => {
+ ok(false, "Got unexpected reply from sandbox");
+ }, true);
+
+ info("Nuke sandbox");
+ Cu.nukeSandbox(sandbox);
+
+
+ info("Dispatch FromTest event");
+ fromTestPromise = promiseEvent(window, "FromTest");
+ window.dispatchEvent(new window.CustomEvent("FromTest"));
+ await fromTestPromise;
+ info("Got event from test");
+
+
+ // Force cycle collection, which should cause our callback reference
+ // to be dropped, and dredge up potential issues there.
+ Cu.forceGC();
+ Cu.forceCC();
+
+ ok(Cu.isDeadWrapper(window.onload),
+ "window.onload should contain a dead wrapper after sandbox is nuked");
+
+ info("Dispatch FromTest event");
+ fromTestPromise = promiseEvent(window, "FromTest");
+ window.dispatchEvent(new window.CustomEvent("FromTest"));
+ await fromTestPromise;
+ info("Got event from test");
+
+ let listeners = Services.els.getListenerInfoFor(window);
+ ok(!listeners.some(info => info.type == "FromTest"),
+ "No 'FromTest' listeners returned for nuked sandbox");
+
+ webnav.close();
+});
diff --git a/js/xpconnect/tests/unit/test_nuke_webextension_wrappers.js b/js/xpconnect/tests/unit/test_nuke_webextension_wrappers.js
new file mode 100644
index 0000000000..456a94d97c
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_nuke_webextension_wrappers.js
@@ -0,0 +1,70 @@
+// See https://bugzilla.mozilla.org/show_bug.cgi?id=1273251
+
+const {NetUtil} = ChromeUtils.importESModule("resource://gre/modules/NetUtil.sys.mjs");
+const {TestUtils} = ChromeUtils.importESModule("resource://testing-common/TestUtils.sys.mjs");
+
+function getWindowlessBrowser(url) {
+ let ssm = Services.scriptSecurityManager;
+
+ let uri = NetUtil.newURI(url);
+
+ let principal = ssm.createContentPrincipal(uri, {});
+
+ let webnav = Services.appShell.createWindowlessBrowser(false);
+
+ let docShell = webnav.docShell;
+
+ docShell.createAboutBlankDocumentViewer(principal, principal);
+
+ return webnav;
+}
+
+function StubPolicy(id) {
+ return new WebExtensionPolicy({
+ id,
+ mozExtensionHostname: id,
+ baseURL: `file:///{id}`,
+
+ allowedOrigins: new MatchPatternSet([]),
+ localizeCallback(string) {},
+ });
+}
+
+add_task(async function() {
+ let policy = StubPolicy("foo");
+ policy.active = true;
+
+ let webnavA = getWindowlessBrowser("moz-extension://foo/a.html");
+ let webnavB = getWindowlessBrowser("moz-extension://foo/b.html");
+
+ let winA = Cu.waiveXrays(webnavA.document.defaultView);
+ let winB = Cu.waiveXrays(webnavB.document.defaultView);
+
+ winB.winA = winA;
+ winB.eval(`winA.thing = {foo: "bar"};`);
+
+ let getThing = winA.eval(String(() => {
+ try {
+ return thing.foo;
+ } catch (e) {
+ return String(e);
+ }
+ }));
+
+ // Check that the object can be accessed normally before windowB is closed.
+ equal(getThing(), "bar");
+
+ webnavB.close();
+
+ // Wrappers are nuked asynchronously, so wait for that to happen.
+ await TestUtils.topicObserved("inner-window-nuked");
+
+ // Check that it can't be accessed after he window has been closed.
+ let result = getThing();
+ ok(/dead object/.test(result),
+ `Result should show a dead wrapper error: ${result}`);
+
+ webnavA.close();
+
+ policy.active = false;
+});
diff --git a/js/xpconnect/tests/unit/test_onGarbageCollection-01.js b/js/xpconnect/tests/unit/test_onGarbageCollection-01.js
new file mode 100644
index 0000000000..81ac713865
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_onGarbageCollection-01.js
@@ -0,0 +1,69 @@
+// Test basic usage of onGarbageCollection
+
+const root = newGlobal();
+const dbg = new Debugger();
+const wrappedRoot = dbg.addDebuggee(root)
+
+const NUM_SLICES = root.NUM_SLICES = 10;
+
+let fired = false;
+let slicesFound = 0;
+
+Services.prefs.setBoolPref("security.allow_eval_with_system_principal", true);
+registerCleanupFunction(() => {
+ Services.prefs.clearUserPref("security.allow_eval_with_system_principal");
+});
+
+dbg.memory.onGarbageCollection = data => {
+ fired = true;
+
+ print("Got onGarbageCollection: " + JSON.stringify(data, null, 2));
+
+ equal(typeof data.reason, "string");
+ equal(typeof data.nonincrementalReason == "string" || data.nonincrementalReason === null,
+ true);
+
+ let lastStartTimestamp = 0;
+ for (let i = 0; i < data.collections.length; i++) {
+ let slice = data.collections[i];
+
+ equal(slice.startTimestamp >= lastStartTimestamp, true);
+ equal(slice.startTimestamp <= slice.endTimestamp, true);
+
+ lastStartTimestamp = slice.startTimestamp;
+ }
+
+ equal(data.collections.length >= 1, true);
+ slicesFound += data.collections.length;
+}
+
+function run_test() {
+ do_test_pending();
+
+ root.eval(
+ `
+ this.allocs = [];
+
+ // GC slices
+ for (var i = 0; i < NUM_SLICES; i++) {
+ this.allocs.push({});
+ gcslice();
+ }
+
+ // Full GC
+ this.allocs.push({});
+ gc();
+ `
+ );
+
+ executeSoon(() => {
+ equal(fired, true, "The GC hook should have fired at least once");
+
+ // NUM_SLICES + 1 full gc + however many were triggered naturally (due to
+ // whatever zealousness setting).
+ print("Found " + slicesFound + " slices");
+ equal(slicesFound >= NUM_SLICES + 1, true);
+
+ do_test_finished();
+ });
+}
diff --git a/js/xpconnect/tests/unit/test_onGarbageCollection-02.js b/js/xpconnect/tests/unit/test_onGarbageCollection-02.js
new file mode 100644
index 0000000000..fc3bf685ef
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_onGarbageCollection-02.js
@@ -0,0 +1,99 @@
+// Test multiple debuggers, GCs, and zones interacting with each other.
+//
+// Note: when observing both globals, but GC'ing in only one, we don't test that
+// we *didn't* GC in the other zone because GCs are finicky and unreliable. That
+// used to work when this was a jit-test, but in the process of migrating to
+// xpcshell, we lost some amount of reliability and determinism.
+
+const root1 = newGlobal();
+const dbg1 = new Debugger();
+dbg1.addDebuggee(root1)
+
+const root2 = newGlobal();
+const dbg2 = new Debugger();
+dbg2.addDebuggee(root2)
+
+let fired1 = false;
+let fired2 = false;
+dbg1.memory.onGarbageCollection = _ => fired1 = true;
+dbg2.memory.onGarbageCollection = _ => fired2 = true;
+
+Services.prefs.setBoolPref("security.allow_eval_with_system_principal", true);
+registerCleanupFunction(() => {
+ Services.prefs.clearUserPref("security.allow_eval_with_system_principal");
+});
+
+function reset() {
+ fired1 = false;
+ fired2 = false;
+}
+
+function run_test() {
+ do_test_pending();
+
+ gc();
+ executeSoon(() => {
+ reset();
+
+ // GC 1 only
+ root1.eval(`gc(this)`);
+ executeSoon(() => {
+ equal(fired1, true);
+
+ // GC 2 only
+ reset();
+ root2.eval(`gc(this)`);
+ executeSoon(() => {
+ equal(fired2, true);
+
+ // Full GC
+ reset();
+ gc();
+ executeSoon(() => {
+ equal(fired1, true);
+ equal(fired2, true);
+
+ // Full GC with no debuggees
+ reset();
+ dbg1.removeAllDebuggees();
+ dbg2.removeAllDebuggees();
+ gc();
+ executeSoon(() => {
+ equal(fired1, false);
+ equal(fired2, false);
+
+ // One debugger with multiple debuggees in different zones.
+
+ dbg1.addDebuggee(root1);
+ dbg1.addDebuggee(root2);
+
+ // Just debuggee 1
+ reset();
+ root1.eval(`gc(this)`);
+ executeSoon(() => {
+ equal(fired1, true);
+ equal(fired2, false);
+
+ // Just debuggee 2
+ reset();
+ root2.eval(`gc(this)`);
+ executeSoon(() => {
+ equal(fired1, true);
+ equal(fired2, false);
+
+ // All debuggees
+ reset();
+ gc();
+ executeSoon(() => {
+ equal(fired1, true);
+ equal(fired2, false);
+ do_test_finished();
+ });
+ });
+ });
+ });
+ });
+ });
+ });
+ });
+}
diff --git a/js/xpconnect/tests/unit/test_onGarbageCollection-03.js b/js/xpconnect/tests/unit/test_onGarbageCollection-03.js
new file mode 100644
index 0000000000..d983e2cd11
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_onGarbageCollection-03.js
@@ -0,0 +1,39 @@
+// Test that the onGarbageCollection hook is not reentrant.
+
+Services.prefs.setBoolPref("security.allow_eval_with_system_principal", true);
+registerCleanupFunction(() => {
+ Services.prefs.clearUserPref("security.allow_eval_with_system_principal");
+});
+
+function run_test() {
+ do_test_pending();
+
+ const root = newGlobal();
+ const dbg = new Debugger();
+ const wrappedRoot = dbg.addDebuggee(root)
+
+ let fired = true;
+ let depth = 0;
+
+ dbg.memory.onGarbageCollection = _ => {
+ fired = true;
+
+ equal(depth, 0);
+ depth++;
+ try {
+ root.eval(`gc()`);
+ } finally {
+ equal(depth, 1);
+ depth--;
+ }
+ }
+
+ root.eval(`gc()`);
+
+ executeSoon(() => {
+ ok(fired);
+ equal(depth, 0);
+ dbg.memory.onGarbageCollection = undefined;
+ do_test_finished();
+ });
+}
diff --git a/js/xpconnect/tests/unit/test_onGarbageCollection-04.js b/js/xpconnect/tests/unit/test_onGarbageCollection-04.js
new file mode 100644
index 0000000000..72e6d32284
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_onGarbageCollection-04.js
@@ -0,0 +1,72 @@
+// Test that the onGarbageCollection reentrancy guard is on a per Debugger
+// basis. That is if our first Debugger is observing our second Debugger's
+// compartment, and this second Debugger triggers a GC inside its
+// onGarbageCollection hook, the first Debugger's onGarbageCollection hook is
+// still called.
+//
+// This is the scenario we are setting up: top level debugging the `debuggeree`
+// global, which is debugging the `debuggee` global. Then, we trigger the
+// following events:
+//
+// debuggee gc
+// |
+// V
+// debuggeree's onGarbageCollection
+// |
+// V
+// debuggeree gc
+// |
+// V
+// top level onGarbageCollection
+//
+// Note that the top level's onGarbageCollection hook should be fired, at the
+// same time that we are preventing reentrancy into debuggeree's
+// onGarbageCollection hook.
+
+Services.prefs.setBoolPref("security.allow_eval_with_system_principal", true);
+registerCleanupFunction(() => {
+ Services.prefs.clearUserPref("security.allow_eval_with_system_principal");
+});
+
+function run_test() {
+ do_test_pending();
+
+ const debuggeree = newGlobal();
+ const debuggee = debuggeree.debuggee = newGlobal();
+
+ debuggeree.eval(
+ `
+ var dbg = new Debugger(this.debuggee);
+ var fired = 0;
+ dbg.memory.onGarbageCollection = _ => {
+ fired++;
+ gc(this);
+ };
+ `
+ );
+
+ const dbg = new Debugger(debuggeree);
+ let fired = 0;
+ dbg.memory.onGarbageCollection = _ => {
+ fired++;
+ };
+
+ debuggee.eval(`gc(this)`);
+
+ // Let first onGarbageCollection runnable get run.
+ executeSoon(() => {
+
+ // Let second onGarbageCollection runnable get run.
+ executeSoon(() => {
+
+ // Even though we request GC'ing a single zone, we can't rely on that
+ // behavior and both zones could have been scheduled for gc for both
+ // gc(this) calls.
+ ok(debuggeree.fired >= 1);
+ ok(fired >= 1);
+
+ debuggeree.dbg.removeAllDebuggees();
+ do_test_finished();
+ });
+ });
+}
diff --git a/js/xpconnect/tests/unit/test_onGarbageCollection-05.js b/js/xpconnect/tests/unit/test_onGarbageCollection-05.js
new file mode 100644
index 0000000000..e3b5e5fd9e
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_onGarbageCollection-05.js
@@ -0,0 +1,42 @@
+// Test that the onGarbageCollection hook reports its gc cycle's number (aka the
+// major GC number) and that it is monotonically increasing.
+
+const root = newGlobal();
+const dbg = new Debugger();
+const wrappedRoot = dbg.addDebuggee(root)
+
+Services.prefs.setBoolPref("security.allow_eval_with_system_principal", true);
+registerCleanupFunction(() => {
+ Services.prefs.clearUserPref("security.allow_eval_with_system_principal");
+});
+
+function run_test() {
+ do_test_pending();
+
+ let numFired = 0;
+ let lastGCCycleNumber = undefined;
+
+ (function loop() {
+ if (numFired == 10) {
+ dbg.memory.onGarbageCollection = undefined;
+ dbg.enabled = false;
+ return void do_test_finished();
+ }
+
+ dbg.memory.onGarbageCollection = data => {
+ print("onGarbageCollection: " + uneval(data));
+
+ if (numFired != 0) {
+ equal(typeof lastGCCycleNumber, "number");
+ equal(data.gcCycleNumber - lastGCCycleNumber, 1);
+ }
+
+ numFired++;
+ lastGCCycleNumber = data.gcCycleNumber;
+
+ executeSoon(loop);
+ };
+
+ root.eval("gc(this)");
+ }());
+}
diff --git a/js/xpconnect/tests/unit/test_params.js b/js/xpconnect/tests/unit/test_params.js
new file mode 100644
index 0000000000..fc986424c6
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_params.js
@@ -0,0 +1,384 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+function TestParams() {
+}
+
+/* For once I'm happy that JS is weakly typed. */
+function f(a, b) {
+ var rv = b.value;
+ b.value = a;
+ return rv;
+};
+
+/* Implementation for size_is and iid_is methods. */
+function f_is(aIs, a, bIs, b, rvIs) {
+
+ // Set up the return value and its 'is' parameter.
+ var rv = b.value;
+ rvIs.value = bIs.value;
+
+ // Set up b and its 'is' parameter.
+ b.value = a;
+ bIs.value = aIs;
+
+ return rv;
+}
+
+function f_size_and_iid(aSize, aIID, a, bSize, bIID, b, rvSize, rvIID) {
+
+ // Copy the iids.
+ rvIID.value = bIID.value;
+ bIID.value = aIID;
+
+ // Now that we've reduced the problem to one dependent variable, use f_is.
+ return f_is(aSize, a, bSize, b, rvSize);
+}
+
+TestParams.prototype = {
+ QueryInterface: ChromeUtils.generateQI(["nsIXPCTestParams"]),
+
+ /* nsIXPCTestParams */
+ testBoolean: f,
+ testOctet: f,
+ testShort: f,
+ testLong: f,
+ testLongLong: f,
+ testUnsignedShort: f,
+ testUnsignedLong: f,
+ testUnsignedLongLong: f,
+ testFloat: f,
+ testDouble: f,
+ testChar: f,
+ testString: f,
+ testWchar: f,
+ testWstring: f,
+ testAString: f,
+ testAUTF8String: f,
+ testACString: f,
+ testJsval: f,
+ testShortSequence: f,
+ testDoubleSequence: f,
+ testAStringSequence: f,
+ testACStringSequence: f,
+ testInterfaceSequence: f,
+ testJsvalSequence: f,
+ testInterfaceIsSequence: f_is,
+ testOptionalSequence: function (arr) { return arr; },
+ testShortArray: f_is,
+ testDoubleArray: f_is,
+ testStringArray: f_is,
+ testByteArrayOptionalLength(arr) { return arr.length; },
+ testWstringArray: f_is,
+ testInterfaceArray: f_is,
+ testJsvalArray: f_is,
+ testSizedString: f_is,
+ testSizedWstring: f_is,
+ testInterfaceIs: f_is,
+ testInterfaceIsArray: f_size_and_iid,
+ testOutAString: function(o) { o.value = "out"; },
+ testStringArrayOptionalSize: function(arr, size) {
+ if (arr.length != size) { throw "bad size passed to test method"; }
+ var rv = "";
+ arr.forEach((x) => rv += x);
+ return rv;
+ },
+ testOmittedOptionalOut(jsObj, o) {
+ if (typeof o != "object" || o.value !== undefined) {
+ throw new Components.Exception(
+ "unexpected value",
+ Cr.NS_ERROR_ILLEGAL_VALUE
+ );
+ }
+ o.value = Cc["@mozilla.org/network/io-service;1"]
+ .getService(Ci.nsIIOService)
+ .newURI("http://example.com/");
+ },
+ testNaN: NaN,
+};
+
+function TestInterfaceA() {}
+TestInterfaceA.prototype = {
+ QueryInterface: ChromeUtils.generateQI(["nsIXPCTestInterfaceA"]),
+
+ /* nsIXPCTestInterfaceA */
+ name: "TestInterfaceADefaultName"
+};
+
+function TestInterfaceB() {}
+TestInterfaceB.prototype = {
+ QueryInterface: ChromeUtils.generateQI(["nsIXPCTestInterfaceB"]),
+
+ /* nsIXPCTestInterfaceA */
+ name: "TestInterfaceADefaultName"
+};
+
+function run_test() {
+
+ // Load the component manifests.
+ registerXPCTestComponents();
+
+ // Test for each component.
+ test_component(Cc["@mozilla.org/js/xpc/test/native/Params;1"].createInstance());
+ test_component(xpcWrap(new TestParams()));
+}
+
+function test_component(obj) {
+ var o = obj.QueryInterface(Ci.nsIXPCTestParams);
+
+ // Possible comparator functions.
+ var standardComparator = function(a,b) {return a == b;};
+ var dotEqualsComparator = function(a,b) {return a.equals(b); }
+ var fuzzComparator = function(a,b) {return Math.abs(a - b) < 0.1;};
+ var interfaceComparator = function(a,b) {return a.name == b.name; }
+ var arrayComparator = function(innerComparator) {
+ return function(a,b) {
+ if (a.length != b.length)
+ return false;
+ for (var i = 0; i < a.length; ++i)
+ if (!innerComparator(a[i], b[i]))
+ return false;
+ return true;
+ };
+ };
+
+ // Helper test function - takes the name of test method and two values of
+ // the given type.
+ //
+ // The optional comparator argument can be used for alternative notions of
+ // equality. The comparator should return true on equality.
+ function doTest(name, val1, val2, comparator) {
+ if (!comparator)
+ comparator = standardComparator;
+ var a = val1;
+ var b = {value: val2};
+ var rv = o[name].call(o, a, b);
+ Assert.ok(comparator(rv, val2));
+ Assert.ok(comparator(val1, b.value));
+ };
+
+ function doIsTest(name, val1, val1Is, val2, val2Is, valComparator, isComparator) {
+ if (!isComparator)
+ isComparator = standardComparator;
+ var a = val1;
+ var aIs = val1Is;
+ var b = {value: val2};
+ var bIs = {value: val2Is};
+ var rvIs = {};
+ var rv = o[name].call(o, aIs, a, bIs, b, rvIs);
+ Assert.ok(valComparator(rv, val2));
+ Assert.ok(isComparator(rvIs.value, val2Is));
+ Assert.ok(valComparator(val1, b.value));
+ Assert.ok(isComparator(val1Is, bIs.value));
+ }
+
+ // Special-purpose function for testing arrays of iid_is interfaces, where we
+ // have 2 distinct sets of dependent parameters.
+ function doIs2Test(name, val1, val1Size, val1IID, val2, val2Size, val2IID) {
+ var a = val1;
+ var aSize = val1Size;
+ var aIID = val1IID;
+ var b = {value: val2};
+ var bSize = {value: val2Size};
+ var bIID = {value: val2IID};
+ var rvSize = {};
+ var rvIID = {};
+ var rv = o[name].call(o, aSize, aIID, a, bSize, bIID, b, rvSize, rvIID);
+ Assert.ok(arrayComparator(interfaceComparator)(rv, val2));
+ Assert.ok(standardComparator(rvSize.value, val2Size));
+ Assert.ok(dotEqualsComparator(rvIID.value, val2IID));
+ Assert.ok(arrayComparator(interfaceComparator)(val1, b.value));
+ Assert.ok(standardComparator(val1Size, bSize.value));
+ Assert.ok(dotEqualsComparator(val1IID, bIID.value));
+ }
+
+ // Check that the given call (type mismatch) results in an exception being thrown.
+ function doTypedArrayMismatchTest(name, val1, val1Size, val2, val2Size) {
+ var comparator = arrayComparator(standardComparator);
+ var error = false;
+ try {
+ doIsTest(name, val1, val1Size, val2, val2Size, comparator);
+
+ // An exception was not thrown as would have been expected.
+ Assert.ok(false);
+ }
+ catch (e) {
+ // An exception was thrown as expected.
+ Assert.ok(true);
+ }
+ }
+
+ // Workaround for bug 687612 (inout parameters broken for dipper types).
+ // We do a simple test of copying a into b, and ignore the rv.
+ function doTestWorkaround(name, val1) {
+ var a = val1;
+ var b = {value: ""};
+ o[name].call(o, a, b);
+ Assert.equal(val1, b.value);
+ }
+
+ // Test all the different types
+ doTest("testBoolean", true, false);
+ doTest("testOctet", 4, 156);
+ doTest("testShort", -456, 1299);
+ doTest("testLong", 50060, -12121212);
+ doTest("testLongLong", 12345, -10000000000);
+ doTest("testUnsignedShort", 1532, 65000);
+ doTest("testUnsignedLong", 0, 4000000000);
+ doTest("testUnsignedLongLong", 215435, 3453492580348535809);
+ doTest("testFloat", 4.9, -11.2, fuzzComparator);
+ doTest("testDouble", -80.5, 15000.2, fuzzComparator);
+ doTest("testChar", "a", "2");
+ doTest("testString", "someString", "another string");
+ doTest("testWstring", "Why wasnt this", "turned on before? ಠ_ಠ");
+ doTest("testWchar", "z", "ア");
+ doTestWorkaround("testAString", "Frosty the ☃ ;-)");
+ doTestWorkaround("testAUTF8String", "We deliver 〠!");
+ doTestWorkaround("testACString", "Just a regular C string.");
+ doTest("testJsval", {aprop: 12, bprop: "str"}, 4.22);
+
+ // Test out dipper parameters, since they're special and we can't really test
+ // inouts.
+ let outAString = {};
+ o.testOutAString(outAString);
+ Assert.equal(outAString.value, "out");
+ try { o.testOutAString(undefined); } catch (e) {} // Don't crash
+ try { o.testOutAString(null); } catch (e) {} // Don't crash
+ try { o.testOutAString("string"); } catch (e) {} // Don't crash
+
+ // Helpers to instantiate various test XPCOM objects.
+ var numAsMade = 0;
+ function makeA() {
+ var a = xpcWrap(new TestInterfaceA(), Ci.nsIXPCTestInterfaceA);
+ a.name = 'testA' + numAsMade++;
+ return a;
+ };
+ var numBsMade = 0;
+ function makeB() {
+ var b = xpcWrap(new TestInterfaceB(), Ci.nsIXPCTestInterfaceB);
+ b.name = 'testB' + numBsMade++;
+ return b;
+ };
+
+ // Test arrays.
+ doIsTest("testShortArray", [2, 4, 6], 3, [1, 3, 5, 7], 4, arrayComparator(standardComparator));
+ doIsTest("testDoubleArray", [-10, -0.5], 2, [1, 3, 1e11, -8e-5 ], 4, arrayComparator(fuzzComparator));
+
+ doIsTest("testStringArray", ["mary", "hat", "hey", "lid", "tell", "lam"], 6,
+ ["ids", "fleas", "woes", "wide", "has", "know", "!"], 7, arrayComparator(standardComparator));
+ doIsTest("testWstringArray", ["沒有語言", "的偉大嗎?]"], 2,
+ ["we", "are", "being", "sooo", "international", "right", "now"], 7, arrayComparator(standardComparator));
+ doIsTest("testInterfaceArray", [makeA(), makeA()], 2,
+ [makeA(), makeA(), makeA(), makeA(), makeA(), makeA()], 6, arrayComparator(interfaceComparator));
+ doIsTest("testJsvalArray", [{ cheese: 'whiz', apple: 8 }, [1, 5, '3'], /regex/], 3,
+ ['apple', 2.2e10, 3.3e30, { only: "wheedle", except: {} }], 4, arrayComparator(standardComparator));
+
+ // Test typed arrays and ArrayBuffer aliasing.
+ var arrayBuffer = new ArrayBuffer(16);
+ var int16Array = new Int16Array(arrayBuffer, 2, 3);
+ int16Array.set([-32768, 0, 32767]);
+ doIsTest("testShortArray", int16Array, 3, new Int16Array([1773, -32768, 32767, 7]), 4, arrayComparator(standardComparator));
+ doIsTest("testDoubleArray", new Float64Array([-10, -0.5]), 2, new Float64Array([0, 3.2, 1.0e10, -8.33 ]), 4, arrayComparator(fuzzComparator));
+
+ // Test sized strings.
+ var ssTests = ["Tis not possible, I muttered", "give me back my free hardcore!", "quoth the server:", "4〠4"];
+ doIsTest("testSizedString", ssTests[0], ssTests[0].length, ssTests[1], ssTests[1].length, standardComparator);
+ doIsTest("testSizedWstring", ssTests[2], ssTests[2].length, ssTests[3], ssTests[3].length, standardComparator);
+
+ // Test iid_is.
+ doIsTest("testInterfaceIs", makeA(), Ci['nsIXPCTestInterfaceA'],
+ makeB(), Ci['nsIXPCTestInterfaceB'],
+ interfaceComparator, dotEqualsComparator);
+
+ // Test arrays of iids.
+ doIs2Test("testInterfaceIsArray", [makeA(), makeA(), makeA(), makeA(), makeA()], 5, Ci['nsIXPCTestInterfaceA'],
+ [makeB(), makeB(), makeB()], 3, Ci['nsIXPCTestInterfaceB']);
+
+ // Test optional array size.
+ Assert.equal(o.testStringArrayOptionalSize(["some", "string", "array"]), "somestringarray");
+
+ // Test incorrect (too big) array size parameter; this should throw NOT_ENOUGH_ELEMENTS.
+ doTypedArrayMismatchTest("testShortArray", new Int16Array([-3, 7, 4]), 4,
+ new Int16Array([1, -32, 6]), 3);
+
+ // Test type mismatch (int16 <-> uint16); this should throw BAD_CONVERT_JS.
+ doTypedArrayMismatchTest("testShortArray", new Uint16Array([0, 7, 4, 3]), 4,
+ new Uint16Array([1, 5, 6]), 3);
+
+ // Test Sequence<T> types.
+ doTest("testShortSequence", [2, 4, 6], [1, 3, 5, 7], arrayComparator(standardComparator));
+ doTest("testDoubleSequence", [-10, -0.5], [1, 3, 1e11, -8e-5 ], arrayComparator(fuzzComparator));
+ doTest("testACStringSequence", ["mary", "hat", "hey", "lid", "tell", "lam"],
+ ["ids", "fleas", "woes", "wide", "has", "know", "!"],
+ arrayComparator(standardComparator));
+ doTest("testAStringSequence", ["沒有語言", "的偉大嗎?]"],
+ ["we", "are", "being", "sooo", "international", "right", "now"],
+ arrayComparator(standardComparator));
+
+ doTest("testInterfaceSequence", [makeA(), makeA()],
+ [makeA(), makeA(), makeA(), makeA(), makeA(), makeA()], arrayComparator(interfaceComparator));
+
+ doTest("testJsvalSequence", [{ cheese: 'whiz', apple: 8 }, [1, 5, '3'], /regex/],
+ ['apple', 2.2e10, 3.3e30, { only: "wheedle", except: {} }], arrayComparator(standardComparator));
+
+ doIsTest("testInterfaceIsSequence", [makeA(), makeA(), makeA(), makeA(), makeA()], Ci['nsIXPCTestInterfaceA'],
+ [makeB(), makeB(), makeB()], Ci['nsIXPCTestInterfaceB'],
+ arrayComparator(interfaceComparator), dotEqualsComparator);
+
+ var ret = o.testOptionalSequence();
+ Assert.ok(Array.isArray(ret));
+ Assert.equal(ret.length, 0);
+
+ ret = o.testOptionalSequence([]);
+ Assert.ok(Array.isArray(ret));
+ Assert.equal(ret.length, 0);
+
+ ret = o.testOptionalSequence([1, 2, 3]);
+ Assert.ok(Array.isArray(ret));
+ Assert.equal(ret.length, 3);
+
+ let jsObj = new TestParams();
+ o.testOmittedOptionalOut(jsObj);
+ ret = {};
+ o.testOmittedOptionalOut(jsObj, ret);
+ Assert.equal(ret.value.spec, "http://example.com/")
+
+ // Tests for large ArrayBuffers.
+ var ab = null;
+ try {
+ ab = new ArrayBuffer(4.5 * 1024 * 1024 * 1024); // 4.5 GB.
+ } catch (e) {
+ // Large ArrayBuffers not available (32-bit or disabled).
+ }
+ if (ab) {
+ var uint8 = new Uint8Array(ab);
+
+ // Test length check in JSArray2Native.
+ var ex = null;
+ try {
+ o.testOptionalSequence(uint8);
+ } catch (e) {
+ ex = e;
+ }
+ Assert.ok(ex.message.includes("Could not convert JavaScript argument arg 0"));
+
+ // Test length check for optional length argument in GetArraySizeFromParam.
+ ex = null;
+ try {
+ o.testByteArrayOptionalLength(uint8);
+ } catch (e) {
+ ex = e;
+ }
+ Assert.ok(ex.message.includes("Cannot convert JavaScript object into an array"));
+
+ // Smaller array views on the buffer are fine.
+ uint8 = new Uint8Array(ab, ab.byteLength - 3);
+ uint8[0] = 123;
+ Assert.equal(uint8.byteLength, 3);
+ Assert.equal(o.testOptionalSequence(uint8).toString(), "123,0,0");
+ Assert.equal(o.testByteArrayOptionalLength(uint8), 3);
+ }
+
+ Assert.ok(isNaN(o.testNaN), "Should handle returning NaNs");
+}
diff --git a/js/xpconnect/tests/unit/test_print_stderr.js b/js/xpconnect/tests/unit/test_print_stderr.js
new file mode 100644
index 0000000000..4c2d87ae7a
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_print_stderr.js
@@ -0,0 +1,14 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+add_task(async function() {
+ Assert.throws(
+ () => Cu.printStderr(),
+ /Not enough arguments/,
+ "Without argument printStderr throws"
+ );
+
+ const types = ["", "foo", 42, true, null, {foo: "bar"}];
+ types.forEach(type => Cu.printStderr(type));
+});
diff --git a/js/xpconnect/tests/unit/test_private_field_xrays.js b/js/xpconnect/tests/unit/test_private_field_xrays.js
new file mode 100644
index 0000000000..c3b4c73685
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_private_field_xrays.js
@@ -0,0 +1,56 @@
+'use strict';
+
+add_task(async function () {
+ let webnav = Services.appShell.createWindowlessBrowser(false);
+
+ let docShell = webnav.docShell;
+
+ docShell.createAboutBlankDocumentViewer(null, null);
+
+ let window = webnav.document.defaultView;
+
+ let iframe = window.eval(`
+ iframe = document.createElement("iframe");
+ iframe.id = "iframe"
+ document.body.appendChild(iframe)
+ iframe`);
+
+
+ let unwrapped = Cu.waiveXrays(iframe);
+
+
+ class Base {
+ constructor(o) {
+ return o;
+ }
+ };
+
+
+ class A extends Base {
+ #x = 12;
+ static gx(o) {
+ return o.#x;
+ }
+
+ static sx(o, v) {
+ o.#x = v;
+ }
+ };
+
+ new A(iframe);
+ Assert.equal(A.gx(iframe), 12);
+ A.sx(iframe, 'wrapped');
+
+ // Shouldn't tunnel past xray.
+ Assert.throws(() => A.gx(unwrapped), TypeError);
+ Assert.throws(() => A.sx(unwrapped, 'unwrapped'), TypeError);
+
+ new A(unwrapped);
+ Assert.equal(A.gx(unwrapped), 12);
+ Assert.equal(A.gx(iframe), 'wrapped');
+
+ A.sx(iframe, 'modified');
+ Assert.equal(A.gx(unwrapped), 12);
+ A.sx(unwrapped, 16);
+ Assert.equal(A.gx(iframe), 'modified');
+});
diff --git a/js/xpconnect/tests/unit/test_promise.js b/js/xpconnect/tests/unit/test_promise.js
new file mode 100644
index 0000000000..305e016fb5
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_promise.js
@@ -0,0 +1,7 @@
+function run_test() {
+ sb = new Cu.Sandbox('http://www.example.com');
+ sb.equal = equal;
+ Cu.evalInSandbox('equal(typeof new Promise(function(resolve){resolve();}), "object");',
+ sb);
+ Assert.equal(typeof new Promise(function(resolve){resolve();}), "object");
+}
diff --git a/js/xpconnect/tests/unit/test_recursive_import.js b/js/xpconnect/tests/unit/test_recursive_import.js
new file mode 100644
index 0000000000..94c6b0b7e9
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_recursive_import.js
@@ -0,0 +1,17 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+function run_test() {
+ var scope = {};
+ ChromeUtils.import("resource://test/recursive_importA.jsm", scope);
+
+ // A imported correctly
+ Assert.ok(scope.foo() == "foo");
+
+ // Symbols from B are visible through A
+ Assert.ok(scope.bar.baz() == "baz");
+
+ // Symbols from A are visible through A, B, A.
+ Assert.ok(scope.bar.qux.foo() == "foo");
+}
diff --git a/js/xpconnect/tests/unit/test_reflect_parse.js b/js/xpconnect/tests/unit/test_reflect_parse.js
new file mode 100644
index 0000000000..a96ce0bb61
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_reflect_parse.js
@@ -0,0 +1,27 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+({
+ loc:{start:{line:1, column:0}, end:{line:1, column:12}, source:null},
+ type:"Program",
+ body:[
+ {
+ loc:{start:{line:1, column:0}, end:{line:1, column:12}, source:null},
+ type:"ExpressionStatement",
+ expression:{
+ loc:{start:{line:1, column:0}, end:{line:1, column:12}, source:null},
+ type:"Literal",
+ value:"use strict"
+ }
+ }
+ ]
+})
+*/
+
+function run_test() {
+ // Reflect.parse is better tested in js shell; this basically tests its presence.
+ var parseData = Reflect.parse('"use strict"');
+ Assert.equal(parseData.body[0].expression.value, "use strict");
+}
diff --git a/js/xpconnect/tests/unit/test_resistFingerprinting_date_now.js b/js/xpconnect/tests/unit/test_resistFingerprinting_date_now.js
new file mode 100644
index 0000000000..1acd6c586c
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_resistFingerprinting_date_now.js
@@ -0,0 +1,16 @@
+Services.prefs.setBoolPref("privacy.resistFingerprinting", true);
+registerCleanupFunction(() => {
+ Services.prefs.clearUserPref("privacy.resistFingerprinting");
+});
+
+add_task(function test_sandbox_rfp() {
+ var sb = Cu.Sandbox('http://example.com');
+ var date = Cu.evalInSandbox('Date.now()', sb);
+ ok(date > 1672553000000, "Date.now() works with resistFingerprinting");
+});
+
+add_task(function test_sandbox_system() {
+ var sb = Cu.Sandbox(Services.scriptSecurityManager.getSystemPrincipal());
+ var date = Cu.evalInSandbox('Date.now()', sb);
+ ok(date > 1672553000000, "Date.now() works with systemprincipal");
+});
diff --git a/js/xpconnect/tests/unit/test_resolve_dead_promise.js b/js/xpconnect/tests/unit/test_resolve_dead_promise.js
new file mode 100644
index 0000000000..70615b39c0
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_resolve_dead_promise.js
@@ -0,0 +1,39 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* See https://bugzilla.mozilla.org/show_bug.cgi?id=1298597 */
+
+function run_test()
+{
+ var sb = Cu.Sandbox("http://www.blah.com");
+ var resolveFun;
+ var p1 = new sb.Promise((res, rej) => {resolveFun = res});
+ var rejectFun;
+ var p2 = new sb.Promise((res, rej) => {rejectFun = rej});
+ Cu.nukeSandbox(sb);
+ Assert.ok(Cu.isDeadWrapper(sb), "sb should be dead");
+ Assert.ok(Cu.isDeadWrapper(p1), "p1 should be dead");
+ Assert.ok(Cu.isDeadWrapper(p2), "p2 should be dead");
+
+ var exception;
+
+ try{
+ resolveFun(1);
+ Assert.ok(false);
+ } catch (e) {
+ exception = e;
+ }
+ Assert.ok(exception.toString().includes("can't access dead object"),
+ "Resolving dead wrapped promise should throw");
+
+ exception = undefined;
+ try{
+ rejectFun(1);
+ Assert.ok(false);
+ } catch (e) {
+ exception = e;
+ }
+ Assert.ok(exception.toString().includes("can't access dead object"),
+ "Rejecting dead wrapped promise should throw");
+}
diff --git a/js/xpconnect/tests/unit/test_returncode.js b/js/xpconnect/tests/unit/test_returncode.js
new file mode 100644
index 0000000000..de4289c013
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_returncode.js
@@ -0,0 +1,74 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+function getConsoleMessages() {
+ let consoleService = Cc["@mozilla.org/consoleservice;1"].getService(Ci.nsIConsoleService);
+ let messages = consoleService.getMessageArray().map((m) => m.toString());
+ // reset ready for the next call.
+ consoleService.reset();
+ return messages;
+}
+
+function run_test() {
+ // Load the component manifests.
+ registerXPCTestComponents();
+
+ // and the tests.
+ test_simple("@mozilla.org/js/xpc/test/native/ReturnCodeParent;1");
+ test_nested("@mozilla.org/js/xpc/test/native/ReturnCodeParent;1");
+
+ test_simple("@mozilla.org/js/xpc/test/native/ESMReturnCodeParent;1");
+ test_nested("@mozilla.org/js/xpc/test/native/ESMReturnCodeParent;1");
+}
+
+function test_simple(contractID) {
+ let parent = Cc[contractID].createInstance(Ci.nsIXPCTestReturnCodeParent);
+ let result;
+
+ // flush existing messages before we start testing.
+ getConsoleMessages();
+
+ // Ask the C++ to call the JS object which will throw.
+ result = parent.callChild(Ci.nsIXPCTestReturnCodeChild.CHILD_SHOULD_THROW);
+ Assert.equal(result, Cr.NS_ERROR_XPC_JAVASCRIPT_ERROR_WITH_DETAILS,
+ "exception caused NS_ERROR_XPC_JAVASCRIPT_ERROR_WITH_DETAILS");
+
+ let messages = getConsoleMessages();
+ Assert.equal(messages.length, 1, "got a console message from the exception");
+ Assert.ok(messages[0].includes("a requested error"), "got the message text");
+
+ // Ask the C++ to call the JS object which will return success.
+ result = parent.callChild(Ci.nsIXPCTestReturnCodeChild.CHILD_SHOULD_RETURN_SUCCESS);
+ Assert.equal(result, Cr.NS_OK, "success is success");
+
+ Assert.deepEqual(getConsoleMessages(), [], "no messages reported on success.");
+
+ // And finally the point of this test!
+ // Ask the C++ to call the JS object which will use .returnCode
+ result = parent.callChild(Ci.nsIXPCTestReturnCodeChild.CHILD_SHOULD_RETURN_RESULTCODE);
+ Assert.equal(result, Cr.NS_ERROR_FAILURE,
+ "NS_ERROR_FAILURE was seen as the error code.");
+
+ Assert.deepEqual(getConsoleMessages(), [], "no messages reported with .returnCode");
+}
+
+function test_nested(contractID) {
+ let parent = Cc[contractID].createInstance(Ci.nsIXPCTestReturnCodeParent);
+ let result;
+
+ // flush existing messages before we start testing.
+ getConsoleMessages();
+
+ // Ask the C++ to call the "outer" JS object, which will set .returnCode, but
+ // then create and call *another* component which itself sets the .returnCode
+ // to a different value. This checks the returnCode is correctly saved
+ // across call contexts.
+ result = parent.callChild(Ci.nsIXPCTestReturnCodeChild.CHILD_SHOULD_NEST_RESULTCODES);
+ Assert.equal(result, Cr.NS_ERROR_UNEXPECTED,
+ "NS_ERROR_UNEXPECTED was seen as the error code.");
+ // We expect one message, which is the child reporting what it got as the
+ // return code - which should be NS_ERROR_FAILURE
+ let expected = ["nested child returned " + Cr.NS_ERROR_FAILURE];
+ Assert.deepEqual(getConsoleMessages(), expected, "got the correct sub-error");
+}
diff --git a/js/xpconnect/tests/unit/test_rewrap_dead_wrapper.js b/js/xpconnect/tests/unit/test_rewrap_dead_wrapper.js
new file mode 100644
index 0000000000..10934f550b
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_rewrap_dead_wrapper.js
@@ -0,0 +1,31 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* See https://bugzilla.mozilla.org/show_bug.cgi?id=1354733 */
+
+const global = this;
+
+function run_test()
+{
+ var sb = Cu.Sandbox(global);
+ let obj = new sb.Object();
+ Cu.nukeSandbox(sb);
+
+ ok(Cu.isDeadWrapper(obj), "object should be a dead wrapper");
+
+ // Create a new sandbox to wrap objects for.
+
+ var sb = Cu.Sandbox(global);
+ Cu.evalInSandbox(function echo(val) { return val; },
+ sb);
+
+ let echoed = sb.echo(obj);
+ ok(Cu.isDeadWrapper(echoed), "Rewrapped object should be a dead wrapper");
+ ok(echoed !== obj, "Rewrapped object should be a new dead wrapper");
+
+ ok(obj === obj, "Dead wrapper object should be equal to itself");
+
+ let liveObj = {};
+ ok(liveObj === sb.echo(liveObj), "Rewrapped live object should be equal to itself");
+}
diff --git a/js/xpconnect/tests/unit/test_rtcIdentityProvider.js b/js/xpconnect/tests/unit/test_rtcIdentityProvider.js
new file mode 100644
index 0000000000..7786691513
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_rtcIdentityProvider.js
@@ -0,0 +1,34 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+function run_test() {
+ let sb = new Cu.Sandbox('https://www.example.com',
+ { wantGlobalProperties: ['rtcIdentityProvider'] });
+
+ function exerciseInterface() {
+ equal(typeof rtcIdentityProvider, 'object');
+ equal(typeof rtcIdentityProvider.register, 'function');
+ rtcIdentityProvider.register({
+ generateAssertion: function(a, b, c) {
+ return Promise.resolve({
+ idp: { domain: 'example.com' },
+ assertion: JSON.stringify([a, b, c])
+ });
+ },
+ validateAssertion: function(d, e) {
+ return Promise.resolve({
+ identity: 'user@example.com',
+ contents: JSON.stringify([d, e])
+ });
+ }
+ });
+ }
+
+ sb.equal = equal;
+ Cu.evalInSandbox('(' + exerciseInterface.toSource() + ')();', sb);
+ ok(sb.rtcIdentityProvider.hasIdp);
+
+ Cu.importGlobalProperties(['rtcIdentityProvider']);
+ exerciseInterface();
+ ok(rtcIdentityProvider.hasIdp);
+}
diff --git a/js/xpconnect/tests/unit/test_sandbox_DOMException.js b/js/xpconnect/tests/unit/test_sandbox_DOMException.js
new file mode 100644
index 0000000000..04592c6db2
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_sandbox_DOMException.js
@@ -0,0 +1,10 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+function run_test() {
+ var Cu = Components.utils;
+ var sb = new Cu.Sandbox('http://www.example.com',
+ { wantGlobalProperties: ["DOMException"] });
+ sb.notEqual = Assert.notEqual.bind(Assert);
+ Cu.evalInSandbox('notEqual(DOMException, undefined);', sb);
+}
diff --git a/js/xpconnect/tests/unit/test_sandbox_atob.js b/js/xpconnect/tests/unit/test_sandbox_atob.js
new file mode 100644
index 0000000000..a4bd75c13d
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_sandbox_atob.js
@@ -0,0 +1,9 @@
+function run_test() {
+ sb = new Cu.Sandbox('http://www.example.com',
+ { wantGlobalProperties: ["atob", "btoa"] });
+ sb.equal = equal;
+ Cu.evalInSandbox('var dummy = "Dummy test.";' +
+ 'equal(dummy, atob(btoa(dummy)));' +
+ 'equal(btoa("budapest"), "YnVkYXBlc3Q=");',
+ sb);
+}
diff --git a/js/xpconnect/tests/unit/test_sandbox_metadata.js b/js/xpconnect/tests/unit/test_sandbox_metadata.js
new file mode 100644
index 0000000000..2d0ebe36b0
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_sandbox_metadata.js
@@ -0,0 +1,57 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* See https://bugzilla.mozilla.org/show_bug.cgi?id=898559 */
+
+function run_test()
+{
+ let sandbox = Cu.Sandbox("http://www.blah.com", {
+ metadata: "test metadata",
+ });
+
+ Cu.importGlobalProperties(["XMLHttpRequest"]);
+
+ Assert.equal(Cu.getSandboxMetadata(sandbox), "test metadata");
+
+ sandbox = Cu.Sandbox("http://www.blah.com", {
+ metadata: { foopy: { bar: 2 }, baz: "hi" }
+ });
+
+ let metadata = Cu.getSandboxMetadata(sandbox);
+ Assert.equal(metadata.baz, "hi");
+ Assert.equal(metadata.foopy.bar, 2);
+ metadata.baz = "foo";
+
+ metadata = Cu.getSandboxMetadata(sandbox);
+ Assert.equal(metadata.baz, "foo");
+
+ metadata = { foo: "bar" };
+ Cu.setSandboxMetadata(sandbox, metadata);
+ metadata.foo = "baz";
+ metadata = Cu.getSandboxMetadata(sandbox);
+ Assert.equal(metadata.foo, "bar");
+
+ let thrown = false;
+ let reflector = new XMLHttpRequest();
+
+ try {
+ Cu.setSandboxMetadata(sandbox, { foo: reflector });
+ } catch(e) {
+ thrown = true;
+ }
+
+ Assert.equal(thrown, true);
+
+ sandbox = Cu.Sandbox(this, {
+ metadata: { foopy: { bar: 2 }, baz: "hi" }
+ });
+
+ let inner = Cu.evalInSandbox("Components.utils.Sandbox('http://www.blah.com')", sandbox);
+
+ metadata = Cu.getSandboxMetadata(inner);
+ Assert.equal(metadata.baz, "hi");
+ Assert.equal(metadata.foopy.bar, 2);
+ metadata.baz = "foo";
+}
+
diff --git a/js/xpconnect/tests/unit/test_sandbox_name.js b/js/xpconnect/tests/unit/test_sandbox_name.js
new file mode 100644
index 0000000000..1eaa971a9e
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_sandbox_name.js
@@ -0,0 +1,26 @@
+"use strict";
+
+/**
+ * Test that the name of a sandbox contains the name of all principals.
+ */
+function test_sandbox_name() {
+ let names = [
+ "http://example.com/?" + Math.random(),
+ "http://example.org/?" + Math.random()
+ ];
+ let sandbox = Cu.Sandbox(names);
+ let fileName = Cu.evalInSandbox(
+ "(new Error()).fileName",
+ sandbox,
+ "latest" /*js version*/,
+ ""/*file name*/
+ );
+
+ for (let name of names) {
+ Assert.ok(fileName.includes(name), `Name ${name} appears in ${fileName}`);
+ }
+};
+
+function run_test() {
+ test_sandbox_name();
+}
diff --git a/js/xpconnect/tests/unit/test_storage.js b/js/xpconnect/tests/unit/test_storage.js
new file mode 100644
index 0000000000..1ef6b653b5
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_storage.js
@@ -0,0 +1,12 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+function run_test() {
+ var sb = new Cu.Sandbox('http://www.example.com',
+ { wantGlobalProperties: ["storage"] });
+ sb.ok = ok;
+ Cu.evalInSandbox('ok(storage instanceof StorageManager);',
+ sb);
+ Cu.importGlobalProperties(["storage"]);
+ Assert.ok(storage instanceof StorageManager);
+}
diff --git a/js/xpconnect/tests/unit/test_structuredClone.js b/js/xpconnect/tests/unit/test_structuredClone.js
new file mode 100644
index 0000000000..9498006449
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_structuredClone.js
@@ -0,0 +1,32 @@
+function run_test() {
+ var sb = new Cu.Sandbox("http://www.example.com", {
+ wantGlobalProperties: ["structuredClone"],
+ });
+
+ sb.equal = equal;
+
+ sb.testing = Cu.cloneInto({ xyz: 123 }, sb);
+ Cu.evalInSandbox(
+ `
+ equal(structuredClone("abc"), "abc");
+
+ var obj = { a: 1 };
+ obj.self = obj;
+ var clone = structuredClone(obj);
+ equal(clone.a, 1);
+ equal(clone.self, clone);
+
+ var ab = new ArrayBuffer(1);
+ clone = structuredClone(ab, { transfer: [ab] });
+ equal(clone.byteLength, 1);
+ equal(ab.byteLength, 0);
+
+ clone = structuredClone(testing);
+ equal(clone.xyz, 123);
+ `,
+ sb
+ );
+
+ const clone = structuredClone({ b: 2 });
+ Assert.equal(clone.b, 2);
+}
diff --git a/js/xpconnect/tests/unit/test_subScriptLoader.js b/js/xpconnect/tests/unit/test_subScriptLoader.js
new file mode 100644
index 0000000000..70301f9da4
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_subScriptLoader.js
@@ -0,0 +1,16 @@
+"use strict";
+
+add_task(async function test_executeScriptAfterNuked() {
+ let scriptUrl = Services.io.newFileURI(do_get_file("file_simple_script.js")).spec;
+
+ // Load the script for the first time into a sandbox, and then nuke
+ // that sandbox.
+ let sandbox = Cu.Sandbox(Services.scriptSecurityManager.getSystemPrincipal());
+ Services.scriptloader.loadSubScript(scriptUrl, sandbox);
+ Cu.nukeSandbox(sandbox);
+
+ // Load the script again into a new sandbox, and make sure it
+ // succeeds.
+ sandbox = Cu.Sandbox(Services.scriptSecurityManager.getSystemPrincipal());
+ Services.scriptloader.loadSubScript(scriptUrl, sandbox);
+});
diff --git a/js/xpconnect/tests/unit/test_symbols_as_weak_keys.js b/js/xpconnect/tests/unit/test_symbols_as_weak_keys.js
new file mode 100644
index 0000000000..24734b7594
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_symbols_as_weak_keys.js
@@ -0,0 +1,40 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+function run_test()
+{
+ if (!Services.prefs.getBoolPref("javascript.options.experimental.symbols_as_weakmap_keys")) {
+ return;
+ }
+
+ var strKey = new String("strKey");
+ var symKey = Symbol("symKey");
+
+ var weakset = new WeakSet([strKey, symKey]);
+ var weakmap = new WeakMap();
+ weakmap.set(strKey, 23);
+ weakmap.set(symKey, "oh no");
+
+ var keys = ChromeUtils.nondeterministicGetWeakMapKeys(weakmap);
+ equal(keys.length, 2, "length of nondeterministicGetWeakMapKeys");
+ equal(weakmap.get(strKey), 23, "check strKey in weakmap");
+ equal(weakmap.get(symKey), "oh no", "check symKey in weakmap");
+
+ keys = ChromeUtils.nondeterministicGetWeakSetKeys(weakset);
+ equal(keys.length, 2, "length of nondeterministicGetWeakSetKeys");
+ ok(weakset.has(strKey), "check strKey in weakset");
+ ok(weakset.has(symKey), "check symKey in weakset");
+
+ strKey = null;
+ keys = null;
+
+ Cu.forceGC();
+
+ keys = ChromeUtils.nondeterministicGetWeakMapKeys(weakmap);
+ equal(keys.length, 1, "length of nondeterministicGetWeakMapKeys after GC");
+ equal(weakmap.get(symKey), "oh no", "check symKey still in weakmap");
+
+ keys = ChromeUtils.nondeterministicGetWeakSetKeys(weakset);
+ equal(keys.length, 1, "length of nondeterministicGetWeakSetKeys after GC");
+ ok(weakset.has(symKey), "check symKey still in weakset");
+}
diff --git a/js/xpconnect/tests/unit/test_tearoffs.js b/js/xpconnect/tests/unit/test_tearoffs.js
new file mode 100644
index 0000000000..18b07d1da1
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_tearoffs.js
@@ -0,0 +1,115 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+function TestInterfaceAll() {}
+TestInterfaceAll.prototype = {
+ QueryInterface: ChromeUtils.generateQI(["nsIXPCTestInterfaceA",
+ "nsIXPCTestInterfaceB",
+ "nsIXPCTestInterfaceC"]),
+
+ /* nsIXPCTestInterfaceA / nsIXPCTestInterfaceB */
+ name: "TestInterfaceAllDefaultName",
+
+ /* nsIXPCTestInterfaceC */
+ someInteger: 42
+};
+
+function newWrappedJS() {
+ return xpcWrap(new TestInterfaceAll());
+}
+
+function run_test() {
+ // Shortcut the interfaces we're using.
+ var ifs = {
+ a: Ci['nsIXPCTestInterfaceA'],
+ b: Ci['nsIXPCTestInterfaceB'],
+ c: Ci['nsIXPCTestInterfaceC']
+ };
+
+ // Run through the logic a few times.
+ for (let i = 0; i < 2; ++i)
+ play_with_tearoffs(ifs);
+}
+
+function play_with_tearoffs(ifs) {
+
+ // Allocate a bunch of objects, QI-ed to B.
+ var instances = [];
+ for (var i = 0; i < 300; ++i)
+ instances.push(newWrappedJS().QueryInterface(ifs.b));
+
+ // Nothing to collect.
+ gc();
+
+ // QI them to A.
+ instances.forEach(function(v, i, a) { v.QueryInterface(ifs.a); });
+
+ // QI them to C.
+ instances.forEach(function(v, i, a) { v.QueryInterface(ifs.c); });
+
+ // Check
+ Assert.ok('name' in instances[10], 'Have the prop from A/B');
+ Assert.ok('someInteger' in instances[10], 'Have the prop from C');
+
+ // Grab tearoff reflections for a and b.
+ var aTearOffs = instances.map(function(v, i, a) { return v.nsIXPCTestInterfaceA; } );
+ var bTearOffs = instances.map(function(v, i, a) { return v.nsIXPCTestInterfaceB; } );
+
+ // Check
+ Assert.ok('name' in aTearOffs[1], 'Have the prop from A');
+ Assert.ok(!('someInteger' in aTearOffs[1]), 'Dont have the prop from C');
+
+ // Nothing to collect.
+ gc();
+
+ // Null out every even instance pointer.
+ for (var i = 0; i < instances.length; ++i)
+ if (i % 2 == 0)
+ instances[i] = null;
+
+ // Nothing to collect, since we still have the A and B tearoff reflections.
+ gc();
+
+ // Null out A tearoff reflections that are a multiple of 3.
+ for (var i = 0; i < aTearOffs.length; ++i)
+ if (i % 3 == 0)
+ aTearOffs[i] = null;
+
+ // Nothing to collect, since we still have the B tearoff reflections.
+ gc();
+
+ // Null out B tearoff reflections that are a multiple of 5.
+ for (var i = 0; i < bTearOffs.length; ++i)
+ if (i % 5 == 0)
+ bTearOffs[i] = null;
+
+ // This should collect every 30th object (indices that are multiples of 2, 3, and 5).
+ gc();
+
+ // Kill the b tearoffs entirely.
+ bTearOffs = 0;
+
+ // Collect more.
+ gc();
+
+ // Get C tearoffs.
+ var cTearOffs = instances.map(function(v, i, a) { return v ? v.nsIXPCTestInterfaceC : null; } );
+
+ // Check.
+ Assert.ok(!('name' in cTearOffs[1]), 'Dont have the prop from A');
+ Assert.ok('someInteger' in cTearOffs[1], 'have the prop from C');
+
+ // Null out the a tearoffs.
+ aTearOffs = null;
+
+ // Collect all even indices.
+ gc();
+
+ // Collect all indices.
+ instances = null;
+ gc();
+
+ // Give ourselves a pat on the back. :-)
+ Assert.ok(true, "Got all the way through without crashing!");
+}
diff --git a/js/xpconnect/tests/unit/test_textDecoder.js b/js/xpconnect/tests/unit/test_textDecoder.js
new file mode 100644
index 0000000000..b97aab9f7c
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_textDecoder.js
@@ -0,0 +1,11 @@
+function run_test() {
+ sb = new Cu.Sandbox('http://www.example.com',
+ { wantGlobalProperties: ["TextDecoder", "TextEncoder"] });
+ sb.equal = equal;
+ Cu.evalInSandbox('equal(new TextDecoder().encoding, "utf-8");' +
+ 'equal(new TextEncoder().encoding, "utf-8");',
+ sb);
+ Cu.importGlobalProperties(["TextDecoder", "TextEncoder"]);
+ Assert.equal(new TextDecoder().encoding, "utf-8");
+ Assert.equal(new TextEncoder().encoding, "utf-8");
+}
diff --git a/js/xpconnect/tests/unit/test_uawidget_scope.js b/js/xpconnect/tests/unit/test_uawidget_scope.js
new file mode 100644
index 0000000000..5d83cdffbb
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_uawidget_scope.js
@@ -0,0 +1,55 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+const {NetUtil} = ChromeUtils.importESModule("resource://gre/modules/NetUtil.sys.mjs");
+const {TestUtils} = ChromeUtils.importESModule("resource://testing-common/TestUtils.sys.mjs");
+
+function getWindowlessBrowser(url) {
+ let ssm = Services.scriptSecurityManager;
+ let uri = NetUtil.newURI(url);
+ let principal = ssm.createContentPrincipal(uri, {});
+ let webnav = Services.appShell.createWindowlessBrowser(false);
+
+ let docShell = webnav.docShell;
+ docShell.createAboutBlankDocumentViewer(principal, principal);
+
+ let document = webnav.document;
+ let video = document.createElement("video");
+ document.documentElement.appendChild(video);
+
+ let shadowRoot = video.openOrClosedShadowRoot;
+ ok(shadowRoot, "should have shadowRoot");
+ ok(shadowRoot.isUAWidget(), "ShadowRoot should be a UAWidget");
+ equal(Cu.getGlobalForObject(shadowRoot), Cu.getUAWidgetScope(principal),
+ "shadowRoot should be in UAWidget scope");
+
+ return webnav;
+}
+
+function StubPolicy(id) {
+ return new WebExtensionPolicy({
+ id,
+ mozExtensionHostname: id,
+ baseURL: `file:///{id}`,
+ allowedOrigins: new MatchPatternSet([]),
+ localizeCallback(string) {},
+ });
+}
+
+// See https://bugzilla.mozilla.org/show_bug.cgi?id=1588356
+add_task(async function() {
+ let policy = StubPolicy("foo");
+ policy.active = true;
+
+ let webnav = getWindowlessBrowser("moz-extension://foo/a.html");
+ webnav.close();
+
+ // Wrappers are nuked asynchronously, so wait for that to happen.
+ await TestUtils.topicObserved("inner-window-nuked");
+
+ webnav = getWindowlessBrowser("moz-extension://foo/a.html");
+ webnav.close();
+
+ policy.active = false;
+});
diff --git a/js/xpconnect/tests/unit/test_uninitialized_lexical.js b/js/xpconnect/tests/unit/test_uninitialized_lexical.js
new file mode 100644
index 0000000000..f5f2f254ee
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_uninitialized_lexical.js
@@ -0,0 +1,7 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+Assert.throws(() => ChromeUtils.import("resource://test/uninitialized_lexical.jsm"),
+ /Symbol 'foo' accessed before initialization/,
+ "Uninitialized lexicals result in an exception");
diff --git a/js/xpconnect/tests/unit/test_unload.js b/js/xpconnect/tests/unit/test_unload.js
new file mode 100644
index 0000000000..13e2e0c63e
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_unload.js
@@ -0,0 +1,28 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+function run_test() {
+ var scope1 = {};
+ var exports1 = ChromeUtils.import("resource://test/TestBlob.jsm", scope1);
+
+ var scope2 = {};
+ var exports2 = ChromeUtils.import("resource://test/TestBlob.jsm", scope2);
+
+ Assert.ok(exports1 === exports2);
+ Assert.ok(scope1.TestBlob === scope2.TestBlob);
+
+ Cu.unload("resource://test/TestBlob.jsm");
+
+ var scope3 = {};
+ var exports3 = ChromeUtils.import("resource://test/TestBlob.jsm", scope3);
+
+ Assert.equal(false, exports1 === exports3);
+ Assert.equal(false, scope1.TestBlob === scope3.TestBlob);
+
+ // When the jsm was unloaded, the value of all its global's properties were
+ // set to undefined. While it must be safe (not crash) to call into the
+ // module, we expect it to throw an error (e.g., when trying to use Ci).
+ try { scope1.TestBlob.doTest(() => {}); } catch (e) {}
+ try { scope3.TestBlob.doTest(() => {}); } catch (e) {}
+}
diff --git a/js/xpconnect/tests/unit/test_url.js b/js/xpconnect/tests/unit/test_url.js
new file mode 100644
index 0000000000..0f912d12e9
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_url.js
@@ -0,0 +1,9 @@
+function run_test() {
+ var sb = new Cu.Sandbox('http://www.example.com',
+ { wantGlobalProperties: ["URL"] });
+ sb.equal = equal;
+ Cu.evalInSandbox('equal(new URL("http://www.example.com").host, "www.example.com");',
+ sb);
+ Cu.importGlobalProperties(["URL"]);
+ Assert.equal(new URL("http://www.example.com").host, "www.example.com");
+}
diff --git a/js/xpconnect/tests/unit/test_want_components.js b/js/xpconnect/tests/unit/test_want_components.js
new file mode 100644
index 0000000000..1c203c3e9d
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_want_components.js
@@ -0,0 +1,16 @@
+function run_test() {
+ var sb;
+
+ sb = Cu.Sandbox(this, {wantComponents: false});
+ Assert.equal(Cu.evalInSandbox("this.Components", sb), undefined);
+ Assert.equal(Cu.evalInSandbox("this.Services", sb), undefined);
+
+ sb = Cu.Sandbox(this, {wantComponents: true});
+ Assert.equal(Cu.evalInSandbox("typeof this.Components", sb), "object");
+ Assert.equal(Cu.evalInSandbox("typeof this.Services", sb), "object");
+
+ // wantComponents defaults to true.
+ sb = Cu.Sandbox(this, {});
+ Assert.equal(Cu.evalInSandbox("typeof this.Components", sb), "object");
+ Assert.equal(Cu.evalInSandbox("typeof this.Services", sb), "object");
+}
diff --git a/js/xpconnect/tests/unit/test_wasm_tailcalls_profiler.js b/js/xpconnect/tests/unit/test_wasm_tailcalls_profiler.js
new file mode 100644
index 0000000000..64f29b9510
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_wasm_tailcalls_profiler.js
@@ -0,0 +1,122 @@
+Services.prefs.setBoolPref("javascript.options.wasm_tail_calls", true);
+registerCleanupFunction(() => {
+ Services.prefs.clearUserPref("javascript.options.wasm_tail_calls");
+});
+
+// The tests runs code in tight loop with the profiler enabled. It is testing
+// behavoir of MacroAssembler::wasmCollapseFrameXXXX methods.
+// It is not guarantee 100% hit since the profiler probes stacks every 1ms,
+// but it will happen often enough.
+add_task(async () => {
+ await Services.profiler.StartProfiler(10, 1, ["js"], ["GeckoMain"]);
+ Assert.ok(Services.profiler.IsActive());
+
+/* Wasm module that is tested:
+(module
+ (func $f (param i64 i64 i64 i64 i64 i64 i64 i64 i64)
+ local.get 0
+ i64.eqz
+ br_if 0
+ local.get 0
+ return_call $g
+ )
+ (func $g (param i64)
+ local.get 0
+ i64.const 1
+ i64.sub
+ i64.const 2
+ i64.const 6
+ i64.const 3
+ i64.const 4
+ i64.const 1
+ i64.const 2
+ i64.const 6
+ i64.const 3
+ return_call $f
+ )
+ (func (export "run")
+ i64.const 0x100000
+ call $g
+ )
+)
+*/
+
+ const b = new Uint8Array([
+ 0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x14, 0x03, 0x60,
+ 0x09, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x7e, 0x00, 0x60,
+ 0x01, 0x7e, 0x00, 0x60, 0x00, 0x00, 0x03, 0x04, 0x03, 0x00, 0x01, 0x02,
+ 0x07, 0x07, 0x01, 0x03, 0x72, 0x75, 0x6e, 0x00, 0x02, 0x0a, 0x31, 0x03,
+ 0x0b, 0x00, 0x20, 0x00, 0x50, 0x0d, 0x00, 0x20, 0x00, 0x12, 0x01, 0x0b,
+ 0x19, 0x00, 0x20, 0x00, 0x42, 0x01, 0x7d, 0x42, 0x02, 0x42, 0x06, 0x42,
+ 0x03, 0x42, 0x04, 0x42, 0x01, 0x42, 0x02, 0x42, 0x06, 0x42, 0x03, 0x12,
+ 0x00, 0x0b, 0x09, 0x00, 0x42, 0x80, 0x80, 0xc0, 0x00, 0x10, 0x01, 0x0b
+ ]);
+ const ins = new WebAssembly.Instance(new WebAssembly.Module(b));
+ for (var i = 0; i < 100; i++) {
+ ins.exports.run();
+ }
+
+ Assert.ok(true, "Done");
+ await Services.profiler.StopProfiler();
+});
+
+add_task(async () => {
+ await Services.profiler.StartProfiler(10, 1, ["js"], ["GeckoMain"]);
+ Assert.ok(Services.profiler.IsActive());
+
+/* Wasm modules that are tested:
+(module (func (export "t")))
+
+(module
+ (import "" "t" (func $g))
+ (table $t 1 1 funcref)
+
+ (func $f (return_call_indirect $t (i32.const 0)))
+ (func (export "run") (param i64)
+ loop
+ local.get 0
+ i64.eqz
+ br_if 1
+ call $f
+ local.get 0
+ i64.const 1
+ i64.sub
+ local.set 0
+ br 0
+ end
+ )
+ (elem (i32.const 0) $g)
+)
+*/
+ const b0 = new Uint8Array([
+ 0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x04, 0x01, 0x60,
+ 0x00, 0x00, 0x03, 0x02, 0x01, 0x00, 0x07, 0x05, 0x01, 0x01, 0x74, 0x00,
+ 0x00, 0x0a, 0x04, 0x01, 0x02, 0x00, 0x0b
+ ]);
+ const ins0 = new WebAssembly.Instance(new WebAssembly.Module(b0));
+ const b = new Uint8Array([
+ 0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x08, 0x02, 0x60,
+ 0x00, 0x00, 0x60, 0x01, 0x7e, 0x00, 0x02, 0x06, 0x01, 0x00, 0x01, 0x74,
+ 0x00, 0x00, 0x03, 0x03, 0x02, 0x00, 0x01, 0x04, 0x05, 0x01, 0x70, 0x01,
+ 0x01, 0x01, 0x07, 0x07, 0x01, 0x03, 0x72, 0x75, 0x6e, 0x00, 0x02, 0x09,
+ 0x07, 0x01, 0x00, 0x41, 0x00, 0x0b, 0x01, 0x00, 0x0a, 0x1f, 0x02, 0x07,
+ 0x00, 0x41, 0x00, 0x13, 0x00, 0x00, 0x0b, 0x15, 0x00, 0x03, 0x40, 0x20,
+ 0x00, 0x50, 0x0d, 0x01, 0x10, 0x01, 0x20, 0x00, 0x42, 0x01, 0x7d, 0x21,
+ 0x00, 0x0c, 0x00, 0x0b, 0x0b
+ ]);
+ const ins = new WebAssembly.Instance(new WebAssembly.Module(b), {"": {t: ins0.exports.t,},});
+ for (var i = 0; i < 100; i++) {
+ ins.exports.run(0x100000n);
+ }
+
+ Assert.ok(true, "Done");
+ await Services.profiler.StopProfiler();
+});
+
+/**
+ * All the tests are implemented with add_task, this starts them automatically.
+ */
+function run_test() {
+ do_get_profile();
+ run_next_test();
+}
diff --git a/js/xpconnect/tests/unit/test_watchdog_default.js b/js/xpconnect/tests/unit/test_watchdog_default.js
new file mode 100644
index 0000000000..4634184f3c
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_watchdog_default.js
@@ -0,0 +1,9 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+function testBody() {
+ // Check that we properly implement whatever behavior is specified by the
+ // default profile for this configuration.
+ return checkWatchdog(isWatchdogEnabled());
+}
diff --git a/js/xpconnect/tests/unit/test_watchdog_disable.js b/js/xpconnect/tests/unit/test_watchdog_disable.js
new file mode 100644
index 0000000000..926edf2ffa
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_watchdog_disable.js
@@ -0,0 +1,8 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+function testBody() {
+ setWatchdogEnabled(false);
+ return checkWatchdog(false);
+}
diff --git a/js/xpconnect/tests/unit/test_watchdog_enable.js b/js/xpconnect/tests/unit/test_watchdog_enable.js
new file mode 100644
index 0000000000..f39757dfae
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_watchdog_enable.js
@@ -0,0 +1,8 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+function testBody() {
+ setWatchdogEnabled(true);
+ return checkWatchdog(true);
+}
diff --git a/js/xpconnect/tests/unit/test_watchdog_hibernate.js b/js/xpconnect/tests/unit/test_watchdog_hibernate.js
new file mode 100644
index 0000000000..119cc095e2
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_watchdog_hibernate.js
@@ -0,0 +1,49 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+async function testBody() {
+
+ setWatchdogEnabled(true);
+
+ // It's unlikely that we've ever hibernated at this point, but the timestamps
+ // default to 0, so this should always be true.
+ var now = Date.now() * 1000;
+ var startHibernation = Cu.getWatchdogTimestamp("WatchdogHibernateStart");
+ var stopHibernation = Cu.getWatchdogTimestamp("WatchdogHibernateStop");
+ do_log_info("Pre-hibernation statistics:");
+ do_log_info("now: " + now / 1000000);
+ do_log_info("startHibernation: " + startHibernation / 1000000);
+ do_log_info("stopHibernation: " + stopHibernation / 1000000);
+ Assert.less(startHibernation, now, "startHibernation ok");
+ Assert.less(stopHibernation, now, "stopHibernation ok");
+
+ // When the watchdog runs, it hibernates if there's been no activity for the
+ // last 2 seconds, otherwise it sleeps for 1 second. As such, given perfect
+ // scheduling, we should never have more than 3 seconds of inactivity without
+ // hibernating. To add some padding for automation, we mandate that hibernation
+ // must begin between 2 and 5 seconds from now.
+
+ // Sleep for 10 seconds. Note: we don't use nsITimer here because then we may run
+ // arbitrary (idle) events involving script before it fires.
+ simulateNoScriptActivity(10);
+
+ busyWait(1000); // Give the watchdog time to wake up on the condvar.
+ var stateChange = Cu.getWatchdogTimestamp("ContextStateChange");
+ startHibernation = Cu.getWatchdogTimestamp("WatchdogHibernateStart");
+ stopHibernation = Cu.getWatchdogTimestamp("WatchdogHibernateStop");
+ do_log_info("Post-hibernation statistics:");
+ do_log_info("stateChange: " + stateChange / 1000000);
+ do_log_info("startHibernation: " + startHibernation / 1000000);
+ do_log_info("stopHibernation: " + stopHibernation / 1000000);
+ // XPCOM timers, JS times, and PR_Now() are apparently not directly
+ // comparable, as evidenced by certain seemingly-impossible timing values
+ // that occasionally get logged in windows automation. We're really just
+ // making sure this behavior is roughly as expected on the macro scale,
+ // so we add a 1 second fuzz factor here.
+ const FUZZ_FACTOR = 1 * 1000 * 1000;
+ Assert.greater(stateChange, now + 10*1000*1000 - FUZZ_FACTOR, "stateChange ok");
+ Assert.greater(startHibernation, now + 2*1000*1000 - FUZZ_FACTOR, "startHibernation ok");
+ Assert.less(startHibernation, now + 5*1000*1000 + FUZZ_FACTOR, "startHibernation ok");
+ Assert.greater(stopHibernation, now + 10*1000*1000 - FUZZ_FACTOR, "stopHibernation ok");
+}
diff --git a/js/xpconnect/tests/unit/test_watchdog_toggle.js b/js/xpconnect/tests/unit/test_watchdog_toggle.js
new file mode 100644
index 0000000000..6f43a8b876
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_watchdog_toggle.js
@@ -0,0 +1,10 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+function testBody() {
+ var defaultBehavior = isWatchdogEnabled();
+ setWatchdogEnabled(!defaultBehavior);
+ setWatchdogEnabled(defaultBehavior);
+ return checkWatchdog(defaultBehavior);
+}
diff --git a/js/xpconnect/tests/unit/test_weak_keys.js b/js/xpconnect/tests/unit/test_weak_keys.js
new file mode 100644
index 0000000000..58e3237bd8
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_weak_keys.js
@@ -0,0 +1,45 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* See https://bugzilla.mozilla.org/show_bug.cgi?id=1165807 */
+
+function run_test()
+{
+ var bunnies = new String("bunnies");
+ var lizards = new String("lizards");
+
+ var weakset = new WeakSet([bunnies, lizards]);
+ var weakmap = new WeakMap();
+ weakmap.set(bunnies, 23);
+ weakmap.set(lizards, "oh no");
+
+ var keys = ChromeUtils.nondeterministicGetWeakMapKeys(bunnies);
+ equal(keys, undefined, "test nondeterministicGetWeakMapKeys on non-WeakMap");
+
+ keys = ChromeUtils.nondeterministicGetWeakMapKeys(weakmap);
+ equal(keys.length, 2, "length of nondeterministicGetWeakMapKeys");
+ equal(weakmap.get(bunnies), 23, "check bunnies in weakmap");
+ equal(weakmap.get(lizards), "oh no", "check lizards in weakmap");
+
+ keys = ChromeUtils.nondeterministicGetWeakSetKeys(bunnies);
+ equal(keys, undefined, "test nondeterministicGetWeakSetKeys on non-WeakMap");
+
+ keys = ChromeUtils.nondeterministicGetWeakSetKeys(weakset);
+ equal(keys.length, 2, "length of nondeterministicGetWeakSetKeys");
+ ok(weakset.has(bunnies), "check bunnies in weakset");
+ ok(weakset.has(lizards), "check lizards in weakset");
+
+ bunnies = null;
+ keys = null;
+
+ Cu.forceGC();
+
+ keys = ChromeUtils.nondeterministicGetWeakMapKeys(weakmap);
+ equal(keys.length, 1, "length of nondeterministicGetWeakMapKeys after GC");
+ equal(weakmap.get(lizards), "oh no", "check lizards still in weakmap");
+
+ keys = ChromeUtils.nondeterministicGetWeakSetKeys(weakset);
+ equal(keys.length, 1, "length of nondeterministicGetWeakSetKeys after GC");
+ ok(weakset.has(lizards), "check lizards still in weakset");
+}
diff --git a/js/xpconnect/tests/unit/test_wrapped_js_enumerator.js b/js/xpconnect/tests/unit/test_wrapped_js_enumerator.js
new file mode 100644
index 0000000000..5a366ba25d
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_wrapped_js_enumerator.js
@@ -0,0 +1,71 @@
+"use strict";
+
+// Tests that JS iterators are automatically wrapped into
+// equivalent nsISimpleEnumerator objects.
+
+const Variant = Components.Constructor("@mozilla.org/variant;1",
+ "nsIWritableVariant",
+ "setFromVariant");
+const SupportsInterfacePointer = Components.Constructor(
+ "@mozilla.org/supports-interface-pointer;1", "nsISupportsInterfacePointer");
+
+function wrapEnumerator1(iter) {
+ var ip = SupportsInterfacePointer();
+ ip.data = iter;
+ return ip.data.QueryInterface(Ci.nsISimpleEnumerator);
+}
+
+function wrapEnumerator2(iter) {
+ var ip = SupportsInterfacePointer();
+ ip.data = {
+ QueryInterface: ChromeUtils.generateQI(["nsIFilePicker"]),
+ get files() {
+ return iter;
+ },
+ };
+ return ip.data.QueryInterface(Ci.nsIFilePicker).files;
+}
+
+
+function enumToArray(iter) {
+ let result = [];
+ while (iter.hasMoreElements()) {
+ result.push(iter.getNext().QueryInterface(Ci.nsIVariant));
+ }
+ return result;
+}
+
+add_task(async function test_wrapped_js_enumerator() {
+ let array = [1, 2, 3, 4];
+
+ for (let wrapEnumerator of [wrapEnumerator1, wrapEnumerator2]) {
+ // Test a plain JS iterator. This should automatically be wrapped into
+ // an equivalent nsISimpleEnumerator.
+ {
+ let iter = wrapEnumerator(array.values());
+ let result = enumToArray(iter);
+
+ deepEqual(result, array, "Got correct result");
+ }
+
+ // Test an object with a QueryInterface method, which implements
+ // nsISimpleEnumerator. This should be wrapped and used directly.
+ {
+ let obj = {
+ QueryInterface: ChromeUtils.generateQI(["nsISimpleEnumerator"]),
+ _idx: 0,
+ hasMoreElements() {
+ return this._idx < array.length;
+ },
+ getNext() {
+ return Variant(array[this._idx++]);
+ },
+ };
+
+ let iter = wrapEnumerator(obj);
+ let result = enumToArray(iter);
+
+ deepEqual(result, array, "Got correct result");
+ }
+ }
+});
diff --git a/js/xpconnect/tests/unit/test_xpcomutils.js b/js/xpconnect/tests/unit/test_xpcomutils.js
new file mode 100644
index 0000000000..90bc80ebf4
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_xpcomutils.js
@@ -0,0 +1,248 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 4 -*-
+ * vim: sw=4 ts=4 sts=4 et
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/**
+ * This file tests the methods on XPCOMUtils.sys.mjs.
+ * Also on ComponentUtils.jsm. Which is deprecated.
+ */
+
+const {AppConstants} = ChromeUtils.importESModule("resource://gre/modules/AppConstants.sys.mjs");
+const {ComponentUtils} = ChromeUtils.importESModule("resource://gre/modules/ComponentUtils.sys.mjs");
+const {Preferences} = ChromeUtils.importESModule("resource://gre/modules/Preferences.sys.mjs");
+const {XPCOMUtils} = ChromeUtils.importESModule("resource://gre/modules/XPCOMUtils.sys.mjs");
+
+////////////////////////////////////////////////////////////////////////////////
+//// Tests
+
+add_test(function test_generateQI_string_names()
+{
+ var x = {
+ QueryInterface: ChromeUtils.generateQI([
+ "nsIClassInfo",
+ "nsIObserver"
+ ])
+ };
+
+ try {
+ x.QueryInterface(Ci.nsIClassInfo);
+ } catch(e) {
+ do_throw("Should QI to nsIClassInfo");
+ }
+ try {
+ x.QueryInterface(Ci.nsIObserver);
+ } catch(e) {
+ do_throw("Should QI to nsIObserver");
+ }
+ try {
+ x.QueryInterface(Ci.nsIObserverService);
+ do_throw("QI should not have succeeded!");
+ } catch(e) {}
+ run_next_test();
+});
+
+add_test(function test_defineLazyServiceGetter()
+{
+ let obj = { };
+ XPCOMUtils.defineLazyServiceGetter(obj, "service",
+ "@mozilla.org/consoleservice;1",
+ "nsIConsoleService");
+ let service = Cc["@mozilla.org/consoleservice;1"].
+ getService(Ci.nsIConsoleService);
+
+ // Check that the lazy service getter and the actual service have the same
+ // properties.
+ for (let prop in obj.service)
+ Assert.ok(prop in service);
+ for (let prop in service)
+ Assert.ok(prop in obj.service);
+ run_next_test();
+});
+
+
+add_test(function test_defineLazyPreferenceGetter()
+{
+ const PREF = "xpcomutils.test.pref";
+
+ let obj = {};
+ XPCOMUtils.defineLazyPreferenceGetter(obj, "pref", PREF, "defaultValue");
+
+
+ equal(obj.pref, "defaultValue", "Should return the default value before pref is set");
+
+ Preferences.set(PREF, "currentValue");
+
+
+ info("Create second getter on new object");
+
+ obj = {};
+ XPCOMUtils.defineLazyPreferenceGetter(obj, "pref", PREF, "defaultValue");
+
+
+ equal(obj.pref, "currentValue", "Should return the current value on initial read when pref is already set");
+
+ Preferences.set(PREF, "newValue");
+
+ equal(obj.pref, "newValue", "Should return new value after preference change");
+
+ Preferences.set(PREF, "currentValue");
+
+ equal(obj.pref, "currentValue", "Should return new value after second preference change");
+
+
+ Preferences.reset(PREF);
+
+ equal(obj.pref, "defaultValue", "Should return default value after pref is reset");
+
+ obj = {};
+ XPCOMUtils.defineLazyPreferenceGetter(obj, "pref", PREF, "a,b",
+ null, value => value.split(","));
+
+ deepEqual(obj.pref, ["a", "b"], "transform is applied to default value");
+
+ Preferences.set(PREF, "x,y,z");
+ deepEqual(obj.pref, ["x", "y", "z"], "transform is applied to updated value");
+
+ Preferences.reset(PREF);
+ deepEqual(obj.pref, ["a", "b"], "transform is applied to reset default");
+
+ if (AppConstants.DEBUG) {
+ // Need to use a 'real' pref so it will have a valid prefType
+ obj = {};
+ Assert.throws(
+ () => XPCOMUtils.defineLazyPreferenceGetter(obj, "pref", "javascript.enabled", 1),
+ /Default value does not match preference type/,
+ "Providing a default value of a different type than the preference throws an exception"
+ );
+ }
+
+ run_next_test();
+});
+
+
+add_test(function test_categoryRegistration()
+{
+ const CATEGORY_NAME = "test-cat";
+ const XULAPPINFO_CONTRACTID = "@mozilla.org/xre/app-info;1";
+ const XULAPPINFO_CID = Components.ID("{fc937916-656b-4fb3-a395-8c63569e27a8}");
+
+ // Create a fake app entry for our category registration apps filter.
+ let { newAppInfo } = ChromeUtils.importESModule("resource://testing-common/AppInfo.sys.mjs");
+ let XULAppInfo = newAppInfo({
+ name: "catRegTest",
+ ID: "{adb42a9a-0d19-4849-bf4d-627614ca19be}",
+ version: "1",
+ platformVersion: "",
+ });
+ let XULAppInfoFactory = {
+ createInstance: function (iid) {
+ return XULAppInfo.QueryInterface(iid);
+ }
+ };
+ let registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
+ registrar.registerFactory(
+ XULAPPINFO_CID,
+ "XULAppInfo",
+ XULAPPINFO_CONTRACTID,
+ XULAppInfoFactory
+ );
+
+ // Load test components.
+ do_load_manifest("CatRegistrationComponents.manifest");
+
+ const expectedEntries = new Map([
+ ["CatRegisteredComponent", "@unit.test.com/cat-registered-component;1"],
+ ["CatAppRegisteredComponent", "@unit.test.com/cat-app-registered-component;1"],
+ ]);
+
+ // Verify the correct entries are registered in the "test-cat" category.
+ for (let {entry, value} of Services.catMan.enumerateCategory(CATEGORY_NAME)) {
+ ok(expectedEntries.has(entry), `${entry} is expected`);
+ Assert.equal(value, expectedEntries.get(entry), "${entry} has correct value.");
+ expectedEntries.delete(entry);
+ }
+ Assert.deepEqual(
+ Array.from(expectedEntries.keys()),
+ [],
+ "All expected entries have been deleted."
+ );
+ run_next_test();
+});
+
+add_test(function test_categoryBackgroundTaskRegistration()
+{
+ const CATEGORY_NAME = "test-cat1";
+
+ // Note that this test should succeed whether or not MOZ_BACKGROUNDTASKS is
+ // defined. If it's defined, there's no active task so the `backgroundtask`
+ // directive is processed, dropped, and always succeeds. If it's not defined,
+ // then the `backgroundtask` directive is processed and ignored.
+
+ // Load test components.
+ do_load_manifest("CatBackgroundTaskRegistrationComponents.manifest");
+
+ let expectedEntriesList = [
+ ["Cat1RegisteredComponent", "@unit.test.com/cat1-registered-component;1"],
+ ["Cat1BackgroundTaskNotRegisteredComponent", "@unit.test.com/cat1-backgroundtask-notregistered-component;1"],
+ ];
+ if (!AppConstants.MOZ_BACKGROUNDTASKS) {
+ expectedEntriesList.push(...[
+ ["Cat1BackgroundTaskRegisteredComponent", "@unit.test.com/cat1-backgroundtask-registered-component;1"],
+ ["Cat1BackgroundTaskAlwaysRegisteredComponent", "@unit.test.com/cat1-backgroundtask-alwaysregistered-component;1"],
+ ]);
+ }
+ const expectedEntries = new Map(expectedEntriesList);
+
+ // Verify the correct entries are registered in the "test-cat" category.
+ for (let {entry, value} of Services.catMan.enumerateCategory(CATEGORY_NAME)) {
+ ok(expectedEntries.has(entry), `${entry} is expected`);
+ Assert.equal(value, expectedEntries.get(entry), "Verify that the value is correct in the expected entries.");
+ expectedEntries.delete(entry);
+ }
+ Assert.deepEqual(
+ Array.from(expectedEntries.keys()),
+ [],
+ "All expected entries have been deleted."
+ );
+ run_next_test();
+});
+
+add_test(function test_generateSingletonFactory()
+{
+ const XPCCOMPONENT_CONTRACTID = "@mozilla.org/singletonComponentTest;1";
+ const XPCCOMPONENT_CID = Components.ID("{31031c36-5e29-4dd9-9045-333a5d719a3e}");
+
+ function XPCComponent() {}
+ XPCComponent.prototype = {
+ classID: XPCCOMPONENT_CID,
+ QueryInterface: ChromeUtils.generateQI([])
+ };
+ let registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
+ registrar.registerFactory(
+ XPCCOMPONENT_CID,
+ "XPCComponent",
+ XPCCOMPONENT_CONTRACTID,
+ ComponentUtils.generateSingletonFactory(XPCComponent)
+ );
+
+ // First, try to instance the component.
+ let instance = Cc[XPCCOMPONENT_CONTRACTID].createInstance(Ci.nsISupports);
+ // Try again, check that it returns the same instance as before.
+ Assert.equal(instance,
+ Cc[XPCCOMPONENT_CONTRACTID].createInstance(Ci.nsISupports));
+ // Now, for sanity, check that getService is also returning the same instance.
+ Assert.equal(instance,
+ Cc[XPCCOMPONENT_CONTRACTID].getService(Ci.nsISupports));
+
+ run_next_test();
+});
+
+////////////////////////////////////////////////////////////////////////////////
+//// Test Runner
+
+function run_test()
+{
+ run_next_test();
+}
diff --git a/js/xpconnect/tests/unit/test_xpcwn_instanceof.js b/js/xpconnect/tests/unit/test_xpcwn_instanceof.js
new file mode 100644
index 0000000000..96268fd0b4
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_xpcwn_instanceof.js
@@ -0,0 +1,23 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// Tests for custom `instanceof` behavior via XPC_SCRIPTABLE_WANT_HASINSTANCE.
+
+add_task(function id_instanceof() {
+ // ID objects are instances of Components.ID.
+ let id = Components.ID("{f2f5c784-7f6c-43f5-81b0-45ff32c312b1}");
+ Assert.equal(id instanceof Components.ID, true);
+ Assert.equal({} instanceof Components.ID, false);
+ Assert.equal(null instanceof Components.ID, false);
+
+ // Components.ID has a Symbol.hasInstance function.
+ let desc = Object.getOwnPropertyDescriptor(Components.ID, Symbol.hasInstance);
+ Assert.equal(typeof desc, "object");
+ Assert.equal(typeof desc.value, "function");
+
+ // Test error handling when calling this function with unexpected values.
+ Assert.throws(() => desc.value.call(null), /At least 1 argument required/);
+ Assert.throws(() => desc.value.call(null, 1), /unexpected this value/);
+ Assert.throws(() => desc.value.call({}, {}), /NS_ERROR_XPC_BAD_OP_ON_WN_PROTO/);
+});
diff --git a/js/xpconnect/tests/unit/test_xpcwn_tamperproof.js b/js/xpconnect/tests/unit/test_xpcwn_tamperproof.js
new file mode 100644
index 0000000000..62d57533fa
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_xpcwn_tamperproof.js
@@ -0,0 +1,180 @@
+// Test that it's not possible to create expando properties on XPCWNs.
+// See <https://bugzilla.mozilla.org/show_bug.cgi?id=1143810#c5>.
+
+function TestInterfaceAll() {}
+TestInterfaceAll.prototype = {
+ QueryInterface: ChromeUtils.generateQI(["nsIXPCTestInterfaceA",
+ "nsIXPCTestInterfaceB",
+ "nsIXPCTestInterfaceC"]),
+
+ /* nsIXPCTestInterfaceA / nsIXPCTestInterfaceB */
+ name: "TestInterfaceAllDefaultName",
+
+ /* nsIXPCTestInterfaceC */
+ someInteger: 42
+};
+
+function check_throws(f) {
+ try {
+ f();
+ } catch (exc) {
+ return;
+ }
+ throw new TypeError("Expected exception, no exception thrown");
+}
+
+/*
+ * Test that XPCWrappedNative objects do not permit expando properties to be created.
+ *
+ * This function is called twice. The first time, realObj is an nsITimer XPCWN
+ * and accessObj === realObj.
+ *
+ * The second time, accessObj is a scripted proxy with realObj as its target.
+ * So the second time we are testing that scripted proxies don't magically
+ * bypass whatever mechanism enforces the expando policy on XPCWNs.
+ */
+function test_tamperproof(realObj, accessObj, {method, constant, attribute}) {
+ // Assignment can't create an expando property.
+ check_throws(function () { accessObj.expando = 14; });
+ Assert.equal(false, "expando" in realObj);
+
+ // Strict assignment throws.
+ check_throws(function () { "use strict"; accessObj.expando = 14; });
+ Assert.equal(false, "expando" in realObj);
+
+ // Assignment to an inherited method name doesn't work either.
+ check_throws(function () { accessObj.hasOwnProperty = () => "lies"; });
+ check_throws(function () { "use strict"; accessObj.hasOwnProperty = () => "lies"; });
+ Assert.ok(!realObj.hasOwnProperty("hasOwnProperty"));
+
+ // Assignment to a method name doesn't work either.
+ let originalMethod;
+ if (method) {
+ originalMethod = accessObj[method];
+ accessObj[method] = "nope"; // non-writable data property, no exception in non-strict code
+ check_throws(function () { "use strict"; accessObj[method] = "nope"; });
+ Assert.ok(realObj[method] === originalMethod);
+ }
+
+ // A constant is the same thing.
+ let originalConstantValue;
+ if (constant) {
+ originalConstantValue = accessObj[constant];
+ accessObj[constant] = "nope";
+ Assert.equal(realObj[constant], originalConstantValue);
+ check_throws(function () { "use strict"; accessObj[constant] = "nope"; });
+ Assert.equal(realObj[constant], originalConstantValue);
+ }
+
+ // Assignment to a readonly accessor property with no setter doesn't work either.
+ let originalAttributeDesc;
+ if (attribute) {
+ originalAttributeDesc = Object.getOwnPropertyDescriptor(realObj, attribute);
+ Assert.ok("set" in originalAttributeDesc);
+ Assert.ok(originalAttributeDesc.set === undefined);
+
+ accessObj[attribute] = "nope"; // accessor property with no setter: no exception in non-strict code
+ check_throws(function () { "use strict"; accessObj[attribute] = "nope"; });
+
+ let desc = Object.getOwnPropertyDescriptor(realObj, attribute);
+ Assert.ok("set" in desc);
+ Assert.equal(originalAttributeDesc.get, desc.get);
+ Assert.equal(undefined, desc.set);
+ }
+
+ // Reflect.set doesn't work either.
+ if (method) {
+ Assert.ok(!Reflect.set({}, method, "bad", accessObj));
+ Assert.equal(realObj[method], originalMethod);
+ }
+ if (attribute) {
+ Assert.ok(!Reflect.set({}, attribute, "bad", accessObj));
+ Assert.equal(originalAttributeDesc.get, Object.getOwnPropertyDescriptor(realObj, attribute).get);
+ }
+
+ // Object.defineProperty can't do anything either.
+ let names = ["expando"];
+ if (method) names.push(method);
+ if (constant) names.push(constant);
+ if (attribute) names.push(attribute);
+ for (let name of names) {
+ let originalDesc = Object.getOwnPropertyDescriptor(realObj, name);
+ check_throws(function () {
+ Object.defineProperty(accessObj, name, {configurable: true});
+ });
+ check_throws(function () {
+ Object.defineProperty(accessObj, name, {writable: true});
+ });
+ check_throws(function () {
+ Object.defineProperty(accessObj, name, {get: function () { return "lies"; }});
+ });
+ check_throws(function () {
+ Object.defineProperty(accessObj, name, {value: "bad"});
+ });
+ let desc = Object.getOwnPropertyDescriptor(realObj, name);
+ if (originalDesc === undefined) {
+ Assert.equal(undefined, desc);
+ } else {
+ Assert.equal(originalDesc.configurable, desc.configurable);
+ Assert.equal(originalDesc.enumerable, desc.enumerable);
+ Assert.equal(originalDesc.writable, desc.writable);
+ Assert.equal(originalDesc.value, desc.value);
+ Assert.equal(originalDesc.get, desc.get);
+ Assert.equal(originalDesc.set, desc.set);
+ }
+ }
+
+ // Existing properties can't be deleted.
+ if (method) {
+ Assert.equal(false, delete accessObj[method]);
+ check_throws(function () { "use strict"; delete accessObj[method]; });
+ Assert.equal(realObj[method], originalMethod);
+ }
+ if (constant) {
+ Assert.equal(false, delete accessObj[constant]);
+ check_throws(function () { "use strict"; delete accessObj[constant]; });
+ Assert.equal(realObj[constant], originalConstantValue);
+ }
+ if (attribute) {
+ Assert.equal(false, delete accessObj[attribute]);
+ check_throws(function () { "use strict"; delete accessObj[attribute]; });
+ desc = Object.getOwnPropertyDescriptor(realObj, attribute);
+ Assert.equal(originalAttributeDesc.get, desc.get);
+ }
+}
+
+function test_twice(obj, options) {
+ test_tamperproof(obj, obj, options);
+
+ let handler = {
+ getPrototypeOf(t) {
+ return new Proxy(Object.getPrototypeOf(t), handler);
+ }
+ };
+ test_tamperproof(obj, new Proxy(obj, handler), options);
+}
+
+function run_test() {
+ let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
+ test_twice(timer, {
+ method: "init",
+ constant: "TYPE_ONE_SHOT",
+ attribute: "callback"
+ });
+
+ let cmdline = Cu.createCommandLine([], null, Ci.nsICommandLine.STATE_INITIAL_LAUNCH);
+ test_twice(cmdline, {});
+
+ test_twice(Object.getPrototypeOf(cmdline), {
+ method: "getArgument",
+ constant: "STATE_INITIAL_LAUNCH",
+ attribute: "length"
+ });
+
+ // Test a tearoff object.
+ let b = xpcWrap(new TestInterfaceAll(), Ci.nsIXPCTestInterfaceB);
+ let tearoff = b.nsIXPCTestInterfaceA;
+ test_twice(tearoff, {
+ method: "QueryInterface"
+ });
+}
diff --git a/js/xpconnect/tests/unit/test_xray_SavedFrame-02.js b/js/xpconnect/tests/unit/test_xray_SavedFrame-02.js
new file mode 100644
index 0000000000..e9b5752044
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_xray_SavedFrame-02.js
@@ -0,0 +1,71 @@
+// Test calling SavedFrame getters across wrappers from privileged and
+// un-privileged globals.
+
+const {addDebuggerToGlobal} = ChromeUtils.importESModule("resource://gre/modules/jsdebugger.sys.mjs");
+addDebuggerToGlobal(globalThis);
+
+const lowP = Services.scriptSecurityManager.createNullPrincipal({});
+const highP = Cc["@mozilla.org/systemprincipal;1"].createInstance(Ci.nsIPrincipal);
+
+const low = new Cu.Sandbox(lowP);
+const high = new Cu.Sandbox(highP);
+
+function run_test() {
+ // Privileged compartment accessing unprivileged stack.
+ high.stack = getSavedFrameInstanceFromSandbox(low);
+ Cu.evalInSandbox("this.parent = stack.parent", high);
+ Cu.evalInSandbox("this.asyncParent = stack.asyncParent", high);
+ Cu.evalInSandbox("this.source = stack.source", high);
+ Cu.evalInSandbox("this.functionDisplayName = stack.functionDisplayName", high);
+
+ // Un-privileged compartment accessing privileged stack.
+ low.stack = getSavedFrameInstanceFromSandbox(high);
+ try {
+ Cu.evalInSandbox("this.parent = stack.parent", low);
+ } catch (e) { }
+ try {
+ Cu.evalInSandbox("this.asyncParent = stack.asyncParent", low);
+ } catch (e) { }
+ try {
+ Cu.evalInSandbox("this.source = stack.source", low);
+ } catch (e) { }
+ try {
+ Cu.evalInSandbox("this.functionDisplayName = stack.functionDisplayName", low);
+ } catch (e) { }
+
+ // Privileged compartment accessing privileged stack.
+ let stack = getSavedFrameInstanceFromSandbox(high);
+ let parent = stack.parent;
+ let asyncParent = stack.asyncParent;
+ let source = stack.source;
+ let functionDisplayName = stack.functionDisplayName;
+
+ ok(true, "Didn't crash");
+}
+
+// Get a SavedFrame instance from inside the given sandbox.
+//
+// We can't use Cu.getJSTestingFunctions().saveStack() because Cu isn't
+// available to sandboxes that don't have the system principal. The easiest way
+// to get the SavedFrame is to use the Debugger API to track allocation sites
+// and then do an allocation.
+function getSavedFrameInstanceFromSandbox(sandbox) {
+ const dbg = new Debugger(sandbox);
+
+ dbg.memory.trackingAllocationSites = true;
+ Cu.evalInSandbox("(function iife() { return new RegExp }())", sandbox);
+ const allocs = dbg.memory.drainAllocationsLog().filter(e => e.class === "RegExp");
+ dbg.memory.trackingAllocationSites = false;
+
+ ok(allocs[0], "We should observe the allocation");
+ const { frame } = allocs[0];
+
+ if (sandbox !== high) {
+ ok(Cu.isXrayWrapper(frame), "`frame` should be an xray...");
+ equal(Object.prototype.toString.call(Cu.waiveXrays(frame)),
+ "[object SavedFrame]",
+ "...and that xray should wrap a SavedFrame");
+ }
+
+ return frame;
+}
diff --git a/js/xpconnect/tests/unit/test_xray_SavedFrame.js b/js/xpconnect/tests/unit/test_xray_SavedFrame.js
new file mode 100644
index 0000000000..85c91a2aa1
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_xray_SavedFrame.js
@@ -0,0 +1,104 @@
+// Bug 1117242: Test calling SavedFrame getters from globals that don't subsume
+// that frame's principals.
+
+const {addDebuggerToGlobal} = ChromeUtils.importESModule("resource://gre/modules/jsdebugger.sys.mjs");
+addDebuggerToGlobal(globalThis);
+
+const lowP = Services.scriptSecurityManager.createNullPrincipal({});
+const midP = [lowP, "http://other.com"];
+const highP = Cc["@mozilla.org/systemprincipal;1"].createInstance(Ci.nsIPrincipal);
+
+const low = new Cu.Sandbox(lowP);
+const mid = new Cu.Sandbox(midP);
+const high = new Cu.Sandbox(highP);
+
+function run_test() {
+ // Test that the priveleged view of a SavedFrame from a subsumed compartment
+ // is the same view that the subsumed compartment gets. Create the following
+ // chain of function calls (with some intermediate system-principaled frames
+ // due to implementation):
+ //
+ // low.lowF -> mid.midF -> high.highF -> high.saveStack
+ //
+ // Where high.saveStack gets monkey patched to create stacks in each of our
+ // sandboxes.
+
+ Cu.evalInSandbox("function highF() { return saveStack(); }", high);
+
+ mid.highF = () => high.highF();
+ Cu.evalInSandbox("function midF() { return highF(); }", mid);
+
+ low.midF = () => mid.midF();
+ Cu.evalInSandbox("function lowF() { return midF(); }", low);
+
+ const expected = [
+ {
+ sandbox: low,
+ frames: ["lowF"],
+ },
+ {
+ sandbox: mid,
+ frames: ["midF", "lowF"],
+ },
+ {
+ sandbox: high,
+ frames: ["getSavedFrameInstanceFromSandbox",
+ "saveStack",
+ "highF",
+ "run_test/mid.highF",
+ "midF",
+ "run_test/low.midF",
+ "lowF",
+ "run_test",
+ "_execute_test",
+ null],
+ }
+ ];
+
+ for (let { sandbox, frames } of expected) {
+ high.saveStack = function saveStack() {
+ return getSavedFrameInstanceFromSandbox(sandbox);
+ };
+
+ const xrayStack = low.lowF();
+ equal(xrayStack.functionDisplayName, "getSavedFrameInstanceFromSandbox",
+ "Xrays should always be able to see everything.");
+
+ let waived = Cu.waiveXrays(xrayStack);
+ do {
+ ok(frames.length,
+ "There should still be more expected frames while we have actual frames.");
+ equal(waived.functionDisplayName, frames.shift(),
+ "The waived wrapper should give us the stack's compartment's view.");
+ waived = waived.parent;
+ } while (waived);
+ }
+}
+
+// Get a SavedFrame instance from inside the given sandbox.
+//
+// We can't use Cu.getJSTestingFunctions().saveStack() because Cu isn't
+// available to sandboxes that don't have the system principal. The easiest way
+// to get the SavedFrame is to use the Debugger API to track allocation sites
+// and then do an allocation.
+function getSavedFrameInstanceFromSandbox(sandbox) {
+ const dbg = new Debugger(sandbox);
+
+ dbg.memory.trackingAllocationSites = true;
+ Cu.evalInSandbox("new Object", sandbox);
+ const allocs = dbg.memory.drainAllocationsLog();
+ dbg.memory.trackingAllocationSites = false;
+
+ ok(allocs[0], "We should observe the allocation");
+ const { frame } = allocs[0];
+
+ if (sandbox !== high) {
+ ok(Cu.isXrayWrapper(frame), "`frame` should be an xray...");
+ equal(Object.prototype.toString.call(Cu.waiveXrays(frame)),
+ "[object SavedFrame]",
+ "...and that xray should wrap a SavedFrame");
+ }
+
+ return frame;
+}
+
diff --git a/js/xpconnect/tests/unit/test_xray_instanceof.js b/js/xpconnect/tests/unit/test_xray_instanceof.js
new file mode 100644
index 0000000000..d52d062984
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_xray_instanceof.js
@@ -0,0 +1,206 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+add_task(function instanceof_xrays() {
+ let sandbox = Cu.Sandbox(null);
+ Cu.evalInSandbox(`
+ this.proxy = new Proxy([], {
+ getPrototypeOf() {
+ return Date.prototype;
+ },
+ });
+
+ this.inheritedProxy = Object.create(this.proxy);
+
+ this.FunctionProxy = new Proxy(function() {}, {});
+ this.functionProxyInstance = new this.FunctionProxy();
+
+ this.CustomClass = class {};
+ this.customClassInstance = new this.CustomClass();
+ `, sandbox);
+
+ {
+ // Sanity check that instanceof still works with standard constructors when xrays are present.
+ Assert.ok(Cu.evalInSandbox(`new Date()`, sandbox) instanceof sandbox.Date,
+ "async function result in sandbox instanceof sandbox.Date");
+ Assert.ok(new sandbox.Date() instanceof sandbox.Date,
+ "sandbox.Date() instanceof sandbox.Date");
+
+ Assert.ok(sandbox.CustomClass instanceof sandbox.Function,
+ "Class constructor instanceof sandbox.Function");
+ Assert.ok(sandbox.CustomClass instanceof sandbox.Object,
+ "Class constructor instanceof sandbox.Object");
+
+ // Both operands must have the same kind of Xray vision.
+ Assert.equal(Cu.waiveXrays(sandbox.CustomClass) instanceof sandbox.Function, false,
+ "Class constructor with waived xrays instanceof sandbox.Function");
+ Assert.equal(Cu.waiveXrays(sandbox.CustomClass) instanceof sandbox.Object, false,
+ "Class constructor with waived xrays instanceof sandbox.Object");
+ }
+
+ {
+ let {proxy} = sandbox;
+ Assert.equal(proxy instanceof sandbox.Date, false,
+ "instanceof should ignore the proxy trap");
+ Assert.equal(proxy instanceof sandbox.Array, false,
+ "instanceof should ignore the proxy target");
+ Assert.equal(Cu.waiveXrays(proxy) instanceof sandbox.Date, false,
+ "instanceof should ignore the proxy trap despite the waived xrays on the proxy");
+ Assert.equal(Cu.waiveXrays(proxy) instanceof sandbox.Array, false,
+ "instanceof should ignore the proxy target despite the waived xrays on the proxy");
+
+ Assert.ok(proxy instanceof Cu.waiveXrays(sandbox.Date),
+ "instanceof should trigger the proxy trap after waiving Xrays on the constructor");
+ Assert.equal(proxy instanceof Cu.waiveXrays(sandbox.Array), false,
+ "instanceof should trigger the proxy trap after waiving Xrays on the constructor");
+
+ Assert.ok(Cu.waiveXrays(proxy) instanceof Cu.waiveXrays(sandbox.Date),
+ "instanceof should trigger the proxy trap after waiving both Xrays");
+ }
+
+
+ {
+ let {inheritedProxy} = sandbox;
+ Assert.equal(inheritedProxy instanceof sandbox.Date, false,
+ "instanceof should ignore the inherited proxy trap");
+ Assert.equal(Cu.waiveXrays(inheritedProxy) instanceof sandbox.Date, false,
+ "instanceof should ignore the inherited proxy trap despite the waived xrays on the proxy");
+
+ Assert.ok(inheritedProxy instanceof Cu.waiveXrays(sandbox.Date),
+ "instanceof should trigger the inherited proxy trap after waiving Xrays on the constructor");
+
+ Assert.ok(Cu.waiveXrays(inheritedProxy) instanceof Cu.waiveXrays(sandbox.Date),
+ "instanceof should trigger the inherited proxy trap after waiving both Xrays");
+ }
+
+ {
+ let {FunctionProxy, functionProxyInstance} = sandbox;
+
+ // Ideally, the next two test cases should both throw "... not a function".
+ // However, because the opaque XrayWrapper does not override isCallable, an
+ // opaque XrayWrapper is still considered callable if the proxy target is,
+ // and "instanceof" will try to look up the prototype of the wrapper (and
+ // fail because opaque XrayWrappers hide the properties).
+ Assert.throws(
+ () => functionProxyInstance instanceof FunctionProxy,
+ /'prototype' property of FunctionProxy is not an object/,
+ "Opaque constructor proxy should be hidden by Xrays");
+ Assert.throws(
+ () => functionProxyInstance instanceof sandbox.proxy,
+ /sandbox.proxy is not a function/,
+ "Opaque non-constructor proxy should be hidden by Xrays");
+
+ Assert.ok(functionProxyInstance instanceof Cu.waiveXrays(FunctionProxy),
+ "instanceof should get through the proxy after waiving Xrays on the constructor proxy");
+ Assert.ok(Cu.waiveXrays(functionProxyInstance) instanceof Cu.waiveXrays(FunctionProxy),
+ "instanceof should get through the proxy after waiving both Xrays");
+ }
+
+ {
+ let {CustomClass, customClassInstance} = sandbox;
+ // Under Xray vision, every JS object is either a plain object or array.
+ // Prototypical inheritance is invisible when the constructor is wrapped.
+ Assert.throws(
+ () => customClassInstance instanceof CustomClass,
+ /TypeError: 'prototype' property of CustomClass is not an object/,
+ "instanceof on a custom JS class with xrays should fail");
+ Assert.ok(customClassInstance instanceof Cu.waiveXrays(CustomClass),
+ "instanceof should see the true prototype of CustomClass after waiving Xrays on the class");
+ Assert.ok(Cu.waiveXrays(customClassInstance) instanceof Cu.waiveXrays(CustomClass),
+ "instanceof should see the true prototype of CustomClass after waiving Xrays");
+ }
+});
+
+add_task(function instanceof_dom_xrays_hasInstance() {
+ const principal = Services.scriptSecurityManager.createNullPrincipal({});
+ const webnav = Services.appShell.createWindowlessBrowser(false);
+ webnav.docShell.createAboutBlankDocumentViewer(principal, principal);
+ let window = webnav.document.defaultView;
+
+ let sandbox = Cu.Sandbox(principal);
+ sandbox.DOMObjectWithHasInstance = window.document;
+ Cu.evalInSandbox(`
+ this.DOMObjectWithHasInstance[Symbol.hasInstance] = function() {
+ return true;
+ };
+ this.ObjectWithHasInstance = {
+ [Symbol.hasInstance](v) {
+ v.throwsIfVCannotBeAccessed;
+ return true;
+ },
+ };
+ `, sandbox);
+
+ // Override the hasInstance handler in the window, so that we can detect when
+ // we end up triggering hasInstance in the window's compartment.
+ window.eval(`
+ document[Symbol.hasInstance] = function() {
+ throw "hasInstance_in_window";
+ };
+ `);
+
+ sandbox.domobj = window.document.body;
+ Assert.ok(sandbox.eval(`domobj.wrappedJSObject`),
+ "DOM object is a XrayWrapper");
+ Assert.ok(sandbox.eval(`DOMObjectWithHasInstance.wrappedJSObject`),
+ "DOM object with Symbol.hasInstance is a XrayWrapper");
+
+ for (let Obj of ["ObjectWithHasInstance", "DOMObjectWithHasInstance"]) {
+ // Tests Xray vision *inside* the sandbox. The Symbol.hasInstance member
+ // is a property / expando object in the sandbox's compartment, so the
+ // "instanceof" operator should always trigger the hasInstance function.
+ Assert.ok(sandbox.eval(`[] instanceof ${Obj}`),
+ `Should call ${Obj}[Symbol.hasInstance] when left operand has no Xrays`);
+ Assert.ok(sandbox.eval(`domobj instanceof ${Obj}`),
+ `Should call ${Obj}[Symbol.hasInstance] when left operand has Xrays`);
+ Assert.ok(sandbox.eval(`domobj.wrappedJSObject instanceof ${Obj}`),
+ `Should call ${Obj}[Symbol.hasInstance] when left operand has waived Xrays`);
+
+ // Tests Xray vision *outside* the sandbox. The Symbol.hasInstance member
+ // should be hidden by Xrays.
+ let sandboxObjWithHasInstance = sandbox[Obj];
+ Assert.ok(Cu.isXrayWrapper(sandboxObjWithHasInstance),
+ `sandbox.${Obj} is a XrayWrapper`);
+ Assert.throws(
+ () => sandbox.Object() instanceof sandboxObjWithHasInstance,
+ /sandboxObjWithHasInstance is not a function/,
+ `sandbox.${Obj}[Symbol.hasInstance] should be hidden by Xrays`);
+
+ Assert.throws(
+ () => Cu.waiveXrays(sandbox.Object()) instanceof sandboxObjWithHasInstance,
+ /sandboxObjWithHasInstance is not a function/,
+ `sandbox.${Obj}[Symbol.hasInstance] should be hidden by Xrays, despite the waived Xrays at the left`);
+
+ // (Cases where the left operand has no Xrays are checked below.)
+ }
+
+ // hasInstance is expected to be called, but still trigger an error because
+ // properties of the object from the current context should not be readable
+ // by the hasInstance function in the sandbox with a different principal.
+ Assert.throws(
+ () => [] instanceof Cu.waiveXrays(sandbox.ObjectWithHasInstance),
+ /Permission denied to access property "throwsIfVCannotBeAccessed"/,
+ `Should call (waived) sandbox.ObjectWithHasInstance[Symbol.hasInstance] when the right operand has waived Xrays`);
+
+ // The Xray waiver on the right operand should be sufficient to call
+ // hasInstance even if the left operand still has Xrays.
+ Assert.ok(sandbox.Object() instanceof Cu.waiveXrays(sandbox.ObjectWithHasInstance),
+ `Should call (waived) sandbox.ObjectWithHasInstance[Symbol.hasInstance] when the right operand has waived Xrays`);
+ Assert.ok(Cu.waiveXrays(sandbox.Object()) instanceof Cu.waiveXrays(sandbox.ObjectWithHasInstance),
+ `Should call (waived) sandbox.ObjectWithHasInstance[Symbol.hasInstance] when both operands have waived Xrays`);
+
+ // When Xrays of the DOM object are waived, we end up in the owner document's
+ // compartment (instead of the sandbox).
+ Assert.throws(
+ () => [] instanceof Cu.waiveXrays(sandbox.DOMObjectWithHasInstance),
+ /hasInstance_in_window/,
+ "Should call (waived) sandbox.DOMObjectWithHasInstance[Symbol.hasInstance] when the right operand has waived Xrays");
+
+ Assert.throws(
+ () => Cu.waiveXrays(sandbox.Object()) instanceof Cu.waiveXrays(sandbox.DOMObjectWithHasInstance),
+ /hasInstance_in_window/,
+ "Should call (waived) sandbox.DOMObjectWithHasInstance[Symbol.hasInstance] when both operands have waived Xrays");
+
+ webnav.close();
+});
diff --git a/js/xpconnect/tests/unit/test_xray_named_element_access.js b/js/xpconnect/tests/unit/test_xray_named_element_access.js
new file mode 100644
index 0000000000..ca9394104e
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_xray_named_element_access.js
@@ -0,0 +1,21 @@
+// See https://bugzilla.mozilla.org/show_bug.cgi?id=1273251
+"use strict";
+
+add_task(async function() {
+ let webnav = Services.appShell.createWindowlessBrowser(false);
+
+ let docShell = webnav.docShell;
+
+ docShell.createAboutBlankDocumentViewer(null, null);
+
+ let window = webnav.document.defaultView;
+ let unwrapped = Cu.waiveXrays(window);
+
+ window.document.body.innerHTML = '<div id="foo"></div>';
+
+ equal(window.foo, undefined, "Should not have named X-ray property access");
+ equal(typeof unwrapped.foo, "object", "Should always have non-X-ray named property access");
+
+ webnav.close();
+});
+
diff --git a/js/xpconnect/tests/unit/test_xray_regexp.js b/js/xpconnect/tests/unit/test_xray_regexp.js
new file mode 100644
index 0000000000..72eb9563d0
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_xray_regexp.js
@@ -0,0 +1,7 @@
+function run_test() {
+ var sandbox = Cu.Sandbox('http://www.example.com');
+ var regexp = Cu.evalInSandbox("/test/i", sandbox);
+ equal(RegExp.prototype.toString.call(regexp), "/test/i");
+ var prototype = Cu.evalInSandbox("RegExp.prototype", sandbox);
+ equal(typeof prototype.lastIndex, "undefined");
+}
diff --git a/js/xpconnect/tests/unit/test_xrayed_arguments.js b/js/xpconnect/tests/unit/test_xrayed_arguments.js
new file mode 100644
index 0000000000..fae0a0c865
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_xrayed_arguments.js
@@ -0,0 +1,16 @@
+function run_test() {
+ var sbContent = Cu.Sandbox(null);
+ let xrayedArgs = sbContent.eval("(function(a, b) { return arguments; })('hi', 42)");
+
+ function checkArgs(a) {
+ Assert.equal(a.length, 2);
+ Assert.equal(a[0], 'hi');
+ Assert.equal(a[1], 42);
+ }
+
+ // Check Xrays to the args.
+ checkArgs(xrayedArgs);
+
+ // Make sure the spread operator works.
+ checkArgs([...xrayedArgs]);
+}
diff --git a/js/xpconnect/tests/unit/test_xrayed_iterator.js b/js/xpconnect/tests/unit/test_xrayed_iterator.js
new file mode 100644
index 0000000000..26d40420a3
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_xrayed_iterator.js
@@ -0,0 +1,40 @@
+Services.prefs.setBoolPref("security.allow_eval_with_system_principal", true);
+registerCleanupFunction(() => {
+ Services.prefs.clearUserPref("security.allow_eval_with_system_principal");
+});
+
+function run_test() {
+
+ var toEval = [
+ "var customIterator = {",
+ " _array: [6, 7, 8, 9]",
+ "};",
+ "customIterator[Symbol.iterator] = function* () {",
+ " for (var i = 0; i < this._array.length; ++i)",
+ " yield this._array[i];",
+ "};"
+ ].join('\n');
+
+ function checkIterator(iterator) {
+ var control = [6, 7, 8, 9];
+ var i = 0;
+ for (var item of iterator) {
+ Assert.equal(item, control[i]);
+ ++i;
+ }
+ }
+
+ // First, try in our own scope.
+ eval(toEval);
+ checkIterator(customIterator);
+
+ // Next, try a vanilla CCW.
+ var sbChrome = Cu.Sandbox(this);
+ Cu.evalInSandbox(toEval, sbChrome, '1.7');
+ checkIterator(sbChrome.customIterator);
+
+ // Finally, try an Xray waiver.
+ var sbContent = Cu.Sandbox('http://www.example.com');
+ Cu.evalInSandbox(toEval, sbContent, '1.7');
+ checkIterator(Cu.waiveXrays(sbContent.customIterator));
+}
diff --git a/js/xpconnect/tests/unit/uninitialized_lexical.jsm b/js/xpconnect/tests/unit/uninitialized_lexical.jsm
new file mode 100644
index 0000000000..e0d661bcd4
--- /dev/null
+++ b/js/xpconnect/tests/unit/uninitialized_lexical.jsm
@@ -0,0 +1,2 @@
+var EXPORTED_SYMBOLS = ["foo"];
+const foo = ChromeUtils.import("resource://test/uninitialized_lexical.jsm");
diff --git a/js/xpconnect/tests/unit/xpcshell.toml b/js/xpconnect/tests/unit/xpcshell.toml
new file mode 100644
index 0000000000..97b2dbe559
--- /dev/null
+++ b/js/xpconnect/tests/unit/xpcshell.toml
@@ -0,0 +1,430 @@
+[DEFAULT]
+head = "head.js"
+support-files = [
+ "CatRegistrationComponents.manifest",
+ "CatBackgroundTaskRegistrationComponents.manifest",
+ "bogus_element_type.jsm",
+ "bogus_exports_type.jsm",
+ "bug451678_subscript.js",
+ "TestBlob.jsm",
+ "TestFile.jsm",
+ "environment_script.js",
+ "environment_loadscript.jsm",
+ "environment_checkscript.jsm",
+ "file_simple_script.js",
+ "importer.jsm",
+ "recursive_importA.jsm",
+ "recursive_importB.jsm",
+ "ReturnCodeChild.jsm",
+ "ReturnCodeChild.sys.mjs",
+ "syntax_error.jsm",
+ "uninitialized_lexical.jsm",
+ "es6module.js",
+ "es6import.js",
+ "es6module_throws.js",
+ "es6module_missing_import.js",
+ "es6module_parse_error.js",
+ "es6module_parse_error_in_import.js",
+ "es6module_cycle_a.js",
+ "es6module_cycle_b.js",
+ "es6module_cycle_c.js",
+ "es6module_top_level_await.js",
+ "es6module_devtoolsLoader.js",
+ "es6module_devtoolsLoader.sys.mjs",
+ "es6module_devtoolsLoader_only.js",
+ "esmified-1.sys.mjs",
+ "esmified-2.sys.mjs",
+ "esmified-3.sys.mjs",
+ "esmified-4.sys.mjs",
+ "esmified-5.sys.mjs",
+ "esmified-6.sys.mjs",
+ "esmified-not-exported.sys.mjs",
+ "not-esmified-not-exported.jsm",
+ "esm_lazy-1.sys.mjs",
+ "esm_lazy-2.sys.mjs",
+ "jsm_loaded-1.jsm",
+ "jsm_loaded-2.jsm",
+ "jsm_loaded-3.jsm",
+ "es6module_loaded-1.sys.mjs",
+ "es6module_loaded-2.sys.mjs",
+ "es6module_loaded-3.sys.mjs",
+ "api_script.js",
+ "import_stack.jsm",
+ "import_stack.sys.mjs",
+ "import_stack_static_1.sys.mjs",
+ "import_stack_static_2.sys.mjs",
+ "import_stack_static_3.sys.mjs",
+ "import_stack_static_4.sys.mjs",
+ "es6module_import_error.js",
+ "es6module_import_error2.js",
+ "es6module_dynamic_import.js",
+ "es6module_dynamic_import2.js",
+ "es6module_dynamic_import3.js",
+ "es6module_dynamic_import_static.js",
+ "es6module_dynamic_import_missing.js",
+ "es6module_dynamic_import_syntax_error.js",
+ "es6module_dynamic_import_syntax_error2.js",
+ "es6module_dynamic_import_syntax_error3.js",
+ "es6module_dynamic_import_runtime_error.js",
+ "es6module_dynamic_import_runtime_error2.js",
+ "es6module_dynamic_import_runtime_error3.js",
+ "es6module_absolute.js",
+ "es6module_absolute2.js",
+ "envChain.jsm",
+ "envChain_subscript.jsm",
+ "error_export.sys.mjs",
+ "error_import.sys.mjs",
+ "error_other.sys.mjs",
+ "non_shared_1.mjs",
+ "non_shared_2.mjs",
+ "import_non_shared_1.mjs",
+ "non_shared_nest_import_shared_1.mjs",
+ "non_shared_nest_import_shared_target_1.sys.mjs",
+ "non_shared_nest_import_shared_target_2.sys.mjs",
+ "non_shared_nest_import_non_shared_1.mjs",
+ "non_shared_nest_import_non_shared_target_1.mjs",
+ "non_shared_nest_import_non_shared_2.mjs",
+ "non_shared_nest_import_non_shared_target_2.mjs",
+ "non_shared_nest_import_non_shared_3.mjs",
+ "non_shared_nest_import_non_shared_target_3.mjs",
+ "contextual.sys.mjs",
+ "non_shared_worker_1.js",
+ "import_shared_in_worker.js",
+ "contextual_worker.js",
+ "sync_and_async_in_worker.js",
+ "lazy_non_shared_in_worker.js",
+ "lazy_shared_in_worker.js",
+]
+
+["test_ComponentEnvironment.js"]
+
+["test_Cu_reportError_column.js"]
+
+["test_FrameScriptEnvironment.js"]
+
+["test_ReadableStream_from.js"]
+
+["test_SubscriptLoaderEnvironment.js"]
+
+["test_SubscriptLoaderJSMEnvironment.js"]
+
+["test_SubscriptLoaderSandboxEnvironment.js"]
+
+["test_URLSearchParams.js"]
+
+["test_allowWaivers.js"]
+
+["test_allowedDomains.js"]
+
+["test_allowedDomainsXHR.js"]
+
+["test_attributes.js"]
+
+["test_blob.js"]
+
+["test_blob2.js"]
+
+["test_bogus_files.js"]
+
+["test_bug267645.js"]
+
+["test_bug408412.js"]
+
+["test_bug451678.js"]
+
+["test_bug604362.js"]
+
+["test_bug677864.js"]
+
+["test_bug711404.js"]
+
+["test_bug742444.js"]
+
+["test_bug778409.js"]
+
+["test_bug780370.js"]
+
+["test_bug809652.js"]
+
+["test_bug809674.js"]
+
+["test_bug813901.js"]
+
+["test_bug845201.js"]
+
+["test_bug845862.js"]
+
+["test_bug849730.js"]
+
+["test_bug851895.js"]
+
+["test_bug853709.js"]
+
+["test_bug856067.js"]
+
+["test_bug867486.js"]
+
+["test_bug868675.js"]
+
+["test_bug872772.js"]
+
+["test_bug885800.js"]
+
+["test_bug930091.js"]
+
+["test_bug976151.js"]
+
+["test_bug1001094.js"]
+
+["test_bug1021312.js"]
+
+["test_bug1033253.js"]
+
+["test_bug1033920.js"]
+
+["test_bug1033927.js"]
+
+["test_bug1034262.js"]
+
+["test_bug1081990.js"]
+
+["test_bug1110546.js"]
+
+["test_bug1131707.js"]
+
+["test_bug1150771.js"]
+
+["test_bug1151385.js"]
+
+["test_bug1170311.js"]
+
+["test_bug1244222.js"]
+
+["test_bug1617527.js"]
+
+["test_bug_442086.js"]
+
+["test_callFunctionWithAsyncStack.js"]
+
+["test_cenums.js"]
+
+["test_compileScript.js"]
+
+["test_components.js"]
+
+["test_crypto.js"]
+
+["test_css.js"]
+
+["test_deepFreezeClone.js"]
+
+["test_defineESModuleGetters.js"]
+
+["test_defineESModuleGetters_options.js"]
+
+["test_defineESModuleGetters_options_worker.js"]
+skip-if = ["os == 'android'"]
+
+["test_defineModuleGetter.js"]
+
+["test_envChain_JSM.js"]
+
+["test_envChain_frameScript.js"]
+
+["test_envChain_subscript.js"]
+
+["test_envChain_subscript_in_JSM.js"]
+
+["test_error_to_exception.js"]
+
+["test_eventSource.js"]
+
+["test_exportFunction.js"]
+
+["test_file.js"]
+skip-if = ["os == 'android' && processor == 'x86_64'"]
+
+["test_file2.js"]
+skip-if = ["os == 'android' && processor == 'x86_64'"]
+
+["test_fileReader.js"]
+
+["test_function_names.js"]
+
+["test_generateQI.js"]
+
+["test_getCallerLocation.js"]
+
+["test_getObjectPrincipal.js"]
+
+["test_import.js"]
+
+["test_import_devtools_loader.js"]
+
+["test_import_es6_modules.js"]
+
+["test_import_fail.js"]
+
+["test_import_from_sandbox.js"]
+
+["test_import_global.js"]
+
+["test_import_global_worker.js"]
+skip-if = ["os == 'android'"]
+
+["test_import_global_contextual.js"]
+
+["test_import_global_contextual_worker.js"]
+skip-if = ["os == 'android'"]
+
+["test_import_global_current.js"]
+
+["test_import_global_current_worker.js"]
+skip-if = ["os == 'android'"]
+
+["test_import_shim.js"]
+
+["test_import_stack.js"]
+skip-if = [
+ "!nightly_build",
+ "!debug",
+]
+
+["test_import_syntax_error.js"]
+
+["test_isModuleLoaded.js"]
+
+["test_isProxy.js"]
+
+["test_js_memory_telemetry.js"]
+
+["test_js_weak_references.js"]
+
+["test_loadedESModules.js"]
+
+["test_localeCompare.js"]
+
+["test_malformed_utf8.js"]
+
+["test_messageChannel.js"]
+
+["test_nuke_sandbox.js"]
+
+["test_nuke_sandbox_event_listeners.js"]
+
+["test_nuke_webextension_wrappers.js"]
+
+["test_onGarbageCollection-01.js"]
+head = "head_ongc.js"
+
+["test_onGarbageCollection-02.js"]
+head = "head_ongc.js"
+
+["test_onGarbageCollection-03.js"]
+head = "head_ongc.js"
+
+["test_onGarbageCollection-04.js"]
+head = "head_ongc.js"
+
+["test_onGarbageCollection-05.js"]
+head = "head_ongc.js"
+
+["test_params.js"]
+
+["test_print_stderr.js"]
+
+["test_private_field_xrays.js"]
+
+["test_promise.js"]
+
+["test_recursive_import.js"]
+
+["test_reflect_parse.js"]
+
+["test_resistFingerprinting_date_now.js"]
+
+["test_resolve_dead_promise.js"]
+
+["test_returncode.js"]
+
+["test_rewrap_dead_wrapper.js"]
+
+["test_rtcIdentityProvider.js"]
+
+["test_sandbox_DOMException.js"]
+
+["test_sandbox_atob.js"]
+
+["test_sandbox_metadata.js"]
+
+["test_sandbox_name.js"]
+
+["test_storage.js"]
+
+["test_structuredClone.js"]
+
+["test_subScriptLoader.js"]
+
+["test_symbols_as_weak_keys.js"]
+skip-if = [
+ "!nightly_build",
+]
+
+["test_tearoffs.js"]
+
+["test_textDecoder.js"]
+
+["test_uawidget_scope.js"]
+
+["test_uninitialized_lexical.js"]
+
+["test_unload.js"]
+
+["test_url.js"]
+
+["test_want_components.js"]
+
+["test_wasm_tailcalls_profiler.js"]
+skip-if = [
+ "tsan",
+ "!nightly_build",
+]
+
+["test_watchdog_default.js"]
+head = "head_watchdog.js"
+
+["test_watchdog_disable.js"]
+head = "head_watchdog.js"
+
+["test_watchdog_enable.js"]
+head = "head_watchdog.js"
+
+["test_watchdog_hibernate.js"]
+head = "head_watchdog.js"
+
+["test_watchdog_toggle.js"]
+head = "head_watchdog.js"
+
+["test_weak_keys.js"]
+
+["test_wrapped_js_enumerator.js"]
+
+["test_xpcomutils.js"]
+
+["test_xpcwn_instanceof.js"]
+
+["test_xpcwn_tamperproof.js"]
+
+["test_xray_SavedFrame-02.js"]
+
+["test_xray_SavedFrame.js"]
+
+["test_xray_instanceof.js"]
+
+["test_xray_named_element_access.js"]
+
+["test_xray_regexp.js"]
+
+["test_xrayed_arguments.js"]
+
+["test_xrayed_iterator.js"]