From 6bf0a5cb5034a7e684dcc3500e841785237ce2dd Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 19:32:43 +0200 Subject: Adding upstream version 1:115.7.0. Signed-off-by: Daniel Baumann --- dom/workers/test/404_server.sjs | 9 + .../test/WorkerDebugger.console_childWorker.js | 3 + .../test/WorkerDebugger.console_debugger.js | 49 + dom/workers/test/WorkerDebugger.console_worker.js | 10 + .../test/WorkerDebugger.initialize_childWorker.js | 6 + .../test/WorkerDebugger.initialize_debugger.js | 6 + ...WorkerDebugger.initialize_debugger_es_worker.js | 10 + .../test/WorkerDebugger.initialize_es_worker.js | 10 + .../test/WorkerDebugger.initialize_worker.js | 9 + .../test/WorkerDebugger.postMessage_childWorker.js | 3 + .../test/WorkerDebugger.postMessage_debugger.js | 9 + .../test/WorkerDebugger.postMessage_worker.js | 3 + ...erDebuggerGlobalScope.createSandbox_debugger.js | 9 + ...kerDebuggerGlobalScope.createSandbox_sandbox.js | 9 + ...rkerDebuggerGlobalScope.createSandbox_worker.js | 3 + ...buggerGlobalScope.enterEventLoop_childWorker.js | 14 + ...rDebuggerGlobalScope.enterEventLoop_debugger.js | 29 + ...kerDebuggerGlobalScope.enterEventLoop_worker.js | 27 + ...rDebuggerGlobalScope.reportError_childWorker.js | 5 + ...rkerDebuggerGlobalScope.reportError_debugger.js | 12 + ...WorkerDebuggerGlobalScope.reportError_worker.js | 11 + ...kerDebuggerGlobalScope.setImmediate_debugger.js | 12 + ...orkerDebuggerGlobalScope.setImmediate_worker.js | 3 + .../test/WorkerDebuggerManager_childWorker.js | 3 + dom/workers/test/WorkerDebuggerManager_worker.js | 3 + dom/workers/test/WorkerDebugger_childWorker.js | 3 + .../test/WorkerDebugger_frozen_window1.html | 15 + .../test/WorkerDebugger_frozen_window2.html | 15 + dom/workers/test/WorkerDebugger_frozen_worker1.js | 5 + dom/workers/test/WorkerDebugger_frozen_worker2.js | 5 + .../test/WorkerDebugger_promise_debugger.js | 34 + dom/workers/test/WorkerDebugger_promise_worker.js | 25 + dom/workers/test/WorkerDebugger_sharedWorker.js | 17 + .../test/WorkerDebugger_suspended_debugger.js | 6 + .../test/WorkerDebugger_suspended_worker.js | 6 + dom/workers/test/WorkerDebugger_worker.js | 8 + dom/workers/test/WorkerTest.jsm | 15 + dom/workers/test/WorkerTest_badworker.js | 1 + dom/workers/test/WorkerTest_subworker.js | 37 + dom/workers/test/WorkerTest_worker.js | 11 + dom/workers/test/atob_worker.js | 55 ++ dom/workers/test/blank.html | 14 + dom/workers/test/browser.ini | 36 + .../test/browser_WorkerDebugger.initialize.js | 54 ++ dom/workers/test/browser_bug1047663.js | 56 ++ dom/workers/test/browser_bug1104623.js | 60 ++ dom/workers/test/browser_consoleSharedWorkers.js | 70 ++ dom/workers/test/browser_fileURL.js | 73 ++ .../test/browser_privilegedmozilla_remoteworker.js | 116 +++ .../browser_serviceworker_fetch_new_process.js | 405 ++++++++ dom/workers/test/browser_worker_use_counters.js | 176 ++++ dom/workers/test/bug1014466_data1.txt | 1 + dom/workers/test/bug1014466_data2.txt | 1 + dom/workers/test/bug1014466_worker.js | 63 ++ dom/workers/test/bug1020226_frame.html | 19 + dom/workers/test/bug1020226_worker.js | 12 + dom/workers/test/bug1047663_tab.html | 8 + dom/workers/test/bug1047663_worker.sjs | 41 + dom/workers/test/bug1060621_worker.js | 2 + dom/workers/test/bug1062920_worker.js | 8 + dom/workers/test/bug1063538.sjs | 11 + dom/workers/test/bug1063538_worker.js | 25 + dom/workers/test/bug1104064_worker.js | 10 + dom/workers/test/bug1132395_sharedWorker.js | 12 + dom/workers/test/bug1132924_worker.js | 10 + dom/workers/test/bug978260_worker.js | 7 + dom/workers/test/bug998474_worker.js | 6 + dom/workers/test/chrome.ini | 96 ++ dom/workers/test/chromeWorker_subworker.js | 7 + dom/workers/test/chromeWorker_worker.js | 20 + dom/workers/test/chromeWorker_worker.sys.mjs | 16 + .../test/chromeWorker_worker_submod.sys.mjs | 9 + dom/workers/test/clearTimeoutsImplicit_worker.js | 11 + dom/workers/test/clearTimeouts_worker.js | 12 + dom/workers/test/consoleReplaceable_worker.js | 24 + dom/workers/test/console_worker.js | 112 +++ dom/workers/test/content_worker.js | 12 + dom/workers/test/crashtests/1153636.html | 5 + dom/workers/test/crashtests/1158031.html | 11 + dom/workers/test/crashtests/1228456.html | 14 + dom/workers/test/crashtests/1348882.html | 18 + dom/workers/test/crashtests/1819146.html | 23 + dom/workers/test/crashtests/1821066.html | 9 + dom/workers/test/crashtests/779707.html | 19 + dom/workers/test/crashtests/943516.html | 10 + dom/workers/test/crashtests/crashtests.list | 8 + dom/workers/test/csp_worker.js | 25 + dom/workers/test/csp_worker.js^headers^ | 1 + dom/workers/test/dom_worker_helper.js | 171 ++++ dom/workers/test/dynamicImport_nested.mjs | 8 + dom/workers/test/dynamicImport_postMessage.mjs | 8 + dom/workers/test/dynamicImport_worker.js | 15 + dom/workers/test/empty.html | 0 dom/workers/test/empty_worker.js | 1 + dom/workers/test/errorPropagation_iframe.html | 55 ++ dom/workers/test/errorPropagation_worker.js | 50 + dom/workers/test/errorwarning_worker.js | 44 + dom/workers/test/eventDispatch_worker.js | 84 ++ dom/workers/test/fibonacci_worker.js | 24 + dom/workers/test/fileBlobSubWorker_worker.js | 17 + dom/workers/test/fileBlob_worker.js | 13 + dom/workers/test/filePosting_worker.js | 3 + dom/workers/test/fileReadSlice_worker.js | 16 + dom/workers/test/fileReaderSyncErrors_worker.js | 82 ++ dom/workers/test/fileReaderSync_worker.js | 25 + dom/workers/test/fileSlice_worker.js | 27 + dom/workers/test/fileSubWorker_worker.js | 17 + dom/workers/test/file_bug1010784_worker.js | 9 + dom/workers/test/file_service_worker.js | 3 + .../test/file_service_worker_container.html | 15 + .../test/file_service_worker_fetch_synthetic.js | 59 ++ .../test/file_use_counter_service_worker.js | 9 + dom/workers/test/file_use_counter_shared_worker.js | 10 + .../file_use_counter_shared_worker_microtask.js | 12 + dom/workers/test/file_use_counter_worker.html | 13 + dom/workers/test/file_use_counter_worker.js | 2 + dom/workers/test/file_worker.js | 16 + dom/workers/test/foreign.js | 1 + dom/workers/test/head.js | 75 ++ dom/workers/test/importForeignScripts_worker.js | 55 ++ dom/workers/test/importScripts_3rdParty_worker.js | 88 ++ dom/workers/test/importScripts_mixedcontent.html | 46 + dom/workers/test/importScripts_worker.js | 63 ++ dom/workers/test/importScripts_worker_imported1.js | 9 + dom/workers/test/importScripts_worker_imported2.js | 9 + dom/workers/test/importScripts_worker_imported3.js | 6 + dom/workers/test/importScripts_worker_imported4.js | 6 + dom/workers/test/instanceof_worker.js | 16 + dom/workers/test/invalid.js | 1 + dom/workers/test/json_worker.js | 354 +++++++ dom/workers/test/loadEncoding_worker.js | 7 + dom/workers/test/location_worker.js | 12 + dom/workers/test/longThread_worker.js | 14 + dom/workers/test/marionette/manifest.ini | 2 + .../test/marionette/service_worker_utils.py | 63 ++ .../marionette/test_service_workers_at_startup.py | 31 + .../marionette/test_service_workers_disabled.py | 37 + dom/workers/test/mochitest.ini | 249 +++++ dom/workers/test/multi_sharedWorker_frame.html | 58 ++ .../test/multi_sharedWorker_frame_bfcache.html | 13 + .../test/multi_sharedWorker_frame_nobfcache.html | 13 + ...ulti_sharedWorker_frame_nobfcache.html^headers^ | 1 + dom/workers/test/multi_sharedWorker_manager.js | 58 ++ .../test/multi_sharedWorker_sharedWorker.js | 72 ++ dom/workers/test/navigate.html | 13 + dom/workers/test/navigator_languages_worker.js | 11 + dom/workers/test/navigator_worker.js | 85 ++ dom/workers/test/newError_worker.js | 5 + dom/workers/test/notification_permission_worker.js | 59 ++ dom/workers/test/notification_worker.js | 104 ++ .../test/notification_worker_child-child.js | 93 ++ .../test/notification_worker_child-parent.js | 26 + dom/workers/test/onLine_worker.js | 70 ++ dom/workers/test/onLine_worker_child.js | 91 ++ dom/workers/test/onLine_worker_head.js | 50 + dom/workers/test/promise_worker.js | 1007 ++++++++++++++++++++ dom/workers/test/recursion_worker.js | 46 + dom/workers/test/recursiveOnerror_worker.js | 11 + dom/workers/test/redirect_to_foreign.sjs | 7 + dom/workers/test/referrer.sjs | 14 + dom/workers/test/referrer_test_server.sjs | 99 ++ dom/workers/test/referrer_worker.html | 144 +++ dom/workers/test/rvals_worker.js | 13 + dom/workers/test/script_createFile.js | 42 + dom/workers/test/server_fetch_synthetic.sjs | 50 + dom/workers/test/sharedWorker_console.js | 11 + dom/workers/test/sharedWorker_lifetime.js | 5 + dom/workers/test/sharedWorker_ports.js | 30 + dom/workers/test/sharedWorker_privateBrowsing.js | 4 + dom/workers/test/sharedWorker_sharedWorker.js | 99 ++ .../test/sharedWorker_thirdparty_frame.html | 16 + .../test/sharedWorker_thirdparty_window.html | 26 + dom/workers/test/simpleThread_worker.js | 52 + dom/workers/test/sourcemap_header.js | 65 ++ dom/workers/test/sourcemap_header_debugger.js | 29 + dom/workers/test/sourcemap_header_iframe.html | 19 + dom/workers/test/sourcemap_header_worker.js | 8 + .../test/sourcemap_header_worker.js^headers^ | 1 + dom/workers/test/suspend_blank.html | 23 + dom/workers/test/suspend_window.html | 82 ++ dom/workers/test/suspend_worker.js | 13 + dom/workers/test/terminate_worker.js | 11 + dom/workers/test/test_404.html | 39 + .../test/test_WorkerDebugger.initialize.xhtml | 56 ++ .../test/test_WorkerDebugger.postMessage.xhtml | 59 ++ dom/workers/test/test_WorkerDebugger.xhtml | 145 +++ ...t_WorkerDebuggerGlobalScope.createSandbox.xhtml | 49 + ..._WorkerDebuggerGlobalScope.enterEventLoop.xhtml | 124 +++ ...est_WorkerDebuggerGlobalScope.reportError.xhtml | 95 ++ ...st_WorkerDebuggerGlobalScope.setImmediate.xhtml | 52 + dom/workers/test/test_WorkerDebuggerManager.xhtml | 104 ++ dom/workers/test/test_WorkerDebugger_console.xhtml | 98 ++ dom/workers/test/test_WorkerDebugger_frozen.xhtml | 77 ++ dom/workers/test/test_WorkerDebugger_promise.xhtml | 68 ++ .../test/test_WorkerDebugger_suspended.xhtml | 72 ++ dom/workers/test/test_atob.html | 57 ++ dom/workers/test/test_blobConstructor.html | 60 ++ dom/workers/test/test_blobWorkers.html | 31 + dom/workers/test/test_bug1002702.html | 26 + dom/workers/test/test_bug1010784.html | 35 + dom/workers/test/test_bug1014466.html | 42 + dom/workers/test/test_bug1020226.html | 38 + dom/workers/test/test_bug1036484.html | 52 + dom/workers/test/test_bug1060621.html | 30 + dom/workers/test/test_bug1062920.html | 70 ++ dom/workers/test/test_bug1062920.xhtml | 67 ++ dom/workers/test/test_bug1063538.html | 47 + dom/workers/test/test_bug1104064.html | 27 + dom/workers/test/test_bug1132395.html | 40 + dom/workers/test/test_bug1132924.html | 28 + dom/workers/test/test_bug1278777.html | 31 + dom/workers/test/test_bug1301094.html | 69 ++ dom/workers/test/test_bug1317725.html | 49 + dom/workers/test/test_bug1317725.js | 8 + dom/workers/test/test_bug1824498.html | 38 + dom/workers/test/test_bug949946.html | 26 + dom/workers/test/test_bug978260.html | 35 + dom/workers/test/test_bug998474.html | 40 + dom/workers/test/test_chromeWorker.html | 26 + dom/workers/test/test_chromeWorker.xhtml | 58 ++ dom/workers/test/test_chromeWorkerJSM.xhtml | 54 ++ dom/workers/test/test_clearTimeouts.html | 31 + dom/workers/test/test_clearTimeoutsImplicit.html | 31 + dom/workers/test/test_console.html | 44 + dom/workers/test/test_consoleAndBlobs.html | 46 + dom/workers/test/test_consoleReplaceable.html | 44 + dom/workers/test/test_contentWorker.html | 48 + dom/workers/test/test_csp.html | 18 + dom/workers/test/test_csp.html^headers^ | 2 + dom/workers/test/test_csp.js | 54 ++ dom/workers/test/test_dataURLWorker.html | 30 + dom/workers/test/test_dynamicImport.html | 76 ++ .../test/test_dynamicImport_and_terminate.html | 34 + .../test/test_dynamicImport_early_termination.html | 79 ++ dom/workers/test/test_errorPropagation.html | 65 ++ dom/workers/test/test_errorwarning.html | 95 ++ dom/workers/test/test_eventDispatch.html | 32 + dom/workers/test/test_fibonacci.html | 51 + dom/workers/test/test_file.xhtml | 96 ++ dom/workers/test/test_fileBlobPosting.xhtml | 85 ++ dom/workers/test/test_fileBlobSubWorker.xhtml | 97 ++ dom/workers/test/test_filePosting.xhtml | 85 ++ dom/workers/test/test_fileReadSlice.xhtml | 93 ++ dom/workers/test/test_fileReaderSync.xhtml | 198 ++++ dom/workers/test/test_fileReaderSyncErrors.xhtml | 83 ++ .../test/test_fileReaderSync_when_closing.html | 54 ++ dom/workers/test/test_fileSlice.xhtml | 105 ++ dom/workers/test/test_fileSubWorker.xhtml | 98 ++ dom/workers/test/test_importScripts.html | 53 ++ dom/workers/test/test_importScripts_3rdparty.html | 136 +++ .../test/test_importScripts_mixedcontent.html | 50 + dom/workers/test/test_instanceof.html | 40 + dom/workers/test/test_json.html | 89 ++ dom/workers/test/test_loadEncoding.html | 50 + dom/workers/test/test_loadError.html | 67 ++ dom/workers/test/test_location.html | 72 ++ dom/workers/test/test_longThread.html | 58 ++ dom/workers/test/test_multi_sharedWorker.html | 241 +++++ .../test_multi_sharedWorker_lifetimes_bfcache.html | 151 +++ ...est_multi_sharedWorker_lifetimes_nobfcache.html | 126 +++ dom/workers/test/test_navigator.html | 27 + dom/workers/test/test_navigator.js | 10 + dom/workers/test/test_navigator_iframe.html | 24 + dom/workers/test/test_navigator_iframe.js | 64 ++ dom/workers/test/test_navigator_languages.html | 58 ++ dom/workers/test/test_navigator_secureContext.html | 27 + ...test_navigator_workers_hardwareConcurrency.html | 56 ++ dom/workers/test/test_newError.html | 33 + dom/workers/test/test_notification.html | 47 + dom/workers/test/test_notification_child.html | 46 + dom/workers/test/test_notification_permission.html | 48 + dom/workers/test/test_onLine.html | 73 ++ dom/workers/test/test_promise.html | 43 + .../test/test_promise_resolved_with_string.html | 41 + .../test/test_readableStream_when_closing.html | 61 ++ dom/workers/test/test_recursion.html | 69 ++ dom/workers/test/test_recursiveOnerror.html | 44 + dom/workers/test/test_referrer.html | 58 ++ dom/workers/test/test_referrer_header_worker.html | 38 + .../test/test_resolveWorker-assignment.html | 30 + dom/workers/test/test_resolveWorker.html | 31 + dom/workers/test/test_rvals.html | 35 + dom/workers/test/test_setTimeoutWith0.html | 19 + dom/workers/test/test_sharedWorker.html | 71 ++ dom/workers/test/test_sharedWorker_lifetime.html | 30 + dom/workers/test/test_sharedWorker_ports.html | 41 + .../test/test_sharedWorker_privateBrowsing.html | 102 ++ dom/workers/test/test_sharedWorker_thirdparty.html | 54 ++ .../test_sharedworker_event_listener_leaks.html | 51 + dom/workers/test/test_shutdownCheck.xhtml | 61 ++ dom/workers/test/test_simpleThread.html | 74 ++ dom/workers/test/test_sourcemap_header.html | 22 + dom/workers/test/test_subworkers_suspended.html | 145 +++ dom/workers/test/test_suspend.html | 188 ++++ dom/workers/test/test_terminate.html | 100 ++ dom/workers/test/test_threadErrors.html | 64 ++ dom/workers/test/test_threadTimeouts.html | 60 ++ dom/workers/test/test_throwingOnerror.html | 54 ++ dom/workers/test/test_timeoutTracing.html | 47 + dom/workers/test/test_transferable.html | 123 +++ dom/workers/test/test_worker_interfaces.html | 19 + dom/workers/test/test_worker_interfaces.js | 543 +++++++++++ .../test/test_worker_interfaces_secureContext.html | 19 + dom/workers/test/threadErrors_worker1.js | 8 + dom/workers/test/threadErrors_worker2.js | 8 + dom/workers/test/threadErrors_worker3.js | 8 + dom/workers/test/threadErrors_worker4.js | 8 + dom/workers/test/threadTimeouts_worker.js | 44 + dom/workers/test/throwingOnerror_worker.js | 15 + dom/workers/test/timeoutTracing_worker.js | 15 + dom/workers/test/transferable_worker.js | 40 + dom/workers/test/window_suspended.html | 71 ++ dom/workers/test/worker_bug1278777.js | 9 + dom/workers/test/worker_bug1301094.js | 11 + dom/workers/test/worker_bug1824498.mjs | 4 + dom/workers/test/worker_consoleAndBlobs.js | 8 + dom/workers/test/worker_driver.js | 84 ++ dom/workers/test/worker_dynamicImport.mjs | 2 + dom/workers/test/worker_referrer.js | 9 + dom/workers/test/worker_setTimeoutWith0.js | 3 + dom/workers/test/worker_shutdownCheck.js | 1 + dom/workers/test/worker_suspended.js | 39 + dom/workers/test/worker_wrapper.js | 79 ++ dom/workers/test/xpcshell/data/chrome.manifest | 1 + dom/workers/test/xpcshell/data/worker.js | 6 + .../test/xpcshell/data/worker_fileReader.js | 7 + .../test/xpcshell/test_ext_redirects_sw_scripts.js | 415 ++++++++ dom/workers/test/xpcshell/test_fileReader.js | 33 + .../test_remoteworker_launch_new_process.js | 94 ++ dom/workers/test/xpcshell/test_workers.js | 37 + .../test/xpcshell/test_workers_clone_error.js | 42 + dom/workers/test/xpcshell/xpcshell.ini | 28 + 332 files changed, 15992 insertions(+) create mode 100644 dom/workers/test/404_server.sjs create mode 100644 dom/workers/test/WorkerDebugger.console_childWorker.js create mode 100644 dom/workers/test/WorkerDebugger.console_debugger.js create mode 100644 dom/workers/test/WorkerDebugger.console_worker.js create mode 100644 dom/workers/test/WorkerDebugger.initialize_childWorker.js create mode 100644 dom/workers/test/WorkerDebugger.initialize_debugger.js create mode 100644 dom/workers/test/WorkerDebugger.initialize_debugger_es_worker.js create mode 100644 dom/workers/test/WorkerDebugger.initialize_es_worker.js create mode 100644 dom/workers/test/WorkerDebugger.initialize_worker.js create mode 100644 dom/workers/test/WorkerDebugger.postMessage_childWorker.js create mode 100644 dom/workers/test/WorkerDebugger.postMessage_debugger.js create mode 100644 dom/workers/test/WorkerDebugger.postMessage_worker.js create mode 100644 dom/workers/test/WorkerDebuggerGlobalScope.createSandbox_debugger.js create mode 100644 dom/workers/test/WorkerDebuggerGlobalScope.createSandbox_sandbox.js create mode 100644 dom/workers/test/WorkerDebuggerGlobalScope.createSandbox_worker.js create mode 100644 dom/workers/test/WorkerDebuggerGlobalScope.enterEventLoop_childWorker.js create mode 100644 dom/workers/test/WorkerDebuggerGlobalScope.enterEventLoop_debugger.js create mode 100644 dom/workers/test/WorkerDebuggerGlobalScope.enterEventLoop_worker.js create mode 100644 dom/workers/test/WorkerDebuggerGlobalScope.reportError_childWorker.js create mode 100644 dom/workers/test/WorkerDebuggerGlobalScope.reportError_debugger.js create mode 100644 dom/workers/test/WorkerDebuggerGlobalScope.reportError_worker.js create mode 100644 dom/workers/test/WorkerDebuggerGlobalScope.setImmediate_debugger.js create mode 100644 dom/workers/test/WorkerDebuggerGlobalScope.setImmediate_worker.js create mode 100644 dom/workers/test/WorkerDebuggerManager_childWorker.js create mode 100644 dom/workers/test/WorkerDebuggerManager_worker.js create mode 100644 dom/workers/test/WorkerDebugger_childWorker.js create mode 100644 dom/workers/test/WorkerDebugger_frozen_window1.html create mode 100644 dom/workers/test/WorkerDebugger_frozen_window2.html create mode 100644 dom/workers/test/WorkerDebugger_frozen_worker1.js create mode 100644 dom/workers/test/WorkerDebugger_frozen_worker2.js create mode 100644 dom/workers/test/WorkerDebugger_promise_debugger.js create mode 100644 dom/workers/test/WorkerDebugger_promise_worker.js create mode 100644 dom/workers/test/WorkerDebugger_sharedWorker.js create mode 100644 dom/workers/test/WorkerDebugger_suspended_debugger.js create mode 100644 dom/workers/test/WorkerDebugger_suspended_worker.js create mode 100644 dom/workers/test/WorkerDebugger_worker.js create mode 100644 dom/workers/test/WorkerTest.jsm create mode 100644 dom/workers/test/WorkerTest_badworker.js create mode 100644 dom/workers/test/WorkerTest_subworker.js create mode 100644 dom/workers/test/WorkerTest_worker.js create mode 100644 dom/workers/test/atob_worker.js create mode 100644 dom/workers/test/blank.html create mode 100644 dom/workers/test/browser.ini create mode 100644 dom/workers/test/browser_WorkerDebugger.initialize.js create mode 100644 dom/workers/test/browser_bug1047663.js create mode 100644 dom/workers/test/browser_bug1104623.js create mode 100644 dom/workers/test/browser_consoleSharedWorkers.js create mode 100644 dom/workers/test/browser_fileURL.js create mode 100644 dom/workers/test/browser_privilegedmozilla_remoteworker.js create mode 100644 dom/workers/test/browser_serviceworker_fetch_new_process.js create mode 100644 dom/workers/test/browser_worker_use_counters.js create mode 100644 dom/workers/test/bug1014466_data1.txt create mode 100644 dom/workers/test/bug1014466_data2.txt create mode 100644 dom/workers/test/bug1014466_worker.js create mode 100644 dom/workers/test/bug1020226_frame.html create mode 100644 dom/workers/test/bug1020226_worker.js create mode 100644 dom/workers/test/bug1047663_tab.html create mode 100644 dom/workers/test/bug1047663_worker.sjs create mode 100644 dom/workers/test/bug1060621_worker.js create mode 100644 dom/workers/test/bug1062920_worker.js create mode 100644 dom/workers/test/bug1063538.sjs create mode 100644 dom/workers/test/bug1063538_worker.js create mode 100644 dom/workers/test/bug1104064_worker.js create mode 100644 dom/workers/test/bug1132395_sharedWorker.js create mode 100644 dom/workers/test/bug1132924_worker.js create mode 100644 dom/workers/test/bug978260_worker.js create mode 100644 dom/workers/test/bug998474_worker.js create mode 100644 dom/workers/test/chrome.ini create mode 100644 dom/workers/test/chromeWorker_subworker.js create mode 100644 dom/workers/test/chromeWorker_worker.js create mode 100644 dom/workers/test/chromeWorker_worker.sys.mjs create mode 100644 dom/workers/test/chromeWorker_worker_submod.sys.mjs create mode 100644 dom/workers/test/clearTimeoutsImplicit_worker.js create mode 100644 dom/workers/test/clearTimeouts_worker.js create mode 100644 dom/workers/test/consoleReplaceable_worker.js create mode 100644 dom/workers/test/console_worker.js create mode 100644 dom/workers/test/content_worker.js create mode 100644 dom/workers/test/crashtests/1153636.html create mode 100644 dom/workers/test/crashtests/1158031.html create mode 100644 dom/workers/test/crashtests/1228456.html create mode 100644 dom/workers/test/crashtests/1348882.html create mode 100644 dom/workers/test/crashtests/1819146.html create mode 100644 dom/workers/test/crashtests/1821066.html create mode 100644 dom/workers/test/crashtests/779707.html create mode 100644 dom/workers/test/crashtests/943516.html create mode 100644 dom/workers/test/crashtests/crashtests.list create mode 100644 dom/workers/test/csp_worker.js create mode 100644 dom/workers/test/csp_worker.js^headers^ create mode 100644 dom/workers/test/dom_worker_helper.js create mode 100644 dom/workers/test/dynamicImport_nested.mjs create mode 100644 dom/workers/test/dynamicImport_postMessage.mjs create mode 100644 dom/workers/test/dynamicImport_worker.js create mode 100644 dom/workers/test/empty.html create mode 100644 dom/workers/test/empty_worker.js create mode 100644 dom/workers/test/errorPropagation_iframe.html create mode 100644 dom/workers/test/errorPropagation_worker.js create mode 100644 dom/workers/test/errorwarning_worker.js create mode 100644 dom/workers/test/eventDispatch_worker.js create mode 100644 dom/workers/test/fibonacci_worker.js create mode 100644 dom/workers/test/fileBlobSubWorker_worker.js create mode 100644 dom/workers/test/fileBlob_worker.js create mode 100644 dom/workers/test/filePosting_worker.js create mode 100644 dom/workers/test/fileReadSlice_worker.js create mode 100644 dom/workers/test/fileReaderSyncErrors_worker.js create mode 100644 dom/workers/test/fileReaderSync_worker.js create mode 100644 dom/workers/test/fileSlice_worker.js create mode 100644 dom/workers/test/fileSubWorker_worker.js create mode 100644 dom/workers/test/file_bug1010784_worker.js create mode 100644 dom/workers/test/file_service_worker.js create mode 100644 dom/workers/test/file_service_worker_container.html create mode 100644 dom/workers/test/file_service_worker_fetch_synthetic.js create mode 100644 dom/workers/test/file_use_counter_service_worker.js create mode 100644 dom/workers/test/file_use_counter_shared_worker.js create mode 100644 dom/workers/test/file_use_counter_shared_worker_microtask.js create mode 100644 dom/workers/test/file_use_counter_worker.html create mode 100644 dom/workers/test/file_use_counter_worker.js create mode 100644 dom/workers/test/file_worker.js create mode 100644 dom/workers/test/foreign.js create mode 100644 dom/workers/test/head.js create mode 100644 dom/workers/test/importForeignScripts_worker.js create mode 100644 dom/workers/test/importScripts_3rdParty_worker.js create mode 100644 dom/workers/test/importScripts_mixedcontent.html create mode 100644 dom/workers/test/importScripts_worker.js create mode 100644 dom/workers/test/importScripts_worker_imported1.js create mode 100644 dom/workers/test/importScripts_worker_imported2.js create mode 100644 dom/workers/test/importScripts_worker_imported3.js create mode 100644 dom/workers/test/importScripts_worker_imported4.js create mode 100644 dom/workers/test/instanceof_worker.js create mode 100644 dom/workers/test/invalid.js create mode 100644 dom/workers/test/json_worker.js create mode 100644 dom/workers/test/loadEncoding_worker.js create mode 100644 dom/workers/test/location_worker.js create mode 100644 dom/workers/test/longThread_worker.js create mode 100644 dom/workers/test/marionette/manifest.ini create mode 100644 dom/workers/test/marionette/service_worker_utils.py create mode 100644 dom/workers/test/marionette/test_service_workers_at_startup.py create mode 100644 dom/workers/test/marionette/test_service_workers_disabled.py create mode 100644 dom/workers/test/mochitest.ini create mode 100644 dom/workers/test/multi_sharedWorker_frame.html create mode 100644 dom/workers/test/multi_sharedWorker_frame_bfcache.html create mode 100644 dom/workers/test/multi_sharedWorker_frame_nobfcache.html create mode 100644 dom/workers/test/multi_sharedWorker_frame_nobfcache.html^headers^ create mode 100644 dom/workers/test/multi_sharedWorker_manager.js create mode 100644 dom/workers/test/multi_sharedWorker_sharedWorker.js create mode 100644 dom/workers/test/navigate.html create mode 100644 dom/workers/test/navigator_languages_worker.js create mode 100644 dom/workers/test/navigator_worker.js create mode 100644 dom/workers/test/newError_worker.js create mode 100644 dom/workers/test/notification_permission_worker.js create mode 100644 dom/workers/test/notification_worker.js create mode 100644 dom/workers/test/notification_worker_child-child.js create mode 100644 dom/workers/test/notification_worker_child-parent.js create mode 100644 dom/workers/test/onLine_worker.js create mode 100644 dom/workers/test/onLine_worker_child.js create mode 100644 dom/workers/test/onLine_worker_head.js create mode 100644 dom/workers/test/promise_worker.js create mode 100644 dom/workers/test/recursion_worker.js create mode 100644 dom/workers/test/recursiveOnerror_worker.js create mode 100644 dom/workers/test/redirect_to_foreign.sjs create mode 100644 dom/workers/test/referrer.sjs create mode 100644 dom/workers/test/referrer_test_server.sjs create mode 100644 dom/workers/test/referrer_worker.html create mode 100644 dom/workers/test/rvals_worker.js create mode 100644 dom/workers/test/script_createFile.js create mode 100644 dom/workers/test/server_fetch_synthetic.sjs create mode 100644 dom/workers/test/sharedWorker_console.js create mode 100644 dom/workers/test/sharedWorker_lifetime.js create mode 100644 dom/workers/test/sharedWorker_ports.js create mode 100644 dom/workers/test/sharedWorker_privateBrowsing.js create mode 100644 dom/workers/test/sharedWorker_sharedWorker.js create mode 100644 dom/workers/test/sharedWorker_thirdparty_frame.html create mode 100644 dom/workers/test/sharedWorker_thirdparty_window.html create mode 100644 dom/workers/test/simpleThread_worker.js create mode 100644 dom/workers/test/sourcemap_header.js create mode 100644 dom/workers/test/sourcemap_header_debugger.js create mode 100644 dom/workers/test/sourcemap_header_iframe.html create mode 100644 dom/workers/test/sourcemap_header_worker.js create mode 100644 dom/workers/test/sourcemap_header_worker.js^headers^ create mode 100644 dom/workers/test/suspend_blank.html create mode 100644 dom/workers/test/suspend_window.html create mode 100644 dom/workers/test/suspend_worker.js create mode 100644 dom/workers/test/terminate_worker.js create mode 100644 dom/workers/test/test_404.html create mode 100644 dom/workers/test/test_WorkerDebugger.initialize.xhtml create mode 100644 dom/workers/test/test_WorkerDebugger.postMessage.xhtml create mode 100644 dom/workers/test/test_WorkerDebugger.xhtml create mode 100644 dom/workers/test/test_WorkerDebuggerGlobalScope.createSandbox.xhtml create mode 100644 dom/workers/test/test_WorkerDebuggerGlobalScope.enterEventLoop.xhtml create mode 100644 dom/workers/test/test_WorkerDebuggerGlobalScope.reportError.xhtml create mode 100644 dom/workers/test/test_WorkerDebuggerGlobalScope.setImmediate.xhtml create mode 100644 dom/workers/test/test_WorkerDebuggerManager.xhtml create mode 100644 dom/workers/test/test_WorkerDebugger_console.xhtml create mode 100644 dom/workers/test/test_WorkerDebugger_frozen.xhtml create mode 100644 dom/workers/test/test_WorkerDebugger_promise.xhtml create mode 100644 dom/workers/test/test_WorkerDebugger_suspended.xhtml create mode 100644 dom/workers/test/test_atob.html create mode 100644 dom/workers/test/test_blobConstructor.html create mode 100644 dom/workers/test/test_blobWorkers.html create mode 100644 dom/workers/test/test_bug1002702.html create mode 100644 dom/workers/test/test_bug1010784.html create mode 100644 dom/workers/test/test_bug1014466.html create mode 100644 dom/workers/test/test_bug1020226.html create mode 100644 dom/workers/test/test_bug1036484.html create mode 100644 dom/workers/test/test_bug1060621.html create mode 100644 dom/workers/test/test_bug1062920.html create mode 100644 dom/workers/test/test_bug1062920.xhtml create mode 100644 dom/workers/test/test_bug1063538.html create mode 100644 dom/workers/test/test_bug1104064.html create mode 100644 dom/workers/test/test_bug1132395.html create mode 100644 dom/workers/test/test_bug1132924.html create mode 100644 dom/workers/test/test_bug1278777.html create mode 100644 dom/workers/test/test_bug1301094.html create mode 100644 dom/workers/test/test_bug1317725.html create mode 100644 dom/workers/test/test_bug1317725.js create mode 100644 dom/workers/test/test_bug1824498.html create mode 100644 dom/workers/test/test_bug949946.html create mode 100644 dom/workers/test/test_bug978260.html create mode 100644 dom/workers/test/test_bug998474.html create mode 100644 dom/workers/test/test_chromeWorker.html create mode 100644 dom/workers/test/test_chromeWorker.xhtml create mode 100644 dom/workers/test/test_chromeWorkerJSM.xhtml create mode 100644 dom/workers/test/test_clearTimeouts.html create mode 100644 dom/workers/test/test_clearTimeoutsImplicit.html create mode 100644 dom/workers/test/test_console.html create mode 100644 dom/workers/test/test_consoleAndBlobs.html create mode 100644 dom/workers/test/test_consoleReplaceable.html create mode 100644 dom/workers/test/test_contentWorker.html create mode 100644 dom/workers/test/test_csp.html create mode 100644 dom/workers/test/test_csp.html^headers^ create mode 100644 dom/workers/test/test_csp.js create mode 100644 dom/workers/test/test_dataURLWorker.html create mode 100644 dom/workers/test/test_dynamicImport.html create mode 100644 dom/workers/test/test_dynamicImport_and_terminate.html create mode 100644 dom/workers/test/test_dynamicImport_early_termination.html create mode 100644 dom/workers/test/test_errorPropagation.html create mode 100644 dom/workers/test/test_errorwarning.html create mode 100644 dom/workers/test/test_eventDispatch.html create mode 100644 dom/workers/test/test_fibonacci.html create mode 100644 dom/workers/test/test_file.xhtml create mode 100644 dom/workers/test/test_fileBlobPosting.xhtml create mode 100644 dom/workers/test/test_fileBlobSubWorker.xhtml create mode 100644 dom/workers/test/test_filePosting.xhtml create mode 100644 dom/workers/test/test_fileReadSlice.xhtml create mode 100644 dom/workers/test/test_fileReaderSync.xhtml create mode 100644 dom/workers/test/test_fileReaderSyncErrors.xhtml create mode 100644 dom/workers/test/test_fileReaderSync_when_closing.html create mode 100644 dom/workers/test/test_fileSlice.xhtml create mode 100644 dom/workers/test/test_fileSubWorker.xhtml create mode 100644 dom/workers/test/test_importScripts.html create mode 100644 dom/workers/test/test_importScripts_3rdparty.html create mode 100644 dom/workers/test/test_importScripts_mixedcontent.html create mode 100644 dom/workers/test/test_instanceof.html create mode 100644 dom/workers/test/test_json.html create mode 100644 dom/workers/test/test_loadEncoding.html create mode 100644 dom/workers/test/test_loadError.html create mode 100644 dom/workers/test/test_location.html create mode 100644 dom/workers/test/test_longThread.html create mode 100644 dom/workers/test/test_multi_sharedWorker.html create mode 100644 dom/workers/test/test_multi_sharedWorker_lifetimes_bfcache.html create mode 100644 dom/workers/test/test_multi_sharedWorker_lifetimes_nobfcache.html create mode 100644 dom/workers/test/test_navigator.html create mode 100644 dom/workers/test/test_navigator.js create mode 100644 dom/workers/test/test_navigator_iframe.html create mode 100644 dom/workers/test/test_navigator_iframe.js create mode 100644 dom/workers/test/test_navigator_languages.html create mode 100644 dom/workers/test/test_navigator_secureContext.html create mode 100644 dom/workers/test/test_navigator_workers_hardwareConcurrency.html create mode 100644 dom/workers/test/test_newError.html create mode 100644 dom/workers/test/test_notification.html create mode 100644 dom/workers/test/test_notification_child.html create mode 100644 dom/workers/test/test_notification_permission.html create mode 100644 dom/workers/test/test_onLine.html create mode 100644 dom/workers/test/test_promise.html create mode 100644 dom/workers/test/test_promise_resolved_with_string.html create mode 100644 dom/workers/test/test_readableStream_when_closing.html create mode 100644 dom/workers/test/test_recursion.html create mode 100644 dom/workers/test/test_recursiveOnerror.html create mode 100644 dom/workers/test/test_referrer.html create mode 100644 dom/workers/test/test_referrer_header_worker.html create mode 100644 dom/workers/test/test_resolveWorker-assignment.html create mode 100644 dom/workers/test/test_resolveWorker.html create mode 100644 dom/workers/test/test_rvals.html create mode 100644 dom/workers/test/test_setTimeoutWith0.html create mode 100644 dom/workers/test/test_sharedWorker.html create mode 100644 dom/workers/test/test_sharedWorker_lifetime.html create mode 100644 dom/workers/test/test_sharedWorker_ports.html create mode 100644 dom/workers/test/test_sharedWorker_privateBrowsing.html create mode 100644 dom/workers/test/test_sharedWorker_thirdparty.html create mode 100644 dom/workers/test/test_sharedworker_event_listener_leaks.html create mode 100644 dom/workers/test/test_shutdownCheck.xhtml create mode 100644 dom/workers/test/test_simpleThread.html create mode 100644 dom/workers/test/test_sourcemap_header.html create mode 100644 dom/workers/test/test_subworkers_suspended.html create mode 100644 dom/workers/test/test_suspend.html create mode 100644 dom/workers/test/test_terminate.html create mode 100644 dom/workers/test/test_threadErrors.html create mode 100644 dom/workers/test/test_threadTimeouts.html create mode 100644 dom/workers/test/test_throwingOnerror.html create mode 100644 dom/workers/test/test_timeoutTracing.html create mode 100644 dom/workers/test/test_transferable.html create mode 100644 dom/workers/test/test_worker_interfaces.html create mode 100644 dom/workers/test/test_worker_interfaces.js create mode 100644 dom/workers/test/test_worker_interfaces_secureContext.html create mode 100644 dom/workers/test/threadErrors_worker1.js create mode 100644 dom/workers/test/threadErrors_worker2.js create mode 100644 dom/workers/test/threadErrors_worker3.js create mode 100644 dom/workers/test/threadErrors_worker4.js create mode 100644 dom/workers/test/threadTimeouts_worker.js create mode 100644 dom/workers/test/throwingOnerror_worker.js create mode 100644 dom/workers/test/timeoutTracing_worker.js create mode 100644 dom/workers/test/transferable_worker.js create mode 100644 dom/workers/test/window_suspended.html create mode 100644 dom/workers/test/worker_bug1278777.js create mode 100644 dom/workers/test/worker_bug1301094.js create mode 100644 dom/workers/test/worker_bug1824498.mjs create mode 100644 dom/workers/test/worker_consoleAndBlobs.js create mode 100644 dom/workers/test/worker_driver.js create mode 100644 dom/workers/test/worker_dynamicImport.mjs create mode 100644 dom/workers/test/worker_referrer.js create mode 100644 dom/workers/test/worker_setTimeoutWith0.js create mode 100644 dom/workers/test/worker_shutdownCheck.js create mode 100644 dom/workers/test/worker_suspended.js create mode 100644 dom/workers/test/worker_wrapper.js create mode 100644 dom/workers/test/xpcshell/data/chrome.manifest create mode 100644 dom/workers/test/xpcshell/data/worker.js create mode 100644 dom/workers/test/xpcshell/data/worker_fileReader.js create mode 100644 dom/workers/test/xpcshell/test_ext_redirects_sw_scripts.js create mode 100644 dom/workers/test/xpcshell/test_fileReader.js create mode 100644 dom/workers/test/xpcshell/test_remoteworker_launch_new_process.js create mode 100644 dom/workers/test/xpcshell/test_workers.js create mode 100644 dom/workers/test/xpcshell/test_workers_clone_error.js create mode 100644 dom/workers/test/xpcshell/xpcshell.ini (limited to 'dom/workers/test') diff --git a/dom/workers/test/404_server.sjs b/dom/workers/test/404_server.sjs new file mode 100644 index 0000000000..f83281efa8 --- /dev/null +++ b/dom/workers/test/404_server.sjs @@ -0,0 +1,9 @@ +function handleRequest(request, response) { + response.setStatusLine(request.httpVersion, 404, "Not found"); + + // Any valid JS. + if (request.queryString == "js") { + response.setHeader("Content-Type", "text/javascript", false); + response.write("4 + 4"); + } +} diff --git a/dom/workers/test/WorkerDebugger.console_childWorker.js b/dom/workers/test/WorkerDebugger.console_childWorker.js new file mode 100644 index 0000000000..8cee6809e5 --- /dev/null +++ b/dom/workers/test/WorkerDebugger.console_childWorker.js @@ -0,0 +1,3 @@ +"use strict"; + +self.onmessage = function () {}; diff --git a/dom/workers/test/WorkerDebugger.console_debugger.js b/dom/workers/test/WorkerDebugger.console_debugger.js new file mode 100644 index 0000000000..a8b2493200 --- /dev/null +++ b/dom/workers/test/WorkerDebugger.console_debugger.js @@ -0,0 +1,49 @@ +"use strict"; + +function ok(a, msg) { + postMessage(JSON.stringify({ type: "status", what: !!a, msg })); +} + +function is(a, b, msg) { + ok(a === b, msg); +} + +function finish() { + postMessage(JSON.stringify({ type: "finish" })); +} + +function magic() { + console.log("Hello from the debugger script!"); + + var foo = retrieveConsoleEvents(); + ok(Array.isArray(foo), "We received an array."); + ok(foo.length >= 2, "At least 2 messages."); + + is( + foo[0].arguments[0], + "Can you see this console message?", + "First message ok." + ); + is( + foo[1].arguments[0], + "Can you see this second console message?", + "Second message ok." + ); + + setConsoleEventHandler(function (consoleData) { + is(consoleData.arguments[0], "Random message.", "Random message ok!"); + + // The consoleEventHandler can be null. + setConsoleEventHandler(null); + + finish(); + }); +} + +this.onmessage = function (event) { + switch (event.data) { + case "do magic": + magic(); + break; + } +}; diff --git a/dom/workers/test/WorkerDebugger.console_worker.js b/dom/workers/test/WorkerDebugger.console_worker.js new file mode 100644 index 0000000000..a4d6af2e0f --- /dev/null +++ b/dom/workers/test/WorkerDebugger.console_worker.js @@ -0,0 +1,10 @@ +"use strict"; + +console.log("Can you see this console message?"); +console.warn("Can you see this second console message?"); + +var worker = new Worker("WorkerDebugger.console_childWorker.js"); + +setInterval(function () { + console.log("Random message."); +}, 200); diff --git a/dom/workers/test/WorkerDebugger.initialize_childWorker.js b/dom/workers/test/WorkerDebugger.initialize_childWorker.js new file mode 100644 index 0000000000..a85764bd9c --- /dev/null +++ b/dom/workers/test/WorkerDebugger.initialize_childWorker.js @@ -0,0 +1,6 @@ +"use strict"; + +self.onmessage = function () {}; + +debugger; +postMessage("worker"); diff --git a/dom/workers/test/WorkerDebugger.initialize_debugger.js b/dom/workers/test/WorkerDebugger.initialize_debugger.js new file mode 100644 index 0000000000..f52e95b159 --- /dev/null +++ b/dom/workers/test/WorkerDebugger.initialize_debugger.js @@ -0,0 +1,6 @@ +"use strict"; + +var dbg = new Debugger(global); +dbg.onDebuggerStatement = function (frame) { + frame.eval("postMessage('debugger');"); +}; diff --git a/dom/workers/test/WorkerDebugger.initialize_debugger_es_worker.js b/dom/workers/test/WorkerDebugger.initialize_debugger_es_worker.js new file mode 100644 index 0000000000..d357fca2d3 --- /dev/null +++ b/dom/workers/test/WorkerDebugger.initialize_debugger_es_worker.js @@ -0,0 +1,10 @@ +"use strict"; + +// The following check is one possible way to identify +// if this script is loaded as a ES Module or the classic way. +const isLoadedAsEsModule = this != globalThis; + +// We expect the debugger script to always be loaded as classic +if (!isLoadedAsEsModule) { + postMessage("debugger script ran"); +} diff --git a/dom/workers/test/WorkerDebugger.initialize_es_worker.js b/dom/workers/test/WorkerDebugger.initialize_es_worker.js new file mode 100644 index 0000000000..1b9ddda769 --- /dev/null +++ b/dom/workers/test/WorkerDebugger.initialize_es_worker.js @@ -0,0 +1,10 @@ +"use strict"; + +// The following check is one possible way to identify +// if this script is loaded as a ES Module or the classic way. +const isLoadedAsEsModule = this != globalThis; + +// Here we expect the worker to be loaded a ES Module +if (isLoadedAsEsModule) { + postMessage("worker"); +} diff --git a/dom/workers/test/WorkerDebugger.initialize_worker.js b/dom/workers/test/WorkerDebugger.initialize_worker.js new file mode 100644 index 0000000000..5a24efd3ac --- /dev/null +++ b/dom/workers/test/WorkerDebugger.initialize_worker.js @@ -0,0 +1,9 @@ +"use strict"; + +var worker = new Worker("WorkerDebugger.initialize_childWorker.js"); +worker.onmessage = function (event) { + postMessage("child:" + event.data); +}; + +debugger; +postMessage("worker"); diff --git a/dom/workers/test/WorkerDebugger.postMessage_childWorker.js b/dom/workers/test/WorkerDebugger.postMessage_childWorker.js new file mode 100644 index 0000000000..8cee6809e5 --- /dev/null +++ b/dom/workers/test/WorkerDebugger.postMessage_childWorker.js @@ -0,0 +1,3 @@ +"use strict"; + +self.onmessage = function () {}; diff --git a/dom/workers/test/WorkerDebugger.postMessage_debugger.js b/dom/workers/test/WorkerDebugger.postMessage_debugger.js new file mode 100644 index 0000000000..14236a4430 --- /dev/null +++ b/dom/workers/test/WorkerDebugger.postMessage_debugger.js @@ -0,0 +1,9 @@ +"use strict"; + +this.onmessage = function (event) { + switch (event.data) { + case "ping": + postMessage("pong"); + break; + } +}; diff --git a/dom/workers/test/WorkerDebugger.postMessage_worker.js b/dom/workers/test/WorkerDebugger.postMessage_worker.js new file mode 100644 index 0000000000..8ddf6cf865 --- /dev/null +++ b/dom/workers/test/WorkerDebugger.postMessage_worker.js @@ -0,0 +1,3 @@ +"use strict"; + +var worker = new Worker("WorkerDebugger.postMessage_childWorker.js"); diff --git a/dom/workers/test/WorkerDebuggerGlobalScope.createSandbox_debugger.js b/dom/workers/test/WorkerDebuggerGlobalScope.createSandbox_debugger.js new file mode 100644 index 0000000000..908c9f3161 --- /dev/null +++ b/dom/workers/test/WorkerDebuggerGlobalScope.createSandbox_debugger.js @@ -0,0 +1,9 @@ +"use strict"; + +const SANDBOX_URL = "WorkerDebuggerGlobalScope.createSandbox_sandbox.js"; + +var prototype = { + self: this, +}; +var sandbox = createSandbox(SANDBOX_URL, prototype); +loadSubScript(SANDBOX_URL, sandbox); diff --git a/dom/workers/test/WorkerDebuggerGlobalScope.createSandbox_sandbox.js b/dom/workers/test/WorkerDebuggerGlobalScope.createSandbox_sandbox.js new file mode 100644 index 0000000000..d2de6de924 --- /dev/null +++ b/dom/workers/test/WorkerDebuggerGlobalScope.createSandbox_sandbox.js @@ -0,0 +1,9 @@ +"use strict"; + +self.addEventListener("message", function (event) { + switch (event.data) { + case "ping": + self.postMessage("pong"); + break; + } +}); diff --git a/dom/workers/test/WorkerDebuggerGlobalScope.createSandbox_worker.js b/dom/workers/test/WorkerDebuggerGlobalScope.createSandbox_worker.js new file mode 100644 index 0000000000..8cee6809e5 --- /dev/null +++ b/dom/workers/test/WorkerDebuggerGlobalScope.createSandbox_worker.js @@ -0,0 +1,3 @@ +"use strict"; + +self.onmessage = function () {}; diff --git a/dom/workers/test/WorkerDebuggerGlobalScope.enterEventLoop_childWorker.js b/dom/workers/test/WorkerDebuggerGlobalScope.enterEventLoop_childWorker.js new file mode 100644 index 0000000000..e0b838759e --- /dev/null +++ b/dom/workers/test/WorkerDebuggerGlobalScope.enterEventLoop_childWorker.js @@ -0,0 +1,14 @@ +"use strict"; + +function f() { + debugger; +} + +self.onmessage = function (event) { + switch (event.data) { + case "ping": + debugger; + postMessage("pong"); + break; + } +}; diff --git a/dom/workers/test/WorkerDebuggerGlobalScope.enterEventLoop_debugger.js b/dom/workers/test/WorkerDebuggerGlobalScope.enterEventLoop_debugger.js new file mode 100644 index 0000000000..d2a1a21e85 --- /dev/null +++ b/dom/workers/test/WorkerDebuggerGlobalScope.enterEventLoop_debugger.js @@ -0,0 +1,29 @@ +"use strict"; + +var frames = []; + +var dbg = new Debugger(global); +dbg.onDebuggerStatement = function (frame) { + frames.push(frame); + postMessage("paused"); + enterEventLoop(); + frames.pop(); + postMessage("resumed"); +}; + +this.onmessage = function (event) { + switch (event.data) { + case "eval": + frames[frames.length - 1].eval("f()"); + postMessage("evalled"); + break; + + case "ping": + postMessage("pong"); + break; + + case "resume": + leaveEventLoop(); + break; + } +}; diff --git a/dom/workers/test/WorkerDebuggerGlobalScope.enterEventLoop_worker.js b/dom/workers/test/WorkerDebuggerGlobalScope.enterEventLoop_worker.js new file mode 100644 index 0000000000..8108a20289 --- /dev/null +++ b/dom/workers/test/WorkerDebuggerGlobalScope.enterEventLoop_worker.js @@ -0,0 +1,27 @@ +"use strict"; + +function f() { + debugger; +} + +var worker = new Worker( + "WorkerDebuggerGlobalScope.enterEventLoop_childWorker.js" +); + +worker.onmessage = function (event) { + postMessage("child:" + event.data); +}; + +self.onmessage = function (event) { + var message = event.data; + if (message.includes(":")) { + worker.postMessage(message.split(":")[1]); + return; + } + switch (message) { + case "ping": + debugger; + postMessage("pong"); + break; + } +}; diff --git a/dom/workers/test/WorkerDebuggerGlobalScope.reportError_childWorker.js b/dom/workers/test/WorkerDebuggerGlobalScope.reportError_childWorker.js new file mode 100644 index 0000000000..0fb4113a24 --- /dev/null +++ b/dom/workers/test/WorkerDebuggerGlobalScope.reportError_childWorker.js @@ -0,0 +1,5 @@ +"use strict"; + +self.onerror = function () { + postMessage("error"); +}; diff --git a/dom/workers/test/WorkerDebuggerGlobalScope.reportError_debugger.js b/dom/workers/test/WorkerDebuggerGlobalScope.reportError_debugger.js new file mode 100644 index 0000000000..9d50dc67cd --- /dev/null +++ b/dom/workers/test/WorkerDebuggerGlobalScope.reportError_debugger.js @@ -0,0 +1,12 @@ +"use strict"; + +this.onmessage = function (event) { + switch (event.data) { + case "report": + reportError("reported"); + break; + case "throw": + throw new Error("thrown"); + break; + } +}; diff --git a/dom/workers/test/WorkerDebuggerGlobalScope.reportError_worker.js b/dom/workers/test/WorkerDebuggerGlobalScope.reportError_worker.js new file mode 100644 index 0000000000..67ccfc2ca0 --- /dev/null +++ b/dom/workers/test/WorkerDebuggerGlobalScope.reportError_worker.js @@ -0,0 +1,11 @@ +"use strict"; + +var worker = new Worker("WorkerDebuggerGlobalScope.reportError_childWorker.js"); + +worker.onmessage = function (event) { + postMessage("child:" + event.data); +}; + +self.onerror = function () { + postMessage("error"); +}; diff --git a/dom/workers/test/WorkerDebuggerGlobalScope.setImmediate_debugger.js b/dom/workers/test/WorkerDebuggerGlobalScope.setImmediate_debugger.js new file mode 100644 index 0000000000..b2a01d380c --- /dev/null +++ b/dom/workers/test/WorkerDebuggerGlobalScope.setImmediate_debugger.js @@ -0,0 +1,12 @@ +"use strict"; + +this.onmessage = function (event) { + switch (event.data) { + case "ping": + setImmediate(function () { + postMessage("pong1"); + }); + postMessage("pong2"); + break; + } +}; diff --git a/dom/workers/test/WorkerDebuggerGlobalScope.setImmediate_worker.js b/dom/workers/test/WorkerDebuggerGlobalScope.setImmediate_worker.js new file mode 100644 index 0000000000..8cee6809e5 --- /dev/null +++ b/dom/workers/test/WorkerDebuggerGlobalScope.setImmediate_worker.js @@ -0,0 +1,3 @@ +"use strict"; + +self.onmessage = function () {}; diff --git a/dom/workers/test/WorkerDebuggerManager_childWorker.js b/dom/workers/test/WorkerDebuggerManager_childWorker.js new file mode 100644 index 0000000000..8cee6809e5 --- /dev/null +++ b/dom/workers/test/WorkerDebuggerManager_childWorker.js @@ -0,0 +1,3 @@ +"use strict"; + +self.onmessage = function () {}; diff --git a/dom/workers/test/WorkerDebuggerManager_worker.js b/dom/workers/test/WorkerDebuggerManager_worker.js new file mode 100644 index 0000000000..0737d17ebc --- /dev/null +++ b/dom/workers/test/WorkerDebuggerManager_worker.js @@ -0,0 +1,3 @@ +"use strict"; + +var worker = new Worker("WorkerDebuggerManager_childWorker.js"); diff --git a/dom/workers/test/WorkerDebugger_childWorker.js b/dom/workers/test/WorkerDebugger_childWorker.js new file mode 100644 index 0000000000..8cee6809e5 --- /dev/null +++ b/dom/workers/test/WorkerDebugger_childWorker.js @@ -0,0 +1,3 @@ +"use strict"; + +self.onmessage = function () {}; diff --git a/dom/workers/test/WorkerDebugger_frozen_window1.html b/dom/workers/test/WorkerDebugger_frozen_window1.html new file mode 100644 index 0000000000..05d65bbb54 --- /dev/null +++ b/dom/workers/test/WorkerDebugger_frozen_window1.html @@ -0,0 +1,15 @@ + + + + + + + + This is page 1. + + diff --git a/dom/workers/test/WorkerDebugger_frozen_window2.html b/dom/workers/test/WorkerDebugger_frozen_window2.html new file mode 100644 index 0000000000..3a2445ba54 --- /dev/null +++ b/dom/workers/test/WorkerDebugger_frozen_window2.html @@ -0,0 +1,15 @@ + + + + + + + + This is page 2. + + diff --git a/dom/workers/test/WorkerDebugger_frozen_worker1.js b/dom/workers/test/WorkerDebugger_frozen_worker1.js new file mode 100644 index 0000000000..371d2c064b --- /dev/null +++ b/dom/workers/test/WorkerDebugger_frozen_worker1.js @@ -0,0 +1,5 @@ +"use strict"; + +onmessage = function () {}; + +postMessage("ready"); diff --git a/dom/workers/test/WorkerDebugger_frozen_worker2.js b/dom/workers/test/WorkerDebugger_frozen_worker2.js new file mode 100644 index 0000000000..371d2c064b --- /dev/null +++ b/dom/workers/test/WorkerDebugger_frozen_worker2.js @@ -0,0 +1,5 @@ +"use strict"; + +onmessage = function () {}; + +postMessage("ready"); diff --git a/dom/workers/test/WorkerDebugger_promise_debugger.js b/dom/workers/test/WorkerDebugger_promise_debugger.js new file mode 100644 index 0000000000..dd4d1bf2c9 --- /dev/null +++ b/dom/workers/test/WorkerDebugger_promise_debugger.js @@ -0,0 +1,34 @@ +"use strict"; + +var self = this; + +self.onmessage = function (event) { + if (event.data !== "resolve") { + return; + } + // This then-handler should be executed inside the top-level event loop, + // within the context of the debugger's global. + Promise.resolve().then(function () { + var dbg = new Debugger(global); + dbg.onDebuggerStatement = function () { + self.onmessage = function (e) { + if (e.data !== "resume") { + return; + } + // This then-handler should be executed inside the nested event loop, + // within the context of the debugger's global. + Promise.resolve().then(function () { + postMessage("resumed"); + leaveEventLoop(); + }); + }; + // Test bug 1392540 where DOM Promises from debugger principal + // where frozen while hitting a worker breakpoint. + Promise.resolve().then(() => { + postMessage("paused"); + }); + enterEventLoop(); + }; + postMessage("resolved"); + }); +}; diff --git a/dom/workers/test/WorkerDebugger_promise_worker.js b/dom/workers/test/WorkerDebugger_promise_worker.js new file mode 100644 index 0000000000..4b1042e80b --- /dev/null +++ b/dom/workers/test/WorkerDebugger_promise_worker.js @@ -0,0 +1,25 @@ +"use strict"; + +self.onmessage = function (event) { + if (event.data !== "resolve") { + return; + } + // This then-handler should be executed inside the top-level event loop, + // within the context of the worker's global. + Promise.resolve().then(function () { + self.onmessage = function (e) { + if (e.data !== "pause") { + return; + } + // This then-handler should be executed inside the top-level event loop, + // within the context of the worker's global. Because the debugger + // statement here below enters a nested event loop, the then-handler + // should not be executed until the debugger statement returns. + Promise.resolve().then(function () { + postMessage("resumed"); + }); + debugger; + }; + postMessage("resolved"); + }); +}; diff --git a/dom/workers/test/WorkerDebugger_sharedWorker.js b/dom/workers/test/WorkerDebugger_sharedWorker.js new file mode 100644 index 0000000000..18dfa87783 --- /dev/null +++ b/dom/workers/test/WorkerDebugger_sharedWorker.js @@ -0,0 +1,17 @@ +"use strict"; + +self.onconnect = function (event) { + event.ports[0].onmessage = function (e) { + switch (e.data) { + case "close": + close(); + break; + + case "close_loop": + close(); + // Let's loop forever. + while (1) {} + break; + } + }; +}; diff --git a/dom/workers/test/WorkerDebugger_suspended_debugger.js b/dom/workers/test/WorkerDebugger_suspended_debugger.js new file mode 100644 index 0000000000..2ed4e16c44 --- /dev/null +++ b/dom/workers/test/WorkerDebugger_suspended_debugger.js @@ -0,0 +1,6 @@ +"use strict"; + +var dbg = new Debugger(global); +dbg.onDebuggerStatement = function (frame) { + postMessage("debugger"); +}; diff --git a/dom/workers/test/WorkerDebugger_suspended_worker.js b/dom/workers/test/WorkerDebugger_suspended_worker.js new file mode 100644 index 0000000000..c096be7e46 --- /dev/null +++ b/dom/workers/test/WorkerDebugger_suspended_worker.js @@ -0,0 +1,6 @@ +"use strict"; + +self.onmessage = function () { + postMessage("worker"); + debugger; +}; diff --git a/dom/workers/test/WorkerDebugger_worker.js b/dom/workers/test/WorkerDebugger_worker.js new file mode 100644 index 0000000000..f301ac1e85 --- /dev/null +++ b/dom/workers/test/WorkerDebugger_worker.js @@ -0,0 +1,8 @@ +"use strict"; + +var worker = new Worker("WorkerDebugger_childWorker.js"); +self.onmessage = function (event) { + postMessage("child:" + event.data); +}; +debugger; +postMessage("worker"); diff --git a/dom/workers/test/WorkerTest.jsm b/dom/workers/test/WorkerTest.jsm new file mode 100644 index 0000000000..702dc9e46a --- /dev/null +++ b/dom/workers/test/WorkerTest.jsm @@ -0,0 +1,15 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +var EXPORTED_SYMBOLS = ["WorkerTest"]; + +var WorkerTest = { + go(message, messageCallback, errorCallback) { + let worker = new ChromeWorker("WorkerTest_worker.js"); + worker.onmessage = messageCallback; + worker.onerror = errorCallback; + worker.postMessage(message); + return worker; + }, +}; diff --git a/dom/workers/test/WorkerTest_badworker.js b/dom/workers/test/WorkerTest_badworker.js new file mode 100644 index 0000000000..5fbd8a5e63 --- /dev/null +++ b/dom/workers/test/WorkerTest_badworker.js @@ -0,0 +1 @@ +// doesn't matter what is in here if the URL is bad diff --git a/dom/workers/test/WorkerTest_subworker.js b/dom/workers/test/WorkerTest_subworker.js new file mode 100644 index 0000000000..0022fc5046 --- /dev/null +++ b/dom/workers/test/WorkerTest_subworker.js @@ -0,0 +1,37 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +onmessage = function (event) { + let chromeURL = event.data.replace( + "test_chromeWorkerJSM.xhtml", + "WorkerTest_badworker.js" + ); + + let mochitestURL = event.data + .replace("test_chromeWorkerJSM.xhtml", "WorkerTest_badworker.js") + .replace( + "chrome://mochitests/content/chrome", + "http://mochi.test:8888/tests" + ); + + // We should be able to XHR to anything we want, including a chrome URL. + let xhr = new XMLHttpRequest(); + xhr.open("GET", mochitestURL, false); + xhr.send(); + + if (!xhr.responseText) { + throw "Can't load script file via XHR!"; + } + + // We shouldn't be able to make a ChromeWorker to a non-chrome URL. + try { + new ChromeWorker(mochitestURL); + } catch (e) { + if (e.name === "SecurityError") { + postMessage("Done"); + return; + } + } + throw "creating a chrome worker with a bad URL should throw a SecurityError"; +}; diff --git a/dom/workers/test/WorkerTest_worker.js b/dom/workers/test/WorkerTest_worker.js new file mode 100644 index 0000000000..53a380be50 --- /dev/null +++ b/dom/workers/test/WorkerTest_worker.js @@ -0,0 +1,11 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +onmessage = function (event) { + let worker = new ChromeWorker("WorkerTest_subworker.js"); + worker.onmessage = function (e) { + postMessage(e.data); + }; + worker.postMessage(event.data); +}; diff --git a/dom/workers/test/atob_worker.js b/dom/workers/test/atob_worker.js new file mode 100644 index 0000000000..f55ec77e81 --- /dev/null +++ b/dom/workers/test/atob_worker.js @@ -0,0 +1,55 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +var data = [ + -1, + 0, + 1, + 1.5, + /* null ,*/ undefined, + true, + false, + "foo", + "123456789012345", + "1234567890123456", + "12345678901234567", +]; + +var str = ""; +for (var i = 0; i < 30; i++) { + data.push(str); + str += i % 2 ? "b" : "a"; +} + +onmessage = function (event) { + data.forEach(function (string) { + var encoded = btoa(string); + postMessage({ type: "btoa", value: encoded }); + postMessage({ type: "atob", value: atob(encoded) }); + }); + + var threw; + try { + atob(); + } catch (e) { + threw = true; + } + + if (!threw) { + throw "atob didn't throw when called without an argument!"; + } + threw = false; + + try { + btoa(); + } catch (e) { + threw = true; + } + + if (!threw) { + throw "btoa didn't throw when called without an argument!"; + } + + postMessage({ type: "done" }); +}; diff --git a/dom/workers/test/blank.html b/dom/workers/test/blank.html new file mode 100644 index 0000000000..fcbbdb17e9 --- /dev/null +++ b/dom/workers/test/blank.html @@ -0,0 +1,14 @@ + + + + + Blank + + + + + diff --git a/dom/workers/test/browser.ini b/dom/workers/test/browser.ini new file mode 100644 index 0000000000..2436cdcafb --- /dev/null +++ b/dom/workers/test/browser.ini @@ -0,0 +1,36 @@ +[DEFAULT] +support-files = + bug1047663_tab.html + bug1047663_worker.sjs + head.js + !/dom/base/test/file_empty.html + +[browser_bug1047663.js] +[browser_bug1104623.js] +run-if = buildapp == 'browser' +[browser_consoleSharedWorkers.js] +support-files = sharedWorker_console.js empty.html +[browser_fileURL.js] +support-files = empty.html empty_worker.js +[browser_worker_use_counters.js] +support-files = + file_use_counter_worker.html + file_use_counter_worker.js + file_use_counter_shared_worker.js + file_use_counter_shared_worker_microtask.js + file_use_counter_service_worker.js +skip-if = + os == "win" && os_version == "6.1" # Skip on Azure - frequent failure +[browser_privilegedmozilla_remoteworker.js] +support-files = + file_service_worker.js + file_service_worker_container.html +[browser_serviceworker_fetch_new_process.js] +support-files = + file_service_worker_fetch_synthetic.js + server_fetch_synthetic.sjs +[browser_WorkerDebugger.initialize.js] +support-files = + WorkerDebugger.initialize_debugger_es_worker.js + WorkerDebugger.initialize_es_worker.js +skip-if = !nightly_build # to be enabled once ES module in workers is enabled (bug 1812591) diff --git a/dom/workers/test/browser_WorkerDebugger.initialize.js b/dom/workers/test/browser_WorkerDebugger.initialize.js new file mode 100644 index 0000000000..edc20af4e0 --- /dev/null +++ b/dom/workers/test/browser_WorkerDebugger.initialize.js @@ -0,0 +1,54 @@ +/* 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 wdm = Cc["@mozilla.org/dom/workers/workerdebuggermanager;1"].getService( + Ci.nsIWorkerDebuggerManager +); + +const BASE_URL = "chrome://mochitests/content/browser/dom/workers/test/"; +const WORKER_URL = BASE_URL + "WorkerDebugger.initialize_es_worker.js"; +const DEBUGGER_URL = + BASE_URL + "WorkerDebugger.initialize_debugger_es_worker.js"; + +add_task(async function test() { + const onDbg = waitForRegister(WORKER_URL); + const worker = new Worker(WORKER_URL, { type: "module" }); + + info("Wait for worker message"); + await new Promise(resolve => (worker.onmessage = resolve)); + + const dbg = await onDbg; + + info("Calling WorkerDebugger::Initialize"); + const onDebuggerScriptEvaluated = new Promise(resolve => { + const listener = { + onMessage(msg) { + is(msg, "debugger script ran"); + dbg.removeListener(listener); + resolve(); + }, + }; + dbg.addListener(listener); + }); + dbg.initialize(DEBUGGER_URL); + ok(true, "dbg.initialize didn't throw"); + + info("Waiting for debugger script to be evaluated and dispatching a message"); + await onDebuggerScriptEvaluated; +}); + +function waitForRegister(url, dbgUrl) { + return new Promise(function (resolve) { + wdm.addListener({ + onRegister(dbg) { + if (dbg.url !== url) { + return; + } + ok(true, "Debugger with url " + url + " should be registered."); + wdm.removeListener(this); + resolve(dbg); + }, + }); + }); +} diff --git a/dom/workers/test/browser_bug1047663.js b/dom/workers/test/browser_bug1047663.js new file mode 100644 index 0000000000..f55f1152f6 --- /dev/null +++ b/dom/workers/test/browser_bug1047663.js @@ -0,0 +1,56 @@ +/* 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"; + +const EXAMPLE_URL = "https://example.com/browser/dom/workers/test/"; +const TAB_URL = EXAMPLE_URL + "bug1047663_tab.html"; +const WORKER_URL = EXAMPLE_URL + "bug1047663_worker.sjs"; + +function test() { + waitForExplicitFinish(); + + (async function () { + // Disable rcwn to make cache behavior deterministic. + await SpecialPowers.pushPrefEnv({ + set: [["network.http.rcwn.enabled", false]], + }); + + let tab = await addTab(TAB_URL); + + // Create a worker. Post a message to it, and check the reply. Since the + // server side JavaScript file returns the first source for the first + // request, the reply should be "one". If the reply is correct, terminate + // the worker. + await createWorkerInTab(tab, WORKER_URL); + let message = await postMessageToWorkerInTab(tab, WORKER_URL, "ping"); + is(message, "one"); + await terminateWorkerInTab(tab, WORKER_URL); + + // Create a second worker with the same URL. Post a message to it, and check + // the reply. The server side JavaScript file returns the second source for + // all subsequent requests, but since the cache is still enabled, the reply + // should still be "one". If the reply is correct, terminate the worker. + await createWorkerInTab(tab, WORKER_URL); + message = await postMessageToWorkerInTab(tab, WORKER_URL, "ping"); + is(message, "one"); + await terminateWorkerInTab(tab, WORKER_URL); + + // Disable the cache in this tab. This should also disable the cache for all + // workers in this tab. + await disableCacheInTab(tab); + + // Create a third worker with the same URL. Post a message to it, and check + // the reply. Since the server side JavaScript file returns the second + // source for all subsequent requests, and the cache is now disabled, the + // reply should now be "two". If the reply is correct, terminate the worker. + await createWorkerInTab(tab, WORKER_URL); + message = await postMessageToWorkerInTab(tab, WORKER_URL, "ping"); + is(message, "two"); + await terminateWorkerInTab(tab, WORKER_URL); + + removeTab(tab); + + finish(); + })(); +} diff --git a/dom/workers/test/browser_bug1104623.js b/dom/workers/test/browser_bug1104623.js new file mode 100644 index 0000000000..7dc421b873 --- /dev/null +++ b/dom/workers/test/browser_bug1104623.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/. */ + +function whenBrowserLoaded(aBrowser, aCallback) { + aBrowser.addEventListener( + "load", + function onLoad(event) { + if (event.target == aBrowser.contentDocument) { + aBrowser.removeEventListener("load", onLoad, true); + executeSoon(aCallback); + } + }, + true + ); +} + +function test() { + waitForExplicitFinish(); + + let testURL = + "chrome://mochitests/content/chrome/dom/base/test/file_empty.html"; + + let tab = BrowserTestUtils.addTab(gBrowser, testURL); + gBrowser.selectedTab = tab; + + whenBrowserLoaded(tab.linkedBrowser, function () { + let doc = tab.linkedBrowser.contentDocument; + let contentWin = tab.linkedBrowser.contentWindow; + + let blob = new contentWin.Blob([ + "onmessage = function() { postMessage(true); }", + ]); + ok(blob, "Blob has been created"); + + let blobURL = contentWin.URL.createObjectURL(blob); + ok(blobURL, "Blob URL has been created"); + + let worker = new contentWin.Worker(blobURL); + ok(worker, "Worker has been created"); + + worker.onerror = function (error) { + ok(false, "Worker.onerror:" + error.message); + worker.terminate(); + contentWin.URL.revokeObjectURL(blob); + gBrowser.removeTab(tab); + executeSoon(finish); + }; + + worker.onmessage = function () { + ok(true, "Worker.onmessage"); + worker.terminate(); + contentWin.URL.revokeObjectURL(blob); + gBrowser.removeTab(tab); + executeSoon(finish); + }; + + worker.postMessage(true); + }); +} diff --git a/dom/workers/test/browser_consoleSharedWorkers.js b/dom/workers/test/browser_consoleSharedWorkers.js new file mode 100644 index 0000000000..a8f9b79760 --- /dev/null +++ b/dom/workers/test/browser_consoleSharedWorkers.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/. */ + +add_task(async function test() { + const testURL = getRootDirectory(gTestPath) + "empty.html"; + let tab = BrowserTestUtils.addTab(gBrowser, testURL); + gBrowser.selectedTab = tab; + + await BrowserTestUtils.browserLoaded(gBrowser.getBrowserForTab(tab)); + + let promise = new Promise(resolve => { + const ConsoleAPIStorage = SpecialPowers.Cc[ + "@mozilla.org/consoleAPI-storage;1" + ].getService(SpecialPowers.Ci.nsIConsoleAPIStorage); + + function consoleListener() { + this.onConsoleLogEvent = this.onConsoleLogEvent.bind(this); + ConsoleAPIStorage.addLogEventListener( + this.onConsoleLogEvent, + SpecialPowers.wrap(document).nodePrincipal + ); + Services.obs.addObserver(this, "console-api-profiler"); + } + + var order = 0; + consoleListener.prototype = { + onConsoleLogEvent(aSubject) { + var obj = aSubject.wrappedJSObject; + is( + obj.arguments[0], + "Hello world from a SharedWorker!", + "A message from a SharedWorker \\o/" + ); + is(obj.ID, "sharedWorker_console.js", "The ID is SharedWorker"); + is(obj.innerID, "SharedWorker", "The ID is SharedWorker"); + is(order++, 1, "Then a log message."); + + ConsoleAPIStorage.removeLogEventListener(this.onConsoleLogEvent); + resolve(); + }, + + observe: (aSubject, aTopic) => { + ok(true, "Something has been received"); + + if (aTopic == "console-api-profiler") { + var obj = aSubject.wrappedJSObject; + is( + obj.arguments[0], + "Hello profiling from a SharedWorker!", + "A message from a SharedWorker \\o/" + ); + is(order++, 0, "First a profiler message."); + + Services.obs.removeObserver(cl, "console-api-profiler"); + } + }, + }; + + var cl = new consoleListener(); + }); + + await SpecialPowers.spawn(tab.linkedBrowser, [], async function () { + new content.SharedWorker("sharedWorker_console.js"); + }); + + await promise; + + await BrowserTestUtils.removeTab(tab); +}); diff --git a/dom/workers/test/browser_fileURL.js b/dom/workers/test/browser_fileURL.js new file mode 100644 index 0000000000..9891bdcba9 --- /dev/null +++ b/dom/workers/test/browser_fileURL.js @@ -0,0 +1,73 @@ +/* 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"; + +const EMPTY_URL = "/browser/dom/workers/test/empty.html"; +const WORKER_URL = "/browser/dom/workers/test/empty_worker.js"; + +add_task(async function () { + let tab = BrowserTestUtils.addTab( + gBrowser, + "http://mochi.test:8888" + EMPTY_URL + ); + gBrowser.selectedTab = tab; + + let browser = gBrowser.getBrowserForTab(tab); + await BrowserTestUtils.browserLoaded(browser); + + await SpecialPowers.spawn( + browser, + ["http://example.org" + WORKER_URL], + function (spec) { + return new content.Promise((resolve, reject) => { + let w = new content.window.Worker(spec); + w.onerror = _ => { + resolve(); + }; + w.onmessage = _ => { + reject(); + }; + }); + } + ); + ok( + true, + "The worker is not loaded when the script is from different origin." + ); + + BrowserTestUtils.removeTab(tab); +}); + +add_task(async function () { + let tab = BrowserTestUtils.addTab( + gBrowser, + "https://example.org" + EMPTY_URL + ); + gBrowser.selectedTab = tab; + + let browser = gBrowser.getBrowserForTab(tab); + await BrowserTestUtils.browserLoaded(browser); + + await SpecialPowers.spawn( + browser, + ["http://example.org" + WORKER_URL], + function (spec) { + return new content.Promise((resolve, reject) => { + let w = new content.window.Worker(spec); + w.onerror = _ => { + resolve(); + }; + w.onmessage = _ => { + reject(); + }; + }); + } + ); + ok( + true, + "The worker is not loaded when the script is from different origin." + ); + + BrowserTestUtils.removeTab(tab); +}); diff --git a/dom/workers/test/browser_privilegedmozilla_remoteworker.js b/dom/workers/test/browser_privilegedmozilla_remoteworker.js new file mode 100644 index 0000000000..573cae5e61 --- /dev/null +++ b/dom/workers/test/browser_privilegedmozilla_remoteworker.js @@ -0,0 +1,116 @@ +add_setup(async function () { + await SpecialPowers.pushPrefEnv({ + set: [ + ["browser.tabs.remote.separatePrivilegedMozillaWebContentProcess", true], + ["browser.tabs.remote.separatedMozillaDomains", "example.org"], + ["dom.ipc.processCount.web", 1], + ["dom.ipc.processCount.privilegedmozilla", 1], + ["dom.serviceWorkers.enabled", true], + ["dom.serviceWorkers.testing.enabled", true], + ], + }); +}); + +// This test attempts to verify proper placement of spawned remoteworkers +// by spawning them and then verifying that they were spawned in the expected +// process by way of nsIWorkerDebuggerManager enumeration. +// +// Unfortunately, there's no other way to introspect where a worker global was +// spawned at this time. (devtools just ends up enumerating all workers in all +// processes and we don't want to depend on devtools in this test). +// +// As a result, this test currently only tests situations where it's known that +// a remote worker will be spawned in the same process that is initiating its +// spawning. +// +// This should be enhanced in the future. +add_task(async function test_serviceworker() { + const basePath = "browser/dom/workers/test"; + const pagePath = `${basePath}/file_service_worker_container.html`; + const scriptPath = `${basePath}/file_service_worker.js`; + + Services.ppmm.releaseCachedProcesses(); + + async function runWorkerInProcess() { + function getActiveWorkerURLs() { + const wdm = Cc[ + "@mozilla.org/dom/workers/workerdebuggermanager;1" + ].getService(Ci.nsIWorkerDebuggerManager); + + const workerDebuggerUrls = Array.from( + wdm.getWorkerDebuggerEnumerator() + ).map(wd => { + return wd.url; + }); + + return workerDebuggerUrls; + } + + return new Promise(resolve => { + content.navigator.serviceWorker.ready.then(({ active }) => { + const { port1, port2 } = new content.MessageChannel(); + active.postMessage("webpage->serviceworker", [port2]); + port1.onmessage = evt => { + resolve({ + msg: evt.data, + workerUrls: getActiveWorkerURLs(), + }); + }; + }); + }).then(async res => { + // Unregister the service worker used in this test. + const registration = await content.navigator.serviceWorker.ready; + await registration.unregister(); + return res; + }); + } + + const testCaseList = [ + // TODO: find a reasonable way to test the non-privileged scenario + // (because more than 1 process is usually available and the worker + // can be launched in a different one from the one where the tab + // is running). + /*{ + remoteType: "web", + hostname: "example.com", + },*/ + { + remoteType: "privilegedmozilla", + hostname: `example.org`, + }, + ]; + + for (const testCase of testCaseList) { + const { remoteType, hostname } = testCase; + + info(`Test remote serviceworkers launch selects a ${remoteType} process`); + + const tab = await BrowserTestUtils.openNewForegroundTab({ + gBrowser, + url: `https://${hostname}/${pagePath}`, + }); + + is( + tab.linkedBrowser.remoteType, + remoteType, + `Got the expected remoteType for ${hostname} tab` + ); + + const results = await SpecialPowers.spawn( + tab.linkedBrowser, + [], + runWorkerInProcess + ); + + Assert.deepEqual( + results, + { + msg: "serviceworker-reply", + workerUrls: [`https://${hostname}/${scriptPath}`], + }, + `Got the expected results for ${hostname} tab` + ); + + BrowserTestUtils.removeTab(tab); + } +}); diff --git a/dom/workers/test/browser_serviceworker_fetch_new_process.js b/dom/workers/test/browser_serviceworker_fetch_new_process.js new file mode 100644 index 0000000000..801c505387 --- /dev/null +++ b/dom/workers/test/browser_serviceworker_fetch_new_process.js @@ -0,0 +1,405 @@ +const DIRPATH = getRootDirectory(gTestPath).replace( + "chrome://mochitests/content/", + "" +); + +/** + * We choose blob contents that will roundtrip cleanly through the `textContent` + * of our returned HTML page. + */ +const TEST_BLOB_CONTENTS = `I'm a disk-backed test blob! Hooray!`; + +add_setup(async function () { + await SpecialPowers.pushPrefEnv({ + set: [ + // Set preferences so that opening a page with the origin "example.org" + // will result in a remoteType of "privilegedmozilla" for both the + // page and the ServiceWorker. + ["browser.tabs.remote.separatePrivilegedMozillaWebContentProcess", true], + ["browser.tabs.remote.separatedMozillaDomains", "example.org"], + ["dom.ipc.processCount.privilegedmozilla", 1], + ["dom.ipc.processPrelaunch.enabled", false], + ["dom.serviceWorkers.enabled", true], + ["dom.serviceWorkers.testing.enabled", true], + // ServiceWorker worker instances should stay alive until explicitly + // caused to terminate by dropping these timeouts to 0 in + // `waitForWorkerAndProcessShutdown`. + ["dom.serviceWorkers.idle_timeout", 299999], + ["dom.serviceWorkers.idle_extended_timeout", 299999], + ], + }); +}); + +function countRemoteType(remoteType) { + return ChromeUtils.getAllDOMProcesses().filter( + p => p.remoteType == remoteType + ).length; +} + +/** + * Helper function to get a list of all current processes and their remote + * types. Note that when in used in a templated literal that it is + * synchronously invoked when the string is evaluated and captures system state + * at that instant. + */ +function debugRemotes() { + return ChromeUtils.getAllDOMProcesses() + .map(p => p.remoteType || "parent") + .join(","); +} + +/** + * Wait for there to be zero processes of the given remoteType. This check is + * considered successful if there are already no processes of the given type + * at this very moment. + */ +async function waitForNoProcessesOfType(remoteType) { + info(`waiting for there to be no ${remoteType} procs`); + await TestUtils.waitForCondition( + () => countRemoteType(remoteType) == 0, + "wait for the worker's process to shutdown" + ); +} + +/** + * Given a ServiceWorkerRegistrationInfo with an active ServiceWorker that + * has no active ExtendableEvents but would otherwise continue running thanks + * to the idle keepalive: + * - Assert that there is a ServiceWorker instance in the given registration's + * active slot. (General invariant check.) + * - Assert that a single process with the given remoteType currently exists. + * (This doesn't mean the SW is alive in that process, though this test + * verifies that via other checks when appropriate.) + * - Induce the worker to shutdown by temporarily dropping the idle timeout to 0 + * and causing the idle timer to be reset due to rapid debugger attach/detach. + * - Wait for the the single process with the given remoteType to go away. + * - Reset the idle timeouts back to their previous high values. + */ +async function waitForWorkerAndProcessShutdown(swRegInfo, remoteType) { + info(`terminating worker and waiting for ${remoteType} procs to shut down`); + ok(swRegInfo.activeWorker, "worker should be in the active slot"); + is( + countRemoteType(remoteType), + 1, + `should have a single ${remoteType} process but have: ${debugRemotes()}` + ); + + // Let's not wait too long for the process to shutdown. + await SpecialPowers.pushPrefEnv({ + set: [ + ["dom.serviceWorkers.idle_timeout", 0], + ["dom.serviceWorkers.idle_extended_timeout", 0], + ], + }); + + // We need to cause the worker to re-evaluate its idle timeout. The easiest + // way to do this I could think of is to attach and then detach the debugger + // from the active worker. + swRegInfo.activeWorker.attachDebugger(); + await new Promise(resolve => Cu.dispatch(resolve)); + swRegInfo.activeWorker.detachDebugger(); + + // Eventually the length will reach 0, meaning we're done! + await waitForNoProcessesOfType(remoteType); + + is( + countRemoteType(remoteType), + 0, + `processes with remoteType=${remoteType} type should have shut down` + ); + + // Make sure we never kill workers on idle except when this is called. + await SpecialPowers.popPrefEnv(); +} + +async function do_test_sw(host, remoteType, swMode, fileBlob) { + info( + `### entering test: host=${host}, remoteType=${remoteType}, mode=${swMode}` + ); + + const prin = Services.scriptSecurityManager.createContentPrincipal( + Services.io.newURI(`https://${host}`), + {} + ); + const sw = `https://${host}/${DIRPATH}file_service_worker_fetch_synthetic.js`; + const scope = `https://${host}/${DIRPATH}server_fetch_synthetic.sjs`; + + const swm = Cc["@mozilla.org/serviceworkers/manager;1"].getService( + Ci.nsIServiceWorkerManager + ); + const swRegInfo = await swm.registerForTest(prin, scope, sw); + swRegInfo.QueryInterface(Ci.nsIServiceWorkerRegistrationInfo); + + info( + `service worker registered: ${JSON.stringify({ + principal: swRegInfo.principal.spec, + scope: swRegInfo.scope, + })}` + ); + + // Wait for the worker to install & shut down. + await TestUtils.waitForCondition( + () => swRegInfo.activeWorker, + "wait for the worker to become active" + ); + await waitForWorkerAndProcessShutdown(swRegInfo, remoteType); + + info( + `test navigation interception with mode=${swMode} starting from about:blank` + ); + await BrowserTestUtils.withNewTab( + { + gBrowser, + url: "about:blank", + }, + async browser => { + // NOTE: We intentionally trigger the navigation from content in order to + // make sure frontend doesn't eagerly process-switch for us. + SpecialPowers.spawn( + browser, + [scope, swMode, fileBlob], + // eslint-disable-next-line no-shadow + async (scope, swMode, fileBlob) => { + const pageUrl = `${scope}?mode=${swMode}`; + if (!fileBlob) { + content.location.href = pageUrl; + } else { + const doc = content.document; + const formElem = doc.createElement("form"); + doc.body.appendChild(formElem); + + formElem.action = pageUrl; + formElem.method = "POST"; + formElem.enctype = "multipart/form-data"; + + const fileElem = doc.createElement("input"); + formElem.appendChild(fileElem); + + fileElem.type = "file"; + fileElem.name = "foo"; + + fileElem.mozSetFileArray([fileBlob]); + + formElem.submit(); + } + } + ); + + await BrowserTestUtils.browserLoaded(browser); + + is( + countRemoteType(remoteType), + 1, + `should have spawned a content process with remoteType=${remoteType}` + ); + + const { source, blobContents } = await SpecialPowers.spawn( + browser, + [], + () => { + return { + source: content.document.getElementById("source").textContent, + blobContents: content.document.getElementById("blob").textContent, + }; + } + ); + + is( + source, + swMode === "synthetic" ? "ServiceWorker" : "ServerJS", + "The page contents should come from the right place." + ); + + is( + blobContents, + fileBlob ? TEST_BLOB_CONTENTS : "", + "The request blob contents should be the blob/empty as appropriate." + ); + + // Ensure the worker was loaded in this process. + const workerDebuggerURLs = await SpecialPowers.spawn( + browser, + [sw], + async url => { + if (!content.navigator.serviceWorker.controller) { + throw new Error("document not controlled!"); + } + const wdm = Cc[ + "@mozilla.org/dom/workers/workerdebuggermanager;1" + ].getService(Ci.nsIWorkerDebuggerManager); + + return Array.from(wdm.getWorkerDebuggerEnumerator()) + .map(wd => { + return wd.url; + }) + .filter(swURL => swURL == url); + } + ); + if (remoteType.startsWith("webServiceWorker=")) { + Assert.notDeepEqual( + workerDebuggerURLs, + [sw], + "Isolated workers should not be running in the content child process" + ); + } else { + Assert.deepEqual( + workerDebuggerURLs, + [sw], + "The worker should be running in the correct child process" + ); + } + + // Unregister the ServiceWorker. The registration will continue to control + // `browser` and therefore continue to exist and its worker to continue + // running until the tab is closed. + await SpecialPowers.spawn(browser, [], async () => { + let registration = await content.navigator.serviceWorker.ready; + await registration.unregister(); + }); + } + ); + + // Now that the controlled tab is closed and the registration has been + // removed, the ServiceWorker will be made redundant which will forcibly + // terminate it, which will result in the shutdown of the given content + // process. Wait for that to happen both as a verification and so the next + // test has a sufficiently clean slate. + await waitForNoProcessesOfType(remoteType); +} + +/** + * Create a File-backed blob. This will happen synchronously from the main + * thread, which isn't optimal, but the test blocks on this progress anyways. + * Bug 1669578 has been filed on improving this idiom and avoiding the sync + * writes. + */ +async function makeFileBlob(blobContents) { + const tmpFile = Cc["@mozilla.org/file/directory_service;1"] + .getService(Ci.nsIDirectoryService) + .QueryInterface(Ci.nsIProperties) + .get("TmpD", Ci.nsIFile); + tmpFile.append("test-file-backed-blob.txt"); + tmpFile.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0o600); + + var outStream = Cc[ + "@mozilla.org/network/file-output-stream;1" + ].createInstance(Ci.nsIFileOutputStream); + outStream.init( + tmpFile, + 0x02 | 0x08 | 0x20, // write, create, truncate + 0o666, + 0 + ); + outStream.write(blobContents, blobContents.length); + outStream.close(); + + const fileBlob = await File.createFromNsIFile(tmpFile); + return fileBlob; +} + +function getSWTelemetrySums() { + let telemetry = Cc["@mozilla.org/base/telemetry;1"].getService( + Ci.nsITelemetry + ); + let keyedhistograms = telemetry.getSnapshotForKeyedHistograms( + "main", + false + ).parent; + let keyedscalars = telemetry.getSnapshotForKeyedScalars("main", false).parent; + // We're not looking at the distribution of the histograms, just that they changed + return { + SERVICE_WORKER_RUNNING_All: keyedhistograms.SERVICE_WORKER_RUNNING + ? keyedhistograms.SERVICE_WORKER_RUNNING.All.sum + : 0, + SERVICE_WORKER_RUNNING_Fetch: keyedhistograms.SERVICE_WORKER_RUNNING + ? keyedhistograms.SERVICE_WORKER_RUNNING.Fetch.sum + : 0, + SERVICEWORKER_RUNNING_MAX_All: keyedscalars["serviceworker.running_max"] + ? keyedscalars["serviceworker.running_max"].All + : 0, + SERVICEWORKER_RUNNING_MAX_Fetch: keyedscalars["serviceworker.running_max"] + ? keyedscalars["serviceworker.running_max"].Fetch + : 0, + }; +} + +add_task(async function test() { + // Can't test telemetry without this since we may not be on the nightly channel + let oldCanRecord = Services.telemetry.canRecordExtended; + Services.telemetry.canRecordExtended = true; + registerCleanupFunction(() => { + Services.telemetry.canRecordExtended = oldCanRecord; + }); + + let initialSums = getSWTelemetrySums(); + + // ## Isolated Privileged Process + // Trigger a straightforward intercepted navigation with no request body that + // returns a synthetic response. + await do_test_sw("example.org", "privilegedmozilla", "synthetic", null); + + // Trigger an intercepted navigation with FormData containing an + // which will result in the request body containing a + // RemoteLazyInputStream which will be consumed in the content process by the + // ServiceWorker while generating the synthetic response. + const fileBlob = await makeFileBlob(TEST_BLOB_CONTENTS); + await do_test_sw("example.org", "privilegedmozilla", "synthetic", fileBlob); + + // Trigger an intercepted navigation with FormData containing an + // which will result in the request body containing a + // RemoteLazyInputStream which will be relayed back to the parent process + // via direct invocation of fetch() on the event.request but without any + // cloning. + await do_test_sw("example.org", "privilegedmozilla", "fetch", fileBlob); + + // Same as the above but cloning the request before fetching it. + await do_test_sw("example.org", "privilegedmozilla", "clone", fileBlob); + + // ## Fission Isolation + if (Services.appinfo.fissionAutostart) { + // ## ServiceWorker isolation + const isolateUrl = "example.com"; + const isolateRemoteType = `webServiceWorker=https://` + isolateUrl; + await do_test_sw(isolateUrl, isolateRemoteType, "synthetic", null); + await do_test_sw(isolateUrl, isolateRemoteType, "synthetic", fileBlob); + } + let telemetrySums = getSWTelemetrySums(); + info(JSON.stringify(telemetrySums)); + info( + "Initial Running All: " + + initialSums.SERVICE_WORKER_RUNNING_All + + ", Fetch: " + + initialSums.SERVICE_WORKER_RUNNING_Fetch + ); + info( + "Initial Max Running All: " + + initialSums.SERVICEWORKER_RUNNING_MAX_All + + ", Fetch: " + + initialSums.SERVICEWORKER_RUNNING_MAX_Fetch + ); + info( + "Running All: " + + telemetrySums.SERVICE_WORKER_RUNNING_All + + ", Fetch: " + + telemetrySums.SERVICE_WORKER_RUNNING_Fetch + ); + info( + "Max Running All: " + + telemetrySums.SERVICEWORKER_RUNNING_MAX_All + + ", Fetch: " + + telemetrySums.SERVICEWORKER_RUNNING_MAX_Fetch + ); + ok( + telemetrySums.SERVICE_WORKER_RUNNING_All > + initialSums.SERVICE_WORKER_RUNNING_All, + "ServiceWorker running count changed" + ); + ok( + telemetrySums.SERVICE_WORKER_RUNNING_Fetch > + initialSums.SERVICE_WORKER_RUNNING_Fetch, + "ServiceWorker running count changed" + ); + // We don't use ok()'s for MAX because MAX may have been set before we + // set canRecordExtended, and if so we won't record a new value unless + // the max increases again. +}); diff --git a/dom/workers/test/browser_worker_use_counters.js b/dom/workers/test/browser_worker_use_counters.js new file mode 100644 index 0000000000..6f115916d8 --- /dev/null +++ b/dom/workers/test/browser_worker_use_counters.js @@ -0,0 +1,176 @@ +/* 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"; + +const gHttpTestRoot = "https://example.com/browser/dom/workers/test/"; + +function grabHistogramsFromContent( + use_counter_name, + worker_type, + counter_before = null +) { + let telemetry = Cc["@mozilla.org/base/telemetry;1"].getService( + Ci.nsITelemetry + ); + let gather = () => { + let snapshots; + if (Services.appinfo.browserTabsRemoteAutostart) { + snapshots = telemetry.getSnapshotForHistograms("main", false).content; + } else { + snapshots = telemetry.getSnapshotForHistograms("main", false).parent; + } + let checkedGet = probe => { + return snapshots[probe] ? snapshots[probe].sum : 0; + }; + return [ + checkedGet(`USE_COUNTER2_${use_counter_name}_${worker_type}_WORKER`), + checkedGet(`${worker_type}_WORKER_DESTROYED`), + ]; + }; + return BrowserTestUtils.waitForCondition(() => { + return counter_before != gather()[0]; + }).then(gather, gather); +} + +var check_use_counter_worker = async function ( + use_counter_name, + worker_type, + content_task +) { + info(`checking ${use_counter_name} use counters for ${worker_type} worker`); + + let newTab = BrowserTestUtils.addTab(gBrowser, "about:blank"); + gBrowser.selectedTab = newTab; + newTab.linkedBrowser.stop(); + + // Hold on to the current values of the telemetry histograms we're + // interested in. + let [histogram_before, destructions_before] = await grabHistogramsFromContent( + use_counter_name, + worker_type + ); + + BrowserTestUtils.loadURIString( + gBrowser.selectedBrowser, + gHttpTestRoot + "file_use_counter_worker.html" + ); + await BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser); + await content_task(gBrowser.selectedBrowser); + + // Tear down the page. + let tabClosed = BrowserTestUtils.waitForTabClosing(newTab); + gBrowser.removeTab(newTab); + await tabClosed; + + // Grab histograms again and compare. + let [histogram_after, destructions_after] = await grabHistogramsFromContent( + use_counter_name, + worker_type, + histogram_before + ); + + is( + histogram_after, + histogram_before + 1, + `histogram ${use_counter_name} counts for ${worker_type} worker are correct` + ); + // There might be other workers created by prior tests get destroyed during + // this tests. + ok( + destructions_after > destructions_before, + `${worker_type} worker counts are correct` + ); +}; + +add_task(async function test_dedicated_worker() { + await check_use_counter_worker("CONSOLE_LOG", "DEDICATED", async browser => { + await ContentTask.spawn(browser, {}, function () { + return new Promise(resolve => { + let worker = new content.Worker("file_use_counter_worker.js"); + worker.onmessage = function (e) { + if (e.data === "DONE") { + worker.terminate(); + resolve(); + } + }; + }); + }); + }); +}); + +add_task(async function test_shared_worker() { + await check_use_counter_worker("CONSOLE_LOG", "SHARED", async browser => { + await ContentTask.spawn(browser, {}, function () { + return new Promise(resolve => { + let worker = new content.SharedWorker( + "file_use_counter_shared_worker.js" + ); + worker.port.onmessage = function (e) { + if (e.data === "DONE") { + resolve(); + } + }; + worker.port.postMessage("RUN"); + }); + }); + }); +}); + +add_task(async function test_shared_worker_microtask() { + await check_use_counter_worker("CONSOLE_LOG", "SHARED", async browser => { + await ContentTask.spawn(browser, {}, function () { + return new Promise(resolve => { + let worker = new content.SharedWorker( + "file_use_counter_shared_worker_microtask.js" + ); + worker.port.onmessage = function (e) { + if (e.data === "DONE") { + resolve(); + } + }; + worker.port.postMessage("RUN"); + }); + }); + }); +}); + +add_task(async function test_service_worker() { + await check_use_counter_worker("CONSOLE_LOG", "SERVICE", async browser => { + await ContentTask.spawn(browser, {}, function () { + let waitForActivated = async function (registration) { + return new Promise(resolve => { + let worker = + registration.installing || + registration.waiting || + registration.active; + if (worker.state === "activated") { + resolve(worker); + return; + } + + worker.addEventListener("statechange", function onStateChange() { + if (worker.state === "activated") { + worker.removeEventListener("statechange", onStateChange); + resolve(worker); + } + }); + }); + }; + + return new Promise(resolve => { + content.navigator.serviceWorker + .register("file_use_counter_service_worker.js") + .then(async registration => { + content.navigator.serviceWorker.onmessage = function (e) { + if (e.data === "DONE") { + registration.unregister().then(resolve); + } + }; + let worker = await waitForActivated(registration); + worker.postMessage("RUN"); + }); + }); + }); + }); +}); diff --git a/dom/workers/test/bug1014466_data1.txt b/dom/workers/test/bug1014466_data1.txt new file mode 100644 index 0000000000..a32a4347a4 --- /dev/null +++ b/dom/workers/test/bug1014466_data1.txt @@ -0,0 +1 @@ +1234567890 diff --git a/dom/workers/test/bug1014466_data2.txt b/dom/workers/test/bug1014466_data2.txt new file mode 100644 index 0000000000..4d40154cef --- /dev/null +++ b/dom/workers/test/bug1014466_data2.txt @@ -0,0 +1 @@ +ABCDEFGH diff --git a/dom/workers/test/bug1014466_worker.js b/dom/workers/test/bug1014466_worker.js new file mode 100644 index 0000000000..2161954d2b --- /dev/null +++ b/dom/workers/test/bug1014466_worker.js @@ -0,0 +1,63 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +function ok(a, msg) { + postMessage({ type: "status", status: !!a, msg }); +} + +onmessage = function (event) { + function getResponse(url) { + var xhr = new XMLHttpRequest(); + xhr.open("GET", url, false); + xhr.send(); + return xhr.responseText; + } + + const testFile1 = "bug1014466_data1.txt"; + const testFile2 = "bug1014466_data2.txt"; + const testData1 = getResponse(testFile1); + const testData2 = getResponse(testFile2); + + var response_count = 0; + var xhr = new XMLHttpRequest(); + xhr.onreadystatechange = function () { + if (xhr.readyState == xhr.DONE && xhr.status == 200) { + response_count++; + switch (response_count) { + case 1: + ok(xhr.responseText == testData1, "Check data 1"); + test_data2(); + break; + case 2: + ok(xhr.responseText == testData2, "Check data 2"); + postMessage({ type: "finish" }); + break; + default: + ok(false, "Unexpected response received"); + postMessage({ type: "finish" }); + break; + } + } + }; + xhr.onerror = function (e) { + ok(false, "Got an error event: " + e); + postMessage({ type: "finish" }); + }; + + function test_data1() { + xhr.open("GET", testFile1, true); + xhr.responseType = "text"; + xhr.send(); + } + + function test_data2() { + xhr.abort(); + xhr.open("GET", testFile2, true); + xhr.responseType = "text"; + xhr.send(); + } + + test_data1(); +}; diff --git a/dom/workers/test/bug1020226_frame.html b/dom/workers/test/bug1020226_frame.html new file mode 100644 index 0000000000..039dc9480a --- /dev/null +++ b/dom/workers/test/bug1020226_frame.html @@ -0,0 +1,19 @@ + + + + + + Test for Bug 1020226 + + + + + + diff --git a/dom/workers/test/bug1020226_worker.js b/dom/workers/test/bug1020226_worker.js new file mode 100644 index 0000000000..c09abdd7de --- /dev/null +++ b/dom/workers/test/bug1020226_worker.js @@ -0,0 +1,12 @@ +var p = new Promise(function (resolve, reject) { + // This causes a runnable to be queued. + reject(new Error()); + postMessage("loaded"); + + // This prevents that runnable from running until the window calls terminate(), + // at which point the worker goes into the Canceling state and then an + // HoldWorker() is attempted, which fails, which used to result in + // multiple calls to the error reporter, one after the worker's context had + // been GCed. + while (true) {} +}); diff --git a/dom/workers/test/bug1047663_tab.html b/dom/workers/test/bug1047663_tab.html new file mode 100644 index 0000000000..62ab9be7d2 --- /dev/null +++ b/dom/workers/test/bug1047663_tab.html @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/dom/workers/test/bug1047663_worker.sjs b/dom/workers/test/bug1047663_worker.sjs new file mode 100644 index 0000000000..169ca05f89 --- /dev/null +++ b/dom/workers/test/bug1047663_worker.sjs @@ -0,0 +1,41 @@ +/* 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"; + +const WORKER_1 = ` + "use strict"; + + self.onmessage = function () { + postMessage("one"); + }; +`; + +const WORKER_2 = ` + "use strict"; + + self.onmessage = function () { + postMessage("two"); + }; +`; + +function handleRequest(request, response) { + let count = getState("count"); + if (count === "") { + count = "1"; + } + + // This header is necessary for the cache to trigger. + response.setHeader("Cache-control", "max-age=3600"); + response.setHeader("Content-Type", "text/javascript", false); + + // If this is the first request, return the first source. + if (count === "1") { + response.write(WORKER_1); + setState("count", "2"); + } + // For all subsequent requests, return the second source. + else { + response.write(WORKER_2); + } +} diff --git a/dom/workers/test/bug1060621_worker.js b/dom/workers/test/bug1060621_worker.js new file mode 100644 index 0000000000..4137e3b06f --- /dev/null +++ b/dom/workers/test/bug1060621_worker.js @@ -0,0 +1,2 @@ +navigator.foobar = 42; +postMessage("done"); diff --git a/dom/workers/test/bug1062920_worker.js b/dom/workers/test/bug1062920_worker.js new file mode 100644 index 0000000000..238bf949ec --- /dev/null +++ b/dom/workers/test/bug1062920_worker.js @@ -0,0 +1,8 @@ +postMessage({ + appCodeName: navigator.appCodeName, + appName: navigator.appName, + appVersion: navigator.appVersion, + platform: navigator.platform, + userAgent: navigator.userAgent, + product: navigator.product, +}); diff --git a/dom/workers/test/bug1063538.sjs b/dom/workers/test/bug1063538.sjs new file mode 100644 index 0000000000..7e4ba33998 --- /dev/null +++ b/dom/workers/test/bug1063538.sjs @@ -0,0 +1,11 @@ +const { setTimeout } = ChromeUtils.importESModule( + "resource://gre/modules/Timer.sys.mjs" +); + +function handleRequest(request, response) { + response.processAsync(); + response.write("Hello"); + setTimeout(function () { + response.finish(); + }, 100000); // wait 100 seconds. +} diff --git a/dom/workers/test/bug1063538_worker.js b/dom/workers/test/bug1063538_worker.js new file mode 100644 index 0000000000..33e60f0a16 --- /dev/null +++ b/dom/workers/test/bug1063538_worker.js @@ -0,0 +1,25 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var gURL = "http://example.org/tests/dom/workers/test/bug1063538.sjs"; +var xhr = new XMLHttpRequest({ mozAnon: true, mozSystem: true }); +var progressFired = false; + +xhr.onloadend = function (e) { + postMessage({ type: "finish", progressFired }); + self.close(); +}; + +xhr.onprogress = function (e) { + if (e.loaded > 0) { + progressFired = true; + xhr.abort(); + } +}; + +onmessage = function (e) { + xhr.open("GET", gURL, true); + xhr.send(); +}; diff --git a/dom/workers/test/bug1104064_worker.js b/dom/workers/test/bug1104064_worker.js new file mode 100644 index 0000000000..02b802a826 --- /dev/null +++ b/dom/workers/test/bug1104064_worker.js @@ -0,0 +1,10 @@ +onmessage = function () { + var counter = 0; + var id = setInterval(function () { + ++counter; + if (counter == 2) { + clearInterval(id); + postMessage("done"); + } + }, 0); +}; diff --git a/dom/workers/test/bug1132395_sharedWorker.js b/dom/workers/test/bug1132395_sharedWorker.js new file mode 100644 index 0000000000..851c7f7f6c --- /dev/null +++ b/dom/workers/test/bug1132395_sharedWorker.js @@ -0,0 +1,12 @@ +dump("SW created\n"); +onconnect = function (evt) { + dump("SW onconnect\n"); + evt.ports[0].onmessage = function (e) { + dump("SW onmessage\n"); + var blob = new Blob(["123"], { type: "text/plain" }); + dump("SW blob created\n"); + var url = URL.createObjectURL(blob); + dump("SW url created: " + url + "\n"); + evt.ports[0].postMessage("alive \\o/"); + }; +}; diff --git a/dom/workers/test/bug1132924_worker.js b/dom/workers/test/bug1132924_worker.js new file mode 100644 index 0000000000..db67ba323c --- /dev/null +++ b/dom/workers/test/bug1132924_worker.js @@ -0,0 +1,10 @@ +onmessage = function () { + var a = new XMLHttpRequest(); + a.open("GET", "empty.html", false); + a.onreadystatechange = function () { + if (a.readyState == 4) { + postMessage(a.response); + } + }; + a.send(null); +}; diff --git a/dom/workers/test/bug978260_worker.js b/dom/workers/test/bug978260_worker.js new file mode 100644 index 0000000000..126b9c901a --- /dev/null +++ b/dom/workers/test/bug978260_worker.js @@ -0,0 +1,7 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +// Tell the main thread we're here. +postMessage("loaded"); diff --git a/dom/workers/test/bug998474_worker.js b/dom/workers/test/bug998474_worker.js new file mode 100644 index 0000000000..c9c85db83b --- /dev/null +++ b/dom/workers/test/bug998474_worker.js @@ -0,0 +1,6 @@ +self.addEventListener("connect", function (e) { + var port = e.ports[0]; + port.onmessage = function (msg) { + port.postMessage(eval(msg.data)); + }; +}); diff --git a/dom/workers/test/chrome.ini b/dom/workers/test/chrome.ini new file mode 100644 index 0000000000..1b8658063f --- /dev/null +++ b/dom/workers/test/chrome.ini @@ -0,0 +1,96 @@ +[DEFAULT] +skip-if = os == 'android' +support-files = + WorkerDebugger.console_childWorker.js + WorkerDebugger.console_debugger.js + WorkerDebugger.console_worker.js + WorkerDebugger.initialize_childWorker.js + WorkerDebugger.initialize_debugger.js + WorkerDebugger.initialize_worker.js + WorkerDebugger.postMessage_childWorker.js + WorkerDebugger.postMessage_debugger.js + WorkerDebugger.postMessage_worker.js + WorkerDebuggerGlobalScope.createSandbox_debugger.js + WorkerDebuggerGlobalScope.createSandbox_sandbox.js + WorkerDebuggerGlobalScope.createSandbox_worker.js + WorkerDebuggerGlobalScope.enterEventLoop_childWorker.js + WorkerDebuggerGlobalScope.enterEventLoop_debugger.js + WorkerDebuggerGlobalScope.enterEventLoop_worker.js + WorkerDebuggerGlobalScope.reportError_childWorker.js + WorkerDebuggerGlobalScope.reportError_debugger.js + WorkerDebuggerGlobalScope.reportError_worker.js + WorkerDebuggerGlobalScope.setImmediate_debugger.js + WorkerDebuggerGlobalScope.setImmediate_worker.js + WorkerDebuggerManager_childWorker.js + WorkerDebuggerManager_worker.js + WorkerDebugger_childWorker.js + WorkerDebugger_frozen_window1.html + WorkerDebugger_frozen_window2.html + WorkerDebugger_frozen_worker1.js + WorkerDebugger_frozen_worker2.js + WorkerDebugger_promise_debugger.js + WorkerDebugger_promise_worker.js + WorkerDebugger_sharedWorker.js + WorkerDebugger_suspended_debugger.js + WorkerDebugger_suspended_worker.js + WorkerDebugger_worker.js + WorkerTest.jsm + WorkerTest_subworker.js + WorkerTest_worker.js + bug1062920_worker.js + chromeWorker_subworker.js + chromeWorker_worker_submod.sys.mjs + chromeWorker_worker.js + chromeWorker_worker.sys.mjs + dom_worker_helper.js + empty.html + fileBlobSubWorker_worker.js + fileBlob_worker.js + filePosting_worker.js + fileReadSlice_worker.js + fileReaderSyncErrors_worker.js + fileReaderSync_worker.js + fileSlice_worker.js + fileSubWorker_worker.js + file_worker.js + sharedWorker_privateBrowsing.js + sourcemap_header.js + sourcemap_header_debugger.js + +[test_WorkerDebugger.initialize.xhtml] +[test_WorkerDebugger.postMessage.xhtml] +[test_WorkerDebugger.xhtml] +skip-if = + true + (verify && !debug && os == 'linux') # Frequent intermittent on QR platforms Bug 1454935 +[test_WorkerDebuggerGlobalScope.createSandbox.xhtml] +[test_WorkerDebuggerGlobalScope.enterEventLoop.xhtml] +[test_WorkerDebuggerGlobalScope.reportError.xhtml] +[test_WorkerDebuggerGlobalScope.setImmediate.xhtml] +[test_WorkerDebuggerManager.xhtml] +skip-if = true # Frequent intermittent on QR platforms Bug 1454935 +[test_WorkerDebugger_console.xhtml] +[test_WorkerDebugger_frozen.xhtml] +[test_WorkerDebugger_promise.xhtml] +[test_WorkerDebugger_suspended.xhtml] +[test_chromeWorker.xhtml] +[test_chromeWorkerJSM.xhtml] +[test_file.xhtml] +skip-if = + os == "linux" && bits == 64 && debug # Bug 1765445 + os == "mac" && os_version == "10.15" && !debug # Bug 1765445 + win10_2004 && !debug # Bug 1765445 +[test_fileBlobPosting.xhtml] +[test_fileBlobSubWorker.xhtml] +[test_filePosting.xhtml] +[test_fileReadSlice.xhtml] +[test_fileReaderSync.xhtml] +[test_fileReaderSyncErrors.xhtml] +[test_fileSlice.xhtml] +[test_fileSubWorker.xhtml] +[test_bug1062920.xhtml] +[test_sharedWorker_privateBrowsing.html] +[test_shutdownCheck.xhtml] +support-files = worker_shutdownCheck.js +[test_sourcemap_header.html] +[test_readableStream_when_closing.html] diff --git a/dom/workers/test/chromeWorker_subworker.js b/dom/workers/test/chromeWorker_subworker.js new file mode 100644 index 0000000000..6e89453150 --- /dev/null +++ b/dom/workers/test/chromeWorker_subworker.js @@ -0,0 +1,7 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +onmessage = function (event) { + postMessage("Done!"); +}; diff --git a/dom/workers/test/chromeWorker_worker.js b/dom/workers/test/chromeWorker_worker.js new file mode 100644 index 0000000000..2d354b087b --- /dev/null +++ b/dom/workers/test/chromeWorker_worker.js @@ -0,0 +1,20 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +if (!("ctypes" in self)) { + throw "No ctypes!"; +} + +// Go ahead and verify that the ctypes lazy getter actually works. +if (ctypes.toString() != "[object ctypes]") { + throw "Bad ctypes object: " + ctypes.toString(); +} + +onmessage = function (event) { + let worker = new ChromeWorker("chromeWorker_subworker.js"); + worker.onmessage = function (msg) { + postMessage(msg.data); + }; + worker.postMessage(event.data); +}; diff --git a/dom/workers/test/chromeWorker_worker.sys.mjs b/dom/workers/test/chromeWorker_worker.sys.mjs new file mode 100644 index 0000000000..ee96d7829b --- /dev/null +++ b/dom/workers/test/chromeWorker_worker.sys.mjs @@ -0,0 +1,16 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +// our onmessage handler lives in the module. +import _ from "./chromeWorker_worker_submod.sys.mjs"; + +if (!("ctypes" in self)) { + throw "No ctypes!"; +} + +// Go ahead and verify that the ctypes lazy getter actually works. +if (ctypes.toString() != "[object ctypes]") { + throw "Bad ctypes object: " + ctypes.toString(); +} diff --git a/dom/workers/test/chromeWorker_worker_submod.sys.mjs b/dom/workers/test/chromeWorker_worker_submod.sys.mjs new file mode 100644 index 0000000000..c2fc29175c --- /dev/null +++ b/dom/workers/test/chromeWorker_worker_submod.sys.mjs @@ -0,0 +1,9 @@ +onmessage = function (event) { + let worker = new ChromeWorker("chromeWorker_subworker.js"); + worker.onmessage = function (msg) { + postMessage(msg.data); + }; + worker.postMessage(event.data); +}; + +export default "go away linter"; diff --git a/dom/workers/test/clearTimeoutsImplicit_worker.js b/dom/workers/test/clearTimeoutsImplicit_worker.js new file mode 100644 index 0000000000..dfb8d0f6ce --- /dev/null +++ b/dom/workers/test/clearTimeoutsImplicit_worker.js @@ -0,0 +1,11 @@ +var count = 0; +function timerFunction() { + if (++count == 30) { + postMessage("ready"); + while (true) {} + } +} + +for (var i = 0; i < 10; i++) { + setInterval(timerFunction, 500); +} diff --git a/dom/workers/test/clearTimeouts_worker.js b/dom/workers/test/clearTimeouts_worker.js new file mode 100644 index 0000000000..6e6198c6b5 --- /dev/null +++ b/dom/workers/test/clearTimeouts_worker.js @@ -0,0 +1,12 @@ +var count = 0; +function timerFunction() { + if (++count == 30) { + close(); + postMessage("ready"); + while (true) {} + } +} + +for (var i = 0; i < 10; i++) { + setInterval(timerFunction, 500); +} diff --git a/dom/workers/test/consoleReplaceable_worker.js b/dom/workers/test/consoleReplaceable_worker.js new file mode 100644 index 0000000000..7e6be8cdd6 --- /dev/null +++ b/dom/workers/test/consoleReplaceable_worker.js @@ -0,0 +1,24 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +onmessage = function (event) { + postMessage({ event: "console exists", status: !!console, last: false }); + var logCalled = false; + console.log = function () { + logCalled = true; + }; + console.log("foo"); + postMessage({ + event: "console.log is replaceable", + status: logCalled, + last: false, + }); + console = 42; + postMessage({ + event: "console is replaceable", + status: console === 42, + last: true, + }); +}; diff --git a/dom/workers/test/console_worker.js b/dom/workers/test/console_worker.js new file mode 100644 index 0000000000..798ede1671 --- /dev/null +++ b/dom/workers/test/console_worker.js @@ -0,0 +1,112 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +onmessage = function (event) { + // TEST: does console exist? + postMessage({ event: "console exists", status: !!console, last: false }); + + postMessage({ + event: "console is the same object", + status: console === console, + last: false, + }); + + postMessage({ event: "trace without function", status: true, last: false }); + + for (var i = 0; i < 10; ++i) { + console.log(i, i, i); + } + + function trace1() { + function trace2() { + function trace3() { + console.trace("trace " + i); + } + trace3(); + } + trace2(); + } + trace1(); + + foobar585956c = function (a) { + console.trace(); + return a + "c"; + }; + + function foobar585956b(a) { + return foobar585956c(a + "b"); + } + + function foobar585956a(omg) { + return foobar585956b(omg + "a"); + } + + function foobar646025(omg) { + console.log(omg, "o", "d"); + } + + function startTimer(timer) { + console.time(timer); + } + + function stopTimer(timer) { + console.timeEnd(timer); + } + + function timeStamp(label) { + console.timeStamp(label); + } + + function testGroups() { + console.groupCollapsed("a", "group"); + console.group("b", "group"); + console.groupEnd(); + } + + foobar585956a("omg"); + foobar646025("omg"); + timeStamp(); + timeStamp("foo"); + testGroups(); + startTimer("foo"); + setTimeout(function () { + stopTimer("foo"); + nextSteps(event); + }, 10); +}; + +function nextSteps(event) { + function namelessTimer() { + console.time(); + console.timeEnd(); + } + + namelessTimer(); + + var str = "Test Message."; + console.log(str); + console.info(str); + console.warn(str); + console.error(str); + console.exception(str); + console.assert(true, str); + console.assert(false, str); + console.profile(str); + console.profileEnd(str); + console.timeStamp(); + console.clear(); + postMessage({ event: "4 messages", status: true, last: false }); + + // Recursive: + if (event.data == true) { + var worker = new Worker("console_worker.js"); + worker.onmessage = function (msg) { + postMessage(msg.data); + }; + worker.postMessage(false); + } else { + postMessage({ event: "bye bye", status: true, last: true }); + } +} diff --git a/dom/workers/test/content_worker.js b/dom/workers/test/content_worker.js new file mode 100644 index 0000000000..d79732b281 --- /dev/null +++ b/dom/workers/test/content_worker.js @@ -0,0 +1,12 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +var props = { + ctypes: 1, + OS: 1, +}; +for (var prop in props) { + postMessage({ prop, value: self[prop] }); +} +postMessage({ testfinished: 1 }); diff --git a/dom/workers/test/crashtests/1153636.html b/dom/workers/test/crashtests/1153636.html new file mode 100644 index 0000000000..6ad0d550fd --- /dev/null +++ b/dom/workers/test/crashtests/1153636.html @@ -0,0 +1,5 @@ + diff --git a/dom/workers/test/crashtests/1158031.html b/dom/workers/test/crashtests/1158031.html new file mode 100644 index 0000000000..6d896bc466 --- /dev/null +++ b/dom/workers/test/crashtests/1158031.html @@ -0,0 +1,11 @@ + + + diff --git a/dom/workers/test/crashtests/1228456.html b/dom/workers/test/crashtests/1228456.html new file mode 100644 index 0000000000..6d1f0f0a72 --- /dev/null +++ b/dom/workers/test/crashtests/1228456.html @@ -0,0 +1,14 @@ + + + diff --git a/dom/workers/test/crashtests/1348882.html b/dom/workers/test/crashtests/1348882.html new file mode 100644 index 0000000000..e0288c4ccb --- /dev/null +++ b/dom/workers/test/crashtests/1348882.html @@ -0,0 +1,18 @@ + + + + + + + diff --git a/dom/workers/test/crashtests/1819146.html b/dom/workers/test/crashtests/1819146.html new file mode 100644 index 0000000000..611acb3ed2 --- /dev/null +++ b/dom/workers/test/crashtests/1819146.html @@ -0,0 +1,23 @@ + + + + + + + diff --git a/dom/workers/test/crashtests/1821066.html b/dom/workers/test/crashtests/1821066.html new file mode 100644 index 0000000000..d654b37768 --- /dev/null +++ b/dom/workers/test/crashtests/1821066.html @@ -0,0 +1,9 @@ + + + + + + + diff --git a/dom/workers/test/crashtests/779707.html b/dom/workers/test/crashtests/779707.html new file mode 100644 index 0000000000..97a8113dab --- /dev/null +++ b/dom/workers/test/crashtests/779707.html @@ -0,0 +1,19 @@ + + + + + + + + diff --git a/dom/workers/test/crashtests/943516.html b/dom/workers/test/crashtests/943516.html new file mode 100644 index 0000000000..5f4667850f --- /dev/null +++ b/dom/workers/test/crashtests/943516.html @@ -0,0 +1,10 @@ + + + diff --git a/dom/workers/test/crashtests/crashtests.list b/dom/workers/test/crashtests/crashtests.list new file mode 100644 index 0000000000..528f4c8a10 --- /dev/null +++ b/dom/workers/test/crashtests/crashtests.list @@ -0,0 +1,8 @@ +load 779707.html +load 943516.html +load 1153636.html +load 1158031.html +load 1228456.html +load 1348882.html +load 1821066.html +load 1819146.html diff --git a/dom/workers/test/csp_worker.js b/dom/workers/test/csp_worker.js new file mode 100644 index 0000000000..e5adf3fe78 --- /dev/null +++ b/dom/workers/test/csp_worker.js @@ -0,0 +1,25 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +onmessage = function (event) { + if (event.data.do == "eval") { + var res; + try { + res = eval("40+2"); + } catch (ex) { + res = ex + ""; + } + postMessage(res); + } else if (event.data.do == "nest") { + var worker = new Worker(event.data.uri); + if (--event.data.level) { + worker.postMessage(event.data); + } else { + worker.postMessage({ do: "eval" }); + } + worker.onmessage = e => { + postMessage(e.data); + }; + } +}; diff --git a/dom/workers/test/csp_worker.js^headers^ b/dom/workers/test/csp_worker.js^headers^ new file mode 100644 index 0000000000..7b835bf2a8 --- /dev/null +++ b/dom/workers/test/csp_worker.js^headers^ @@ -0,0 +1 @@ +Content-Security-Policy: default-src 'self' blob: ; script-src 'unsafe-eval' diff --git a/dom/workers/test/dom_worker_helper.js b/dom/workers/test/dom_worker_helper.js new file mode 100644 index 0000000000..4768fc4840 --- /dev/null +++ b/dom/workers/test/dom_worker_helper.js @@ -0,0 +1,171 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +const wdm = Cc["@mozilla.org/dom/workers/workerdebuggermanager;1"].getService( + Ci.nsIWorkerDebuggerManager +); + +const BASE_URL = "chrome://mochitests/content/chrome/dom/workers/test/"; + +var gRemainingTests = 0; + +function waitForWorkerFinish() { + if (gRemainingTests == 0) { + SimpleTest.waitForExplicitFinish(); + } + ++gRemainingTests; +} + +function finish() { + --gRemainingTests; + if (gRemainingTests == 0) { + SimpleTest.finish(); + } +} + +function assertThrows(fun, message) { + let throws = false; + try { + fun(); + } catch (e) { + throws = true; + } + ok(throws, message); +} + +function generateDebuggers() { + return wdm.getWorkerDebuggerEnumerator(); +} + +function findDebugger(url) { + for (let dbg of generateDebuggers()) { + if (dbg.url === url) { + return dbg; + } + } + return null; +} + +function waitForRegister(url, dbgUrl) { + return new Promise(function (resolve) { + wdm.addListener({ + onRegister(dbg) { + if (dbg.url !== url) { + return; + } + ok(true, "Debugger with url " + url + " should be registered."); + wdm.removeListener(this); + if (dbgUrl) { + info("Initializing worker debugger with url " + url + "."); + dbg.initialize(dbgUrl); + } + resolve(dbg); + }, + }); + }); +} + +function waitForUnregister(url) { + return new Promise(function (resolve) { + wdm.addListener({ + onUnregister(dbg) { + if (dbg.url !== url) { + return; + } + ok(true, "Debugger with url " + url + " should be unregistered."); + wdm.removeListener(this); + resolve(); + }, + }); + }); +} + +function waitForDebuggerClose(dbg) { + return new Promise(function (resolve) { + dbg.addListener({ + onClose() { + ok(true, "Debugger should be closed."); + dbg.removeListener(this); + resolve(); + }, + }); + }); +} + +function waitForDebuggerError(dbg) { + return new Promise(function (resolve) { + dbg.addListener({ + onError(filename, lineno, message) { + dbg.removeListener(this); + resolve(new Error(message, filename, lineno)); + }, + }); + }); +} + +function waitForDebuggerMessage(dbg, message) { + return new Promise(function (resolve) { + dbg.addListener({ + onMessage(message1) { + if (message !== message1) { + return; + } + ok(true, "Should receive " + message + " message from debugger."); + dbg.removeListener(this); + resolve(); + }, + }); + }); +} + +function waitForWindowMessage(window, message) { + return new Promise(function (resolve) { + let onmessage = function (event) { + if (event.data !== event.data) { + return; + } + window.removeEventListener("message", onmessage); + resolve(); + }; + window.addEventListener("message", onmessage); + }); +} + +function waitForWorkerMessage(worker, message) { + return new Promise(function (resolve) { + worker.addEventListener("message", function onmessage(event) { + if (event.data !== message) { + return; + } + ok(true, "Should receive " + message + " message from worker."); + worker.removeEventListener("message", onmessage); + resolve(); + }); + }); +} + +function waitForMultiple(promises) { + return new Promise(function (resolve) { + let values = []; + for (let i = 0; i < promises.length; ++i) { + let index = i; + promises[i].then(function (value) { + is( + index + 1, + values.length + 1, + "Promise " + + (values.length + 1) + + " out of " + + promises.length + + " should be resolved." + ); + values.push(value); + if (values.length === promises.length) { + resolve(values); + } + }); + } + }); +} diff --git a/dom/workers/test/dynamicImport_nested.mjs b/dom/workers/test/dynamicImport_nested.mjs new file mode 100644 index 0000000000..688078d803 --- /dev/null +++ b/dom/workers/test/dynamicImport_nested.mjs @@ -0,0 +1,8 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +await import("./dynamicImport_postMessage.mjs"); + +export const message = "first"; diff --git a/dom/workers/test/dynamicImport_postMessage.mjs b/dom/workers/test/dynamicImport_postMessage.mjs new file mode 100644 index 0000000000..ddb9ee0644 --- /dev/null +++ b/dom/workers/test/dynamicImport_postMessage.mjs @@ -0,0 +1,8 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +postMessage("second"); + +export const message = "second"; diff --git a/dom/workers/test/dynamicImport_worker.js b/dom/workers/test/dynamicImport_worker.js new file mode 100644 index 0000000000..44e5fc4e0a --- /dev/null +++ b/dom/workers/test/dynamicImport_worker.js @@ -0,0 +1,15 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +onmessage = function (event) { + switch (event.data) { + case "start": + import("./dynamicImport_nested.mjs").then(m => postMessage(m.message)); + break; + default: + throw new Error("Bad message: " + event.data); + break; + } +}; diff --git a/dom/workers/test/empty.html b/dom/workers/test/empty.html new file mode 100644 index 0000000000..e69de29bb2 diff --git a/dom/workers/test/empty_worker.js b/dom/workers/test/empty_worker.js new file mode 100644 index 0000000000..9dda1c9fd1 --- /dev/null +++ b/dom/workers/test/empty_worker.js @@ -0,0 +1 @@ +postMessage(42); diff --git a/dom/workers/test/errorPropagation_iframe.html b/dom/workers/test/errorPropagation_iframe.html new file mode 100644 index 0000000000..cba0ee8ca2 --- /dev/null +++ b/dom/workers/test/errorPropagation_iframe.html @@ -0,0 +1,55 @@ + + + + + + + + diff --git a/dom/workers/test/errorPropagation_worker.js b/dom/workers/test/errorPropagation_worker.js new file mode 100644 index 0000000000..2c4de27460 --- /dev/null +++ b/dom/workers/test/errorPropagation_worker.js @@ -0,0 +1,50 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +var seenScopeError; +onerror = function (message, filename, lineno) { + if (!seenScopeError) { + seenScopeError = true; + postMessage({ + type: "scope", + data: { message, filename, lineno }, + }); + return true; + } +}; + +onmessage = function (event) { + var workerId = parseInt(event.data); + + if (workerId > 1) { + var worker = new Worker("errorPropagation_worker.js"); + + worker.onmessage = function (msg) { + postMessage(msg.data); + }; + + var seenWorkerError; + worker.onerror = function (error) { + if (!seenWorkerError) { + seenWorkerError = true; + postMessage({ + type: "worker", + data: { + message: error.message, + filename: error.filename, + lineno: error.lineno, + }, + }); + error.preventDefault(); + } + }; + + worker.postMessage(workerId - 1); + return; + } + + var interval = setInterval(function () { + throw new Error("expectedError"); + }, 100); +}; diff --git a/dom/workers/test/errorwarning_worker.js b/dom/workers/test/errorwarning_worker.js new file mode 100644 index 0000000000..5c951fbede --- /dev/null +++ b/dom/workers/test/errorwarning_worker.js @@ -0,0 +1,44 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +function errorHandler() { + postMessage({ type: "error" }); +} + +onmessage = function (event) { + if (event.data.errors) { + try { + // This is an error: + postMessage({ type: "ignore", value: b.aaa }); + } catch (e) { + errorHandler(); + } + } else { + var a = {}; + // This is a warning: + postMessage({ type: "ignore", value: a.foo }); + } + + if (event.data.loop != 0) { + var worker = new Worker("errorwarning_worker.js"); + worker.onerror = errorHandler; + worker.postMessage({ + loop: event.data.loop - 1, + errors: event.data.errors, + }); + + worker.onmessage = function (e) { + postMessage(e.data); + }; + } else { + postMessage({ type: "finish" }); + } +}; + +onerror = errorHandler; +onerror = onerror; +if (!onerror || onerror != onerror) { + throw "onerror wasn't set properly"; +} diff --git a/dom/workers/test/eventDispatch_worker.js b/dom/workers/test/eventDispatch_worker.js new file mode 100644 index 0000000000..57bbc99aa3 --- /dev/null +++ b/dom/workers/test/eventDispatch_worker.js @@ -0,0 +1,84 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +const fakeEventType = "foo"; + +function testEventTarget(event) { + if (event.target !== self) { + throw new Error("Event has a bad target!"); + } + if (event.currentTarget) { + throw new Error("Event has a bad currentTarget!"); + } + postMessage(event.data); +} + +addEventListener( + fakeEventType, + function (event) { + throw new Error("Trusted event listener received untrusted event!"); + }, + false, + false +); + +addEventListener( + fakeEventType, + function (event) { + if (event.target !== self || event.currentTarget !== self) { + throw new Error("Fake event has bad target!"); + } + if (event.isTrusted) { + throw new Error("Event should be untrusted!"); + } + event.stopImmediatePropagation(); + postMessage(event.data); + }, + false, + true +); + +addEventListener( + fakeEventType, + function (event) { + throw new Error( + "This shouldn't get called because of stopImmediatePropagation." + ); + }, + false, + true +); + +var count = 0; +onmessage = function (event) { + if (event.target !== self || event.currentTarget !== self) { + throw new Error("Event has bad target!"); + } + + if (!count++) { + var exception; + try { + self.dispatchEvent(event); + } catch (e) { + exception = e; + } + + if (!exception) { + throw new Error("Recursive dispatch didn't fail!"); + } + + event = new MessageEvent(fakeEventType, { + bubbles: event.bubbles, + cancelable: event.cancelable, + data: event.data, + origin: "*", + source: null, + }); + self.dispatchEvent(event); + + return; + } + + setTimeout(testEventTarget, 0, event); +}; diff --git a/dom/workers/test/fibonacci_worker.js b/dom/workers/test/fibonacci_worker.js new file mode 100644 index 0000000000..0efe5a18d9 --- /dev/null +++ b/dom/workers/test/fibonacci_worker.js @@ -0,0 +1,24 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +onmessage = function (event) { + var n = parseInt(event.data); + + if (n < 2) { + postMessage(n); + return; + } + + var results = []; + for (var i = 1; i <= 2; i++) { + var worker = new Worker("fibonacci_worker.js"); + worker.onmessage = function (msg) { + results.push(parseInt(msg.data)); + if (results.length == 2) { + postMessage(results[0] + results[1]); + } + }; + worker.postMessage(n - i); + } +}; diff --git a/dom/workers/test/fileBlobSubWorker_worker.js b/dom/workers/test/fileBlobSubWorker_worker.js new file mode 100644 index 0000000000..170362b1a4 --- /dev/null +++ b/dom/workers/test/fileBlobSubWorker_worker.js @@ -0,0 +1,17 @@ +/** + * Expects a blob. Returns an object containing the size, type. + * Used to test posting of blob from worker to worker. + */ +onmessage = function (event) { + var worker = new Worker("fileBlob_worker.js"); + + worker.postMessage(event.data); + + worker.onmessage = function (msg) { + postMessage(msg.data); + }; + + worker.onerror = function (error) { + postMessage(undefined); + }; +}; diff --git a/dom/workers/test/fileBlob_worker.js b/dom/workers/test/fileBlob_worker.js new file mode 100644 index 0000000000..fbb50c7c10 --- /dev/null +++ b/dom/workers/test/fileBlob_worker.js @@ -0,0 +1,13 @@ +/** + * Expects a blob. Returns an object containing the size, type. + */ +onmessage = function (event) { + var file = event.data; + + var rtnObj = new Object(); + + rtnObj.size = file.size; + rtnObj.type = file.type; + + postMessage(rtnObj); +}; diff --git a/dom/workers/test/filePosting_worker.js b/dom/workers/test/filePosting_worker.js new file mode 100644 index 0000000000..052d400374 --- /dev/null +++ b/dom/workers/test/filePosting_worker.js @@ -0,0 +1,3 @@ +onmessage = function (event) { + postMessage(event.data); +}; diff --git a/dom/workers/test/fileReadSlice_worker.js b/dom/workers/test/fileReadSlice_worker.js new file mode 100644 index 0000000000..edbad88802 --- /dev/null +++ b/dom/workers/test/fileReadSlice_worker.js @@ -0,0 +1,16 @@ +/** + * Expects an object containing a blob, a start index and an end index + * for slicing. Returns the contents of the blob read as text. + */ +onmessage = function (event) { + var blob = event.data.blob; + var start = event.data.start; + var end = event.data.end; + + var slicedBlob = blob.slice(start, end); + + var fileReader = new FileReaderSync(); + var text = fileReader.readAsText(slicedBlob); + + postMessage(text); +}; diff --git a/dom/workers/test/fileReaderSyncErrors_worker.js b/dom/workers/test/fileReaderSyncErrors_worker.js new file mode 100644 index 0000000000..0ba91469c8 --- /dev/null +++ b/dom/workers/test/fileReaderSyncErrors_worker.js @@ -0,0 +1,82 @@ +/** + * Delegates "is" evaluation back to main thread. + */ +function is(actual, expected, message) { + var rtnObj = new Object(); + rtnObj.actual = actual; + rtnObj.expected = expected; + rtnObj.message = message; + postMessage(rtnObj); +} + +/** + * Tries to write to property. + */ +function writeProperty(file, property) { + var oldValue = file[property]; + file[property] = -1; + is(file[property], oldValue, "Property " + property + " should be readonly."); +} + +/** + * Passes junk arguments to FileReaderSync methods and expects an exception to + * be thrown. + */ +function fileReaderJunkArgument(blob) { + var fileReader = new FileReaderSync(); + + try { + fileReader.readAsBinaryString(blob); + is( + false, + true, + "Should have thrown an exception calling readAsBinaryString." + ); + } catch (ex) { + is(true, true, "Should have thrown an exception."); + } + + try { + fileReader.readAsDataURL(blob); + is(false, true, "Should have thrown an exception calling readAsDataURL."); + } catch (ex) { + is(true, true, "Should have thrown an exception."); + } + + try { + fileReader.readAsArrayBuffer(blob); + is( + false, + true, + "Should have thrown an exception calling readAsArrayBuffer." + ); + } catch (ex) { + is(true, true, "Should have thrown an exception."); + } + + try { + fileReader.readAsText(blob); + is(false, true, "Should have thrown an exception calling readAsText."); + } catch (ex) { + is(true, true, "Should have thrown an exception."); + } +} + +onmessage = function (event) { + var file = event.data; + + // Test read only properties. + writeProperty(file, "size"); + writeProperty(file, "type"); + writeProperty(file, "name"); + + // Bad types. + fileReaderJunkArgument(undefined); + fileReaderJunkArgument(-1); + fileReaderJunkArgument(1); + fileReaderJunkArgument(new Object()); + fileReaderJunkArgument("hello"); + + // Post undefined to indicate that testing has finished. + postMessage(undefined); +}; diff --git a/dom/workers/test/fileReaderSync_worker.js b/dom/workers/test/fileReaderSync_worker.js new file mode 100644 index 0000000000..d5512ef024 --- /dev/null +++ b/dom/workers/test/fileReaderSync_worker.js @@ -0,0 +1,25 @@ +var reader = new FileReaderSync(); + +/** + * Expects an object containing a file and an encoding then uses a + * FileReaderSync to read the file. Returns an object containing the + * file read a binary string, text, url and ArrayBuffer. + */ +onmessage = function (event) { + var file = event.data.file; + var encoding = event.data.encoding; + + var rtnObj = new Object(); + + if (encoding != undefined) { + rtnObj.text = reader.readAsText(file, encoding); + } else { + rtnObj.text = reader.readAsText(file); + } + + rtnObj.bin = reader.readAsBinaryString(file); + rtnObj.url = reader.readAsDataURL(file); + rtnObj.arrayBuffer = reader.readAsArrayBuffer(file); + + postMessage(rtnObj); +}; diff --git a/dom/workers/test/fileSlice_worker.js b/dom/workers/test/fileSlice_worker.js new file mode 100644 index 0000000000..94a283033a --- /dev/null +++ b/dom/workers/test/fileSlice_worker.js @@ -0,0 +1,27 @@ +/** + * Expects an object containing a blob, a start offset, an end offset + * and an optional content type to slice the blob. Returns an object + * containing the size and type of the sliced blob. + */ +onmessage = function (event) { + var blob = event.data.blob; + var start = event.data.start; + var end = event.data.end; + var contentType = event.data.contentType; + + var slicedBlob; + if (contentType == undefined && end == undefined) { + slicedBlob = blob.slice(start); + } else if (contentType == undefined) { + slicedBlob = blob.slice(start, end); + } else { + slicedBlob = blob.slice(start, end, contentType); + } + + var rtnObj = new Object(); + + rtnObj.size = slicedBlob.size; + rtnObj.type = slicedBlob.type; + + postMessage(rtnObj); +}; diff --git a/dom/workers/test/fileSubWorker_worker.js b/dom/workers/test/fileSubWorker_worker.js new file mode 100644 index 0000000000..8a5c002865 --- /dev/null +++ b/dom/workers/test/fileSubWorker_worker.js @@ -0,0 +1,17 @@ +/** + * Expects a file. Returns an object containing the size, type, name and path + * using another worker. Used to test posting of file from worker to worker. + */ +onmessage = function (event) { + var worker = new Worker("file_worker.js"); + + worker.postMessage(event.data); + + worker.onmessage = function (msg) { + postMessage(msg.data); + }; + + worker.onerror = function (error) { + postMessage(undefined); + }; +}; diff --git a/dom/workers/test/file_bug1010784_worker.js b/dom/workers/test/file_bug1010784_worker.js new file mode 100644 index 0000000000..3e88f5cc97 --- /dev/null +++ b/dom/workers/test/file_bug1010784_worker.js @@ -0,0 +1,9 @@ +onmessage = function (event) { + var xhr = new XMLHttpRequest(); + + xhr.open("GET", event.data, false); + xhr.send(); + xhr.open("GET", event.data, false); + xhr.send(); + postMessage("done"); +}; diff --git a/dom/workers/test/file_service_worker.js b/dom/workers/test/file_service_worker.js new file mode 100644 index 0000000000..dd264e340e --- /dev/null +++ b/dom/workers/test/file_service_worker.js @@ -0,0 +1,3 @@ +self.onmessage = evt => { + evt.ports[0].postMessage("serviceworker-reply"); +}; diff --git a/dom/workers/test/file_service_worker_container.html b/dom/workers/test/file_service_worker_container.html new file mode 100644 index 0000000000..625e911adc --- /dev/null +++ b/dom/workers/test/file_service_worker_container.html @@ -0,0 +1,15 @@ + + + + + + + + Service Worker Container + + diff --git a/dom/workers/test/file_service_worker_fetch_synthetic.js b/dom/workers/test/file_service_worker_fetch_synthetic.js new file mode 100644 index 0000000000..c58aeb294a --- /dev/null +++ b/dom/workers/test/file_service_worker_fetch_synthetic.js @@ -0,0 +1,59 @@ +addEventListener("install", function (evt) { + evt.waitUntil(self.skipWaiting()); +}); + +/** + * Given a multipart/form-data encoded string that we know to have only a single + * part, return the contents of the part. (MIME multipart encoding is too + * exciting to delve into.) + */ +function extractBlobFromMultipartFormData(text) { + const lines = text.split(/\r\n/g); + const firstBlank = lines.indexOf(""); + const foo = lines.slice(firstBlank + 1, -2).join("\n"); + return foo; +} + +self.addEventListener("fetch", event => { + const url = new URL(event.request.url); + const mode = url.searchParams.get("mode"); + + if (mode === "synthetic") { + event.respondWith( + (async () => { + // This works even if there wasn't a body explicitly associated with the + // request. We just get a zero-length string in that case. + const requestBodyContents = await event.request.text(); + const blobContents = + extractBlobFromMultipartFormData(requestBodyContents); + + return new Response( + ` +

${event.request.url}

+
ServiceWorker
+
${blobContents}
+ `, + { headers: { "Content-Type": "text/html" } } + ); + })() + ); + } else if (mode === "fetch") { + event.respondWith(fetch(event.request)); + } else if (mode === "clone") { + // In order for the act of cloning to be interesting, we want the original + // request to remain alive so that any pipes end up having to buffer. + self.originalRequest = event.request; + event.respondWith(fetch(event.request.clone())); + } else { + event.respondWith( + new Response( + ` +

Bad mode: ${mode}

+
ServiceWorker::Error
+
No, this is an error.
+ `, + { headers: { "Content-Type": "text/html" }, status: 400 } + ) + ); + } +}); diff --git a/dom/workers/test/file_use_counter_service_worker.js b/dom/workers/test/file_use_counter_service_worker.js new file mode 100644 index 0000000000..8ee0d2e04a --- /dev/null +++ b/dom/workers/test/file_use_counter_service_worker.js @@ -0,0 +1,9 @@ +onmessage = async function (e) { + if (e.data === "RUN") { + console.log("worker runs"); + await clients.claim(); + clients.matchAll().then(res => { + res.forEach(client => client.postMessage("DONE")); + }); + } +}; diff --git a/dom/workers/test/file_use_counter_shared_worker.js b/dom/workers/test/file_use_counter_shared_worker.js new file mode 100644 index 0000000000..7e4f95af3b --- /dev/null +++ b/dom/workers/test/file_use_counter_shared_worker.js @@ -0,0 +1,10 @@ +onconnect = function (e) { + let port = e.ports[0]; + port.onmessage = function (m) { + if (m.data === "RUN") { + console.log("worker runs"); + port.postMessage("DONE"); + close(); + } + }; +}; diff --git a/dom/workers/test/file_use_counter_shared_worker_microtask.js b/dom/workers/test/file_use_counter_shared_worker_microtask.js new file mode 100644 index 0000000000..b219da2225 --- /dev/null +++ b/dom/workers/test/file_use_counter_shared_worker_microtask.js @@ -0,0 +1,12 @@ +onconnect = function (e) { + let port = e.ports[0]; + port.onmessage = function (m) { + if (m.data === "RUN") { + queueMicrotask(() => { + console.log("worker runs"); + }); + port.postMessage("DONE"); + close(); + } + }; +}; diff --git a/dom/workers/test/file_use_counter_worker.html b/dom/workers/test/file_use_counter_worker.html new file mode 100644 index 0000000000..d034ad6f5f --- /dev/null +++ b/dom/workers/test/file_use_counter_worker.html @@ -0,0 +1,13 @@ + + + + + + Test for Bug 1202706 + + +Mozilla Bug 1202706 + + diff --git a/dom/workers/test/file_use_counter_worker.js b/dom/workers/test/file_use_counter_worker.js new file mode 100644 index 0000000000..12046940a8 --- /dev/null +++ b/dom/workers/test/file_use_counter_worker.js @@ -0,0 +1,2 @@ +console.log("worker runs"); +postMessage("DONE"); diff --git a/dom/workers/test/file_worker.js b/dom/workers/test/file_worker.js new file mode 100644 index 0000000000..6ed8fe1816 --- /dev/null +++ b/dom/workers/test/file_worker.js @@ -0,0 +1,16 @@ +/** + * Expects a file. Returns an object containing the size, type, name and path. + */ +onmessage = function (event) { + var file = event.data; + + var rtnObj = new Object(); + + rtnObj.size = file.size; + rtnObj.type = file.type; + rtnObj.name = file.name; + rtnObj.path = file.path; + rtnObj.lastModified = file.lastModified; + + postMessage(rtnObj); +}; diff --git a/dom/workers/test/foreign.js b/dom/workers/test/foreign.js new file mode 100644 index 0000000000..33c982fa8f --- /dev/null +++ b/dom/workers/test/foreign.js @@ -0,0 +1 @@ +response = "bad"; diff --git a/dom/workers/test/head.js b/dom/workers/test/head.js new file mode 100644 index 0000000000..ce6cebbd06 --- /dev/null +++ b/dom/workers/test/head.js @@ -0,0 +1,75 @@ +/* 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 a tab with given `url`. Returns a promise + * that will be resolved when the tab finished loading. + */ +function addTab(url) { + return BrowserTestUtils.openNewForegroundTab(gBrowser, TAB_URL); +} + +/** + * Remove the given `tab`. + */ +function removeTab(tab) { + gBrowser.removeTab(tab); +} + +/** + * Create a worker with the given `url` in the given `tab`. + */ +function createWorkerInTab(tab, url) { + info("Creating worker with url '" + url + "'\n"); + return SpecialPowers.spawn(tab.linkedBrowser, [url], urlChild => { + if (!content._workers) { + content._workers = {}; + } + content._workers[urlChild] = new content.Worker(urlChild); + }); +} + +/** + * Terminate the worker with the given `url` in the given `tab`. + */ +function terminateWorkerInTab(tab, url) { + info("Terminating worker with url '" + url + "'\n"); + return SpecialPowers.spawn(tab.linkedBrowser, [url], urlChild => { + content._workers[urlChild].terminate(); + delete content._workers[urlChild]; + }); +} + +/** + * Post the given `message` to the worker with the given `url` in the given + * `tab`. + */ +function postMessageToWorkerInTab(tab, url, message) { + info("Posting message to worker with url '" + url + "'\n"); + return SpecialPowers.spawn( + tab.linkedBrowser, + [url, message], + (urlChild, messageChild) => { + let worker = content._workers[urlChild]; + worker.postMessage(messageChild); + return new Promise(function (resolve) { + worker.onmessage = function (event) { + worker.onmessage = null; + resolve(event.data); + }; + }); + } + ); +} + +/** + * Disable the cache in the given `tab`. + */ +function disableCacheInTab(tab) { + return SpecialPowers.spawn(tab.linkedBrowser, [], () => { + content.docShell.defaultLoadFlags = + Ci.nsIRequest.LOAD_BYPASS_CACHE | Ci.nsIRequest.INHIBIT_CACHING; + }); +} diff --git a/dom/workers/test/importForeignScripts_worker.js b/dom/workers/test/importForeignScripts_worker.js new file mode 100644 index 0000000000..4e3d65483f --- /dev/null +++ b/dom/workers/test/importForeignScripts_worker.js @@ -0,0 +1,55 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var target = self; +var response; + +function runTests() { + response = "good"; + try { + importScripts("http://example.org/tests/dom/workers/test/foreign.js"); + } catch (e) { + dump("Got error " + e + " when calling importScripts"); + } + if (response === "good") { + try { + importScripts("redirect_to_foreign.sjs"); + } catch (e) { + dump("Got error " + e + " when calling importScripts"); + } + } + target.postMessage(response); + + // Now, test a nested worker. + if (location.search !== "?nested") { + var worker = new Worker("importForeignScripts_worker.js?nested"); + + worker.onmessage = function (e) { + target.postMessage(e.data); + target.postMessage("finish"); + }; + + worker.onerror = function () { + target.postMessage("nested worker error"); + }; + + worker.postMessage("start"); + } +} + +onmessage = function (e) { + if (e.data === "start") { + runTests(); + } +}; + +onconnect = function (e) { + target = e.ports[0]; + e.ports[0].onmessage = function (msg) { + if (msg.data === "start") { + runTests(); + } + }; +}; diff --git a/dom/workers/test/importScripts_3rdParty_worker.js b/dom/workers/test/importScripts_3rdParty_worker.js new file mode 100644 index 0000000000..326d48f77a --- /dev/null +++ b/dom/workers/test/importScripts_3rdParty_worker.js @@ -0,0 +1,88 @@ +const workerURL = + "http://mochi.test:8888/tests/dom/workers/test/importScripts_3rdParty_worker.js"; + +onmessage = function (a) { + if (a.data.nested) { + var worker = new Worker(workerURL); + worker.onmessage = function (event) { + postMessage(event.data); + }; + + worker.onerror = function (event) { + event.preventDefault(); + postMessage({ + error: event instanceof ErrorEvent && event.filename == workerURL, + }); + }; + + a.data.nested = false; + worker.postMessage(a.data); + return; + } + + // This first URL will use the same origin of this script. + var sameOriginURL = new URL(a.data.url); + var fileName1 = 42; + + // This is cross-origin URL. + var crossOriginURL = new URL(a.data.url); + crossOriginURL.host = "example.com"; + crossOriginURL.port = 80; + var fileName2 = 42; + + if (a.data.test == "none") { + importScripts(crossOriginURL.href); + return; + } + + try { + importScripts(sameOriginURL.href); + } catch (e) { + if (!(e instanceof SyntaxError)) { + postMessage({ result: false }); + return; + } + + fileName1 = e.fileName; + } + + if (fileName1 != sameOriginURL.href || !fileName1) { + postMessage({ result: false }); + return; + } + + if (a.data.test == "try") { + var exception; + try { + importScripts(crossOriginURL.href); + } catch (e) { + fileName2 = e.filename; + exception = e; + } + + postMessage({ + result: + fileName2 == workerURL && + exception.name == "NetworkError" && + exception.code == DOMException.NETWORK_ERR, + }); + return; + } + + if (a.data.test == "eventListener") { + addEventListener("error", function (event) { + event.preventDefault(); + postMessage({ + result: event instanceof ErrorEvent && event.filename == workerURL, + }); + }); + } + + if (a.data.test == "onerror") { + onerror = function (...args) { + postMessage({ result: args[1] == workerURL }); + }; + } + + importScripts(crossOriginURL.href); +}; diff --git a/dom/workers/test/importScripts_mixedcontent.html b/dom/workers/test/importScripts_mixedcontent.html new file mode 100644 index 0000000000..2955fdc46e --- /dev/null +++ b/dom/workers/test/importScripts_mixedcontent.html @@ -0,0 +1,46 @@ + + + diff --git a/dom/workers/test/importScripts_worker.js b/dom/workers/test/importScripts_worker.js new file mode 100644 index 0000000000..b206df1e75 --- /dev/null +++ b/dom/workers/test/importScripts_worker.js @@ -0,0 +1,63 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +// Try no args. This shouldn't do anything. +importScripts(); + +// This caused security exceptions in the past, make sure it doesn't! +var constructor = {}.constructor; + +importScripts("importScripts_worker_imported1.js"); + +// Try to call a function defined in the imported script. +importedScriptFunction(); + +function tryBadScripts() { + var badScripts = [ + // Has a syntax error + "importScripts_worker_imported3.js", + // Throws an exception + "importScripts_worker_imported4.js", + // Shouldn't exist! + "http://example.com/non-existing/importScripts_worker_foo.js", + // Not a valid url + "http://notadomain::notafile aword", + ]; + + for (var i = 0; i < badScripts.length; i++) { + var caughtException = false; + var url = badScripts[i]; + try { + importScripts(url); + } catch (e) { + caughtException = true; + } + if (!caughtException) { + throw "Bad script didn't throw exception: " + url; + } + } +} + +const url = "data:text/javascript,const startResponse = 'started';"; +importScripts(url); + +onmessage = function (event) { + switch (event.data) { + case "start": + importScripts("importScripts_worker_imported2.js"); + importedScriptFunction2(); + tryBadScripts(); + postMessage(startResponse); + break; + case "stop": + tryBadScripts(); + postMessage("stopped"); + break; + default: + throw new Error("Bad message: " + event.data); + break; + } +}; + +tryBadScripts(); diff --git a/dom/workers/test/importScripts_worker_imported1.js b/dom/workers/test/importScripts_worker_imported1.js new file mode 100644 index 0000000000..2a1d28c44d --- /dev/null +++ b/dom/workers/test/importScripts_worker_imported1.js @@ -0,0 +1,9 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +// This caused security exceptions in the past, make sure it doesn't! +var myConstructor = {}.constructor; + +// Try to call a function defined in the imported script. +function importedScriptFunction() {} diff --git a/dom/workers/test/importScripts_worker_imported2.js b/dom/workers/test/importScripts_worker_imported2.js new file mode 100644 index 0000000000..3f275e237b --- /dev/null +++ b/dom/workers/test/importScripts_worker_imported2.js @@ -0,0 +1,9 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +// This caused security exceptions in the past, make sure it doesn't! +var myConstructor2 = {}.constructor; + +// Try to call a function defined in the imported script. +function importedScriptFunction2() {} diff --git a/dom/workers/test/importScripts_worker_imported3.js b/dom/workers/test/importScripts_worker_imported3.js new file mode 100644 index 0000000000..c54be3e5f7 --- /dev/null +++ b/dom/workers/test/importScripts_worker_imported3.js @@ -0,0 +1,6 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +// Deliberate syntax error, should generate a worker exception! +for (var index = 0; index < 100) {} diff --git a/dom/workers/test/importScripts_worker_imported4.js b/dom/workers/test/importScripts_worker_imported4.js new file mode 100644 index 0000000000..82f8708c59 --- /dev/null +++ b/dom/workers/test/importScripts_worker_imported4.js @@ -0,0 +1,6 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +// Deliberate throw, should generate a worker exception! +throw new Error("Bah!"); diff --git a/dom/workers/test/instanceof_worker.js b/dom/workers/test/instanceof_worker.js new file mode 100644 index 0000000000..a7f3ab418d --- /dev/null +++ b/dom/workers/test/instanceof_worker.js @@ -0,0 +1,16 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +onmessage = function (event) { + postMessage({ + event: "XMLHttpRequest", + status: new XMLHttpRequest() instanceof XMLHttpRequest, + last: false, + }); + postMessage({ + event: "XMLHttpRequestUpload", + status: new XMLHttpRequest().upload instanceof XMLHttpRequestUpload, + last: true, + }); +}; diff --git a/dom/workers/test/invalid.js b/dom/workers/test/invalid.js new file mode 100644 index 0000000000..8912b7ee06 --- /dev/null +++ b/dom/workers/test/invalid.js @@ -0,0 +1 @@ +invalid> diff --git a/dom/workers/test/json_worker.js b/dom/workers/test/json_worker.js new file mode 100644 index 0000000000..229ac954c9 --- /dev/null +++ b/dom/workers/test/json_worker.js @@ -0,0 +1,354 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +var cyclicalObject = {}; +cyclicalObject.foo = cyclicalObject; + +var cyclicalArray = []; +cyclicalArray.push(cyclicalArray); + +function makeNested(obj, count) { + var innermostobj; + for (var i = 0; i < count; i++) { + obj.foo = { bar: 5 }; + innermostobj = obj.foo; + obj = innermostobj; + } + return innermostobj; +} + +var nestedObject = {}; +makeNested(nestedObject, 100); + +var cyclicalObject = {}; +var innermost = makeNested(cyclicalObject, 1000); +innermost.baz = cyclicalObject; + +var objectWithSaneGetter = {}; +objectWithSaneGetter.__defineGetter__("foo", function () { + return 5; +}); + +// We don't walk prototype chains for cloning so this won't actually do much... +function objectWithSaneGetter2() {} +objectWithSaneGetter2.prototype = { + get foo() { + return 5; + }, +}; + +const throwingGetterThrownString = "bad"; + +var objectWithThrowingGetter = {}; +objectWithThrowingGetter.__defineGetter__("foo", function () { + throw throwingGetterThrownString; +}); + +var typedArrayWithValues = new Int8Array(5); +for (let index in typedArrayWithValues) { + typedArrayWithValues[index] = index; +} + +var typedArrayWithFunBuffer = new Int8Array(4); +for (let index in typedArrayWithFunBuffer) { + typedArrayWithFunBuffer[index] = 255; +} + +var typedArrayWithFunBuffer2 = new Int32Array(typedArrayWithFunBuffer.buffer); + +var xhr = new XMLHttpRequest(); + +var messages = [ + { + type: "object", + value: {}, + jsonValue: "{}", + }, + { + type: "object", + value: { foo: "bar" }, + jsonValue: '{"foo":"bar"}', + }, + { + type: "object", + value: { foo: "bar", foo2: { bee: "bop" } }, + jsonValue: '{"foo":"bar","foo2":{"bee":"bop"}}', + }, + { + type: "object", + value: { foo: "bar", foo2: { bee: "bop" }, foo3: "baz" }, + jsonValue: '{"foo":"bar","foo2":{"bee":"bop"},"foo3":"baz"}', + }, + { + type: "object", + value: { foo: "bar", foo2: [1, 2, 3] }, + jsonValue: '{"foo":"bar","foo2":[1,2,3]}', + }, + { + type: "object", + value: cyclicalObject, + }, + { + type: "object", + value: [null, 2, false, cyclicalObject], + }, + { + type: "object", + value: cyclicalArray, + }, + { + type: "object", + value: { foo: 1, bar: cyclicalArray }, + }, + { + type: "object", + value: nestedObject, + jsonValue: JSON.stringify(nestedObject), + }, + { + type: "object", + value: cyclicalObject, + }, + { + type: "object", + value: objectWithSaneGetter, + jsonValue: '{"foo":5}', + }, + { + type: "object", + value: new objectWithSaneGetter2(), + jsonValue: "{}", + }, + { + type: "object", + value: objectWithThrowingGetter, + exception: true, + }, + { + type: "object", + array: true, + value: [9, 8, 7], + jsonValue: "[9,8,7]", + }, + { + type: "object", + array: true, + value: [9, false, 10.5, { foo: "bar" }], + jsonValue: '[9,false,10.5,{"foo":"bar"}]', + }, + { + type: "object", + shouldEqual: true, + value: null, + }, + { + type: "undefined", + shouldEqual: true, + value: undefined, + }, + { + type: "string", + shouldEqual: true, + value: "Hello", + }, + { + type: "string", + shouldEqual: true, + value: JSON.stringify({ foo: "bar" }), + compareValue: '{"foo":"bar"}', + }, + { + type: "number", + shouldEqual: true, + value: 1, + }, + { + type: "number", + shouldEqual: true, + value: 0, + }, + { + type: "number", + shouldEqual: true, + value: -1, + }, + { + type: "number", + shouldEqual: true, + value: 12345678901234567000, + }, + { + type: "number", + shouldEqual: true, + value: -12345678901234567000, + }, + { + type: "number", + shouldEqual: true, + value: 0.25, + }, + { + type: "number", + shouldEqual: true, + value: -0.25, + }, + { + type: "boolean", + shouldEqual: true, + value: true, + }, + { + type: "boolean", + shouldEqual: true, + value: false, + }, + { + type: "object", + value(foo) { + return "Bad!"; + }, + exception: true, + }, + { + type: "number", + isNaN: true, + value: NaN, + }, + { + type: "number", + isInfinity: true, + value: Infinity, + }, + { + type: "number", + isNegativeInfinity: true, + value: -Infinity, + }, + { + type: "object", + value: new Int32Array(10), + jsonValue: '{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0}', + }, + { + type: "object", + value: new Float32Array(5), + jsonValue: '{"0":0,"1":0,"2":0,"3":0,"4":0}', + }, + { + type: "object", + value: typedArrayWithValues, + jsonValue: '{"0":0,"1":1,"2":2,"3":3,"4":4}', + }, + { + type: "number", + value: typedArrayWithValues[2], + compareValue: 2, + shouldEqual: true, + }, + { + type: "object", + value: typedArrayWithValues.buffer, + jsonValue: "{}", + }, + { + type: "object", + value: typedArrayWithFunBuffer2, + jsonValue: '{"0":-1}', + }, + { + type: "object", + value: { foo: typedArrayWithFunBuffer2 }, + jsonValue: '{"foo":{"0":-1}}', + }, + { + type: "object", + value: [typedArrayWithFunBuffer2], + jsonValue: '[{"0":-1}]', + }, + { + type: "object", + value: { + foo(a) { + alert(b); + }, + }, + exception: true, + }, + { + type: "object", + value: xhr, + exception: true, + }, + { + type: "number", + value: xhr.readyState, + shouldEqual: true, + }, + { + type: "object", + value: { xhr }, + exception: true, + }, + { + type: "object", + value: self, + exception: true, + }, + { + type: "object", + value: { p: ArrayBuffer.prototype }, + exception: true, + }, + { + type: "string", + shouldEqual: true, + value: "testFinished", + }, +]; + +for (let index = 0; index < messages.length; index++) { + var message = messages[index]; + if (message.hasOwnProperty("compareValue")) { + continue; + } + if ( + message.hasOwnProperty("shouldEqual") || + message.hasOwnProperty("shouldCompare") + ) { + message.compareValue = message.value; + } +} + +onmessage = function (event) { + for (let index = 0; index < messages.length; index++) { + var exception = undefined; + + try { + postMessage(messages[index].value); + } catch (e) { + if (e instanceof DOMException) { + if (e.code != DOMException.DATA_CLONE_ERR) { + throw "DOMException with the wrong code: " + e.code; + } + } else if (e != throwingGetterThrownString) { + throw "Exception of the wrong type: " + e; + } + exception = e; + } + + if ( + (exception !== undefined && !messages[index].exception) || + (exception === undefined && messages[index].exception) + ) { + throw ( + "Exception inconsistency [index = " + + index + + ", " + + messages[index].toSource() + + "]: " + + exception + ); + } + } +}; diff --git a/dom/workers/test/loadEncoding_worker.js b/dom/workers/test/loadEncoding_worker.js new file mode 100644 index 0000000000..5e40478445 --- /dev/null +++ b/dom/workers/test/loadEncoding_worker.js @@ -0,0 +1,7 @@ +/* + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +*/ +// Bug 484305 - Load workers as UTF-8. +postMessage({ encoding: "KOI8-R", text: "ðÒÉ×ÅÔ" }); +postMessage({ encoding: "UTF-8", text: "Привет" }); diff --git a/dom/workers/test/location_worker.js b/dom/workers/test/location_worker.js new file mode 100644 index 0000000000..2f16364e1f --- /dev/null +++ b/dom/workers/test/location_worker.js @@ -0,0 +1,12 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +for (var string in self.location) { + var value = + typeof self.location[string] === "function" + ? self.location[string]() + : self.location[string]; + postMessage({ string, value }); +} +postMessage({ string: "testfinished" }); diff --git a/dom/workers/test/longThread_worker.js b/dom/workers/test/longThread_worker.js new file mode 100644 index 0000000000..4096354aca --- /dev/null +++ b/dom/workers/test/longThread_worker.js @@ -0,0 +1,14 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +onmessage = function (event) { + switch (event.data) { + case "start": + for (var i = 0; i < 10000000; i++) {} + postMessage("done"); + break; + default: + throw "Bad message: " + event.data; + } +}; diff --git a/dom/workers/test/marionette/manifest.ini b/dom/workers/test/marionette/manifest.ini new file mode 100644 index 0000000000..05aa421e49 --- /dev/null +++ b/dom/workers/test/marionette/manifest.ini @@ -0,0 +1,2 @@ +[test_service_workers_at_startup.py] +[test_service_workers_disabled.py] diff --git a/dom/workers/test/marionette/service_worker_utils.py b/dom/workers/test/marionette/service_worker_utils.py new file mode 100644 index 0000000000..b29789b6f0 --- /dev/null +++ b/dom/workers/test/marionette/service_worker_utils.py @@ -0,0 +1,63 @@ +# 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/. + +import os + +from marionette_driver import Wait +from marionette_harness import MarionetteTestCase + + +class MarionetteServiceWorkerTestCase(MarionetteTestCase): + def install_service_worker(self, path): + install_url = self.marionette.absolute_url(path) + self.marionette.navigate(install_url) + Wait(self.marionette).until( + lambda _: self.is_service_worker_registered, + message="Service worker not successfully installed", + ) + + # Wait for the registered service worker to be stored in the Firefox + # profile before restarting the instance to prevent intermittent + # failures (Bug 1665184). + Wait(self.marionette, timeout=10).until( + lambda _: self.profile_serviceworker_txt_exists, + message="Service worker not stored in profile", + ) + + # self.marionette.restart(in_app=True) will restore service workers if + # we don't navigate away before restarting. + self.marionette.navigate("about:blank") + + # Using @property helps avoid the case where missing parens at the call site + # yields an unvarying 'true' value. + @property + def profile_serviceworker_txt_exists(self): + return "serviceworker.txt" in os.listdir(self.marionette.profile_path) + + @property + def is_service_worker_registered(self): + with self.marionette.using_context("chrome"): + return self.marionette.execute_script( + """ + let swm = Cc["@mozilla.org/serviceworkers/manager;1"].getService( + Ci.nsIServiceWorkerManager + ); + let ssm = Services.scriptSecurityManager; + + let principal = ssm.createContentPrincipalFromOrigin(arguments[0]); + + let serviceWorkers = swm.getAllRegistrations(); + for (let i = 0; i < serviceWorkers.length; i++) { + let sw = serviceWorkers.queryElementAt( + i, + Ci.nsIServiceWorkerRegistrationInfo + ); + if (sw.principal.origin == principal.origin) { + return true; + } + } + return false; + """, + script_args=(self.marionette.absolute_url(""),), + ) diff --git a/dom/workers/test/marionette/test_service_workers_at_startup.py b/dom/workers/test/marionette/test_service_workers_at_startup.py new file mode 100644 index 0000000000..14ff32f87f --- /dev/null +++ b/dom/workers/test/marionette/test_service_workers_at_startup.py @@ -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/. + +import os +import sys + +# Add this directory to the import path. +sys.path.append(os.path.dirname(__file__)) + +from marionette_driver import Wait +from service_worker_utils import MarionetteServiceWorkerTestCase + + +class ServiceWorkerAtStartupTestCase(MarionetteServiceWorkerTestCase): + def setUp(self): + super(ServiceWorkerAtStartupTestCase, self).setUp() + self.install_service_worker("serviceworker/install_serviceworker.html") + + def tearDown(self): + self.marionette.restart(in_app=False, clean=True) + super(ServiceWorkerAtStartupTestCase, self).tearDown() + + def test_registered_service_worker_after_restart(self): + self.marionette.restart() + + Wait(self.marionette).until( + lambda _: self.is_service_worker_registered, + message="Service worker not registered after restart", + ) + self.assertTrue(self.is_service_worker_registered) diff --git a/dom/workers/test/marionette/test_service_workers_disabled.py b/dom/workers/test/marionette/test_service_workers_disabled.py new file mode 100644 index 0000000000..deed164242 --- /dev/null +++ b/dom/workers/test/marionette/test_service_workers_disabled.py @@ -0,0 +1,37 @@ +# 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/. + +import os +import sys + +# Add this directory to the import path. +sys.path.append(os.path.dirname(__file__)) + +from service_worker_utils import MarionetteServiceWorkerTestCase + + +class ServiceWorkersDisabledTestCase(MarionetteServiceWorkerTestCase): + def setUp(self): + super(ServiceWorkersDisabledTestCase, self).setUp() + self.install_service_worker("serviceworker/install_serviceworker.html") + + def tearDown(self): + self.marionette.restart(in_app=False, clean=True) + super(ServiceWorkersDisabledTestCase, self).tearDown() + + def test_service_workers_disabled_at_startup(self): + # self.marionette.set_pref sets preferences after startup. Using it + # here causes intermittent failures. + self.marionette.instance.profile.set_preferences( + { + "dom.serviceWorkers.enabled": False, + } + ) + + self.marionette.restart() + + self.assertFalse( + self.is_service_worker_registered, + "Service worker registration should have been purged", + ) diff --git a/dom/workers/test/mochitest.ini b/dom/workers/test/mochitest.ini new file mode 100644 index 0000000000..66d5ea6422 --- /dev/null +++ b/dom/workers/test/mochitest.ini @@ -0,0 +1,249 @@ +[DEFAULT] +support-files = + WorkerTest_badworker.js + atob_worker.js + blank.html + bug978260_worker.js + bug1014466_data1.txt + bug1014466_data2.txt + bug1014466_worker.js + bug1020226_worker.js + bug1020226_frame.html + bug998474_worker.js + bug1063538_worker.js + bug1063538.sjs + clearTimeouts_worker.js + clearTimeoutsImplicit_worker.js + content_worker.js + console_worker.js + consoleReplaceable_worker.js + csp_worker.js + csp_worker.js^headers^ + dynamicImport_nested.mjs + dynamicImport_postMessage.mjs + dynamicImport_worker.js + 404_server.sjs + errorPropagation_iframe.html + errorPropagation_worker.js + errorwarning_worker.js + eventDispatch_worker.js + fibonacci_worker.js + file_bug1010784_worker.js + foreign.js + importForeignScripts_worker.js + importScripts_mixedcontent.html + importScripts_worker.js + importScripts_worker_imported1.js + importScripts_worker_imported2.js + importScripts_worker_imported3.js + importScripts_worker_imported4.js + instanceof_worker.js + json_worker.js + loadEncoding_worker.js + location_worker.js + longThread_worker.js + multi_sharedWorker_frame.html + multi_sharedWorker_sharedWorker.js + navigator_languages_worker.js + navigator_worker.js + newError_worker.js + notification_worker.js + notification_worker_child-child.js + notification_worker_child-parent.js + notification_permission_worker.js + onLine_worker.js + onLine_worker_child.js + onLine_worker_head.js + promise_worker.js + recursion_worker.js + recursiveOnerror_worker.js + redirect_to_foreign.sjs + rvals_worker.js + sharedWorker_sharedWorker.js + simpleThread_worker.js + suspend_window.html + suspend_worker.js + terminate_worker.js + test_csp.html^headers^ + test_csp.js + referrer_worker.html + sourcemap_header_iframe.html + sourcemap_header_worker.js + sourcemap_header_worker.js^headers^ + threadErrors_worker1.js + threadErrors_worker2.js + threadErrors_worker3.js + threadErrors_worker4.js + threadTimeouts_worker.js + throwingOnerror_worker.js + timeoutTracing_worker.js + transferable_worker.js + test_worker_interfaces.js + worker_driver.js + worker_wrapper.js + bug1060621_worker.js + bug1062920_worker.js + bug1104064_worker.js + worker_consoleAndBlobs.js + bug1132395_sharedWorker.js + bug1132924_worker.js + empty.html + referrer.sjs + referrer_test_server.sjs + sharedWorker_ports.js + sharedWorker_lifetime.js + worker_referrer.js + importScripts_3rdParty_worker.js + invalid.js + worker_bug1278777.js + worker_setTimeoutWith0.js + worker_bug1301094.js + script_createFile.js + worker_suspended.js + window_suspended.html + suspend_blank.html + multi_sharedWorker_manager.js + multi_sharedWorker_frame_nobfcache.html + multi_sharedWorker_frame_nobfcache.html^headers^ + multi_sharedWorker_frame_bfcache.html + navigate.html + !/dom/notification/test/mochitest/MockServices.js + !/dom/notification/test/mochitest/NotificationTest.js + !/dom/xhr/tests/relativeLoad_import.js + !/dom/xhr/tests/relativeLoad_worker.js + !/dom/xhr/tests/relativeLoad_worker2.js + !/dom/xhr/tests/subdir/relativeLoad_sub_worker.js + !/dom/xhr/tests/subdir/relativeLoad_sub_worker2.js + !/dom/xhr/tests/subdir/relativeLoad_sub_import.js + !/dom/events/test/event_leak_utils.js + +[test_404.html] +[test_atob.html] +[test_blobConstructor.html] +[test_blobWorkers.html] +[test_bug949946.html] +[test_bug978260.html] +[test_bug998474.html] +[test_bug1002702.html] +[test_bug1010784.html] +[test_bug1014466.html] +[test_bug1020226.html] +[test_bug1036484.html] +[test_bug1060621.html] +[test_bug1062920.html] +[test_bug1063538.html] +skip-if = + http3 +[test_bug1104064.html] +[test_bug1132395.html] +skip-if = true # bug 1176225 +[test_bug1132924.html] +[test_chromeWorker.html] +[test_clearTimeouts.html] +[test_clearTimeoutsImplicit.html] +[test_console.html] +[test_consoleAndBlobs.html] +[test_consoleReplaceable.html] +[test_contentWorker.html] +[test_csp.html] +[test_dataURLWorker.html] +[test_dynamicImport.html] +[test_dynamicImport_early_termination.html] +[test_dynamicImport_and_terminate.html] +support-files = worker_dynamicImport.mjs +[test_errorPropagation.html] +skip-if = + http3 +[test_errorwarning.html] +[test_eventDispatch.html] +[test_fibonacci.html] +[test_importScripts.html] +[test_importScripts_mixedcontent.html] +tags = mcb +[test_instanceof.html] +[test_json.html] +[test_loadEncoding.html] +[test_loadError.html] +[test_location.html] +skip-if = + http3 +[test_longThread.html] +[test_multi_sharedWorker.html] +skip-if = + http3 +[test_multi_sharedWorker_lifetimes_nobfcache.html] +[test_multi_sharedWorker_lifetimes_bfcache.html] +[test_navigator.html] +support-files = + test_navigator.js + test_navigator_iframe.html + test_navigator_iframe.js +skip-if = + http3 +[test_navigator_secureContext.html] +scheme=https +support-files = + test_navigator.js + test_navigator_iframe.html + test_navigator_iframe.js +[test_navigator_languages.html] +[test_newError.html] +[test_notification.html] +[test_notification_child.html] +[test_notification_permission.html] +[test_onLine.html] +[test_promise.html] +[test_promise_resolved_with_string.html] +[test_recursion.html] +[test_recursiveOnerror.html] +skip-if = + http3 +[test_resolveWorker.html] +[test_resolveWorker-assignment.html] +[test_rvals.html] +[test_sharedWorker.html] +[test_sharedWorker_thirdparty.html] +support-files = + sharedWorker_thirdparty_frame.html + sharedWorker_thirdparty_window.html +skip-if = + http3 +[test_simpleThread.html] +skip-if = + http3 +[test_suspend.html] +[test_terminate.html] +[test_threadErrors.html] +[test_threadTimeouts.html] +[test_throwingOnerror.html] +[test_timeoutTracing.html] +[test_transferable.html] +[test_worker_interfaces.html] +skip-if = + http3 +[test_worker_interfaces_secureContext.html] +scheme=https +[test_referrer.html] +[test_referrer_header_worker.html] +skip-if = + http3 +[test_importScripts_3rdparty.html] +skip-if = + http3 +[test_sharedWorker_ports.html] +[test_sharedWorker_lifetime.html] +[test_navigator_workers_hardwareConcurrency.html] +[test_bug1278777.html] +[test_setTimeoutWith0.html] +[test_bug1301094.html] +[test_subworkers_suspended.html] +scheme=https +[test_bug1317725.html] +support-files = test_bug1317725.js +[test_bug1824498.html] +support-files = worker_bug1824498.mjs +[test_sharedworker_event_listener_leaks.html] +skip-if = + (bits == 64 && os == 'linux' && asan && !debug) # Disabled on Linux64 opt asan, bug 1493563 + os == "win" && debug && xorigin # high frequency intermittent +[test_fileReaderSync_when_closing.html] diff --git a/dom/workers/test/multi_sharedWorker_frame.html b/dom/workers/test/multi_sharedWorker_frame.html new file mode 100644 index 0000000000..94866b918d --- /dev/null +++ b/dom/workers/test/multi_sharedWorker_frame.html @@ -0,0 +1,58 @@ + + + + + Test for SharedWorker + + + + + diff --git a/dom/workers/test/multi_sharedWorker_frame_bfcache.html b/dom/workers/test/multi_sharedWorker_frame_bfcache.html new file mode 100644 index 0000000000..dea14a8f78 --- /dev/null +++ b/dom/workers/test/multi_sharedWorker_frame_bfcache.html @@ -0,0 +1,13 @@ + + + + + Test for SharedWorker + + + + + diff --git a/dom/workers/test/multi_sharedWorker_frame_nobfcache.html b/dom/workers/test/multi_sharedWorker_frame_nobfcache.html new file mode 100644 index 0000000000..dea14a8f78 --- /dev/null +++ b/dom/workers/test/multi_sharedWorker_frame_nobfcache.html @@ -0,0 +1,13 @@ + + + + + Test for SharedWorker + + + + + diff --git a/dom/workers/test/multi_sharedWorker_frame_nobfcache.html^headers^ b/dom/workers/test/multi_sharedWorker_frame_nobfcache.html^headers^ new file mode 100644 index 0000000000..2567dc2fe5 --- /dev/null +++ b/dom/workers/test/multi_sharedWorker_frame_nobfcache.html^headers^ @@ -0,0 +1 @@ +Cache-Control: no-store \ No newline at end of file diff --git a/dom/workers/test/multi_sharedWorker_manager.js b/dom/workers/test/multi_sharedWorker_manager.js new file mode 100644 index 0000000000..c0a9455ef1 --- /dev/null +++ b/dom/workers/test/multi_sharedWorker_manager.js @@ -0,0 +1,58 @@ +var query = window.location.search; +var bc = new BroadcastChannel("bugSharedWorkerLiftetime" + query); +bc.onmessage = msgEvent => { + var msg = msgEvent.data; + var command = msg.command; + if (command == "postToWorker") { + postToWorker(msg.workerMessage); + } else if (command == "navigate") { + window.location = msg.location; + } else if (command == "finish") { + bc.postMessage({ command: "finished" }); + bc.close(); + window.close(); + } +}; + +window.onload = () => { + bc.postMessage({ command: "loaded" }); +}; + +function debug(message) { + if (typeof message != "string") { + throw new Error("debug() only accepts strings!"); + } + bc.postMessage({ command: "debug", message }); +} + +let worker; + +function postToWorker(msg) { + if (!worker) { + worker = new SharedWorker( + "multi_sharedWorker_sharedWorker.js", + "FrameWorker" + ); + worker.onerror = function (error) { + debug("Worker error: " + error.message); + error.preventDefault(); + + let data = { + type: "error", + message: error.message, + filename: error.filename, + lineno: error.lineno, + isErrorEvent: error instanceof ErrorEvent, + }; + bc.postMessage({ command: "fromWorker", workerMessage: data }); + }; + + worker.port.onmessage = function (message) { + debug("Worker message: " + JSON.stringify(message.data)); + bc.postMessage({ command: "fromWorker", workerMessage: message.data }); + }; + } + + debug("Posting message: " + JSON.stringify(msg)); + worker.port.postMessage(msg); +} diff --git a/dom/workers/test/multi_sharedWorker_sharedWorker.js b/dom/workers/test/multi_sharedWorker_sharedWorker.js new file mode 100644 index 0000000000..ec0b459d55 --- /dev/null +++ b/dom/workers/test/multi_sharedWorker_sharedWorker.js @@ -0,0 +1,72 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +"use strict"; + +if (self.name != "FrameWorker") { + throw new Error("Bad worker name: " + self.name); +} + +var registeredPorts = []; +var errorCount = 0; +var storedData; + +self.onconnect = function (event) { + var port = event.ports[0]; + + if (registeredPorts.length) { + let data = { + type: "connect", + }; + + registeredPorts.forEach(function (registeredPort) { + registeredPort.postMessage(data); + }); + } + + port.onmessage = function (msg) { + switch (msg.data.command) { + case "start": + break; + + case "error": + throw new Error("Expected"); + + case "store": + storedData = msg.data.data; + break; + + case "retrieve": + var data = { + type: "result", + data: storedData, + }; + port.postMessage(data); + break; + + default: + throw new Error("Unknown command '" + error.data.command + "'"); + } + }; + + registeredPorts.push(port); +}; + +self.onerror = function (message, filename, lineno) { + if (!errorCount++) { + var data = { + type: "worker-error", + message, + filename, + lineno, + }; + + registeredPorts.forEach(function (registeredPort) { + registeredPort.postMessage(data); + }); + + // Prevent the error from propagating the first time only. + return true; + } +}; diff --git a/dom/workers/test/navigate.html b/dom/workers/test/navigate.html new file mode 100644 index 0000000000..e38ab4540a --- /dev/null +++ b/dom/workers/test/navigate.html @@ -0,0 +1,13 @@ + + diff --git a/dom/workers/test/navigator_languages_worker.js b/dom/workers/test/navigator_languages_worker.js new file mode 100644 index 0000000000..22ece09ef2 --- /dev/null +++ b/dom/workers/test/navigator_languages_worker.js @@ -0,0 +1,11 @@ +var active = true; +onmessage = function (e) { + if (e.data == "finish") { + active = false; + return; + } + + if (active) { + postMessage(navigator.languages); + } +}; diff --git a/dom/workers/test/navigator_worker.js b/dom/workers/test/navigator_worker.js new file mode 100644 index 0000000000..e9379032a6 --- /dev/null +++ b/dom/workers/test/navigator_worker.js @@ -0,0 +1,85 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +// IMPORTANT: Do not change the list below without review from a DOM peer! +var supportedProps = [ + "appCodeName", + "appName", + "appVersion", + "platform", + "product", + "userAgent", + "onLine", + "language", + "languages", + { name: "locks", isSecureContext: true }, + "mediaCapabilities", + "hardwareConcurrency", + { name: "storage", isSecureContext: true }, + "connection", +]; + +self.onmessage = function (event) { + if (!event || !event.data) { + return; + } + + startTest(event.data); +}; + +function startTest(channelData) { + // Prepare the interface map showing if a propery should exist in this build. + // For example, if interfaceMap[foo] = true means navigator.foo should exist. + var interfaceMap = {}; + + for (var prop of supportedProps) { + if (typeof prop === "string") { + interfaceMap[prop] = true; + continue; + } + + if ( + prop.nightly === !channelData.isNightly || + prop.release === !channelData.isRelease || + prop.isSecureContext === !isSecureContext + ) { + interfaceMap[prop.name] = false; + continue; + } + + interfaceMap[prop.name] = true; + } + + for (var prop in navigator) { + // Make sure the list is current! + if (!interfaceMap[prop]) { + throw "Navigator has the '" + prop + "' property that isn't in the list!"; + } + } + + var obj; + + for (var prop in interfaceMap) { + // Skip the property that is not supposed to exist in this build. + if (!interfaceMap[prop]) { + continue; + } + + if (typeof navigator[prop] == "undefined") { + throw "Navigator has no '" + prop + "' property!"; + } + + obj = { name: prop }; + obj.value = navigator[prop]; + + postMessage(JSON.stringify(obj)); + } + + obj = { + name: "testFinished", + }; + + postMessage(JSON.stringify(obj)); +} diff --git a/dom/workers/test/newError_worker.js b/dom/workers/test/newError_worker.js new file mode 100644 index 0000000000..46e6226f73 --- /dev/null +++ b/dom/workers/test/newError_worker.js @@ -0,0 +1,5 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +throw new Error("foo!"); diff --git a/dom/workers/test/notification_permission_worker.js b/dom/workers/test/notification_permission_worker.js new file mode 100644 index 0000000000..0e6b96d975 --- /dev/null +++ b/dom/workers/test/notification_permission_worker.js @@ -0,0 +1,59 @@ +function info(message) { + dump("INFO: " + message + "\n"); +} + +function ok(test, message) { + postMessage({ type: "ok", test, message }); +} + +function is(a, b, message) { + postMessage({ type: "is", test1: a, test2: b, message }); +} + +if (self.Notification) { + var steps = [ + function (done) { + info("Test notification permission"); + ok(typeof Notification === "function", "Notification constructor exists"); + ok( + Notification.permission === "denied", + "Notification.permission is denied." + ); + + var n = new Notification("Hello"); + n.onerror = function (e) { + ok(true, "error called due to permission denied."); + done(); + }; + }, + ]; + + onmessage = function (e) { + var context = {}; + (function executeRemainingTests(remainingTests) { + if (!remainingTests.length) { + postMessage({ type: "finish" }); + return; + } + + var nextTest = remainingTests.shift(); + var finishTest = executeRemainingTests.bind(null, remainingTests); + var startTest = nextTest.call.bind(nextTest, context, finishTest); + + try { + startTest(); + // if no callback was defined for test function, + // we must manually invoke finish to continue + if (nextTest.length === 0) { + finishTest(); + } + } catch (ex) { + ok(false, "Test threw exception! " + nextTest + " " + ex); + finishTest(); + } + })(steps); + }; +} else { + ok(true, "Notifications are not enabled in workers on the platform."); + postMessage({ type: "finish" }); +} diff --git a/dom/workers/test/notification_worker.js b/dom/workers/test/notification_worker.js new file mode 100644 index 0000000000..87aa02ac05 --- /dev/null +++ b/dom/workers/test/notification_worker.js @@ -0,0 +1,104 @@ +function ok(test, message) { + postMessage({ type: "ok", test, message }); +} + +function is(a, b, message) { + postMessage({ type: "is", test1: a, test2: b, message }); +} + +if (self.Notification) { + var steps = [ + function () { + ok(typeof Notification === "function", "Notification constructor exists"); + ok(Notification.permission, "Notification.permission exists"); + ok( + typeof Notification.requestPermission === "undefined", + "Notification.requestPermission should not exist" + ); + }, + + function (done) { + var options = { + dir: "auto", + lang: "", + body: "This is a notification body", + tag: "sometag", + icon: "icon.png", + data: ["a complex object that should be", { structured: "cloned" }], + mozbehavior: { vibrationPattern: [30, 200, 30] }, + }; + var notification = new Notification("This is a title", options); + + ok(notification !== undefined, "Notification exists"); + is(notification.onclick, null, "onclick() should be null"); + is(notification.onshow, null, "onshow() should be null"); + is(notification.onerror, null, "onerror() should be null"); + is(notification.onclose, null, "onclose() should be null"); + is(typeof notification.close, "function", "close() should exist"); + + is(notification.dir, options.dir, "auto should get set"); + is(notification.lang, options.lang, "lang should get set"); + is(notification.body, options.body, "body should get set"); + is(notification.tag, options.tag, "tag should get set"); + is(notification.icon, options.icon, "icon should get set"); + is( + notification.data[0], + "a complex object that should be", + "data item 0 should be a matching string" + ); + is( + notification.data[1].structured, + "cloned", + "data item 1 should be a matching object literal" + ); + + // store notification in test context + this.notification = notification; + + notification.onshow = function () { + ok(true, "onshow handler should be called"); + done(); + }; + }, + + function (done) { + var notification = this.notification; + + notification.onclose = function () { + ok(true, "onclose handler should be called"); + done(); + }; + + notification.close(); + }, + ]; + + onmessage = function (e) { + var context = {}; + (function executeRemainingTests(remainingTests) { + if (!remainingTests.length) { + postMessage({ type: "finish" }); + return; + } + + var nextTest = remainingTests.shift(); + var finishTest = executeRemainingTests.bind(null, remainingTests); + var startTest = nextTest.call.bind(nextTest, context, finishTest); + + try { + startTest(); + // if no callback was defined for test function, + // we must manually invoke finish to continue + if (nextTest.length === 0) { + finishTest(); + } + } catch (ex) { + ok(false, "Test threw exception! " + nextTest + " " + ex); + finishTest(); + } + })(steps); + }; +} else { + ok(true, "Notifications are not enabled in workers on the platform."); + postMessage({ type: "finish" }); +} diff --git a/dom/workers/test/notification_worker_child-child.js b/dom/workers/test/notification_worker_child-child.js new file mode 100644 index 0000000000..236e314e47 --- /dev/null +++ b/dom/workers/test/notification_worker_child-child.js @@ -0,0 +1,93 @@ +function ok(test, message) { + postMessage({ type: "ok", test, message }); +} + +function is(a, b, message) { + postMessage({ type: "is", test1: a, test2: b, message }); +} + +if (self.Notification) { + var steps = [ + function () { + ok(typeof Notification === "function", "Notification constructor exists"); + ok(Notification.permission, "Notification.permission exists"); + ok( + typeof Notification.requestPermission === "undefined", + "Notification.requestPermission should not exist" + ); + //ok(typeof Notification.get === "function", "Notification.get exists"); + }, + + function (done) { + var options = { + dir: "auto", + lang: "", + body: "This is a notification body", + tag: "sometag", + icon: "icon.png", + }; + var notification = new Notification("This is a title", options); + + ok(notification !== undefined, "Notification exists"); + is(notification.onclick, null, "onclick() should be null"); + is(notification.onshow, null, "onshow() should be null"); + is(notification.onerror, null, "onerror() should be null"); + is(notification.onclose, null, "onclose() should be null"); + is(typeof notification.close, "function", "close() should exist"); + + is(notification.dir, options.dir, "auto should get set"); + is(notification.lang, options.lang, "lang should get set"); + is(notification.body, options.body, "body should get set"); + is(notification.tag, options.tag, "tag should get set"); + is(notification.icon, options.icon, "icon should get set"); + + // store notification in test context + this.notification = notification; + + notification.onshow = function () { + ok(true, "onshow handler should be called"); + done(); + }; + }, + + function (done) { + var notification = this.notification; + + notification.onclose = function () { + ok(true, "onclose handler should be called"); + done(); + }; + + notification.close(); + }, + ]; + + onmessage = function (e) { + var context = {}; + (function executeRemainingTests(remainingTests) { + if (!remainingTests.length) { + postMessage({ type: "finish" }); + return; + } + + var nextTest = remainingTests.shift(); + var finishTest = executeRemainingTests.bind(null, remainingTests); + var startTest = nextTest.call.bind(nextTest, context, finishTest); + + try { + startTest(); + // if no callback was defined for test function, + // we must manually invoke finish to continue + if (nextTest.length === 0) { + finishTest(); + } + } catch (ex) { + ok(false, "Test threw exception! " + nextTest + " " + ex); + finishTest(); + } + })(steps); + }; +} else { + ok(true, "Notifications are not enabled in workers on the platform."); + postMessage({ type: "finish" }); +} diff --git a/dom/workers/test/notification_worker_child-parent.js b/dom/workers/test/notification_worker_child-parent.js new file mode 100644 index 0000000000..45dd061993 --- /dev/null +++ b/dom/workers/test/notification_worker_child-parent.js @@ -0,0 +1,26 @@ +function ok(test, message) { + postMessage({ type: "ok", test, message }); +} + +function is(a, b, message) { + postMessage({ type: "is", test1: a, test2: b, message }); +} + +if (self.Notification) { + var child = new Worker("notification_worker_child-child.js"); + child.onerror = function (e) { + ok(false, "Error loading child worker " + e); + postMessage({ type: "finish" }); + }; + + child.onmessage = function (e) { + postMessage(e.data); + }; + + onmessage = function (e) { + child.postMessage("start"); + }; +} else { + ok(true, "Notifications are not enabled in workers on the platform."); + postMessage({ type: "finish" }); +} diff --git a/dom/workers/test/onLine_worker.js b/dom/workers/test/onLine_worker.js new file mode 100644 index 0000000000..94c10c699c --- /dev/null +++ b/dom/workers/test/onLine_worker.js @@ -0,0 +1,70 @@ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ + */ + +importScripts("onLine_worker_head.js"); + +var N_CHILDREN = 3; +var children = []; +var finishedChildrenCount = 0; +var lastTest = false; + +for (var event of ["online", "offline"]) { + addEventListener( + event, + makeHandler( + "addEventListener('%1', ..., false)", + event, + 1, + "Parent Worker" + ), + false + ); +} + +onmessage = function (e) { + if (e.data === "lastTest") { + children.forEach(function (w) { + w.postMessage({ type: "lastTest" }); + }); + lastTest = true; + } +}; + +function setupChildren(cb) { + var readyCount = 0; + for (var i = 0; i < N_CHILDREN; ++i) { + var w = new Worker("onLine_worker_child.js"); + children.push(w); + + w.onerror = function (e) { + info("Error creating child " + e.message); + }; + + w.onmessage = function (e) { + if (e.data.type === "ready") { + info("Got ready from child"); + readyCount++; + if (readyCount === N_CHILDREN) { + cb(); + } + } else if (e.data.type === "finished") { + finishedChildrenCount++; + + if (lastTest && finishedChildrenCount === N_CHILDREN) { + postMessage({ type: "finished" }); + children = []; + close(); + } + } else if (e.data.type === "ok") { + // Pass on test to page. + postMessage(e.data); + } + }; + } +} + +setupChildren(function () { + postMessage({ type: "ready" }); +}); diff --git a/dom/workers/test/onLine_worker_child.js b/dom/workers/test/onLine_worker_child.js new file mode 100644 index 0000000000..92542c018f --- /dev/null +++ b/dom/workers/test/onLine_worker_child.js @@ -0,0 +1,91 @@ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ + */ + +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ + */ + +function info(text) { + dump("Test for Bug 925437: worker: " + text + "\n"); +} + +function ok(test, message) { + postMessage({ type: "ok", test, message }); +} + +/** + * Returns a handler function for an online/offline event. The returned handler + * ensures the passed event object has expected properties and that the handler + * is called at the right moment (according to the gState variable). + * @param nameTemplate The string identifying the hanlder. '%1' in that + * string will be replaced with the event name. + * @param eventName 'online' or 'offline' + * @param expectedState value of gState at the moment the handler is called. + * The handler increases gState by one before checking + * if it matches expectedState. + */ +function makeHandler(nameTemplate, eventName, expectedState, prefix, custom) { + prefix += ": "; + return function (e) { + var name = nameTemplate.replace(/%1/, eventName); + ok(e.constructor == Event, prefix + "event should be an Event"); + ok(e.type == eventName, prefix + "event type should be " + eventName); + ok(!e.bubbles, prefix + "event should not bubble"); + ok(!e.cancelable, prefix + "event should not be cancelable"); + ok( + e.target == self, + prefix + "the event target should be the worker scope" + ); + ok( + eventName == "online" ? navigator.onLine : !navigator.onLine, + prefix + + "navigator.onLine " + + navigator.onLine + + " should reflect event " + + eventName + ); + + if (custom) { + custom(); + } + }; +} + +var lastTest = false; + +function lastTestTest() { + if (lastTest) { + postMessage({ type: "finished" }); + close(); + } +} + +for (var event of ["online", "offline"]) { + addEventListener( + event, + makeHandler( + "addEventListener('%1', ..., false)", + event, + 1, + "Child Worker", + lastTestTest + ), + false + ); +} + +onmessage = function (e) { + if (e.data.type === "lastTest") { + lastTest = true; + } else if (e.data.type === "navigatorState") { + ok( + e.data.state === navigator.onLine, + "Child and parent navigator state should match" + ); + } +}; + +postMessage({ type: "ready" }); diff --git a/dom/workers/test/onLine_worker_head.js b/dom/workers/test/onLine_worker_head.js new file mode 100644 index 0000000000..632821b1f4 --- /dev/null +++ b/dom/workers/test/onLine_worker_head.js @@ -0,0 +1,50 @@ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ + */ + +function info(text) { + dump("Test for Bug 925437: worker: " + text + "\n"); +} + +function ok(test, message) { + postMessage({ type: "ok", test, message }); +} + +/** + * Returns a handler function for an online/offline event. The returned handler + * ensures the passed event object has expected properties and that the handler + * is called at the right moment (according to the gState variable). + * @param nameTemplate The string identifying the hanlder. '%1' in that + * string will be replaced with the event name. + * @param eventName 'online' or 'offline' + * @param expectedState value of gState at the moment the handler is called. + * The handler increases gState by one before checking + * if it matches expectedState. + */ +function makeHandler(nameTemplate, eventName, expectedState, prefix, custom) { + prefix += ": "; + return function (e) { + var name = nameTemplate.replace(/%1/, eventName); + ok(e.constructor == Event, prefix + "event should be an Event"); + ok(e.type == eventName, prefix + "event type should be " + eventName); + ok(!e.bubbles, prefix + "event should not bubble"); + ok(!e.cancelable, prefix + "event should not be cancelable"); + ok( + e.target == self, + prefix + "the event target should be the worker scope" + ); + ok( + eventName == "online" ? navigator.onLine : !navigator.onLine, + prefix + + "navigator.onLine " + + navigator.onLine + + " should reflect event " + + eventName + ); + + if (custom) { + custom(); + } + }; +} diff --git a/dom/workers/test/promise_worker.js b/dom/workers/test/promise_worker.js new file mode 100644 index 0000000000..fd4a9177d6 --- /dev/null +++ b/dom/workers/test/promise_worker.js @@ -0,0 +1,1007 @@ +"use strict"; + +function ok(a, msg) { + dump("OK: " + !!a + " => " + a + " " + msg + "\n"); + postMessage({ type: "status", status: !!a, msg: a + ": " + msg }); +} + +function is(a, b, msg) { + dump("IS: " + (a === b) + " => " + a + " | " + b + " " + msg + "\n"); + postMessage({ + type: "status", + status: a === b, + msg: a + " === " + b + ": " + msg, + }); +} + +function isnot(a, b, msg) { + dump("ISNOT: " + (a !== b) + " => " + a + " | " + b + " " + msg + "\n"); + postMessage({ + type: "status", + status: a !== b, + msg: a + " !== " + b + ": " + msg, + }); +} + +function promiseResolve() { + ok(Promise, "Promise object should exist"); + + var promise = new Promise(function (resolve, reject) { + ok(resolve, "Promise.resolve exists"); + ok(reject, "Promise.reject exists"); + + resolve(42); + }).then( + function (what) { + ok(true, "Then - resolveCb has been called"); + is(what, 42, "ResolveCb received 42"); + runTest(); + }, + function () { + ok(false, "Then - rejectCb has been called"); + runTest(); + } + ); +} + +function promiseResolveNoArg() { + var promise = new Promise(function (resolve, reject) { + ok(resolve, "Promise.resolve exists"); + ok(reject, "Promise.reject exists"); + + resolve(); + }).then( + function (what) { + ok(true, "Then - resolveCb has been called"); + is(what, undefined, "ResolveCb received undefined"); + runTest(); + }, + function () { + ok(false, "Then - rejectCb has been called"); + runTest(); + } + ); +} + +function promiseRejectNoHandler() { + // This test only checks that the code that reports unhandled errors in the + // Promises implementation does not crash or leak. + var promise = new Promise(function (res, rej) { + noSuchMethod(); + }); + runTest(); +} + +function promiseReject() { + var promise = new Promise(function (resolve, reject) { + reject(42); + }).then( + function (what) { + ok(false, "Then - resolveCb has been called"); + runTest(); + }, + function (what) { + ok(true, "Then - rejectCb has been called"); + is(what, 42, "RejectCb received 42"); + runTest(); + } + ); +} + +function promiseRejectNoArg() { + var promise = new Promise(function (resolve, reject) { + reject(); + }).then( + function (what) { + ok(false, "Then - resolveCb has been called"); + runTest(); + }, + function (what) { + ok(true, "Then - rejectCb has been called"); + is(what, undefined, "RejectCb received undefined"); + runTest(); + } + ); +} + +function promiseException() { + var promise = new Promise(function (resolve, reject) { + throw 42; + }).then( + function (what) { + ok(false, "Then - resolveCb has been called"); + runTest(); + }, + function (what) { + ok(true, "Then - rejectCb has been called"); + is(what, 42, "RejectCb received 42"); + runTest(); + } + ); +} + +function promiseAsync_TimeoutResolveThen() { + var handlerExecuted = false; + + setTimeout(function () { + ok(handlerExecuted, "Handler should have been called before the timeout."); + + // Allow other assertions to run so the test could fail before the next one. + setTimeout(runTest, 0); + }, 0); + + Promise.resolve().then(function () { + handlerExecuted = true; + }); + + ok(!handlerExecuted, "Handlers are not called before 'then' returns."); +} + +function promiseAsync_ResolveTimeoutThen() { + var handlerExecuted = false; + + var promise = Promise.resolve(); + + setTimeout(function () { + ok(handlerExecuted, "Handler should have been called before the timeout."); + + // Allow other assertions to run so the test could fail before the next one. + setTimeout(runTest, 0); + }, 0); + + promise.then(function () { + handlerExecuted = true; + }); + + ok(!handlerExecuted, "Handlers are not called before 'then' returns."); +} + +function promiseAsync_ResolveThenTimeout() { + var handlerExecuted = false; + + Promise.resolve().then(function () { + handlerExecuted = true; + }); + + setTimeout(function () { + ok(handlerExecuted, "Handler should have been called before the timeout."); + + // Allow other assertions to run so the test could fail before the next one. + setTimeout(runTest, 0); + }, 0); + + ok(!handlerExecuted, "Handlers are not called before 'then' returns."); +} + +function promiseAsync_SyncXHRAndImportScripts() { + var handlerExecuted = false; + + Promise.resolve().then(function () { + handlerExecuted = true; + + // Allow other assertions to run so the test could fail before the next one. + setTimeout(runTest, 0); + }); + + ok(!handlerExecuted, "Handlers are not called until the next microtask."); + + var xhr = new XMLHttpRequest(); + xhr.open("GET", "testXHR.txt", false); + xhr.send(null); + + ok(!handlerExecuted, "Sync XHR should not trigger microtask execution."); + + importScripts("../../../dom/xhr/tests/relativeLoad_import.js"); + + ok(!handlerExecuted, "importScripts should not trigger microtask execution."); +} + +function promiseDoubleThen() { + var steps = 0; + var promise = new Promise(function (r1, r2) { + r1(42); + }); + + promise.then( + function (what) { + ok(true, "Then.resolve has been called"); + is(what, 42, "Value == 42"); + steps++; + }, + function (what) { + ok(false, "Then.reject has been called"); + } + ); + + promise.then( + function (what) { + ok(true, "Then.resolve has been called"); + is(steps, 1, "Then.resolve - step == 1"); + is(what, 42, "Value == 42"); + runTest(); + }, + function (what) { + ok(false, "Then.reject has been called"); + } + ); +} + +function promiseThenException() { + var promise = new Promise(function (resolve, reject) { + resolve(42); + }); + + promise + .then(function (what) { + ok(true, "Then.resolve has been called"); + throw "booh"; + }) + .catch(function (e) { + ok(true, "Catch has been called!"); + runTest(); + }); +} + +function promiseThenCatchThen() { + var promise = new Promise(function (resolve, reject) { + resolve(42); + }); + + var promise2 = promise.then( + function (what) { + ok(true, "Then.resolve has been called"); + is(what, 42, "Value == 42"); + return what + 1; + }, + function (what) { + ok(false, "Then.reject has been called"); + } + ); + + isnot(promise, promise2, "These 2 promise objs are different"); + + promise2 + .then( + function (what) { + ok(true, "Then.resolve has been called"); + is(what, 43, "Value == 43"); + return what + 1; + }, + function (what) { + ok(false, "Then.reject has been called"); + } + ) + .catch(function () { + ok(false, "Catch has been called"); + }) + .then( + function (what) { + ok(true, "Then.resolve has been called"); + is(what, 44, "Value == 44"); + runTest(); + }, + function (what) { + ok(false, "Then.reject has been called"); + } + ); +} + +function promiseRejectThenCatchThen() { + var promise = new Promise(function (resolve, reject) { + reject(42); + }); + + var promise2 = promise.then( + function (what) { + ok(false, "Then.resolve has been called"); + }, + function (what) { + ok(true, "Then.reject has been called"); + is(what, 42, "Value == 42"); + return what + 1; + } + ); + + isnot(promise, promise2, "These 2 promise objs are different"); + + promise2 + .then(function (what) { + ok(true, "Then.resolve has been called"); + is(what, 43, "Value == 43"); + return what + 1; + }) + .catch(function (what) { + ok(false, "Catch has been called"); + }) + .then(function (what) { + ok(true, "Then.resolve has been called"); + is(what, 44, "Value == 44"); + runTest(); + }); +} + +function promiseRejectThenCatchThen2() { + var promise = new Promise(function (resolve, reject) { + reject(42); + }); + + promise + .then(function (what) { + ok(true, "Then.resolve has been called"); + is(what, 42, "Value == 42"); + return what + 1; + }) + .catch(function (what) { + is(what, 42, "Value == 42"); + ok(true, "Catch has been called"); + return what + 1; + }) + .then(function (what) { + ok(true, "Then.resolve has been called"); + is(what, 43, "Value == 43"); + runTest(); + }); +} + +function promiseRejectThenCatchExceptionThen() { + var promise = new Promise(function (resolve, reject) { + reject(42); + }); + + promise + .then( + function (what) { + ok(false, "Then.resolve has been called"); + }, + function (what) { + ok(true, "Then.reject has been called"); + is(what, 42, "Value == 42"); + throw what + 1; + } + ) + .catch(function (what) { + ok(true, "Catch has been called"); + is(what, 43, "Value == 43"); + return what + 1; + }) + .then(function (what) { + ok(true, "Then.resolve has been called"); + is(what, 44, "Value == 44"); + runTest(); + }); +} + +function promiseThenCatchOrderingResolve() { + var global = 0; + var f = new Promise(function (r1, r2) { + r1(42); + }); + + f.then(function () { + f.then(function () { + global++; + }); + f.catch(function () { + global++; + }); + f.then(function () { + global++; + }); + setTimeout(function () { + is(global, 2, "Many steps... should return 2"); + runTest(); + }, 0); + }); +} + +function promiseThenCatchOrderingReject() { + var global = 0; + var f = new Promise(function (r1, r2) { + r2(42); + }); + + f.then( + function () {}, + function () { + f.then(function () { + global++; + }); + f.catch(function () { + global++; + }); + f.then( + function () {}, + function () { + global++; + } + ); + setTimeout(function () { + is(global, 2, "Many steps... should return 2"); + runTest(); + }, 0); + } + ); +} + +function promiseThenNoArg() { + var promise = new Promise(function (resolve, reject) { + resolve(42); + }); + + var clone = promise.then(); + isnot(promise, clone, "These 2 promise objs are different"); + promise.then(function (v) { + clone.then(function (cv) { + is(v, cv, "Both resolve to the same value"); + runTest(); + }); + }); +} + +function promiseThenUndefinedResolveFunction() { + var promise = new Promise(function (resolve, reject) { + reject(42); + }); + + try { + promise.then(undefined, function (v) { + is(v, 42, "Promise rejected with 42"); + runTest(); + }); + } catch (e) { + ok(false, "then should not throw on undefined resolve function"); + } +} + +function promiseThenNullResolveFunction() { + var promise = new Promise(function (resolve, reject) { + reject(42); + }); + + try { + promise.then(null, function (v) { + is(v, 42, "Promise rejected with 42"); + runTest(); + }); + } catch (e) { + ok(false, "then should not throw on null resolve function"); + } +} + +function promiseCatchNoArg() { + var promise = new Promise(function (resolve, reject) { + reject(42); + }); + + var clone = promise.catch(); + isnot(promise, clone, "These 2 promise objs are different"); + promise.catch(function (v) { + clone.catch(function (cv) { + is(v, cv, "Both reject to the same value"); + runTest(); + }); + }); +} + +function promiseNestedPromise() { + new Promise(function (resolve, reject) { + resolve( + new Promise(function (r) { + ok(true, "Nested promise is executed"); + r(42); + }) + ); + }).then(function (value) { + is(value, 42, "Nested promise is executed and then == 42"); + runTest(); + }); +} + +function promiseNestedNestedPromise() { + new Promise(function (resolve, reject) { + resolve( + new Promise(function (r) { + ok(true, "Nested promise is executed"); + r(42); + }).then(function (what) { + return what + 1; + }) + ); + }).then(function (value) { + is(value, 43, "Nested promise is executed and then == 43"); + runTest(); + }); +} + +function promiseWrongNestedPromise() { + new Promise(function (resolve, reject) { + resolve( + new Promise(function (r, r2) { + ok(true, "Nested promise is executed"); + r(42); + }) + ); + reject(42); + }).then( + function (value) { + is(value, 42, "Nested promise is executed and then == 42"); + runTest(); + }, + function (value) { + ok(false, "This is wrong"); + } + ); +} + +function promiseLoop() { + new Promise(function (resolve, reject) { + resolve( + new Promise(function (r1, r2) { + ok(true, "Nested promise is executed"); + r1( + new Promise(function (r3, r4) { + ok(true, "Nested nested promise is executed"); + r3(42); + }) + ); + }) + ); + }).then( + function (value) { + is(value, 42, "Nested nested promise is executed and then == 42"); + runTest(); + }, + function (value) { + ok(false, "This is wrong"); + } + ); +} + +function promiseStaticReject() { + var promise = Promise.reject(42).then( + function (what) { + ok(false, "This should not be called"); + }, + function (what) { + is(what, 42, "Value == 42"); + runTest(); + } + ); +} + +function promiseStaticResolve() { + var promise = Promise.resolve(42).then( + function (what) { + is(what, 42, "Value == 42"); + runTest(); + }, + function () { + ok(false, "This should not be called"); + } + ); +} + +function promiseResolveNestedPromise() { + var promise = Promise.resolve( + new Promise( + function (r, r2) { + ok(true, "Nested promise is executed"); + r(42); + }, + function () { + ok(false, "This should not be called"); + } + ) + ).then( + function (what) { + is(what, 42, "Value == 42"); + runTest(); + }, + function () { + ok(false, "This should not be called"); + } + ); +} + +function promiseUtilitiesDefined() { + ok(Promise.all, "Promise.all must be defined when Promise is enabled."); + ok(Promise.race, "Promise.race must be defined when Promise is enabled."); + runTest(); +} + +function promiseAllArray() { + var p = Promise.all([1, new Date(), Promise.resolve("firefox")]); + ok(p instanceof Promise, "Return value of Promise.all should be a Promise."); + p.then( + function (values) { + ok(Array.isArray(values), "Resolved value should be an array."); + is( + values.length, + 3, + "Resolved array length should match iterable's length." + ); + is(values[0], 1, "Array values should match."); + ok(values[1] instanceof Date, "Array values should match."); + is(values[2], "firefox", "Array values should match."); + runTest(); + }, + function () { + ok( + false, + "Promise.all shouldn't fail when iterable has no rejected Promises." + ); + runTest(); + } + ); +} + +function promiseAllWaitsForAllPromises() { + var arr = [ + new Promise(function (resolve) { + setTimeout(resolve.bind(undefined, 1), 50); + }), + new Promise(function (resolve) { + setTimeout(resolve.bind(undefined, 2), 10); + }), + new Promise(function (resolve) { + setTimeout( + resolve.bind( + undefined, + new Promise(function (resolve2) { + resolve2(3); + }) + ), + 10 + ); + }), + new Promise(function (resolve) { + setTimeout(resolve.bind(undefined, 4), 20); + }), + ]; + + var p = Promise.all(arr); + p.then( + function (values) { + ok(Array.isArray(values), "Resolved value should be an array."); + is( + values.length, + 4, + "Resolved array length should match iterable's length." + ); + is(values[0], 1, "Array values should match."); + is(values[1], 2, "Array values should match."); + is(values[2], 3, "Array values should match."); + is(values[3], 4, "Array values should match."); + runTest(); + }, + function () { + ok( + false, + "Promise.all shouldn't fail when iterable has no rejected Promises." + ); + runTest(); + } + ); +} + +function promiseAllRejectFails() { + var arr = [ + new Promise(function (resolve) { + setTimeout(resolve.bind(undefined, 1), 50); + }), + new Promise(function (resolve, reject) { + setTimeout(reject.bind(undefined, 2), 10); + }), + new Promise(function (resolve) { + setTimeout(resolve.bind(undefined, 3), 10); + }), + new Promise(function (resolve) { + setTimeout(resolve.bind(undefined, 4), 20); + }), + ]; + + var p = Promise.all(arr); + p.then( + function (values) { + ok( + false, + "Promise.all shouldn't resolve when iterable has rejected Promises." + ); + runTest(); + }, + function (e) { + ok( + true, + "Promise.all should reject when iterable has rejected Promises." + ); + is(e, 2, "Rejection value should match."); + runTest(); + } + ); +} + +function promiseRaceEmpty() { + var p = Promise.race([]); + ok(p instanceof Promise, "Should return a Promise."); + // An empty race never resolves! + runTest(); +} + +function promiseRaceValuesArray() { + var p = Promise.race([true, new Date(), 3]); + ok(p instanceof Promise, "Should return a Promise."); + p.then( + function (winner) { + is(winner, true, "First value should win."); + runTest(); + }, + function (err) { + ok(false, "Should not fail " + err + "."); + runTest(); + } + ); +} + +function promiseRacePromiseArray() { + var arr = [ + new Promise(function (resolve) { + resolve("first"); + }), + Promise.resolve("second"), + new Promise(function () {}), + new Promise(function (resolve) { + setTimeout(function () { + setTimeout(function () { + resolve("fourth"); + }, 0); + }, 0); + }), + ]; + + var p = Promise.race(arr); + p.then(function (winner) { + is(winner, "first", "First queued resolution should win the race."); + runTest(); + }); +} + +function promiseRaceReject() { + var p = Promise.race([ + Promise.reject(new Error("Fail bad!")), + new Promise(function (resolve) { + setTimeout(resolve, 0); + }), + ]); + + p.then( + function () { + ok(false, "Should not resolve when winning Promise rejected."); + runTest(); + }, + function (e) { + ok(true, "Should be rejected"); + ok(e instanceof Error, "Should reject with Error."); + ok(e.message == "Fail bad!", "Message should match."); + runTest(); + } + ); +} + +function promiseRaceThrow() { + var p = Promise.race([ + new Promise(function (resolve) { + nonExistent(); + }), + new Promise(function (resolve) { + setTimeout(resolve, 0); + }), + ]); + + p.then( + function () { + ok(false, "Should not resolve when winning Promise had an error."); + runTest(); + }, + function (e) { + ok(true, "Should be rejected"); + ok( + e instanceof ReferenceError, + "Should reject with ReferenceError for function nonExistent()." + ); + runTest(); + } + ); +} + +function promiseResolveArray() { + var p = Promise.resolve([1, 2, 3]); + ok(p instanceof Promise, "Should return a Promise."); + p.then(function (v) { + ok(Array.isArray(v), "Resolved value should be an Array"); + is(v.length, 3, "Length should match"); + is(v[0], 1, "Resolved value should match original"); + is(v[1], 2, "Resolved value should match original"); + is(v[2], 3, "Resolved value should match original"); + runTest(); + }); +} + +function promiseResolveThenable() { + var p = Promise.resolve({ + then(onFulfill, onReject) { + onFulfill(2); + }, + }); + ok(p instanceof Promise, "Should cast to a Promise."); + p.then( + function (v) { + is(v, 2, "Should resolve to 2."); + runTest(); + }, + function (e) { + ok(false, "promiseResolveThenable should've resolved"); + runTest(); + } + ); +} + +function promiseResolvePromise() { + var original = Promise.resolve(true); + var cast = Promise.resolve(original); + + ok(cast instanceof Promise, "Should cast to a Promise."); + is(cast, original, "Should return original Promise."); + cast.then(function (v) { + is(v, true, "Should resolve to true."); + runTest(); + }); +} + +// Bug 1009569. +// Ensure that thenables are run on a clean stack asynchronously. +// Test case adopted from +// https://gist.github.com/getify/d64bb01751b50ed6b281#file-bug1-js. +function promiseResolveThenableCleanStack() { + function immed(s) { + x++; + s(); + } + function incX() { + x++; + } + + var x = 0; + var thenable = { then: immed }; + var results = []; + + var p = Promise.resolve(thenable).then(incX); + results.push(x); + + // check what happens after all "next cycle" steps + // have had a chance to complete + setTimeout(function () { + // Result should be [0, 2] since `thenable` will be called async. + is(results[0], 0, "Expected thenable to be called asynchronously"); + // See Bug 1023547 comment 13 for why this check has to be gated on p. + p.then(function () { + results.push(x); + is(results[1], 2, "Expected thenable to be called asynchronously"); + runTest(); + }); + }, 1000); +} + +// Bug 1062323 +function promiseWrapperAsyncResolution() { + var p = new Promise(function (resolve, reject) { + resolve(); + }); + + var results = []; + var q = p + .then(function () { + results.push("1-1"); + }) + .then(function () { + results.push("1-2"); + }) + .then(function () { + results.push("1-3"); + }); + + var r = p + .then(function () { + results.push("2-1"); + }) + .then(function () { + results.push("2-2"); + }) + .then(function () { + results.push("2-3"); + }); + + Promise.all([q, r]).then( + function () { + var match = + results[0] == "1-1" && + results[1] == "2-1" && + results[2] == "1-2" && + results[3] == "2-2" && + results[4] == "1-3" && + results[5] == "2-3"; + ok(match, "Chained promises should resolve asynchronously."); + runTest(); + }, + function () { + ok(false, "promiseWrapperAsyncResolution: One of the promises failed."); + runTest(); + } + ); +} + +var tests = [ + promiseResolve, + promiseReject, + promiseException, + promiseAsync_TimeoutResolveThen, + promiseAsync_ResolveTimeoutThen, + promiseAsync_ResolveThenTimeout, + promiseAsync_SyncXHRAndImportScripts, + promiseDoubleThen, + promiseThenException, + promiseThenCatchThen, + promiseRejectThenCatchThen, + promiseRejectThenCatchThen2, + promiseRejectThenCatchExceptionThen, + promiseThenCatchOrderingResolve, + promiseThenCatchOrderingReject, + promiseNestedPromise, + promiseNestedNestedPromise, + promiseWrongNestedPromise, + promiseLoop, + promiseStaticReject, + promiseStaticResolve, + promiseResolveNestedPromise, + promiseResolveNoArg, + promiseRejectNoArg, + + promiseThenNoArg, + promiseThenUndefinedResolveFunction, + promiseThenNullResolveFunction, + promiseCatchNoArg, + promiseRejectNoHandler, + + promiseUtilitiesDefined, + + promiseAllArray, + promiseAllWaitsForAllPromises, + promiseAllRejectFails, + + promiseRaceEmpty, + promiseRaceValuesArray, + promiseRacePromiseArray, + promiseRaceReject, + promiseRaceThrow, + + promiseResolveArray, + promiseResolveThenable, + promiseResolvePromise, + + promiseResolveThenableCleanStack, + + promiseWrapperAsyncResolution, +]; + +function runTest() { + if (!tests.length) { + postMessage({ type: "finish" }); + return; + } + + var test = tests.shift(); + test(); +} + +onmessage = function () { + runTest(); +}; diff --git a/dom/workers/test/recursion_worker.js b/dom/workers/test/recursion_worker.js new file mode 100644 index 0000000000..73c86358bc --- /dev/null +++ b/dom/workers/test/recursion_worker.js @@ -0,0 +1,46 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +// This function should never run on a too much recursion error. +onerror = function (event) { + postMessage(event.message); +}; + +// Pure JS recursion +function recurse() { + recurse(); +} + +// JS -> C++ -> JS -> C++ recursion +function recurse2() { + var xhr = new XMLHttpRequest(); + xhr.onreadystatechange = function () { + xhr.open("GET", "nonexistent.file"); + }; + xhr.open("GET", "nonexistent.file"); +} + +var messageCount = 0; +onmessage = function (event) { + switch (++messageCount) { + case 2: + recurse2(); + + // An exception thrown from an event handler like xhr.onreadystatechange + // should not leave an exception pending in the code that generated the + // event. + postMessage("Done"); + return; + + case 1: + recurse(); + throw "Exception should have prevented us from getting here!"; + + default: + throw "Weird number of messages: " + messageCount; + } + + throw "Impossible to get here!"; +}; diff --git a/dom/workers/test/recursiveOnerror_worker.js b/dom/workers/test/recursiveOnerror_worker.js new file mode 100644 index 0000000000..35d2e2b80d --- /dev/null +++ b/dom/workers/test/recursiveOnerror_worker.js @@ -0,0 +1,11 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +onerror = function (message, filename, lineno) { + throw new Error("2"); +}; + +onmessage = function (event) { + throw new Error("1"); +}; diff --git a/dom/workers/test/redirect_to_foreign.sjs b/dom/workers/test/redirect_to_foreign.sjs new file mode 100644 index 0000000000..06fd12052b --- /dev/null +++ b/dom/workers/test/redirect_to_foreign.sjs @@ -0,0 +1,7 @@ +function handleRequest(request, response) { + response.setStatusLine("1.1", 302, "Found"); + response.setHeader( + "Location", + "http://example.org/tests/dom/workers/test/foreign.js" + ); +} diff --git a/dom/workers/test/referrer.sjs b/dom/workers/test/referrer.sjs new file mode 100644 index 0000000000..f91c434c16 --- /dev/null +++ b/dom/workers/test/referrer.sjs @@ -0,0 +1,14 @@ +function handleRequest(request, response) { + if (request.queryString == "result") { + response.write(getState("referer")); + setState("referer", "INVALID"); + } else if (request.queryString == "worker") { + response.setHeader("Content-Type", "text/javascript", false); + response.write("onmessage = function() { postMessage(42); }"); + setState("referer", request.getHeader("referer")); + } else if (request.queryString == "import") { + setState("referer", request.getHeader("referer")); + response.setHeader("Content-Type", "text/javascript", false); + response.write("'hello world'"); + } +} diff --git a/dom/workers/test/referrer_test_server.sjs b/dom/workers/test/referrer_test_server.sjs new file mode 100644 index 0000000000..44ec10c456 --- /dev/null +++ b/dom/workers/test/referrer_test_server.sjs @@ -0,0 +1,99 @@ +Components.utils.importGlobalProperties(["URLSearchParams"]); +const SJS = "referrer_test_server.sjs?"; +const SHARED_KEY = SJS; + +var SAME_ORIGIN = "https://example.com/tests/dom/workers/test/" + SJS; +var CROSS_ORIGIN = "https://test2.example.com/tests/dom/workers/test/" + SJS; +var DOWNGRADE = "http://example.com/tests/dom/workers/test/" + SJS; + +function createUrl(aRequestType, aPolicy) { + var searchParams = new URLSearchParams(); + searchParams.append("ACTION", "request-worker"); + searchParams.append("Referrer-Policy", aPolicy); + searchParams.append("TYPE", aRequestType); + + var url = SAME_ORIGIN; + + if (aRequestType === "cross-origin") { + url = CROSS_ORIGIN; + } else if (aRequestType === "downgrade") { + url = DOWNGRADE; + } + + return url + searchParams.toString(); +} +function createWorker(aRequestType, aPolicy) { + return ` + onmessage = function() { + fetch("${createUrl(aRequestType, aPolicy)}").then(function () { + postMessage(42); + close(); + }); + } + `; +} + +function handleRequest(request, response) { + var params = new URLSearchParams(request.queryString); + var policy = params.get("Referrer-Policy"); + var type = params.get("TYPE"); + var action = params.get("ACTION"); + response.setHeader("Content-Security-Policy", "default-src *", false); + response.setHeader("Access-Control-Allow-Origin", "*", false); + + if (policy) { + response.setHeader("Referrer-Policy", policy, false); + } + + if (action === "test") { + response.setHeader("Content-Type", "text/javascript", false); + response.write(createWorker(type, policy)); + return; + } + + if (action === "resetState") { + setSharedState(SHARED_KEY, "{}"); + response.write(""); + return; + } + + if (action === "get-test-results") { + response.setHeader("Cache-Control", "no-cache", false); + response.setHeader("Content-Type", "text/plain", false); + response.write(getSharedState(SHARED_KEY)); + return; + } + + if (action === "request-worker") { + var result = getSharedState(SHARED_KEY); + result = result ? JSON.parse(result) : {}; + var referrerLevel = "none"; + var test = {}; + + if (request.hasHeader("Referer")) { + var referrer = request.getHeader("Referer"); + if (referrer.indexOf("referrer_test_server") > 0) { + referrerLevel = "full"; + } else if (referrer.indexOf("https://example.com") == 0) { + referrerLevel = "origin"; + } else { + // this is never supposed to happen + referrerLevel = "other-origin"; + } + test.referrer = referrer; + } else { + test.referrer = ""; + } + + test.policy = referrerLevel; + test.expected = policy; + + // test id equals type + "-" + policy + // Ex: same-origin-default + result[type + "-" + policy] = test; + setSharedState(SHARED_KEY, JSON.stringify(result)); + + response.write("'hello world'"); + return; + } +} diff --git a/dom/workers/test/referrer_worker.html b/dom/workers/test/referrer_worker.html new file mode 100644 index 0000000000..f4c6719912 --- /dev/null +++ b/dom/workers/test/referrer_worker.html @@ -0,0 +1,144 @@ + + + + + + + + diff --git a/dom/workers/test/rvals_worker.js b/dom/workers/test/rvals_worker.js new file mode 100644 index 0000000000..221d701443 --- /dev/null +++ b/dom/workers/test/rvals_worker.js @@ -0,0 +1,13 @@ +onmessage = function (evt) { + postMessage(postMessage("ignore") == undefined); + + var id = setInterval(function () {}, 200); + postMessage(clearInterval(id) == undefined); + + id = setTimeout(function () {}, 200); + postMessage(clearTimeout(id) == undefined); + + postMessage(dump(42 + "\n") == undefined); + + postMessage("finished"); +}; diff --git a/dom/workers/test/script_createFile.js b/dom/workers/test/script_createFile.js new file mode 100644 index 0000000000..d49ea18867 --- /dev/null +++ b/dom/workers/test/script_createFile.js @@ -0,0 +1,42 @@ +/* eslint-env mozilla/chrome-script */ + +Cu.importGlobalProperties(["File"]); + +addMessageListener("file.open", function (e) { + var tmpFile = Cc["@mozilla.org/file/directory_service;1"] + .getService(Ci.nsIDirectoryService) + .QueryInterface(Ci.nsIProperties) + .get("TmpD", Ci.nsIFile); + tmpFile.append("file.txt"); + tmpFile.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0o600); + + File.createFromNsIFile(tmpFile).then(function (file) { + sendAsyncMessage("file.opened", { data: file }); + }); +}); + +addMessageListener("nonEmptyFile.open", function (e) { + var tmpFile = Cc["@mozilla.org/file/directory_service;1"] + .getService(Ci.nsIDirectoryService) + .QueryInterface(Ci.nsIProperties) + .get("TmpD", Ci.nsIFile); + tmpFile.append("file.txt"); + tmpFile.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0o600); + + var outStream = Cc[ + "@mozilla.org/network/file-output-stream;1" + ].createInstance(Ci.nsIFileOutputStream); + outStream.init( + tmpFile, + 0x02 | 0x08 | 0x20, // write, create, truncate + 0o666, + 0 + ); + var fileData = "Hello world!"; + outStream.write(fileData, fileData.length); + outStream.close(); + + File.createFromNsIFile(tmpFile).then(function (file) { + sendAsyncMessage("nonEmptyFile.opened", { data: file }); + }); +}); diff --git a/dom/workers/test/server_fetch_synthetic.sjs b/dom/workers/test/server_fetch_synthetic.sjs new file mode 100644 index 0000000000..703b26d0d2 --- /dev/null +++ b/dom/workers/test/server_fetch_synthetic.sjs @@ -0,0 +1,50 @@ +const CC = Components.Constructor; +const BinaryInputStream = CC( + "@mozilla.org/binaryinputstream;1", + "nsIBinaryInputStream", + "setInputStream" +); + +function log(str) { + //dump(`SJS LOG: ${str}\n`); +} + +/** + * Given a multipart/form-data encoded string that we know to have only a single + * part, return the contents of the part. (MIME multipart encoding is too + * exciting to delve into.) + */ +function extractBlobFromMultipartFormData(text) { + const lines = text.split(/\r\n/g); + const firstBlank = lines.indexOf(""); + const foo = lines.slice(firstBlank + 1, -2).join("\n"); + return foo; +} + +async function handleRequest(request, response) { + let blobContents = ""; + if (request.method !== "POST") { + } else { + var body = new BinaryInputStream(request.bodyInputStream); + + var avail; + var bytes = []; + + while ((avail = body.available()) > 0) { + Array.prototype.push.apply(bytes, body.readByteArray(avail)); + } + let requestBodyContents = String.fromCharCode.apply(null, bytes); + log(requestBodyContents); + blobContents = extractBlobFromMultipartFormData(requestBodyContents); + } + + log("Setting Headers"); + response.setHeader("Content-Type", "text/html", false); + response.setStatusLine(request.httpVersion, "200", "OK"); + response.write(` +

${request.scheme}${request.host}${request.port}${request.path}

+
ServerJS
+
${blobContents}
+ `); + log("Done"); +} diff --git a/dom/workers/test/sharedWorker_console.js b/dom/workers/test/sharedWorker_console.js new file mode 100644 index 0000000000..f9c8416ea6 --- /dev/null +++ b/dom/workers/test/sharedWorker_console.js @@ -0,0 +1,11 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +"use strict"; + +onconnect = function (evt) { + console.profile("Hello profiling from a SharedWorker!"); + console.log("Hello world from a SharedWorker!"); + evt.ports[0].postMessage("ok!"); +}; diff --git a/dom/workers/test/sharedWorker_lifetime.js b/dom/workers/test/sharedWorker_lifetime.js new file mode 100644 index 0000000000..594bd5833d --- /dev/null +++ b/dom/workers/test/sharedWorker_lifetime.js @@ -0,0 +1,5 @@ +onconnect = function (e) { + setTimeout(function () { + e.ports[0].postMessage("Still alive!"); + }, 500); +}; diff --git a/dom/workers/test/sharedWorker_ports.js b/dom/workers/test/sharedWorker_ports.js new file mode 100644 index 0000000000..6bdb5695db --- /dev/null +++ b/dom/workers/test/sharedWorker_ports.js @@ -0,0 +1,30 @@ +var port; +onconnect = function (evt) { + evt.source.postMessage({ type: "connected" }); + + if (!port) { + port = evt.source; + evt.source.onmessage = function (evtFromPort) { + port.postMessage({ + type: "status", + test: "Port from the main-thread!" == evtFromPort.data, + msg: "The message is coming from the main-thread", + }); + port.postMessage({ + type: "status", + test: evtFromPort.ports.length == 1, + msg: "1 port transferred", + }); + + evtFromPort.ports[0].onmessage = function (evtFromPort2) { + port.postMessage({ + type: "status", + test: evtFromPort2.data.type == "connected", + msg: "The original message received", + }); + port.postMessage({ type: "finish" }); + close(); + }; + }; + } +}; diff --git a/dom/workers/test/sharedWorker_privateBrowsing.js b/dom/workers/test/sharedWorker_privateBrowsing.js new file mode 100644 index 0000000000..c16bd209f0 --- /dev/null +++ b/dom/workers/test/sharedWorker_privateBrowsing.js @@ -0,0 +1,4 @@ +var counter = 0; +onconnect = function (evt) { + evt.ports[0].postMessage(++counter); +}; diff --git a/dom/workers/test/sharedWorker_sharedWorker.js b/dom/workers/test/sharedWorker_sharedWorker.js new file mode 100644 index 0000000000..f5dd52d72b --- /dev/null +++ b/dom/workers/test/sharedWorker_sharedWorker.js @@ -0,0 +1,99 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +"use strict"; + +if (!("self" in this)) { + throw new Error("No 'self' exists on SharedWorkerGlobalScope!"); +} +if (this !== self) { + throw new Error("'self' not equal to global object!"); +} +if (!(self instanceof SharedWorkerGlobalScope)) { + throw new Error("self not a SharedWorkerGlobalScope instance!"); +} + +var propsToCheck = [ + "location", + "navigator", + "close", + "importScripts", + "setTimeout", + "clearTimeout", + "setInterval", + "clearInterval", + "dump", + "atob", + "btoa", +]; + +for (var index = 0; index < propsToCheck.length; index++) { + var prop = propsToCheck[index]; + if (!(prop in self)) { + throw new Error("SharedWorkerGlobalScope has no '" + prop + "' property!"); + } +} + +onconnect = function (event) { + if (!("SharedWorkerGlobalScope" in self)) { + throw new Error("SharedWorkerGlobalScope should be visible!"); + } + if (!(self instanceof SharedWorkerGlobalScope)) { + throw new Error("The global should be a SharedWorkerGlobalScope!"); + } + if (!(self instanceof WorkerGlobalScope)) { + throw new Error("The global should be a WorkerGlobalScope!"); + } + if ("DedicatedWorkerGlobalScope" in self) { + throw new Error("DedicatedWorkerGlobalScope should not be visible!"); + } + if (!(event instanceof MessageEvent)) { + throw new Error("'connect' event is not a MessageEvent!"); + } + if (!("ports" in event)) { + throw new Error("'connect' event doesn't have a 'ports' property!"); + } + if (event.ports.length != 1) { + throw new Error( + "'connect' event has a 'ports' property with length '" + + event.ports.length + + "'!" + ); + } + if (!event.ports[0]) { + throw new Error("'connect' event has a null 'ports[0]' property!"); + } + if (!(event.ports[0] instanceof MessagePort)) { + throw new Error( + "'connect' event has a 'ports[0]' property that isn't a " + "MessagePort!" + ); + } + if (!(event.ports[0] == event.source)) { + throw new Error("'connect' event source property is incorrect!"); + } + if (event.data) { + throw new Error("'connect' event has data: " + event.data); + } + + // Statement after return should trigger a warning, but NOT fire error events + // at us. + (function () { + return; + 1; + }); + + event.ports[0].onmessage = function (msg) { + if (!(msg instanceof MessageEvent)) { + throw new Error("'message' event is not a MessageEvent!"); + } + if (!("ports" in msg)) { + throw new Error("'message' event doesn't have a 'ports' property!"); + } + if (msg.ports === null) { + throw new Error("'message' event has a null 'ports' property!"); + } + msg.target.postMessage(msg.data); + throw new Error(msg.data); + }; +}; diff --git a/dom/workers/test/sharedWorker_thirdparty_frame.html b/dom/workers/test/sharedWorker_thirdparty_frame.html new file mode 100644 index 0000000000..ebd78412a6 --- /dev/null +++ b/dom/workers/test/sharedWorker_thirdparty_frame.html @@ -0,0 +1,16 @@ + + diff --git a/dom/workers/test/sharedWorker_thirdparty_window.html b/dom/workers/test/sharedWorker_thirdparty_window.html new file mode 100644 index 0000000000..f5f01066c2 --- /dev/null +++ b/dom/workers/test/sharedWorker_thirdparty_window.html @@ -0,0 +1,26 @@ + + + + + Test for SharedWorker in 3rd Party Iframes + + + + + diff --git a/dom/workers/test/simpleThread_worker.js b/dom/workers/test/simpleThread_worker.js new file mode 100644 index 0000000000..9d6fb30f2f --- /dev/null +++ b/dom/workers/test/simpleThread_worker.js @@ -0,0 +1,52 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +"use strict"; + +function messageListener(event) { + var exception; + try { + event.bubbles = true; + } catch (e) { + exception = e; + } + + if (!(exception instanceof TypeError)) { + throw exception; + } + + switch (event.data) { + case "no-op": + break; + case "components": + postMessage(Components.toString()); + break; + case "start": + for (var i = 0; i < 1000; i++) {} + postMessage("started"); + break; + case "stop": + self.postMessage("no-op"); + postMessage("stopped"); + self.removeEventListener("message", messageListener); + break; + default: + throw "Bad message: " + event.data; + } +} + +if (!("DedicatedWorkerGlobalScope" in self)) { + throw new Error("DedicatedWorkerGlobalScope should be visible!"); +} +if (!(self instanceof DedicatedWorkerGlobalScope)) { + throw new Error("The global should be a SharedWorkerGlobalScope!"); +} +if (!(self instanceof WorkerGlobalScope)) { + throw new Error("The global should be a WorkerGlobalScope!"); +} +if ("SharedWorkerGlobalScope" in self) { + throw new Error("SharedWorkerGlobalScope should not be visible!"); +} + +addEventListener("message", { handleEvent: messageListener }); diff --git a/dom/workers/test/sourcemap_header.js b/dom/workers/test/sourcemap_header.js new file mode 100644 index 0000000000..9f10b35ed9 --- /dev/null +++ b/dom/workers/test/sourcemap_header.js @@ -0,0 +1,65 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +"use strict"; + +(async () => { + SimpleTest.waitForExplicitFinish(); + + const HTTP_BASE_URL = "http://mochi.test:8888/tests/dom/workers/test/"; + const IFRAME_URL = HTTP_BASE_URL + "sourcemap_header_iframe.html"; + const WORKER_URL = HTTP_BASE_URL + "sourcemap_header_worker.js"; + const DEBUGGER_URL = BASE_URL + "sourcemap_header_debugger.js"; + + const workerFrame = document.getElementById("worker-frame"); + ok(workerFrame, "has frame"); + + await new Promise(r => { + workerFrame.onload = r; + workerFrame.src = IFRAME_URL; + }); + + info("Start worker and watch for registration"); + const workerLoadedChannel = new MessageChannel(); + + const loadDebuggerAndWorker = Promise.all([ + waitForRegister(WORKER_URL, DEBUGGER_URL), + // We need to wait for the worker to load so a Debugger.Source will be + // guaranteed to exist. + new Promise(r => { + workerLoadedChannel.port1.onmessage = r; + }), + ]); + workerFrame.contentWindow.postMessage(WORKER_URL, "*", [ + workerLoadedChannel.port2, + ]); + const [dbg] = await loadDebuggerAndWorker; + + // Wait for the debugger server to reply with the sourceMapURL of the + // loaded worker scripts. + info("Querying for the sourceMapURL of the worker script"); + const urls = await new Promise(res => { + dbg.addListener({ + onMessage(msg) { + const data = JSON.parse(msg); + if (data.type !== "response-sourceMapURL") { + return; + } + dbg.removeListener(this); + res(data.value); + }, + }); + dbg.postMessage( + JSON.stringify({ + type: "request-sourceMapURL", + url: WORKER_URL, + }) + ); + }); + + ok(Array.isArray(urls) && urls.length === 1, "has a single source actor"); + is(urls[0], "worker-header.js.map", "has the right map URL"); + + SimpleTest.finish(); +})(); diff --git a/dom/workers/test/sourcemap_header_debugger.js b/dom/workers/test/sourcemap_header_debugger.js new file mode 100644 index 0000000000..bb8ed0c1f7 --- /dev/null +++ b/dom/workers/test/sourcemap_header_debugger.js @@ -0,0 +1,29 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +"use strict"; + +addEventListener("message", function (event) { + let data; + try { + data = JSON.parse(event.data); + } catch {} + + switch (data.type) { + case "request-sourceMapURL": + const dbg = new Debugger(global); + const sourceMapURLs = dbg + .findSources() + .filter(source => source.url === data.url) + .map(source => source.sourceMapURL); + + postMessage( + JSON.stringify({ + type: "response-sourceMapURL", + value: sourceMapURLs, + }) + ); + break; + } +}); diff --git a/dom/workers/test/sourcemap_header_iframe.html b/dom/workers/test/sourcemap_header_iframe.html new file mode 100644 index 0000000000..82278f41a1 --- /dev/null +++ b/dom/workers/test/sourcemap_header_iframe.html @@ -0,0 +1,19 @@ + + + + + + + + diff --git a/dom/workers/test/sourcemap_header_worker.js b/dom/workers/test/sourcemap_header_worker.js new file mode 100644 index 0000000000..ca094686d3 --- /dev/null +++ b/dom/workers/test/sourcemap_header_worker.js @@ -0,0 +1,8 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +// Send a message pack so the test knows that the source has loaded before +// it tries to search for the Debugger.Source. +postMessage("loaded"); diff --git a/dom/workers/test/sourcemap_header_worker.js^headers^ b/dom/workers/test/sourcemap_header_worker.js^headers^ new file mode 100644 index 0000000000..833288ef02 --- /dev/null +++ b/dom/workers/test/sourcemap_header_worker.js^headers^ @@ -0,0 +1 @@ +X-SourceMap: worker-header.js.map diff --git a/dom/workers/test/suspend_blank.html b/dom/workers/test/suspend_blank.html new file mode 100644 index 0000000000..b6dd0075e6 --- /dev/null +++ b/dom/workers/test/suspend_blank.html @@ -0,0 +1,23 @@ + + diff --git a/dom/workers/test/suspend_window.html b/dom/workers/test/suspend_window.html new file mode 100644 index 0000000000..9bede3aaa6 --- /dev/null +++ b/dom/workers/test/suspend_window.html @@ -0,0 +1,82 @@ + + + + + + Test for DOM Worker Threads Suspending + + + +
+
+ +
+ + diff --git a/dom/workers/test/suspend_worker.js b/dom/workers/test/suspend_worker.js new file mode 100644 index 0000000000..e024972737 --- /dev/null +++ b/dom/workers/test/suspend_worker.js @@ -0,0 +1,13 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +var counter = 0; + +var interval = setInterval(function () { + postMessage(++counter); +}, 100); + +onmessage = function (event) { + clearInterval(interval); +}; diff --git a/dom/workers/test/terminate_worker.js b/dom/workers/test/terminate_worker.js new file mode 100644 index 0000000000..7b9984e869 --- /dev/null +++ b/dom/workers/test/terminate_worker.js @@ -0,0 +1,11 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +onmessage = function (event) { + throw "No messages should reach me!"; +}; + +setInterval(function () { + postMessage("Still alive!"); +}, 100); diff --git a/dom/workers/test/test_404.html b/dom/workers/test/test_404.html new file mode 100644 index 0000000000..59ab691e02 --- /dev/null +++ b/dom/workers/test/test_404.html @@ -0,0 +1,39 @@ + + + + + + Test for DOM Worker Threads + + + + +
+
+
+ + diff --git a/dom/workers/test/test_WorkerDebugger.initialize.xhtml b/dom/workers/test/test_WorkerDebugger.initialize.xhtml new file mode 100644 index 0000000000..9e7ec2e9a6 --- /dev/null +++ b/dom/workers/test/test_WorkerDebugger.initialize.xhtml @@ -0,0 +1,56 @@ + + + + + + + +

+ +

+  
+  
diff --git a/dom/workers/test/test_WorkerDebugger.postMessage.xhtml b/dom/workers/test/test_WorkerDebugger.postMessage.xhtml new file mode 100644 index 0000000000..58e15b3a05 --- /dev/null +++ b/dom/workers/test/test_WorkerDebugger.postMessage.xhtml @@ -0,0 +1,59 @@ + + + + + + + +

+ +

+  
+  
diff --git a/dom/workers/test/test_WorkerDebugger.xhtml b/dom/workers/test/test_WorkerDebugger.xhtml new file mode 100644 index 0000000000..ed7040a437 --- /dev/null +++ b/dom/workers/test/test_WorkerDebugger.xhtml @@ -0,0 +1,145 @@ + + + + + + + +

+ +

+  
+  
diff --git a/dom/workers/test/test_WorkerDebuggerGlobalScope.createSandbox.xhtml b/dom/workers/test/test_WorkerDebuggerGlobalScope.createSandbox.xhtml new file mode 100644 index 0000000000..89114f5e49 --- /dev/null +++ b/dom/workers/test/test_WorkerDebuggerGlobalScope.createSandbox.xhtml @@ -0,0 +1,49 @@ + + + + + + + +

+ +

+  
+  
diff --git a/dom/workers/test/test_WorkerDebuggerGlobalScope.enterEventLoop.xhtml b/dom/workers/test/test_WorkerDebuggerGlobalScope.enterEventLoop.xhtml new file mode 100644 index 0000000000..d5cff95d39 --- /dev/null +++ b/dom/workers/test/test_WorkerDebuggerGlobalScope.enterEventLoop.xhtml @@ -0,0 +1,124 @@ + + + + + + + +

+ +

+  
+  
diff --git a/dom/workers/test/test_WorkerDebuggerGlobalScope.reportError.xhtml b/dom/workers/test/test_WorkerDebuggerGlobalScope.reportError.xhtml new file mode 100644 index 0000000000..20e731c1cf --- /dev/null +++ b/dom/workers/test/test_WorkerDebuggerGlobalScope.reportError.xhtml @@ -0,0 +1,95 @@ + + + + + + + +

+ +

+  
+  
diff --git a/dom/workers/test/test_WorkerDebuggerGlobalScope.setImmediate.xhtml b/dom/workers/test/test_WorkerDebuggerGlobalScope.setImmediate.xhtml new file mode 100644 index 0000000000..4b09f01708 --- /dev/null +++ b/dom/workers/test/test_WorkerDebuggerGlobalScope.setImmediate.xhtml @@ -0,0 +1,52 @@ + + + + + + + +

+ +

+  
+  
diff --git a/dom/workers/test/test_WorkerDebuggerManager.xhtml b/dom/workers/test/test_WorkerDebuggerManager.xhtml new file mode 100644 index 0000000000..c75f5a453e --- /dev/null +++ b/dom/workers/test/test_WorkerDebuggerManager.xhtml @@ -0,0 +1,104 @@ + + + + + + + +

+ +

+  
+  
diff --git a/dom/workers/test/test_WorkerDebugger_console.xhtml b/dom/workers/test/test_WorkerDebugger_console.xhtml new file mode 100644 index 0000000000..2363b53be0 --- /dev/null +++ b/dom/workers/test/test_WorkerDebugger_console.xhtml @@ -0,0 +1,98 @@ + + + + + + + +

+ +

+  
+  
diff --git a/dom/workers/test/test_WorkerDebugger_frozen.xhtml b/dom/workers/test/test_WorkerDebugger_frozen.xhtml new file mode 100644 index 0000000000..57424ae612 --- /dev/null +++ b/dom/workers/test/test_WorkerDebugger_frozen.xhtml @@ -0,0 +1,77 @@ + + + + + + + +

+ +

+  
+  
diff --git a/dom/workers/test/test_WorkerDebugger_promise.xhtml b/dom/workers/test/test_WorkerDebugger_promise.xhtml new file mode 100644 index 0000000000..14d50969b5 --- /dev/null +++ b/dom/workers/test/test_WorkerDebugger_promise.xhtml @@ -0,0 +1,68 @@ + + + + + + + +

+ +

+  
+  
diff --git a/dom/workers/test/test_WorkerDebugger_suspended.xhtml b/dom/workers/test/test_WorkerDebugger_suspended.xhtml new file mode 100644 index 0000000000..d0c9bff552 --- /dev/null +++ b/dom/workers/test/test_WorkerDebugger_suspended.xhtml @@ -0,0 +1,72 @@ + + + + + + + +

+ +

+  
+  
diff --git a/dom/workers/test/test_atob.html b/dom/workers/test/test_atob.html new file mode 100644 index 0000000000..0e82029b46 --- /dev/null +++ b/dom/workers/test/test_atob.html @@ -0,0 +1,57 @@ + + + + + Test for DOM Worker Threads + + + + +

+ +
+
+
+
+ + diff --git a/dom/workers/test/test_blobConstructor.html b/dom/workers/test/test_blobConstructor.html new file mode 100644 index 0000000000..4aff5b545b --- /dev/null +++ b/dom/workers/test/test_blobConstructor.html @@ -0,0 +1,60 @@ + + + + + + Test for DOM Worker Blob constructor + + + + +

+ +
+
+
+ + diff --git a/dom/workers/test/test_blobWorkers.html b/dom/workers/test/test_blobWorkers.html new file mode 100644 index 0000000000..6ecd6c6f4b --- /dev/null +++ b/dom/workers/test/test_blobWorkers.html @@ -0,0 +1,31 @@ + + + + + + + + + + + diff --git a/dom/workers/test/test_bug1002702.html b/dom/workers/test/test_bug1002702.html new file mode 100644 index 0000000000..c020dcfd05 --- /dev/null +++ b/dom/workers/test/test_bug1002702.html @@ -0,0 +1,26 @@ + + + + + Test for bug 1002702 + + + + +

+ +

+
+
+
+
diff --git a/dom/workers/test/test_bug1010784.html b/dom/workers/test/test_bug1010784.html
new file mode 100644
index 0000000000..3e2f62971d
--- /dev/null
+++ b/dom/workers/test/test_bug1010784.html
@@ -0,0 +1,35 @@
+
+
+
+
+  
+  Test for Bug 1010784
+  
+  
+
+
+Mozilla Bug 1010784
+

+ +
+
+
+ + diff --git a/dom/workers/test/test_bug1014466.html b/dom/workers/test/test_bug1014466.html new file mode 100644 index 0000000000..26ef2fb316 --- /dev/null +++ b/dom/workers/test/test_bug1014466.html @@ -0,0 +1,42 @@ + + + + + + + Test for Bug 1014466 + + + + +Mozilla Bug 1014466 +

+ +
+
+
+ + diff --git a/dom/workers/test/test_bug1020226.html b/dom/workers/test/test_bug1020226.html new file mode 100644 index 0000000000..1ed69db41e --- /dev/null +++ b/dom/workers/test/test_bug1020226.html @@ -0,0 +1,38 @@ + + + + + + Test for Bug 1020226 + + + + +Mozilla Bug 1020226 +

+ +
+
+
+ + diff --git a/dom/workers/test/test_bug1036484.html b/dom/workers/test/test_bug1036484.html new file mode 100644 index 0000000000..feada50f5a --- /dev/null +++ b/dom/workers/test/test_bug1036484.html @@ -0,0 +1,52 @@ + + + + + + Test for DOM Worker Threads: bug 1036484 + + + + +
+
+
+ + diff --git a/dom/workers/test/test_bug1060621.html b/dom/workers/test/test_bug1060621.html new file mode 100644 index 0000000000..d2af81c93e --- /dev/null +++ b/dom/workers/test/test_bug1060621.html @@ -0,0 +1,30 @@ + + + + + Test for URLSearchParams object in workers + + + + +

+ +

+
+
+
+
diff --git a/dom/workers/test/test_bug1062920.html b/dom/workers/test/test_bug1062920.html
new file mode 100644
index 0000000000..7a42bd2dfd
--- /dev/null
+++ b/dom/workers/test/test_bug1062920.html
@@ -0,0 +1,70 @@
+
+
+
+
+  Test for navigator property override
+  
+  
+
+
+

+ +

+
+
+
+
diff --git a/dom/workers/test/test_bug1062920.xhtml b/dom/workers/test/test_bug1062920.xhtml
new file mode 100644
index 0000000000..d76e320def
--- /dev/null
+++ b/dom/workers/test/test_bug1062920.xhtml
@@ -0,0 +1,67 @@
+
+
+
+
+  
+
+  
+    

+ +

+  
+  
diff --git a/dom/workers/test/test_bug1063538.html b/dom/workers/test/test_bug1063538.html new file mode 100644 index 0000000000..a1dc6624b9 --- /dev/null +++ b/dom/workers/test/test_bug1063538.html @@ -0,0 +1,47 @@ + + + + + + + Test for Bug 1063538 + + + + +Mozilla Bug 1063538 +

+ +
+
+
+ + diff --git a/dom/workers/test/test_bug1104064.html b/dom/workers/test/test_bug1104064.html new file mode 100644 index 0000000000..1c8b3ac92c --- /dev/null +++ b/dom/workers/test/test_bug1104064.html @@ -0,0 +1,27 @@ + + + + + Test for bug 1104064 + + + + + + + + diff --git a/dom/workers/test/test_bug1132395.html b/dom/workers/test/test_bug1132395.html new file mode 100644 index 0000000000..8d424e5ad4 --- /dev/null +++ b/dom/workers/test/test_bug1132395.html @@ -0,0 +1,40 @@ + + + + + Test for 1132395 + + + + + + + + + diff --git a/dom/workers/test/test_bug1132924.html b/dom/workers/test/test_bug1132924.html new file mode 100644 index 0000000000..b5b952e908 --- /dev/null +++ b/dom/workers/test/test_bug1132924.html @@ -0,0 +1,28 @@ + + + + + Test for 1132924 + + + + + + + + + diff --git a/dom/workers/test/test_bug1278777.html b/dom/workers/test/test_bug1278777.html new file mode 100644 index 0000000000..c995212bd0 --- /dev/null +++ b/dom/workers/test/test_bug1278777.html @@ -0,0 +1,31 @@ + + + + + + Test for Bug 1278777 + + + + + Mozilla Bug 1278777 + + + diff --git a/dom/workers/test/test_bug1301094.html b/dom/workers/test/test_bug1301094.html new file mode 100644 index 0000000000..efe25fa3a9 --- /dev/null +++ b/dom/workers/test/test_bug1301094.html @@ -0,0 +1,69 @@ + + + + + + Test for Bug 1301094 + + + + + Mozilla Bug 1301094 + + + + diff --git a/dom/workers/test/test_bug1317725.html b/dom/workers/test/test_bug1317725.html new file mode 100644 index 0000000000..7da3fc2644 --- /dev/null +++ b/dom/workers/test/test_bug1317725.html @@ -0,0 +1,49 @@ + + + + + Test for bug 1317725 + + + + + + + + + + + diff --git a/dom/workers/test/test_bug1317725.js b/dom/workers/test/test_bug1317725.js new file mode 100644 index 0000000000..858488fac3 --- /dev/null +++ b/dom/workers/test/test_bug1317725.js @@ -0,0 +1,8 @@ +onmessage = function (e) { + var data = new FormData(); + data.append("Filedata", e.data.slice(0, 127), encodeURI(e.data.name)); + var xhr = new XMLHttpRequest(); + xhr.open("POST", location.href, false); + xhr.send(data); + postMessage("No crash \\o/"); +}; diff --git a/dom/workers/test/test_bug1824498.html b/dom/workers/test/test_bug1824498.html new file mode 100644 index 0000000000..ea01cabe1b --- /dev/null +++ b/dom/workers/test/test_bug1824498.html @@ -0,0 +1,38 @@ + + + + + Test for Worker Import failure (Bug 1824498) + + + + + +Worker Import failure test: Bug 1824498 + +

+ +
+
+
+ + diff --git a/dom/workers/test/test_bug949946.html b/dom/workers/test/test_bug949946.html new file mode 100644 index 0000000000..41b021a098 --- /dev/null +++ b/dom/workers/test/test_bug949946.html @@ -0,0 +1,26 @@ + + + + + Test for bug 949946 + + + + +

+ +

+
+
+
+
diff --git a/dom/workers/test/test_bug978260.html b/dom/workers/test/test_bug978260.html
new file mode 100644
index 0000000000..056b8b6c72
--- /dev/null
+++ b/dom/workers/test/test_bug978260.html
@@ -0,0 +1,35 @@
+
+
+
+
+  Test for DOM Worker Threads
+  
+  
+
+
+

+ +
+
+
+ + diff --git a/dom/workers/test/test_bug998474.html b/dom/workers/test/test_bug998474.html new file mode 100644 index 0000000000..93632a5de4 --- /dev/null +++ b/dom/workers/test/test_bug998474.html @@ -0,0 +1,40 @@ + + + + + Test for bug 998474 + + + + +

+ +

+
+
+
+
diff --git a/dom/workers/test/test_chromeWorker.html b/dom/workers/test/test_chromeWorker.html
new file mode 100644
index 0000000000..35d5c08928
--- /dev/null
+++ b/dom/workers/test/test_chromeWorker.html
@@ -0,0 +1,26 @@
+
+
+
+  Test for DOM Worker Threads
+  
+  
+
+
+
+
+
+ + diff --git a/dom/workers/test/test_chromeWorker.xhtml b/dom/workers/test/test_chromeWorker.xhtml new file mode 100644 index 0000000000..65d14f8851 --- /dev/null +++ b/dom/workers/test/test_chromeWorker.xhtml @@ -0,0 +1,58 @@ + + + + + + + +

+ +

+  
+  
diff --git a/dom/workers/test/test_chromeWorkerJSM.xhtml b/dom/workers/test/test_chromeWorkerJSM.xhtml new file mode 100644 index 0000000000..6341737815 --- /dev/null +++ b/dom/workers/test/test_chromeWorkerJSM.xhtml @@ -0,0 +1,54 @@ + + + + + + + +

+ +

+  
+  
diff --git a/dom/workers/test/test_clearTimeouts.html b/dom/workers/test/test_clearTimeouts.html new file mode 100644 index 0000000000..caa87fbf56 --- /dev/null +++ b/dom/workers/test/test_clearTimeouts.html @@ -0,0 +1,31 @@ + + + + + Test for DOM Worker Threads + + + + +

+ +
+
+
+ + diff --git a/dom/workers/test/test_clearTimeoutsImplicit.html b/dom/workers/test/test_clearTimeoutsImplicit.html new file mode 100644 index 0000000000..59a37974ca --- /dev/null +++ b/dom/workers/test/test_clearTimeoutsImplicit.html @@ -0,0 +1,31 @@ + + + + + Test for DOM Worker Threads + + + + +

+ +
+
+
+ + diff --git a/dom/workers/test/test_console.html b/dom/workers/test/test_console.html new file mode 100644 index 0000000000..b8c0f189ab --- /dev/null +++ b/dom/workers/test/test_console.html @@ -0,0 +1,44 @@ + + + + + + Test for DOM Worker Console + + + + +

+ +
+
+
+ + diff --git a/dom/workers/test/test_consoleAndBlobs.html b/dom/workers/test/test_consoleAndBlobs.html new file mode 100644 index 0000000000..e07cfe5dca --- /dev/null +++ b/dom/workers/test/test_consoleAndBlobs.html @@ -0,0 +1,46 @@ + + + + + Test for console API and blobs + + + + + + + diff --git a/dom/workers/test/test_consoleReplaceable.html b/dom/workers/test/test_consoleReplaceable.html new file mode 100644 index 0000000000..b8a60411e4 --- /dev/null +++ b/dom/workers/test/test_consoleReplaceable.html @@ -0,0 +1,44 @@ + + + + + + Test for DOM Worker Console + + + + +

+ +
+
+
+ + diff --git a/dom/workers/test/test_contentWorker.html b/dom/workers/test/test_contentWorker.html new file mode 100644 index 0000000000..38891a88c5 --- /dev/null +++ b/dom/workers/test/test_contentWorker.html @@ -0,0 +1,48 @@ + + + + + Test for DOM Worker privileged properties + + + + +

+ +
+
+
+ + diff --git a/dom/workers/test/test_csp.html b/dom/workers/test/test_csp.html new file mode 100644 index 0000000000..f3ef747372 --- /dev/null +++ b/dom/workers/test/test_csp.html @@ -0,0 +1,18 @@ + + + + + Test for DOM Worker + CSP + + + + +

+ +

+
+
+
diff --git a/dom/workers/test/test_csp.html^headers^ b/dom/workers/test/test_csp.html^headers^
new file mode 100644
index 0000000000..1c93210799
--- /dev/null
+++ b/dom/workers/test/test_csp.html^headers^
@@ -0,0 +1,2 @@
+Cache-Control: no-cache
+Content-Security-Policy: default-src 'self' blob:
diff --git a/dom/workers/test/test_csp.js b/dom/workers/test/test_csp.js
new file mode 100644
index 0000000000..8c2b53586a
--- /dev/null
+++ b/dom/workers/test/test_csp.js
@@ -0,0 +1,54 @@
+/**
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+var tests = 3;
+
+SimpleTest.waitForExplicitFinish();
+
+testDone = function (event) {
+  if (!--tests) {
+    SimpleTest.finish();
+  }
+};
+
+// Workers don't inherit CSP
+worker = new Worker("csp_worker.js");
+worker.postMessage({ do: "eval" });
+worker.onmessage = function (event) {
+  is(event.data, 42, "Eval succeeded!");
+  testDone();
+};
+
+// blob: workers *do* inherit CSP
+xhr = new XMLHttpRequest();
+xhr.open("GET", "csp_worker.js");
+xhr.responseType = "blob";
+xhr.send();
+xhr.onload = e => {
+  uri = URL.createObjectURL(e.target.response);
+  worker = new Worker(uri);
+  worker.postMessage({ do: "eval" });
+  worker.onmessage = function (event) {
+    is(event.data, "EvalError: call to eval() blocked by CSP", "Eval threw");
+    testDone();
+  };
+};
+
+xhr = new XMLHttpRequest();
+xhr.open("GET", "csp_worker.js");
+xhr.responseType = "blob";
+xhr.send();
+xhr.onload = e => {
+  uri = URL.createObjectURL(e.target.response);
+  worker = new Worker(uri);
+  worker.postMessage({ do: "nest", uri, level: 3 });
+  worker.onmessage = function (event) {
+    is(
+      event.data,
+      "EvalError: call to eval() blocked by CSP",
+      "Eval threw in nested worker"
+    );
+    testDone();
+  };
+};
diff --git a/dom/workers/test/test_dataURLWorker.html b/dom/workers/test/test_dataURLWorker.html
new file mode 100644
index 0000000000..145b0e43f1
--- /dev/null
+++ b/dom/workers/test/test_dataURLWorker.html
@@ -0,0 +1,30 @@
+
+
+
+  
+    
+    
+  
+  
+    
+  
+
diff --git a/dom/workers/test/test_dynamicImport.html b/dom/workers/test/test_dynamicImport.html
new file mode 100644
index 0000000000..bffef87d2d
--- /dev/null
+++ b/dom/workers/test/test_dynamicImport.html
@@ -0,0 +1,76 @@
+
+
+
+
+
+  Test for Worker Dynamic Import (Bug 1540913)
+  
+  
+
+
+Worker Dynamic Import
+ Bug 1540913
+

+ +
+
+
+ + diff --git a/dom/workers/test/test_dynamicImport_and_terminate.html b/dom/workers/test/test_dynamicImport_and_terminate.html new file mode 100644 index 0000000000..cf355572e6 --- /dev/null +++ b/dom/workers/test/test_dynamicImport_and_terminate.html @@ -0,0 +1,34 @@ + + + + + Test for Worker create script loader failure + + + + +

+ +
+
+
+ + diff --git a/dom/workers/test/test_dynamicImport_early_termination.html b/dom/workers/test/test_dynamicImport_early_termination.html new file mode 100644 index 0000000000..fb9096df14 --- /dev/null +++ b/dom/workers/test/test_dynamicImport_early_termination.html @@ -0,0 +1,79 @@ + + + + + + Test for Worker Dynamic Import (Bug 1540913) + + + + +Worker Dynamic Import + Bug 1540913 +

+ +
+
+
+ + diff --git a/dom/workers/test/test_errorPropagation.html b/dom/workers/test/test_errorPropagation.html new file mode 100644 index 0000000000..5d6afabe05 --- /dev/null +++ b/dom/workers/test/test_errorPropagation.html @@ -0,0 +1,65 @@ + + + + + + + + + + + + + diff --git a/dom/workers/test/test_errorwarning.html b/dom/workers/test/test_errorwarning.html new file mode 100644 index 0000000000..c65363200d --- /dev/null +++ b/dom/workers/test/test_errorwarning.html @@ -0,0 +1,95 @@ + + + + + + Test javascript.options.strict in Workers + + + + +

+ +
+
+
+ + diff --git a/dom/workers/test/test_eventDispatch.html b/dom/workers/test/test_eventDispatch.html new file mode 100644 index 0000000000..e6bbd7e2d1 --- /dev/null +++ b/dom/workers/test/test_eventDispatch.html @@ -0,0 +1,32 @@ + + + + + + + + + + + diff --git a/dom/workers/test/test_fibonacci.html b/dom/workers/test/test_fibonacci.html new file mode 100644 index 0000000000..c3e3e98574 --- /dev/null +++ b/dom/workers/test/test_fibonacci.html @@ -0,0 +1,51 @@ + + + + + + Test for DOM Worker Threads with Fibonacci + + + + +DOM Worker Threads Fibonacci +

+ +
+
+
+ + diff --git a/dom/workers/test/test_file.xhtml b/dom/workers/test/test_file.xhtml new file mode 100644 index 0000000000..2b628e7f4d --- /dev/null +++ b/dom/workers/test/test_file.xhtml @@ -0,0 +1,96 @@ + + + + + + + diff --git a/dom/workers/test/test_fileBlobPosting.xhtml b/dom/workers/test/test_fileBlobPosting.xhtml new file mode 100644 index 0000000000..61f4a8e909 --- /dev/null +++ b/dom/workers/test/test_fileBlobPosting.xhtml @@ -0,0 +1,85 @@ + + + + + + + diff --git a/dom/workers/test/test_fileBlobSubWorker.xhtml b/dom/workers/test/test_fileBlobSubWorker.xhtml new file mode 100644 index 0000000000..8b67552788 --- /dev/null +++ b/dom/workers/test/test_fileBlobSubWorker.xhtml @@ -0,0 +1,97 @@ + + + + + + + diff --git a/dom/workers/test/test_filePosting.xhtml b/dom/workers/test/test_filePosting.xhtml new file mode 100644 index 0000000000..3ffa219516 --- /dev/null +++ b/dom/workers/test/test_filePosting.xhtml @@ -0,0 +1,85 @@ + + + + + + + diff --git a/dom/workers/test/test_fileReadSlice.xhtml b/dom/workers/test/test_fileReadSlice.xhtml new file mode 100644 index 0000000000..b035fa2872 --- /dev/null +++ b/dom/workers/test/test_fileReadSlice.xhtml @@ -0,0 +1,93 @@ + + + + + + + diff --git a/dom/workers/test/test_fileReaderSync.xhtml b/dom/workers/test/test_fileReaderSync.xhtml new file mode 100644 index 0000000000..7cdcaed791 --- /dev/null +++ b/dom/workers/test/test_fileReaderSync.xhtml @@ -0,0 +1,198 @@ + + + + + + + diff --git a/dom/workers/test/test_fileReaderSyncErrors.xhtml b/dom/workers/test/test_fileReaderSyncErrors.xhtml new file mode 100644 index 0000000000..626b67e1ba --- /dev/null +++ b/dom/workers/test/test_fileReaderSyncErrors.xhtml @@ -0,0 +1,83 @@ + + + + + + + diff --git a/dom/workers/test/test_fileReaderSync_when_closing.html b/dom/workers/test/test_fileReaderSync_when_closing.html new file mode 100644 index 0000000000..d5fadbaa65 --- /dev/null +++ b/dom/workers/test/test_fileReaderSync_when_closing.html @@ -0,0 +1,54 @@ + + + + + Test for FileReaderSync when the worker is closing + + + + + + + diff --git a/dom/workers/test/test_fileSlice.xhtml b/dom/workers/test/test_fileSlice.xhtml new file mode 100644 index 0000000000..b683db2ec9 --- /dev/null +++ b/dom/workers/test/test_fileSlice.xhtml @@ -0,0 +1,105 @@ + + + + + + + diff --git a/dom/workers/test/test_fileSubWorker.xhtml b/dom/workers/test/test_fileSubWorker.xhtml new file mode 100644 index 0000000000..92aa3b1a17 --- /dev/null +++ b/dom/workers/test/test_fileSubWorker.xhtml @@ -0,0 +1,98 @@ + + + + + + + diff --git a/dom/workers/test/test_importScripts.html b/dom/workers/test/test_importScripts.html new file mode 100644 index 0000000000..0e6a3dde5e --- /dev/null +++ b/dom/workers/test/test_importScripts.html @@ -0,0 +1,53 @@ + + + + + + Test for DOM Worker Threads (Bug 437152) + + + + +DOM Worker Threads Bug 437152 +

+ +
+
+
+ + diff --git a/dom/workers/test/test_importScripts_3rdparty.html b/dom/workers/test/test_importScripts_3rdparty.html new file mode 100644 index 0000000000..2e8953edc6 --- /dev/null +++ b/dom/workers/test/test_importScripts_3rdparty.html @@ -0,0 +1,136 @@ + + + + + Test for 3rd party imported script and muted errors + + + + + + + diff --git a/dom/workers/test/test_importScripts_mixedcontent.html b/dom/workers/test/test_importScripts_mixedcontent.html new file mode 100644 index 0000000000..04281deaea --- /dev/null +++ b/dom/workers/test/test_importScripts_mixedcontent.html @@ -0,0 +1,50 @@ + + + + + Bug 1198078 - test that we respect mixed content blocking in importScript() inside workers + + + + +DOM Worker Threads Bug 1198078 + +

+ +
+
+
+ + diff --git a/dom/workers/test/test_instanceof.html b/dom/workers/test/test_instanceof.html new file mode 100644 index 0000000000..3a66674877 --- /dev/null +++ b/dom/workers/test/test_instanceof.html @@ -0,0 +1,40 @@ + + + + + + Test for DOM Worker Navigator + + + + +

+ +
+
+
+
+ + diff --git a/dom/workers/test/test_json.html b/dom/workers/test/test_json.html new file mode 100644 index 0000000000..26e951aa63 --- /dev/null +++ b/dom/workers/test/test_json.html @@ -0,0 +1,89 @@ + + + + + + Test for DOM Worker Navigator + + + + +

+ +
+
+
+
+ + diff --git a/dom/workers/test/test_loadEncoding.html b/dom/workers/test/test_loadEncoding.html new file mode 100644 index 0000000000..e7afb2fc82 --- /dev/null +++ b/dom/workers/test/test_loadEncoding.html @@ -0,0 +1,50 @@ + + + + + Bug 484305 - Load workers as UTF-8 + + + + + +Bug 484305 - Load workers as UTF-8 +

+ +
+
+
+
+ + diff --git a/dom/workers/test/test_loadError.html b/dom/workers/test/test_loadError.html new file mode 100644 index 0000000000..dec29d00c4 --- /dev/null +++ b/dom/workers/test/test_loadError.html @@ -0,0 +1,67 @@ + + + + Test for DOM Worker Threads + + + + +
+
+
+ + diff --git a/dom/workers/test/test_location.html b/dom/workers/test/test_location.html new file mode 100644 index 0000000000..55c94ae1cf --- /dev/null +++ b/dom/workers/test/test_location.html @@ -0,0 +1,72 @@ + + + + + + Test for DOM Worker Location + + + + +

+ +
+
+
+ + diff --git a/dom/workers/test/test_longThread.html b/dom/workers/test/test_longThread.html new file mode 100644 index 0000000000..80602419ca --- /dev/null +++ b/dom/workers/test/test_longThread.html @@ -0,0 +1,58 @@ + + + + + + Test for DOM Worker Threads (Bug 437152) + + + + +DOM Worker Threads Bug 437152 +

+ +
+
+
+ + diff --git a/dom/workers/test/test_multi_sharedWorker.html b/dom/workers/test/test_multi_sharedWorker.html new file mode 100644 index 0000000000..7bfbeaa9e9 --- /dev/null +++ b/dom/workers/test/test_multi_sharedWorker.html @@ -0,0 +1,241 @@ + + + + + Test for SharedWorker + + + + + + + + + diff --git a/dom/workers/test/test_multi_sharedWorker_lifetimes_bfcache.html b/dom/workers/test/test_multi_sharedWorker_lifetimes_bfcache.html new file mode 100644 index 0000000000..346950020f --- /dev/null +++ b/dom/workers/test/test_multi_sharedWorker_lifetimes_bfcache.html @@ -0,0 +1,151 @@ + + + + + Test for SharedWorker + + + + + + + diff --git a/dom/workers/test/test_multi_sharedWorker_lifetimes_nobfcache.html b/dom/workers/test/test_multi_sharedWorker_lifetimes_nobfcache.html new file mode 100644 index 0000000000..5049ead1a9 --- /dev/null +++ b/dom/workers/test/test_multi_sharedWorker_lifetimes_nobfcache.html @@ -0,0 +1,126 @@ + + + + + Test for SharedWorker + + + + + + + diff --git a/dom/workers/test/test_navigator.html b/dom/workers/test/test_navigator.html new file mode 100644 index 0000000000..a9ca9cad66 --- /dev/null +++ b/dom/workers/test/test_navigator.html @@ -0,0 +1,27 @@ + + + + + + Test for DOM Worker Navigator + + + + +

+ +
+
+
+
+ + diff --git a/dom/workers/test/test_navigator.js b/dom/workers/test/test_navigator.js new file mode 100644 index 0000000000..c1eab3aa89 --- /dev/null +++ b/dom/workers/test/test_navigator.js @@ -0,0 +1,10 @@ +SimpleTest.waitForExplicitFinish(); + +// This test loads in an iframe, to ensure that the navigator instance is +// loaded with the correct value of the preference. +SpecialPowers.pushPrefEnv({ set: [["dom.netinfo.enabled", true]] }, () => { + let iframe = document.createElement("iframe"); + iframe.id = "f1"; + iframe.src = "test_navigator_iframe.html"; + document.body.appendChild(iframe); +}); diff --git a/dom/workers/test/test_navigator_iframe.html b/dom/workers/test/test_navigator_iframe.html new file mode 100644 index 0000000000..907739e90a --- /dev/null +++ b/dom/workers/test/test_navigator_iframe.html @@ -0,0 +1,24 @@ + + + + + + Test for DOM Worker Navigator + + + + +

+ +
+
+
+ + diff --git a/dom/workers/test/test_navigator_iframe.js b/dom/workers/test/test_navigator_iframe.js new file mode 100644 index 0000000000..fd6269cebe --- /dev/null +++ b/dom/workers/test/test_navigator_iframe.js @@ -0,0 +1,64 @@ +var worker = new Worker("navigator_worker.js"); + +var is = window.parent.is; +var ok = window.parent.ok; +var SimpleTest = window.parent.SimpleTest; + +worker.onmessage = function (event) { + var args = JSON.parse(event.data); + + if (args.name == "testFinished") { + SimpleTest.finish(); + return; + } + + if (typeof navigator[args.name] == "undefined") { + ok(false, "Navigator has no '" + args.name + "' property!"); + return; + } + + if (args.name === "languages") { + is( + navigator.languages.toString(), + args.value.toString(), + "languages matches" + ); + return; + } + + const objectProperties = [ + "connection", + "gpu", + "locks", + "mediaCapabilities", + "storage", + ]; + + if (objectProperties.includes(args.name)) { + is( + typeof navigator[args.name], + typeof args.value, + `${args.name} type matches` + ); + return; + } + + is( + navigator[args.name], + args.value, + "Mismatched navigator string for " + args.name + "!" + ); +}; + +worker.onerror = function (event) { + ok(false, "Worker had an error: " + event.message); + SimpleTest.finish(); +}; + +var { AppConstants } = SpecialPowers.ChromeUtils.importESModule( + "resource://gre/modules/AppConstants.sys.mjs" +); +var isNightly = AppConstants.NIGHTLY_BUILD; +var isRelease = AppConstants.RELEASE_OR_BETA; + +worker.postMessage({ isNightly, isRelease }); diff --git a/dom/workers/test/test_navigator_languages.html b/dom/workers/test/test_navigator_languages.html new file mode 100644 index 0000000000..e4b6fec9a6 --- /dev/null +++ b/dom/workers/test/test_navigator_languages.html @@ -0,0 +1,58 @@ + + + + + + Test for DOM Worker Navigator.languages + + + + +

+ +
+
+
+ + diff --git a/dom/workers/test/test_navigator_secureContext.html b/dom/workers/test/test_navigator_secureContext.html new file mode 100644 index 0000000000..ac5ceb6628 --- /dev/null +++ b/dom/workers/test/test_navigator_secureContext.html @@ -0,0 +1,27 @@ + + + + + + Test for DOM Worker Navigator + + + + +

+ +
+
+
+
+ + diff --git a/dom/workers/test/test_navigator_workers_hardwareConcurrency.html b/dom/workers/test/test_navigator_workers_hardwareConcurrency.html new file mode 100644 index 0000000000..e80c211ce7 --- /dev/null +++ b/dom/workers/test/test_navigator_workers_hardwareConcurrency.html @@ -0,0 +1,56 @@ + + + + + Test for Navigator.hardwareConcurrency + + + + + +

+ +
+
+ + diff --git a/dom/workers/test/test_newError.html b/dom/workers/test/test_newError.html new file mode 100644 index 0000000000..9dd0889df8 --- /dev/null +++ b/dom/workers/test/test_newError.html @@ -0,0 +1,33 @@ + + + + Test for DOM Worker Threads + + + + +
+
+
+ + diff --git a/dom/workers/test/test_notification.html b/dom/workers/test/test_notification.html new file mode 100644 index 0000000000..0171dea0f2 --- /dev/null +++ b/dom/workers/test/test_notification.html @@ -0,0 +1,47 @@ + + + + + Bug 916893 + + + + + + +Bug 916893 +

+ +
+
+ + + diff --git a/dom/workers/test/test_notification_child.html b/dom/workers/test/test_notification_child.html new file mode 100644 index 0000000000..4f9523a03f --- /dev/null +++ b/dom/workers/test/test_notification_child.html @@ -0,0 +1,46 @@ + + + + + Bug 916893 - Test Notifications in child workers. + + + + + + +Bug 916893 +

+ +
+
+ + + diff --git a/dom/workers/test/test_notification_permission.html b/dom/workers/test/test_notification_permission.html new file mode 100644 index 0000000000..904cfcdef2 --- /dev/null +++ b/dom/workers/test/test_notification_permission.html @@ -0,0 +1,48 @@ + + + + + Bug 916893 - Make sure error is fired on Notification if permission is denied. + + + + + + +Bug 916893 +

+ +
+
+ + + diff --git a/dom/workers/test/test_onLine.html b/dom/workers/test/test_onLine.html new file mode 100644 index 0000000000..b0429e7d96 --- /dev/null +++ b/dom/workers/test/test_onLine.html @@ -0,0 +1,73 @@ + + + + + Test for Bug 925437 (worker online/offline events) + + + +Mozilla Bug 925437 +

+ +
+
+ + + + diff --git a/dom/workers/test/test_promise.html b/dom/workers/test/test_promise.html new file mode 100644 index 0000000000..7c3ef09a98 --- /dev/null +++ b/dom/workers/test/test_promise.html @@ -0,0 +1,43 @@ + + + + + Test for Promise object in workers + + + + +

+ +

+
+
+
+
diff --git a/dom/workers/test/test_promise_resolved_with_string.html b/dom/workers/test/test_promise_resolved_with_string.html
new file mode 100644
index 0000000000..8c0b0aca49
--- /dev/null
+++ b/dom/workers/test/test_promise_resolved_with_string.html
@@ -0,0 +1,41 @@
+
+
+
+
+  
+  Test for Bug 1027221
+  
+  
+  
+
+
+Mozilla Bug 1027221
+

+ +
+
+ + diff --git a/dom/workers/test/test_readableStream_when_closing.html b/dom/workers/test/test_readableStream_when_closing.html new file mode 100644 index 0000000000..24d5bf3821 --- /dev/null +++ b/dom/workers/test/test_readableStream_when_closing.html @@ -0,0 +1,61 @@ + + + + + Test for ReadableStream+fetch when the worker is closing + + + + + + + diff --git a/dom/workers/test/test_recursion.html b/dom/workers/test/test_recursion.html new file mode 100644 index 0000000000..8e38a80351 --- /dev/null +++ b/dom/workers/test/test_recursion.html @@ -0,0 +1,69 @@ + + + + + + Test for DOM Worker Threads Recursion + + + + +

+ +
+
+
+ + diff --git a/dom/workers/test/test_recursiveOnerror.html b/dom/workers/test/test_recursiveOnerror.html new file mode 100644 index 0000000000..e6c040439d --- /dev/null +++ b/dom/workers/test/test_recursiveOnerror.html @@ -0,0 +1,44 @@ + + + + + + + + + + + diff --git a/dom/workers/test/test_referrer.html b/dom/workers/test/test_referrer.html new file mode 100644 index 0000000000..a95fe91809 --- /dev/null +++ b/dom/workers/test/test_referrer.html @@ -0,0 +1,58 @@ + + + + + Test the referrer of workers + + + + +

+ +

+
+
+
+
diff --git a/dom/workers/test/test_referrer_header_worker.html b/dom/workers/test/test_referrer_header_worker.html
new file mode 100644
index 0000000000..64d245232f
--- /dev/null
+++ b/dom/workers/test/test_referrer_header_worker.html
@@ -0,0 +1,38 @@
+
+
+
+
+  
+  Test the referrer of workers
+  
+  
+
+
+  
+
+
diff --git a/dom/workers/test/test_resolveWorker-assignment.html b/dom/workers/test/test_resolveWorker-assignment.html
new file mode 100644
index 0000000000..2f3be19668
--- /dev/null
+++ b/dom/workers/test/test_resolveWorker-assignment.html
@@ -0,0 +1,30 @@
+
+
+
+  
+    
+    
+  
+  
+    
+  
+
diff --git a/dom/workers/test/test_resolveWorker.html b/dom/workers/test/test_resolveWorker.html
new file mode 100644
index 0000000000..0d91a9f90a
--- /dev/null
+++ b/dom/workers/test/test_resolveWorker.html
@@ -0,0 +1,31 @@
+
+
+
+  
+    
+    
+  
+  
+    
+  
+
diff --git a/dom/workers/test/test_rvals.html b/dom/workers/test/test_rvals.html
new file mode 100644
index 0000000000..799c42779d
--- /dev/null
+++ b/dom/workers/test/test_rvals.html
@@ -0,0 +1,35 @@
+
+
+
+
+  Test for bug 911085
+  
+  
+
+
+
+
+
+
diff --git a/dom/workers/test/test_setTimeoutWith0.html b/dom/workers/test/test_setTimeoutWith0.html
new file mode 100644
index 0000000000..8685eacfe5
--- /dev/null
+++ b/dom/workers/test/test_setTimeoutWith0.html
@@ -0,0 +1,19 @@
+
+
+  Test for DOM Worker setTimeout and strings containing 0
+  
+  
+
+
+
+
+
diff --git a/dom/workers/test/test_sharedWorker.html b/dom/workers/test/test_sharedWorker.html
new file mode 100644
index 0000000000..b8ced379d5
--- /dev/null
+++ b/dom/workers/test/test_sharedWorker.html
@@ -0,0 +1,71 @@
+
+
+
+  
+    Test for SharedWorker
+    
+    
+  
+  
+    

+ +
+      
+    
+ + diff --git a/dom/workers/test/test_sharedWorker_lifetime.html b/dom/workers/test/test_sharedWorker_lifetime.html new file mode 100644 index 0000000000..b987f0d7cf --- /dev/null +++ b/dom/workers/test/test_sharedWorker_lifetime.html @@ -0,0 +1,30 @@ + + + + + Test for MessagePort and SharedWorkers + + + + + + + diff --git a/dom/workers/test/test_sharedWorker_ports.html b/dom/workers/test/test_sharedWorker_ports.html new file mode 100644 index 0000000000..6befd4920c --- /dev/null +++ b/dom/workers/test/test_sharedWorker_ports.html @@ -0,0 +1,41 @@ + + + + + Test for MessagePort and SharedWorkers + + + + + + + diff --git a/dom/workers/test/test_sharedWorker_privateBrowsing.html b/dom/workers/test/test_sharedWorker_privateBrowsing.html new file mode 100644 index 0000000000..f76e3db307 --- /dev/null +++ b/dom/workers/test/test_sharedWorker_privateBrowsing.html @@ -0,0 +1,102 @@ + + + Test for SharedWorker - Private Browsing + + + + + + + + diff --git a/dom/workers/test/test_sharedWorker_thirdparty.html b/dom/workers/test/test_sharedWorker_thirdparty.html new file mode 100644 index 0000000000..caeb122bba --- /dev/null +++ b/dom/workers/test/test_sharedWorker_thirdparty.html @@ -0,0 +1,54 @@ + + + + + Test for SharedWorker in 3rd Party Iframes + + + + + + + diff --git a/dom/workers/test/test_sharedworker_event_listener_leaks.html b/dom/workers/test/test_sharedworker_event_listener_leaks.html new file mode 100644 index 0000000000..4016cdda48 --- /dev/null +++ b/dom/workers/test/test_sharedworker_event_listener_leaks.html @@ -0,0 +1,51 @@ + + + + + Bug 1450358 - Test SharedWorker event listener leak conditions + + + + + + + + + diff --git a/dom/workers/test/test_shutdownCheck.xhtml b/dom/workers/test/test_shutdownCheck.xhtml new file mode 100644 index 0000000000..257b37fe32 --- /dev/null +++ b/dom/workers/test/test_shutdownCheck.xhtml @@ -0,0 +1,61 @@ + + + + + + + diff --git a/dom/workers/test/test_simpleThread.html b/dom/workers/test/test_simpleThread.html new file mode 100644 index 0000000000..aee5765170 --- /dev/null +++ b/dom/workers/test/test_simpleThread.html @@ -0,0 +1,74 @@ + + + + + + Test for DOM Worker Threads (Bug 437152) + + + + +DOM Worker Threads Bug 437152 +

+ +
+
+
+ + diff --git a/dom/workers/test/test_sourcemap_header.html b/dom/workers/test/test_sourcemap_header.html new file mode 100644 index 0000000000..250b30f079 --- /dev/null +++ b/dom/workers/test/test_sourcemap_header.html @@ -0,0 +1,22 @@ + + + + + Test for DOM Worker + SourceMap header + + + + + +

+ +

+
+
+
+
+
diff --git a/dom/workers/test/test_subworkers_suspended.html b/dom/workers/test/test_subworkers_suspended.html
new file mode 100644
index 0000000000..d5c62a28d0
--- /dev/null
+++ b/dom/workers/test/test_subworkers_suspended.html
@@ -0,0 +1,145 @@
+
+
+
+  
+  Test for sub workers+bfcache behavior
+  
+  
+
+
+  
+
+
diff --git a/dom/workers/test/test_suspend.html b/dom/workers/test/test_suspend.html
new file mode 100644
index 0000000000..9ab1a6a7ec
--- /dev/null
+++ b/dom/workers/test/test_suspend.html
@@ -0,0 +1,188 @@
+
+
+
+
+  
+  Test for DOM Worker Threads
+  
+  
+
+
+

+ +
+
+
+ + diff --git a/dom/workers/test/test_terminate.html b/dom/workers/test/test_terminate.html new file mode 100644 index 0000000000..c19d65770b --- /dev/null +++ b/dom/workers/test/test_terminate.html @@ -0,0 +1,100 @@ + + + + + + Test for DOM Worker Navigator + + + + +

+ +
+
+
+ + diff --git a/dom/workers/test/test_threadErrors.html b/dom/workers/test/test_threadErrors.html new file mode 100644 index 0000000000..1eb33244ba --- /dev/null +++ b/dom/workers/test/test_threadErrors.html @@ -0,0 +1,64 @@ + + + + + + Test for DOM Worker Threads (Bug 437152) + + + + +DOM Worker Threads Bug 437152 +

+ +
+
+
+ + diff --git a/dom/workers/test/test_threadTimeouts.html b/dom/workers/test/test_threadTimeouts.html new file mode 100644 index 0000000000..93a8d9243c --- /dev/null +++ b/dom/workers/test/test_threadTimeouts.html @@ -0,0 +1,60 @@ + + + + + + Test for DOM Worker Threads (Bug 437152) + + + + +DOM Worker Threads Bug 437152 +

+ +
+
+
+ + diff --git a/dom/workers/test/test_throwingOnerror.html b/dom/workers/test/test_throwingOnerror.html new file mode 100644 index 0000000000..0ed1f74247 --- /dev/null +++ b/dom/workers/test/test_throwingOnerror.html @@ -0,0 +1,54 @@ + + + + + + Test for DOM Worker Threads Recursion + + + + +

+ +
+
+
+ + diff --git a/dom/workers/test/test_timeoutTracing.html b/dom/workers/test/test_timeoutTracing.html new file mode 100644 index 0000000000..8e64564b72 --- /dev/null +++ b/dom/workers/test/test_timeoutTracing.html @@ -0,0 +1,47 @@ + + + + + + Test for DOM Worker Threads + + + + +
+
+
+ + diff --git a/dom/workers/test/test_transferable.html b/dom/workers/test/test_transferable.html new file mode 100644 index 0000000000..ac490f369a --- /dev/null +++ b/dom/workers/test/test_transferable.html @@ -0,0 +1,123 @@ + + + + + + Test for DOM Worker transferable objects + + + + +

+ +
+
+
+ + diff --git a/dom/workers/test/test_worker_interfaces.html b/dom/workers/test/test_worker_interfaces.html new file mode 100644 index 0000000000..b051e01242 --- /dev/null +++ b/dom/workers/test/test_worker_interfaces.html @@ -0,0 +1,19 @@ + + + + + Validate Interfaces Exposed to Workers + + + + + + + + + diff --git a/dom/workers/test/test_worker_interfaces.js b/dom/workers/test/test_worker_interfaces.js new file mode 100644 index 0000000000..d2e42f5922 --- /dev/null +++ b/dom/workers/test/test_worker_interfaces.js @@ -0,0 +1,543 @@ +// This is a list of all interfaces that are exposed to workers. +// Please only add things to this list with great care and proper review +// from the associated module peers. + +// This file lists global interfaces we want exposed and verifies they +// are what we intend. Each entry in the arrays below can either be a +// simple string with the interface name, or an object with a 'name' +// property giving the interface name as a string, and additional +// properties which qualify the exposure of that interface. For example: +// +// [ +// "AGlobalInterface", // secure context only +// { name: "ExperimentalThing", release: false }, +// { name: "ReallyExperimentalThing", nightly: true }, +// { name: "DesktopOnlyThing", desktop: true }, +// { name: "FancyControl", xbl: true }, +// { name: "DisabledEverywhere", disabled: true }, +// ]; +// +// See createInterfaceMap() below for a complete list of properties. +// +// The values of the properties need to be literal true/false +// (e.g. indicating whether something is enabled on a particular +// channel/OS). If we ever end up in a situation where a propert +// value needs to depend on channel or OS, we will need to make sure +// we have that information before setting up the property lists. + +// IMPORTANT: Do not change this list without review from +// a JavaScript Engine peer! +let wasmGlobalEntry = { + name: "WebAssembly", + insecureContext: true, + disabled: !getJSTestingFunctions().wasmIsSupportedByHardware(), +}; +let wasmGlobalInterfaces = [ + { name: "Module", insecureContext: true }, + { name: "Instance", insecureContext: true }, + { name: "Memory", insecureContext: true }, + { name: "Table", insecureContext: true }, + { name: "Global", insecureContext: true }, + { name: "CompileError", insecureContext: true }, + { name: "LinkError", insecureContext: true }, + { name: "RuntimeError", insecureContext: true }, + { name: "Function", insecureContext: true, nightly: true }, + { name: "Exception", insecureContext: true }, + { name: "Tag", insecureContext: true }, + { name: "compile", insecureContext: true }, + { name: "compileStreaming", insecureContext: true }, + { name: "instantiate", insecureContext: true }, + { name: "instantiateStreaming", insecureContext: true }, + { name: "validate", insecureContext: true }, +]; +// IMPORTANT: Do not change this list without review from +// a JavaScript Engine peer! +let ecmaGlobals = [ + { name: "AggregateError", insecureContext: true }, + { name: "Array", insecureContext: true }, + { name: "ArrayBuffer", insecureContext: true }, + { name: "Atomics", insecureContext: true }, + { name: "BigInt", insecureContext: true }, + { name: "BigInt64Array", insecureContext: true }, + { name: "BigUint64Array", insecureContext: true }, + { name: "Boolean", insecureContext: true }, + { name: "DataView", insecureContext: true }, + { name: "Date", insecureContext: true }, + { name: "Error", insecureContext: true }, + { name: "EvalError", insecureContext: true }, + { name: "FinalizationRegistry", insecureContext: true }, + { name: "Float32Array", insecureContext: true }, + { name: "Float64Array", insecureContext: true }, + { name: "Function", insecureContext: true }, + { name: "Infinity", insecureContext: true }, + { name: "Int16Array", insecureContext: true }, + { name: "Int32Array", insecureContext: true }, + { name: "Int8Array", insecureContext: true }, + { name: "InternalError", insecureContext: true }, + { name: "Intl", insecureContext: true }, + { name: "JSON", insecureContext: true }, + { name: "Map", insecureContext: true }, + { name: "MediaCapabilities", insecureContext: true }, + { name: "MediaCapabilitiesInfo", insecureContext: true }, + { name: "Math", insecureContext: true }, + { name: "NaN", insecureContext: true }, + { name: "Number", insecureContext: true }, + { name: "Object", insecureContext: true }, + { name: "Promise", insecureContext: true }, + { name: "Proxy", insecureContext: true }, + { name: "RangeError", insecureContext: true }, + { name: "ReferenceError", insecureContext: true }, + { name: "Reflect", insecureContext: true }, + { name: "RegExp", insecureContext: true }, + { name: "Set", insecureContext: true }, + { + name: "SharedArrayBuffer", + insecureContext: true, + crossOringinIsolated: true, + }, + { name: "String", insecureContext: true }, + { name: "Symbol", insecureContext: true }, + { name: "SyntaxError", insecureContext: true }, + { name: "TypeError", insecureContext: true }, + { name: "Uint16Array", insecureContext: true }, + { name: "Uint32Array", insecureContext: true }, + { name: "Uint8Array", insecureContext: true }, + { name: "Uint8ClampedArray", insecureContext: true }, + { name: "URIError", insecureContext: true }, + { name: "WeakMap", insecureContext: true }, + { name: "WeakRef", insecureContext: true }, + { name: "WeakSet", insecureContext: true }, + wasmGlobalEntry, + { name: "decodeURI", insecureContext: true }, + { name: "decodeURIComponent", insecureContext: true }, + { name: "encodeURI", insecureContext: true }, + { name: "encodeURIComponent", insecureContext: true }, + { name: "escape", insecureContext: true }, + { name: "eval", insecureContext: true }, + { name: "globalThis", insecureContext: true }, + { name: "isFinite", insecureContext: true }, + { name: "isNaN", insecureContext: true }, + { name: "parseFloat", insecureContext: true }, + { name: "parseInt", insecureContext: true }, + { name: "undefined", insecureContext: true }, + { name: "unescape", insecureContext: true }, +]; +// IMPORTANT: Do not change the list above without review from +// a JavaScript Engine peer! + +// IMPORTANT: Do not change the list below without review from a DOM peer! +let interfaceNamesInGlobalScope = [ + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "AbortController", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "AbortSignal", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "Blob", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "BroadcastChannel", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "ByteLengthQueuingStrategy", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + "Cache", + // IMPORTANT: Do not change this list without review from a DOM peer! + "CacheStorage", + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "CanvasGradient", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "CanvasPattern", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "CloseEvent", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "CompressionStream", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "CountQueuingStrategy", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "Crypto", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "CryptoKey" }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "CustomEvent", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "DecompressionStream", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "DedicatedWorkerGlobalScope", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "Directory", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "DOMException", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "DOMMatrix", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "DOMMatrixReadOnly", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "DOMPoint", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "DOMPointReadOnly", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "DOMQuad", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "DOMRect", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "DOMRectReadOnly", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "DOMRequest", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "DOMStringList", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "ErrorEvent", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "Event", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "EventSource", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "EventTarget", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "File", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "FileList", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "FileReader", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "FileReaderSync", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "FileSystemDirectoryHandle" }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "FileSystemFileHandle" }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "FileSystemHandle" }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "FileSystemSyncAccessHandle" }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "FileSystemWritableFileStream" }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "FontFace", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "FontFaceSet", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "FontFaceSetLoadEvent", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "FormData", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "Headers", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "IDBCursor", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "IDBCursorWithValue", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "IDBDatabase", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "IDBFactory", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "IDBIndex", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "IDBKeyRange", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "IDBObjectStore", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "IDBOpenDBRequest", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "IDBRequest", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "IDBTransaction", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "IDBVersionChangeEvent", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "ImageBitmap", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "ImageBitmapRenderingContext", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "ImageData", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + "Lock", + // IMPORTANT: Do not change this list without review from a DOM peer! + "LockManager", + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "MessageChannel", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "MessageEvent", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "MessagePort", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "NetworkInformation", insecureContext: true, disabled: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "Notification", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "OffscreenCanvas", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "OffscreenCanvasRenderingContext2D", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "Path2D", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "Performance", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "PerformanceEntry", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "PerformanceMark", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "PerformanceMeasure", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "PerformanceObserver", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "PerformanceObserverEntryList", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "PerformanceResourceTiming", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "PerformanceServerTiming", insecureContext: false }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "ProgressEvent", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "PromiseRejectionEvent", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "ReadableByteStreamController", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "ReadableStream", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "ReadableStreamBYOBReader", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "ReadableStreamBYOBRequest", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "ReadableStreamDefaultController", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "ReadableStreamDefaultReader", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "Request", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "Response", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "Scheduler", insecureContext: true, nightly: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "StorageManager", fennec: false }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "SubtleCrypto" }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "TaskController", insecureContext: true, nightly: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "TaskPriorityChangeEvent", insecureContext: true, nightly: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "TaskSignal", insecureContext: true, nightly: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "TextDecoder", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "TextDecoderStream", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "TextEncoder", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "TextEncoderStream", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "TextMetrics", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "TransformStream", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { + name: "TransformStreamDefaultController", + insecureContext: true, + }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "XMLHttpRequest", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "XMLHttpRequestEventTarget", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "XMLHttpRequestUpload", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "URL", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "URLSearchParams", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "WebGL2RenderingContext", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "WebGLActiveInfo", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "WebGLBuffer", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "WebGLContextEvent", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "WebGLFramebuffer", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "WebGLProgram", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "WebGLQuery", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "WebGLRenderbuffer", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "WebGLRenderingContext", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "WebGLSampler", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "WebGLShader", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "WebGLShaderPrecisionFormat", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "WebGLSync", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "WebGLTexture", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "WebGLTransformFeedback", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "WebGLUniformLocation", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "WebGLVertexArrayObject", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "WebSocket", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "WebTransport", insecureContext: false }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "WebTransportBidirectionalStream", insecureContext: false }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "WebTransportDatagramDuplexStream", insecureContext: false }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "WebTransportError", insecureContext: false }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "WebTransportReceiveStream", insecureContext: false }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "WebTransportSendStream", insecureContext: false }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "Worker", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "WorkerGlobalScope", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "WorkerLocation", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "WorkerNavigator", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "WritableStream", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "WritableStreamDefaultController", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "WritableStreamDefaultWriter", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "cancelAnimationFrame", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "close", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "console", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "name", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "onmessage", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "onmessageerror", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "postMessage", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "requestAnimationFrame", insecureContext: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! +]; +// IMPORTANT: Do not change the list above without review from a DOM peer! + +// List of functions defined on the global by the test harness or this test +// file. +let testFunctions = [ + "ok", + "is", + "workerTestArrayEquals", + "workerTestDone", + "workerTestGetPermissions", + "workerTestGetHelperData", + "entryDisabled", + "createInterfaceMap", + "runTest", +]; + +function entryDisabled( + entry, + { + isNightly, + isEarlyBetaOrEarlier, + isRelease, + isDesktop, + isAndroid, + isInsecureContext, + isFennec, + isCrossOringinIsolated, + } +) { + return ( + entry.nightly === !isNightly || + (entry.nightlyAndroid === !(isAndroid && isNightly) && isAndroid) || + entry.desktop === !isDesktop || + (entry.android === !isAndroid && !entry.nightlyAndroid) || + entry.fennecOrDesktop === (isAndroid && !isFennec) || + entry.fennec === !isFennec || + entry.release === !isRelease || + // The insecureContext test is very purposefully converting + // entry.insecureContext to boolean, so undefined will convert to + // false. That way entries without an insecureContext annotation + // will get treated as "insecureContext: false", which means exposed + // only in secure contexts. + (isInsecureContext && !entry.insecureContext) || + entry.earlyBetaOrEarlier === !isEarlyBetaOrEarlier || + entry.crossOringinIsolated === !isCrossOringinIsolated || + entry.disabled + ); +} + +function createInterfaceMap(data, ...interfaceGroups) { + var interfaceMap = {}; + + function addInterfaces(interfaces) { + for (var entry of interfaces) { + if (typeof entry === "string") { + ok(!(entry in interfaceMap), "duplicate entry for " + entry); + interfaceMap[entry] = !data.isInsecureContext; + } else { + ok(!(entry.name in interfaceMap), "duplicate entry for " + entry.name); + ok(!("pref" in entry), "Bogus pref annotation for " + entry.name); + interfaceMap[entry.name] = !entryDisabled(entry, data); + } + } + } + + for (let interfaceGroup of interfaceGroups) { + addInterfaces(interfaceGroup); + } + + return interfaceMap; +} + +function runTest(parentName, parent, data, ...interfaceGroups) { + var interfaceMap = createInterfaceMap(data, ...interfaceGroups); + for (var name of Object.getOwnPropertyNames(parent)) { + // Ignore functions on the global that are part of the test (harness). + if (parent === self && testFunctions.includes(name)) { + continue; + } + ok( + interfaceMap[name], + "If this is failing: DANGER, are you sure you want to expose the new interface " + + name + + " to all webpages as a property of " + + parentName + + "? Do not make a change to this file without a " + + " review from a DOM peer for that specific change!!! (or a JS peer for changes to ecmaGlobals)" + ); + delete interfaceMap[name]; + } + for (var name of Object.keys(interfaceMap)) { + ok( + name in parent === interfaceMap[name], + name + + " should " + + (interfaceMap[name] ? "" : " NOT") + + " be defined on " + + parentName + ); + if (!interfaceMap[name]) { + delete interfaceMap[name]; + } + } + is( + Object.keys(interfaceMap).length, + 0, + "The following interface(s) are not enumerated: " + + Object.keys(interfaceMap).join(", ") + ); +} + +workerTestGetHelperData(function (data) { + runTest("self", self, data, ecmaGlobals, interfaceNamesInGlobalScope); + if (WebAssembly && !entryDisabled(wasmGlobalEntry, data)) { + runTest("WebAssembly", WebAssembly, data, wasmGlobalInterfaces); + } + workerTestDone(); +}); diff --git a/dom/workers/test/test_worker_interfaces_secureContext.html b/dom/workers/test/test_worker_interfaces_secureContext.html new file mode 100644 index 0000000000..7d26cd2131 --- /dev/null +++ b/dom/workers/test/test_worker_interfaces_secureContext.html @@ -0,0 +1,19 @@ + + + + + Validate Interfaces Exposed to Workers + + + + + + + + + diff --git a/dom/workers/test/threadErrors_worker1.js b/dom/workers/test/threadErrors_worker1.js new file mode 100644 index 0000000000..c0ddade82c --- /dev/null +++ b/dom/workers/test/threadErrors_worker1.js @@ -0,0 +1,8 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +// Syntax error +onmessage = function(event) { + for (var i = 0; i < 10) { } +} diff --git a/dom/workers/test/threadErrors_worker2.js b/dom/workers/test/threadErrors_worker2.js new file mode 100644 index 0000000000..da79569def --- /dev/null +++ b/dom/workers/test/threadErrors_worker2.js @@ -0,0 +1,8 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +// Bad function error +onmessage = function (event) { + foopy(); +}; diff --git a/dom/workers/test/threadErrors_worker3.js b/dom/workers/test/threadErrors_worker3.js new file mode 100644 index 0000000000..e470680981 --- /dev/null +++ b/dom/workers/test/threadErrors_worker3.js @@ -0,0 +1,8 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +// Unhandled exception in body +onmessage = function (event) {}; + +throw new Error("Bah!"); diff --git a/dom/workers/test/threadErrors_worker4.js b/dom/workers/test/threadErrors_worker4.js new file mode 100644 index 0000000000..88b089aa3b --- /dev/null +++ b/dom/workers/test/threadErrors_worker4.js @@ -0,0 +1,8 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +// Throwing message listener +onmessage = function (event) { + throw new Error("Bah!"); +}; diff --git a/dom/workers/test/threadTimeouts_worker.js b/dom/workers/test/threadTimeouts_worker.js new file mode 100644 index 0000000000..7aaac03d28 --- /dev/null +++ b/dom/workers/test/threadTimeouts_worker.js @@ -0,0 +1,44 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +var gTimeoutId; +var gTimeoutCount = 0; +var gIntervalCount = 0; + +function timeoutFunc() { + if (++gTimeoutCount > 1) { + throw new Error("Timeout called more than once!"); + } + postMessage("timeoutFinished"); +} + +function intervalFunc() { + if (++gIntervalCount == 2) { + postMessage("intervalFinished"); + } +} + +function messageListener(event) { + switch (event.data) { + case "startTimeout": + gTimeoutId = setTimeout(timeoutFunc, 2000); + clearTimeout(gTimeoutId); + gTimeoutId = setTimeout(timeoutFunc, 2000); + break; + case "startInterval": + gTimeoutId = setInterval(intervalFunc, 2000); + break; + case "cancelInterval": + clearInterval(gTimeoutId); + postMessage("intervalCanceled"); + break; + case "startExpression": + setTimeout("this.postMessage('expressionFinished');", 2000); + break; + default: + throw "Bad message: " + event.data; + } +} + +addEventListener("message", messageListener, false); diff --git a/dom/workers/test/throwingOnerror_worker.js b/dom/workers/test/throwingOnerror_worker.js new file mode 100644 index 0000000000..47b727f56a --- /dev/null +++ b/dom/workers/test/throwingOnerror_worker.js @@ -0,0 +1,15 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +onerror = function (event) { + throw "bar"; +}; + +var count = 0; +onmessage = function (event) { + if (!count++) { + throw "foo"; + } + postMessage(""); +}; diff --git a/dom/workers/test/timeoutTracing_worker.js b/dom/workers/test/timeoutTracing_worker.js new file mode 100644 index 0000000000..0a0c6d6fdf --- /dev/null +++ b/dom/workers/test/timeoutTracing_worker.js @@ -0,0 +1,15 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +onmessage = function (event) { + throw "No messages should reach me!"; +}; + +setInterval(function () { + postMessage("Still alive!"); +}, 20); +setInterval(";", 20); + +postMessage("Begin!"); diff --git a/dom/workers/test/transferable_worker.js b/dom/workers/test/transferable_worker.js new file mode 100644 index 0000000000..d0fa41cad1 --- /dev/null +++ b/dom/workers/test/transferable_worker.js @@ -0,0 +1,40 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +onmessage = function (event) { + if ("notEmpty" in event.data && "byteLength" in event.data.notEmpty) { + postMessage({ + event: "W: NotEmpty object received: " + event.data.notEmpty.byteLength, + status: event.data.notEmpty.byteLength != 0, + last: false, + }); + } + + var ab = new ArrayBuffer(event.data.size); + postMessage({ + event: "W: The size is: " + event.data.size + " == " + ab.byteLength, + status: ab.byteLength == event.data.size, + last: false, + }); + + postMessage( + { + event: "W: postMessage with arrayBuffer", + status: true, + notEmpty: ab, + ab, + bc: [ab, ab, { dd: ab }], + }, + [ab] + ); + + postMessage({ + event: "W: The size is: 0 == " + ab.byteLength, + status: ab.byteLength == 0, + last: false, + }); + + postMessage({ event: "W: last one!", status: true, last: true }); +}; diff --git a/dom/workers/test/window_suspended.html b/dom/workers/test/window_suspended.html new file mode 100644 index 0000000000..ae5d25df58 --- /dev/null +++ b/dom/workers/test/window_suspended.html @@ -0,0 +1,71 @@ + diff --git a/dom/workers/test/worker_bug1278777.js b/dom/workers/test/worker_bug1278777.js new file mode 100644 index 0000000000..f596ee978b --- /dev/null +++ b/dom/workers/test/worker_bug1278777.js @@ -0,0 +1,9 @@ +var xhr = new XMLHttpRequest(); +xhr.responseType = "blob"; +xhr.open("GET", "worker_bug1278777.js"); + +xhr.onload = function () { + postMessage(xhr.response instanceof Blob); +}; + +xhr.send(); diff --git a/dom/workers/test/worker_bug1301094.js b/dom/workers/test/worker_bug1301094.js new file mode 100644 index 0000000000..90fbe178b5 --- /dev/null +++ b/dom/workers/test/worker_bug1301094.js @@ -0,0 +1,11 @@ +onmessage = function (e) { + var xhr = new XMLHttpRequest(); + xhr.open("POST", "worker_bug1301094.js", false); + xhr.onload = function () { + self.postMessage("OK"); + }; + + var fd = new FormData(); + fd.append("file", e.data); + xhr.send(fd); +}; diff --git a/dom/workers/test/worker_bug1824498.mjs b/dom/workers/test/worker_bug1824498.mjs new file mode 100644 index 0000000000..932bb530ac --- /dev/null +++ b/dom/workers/test/worker_bug1824498.mjs @@ -0,0 +1,4 @@ +/* eslint-disable import/no-unassigned-import */ +/* eslint-disable import/no-unresolved */ +import {} from "./foo"; +import {} from "./bar"; diff --git a/dom/workers/test/worker_consoleAndBlobs.js b/dom/workers/test/worker_consoleAndBlobs.js new file mode 100644 index 0000000000..e95a87fb83 --- /dev/null +++ b/dom/workers/test/worker_consoleAndBlobs.js @@ -0,0 +1,8 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +"use strict"; + +var b = new Blob(["123"], { type: "foo/bar" }); +console.log({ msg: "consoleAndBlobs", blob: b }); diff --git a/dom/workers/test/worker_driver.js b/dom/workers/test/worker_driver.js new file mode 100644 index 0000000000..29a0d50025 --- /dev/null +++ b/dom/workers/test/worker_driver.js @@ -0,0 +1,84 @@ +// Any copyright is dedicated to the Public Domain. +// http://creativecommons.org/publicdomain/zero/1.0/ +// +// Utility script for writing worker tests. In your main document do: +// +// +// +// +// This will then spawn a worker, define some utility functions, and then +// execute the code in myWorkerTestCase.js. You can then use these +// functions in your worker-side test: +// +// ok() - like the SimpleTest assert +// is() - like the SimpleTest assert +// workerTestDone() - like SimpleTest.finish() indicating the test is complete +// +// There are also some functions for requesting information that requires +// SpecialPowers or other main-thread-only resources: +// +// workerTestGetVersion() - request the current version string from the MT +// workerTestGetUserAgent() - request the user agent string from the MT +// workerTestGetOSCPU() - request the navigator.oscpu string from the MT +// +// For an example see test_worker_interfaces.html and test_worker_interfaces.js. + +function workerTestExec(script) { + SimpleTest.waitForExplicitFinish(); + var worker = new Worker("worker_wrapper.js"); + worker.onmessage = function (event) { + if (event.data.type == "finish") { + SimpleTest.finish(); + } else if (event.data.type == "status") { + ok(event.data.status, event.data.msg); + } else if (event.data.type == "getHelperData") { + const { AppConstants } = SpecialPowers.ChromeUtils.importESModule( + "resource://gre/modules/AppConstants.sys.mjs" + ); + const isNightly = AppConstants.NIGHTLY_BUILD; + const isEarlyBetaOrEarlier = AppConstants.EARLY_BETA_OR_EARLIER; + const isRelease = AppConstants.RELEASE_OR_BETA; + const isDesktop = !/Mobile|Tablet/.test(navigator.userAgent); + const isMac = AppConstants.platform == "macosx"; + const isWindows = AppConstants.platform == "win"; + const isAndroid = AppConstants.platform == "android"; + const isLinux = AppConstants.platform == "linux"; + const isInsecureContext = !window.isSecureContext; + // Currently, MOZ_APP_NAME is always "fennec" for all mobile builds, so we can't use AppConstants for this + const isFennec = + isAndroid && + SpecialPowers.Cc["@mozilla.org/android/bridge;1"].getService( + SpecialPowers.Ci.nsIAndroidBridge + ).isFennec; + const isCrossOriginIsolated = window.crossOriginIsolated; + + const result = { + isNightly, + isEarlyBetaOrEarlier, + isRelease, + isDesktop, + isMac, + isWindows, + isAndroid, + isLinux, + isInsecureContext, + isFennec, + isCrossOriginIsolated, + }; + + worker.postMessage({ + type: "returnHelperData", + result, + }); + } + }; + + worker.onerror = function (event) { + ok(false, "Worker had an error: " + event.data); + SimpleTest.finish(); + }; + + worker.postMessage({ script }); +} diff --git a/dom/workers/test/worker_dynamicImport.mjs b/dom/workers/test/worker_dynamicImport.mjs new file mode 100644 index 0000000000..b5c50d30f7 --- /dev/null +++ b/dom/workers/test/worker_dynamicImport.mjs @@ -0,0 +1,2 @@ +/* eslint-disable import/no-unresolved */ +const { o } = await import("./404.js"); diff --git a/dom/workers/test/worker_referrer.js b/dom/workers/test/worker_referrer.js new file mode 100644 index 0000000000..ec9fb1f8a0 --- /dev/null +++ b/dom/workers/test/worker_referrer.js @@ -0,0 +1,9 @@ +onmessage = function () { + importScripts(["referrer.sjs?import"]); + var xhr = new XMLHttpRequest(); + xhr.open("GET", "referrer.sjs?result", true); + xhr.onload = function () { + postMessage(xhr.responseText); + }; + xhr.send(); +}; diff --git a/dom/workers/test/worker_setTimeoutWith0.js b/dom/workers/test/worker_setTimeoutWith0.js new file mode 100644 index 0000000000..2d8e5e6c22 --- /dev/null +++ b/dom/workers/test/worker_setTimeoutWith0.js @@ -0,0 +1,3 @@ +var x = 0; +setTimeout("x++; '\x00'; x++;"); +setTimeout("postMessage(x);"); diff --git a/dom/workers/test/worker_shutdownCheck.js b/dom/workers/test/worker_shutdownCheck.js new file mode 100644 index 0000000000..f51279daf6 --- /dev/null +++ b/dom/workers/test/worker_shutdownCheck.js @@ -0,0 +1 @@ +postMessage("Ok!"); diff --git a/dom/workers/test/worker_suspended.js b/dom/workers/test/worker_suspended.js new file mode 100644 index 0000000000..f2b4146cba --- /dev/null +++ b/dom/workers/test/worker_suspended.js @@ -0,0 +1,39 @@ +var count = 0; + +function do_magic(data) { + caches + .open("test") + .then(function (cache) { + return cache.put( + "http://mochi.test:888/foo", + new Response(data.type + "-" + count++) + ); + }) + .then(function () { + if (count == 1) { + postMessage("ready"); + } + + if (data.loop) { + setTimeout(function () { + do_magic(data); + }, 500); + } + }); +} + +onmessage = function (e) { + if (e.data.type == "page1") { + if (e.data.count > 0) { + var a = new Worker("worker_suspended.js"); + a.postMessage({ type: "page1", count: e.data - 1 }); + a.onmessage = function () { + postMessage("ready"); + }; + } else { + do_magic({ type: e.data.type, loop: true }); + } + } else if (e.data.type == "page2") { + do_magic({ type: e.data.type, loop: false }); + } +}; diff --git a/dom/workers/test/worker_wrapper.js b/dom/workers/test/worker_wrapper.js new file mode 100644 index 0000000000..6a630d92d0 --- /dev/null +++ b/dom/workers/test/worker_wrapper.js @@ -0,0 +1,79 @@ +// Any copyright is dedicated to the Public Domain. +// http://creativecommons.org/publicdomain/zero/1.0/ +// +// Worker-side wrapper script for the worker_driver.js helper code. See +// the comments at the top of worker_driver.js for more information. + +function ok(a, msg) { + dump("OK: " + !!a + " => " + a + ": " + msg + "\n"); + postMessage({ type: "status", status: !!a, msg: a + ": " + msg }); +} + +function is(a, b, msg) { + dump("IS: " + (a === b) + " => " + a + " | " + b + ": " + msg + "\n"); + postMessage({ + type: "status", + status: a === b, + msg: a + " === " + b + ": " + msg, + }); +} + +function workerTestArrayEquals(a, b) { + if (!Array.isArray(a) || !Array.isArray(b) || a.length != b.length) { + return false; + } + for (var i = 0, n = a.length; i < n; ++i) { + if (a[i] !== b[i]) { + return false; + } + } + return true; +} + +function workerTestDone() { + postMessage({ type: "finish" }); +} + +function workerTestGetPermissions(permissions, cb) { + addEventListener("message", function workerTestGetPermissionsCB(e) { + if ( + e.data.type != "returnPermissions" || + !workerTestArrayEquals(permissions, e.data.permissions) + ) { + return; + } + removeEventListener("message", workerTestGetPermissionsCB); + cb(e.data.result); + }); + postMessage({ + type: "getPermissions", + permissions, + }); +} + +function workerTestGetHelperData(cb) { + addEventListener("message", function workerTestGetHelperDataCB(e) { + if (e.data.type !== "returnHelperData") { + return; + } + removeEventListener("message", workerTestGetHelperDataCB); + cb(e.data.result); + }); + postMessage({ + type: "getHelperData", + }); +} + +addEventListener("message", function workerWrapperOnMessage(e) { + removeEventListener("message", workerWrapperOnMessage); + var data = e.data; + try { + importScripts(data.script); + } catch (ex) { + postMessage({ + type: "status", + status: false, + msg: "worker failed to import " + data.script + "; error: " + ex.message, + }); + } +}); diff --git a/dom/workers/test/xpcshell/data/chrome.manifest b/dom/workers/test/xpcshell/data/chrome.manifest new file mode 100644 index 0000000000..611e81fd4e --- /dev/null +++ b/dom/workers/test/xpcshell/data/chrome.manifest @@ -0,0 +1 @@ +content workers ./ diff --git a/dom/workers/test/xpcshell/data/worker.js b/dom/workers/test/xpcshell/data/worker.js new file mode 100644 index 0000000000..0a455f51c3 --- /dev/null +++ b/dom/workers/test/xpcshell/data/worker.js @@ -0,0 +1,6 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +self.onmessage = function (msg) { + self.postMessage("OK"); +}; diff --git a/dom/workers/test/xpcshell/data/worker_fileReader.js b/dom/workers/test/xpcshell/data/worker_fileReader.js new file mode 100644 index 0000000000..44e7e6499b --- /dev/null +++ b/dom/workers/test/xpcshell/data/worker_fileReader.js @@ -0,0 +1,7 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +self.onmessage = function (msg) { + var fr = new FileReader(); + self.postMessage("OK"); +}; diff --git a/dom/workers/test/xpcshell/test_ext_redirects_sw_scripts.js b/dom/workers/test/xpcshell/test_ext_redirects_sw_scripts.js new file mode 100644 index 0000000000..8dbfc5857f --- /dev/null +++ b/dom/workers/test/xpcshell/test_ext_redirects_sw_scripts.js @@ -0,0 +1,415 @@ +/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set sts=2 sw=2 et tw=80: */ +"use strict"; + +const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm"); + +const { TestUtils } = ChromeUtils.importESModule( + "resource://testing-common/TestUtils.sys.mjs" +); + +const { AddonTestUtils } = ChromeUtils.importESModule( + "resource://testing-common/AddonTestUtils.sys.mjs" +); + +const { ExtensionTestUtils } = ChromeUtils.importESModule( + "resource://testing-common/ExtensionXPCShellUtils.sys.mjs" +); + +const { createHttpServer } = AddonTestUtils; + +// Force ServiceWorkerRegistrar to init by calling do_get_profile. +// (This has to be called before AddonTestUtils.init, because it does +// also call do_get_profile internally but it doesn't notify +// profile-after-change). +do_get_profile(true); + +AddonTestUtils.init(this); +ExtensionTestUtils.init(this); + +const server = createHttpServer({ hosts: ["localhost"] }); + +server.registerPathHandler("/page.html", (request, response) => { + info(`/page.html is being requested: ${JSON.stringify(request)}`); + response.write(``); +}); + +server.registerPathHandler("/sw.js", (request, response) => { + info(`/sw.js is being requested: ${JSON.stringify(request)}`); + response.setHeader("Content-Type", "application/javascript"); + response.write(` + dump('Executing http://localhost/sw.js\\n'); + importScripts('sw-imported.js'); + dump('Executed importScripts from http://localhost/sw.js\\n'); + `); +}); + +server.registerPathHandler("/sw-imported.js", (request, response) => { + info(`/sw-imported.js is being requested: ${JSON.stringify(request)}`); + response.setHeader("Content-Type", "application/javascript"); + response.write(` + dump('importScript loaded from http://localhost/sw-imported.js\\n'); + self.onmessage = evt => evt.ports[0].postMessage('original-imported-script'); + `); +}); + +Services.prefs.setBoolPref("dom.serviceWorkers.testing.enabled", true); +// Make sure this test file doesn't run with the legacy behavior by +// setting explicitly the expected default value. +Services.prefs.setBoolPref( + "extensions.filterResponseServiceWorkerScript.disabled", + false +); +registerCleanupFunction(() => { + Services.prefs.clearUserPref("dom.serviceWorkers.testing.enabled"); + Services.prefs.clearUserPref( + "extensions.filterResponseServiceWorkerScript.disabled" + ); +}); + +// Helper function used to be sure to clear any data that a previous test case +// may have left (e.g. service worker registration, cached service worker +// scripts). +// +// NOTE: Given that xpcshell test are running isolated from each other (unlike +// mochitests), we can just clear every storage type supported by clear data +// (instead of cherry picking what we want to clear based on the test cases +// part of this test file). +async function ensureDataCleanup() { + info("Clear any service worker or data previous test cases may have left"); + await new Promise(resolve => + Services.clearData.deleteData(Ci.nsIClearDataService.CLEAR_ALL, resolve) + ); +} + +// Note that the update algorithm (https://w3c.github.io/ServiceWorker/#update-algorithm) +// builds an "updatedResourceMap" as part of its check process. This means that only a +// single fetch will be performed for "sw-imported.js" as part of the update check and its +// resulting install invocation. The installation's call to importScripts when evaluated +// will load the script directly out of the Cache API. +function testSWUpdate(contentPage) { + return contentPage.legacySpawn([], async () => { + const oldReg = await this.content.navigator.serviceWorker.ready; + const reg = await oldReg.update(); + const sw = reg.installing || reg.waiting || reg.active; + return new Promise(resolve => { + const { port1, port2 } = new MessageChannel(); + port1.onmessage = evt => resolve(evt.data); + sw.postMessage("worker-message", [port2]); + }); + }); +} + +add_task(async function test_extension_invalid_sw_scripts_redirect_ignored() { + const extension = ExtensionTestUtils.loadExtension({ + manifest: { + permissions: ["", "webRequest", "webRequestBlocking"], + }, + background() { + browser.webRequest.onBeforeRequest.addListener( + req => { + if (req.url == "http://localhost/sw.js") { + const filter = browser.webRequest.filterResponseData(req.requestId); + filter.ondata = event => filter.write(event.data); + filter.onstop = event => filter.disconnect(); + filter.onerror = () => { + browser.test.sendMessage( + "filter-response-error:mainscript", + filter.error + ); + }; + return { + redirectUrl: browser.runtime.getURL("sw-unexpected-redirect.js"), + }; + } + + if (req.url == "http://localhost/sw-imported.js") { + const filter = browser.webRequest.filterResponseData(req.requestId); + filter.ondata = event => filter.write(event.data); + filter.onstop = event => filter.disconnect(); + filter.onerror = () => { + browser.test.sendMessage( + "filter-response-error:importscript", + filter.error + ); + }; + return { redirectUrl: "about:blank" }; + } + + return {}; + }, + { urls: ["http://localhost/sw.js", "http://localhost/sw-imported.js"] }, + ["blocking"] + ); + }, + files: { + "sw-unexpected-redirect.js": ` + dump('importScript redirected to moz-extension://UUID/sw-unexpected-redirect.js\\n'); + self.onmessage = evt => evt.ports[0].postMessage('sw-unexpected-redirect'); + `, + }, + }); + + // Start the test extension to redirect importScripts requests. + await extension.startup(); + + function awaitConsoleMessage(regexp) { + return new Promise(resolve => { + Services.console.registerListener(function listener(message) { + if (regexp.test(message.message)) { + Services.console.unregisterListener(listener); + resolve(message); + } + }); + }); + } + + const awaitIgnoredMainScriptRedirect = awaitConsoleMessage( + /Invalid redirectUrl .* on service worker main script/ + ); + const awaitIgnoredImportScriptRedirect = awaitConsoleMessage( + /Invalid redirectUrl .* on service worker imported script/ + ); + + let contentPage = await ExtensionTestUtils.loadContentPage( + "http://localhost/page.html" + ); + + // Register the worker while the test extension isn't loaded and cannot + // intercept and redirect the importedScripts requests. + info("Register service worker from a content webpage"); + let workerMessage = await contentPage.legacySpawn([], async () => { + const reg = await this.content.navigator.serviceWorker.register("/sw.js"); + return new Promise(resolve => { + const { port1, port2 } = new MessageChannel(); + port1.onmessage = evt => resolve(evt.data); + const sw = reg.active || reg.waiting || reg.installing; + sw.postMessage("worker-message", [port2]); + }); + }); + + equal( + workerMessage, + "original-imported-script", + "Got expected worker reply (importScripts not intercepted)" + ); + + info("Wait for the expected error message on main script redirect"); + const errorMsg = await awaitIgnoredMainScriptRedirect; + ok(errorMsg?.message, `Got error message: ${errorMsg?.message}`); + ok( + errorMsg?.message?.includes(extension.id), + "error message should include the addon id" + ); + ok( + errorMsg?.message?.includes("http://localhost/sw.js"), + "error message should include the sw main script url" + ); + + info("Wait for the expected error message on import script redirect"); + const errorMsg2 = await awaitIgnoredImportScriptRedirect; + ok(errorMsg2?.message, `Got error message: ${errorMsg2?.message}`); + ok( + errorMsg2?.message?.includes(extension.id), + "error message should include the addon id" + ); + ok( + errorMsg2?.message?.includes("http://localhost/sw-imported.js"), + "error message should include the sw main script url" + ); + + info("Wait filterResponse error on main script"); + equal( + await extension.awaitMessage("filter-response-error:mainscript"), + "Invalid request ID", + "Got expected error on main script" + ); + info("Wait filterResponse error on import script"); + equal( + await extension.awaitMessage("filter-response-error:importscript"), + "Invalid request ID", + "Got expected error on import script" + ); + + await extension.unload(); + await contentPage.close(); +}); + +add_task(async function test_filter_sw_script() { + const extension = ExtensionTestUtils.loadExtension({ + manifest: { + permissions: [ + "", + "webRequest", + "webRequestBlocking", + "webRequestFilterResponse.serviceWorkerScript", + ], + }, + background() { + browser.webRequest.onBeforeRequest.addListener( + req => { + if (req.url == "http://localhost/sw.js") { + const filter = browser.webRequest.filterResponseData(req.requestId); + let decoder = new TextDecoder("utf-8"); + let encoder = new TextEncoder(); + filter.ondata = event => { + let str = decoder.decode(event.data, { stream: true }); + browser.test.log(`Got filter ondata event: ${str}\n`); + str = ` + dump('Executing filterResponse script for http://localhost/sw.js\\n'); + self.onmessage = evt => evt.ports[0].postMessage('filter-response-script'); + dump('Executed firlterResponse script for http://localhost/sw.js\\n'); + `; + filter.write(encoder.encode(str)); + filter.disconnect(); + }; + } + + return {}; + }, + { urls: ["http://localhost/sw.js", "http://localhost/sw-imported.js"] }, + ["blocking"] + ); + }, + }); + + // Start the test extension to redirect importScripts requests. + await extension.startup(); + + await ensureDataCleanup(); + let contentPage = await ExtensionTestUtils.loadContentPage( + "http://localhost/page.html" + ); + + let workerMessage = await contentPage.legacySpawn([], async () => { + const reg = await this.content.navigator.serviceWorker.register("/sw.js"); + return new Promise(resolve => { + const { port1, port2 } = new MessageChannel(); + port1.onmessage = evt => resolve(evt.data); + const sw = reg.active || reg.waiting || reg.installing; + sw.postMessage("worker-message", [port2]); + }); + }); + + equal( + workerMessage, + "filter-response-script", + "Got expected worker reply (filterResponse script)" + ); + + await extension.unload(); + workerMessage = await testSWUpdate(contentPage); + equal( + workerMessage, + "original-imported-script", + "Got expected worker reply (original script)" + ); + + await contentPage.close(); +}); + +add_task(async function test_extension_redirect_sw_imported_script() { + const extension = ExtensionTestUtils.loadExtension({ + manifest: { + permissions: ["", "webRequest", "webRequestBlocking"], + web_accessible_resources: ["sw-imported-1.js", "sw-imported-2.js"], + }, + background() { + let i = 1; + browser.webRequest.onBeforeRequest.addListener( + req => { + browser.test.log( + "Extension is redirecting http://localhost/sw-imported.js" + ); + browser.test.sendMessage("request-redirected"); + return { + redirectUrl: browser.runtime.getURL(`sw-imported-${i++}.js`), + }; + }, + { urls: ["http://localhost/sw-imported.js"] }, + ["blocking"] + ); + }, + files: { + "sw-imported-1.js": ` + dump('importScript redirected to moz-extension://UUID/sw-imported1.js \\n'); + self.onmessage = evt => evt.ports[0].postMessage('redirected-imported-script-1'); + `, + "sw-imported-2.js": ` + dump('importScript redirected to moz-extension://UUID/sw-imported2.js \\n'); + self.onmessage = evt => evt.ports[0].postMessage('redirected-imported-script-2'); + `, + }, + }); + + await ensureDataCleanup(); + const contentPage = await ExtensionTestUtils.loadContentPage( + "http://localhost/page.html" + ); + + // Register the worker while the test extension isn't loaded and cannot + // intercept and redirect the importedScripts requests. + let workerMessage = await contentPage.legacySpawn([], async () => { + const reg = await this.content.navigator.serviceWorker.register("/sw.js"); + return new Promise(resolve => { + const { port1, port2 } = new MessageChannel(); + port1.onmessage = evt => resolve(evt.data); + const sw = reg.active || reg.waiting || reg.installing; + sw.postMessage("worker-message", [port2]); + }); + }); + + equal( + workerMessage, + "original-imported-script", + "Got expected worker reply (importScripts not intercepted)" + ); + + // Start the test extension to redirect importScripts requests. + await extension.startup(); + + // Trigger an update on the registered service worker, then assert that the + // reply got is coming from the script where the extension is redirecting the + // request. + info("Update service worker and expect extension script to reply"); + workerMessage = await testSWUpdate(contentPage); + await extension.awaitMessage("request-redirected"); + equal( + workerMessage, + "redirected-imported-script-1", + "Got expected worker reply (importScripts redirected to moz-extension url)" + ); + + // Trigger a new update of the registered service worker, then assert that the + // reply got is coming from a different script where the extension is + // redirecting the second request (this confirms that the extension can + // intercept and can change the redirected imported scripts on new service + // worker updates). + info("Update service worker and expect new extension script to reply"); + workerMessage = await testSWUpdate(contentPage); + await extension.awaitMessage("request-redirected"); + equal( + workerMessage, + "redirected-imported-script-2", + "Got expected worker reply (importScripts redirected to moz-extension url again)" + ); + + // Uninstall the extension and trigger one more update of the registered + // service worker, then assert that the reply got is the one coming from the + // server (because difference from the one got from the cache). + // This verify that the service worker are updated as expected after the + // extension is uninstalled and the worker is not stuck on the script where + // the extension did redirect the request the last time. + info( + "Unload extension, update service worker and expect original script to reply" + ); + await extension.unload(); + workerMessage = await testSWUpdate(contentPage); + equal( + workerMessage, + "original-imported-script", + "Got expected worker reply (importScripts not intercepted)" + ); + + await contentPage.close(); +}); diff --git a/dom/workers/test/xpcshell/test_fileReader.js b/dom/workers/test/xpcshell/test_fileReader.js new file mode 100644 index 0000000000..02bc3fa667 --- /dev/null +++ b/dom/workers/test/xpcshell/test_fileReader.js @@ -0,0 +1,33 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Worker must be loaded from a chrome:// uri, not a file:// +// uri, so we first need to load it. +var WORKER_SOURCE_URI = "chrome://workers/content/worker_fileReader.js"; +do_load_manifest("data/chrome.manifest"); + +function talk_with_worker(worker) { + return new Promise((resolve, reject) => { + worker.onmessage = function (event) { + let success = true; + if (event.data == "OK") { + resolve(); + } else { + success = false; + reject(event); + } + Assert.ok(success); + worker.terminate(); + }; + worker.onerror = function (event) { + let error = new Error(event.message, event.filename, event.lineno); + worker.terminate(); + reject(error); + }; + worker.postMessage("START"); + }); +} + +add_task(function test_chrome_worker() { + return talk_with_worker(new ChromeWorker(WORKER_SOURCE_URI)); +}); diff --git a/dom/workers/test/xpcshell/test_remoteworker_launch_new_process.js b/dom/workers/test/xpcshell/test_remoteworker_launch_new_process.js new file mode 100644 index 0000000000..e13b0fc96c --- /dev/null +++ b/dom/workers/test/xpcshell/test_remoteworker_launch_new_process.js @@ -0,0 +1,94 @@ +/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set sts=2 sw=2 et tw=80: */ +"use strict"; + +const { TestUtils } = ChromeUtils.importESModule( + "resource://testing-common/TestUtils.sys.mjs" +); + +const { AddonTestUtils } = ChromeUtils.importESModule( + "resource://testing-common/AddonTestUtils.sys.mjs" +); +const { createHttpServer } = AddonTestUtils; + +// Force ServiceWorkerRegistrar to init by calling do_get_profile. +// (This has to be called before AddonTestUtils.init, because it does +// also call do_get_profile internally but it doesn't notify +// profile-after-change). +do_get_profile(true); + +AddonTestUtils.init(this); + +const server = createHttpServer({ hosts: ["localhost"] }); + +server.registerPathHandler("/sw.js", (request, response) => { + info(`/sw.js is being requested: ${JSON.stringify(request)}`); + response.setHeader("Content-Type", "application/javascript"); + response.write(""); +}); + +add_task(async function setup_prefs() { + equal( + Services.prefs.getBoolPref("browser.tabs.remote.autostart"), + true, + "e10s is expected to be enabled" + ); + + // Enable nsIServiceWorkerManager.registerForTest. + Services.prefs.setBoolPref("dom.serviceWorkers.testing.enabled", true); + + registerCleanupFunction(() => { + Services.prefs.clearUserPref("dom.serviceWorkers.testing.enabled"); + }); +}); + +/** + * This test installs a ServiceWorker via test API and verify that the install + * process spawns a new process. (Normally ServiceWorker installation won't + * cause a new content process to be spawned because the call to register must + * be coming from within an existing content process, but the registerForTest + * API allows us to bypass this restriction.) + * + * This models the real-world situation of a push notification being received + * from the network which results in a ServiceWorker being spawned without their + * necessarily being an existing content process to host it (especially under Fission). + */ +add_task(async function launch_remoteworkers_in_new_processes() { + const swm = Cc["@mozilla.org/serviceworkers/manager;1"].getService( + Ci.nsIServiceWorkerManager + ); + + const ssm = Services.scriptSecurityManager; + + const initialChildCount = Services.ppmm.childCount; + + // A test service worker that should spawn a regular web content child process. + const swRegInfoWeb = await swm.registerForTest( + ssm.createContentPrincipal(Services.io.newURI("http://localhost"), {}), + "http://localhost/scope", + "http://localhost/sw.js" + ); + swRegInfoWeb.QueryInterface(Ci.nsIServiceWorkerRegistrationInfo); + + info( + `web content service worker registered: ${JSON.stringify({ + principal: swRegInfoWeb.principal.spec, + scope: swRegInfoWeb.scope, + })}` + ); + + info("Wait new process to be launched"); + await TestUtils.waitForCondition(() => { + return Services.ppmm.childCount - initialChildCount >= 1; + }, "wait for a new child processes to be started"); + + // Wait both workers to become active to be sure that. besides spawning + // the new child processes as expected, the two remote worker have been + // able to run successfully (in other word their remote worker data did + // pass successfull the IsRemoteTypeAllowed check in RemoteworkerChild). + info("Wait for webcontent worker to become active"); + await TestUtils.waitForCondition( + () => swRegInfoWeb.activeWorker, + `wait workers for scope ${swRegInfoWeb.scope} to be active` + ); +}); diff --git a/dom/workers/test/xpcshell/test_workers.js b/dom/workers/test/xpcshell/test_workers.js new file mode 100644 index 0000000000..5b768f69bb --- /dev/null +++ b/dom/workers/test/xpcshell/test_workers.js @@ -0,0 +1,37 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Worker must be loaded from a chrome:// uri, not a file:// +// uri, so we first need to load it. +var WORKER_SOURCE_URI = "chrome://workers/content/worker.js"; +do_load_manifest("data/chrome.manifest"); + +function talk_with_worker(worker) { + return new Promise((resolve, reject) => { + worker.onmessage = function (event) { + let success = true; + if (event.data == "OK") { + resolve(); + } else { + success = false; + reject(event); + } + Assert.ok(success); + worker.terminate(); + }; + worker.onerror = function (event) { + let error = new Error(event.message, event.filename, event.lineno); + worker.terminate(); + reject(error); + }; + worker.postMessage("START"); + }); +} + +add_task(function test_chrome_worker() { + return talk_with_worker(new ChromeWorker(WORKER_SOURCE_URI)); +}); + +add_task(function test_worker() { + return talk_with_worker(new Worker(WORKER_SOURCE_URI)); +}); diff --git a/dom/workers/test/xpcshell/test_workers_clone_error.js b/dom/workers/test/xpcshell/test_workers_clone_error.js new file mode 100644 index 0000000000..c69f9de3fd --- /dev/null +++ b/dom/workers/test/xpcshell/test_workers_clone_error.js @@ -0,0 +1,42 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Worker must be loaded from a chrome:// uri, not a file:// +// uri, so we first need to load it. +var WORKER_SOURCE_URI = "chrome://workers/content/worker.js"; +do_load_manifest("data/chrome.manifest"); + +function talk_with_worker(worker) { + return new Promise((resolve, reject) => { + worker.onmessage = function (event) { + let success = true; + if (event.data == "OK") { + resolve(); + } else { + success = false; + reject(event); + } + Assert.ok(success); + worker.terminate(); + }; + worker.onerror = function (event) { + let error = new Error(event.message, event.filename, event.lineno); + worker.terminate(); + reject(error); + }; + + try { + eval("/"); + } catch (e) { + worker.postMessage(new ClonedErrorHolder(e)); + } + }); +} + +add_task(function test_chrome_worker() { + return talk_with_worker(new ChromeWorker(WORKER_SOURCE_URI)); +}); + +add_task(function test_worker() { + return talk_with_worker(new Worker(WORKER_SOURCE_URI)); +}); diff --git a/dom/workers/test/xpcshell/xpcshell.ini b/dom/workers/test/xpcshell/xpcshell.ini new file mode 100644 index 0000000000..7b24396197 --- /dev/null +++ b/dom/workers/test/xpcshell/xpcshell.ini @@ -0,0 +1,28 @@ +[DEFAULT] +head = +skip-if = toolkit == 'android' +support-files = + data/worker.js + data/worker_fileReader.js + data/chrome.manifest + +[test_ext_redirects_sw_scripts.js] +# The following firefox-appdir make sure that ExtensionTestUtils.loadExtension +# will be able to successfully start the background page (it does fail without +# it because there wouldn't be a global.tabTracker implementation as we would +# expect in a real Firefox, Fenix or Thunderbird instance). +firefox-appdir = browser +[test_workers.js] +[test_workers_clone_error.js] +[test_fileReader.js] +[test_remoteworker_launch_new_process.js] +# The following firefox-appdir make sure that this xpcshell test will run +# with e10s enabled (which is needed to make sure that the test case is +# going to launch the expected new processes) +firefox-appdir = browser +# Disable plugin loading to make it rr able to record and replay this test. +prefs = + plugin.disable=true +skip-if = + socketprocess_networking # Bug 1759035 + -- cgit v1.2.3