From 5f8de423f190bbb79a62f804151bc24824fa32d8 Mon Sep 17 00:00:00 2001 From: "Matt A. Tobin" Date: Fri, 2 Feb 2018 04:16:08 -0500 Subject: Add m-esr52 at 52.6.0 --- dom/workers/ChromeWorkerScope.cpp | 74 + dom/workers/ChromeWorkerScope.h | 19 + dom/workers/FileReaderSync.cpp | 264 + dom/workers/FileReaderSync.h | 53 + dom/workers/PServiceWorkerManager.ipdl | 45 + dom/workers/Principal.cpp | 51 + dom/workers/Principal.h | 22 + dom/workers/Queue.h | 203 + dom/workers/RegisterBindings.cpp | 55 + dom/workers/RuntimeService.cpp | 2966 +++++++++ dom/workers/RuntimeService.h | 287 + dom/workers/ScriptLoader.cpp | 2290 +++++++ dom/workers/ScriptLoader.h | 68 + dom/workers/ServiceWorker.cpp | 103 + dom/workers/ServiceWorker.h | 85 + dom/workers/ServiceWorkerClient.cpp | 232 + dom/workers/ServiceWorkerClient.h | 118 + dom/workers/ServiceWorkerClients.cpp | 864 +++ dom/workers/ServiceWorkerClients.h | 64 + dom/workers/ServiceWorkerCommon.h | 25 + dom/workers/ServiceWorkerContainer.cpp | 341 + dom/workers/ServiceWorkerContainer.h | 87 + dom/workers/ServiceWorkerEvents.cpp | 1280 ++++ dom/workers/ServiceWorkerEvents.h | 316 + dom/workers/ServiceWorkerInfo.cpp | 229 + dom/workers/ServiceWorkerInfo.h | 151 + dom/workers/ServiceWorkerJob.cpp | 246 + dom/workers/ServiceWorkerJob.h | 155 + dom/workers/ServiceWorkerJobQueue.cpp | 134 + dom/workers/ServiceWorkerJobQueue.h | 49 + dom/workers/ServiceWorkerManager.cpp | 3969 ++++++++++++ dom/workers/ServiceWorkerManager.h | 521 ++ dom/workers/ServiceWorkerManagerChild.cpp | 107 + dom/workers/ServiceWorkerManagerChild.h | 63 + dom/workers/ServiceWorkerManagerParent.cpp | 330 + dom/workers/ServiceWorkerManagerParent.h | 72 + dom/workers/ServiceWorkerManagerService.cpp | 237 + dom/workers/ServiceWorkerManagerService.h | 67 + dom/workers/ServiceWorkerPrivate.cpp | 2088 ++++++ dom/workers/ServiceWorkerPrivate.h | 236 + dom/workers/ServiceWorkerRegisterJob.cpp | 67 + dom/workers/ServiceWorkerRegisterJob.h | 40 + dom/workers/ServiceWorkerRegistrar.cpp | 884 +++ dom/workers/ServiceWorkerRegistrar.h | 101 + dom/workers/ServiceWorkerRegistrarTypes.ipdlh | 23 + dom/workers/ServiceWorkerRegistration.cpp | 1327 ++++ dom/workers/ServiceWorkerRegistration.h | 124 + dom/workers/ServiceWorkerRegistrationInfo.cpp | 546 ++ dom/workers/ServiceWorkerRegistrationInfo.h | 184 + dom/workers/ServiceWorkerScriptCache.cpp | 1060 +++ dom/workers/ServiceWorkerScriptCache.h | 59 + dom/workers/ServiceWorkerUnregisterJob.cpp | 151 + dom/workers/ServiceWorkerUnregisterJob.h | 45 + dom/workers/ServiceWorkerUpdateJob.cpp | 552 ++ dom/workers/ServiceWorkerUpdateJob.h | 104 + dom/workers/ServiceWorkerWindowClient.cpp | 558 ++ dom/workers/ServiceWorkerWindowClient.h | 64 + dom/workers/SharedWorker.cpp | 207 + dom/workers/SharedWorker.h | 105 + dom/workers/WorkerDebuggerManager.cpp | 361 ++ dom/workers/WorkerDebuggerManager.h | 121 + dom/workers/WorkerHolder.cpp | 60 + dom/workers/WorkerHolder.h | 96 + dom/workers/WorkerInlines.h | 25 + dom/workers/WorkerLocation.cpp | 43 + dom/workers/WorkerLocation.h | 116 + dom/workers/WorkerNavigator.cpp | 184 + dom/workers/WorkerNavigator.h | 114 + dom/workers/WorkerPrefs.h | 49 + dom/workers/WorkerPrivate.cpp | 6752 ++++++++++++++++++++ dom/workers/WorkerPrivate.h | 1594 +++++ dom/workers/WorkerRunnable.cpp | 777 +++ dom/workers/WorkerRunnable.h | 509 ++ dom/workers/WorkerScope.cpp | 978 +++ dom/workers/WorkerScope.h | 389 ++ dom/workers/WorkerThread.cpp | 355 + dom/workers/WorkerThread.h | 110 + dom/workers/Workers.h | 383 ++ dom/workers/moz.build | 131 + dom/workers/nsIWorkerDebugger.idl | 50 + dom/workers/nsIWorkerDebuggerManager.idl | 22 + dom/workers/test/404_server.sjs | 10 + .../test/WorkerDebugger.console_childWorker.js | 3 + .../test/WorkerDebugger.console_debugger.js | 41 + dom/workers/test/WorkerDebugger.console_worker.js | 10 + .../test/WorkerDebugger.initialize_childWorker.js | 6 + .../test/WorkerDebugger.initialize_debugger.js | 6 + .../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 | 25 + ...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_iframe1.html | 15 + .../test/WorkerDebugger_frozen_iframe2.html | 15 + dom/workers/test/WorkerDebugger_frozen_worker1.js | 5 + dom/workers/test/WorkerDebugger_frozen_worker2.js | 5 + .../test/WorkerDebugger_promise_debugger.js | 30 + dom/workers/test/WorkerDebugger_promise_worker.js | 25 + dom/workers/test/WorkerDebugger_sharedWorker.js | 11 + .../test/WorkerDebugger_suspended_debugger.js | 6 + .../test/WorkerDebugger_suspended_worker.js | 6 + dom/workers/test/WorkerDebugger_worker.js | 8 + dom/workers/test/WorkerTest.jsm | 17 + dom/workers/test/WorkerTest_badworker.js | 7 + dom/workers/test/WorkerTest_subworker.js | 43 + dom/workers/test/WorkerTest_worker.js | 11 + dom/workers/test/atob_worker.js | 46 + dom/workers/test/browser.ini | 12 + dom/workers/test/browser_bug1047663.js | 50 + dom/workers/test/browser_bug1104623.js | 53 + dom/workers/test/bug1014466_data1.txt | 1 + dom/workers/test/bug1014466_data2.txt | 1 + dom/workers/test/bug1014466_worker.js | 64 + dom/workers/test/bug1020226_frame.html | 20 + dom/workers/test/bug1020226_worker.js | 12 + dom/workers/test/bug1047663_tab.html | 8 + dom/workers/test/bug1047663_worker.sjs | 40 + dom/workers/test/bug1060621_worker.js | 2 + dom/workers/test/bug1062920_worker.js | 6 + 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 | 86 + dom/workers/test/chromeWorker_subworker.js | 7 + dom/workers/test/chromeWorker_worker.js | 20 + dom/workers/test/clearTimeouts_worker.js | 12 + dom/workers/test/consoleReplaceable_worker.js | 16 + dom/workers/test/console_worker.js | 109 + 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/779707.html | 19 + dom/workers/test/crashtests/943516.html | 10 + dom/workers/test/crashtests/crashtests.list | 5 + dom/workers/test/csp_worker.js | 28 + dom/workers/test/csp_worker.js^headers^ | 1 + dom/workers/test/dom_worker_helper.js | 176 + dom/workers/test/empty.html | 0 dom/workers/test/errorPropagation_iframe.html | 55 + dom/workers/test/errorPropagation_worker.js | 50 + dom/workers/test/errorwarning_worker.js | 42 + dom/workers/test/eventDispatch_worker.js | 67 + dom/workers/test/extensions/bootstrap/bootstrap.js | 141 + dom/workers/test/extensions/bootstrap/install.rdf | 31 + dom/workers/test/extensions/bootstrap/jar.mn | 3 + dom/workers/test/extensions/bootstrap/moz.build | 20 + dom/workers/test/extensions/bootstrap/worker.js | 7 + .../bootstrap/workerbootstrap-test@mozilla.org.xpi | Bin 0 -> 7202 bytes dom/workers/test/extensions/moz.build | 7 + .../test/extensions/traditional/WorkerTest.js | 122 + .../extensions/traditional/WorkerTest.manifest | 3 + .../test/extensions/traditional/install.rdf | 30 + dom/workers/test/extensions/traditional/jar.mn | 3 + dom/workers/test/extensions/traditional/moz.build | 30 + .../test/extensions/traditional/nsIWorkerTest.idl | 23 + .../traditional/worker-test@mozilla.org.xpi | Bin 0 -> 8333 bytes dom/workers/test/extensions/traditional/worker.js | 7 + 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 | 74 + 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_worker.js | 16 + dom/workers/test/fileapi_chromeScript.js | 29 + dom/workers/test/foreign.js | 1 + dom/workers/test/frame_script.js | 72 + dom/workers/test/gtest/TestReadWrite.cpp | 499 ++ dom/workers/test/gtest/moz.build | 13 + dom/workers/test/head.js | 91 + dom/workers/test/importForeignScripts_worker.js | 55 + dom/workers/test/importScripts_3rdParty_worker.js | 82 + dom/workers/test/importScripts_mixedcontent.html | 46 + dom/workers/test/importScripts_worker.js | 64 + dom/workers/test/importScripts_worker_imported1.js | 10 + dom/workers/test/importScripts_worker_imported2.js | 10 + dom/workers/test/importScripts_worker_imported3.js | 6 + dom/workers/test/importScripts_worker_imported4.js | 6 + dom/workers/test/instanceof_worker.js | 12 + dom/workers/test/json_worker.js | 338 + dom/workers/test/jsversion_worker.js | 14 + dom/workers/test/loadEncoding_worker.js | 7 + dom/workers/test/location_worker.js | 11 + dom/workers/test/longThread_worker.js | 14 + dom/workers/test/mochitest.ini | 227 + dom/workers/test/multi_sharedWorker_frame.html | 52 + .../test/multi_sharedWorker_sharedWorker.js | 72 + dom/workers/test/navigator_languages_worker.js | 11 + dom/workers/test/navigator_worker.js | 79 + dom/workers/test/newError_worker.js | 5 + dom/workers/test/notification_permission_worker.js | 56 + dom/workers/test/notification_worker.js | 93 + .../test/notification_worker_child-child.js | 92 + .../test/notification_worker_child-parent.js | 26 + dom/workers/test/onLine_worker.js | 65 + dom/workers/test/onLine_worker_child.js | 75 + dom/workers/test/onLine_worker_head.js | 43 + dom/workers/test/promise_worker.js | 856 +++ dom/workers/test/recursion_worker.js | 46 + dom/workers/test/recursiveOnerror_worker.js | 11 + dom/workers/test/redirect_to_foreign.sjs | 4 + dom/workers/test/referrer.sjs | 15 + dom/workers/test/referrer_test_server.sjs | 101 + dom/workers/test/referrer_worker.html | 145 + dom/workers/test/rvals_worker.js | 13 + dom/workers/test/script_createFile.js | 15 + .../serviceworkers/activate_event_error_worker.js | 4 + .../blocking_install_event_worker.js | 23 + dom/workers/test/serviceworkers/browser.ini | 10 + .../serviceworkers/browser_base_force_refresh.html | 30 + .../browser_cached_force_refresh.html | 64 + .../test/serviceworkers/browser_download.js | 83 + .../test/serviceworkers/browser_force_refresh.js | 91 + .../test/serviceworkers/bug1151916_driver.html | 53 + .../test/serviceworkers/bug1151916_worker.js | 13 + .../test/serviceworkers/bug1240436_worker.js | 2 + dom/workers/test/serviceworkers/chrome.ini | 16 + dom/workers/test/serviceworkers/chrome_helpers.js | 74 + .../test/serviceworkers/claim_clients/client.html | 44 + .../test/serviceworkers/claim_fetch_worker.js | 12 + .../test/serviceworkers/claim_oninstall_worker.js | 7 + dom/workers/test/serviceworkers/claim_worker_1.js | 28 + dom/workers/test/serviceworkers/claim_worker_2.js | 27 + dom/workers/test/serviceworkers/close_test.js | 19 + .../test/serviceworkers/controller/index.html | 74 + .../create_another_sharedWorker.html | 6 + .../test/serviceworkers/download/window.html | 46 + dom/workers/test/serviceworkers/download/worker.js | 30 + dom/workers/test/serviceworkers/empty.js | 0 .../test/serviceworkers/error_reporting_helpers.js | 68 + dom/workers/test/serviceworkers/eval_worker.js | 1 + .../eventsource/eventsource.resource | 22 + .../eventsource/eventsource.resource^headers^ | 3 + .../eventsource/eventsource_cors_response.html | 75 + .../eventsource_cors_response_intercept_worker.js | 20 + .../eventsource_mixed_content_cors_response.html | 75 + ...mixed_content_cors_response_intercept_worker.js | 19 + .../eventsource/eventsource_opaque_response.html | 75 + ...eventsource_opaque_response_intercept_worker.js | 20 + .../eventsource/eventsource_register_worker.html | 27 + .../eventsource_synthetic_response.html | 75 + ...ntsource_synthetic_response_intercept_worker.js | 24 + .../eventsource/eventsource_worker_helper.js | 12 + dom/workers/test/serviceworkers/fetch.js | 11 + .../test/serviceworkers/fetch/context/beacon.sjs | 43 + .../serviceworkers/fetch/context/context_test.js | 135 + .../serviceworkers/fetch/context/csp-violate.sjs | 6 + .../test/serviceworkers/fetch/context/index.html | 422 ++ .../fetch/context/parentsharedworker.js | 8 + .../serviceworkers/fetch/context/parentworker.js | 4 + .../test/serviceworkers/fetch/context/ping.html | 7 + .../serviceworkers/fetch/context/realaudio.ogg | Bin 0 -> 6416 bytes .../test/serviceworkers/fetch/context/realimg.jpg | Bin 0 -> 3595 bytes .../serviceworkers/fetch/context/register.html | 14 + .../serviceworkers/fetch/context/sharedworker.js | 5 + .../serviceworkers/fetch/context/unregister.html | 12 + .../test/serviceworkers/fetch/context/worker.js | 1 + .../test/serviceworkers/fetch/context/xml.xml | 3 + .../test/serviceworkers/fetch/deliver-gzip.sjs | 17 + .../test/serviceworkers/fetch/fetch_tests.js | 416 ++ .../serviceworkers/fetch/fetch_worker_script.js | 29 + .../test/serviceworkers/fetch/hsts/embedder.html | 7 + .../test/serviceworkers/fetch/hsts/hsts_test.js | 11 + .../test/serviceworkers/fetch/hsts/image-20px.png | Bin 0 -> 87 bytes .../test/serviceworkers/fetch/hsts/image-40px.png | Bin 0 -> 123 bytes .../test/serviceworkers/fetch/hsts/image.html | 13 + .../test/serviceworkers/fetch/hsts/realindex.html | 8 + .../test/serviceworkers/fetch/hsts/register.html | 14 + .../fetch/hsts/register.html^headers^ | 2 + .../test/serviceworkers/fetch/hsts/unregister.html | 12 + .../fetch/https/clonedresponse/https_test.js | 15 + .../fetch/https/clonedresponse/index.html | 4 + .../fetch/https/clonedresponse/register.html | 14 + .../fetch/https/clonedresponse/unregister.html | 12 + .../test/serviceworkers/fetch/https/https_test.js | 23 + .../test/serviceworkers/fetch/https/index.html | 4 + .../test/serviceworkers/fetch/https/register.html | 20 + .../serviceworkers/fetch/https/unregister.html | 12 + .../fetch/imagecache-maxage/image-20px.png | Bin 0 -> 87 bytes .../fetch/imagecache-maxage/image-40px.png | Bin 0 -> 123 bytes .../fetch/imagecache-maxage/index.html | 29 + .../fetch/imagecache-maxage/maxage_test.js | 41 + .../fetch/imagecache-maxage/register.html | 14 + .../fetch/imagecache-maxage/unregister.html | 12 + .../serviceworkers/fetch/imagecache/image-20px.png | Bin 0 -> 87 bytes .../serviceworkers/fetch/imagecache/image-40px.png | Bin 0 -> 123 bytes .../fetch/imagecache/imagecache_test.js | 15 + .../serviceworkers/fetch/imagecache/index.html | 20 + .../fetch/imagecache/postmortem.html | 9 + .../serviceworkers/fetch/imagecache/register.html | 16 + .../fetch/imagecache/unregister.html | 12 + .../fetch/importscript-mixedcontent/https_test.js | 28 + .../fetch/importscript-mixedcontent/register.html | 14 + .../importscript-mixedcontent/unregister.html | 12 + dom/workers/test/serviceworkers/fetch/index.html | 183 + .../test/serviceworkers/fetch/interrupt.sjs | 20 + .../fetch/origin/https/index-https.sjs | 4 + .../fetch/origin/https/origin_test.js | 29 + .../fetch/origin/https/realindex.html | 6 + .../fetch/origin/https/realindex.html^headers^ | 1 + .../fetch/origin/https/register.html | 14 + .../fetch/origin/https/unregister.html | 12 + .../serviceworkers/fetch/origin/index-to-https.sjs | 4 + .../test/serviceworkers/fetch/origin/index.sjs | 4 + .../serviceworkers/fetch/origin/origin_test.js | 41 + .../serviceworkers/fetch/origin/realindex.html | 6 + .../fetch/origin/realindex.html^headers^ | 1 + .../test/serviceworkers/fetch/origin/register.html | 14 + .../serviceworkers/fetch/origin/unregister.html | 12 + .../test/serviceworkers/fetch/plugin/plugins.html | 43 + .../test/serviceworkers/fetch/plugin/worker.js | 14 + .../test/serviceworkers/fetch/real-file.txt | 1 + dom/workers/test/serviceworkers/fetch/redirect.sjs | 4 + .../serviceworkers/fetch/requesturl/index.html | 7 + .../serviceworkers/fetch/requesturl/redirect.sjs | 4 + .../fetch/requesturl/redirector.html | 2 + .../serviceworkers/fetch/requesturl/register.html | 14 + .../fetch/requesturl/requesturl_test.js | 17 + .../serviceworkers/fetch/requesturl/secret.html | 5 + .../fetch/requesturl/unregister.html | 12 + .../test/serviceworkers/fetch/sandbox/index.html | 5 + .../fetch/sandbox/intercepted_index.html | 5 + .../serviceworkers/fetch/sandbox/register.html | 14 + .../serviceworkers/fetch/sandbox/sandbox_test.js | 5 + .../serviceworkers/fetch/sandbox/unregister.html | 12 + .../fetch/upgrade-insecure/embedder.html | 10 + .../fetch/upgrade-insecure/embedder.html^headers^ | 1 + .../fetch/upgrade-insecure/image-20px.png | Bin 0 -> 87 bytes .../fetch/upgrade-insecure/image-40px.png | Bin 0 -> 123 bytes .../fetch/upgrade-insecure/image.html | 13 + .../fetch/upgrade-insecure/realindex.html | 4 + .../fetch/upgrade-insecure/register.html | 14 + .../fetch/upgrade-insecure/unregister.html | 12 + .../upgrade-insecure/upgrade-insecure_test.js | 11 + .../test/serviceworkers/fetch_event_worker.js | 337 + .../serviceworkers/file_blob_response_worker.js | 38 + .../serviceworkers/force_refresh_browser_worker.js | 34 + .../test/serviceworkers/force_refresh_worker.js | 33 + .../test/serviceworkers/gzip_redirect_worker.js | 13 + dom/workers/test/serviceworkers/header_checker.sjs | 9 + dom/workers/test/serviceworkers/hello.html | 9 + dom/workers/test/serviceworkers/importscript.sjs | 11 + .../test/serviceworkers/importscript_worker.js | 37 + .../serviceworkers/install_event_error_worker.js | 4 + .../test/serviceworkers/install_event_worker.js | 3 + dom/workers/test/serviceworkers/lorem_script.js | 8 + .../serviceworkers/match_all_advanced_worker.js | 5 + .../match_all_client/match_all_client_id.html | 31 + .../serviceworkers/match_all_client_id_worker.js | 28 + .../match_all_clients/match_all_controlled.html | 65 + .../serviceworkers/match_all_properties_worker.js | 20 + .../test/serviceworkers/match_all_worker.js | 10 + .../test/serviceworkers/message_posting_worker.js | 8 + .../test/serviceworkers/message_receiver.html | 6 + dom/workers/test/serviceworkers/mochitest.ini | 317 + .../test/serviceworkers/notification/listener.html | 27 + .../test/serviceworkers/notification/register.html | 11 + .../serviceworkers/notification/unregister.html | 12 + .../serviceworkers/notification_alt/register.html | 11 + .../notification_alt/unregister.html | 12 + .../notification_constructor_error.js | 1 + .../test/serviceworkers/notification_get_sw.js | 49 + .../notificationclick-otherwindow.html | 30 + .../test/serviceworkers/notificationclick.html | 27 + .../test/serviceworkers/notificationclick.js | 19 + .../serviceworkers/notificationclick_focus.html | 28 + .../test/serviceworkers/notificationclick_focus.js | 40 + .../test/serviceworkers/notificationclose.html | 37 + .../test/serviceworkers/notificationclose.js | 19 + dom/workers/test/serviceworkers/notify_loaded.js | 1 + .../test/serviceworkers/opaque_intercept_worker.js | 25 + .../test/serviceworkers/openWindow_worker.js | 116 + .../test/serviceworkers/open_window/client.html | 48 + .../test/serviceworkers/parse_error_worker.js | 2 + dom/workers/test/serviceworkers/redirect.sjs | 5 + dom/workers/test/serviceworkers/redirect_post.sjs | 35 + .../test/serviceworkers/redirect_serviceworker.sjs | 4 + .../test/serviceworkers/register_https.html | 21 + .../sanitize/example_check_and_unregister.html | 23 + .../test/serviceworkers/sanitize/frame.html | 11 + .../test/serviceworkers/sanitize/register.html | 10 + dom/workers/test/serviceworkers/sanitize_worker.js | 5 + .../test/serviceworkers/scope/scope_worker.js | 2 + dom/workers/test/serviceworkers/serviceworker.html | 12 + .../serviceworker_not_sharedworker.js | 21 + .../test/serviceworkers/serviceworker_wrapper.js | 101 + .../serviceworkers/serviceworkerinfo_iframe.html | 26 + .../serviceworkermanager_iframe.html | 34 + .../serviceworkerregistrationinfo_iframe.html | 30 + .../test/serviceworkers/sharedWorker_fetch.js | 29 + .../test/serviceworkers/simpleregister/index.html | 51 + .../test/serviceworkers/simpleregister/ready.html | 15 + .../skip_waiting_installed_worker.js | 6 + .../serviceworkers/skip_waiting_scope/index.html | 37 + .../source_message_posting_worker.js | 16 + .../test/serviceworkers/strict_mode_warning.js | 4 + .../test/serviceworkers/sw_bad_mime_type.js | 1 + .../serviceworkers/sw_bad_mime_type.js^headers^ | 1 + .../sw_clients/file_blob_upload_frame.html | 77 + .../test/serviceworkers/sw_clients/navigator.html | 35 + .../test/serviceworkers/sw_clients/refresher.html | 39 + .../sw_clients/refresher_cached.html | 38 + .../sw_clients/refresher_cached_compressed.html | Bin 0 -> 559 bytes .../refresher_cached_compressed.html^headers^ | 2 + .../sw_clients/refresher_compressed.html | Bin 0 -> 608 bytes .../sw_clients/refresher_compressed.html^headers^ | 2 + .../sw_clients/service_worker_controlled.html | 38 + .../test/serviceworkers/sw_clients/simple.html | 30 + .../serviceworkers/swa/worker_scope_different.js | 0 .../swa/worker_scope_different.js^headers^ | 1 + .../serviceworkers/swa/worker_scope_different2.js | 0 .../swa/worker_scope_different2.js^headers^ | 1 + .../serviceworkers/swa/worker_scope_precise.js | 0 .../swa/worker_scope_precise.js^headers^ | 1 + .../serviceworkers/swa/worker_scope_too_deep.js | 0 .../swa/worker_scope_too_deep.js^headers^ | 1 + .../serviceworkers/swa/worker_scope_too_narrow.js | 0 .../swa/worker_scope_too_narrow.js^headers^ | 1 + .../test/serviceworkers/test_bug1151916.html | 105 + .../test/serviceworkers/test_bug1240436.html | 34 + dom/workers/test/serviceworkers/test_claim.html | 172 + .../test/serviceworkers/test_claim_fetch.html | 98 + .../test/serviceworkers/test_claim_oninstall.html | 78 + .../test/serviceworkers/test_client_focus.html | 96 + dom/workers/test/serviceworkers/test_close.html | 64 + .../test/serviceworkers/test_controller.html | 84 + .../test_cross_origin_url_after_redirect.html | 50 + .../test_csp_upgrade-insecure_intercept.html | 55 + .../serviceworkers/test_empty_serviceworker.html | 46 + .../test/serviceworkers/test_error_reporting.html | 76 + .../test/serviceworkers/test_escapedSlashes.html | 102 + .../test/serviceworkers/test_eval_allowed.html | 51 + .../serviceworkers/test_eval_allowed.html^headers^ | 1 + .../serviceworkers/test_eventsource_intercept.html | 103 + .../test/serviceworkers/test_fetch_event.html | 83 + .../test/serviceworkers/test_fetch_integrity.html | 178 + .../serviceworkers/test_file_blob_response.html | 86 + .../test/serviceworkers/test_file_blob_upload.html | 145 + .../test/serviceworkers/test_force_refresh.html | 84 + .../test/serviceworkers/test_gzip_redirect.html | 84 + .../test_hsts_upgrade_intercept.html | 66 + .../test/serviceworkers/test_https_fetch.html | 62 + .../test_https_fetch_cloned_response.html | 56 + .../test_https_origin_after_redirect.html | 57 + .../test_https_origin_after_redirect_cached.html | 57 + .../test_https_synth_fetch_from_cached_sw.html | 69 + .../test/serviceworkers/test_imagecache.html | 55 + .../serviceworkers/test_imagecache_max_age.html | 71 + .../test/serviceworkers/test_importscript.html | 72 + .../test_importscript_mixedcontent.html | 53 + .../test/serviceworkers/test_install_event.html | 144 + .../test/serviceworkers/test_install_event_gc.html | 121 + .../serviceworkers/test_installation_simple.html | 212 + .../test/serviceworkers/test_match_all.html | 80 + .../serviceworkers/test_match_all_advanced.html | 100 + .../serviceworkers/test_match_all_client_id.html | 91 + .../test_match_all_client_properties.html | 97 + .../test/serviceworkers/test_navigator.html | 40 + .../serviceworkers/test_not_intercept_plugin.html | 78 + .../test_notification_constructor_error.html | 52 + .../test/serviceworkers/test_notification_get.html | 213 + .../test_notificationclick-otherwindow.html | 62 + .../serviceworkers/test_notificationclick.html | 62 + .../test_notificationclick_focus.html | 63 + .../serviceworkers/test_notificationclose.html | 62 + .../test/serviceworkers/test_opaque_intercept.html | 85 + .../test/serviceworkers/test_openWindow.html | 117 + .../serviceworkers/test_origin_after_redirect.html | 58 + .../test_origin_after_redirect_cached.html | 58 + .../test_origin_after_redirect_to_https.html | 57 + ...test_origin_after_redirect_to_https_cached.html | 58 + .../test/serviceworkers/test_post_message.html | 80 + .../serviceworkers/test_post_message_advanced.html | 109 + .../serviceworkers/test_post_message_source.html | 68 + .../test/serviceworkers/test_privateBrowsing.html | 113 + .../test/serviceworkers/test_register_base.html | 41 + .../test_register_https_in_http.html | 46 + .../test/serviceworkers/test_request_context.js | 75 + .../serviceworkers/test_request_context_audio.html | 19 + .../test_request_context_beacon.html | 19 + .../serviceworkers/test_request_context_cache.html | 19 + .../test_request_context_cspreport.html | 19 + .../serviceworkers/test_request_context_embed.html | 19 + .../serviceworkers/test_request_context_fetch.html | 19 + .../serviceworkers/test_request_context_font.html | 19 + .../serviceworkers/test_request_context_frame.html | 19 + .../test_request_context_iframe.html | 19 + .../serviceworkers/test_request_context_image.html | 19 + .../test_request_context_imagesrcset.html | 19 + .../test_request_context_internal.html | 19 + .../test_request_context_nestedworker.html | 19 + ...request_context_nestedworkerinsharedworker.html | 19 + .../test_request_context_object.html | 19 + .../test_request_context_picture.html | 19 + .../serviceworkers/test_request_context_ping.html | 19 + .../test_request_context_plugin.html | 19 + .../test_request_context_script.html | 19 + .../test_request_context_sharedworker.html | 19 + .../serviceworkers/test_request_context_style.html | 19 + .../serviceworkers/test_request_context_track.html | 19 + .../serviceworkers/test_request_context_video.html | 19 + .../test_request_context_worker.html | 19 + .../serviceworkers/test_request_context_xhr.html | 19 + .../serviceworkers/test_request_context_xslt.html | 19 + .../serviceworkers/test_sandbox_intercept.html | 50 + dom/workers/test/serviceworkers/test_sanitize.html | 87 + .../test/serviceworkers/test_sanitize_domain.html | 90 + dom/workers/test/serviceworkers/test_scopes.html | 121 + .../test_service_worker_allowed.html | 74 + .../serviceworkers/test_serviceworker_header.html | 41 + .../test_serviceworker_interfaces.html | 106 + .../test_serviceworker_interfaces.js | 278 + .../test_serviceworker_not_sharedworker.html | 66 + .../test/serviceworkers/test_serviceworkerinfo.xul | 115 + .../serviceworkers/test_serviceworkermanager.xul | 80 + .../test_serviceworkerregistrationinfo.xul | 115 + .../test/serviceworkers/test_skip_waiting.html | 95 + .../serviceworkers/test_strict_mode_warning.html | 42 + .../serviceworkers/test_third_party_iframes.html | 175 + .../test/serviceworkers/test_unregister.html | 138 + .../test_unresolved_fetch_interception.html | 100 + .../test/serviceworkers/test_workerUnregister.html | 82 + .../test/serviceworkers/test_workerUpdate.html | 62 + .../test_workerupdatefoundevent.html | 85 + dom/workers/test/serviceworkers/test_xslt.html | 128 + .../test/serviceworkers/thirdparty/iframe1.html | 30 + .../test/serviceworkers/thirdparty/iframe2.html | 7 + .../test/serviceworkers/thirdparty/register.html | 27 + dom/workers/test/serviceworkers/thirdparty/sw.js | 14 + .../test/serviceworkers/thirdparty/unregister.html | 11 + .../test/serviceworkers/unregister/index.html | 26 + .../test/serviceworkers/unregister/unregister.html | 22 + .../test/serviceworkers/unresolved_fetch_worker.js | 19 + .../test/serviceworkers/updatefoundevent.html | 13 + dom/workers/test/serviceworkers/worker.js | 1 + dom/workers/test/serviceworkers/worker2.js | 1 + dom/workers/test/serviceworkers/worker3.js | 1 + .../test/serviceworkers/workerUpdate/update.html | 24 + .../test/serviceworkers/worker_unregister.js | 16 + dom/workers/test/serviceworkers/worker_update.js | 19 + .../test/serviceworkers/worker_updatefoundevent.js | 23 + .../serviceworkers/worker_updatefoundevent2.js | 1 + dom/workers/test/serviceworkers/xslt/test.xml | 6 + dom/workers/test/serviceworkers/xslt/xslt.sjs | 12 + dom/workers/test/serviceworkers/xslt_worker.js | 52 + dom/workers/test/sharedWorker_console.js | 11 + dom/workers/test/sharedWorker_lifetime.js | 5 + dom/workers/test/sharedWorker_ports.js | 24 + dom/workers/test/sharedWorker_privateBrowsing.js | 5 + dom/workers/test/sharedWorker_sharedWorker.js | 93 + dom/workers/test/simpleThread_worker.js | 53 + dom/workers/test/suspend_iframe.html | 47 + dom/workers/test/suspend_worker.js | 13 + dom/workers/test/terminate_worker.js | 9 + dom/workers/test/test_404.html | 41 + .../test/test_WorkerDebugger.initialize.xul | 58 + .../test/test_WorkerDebugger.postMessage.xul | 61 + dom/workers/test/test_WorkerDebugger.xul | 122 + ...est_WorkerDebuggerGlobalScope.createSandbox.xul | 52 + ...st_WorkerDebuggerGlobalScope.enterEventLoop.xul | 126 + .../test_WorkerDebuggerGlobalScope.reportError.xul | 98 + ...test_WorkerDebuggerGlobalScope.setImmediate.xul | 54 + dom/workers/test/test_WorkerDebuggerManager.xul | 106 + dom/workers/test/test_WorkerDebugger_console.xul | 97 + dom/workers/test/test_WorkerDebugger_frozen.xul | 90 + dom/workers/test/test_WorkerDebugger_promise.xul | 70 + dom/workers/test/test_WorkerDebugger_suspended.xul | 75 + dom/workers/test/test_atob.html | 57 + dom/workers/test/test_blobConstructor.html | 60 + dom/workers/test/test_blobWorkers.html | 32 + dom/workers/test/test_bug1002702.html | 27 + 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 | 54 + dom/workers/test/test_bug1060621.html | 30 + dom/workers/test/test_bug1062920.html | 70 + dom/workers/test/test_bug1062920.xul | 69 + dom/workers/test/test_bug1063538.html | 49 + dom/workers/test/test_bug1104064.html | 28 + 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 | 62 + 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 | 27 + dom/workers/test/test_chromeWorker.xul | 45 + dom/workers/test/test_chromeWorkerJSM.xul | 56 + dom/workers/test/test_clearTimeouts.html | 31 + dom/workers/test/test_console.html | 44 + dom/workers/test/test_consoleAndBlobs.html | 43 + dom/workers/test/test_consoleReplaceable.html | 44 + dom/workers/test/test_consoleSharedWorkers.html | 56 + 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 | 48 + dom/workers/test/test_dataURLWorker.html | 31 + dom/workers/test/test_errorPropagation.html | 66 + dom/workers/test/test_errorwarning.html | 95 + dom/workers/test/test_eventDispatch.html | 33 + dom/workers/test/test_extension.xul | 55 + dom/workers/test/test_extensionBootstrap.xul | 66 + dom/workers/test/test_fibonacci.html | 52 + dom/workers/test/test_file.xul | 97 + dom/workers/test/test_fileBlobPosting.xul | 86 + dom/workers/test/test_fileBlobSubWorker.xul | 98 + dom/workers/test/test_filePosting.xul | 86 + dom/workers/test/test_fileReadSlice.xul | 94 + dom/workers/test/test_fileReader.html | 100 + dom/workers/test/test_fileReaderSync.xul | 199 + dom/workers/test/test_fileReaderSyncErrors.xul | 84 + dom/workers/test/test_fileSlice.xul | 106 + dom/workers/test/test_fileSubWorker.xul | 99 + dom/workers/test/test_importScripts.html | 53 + dom/workers/test/test_importScripts_3rdparty.html | 134 + .../test/test_importScripts_mixedcontent.html | 50 + dom/workers/test/test_instanceof.html | 40 + dom/workers/test/test_json.html | 89 + dom/workers/test/test_jsversion.html | 68 + dom/workers/test/test_loadEncoding.html | 50 + dom/workers/test/test_loadError.html | 77 + dom/workers/test/test_location.html | 72 + dom/workers/test/test_longThread.html | 59 + dom/workers/test/test_multi_sharedWorker.html | 242 + .../test/test_multi_sharedWorker_lifetimes.html | 156 + dom/workers/test/test_navigator.html | 68 + dom/workers/test/test_navigator_languages.html | 53 + ...test_navigator_workers_hardwareConcurrency.html | 30 + dom/workers/test/test_newError.html | 34 + dom/workers/test/test_notification.html | 50 + dom/workers/test/test_notification_child.html | 49 + dom/workers/test/test_notification_permission.html | 51 + dom/workers/test/test_onLine.html | 64 + dom/workers/test/test_promise.html | 44 + .../test/test_promise_resolved_with_string.html | 41 + 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 | 39 + .../test/test_resolveWorker-assignment.html | 30 + dom/workers/test/test_resolveWorker.html | 31 + dom/workers/test/test_rvals.html | 37 + dom/workers/test/test_setTimeoutWith0.html | 20 + dom/workers/test/test_sharedWorker.html | 71 + dom/workers/test/test_sharedWorker_lifetime.html | 30 + dom/workers/test/test_sharedWorker_ports.html | 42 + .../test/test_sharedWorker_privateBrowsing.html | 101 + dom/workers/test/test_simpleThread.html | 75 + dom/workers/test/test_subworkers_suspended.html | 135 + dom/workers/test/test_suspend.html | 138 + dom/workers/test/test_terminate.html | 100 + dom/workers/test/test_threadErrors.html | 64 + dom/workers/test/test_threadTimeouts.html | 61 + dom/workers/test/test_throwingOnerror.html | 54 + dom/workers/test/test_timeoutTracing.html | 48 + dom/workers/test/test_transferable.html | 123 + dom/workers/test/test_webSocket_sharedWorker.html | 30 + dom/workers/test/test_websocket1.html | 46 + dom/workers/test/test_websocket2.html | 46 + dom/workers/test/test_websocket3.html | 46 + dom/workers/test/test_websocket4.html | 46 + dom/workers/test/test_websocket5.html | 46 + dom/workers/test/test_websocket_basic.html | 57 + dom/workers/test/test_websocket_https.html | 30 + dom/workers/test/test_websocket_loadgroup.html | 61 + dom/workers/test/test_worker_interfaces.html | 16 + dom/workers/test/test_worker_interfaces.js | 291 + dom/workers/test/test_workersDisabled.html | 54 + dom/workers/test/test_workersDisabled.xul | 49 + dom/workers/test/threadErrors_worker1.js | 8 + dom/workers/test/threadErrors_worker2.js | 8 + dom/workers/test/threadErrors_worker3.js | 9 + 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 | 13 + dom/workers/test/transferable_worker.js | 23 + dom/workers/test/webSocket_sharedWorker.js | 20 + dom/workers/test/websocket_basic_worker.js | 39 + dom/workers/test/websocket_helpers.js | 19 + dom/workers/test/websocket_https.html | 14 + dom/workers/test/websocket_https_worker.js | 9 + dom/workers/test/websocket_loadgroup_worker.js | 24 + dom/workers/test/websocket_worker1.js | 19 + dom/workers/test/websocket_worker2.js | 19 + dom/workers/test/websocket_worker3.js | 17 + dom/workers/test/websocket_worker4.js | 19 + dom/workers/test/websocket_worker5.js | 13 + dom/workers/test/window_suspended.html | 5 + dom/workers/test/worker_bug1278777.js | 9 + dom/workers/test/worker_bug1301094.js | 11 + dom/workers/test/worker_consoleAndBlobs.js | 8 + dom/workers/test/worker_driver.js | 64 + dom/workers/test/worker_fileReader.js | 417 ++ dom/workers/test/worker_referrer.js | 9 + dom/workers/test/worker_setTimeoutWith0.js | 3 + dom/workers/test/worker_suspended.js | 31 + dom/workers/test/worker_wrapper.js | 99 + dom/workers/test/workersDisabled_worker.js | 7 + dom/workers/test/xpcshell/data/chrome.manifest | 1 + dom/workers/test/xpcshell/data/worker.js | 7 + .../test/xpcshell/data/worker_fileReader.js | 8 + dom/workers/test/xpcshell/test_fileReader.js | 40 + dom/workers/test/xpcshell/test_workers.js | 44 + dom/workers/test/xpcshell/xpcshell.ini | 11 + 730 files changed, 66297 insertions(+) create mode 100644 dom/workers/ChromeWorkerScope.cpp create mode 100644 dom/workers/ChromeWorkerScope.h create mode 100644 dom/workers/FileReaderSync.cpp create mode 100644 dom/workers/FileReaderSync.h create mode 100644 dom/workers/PServiceWorkerManager.ipdl create mode 100644 dom/workers/Principal.cpp create mode 100644 dom/workers/Principal.h create mode 100644 dom/workers/Queue.h create mode 100644 dom/workers/RegisterBindings.cpp create mode 100644 dom/workers/RuntimeService.cpp create mode 100644 dom/workers/RuntimeService.h create mode 100644 dom/workers/ScriptLoader.cpp create mode 100644 dom/workers/ScriptLoader.h create mode 100644 dom/workers/ServiceWorker.cpp create mode 100644 dom/workers/ServiceWorker.h create mode 100644 dom/workers/ServiceWorkerClient.cpp create mode 100644 dom/workers/ServiceWorkerClient.h create mode 100644 dom/workers/ServiceWorkerClients.cpp create mode 100644 dom/workers/ServiceWorkerClients.h create mode 100644 dom/workers/ServiceWorkerCommon.h create mode 100644 dom/workers/ServiceWorkerContainer.cpp create mode 100644 dom/workers/ServiceWorkerContainer.h create mode 100644 dom/workers/ServiceWorkerEvents.cpp create mode 100644 dom/workers/ServiceWorkerEvents.h create mode 100644 dom/workers/ServiceWorkerInfo.cpp create mode 100644 dom/workers/ServiceWorkerInfo.h create mode 100644 dom/workers/ServiceWorkerJob.cpp create mode 100644 dom/workers/ServiceWorkerJob.h create mode 100644 dom/workers/ServiceWorkerJobQueue.cpp create mode 100644 dom/workers/ServiceWorkerJobQueue.h create mode 100644 dom/workers/ServiceWorkerManager.cpp create mode 100644 dom/workers/ServiceWorkerManager.h create mode 100644 dom/workers/ServiceWorkerManagerChild.cpp create mode 100644 dom/workers/ServiceWorkerManagerChild.h create mode 100644 dom/workers/ServiceWorkerManagerParent.cpp create mode 100644 dom/workers/ServiceWorkerManagerParent.h create mode 100644 dom/workers/ServiceWorkerManagerService.cpp create mode 100644 dom/workers/ServiceWorkerManagerService.h create mode 100644 dom/workers/ServiceWorkerPrivate.cpp create mode 100644 dom/workers/ServiceWorkerPrivate.h create mode 100644 dom/workers/ServiceWorkerRegisterJob.cpp create mode 100644 dom/workers/ServiceWorkerRegisterJob.h create mode 100644 dom/workers/ServiceWorkerRegistrar.cpp create mode 100644 dom/workers/ServiceWorkerRegistrar.h create mode 100644 dom/workers/ServiceWorkerRegistrarTypes.ipdlh create mode 100644 dom/workers/ServiceWorkerRegistration.cpp create mode 100644 dom/workers/ServiceWorkerRegistration.h create mode 100644 dom/workers/ServiceWorkerRegistrationInfo.cpp create mode 100644 dom/workers/ServiceWorkerRegistrationInfo.h create mode 100644 dom/workers/ServiceWorkerScriptCache.cpp create mode 100644 dom/workers/ServiceWorkerScriptCache.h create mode 100644 dom/workers/ServiceWorkerUnregisterJob.cpp create mode 100644 dom/workers/ServiceWorkerUnregisterJob.h create mode 100644 dom/workers/ServiceWorkerUpdateJob.cpp create mode 100644 dom/workers/ServiceWorkerUpdateJob.h create mode 100644 dom/workers/ServiceWorkerWindowClient.cpp create mode 100644 dom/workers/ServiceWorkerWindowClient.h create mode 100644 dom/workers/SharedWorker.cpp create mode 100644 dom/workers/SharedWorker.h create mode 100644 dom/workers/WorkerDebuggerManager.cpp create mode 100644 dom/workers/WorkerDebuggerManager.h create mode 100644 dom/workers/WorkerHolder.cpp create mode 100644 dom/workers/WorkerHolder.h create mode 100644 dom/workers/WorkerInlines.h create mode 100644 dom/workers/WorkerLocation.cpp create mode 100644 dom/workers/WorkerLocation.h create mode 100644 dom/workers/WorkerNavigator.cpp create mode 100644 dom/workers/WorkerNavigator.h create mode 100644 dom/workers/WorkerPrefs.h create mode 100644 dom/workers/WorkerPrivate.cpp create mode 100644 dom/workers/WorkerPrivate.h create mode 100644 dom/workers/WorkerRunnable.cpp create mode 100644 dom/workers/WorkerRunnable.h create mode 100644 dom/workers/WorkerScope.cpp create mode 100644 dom/workers/WorkerScope.h create mode 100644 dom/workers/WorkerThread.cpp create mode 100644 dom/workers/WorkerThread.h create mode 100644 dom/workers/Workers.h create mode 100644 dom/workers/moz.build create mode 100644 dom/workers/nsIWorkerDebugger.idl create mode 100644 dom/workers/nsIWorkerDebuggerManager.idl 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_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_iframe1.html create mode 100644 dom/workers/test/WorkerDebugger_frozen_iframe2.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/browser.ini create mode 100644 dom/workers/test/browser_bug1047663.js create mode 100644 dom/workers/test/browser_bug1104623.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_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/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/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/empty.html 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/extensions/bootstrap/bootstrap.js create mode 100644 dom/workers/test/extensions/bootstrap/install.rdf create mode 100644 dom/workers/test/extensions/bootstrap/jar.mn create mode 100644 dom/workers/test/extensions/bootstrap/moz.build create mode 100644 dom/workers/test/extensions/bootstrap/worker.js create mode 100644 dom/workers/test/extensions/bootstrap/workerbootstrap-test@mozilla.org.xpi create mode 100644 dom/workers/test/extensions/moz.build create mode 100644 dom/workers/test/extensions/traditional/WorkerTest.js create mode 100644 dom/workers/test/extensions/traditional/WorkerTest.manifest create mode 100644 dom/workers/test/extensions/traditional/install.rdf create mode 100644 dom/workers/test/extensions/traditional/jar.mn create mode 100644 dom/workers/test/extensions/traditional/moz.build create mode 100644 dom/workers/test/extensions/traditional/nsIWorkerTest.idl create mode 100644 dom/workers/test/extensions/traditional/worker-test@mozilla.org.xpi create mode 100644 dom/workers/test/extensions/traditional/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_worker.js create mode 100644 dom/workers/test/fileapi_chromeScript.js create mode 100644 dom/workers/test/foreign.js create mode 100644 dom/workers/test/frame_script.js create mode 100644 dom/workers/test/gtest/TestReadWrite.cpp create mode 100644 dom/workers/test/gtest/moz.build 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/json_worker.js create mode 100644 dom/workers/test/jsversion_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/mochitest.ini create mode 100644 dom/workers/test/multi_sharedWorker_frame.html create mode 100644 dom/workers/test/multi_sharedWorker_sharedWorker.js 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/serviceworkers/activate_event_error_worker.js create mode 100644 dom/workers/test/serviceworkers/blocking_install_event_worker.js create mode 100644 dom/workers/test/serviceworkers/browser.ini create mode 100644 dom/workers/test/serviceworkers/browser_base_force_refresh.html create mode 100644 dom/workers/test/serviceworkers/browser_cached_force_refresh.html create mode 100644 dom/workers/test/serviceworkers/browser_download.js create mode 100644 dom/workers/test/serviceworkers/browser_force_refresh.js create mode 100644 dom/workers/test/serviceworkers/bug1151916_driver.html create mode 100644 dom/workers/test/serviceworkers/bug1151916_worker.js create mode 100644 dom/workers/test/serviceworkers/bug1240436_worker.js create mode 100644 dom/workers/test/serviceworkers/chrome.ini create mode 100644 dom/workers/test/serviceworkers/chrome_helpers.js create mode 100644 dom/workers/test/serviceworkers/claim_clients/client.html create mode 100644 dom/workers/test/serviceworkers/claim_fetch_worker.js create mode 100644 dom/workers/test/serviceworkers/claim_oninstall_worker.js create mode 100644 dom/workers/test/serviceworkers/claim_worker_1.js create mode 100644 dom/workers/test/serviceworkers/claim_worker_2.js create mode 100644 dom/workers/test/serviceworkers/close_test.js create mode 100644 dom/workers/test/serviceworkers/controller/index.html create mode 100644 dom/workers/test/serviceworkers/create_another_sharedWorker.html create mode 100644 dom/workers/test/serviceworkers/download/window.html create mode 100644 dom/workers/test/serviceworkers/download/worker.js create mode 100644 dom/workers/test/serviceworkers/empty.js create mode 100644 dom/workers/test/serviceworkers/error_reporting_helpers.js create mode 100644 dom/workers/test/serviceworkers/eval_worker.js create mode 100644 dom/workers/test/serviceworkers/eventsource/eventsource.resource create mode 100644 dom/workers/test/serviceworkers/eventsource/eventsource.resource^headers^ create mode 100644 dom/workers/test/serviceworkers/eventsource/eventsource_cors_response.html create mode 100644 dom/workers/test/serviceworkers/eventsource/eventsource_cors_response_intercept_worker.js create mode 100644 dom/workers/test/serviceworkers/eventsource/eventsource_mixed_content_cors_response.html create mode 100644 dom/workers/test/serviceworkers/eventsource/eventsource_mixed_content_cors_response_intercept_worker.js create mode 100644 dom/workers/test/serviceworkers/eventsource/eventsource_opaque_response.html create mode 100644 dom/workers/test/serviceworkers/eventsource/eventsource_opaque_response_intercept_worker.js create mode 100644 dom/workers/test/serviceworkers/eventsource/eventsource_register_worker.html create mode 100644 dom/workers/test/serviceworkers/eventsource/eventsource_synthetic_response.html create mode 100644 dom/workers/test/serviceworkers/eventsource/eventsource_synthetic_response_intercept_worker.js create mode 100644 dom/workers/test/serviceworkers/eventsource/eventsource_worker_helper.js create mode 100644 dom/workers/test/serviceworkers/fetch.js create mode 100644 dom/workers/test/serviceworkers/fetch/context/beacon.sjs create mode 100644 dom/workers/test/serviceworkers/fetch/context/context_test.js create mode 100644 dom/workers/test/serviceworkers/fetch/context/csp-violate.sjs create mode 100644 dom/workers/test/serviceworkers/fetch/context/index.html create mode 100644 dom/workers/test/serviceworkers/fetch/context/parentsharedworker.js create mode 100644 dom/workers/test/serviceworkers/fetch/context/parentworker.js create mode 100644 dom/workers/test/serviceworkers/fetch/context/ping.html create mode 100644 dom/workers/test/serviceworkers/fetch/context/realaudio.ogg create mode 100644 dom/workers/test/serviceworkers/fetch/context/realimg.jpg create mode 100644 dom/workers/test/serviceworkers/fetch/context/register.html create mode 100644 dom/workers/test/serviceworkers/fetch/context/sharedworker.js create mode 100644 dom/workers/test/serviceworkers/fetch/context/unregister.html create mode 100644 dom/workers/test/serviceworkers/fetch/context/worker.js create mode 100644 dom/workers/test/serviceworkers/fetch/context/xml.xml create mode 100644 dom/workers/test/serviceworkers/fetch/deliver-gzip.sjs create mode 100644 dom/workers/test/serviceworkers/fetch/fetch_tests.js create mode 100644 dom/workers/test/serviceworkers/fetch/fetch_worker_script.js create mode 100644 dom/workers/test/serviceworkers/fetch/hsts/embedder.html create mode 100644 dom/workers/test/serviceworkers/fetch/hsts/hsts_test.js create mode 100644 dom/workers/test/serviceworkers/fetch/hsts/image-20px.png create mode 100644 dom/workers/test/serviceworkers/fetch/hsts/image-40px.png create mode 100644 dom/workers/test/serviceworkers/fetch/hsts/image.html create mode 100644 dom/workers/test/serviceworkers/fetch/hsts/realindex.html create mode 100644 dom/workers/test/serviceworkers/fetch/hsts/register.html create mode 100644 dom/workers/test/serviceworkers/fetch/hsts/register.html^headers^ create mode 100644 dom/workers/test/serviceworkers/fetch/hsts/unregister.html create mode 100644 dom/workers/test/serviceworkers/fetch/https/clonedresponse/https_test.js create mode 100644 dom/workers/test/serviceworkers/fetch/https/clonedresponse/index.html create mode 100644 dom/workers/test/serviceworkers/fetch/https/clonedresponse/register.html create mode 100644 dom/workers/test/serviceworkers/fetch/https/clonedresponse/unregister.html create mode 100644 dom/workers/test/serviceworkers/fetch/https/https_test.js create mode 100644 dom/workers/test/serviceworkers/fetch/https/index.html create mode 100644 dom/workers/test/serviceworkers/fetch/https/register.html create mode 100644 dom/workers/test/serviceworkers/fetch/https/unregister.html create mode 100644 dom/workers/test/serviceworkers/fetch/imagecache-maxage/image-20px.png create mode 100644 dom/workers/test/serviceworkers/fetch/imagecache-maxage/image-40px.png create mode 100644 dom/workers/test/serviceworkers/fetch/imagecache-maxage/index.html create mode 100644 dom/workers/test/serviceworkers/fetch/imagecache-maxage/maxage_test.js create mode 100644 dom/workers/test/serviceworkers/fetch/imagecache-maxage/register.html create mode 100644 dom/workers/test/serviceworkers/fetch/imagecache-maxage/unregister.html create mode 100644 dom/workers/test/serviceworkers/fetch/imagecache/image-20px.png create mode 100644 dom/workers/test/serviceworkers/fetch/imagecache/image-40px.png create mode 100644 dom/workers/test/serviceworkers/fetch/imagecache/imagecache_test.js create mode 100644 dom/workers/test/serviceworkers/fetch/imagecache/index.html create mode 100644 dom/workers/test/serviceworkers/fetch/imagecache/postmortem.html create mode 100644 dom/workers/test/serviceworkers/fetch/imagecache/register.html create mode 100644 dom/workers/test/serviceworkers/fetch/imagecache/unregister.html create mode 100644 dom/workers/test/serviceworkers/fetch/importscript-mixedcontent/https_test.js create mode 100644 dom/workers/test/serviceworkers/fetch/importscript-mixedcontent/register.html create mode 100644 dom/workers/test/serviceworkers/fetch/importscript-mixedcontent/unregister.html create mode 100644 dom/workers/test/serviceworkers/fetch/index.html create mode 100644 dom/workers/test/serviceworkers/fetch/interrupt.sjs create mode 100644 dom/workers/test/serviceworkers/fetch/origin/https/index-https.sjs create mode 100644 dom/workers/test/serviceworkers/fetch/origin/https/origin_test.js create mode 100644 dom/workers/test/serviceworkers/fetch/origin/https/realindex.html create mode 100644 dom/workers/test/serviceworkers/fetch/origin/https/realindex.html^headers^ create mode 100644 dom/workers/test/serviceworkers/fetch/origin/https/register.html create mode 100644 dom/workers/test/serviceworkers/fetch/origin/https/unregister.html create mode 100644 dom/workers/test/serviceworkers/fetch/origin/index-to-https.sjs create mode 100644 dom/workers/test/serviceworkers/fetch/origin/index.sjs create mode 100644 dom/workers/test/serviceworkers/fetch/origin/origin_test.js create mode 100644 dom/workers/test/serviceworkers/fetch/origin/realindex.html create mode 100644 dom/workers/test/serviceworkers/fetch/origin/realindex.html^headers^ create mode 100644 dom/workers/test/serviceworkers/fetch/origin/register.html create mode 100644 dom/workers/test/serviceworkers/fetch/origin/unregister.html create mode 100644 dom/workers/test/serviceworkers/fetch/plugin/plugins.html create mode 100644 dom/workers/test/serviceworkers/fetch/plugin/worker.js create mode 100644 dom/workers/test/serviceworkers/fetch/real-file.txt create mode 100644 dom/workers/test/serviceworkers/fetch/redirect.sjs create mode 100644 dom/workers/test/serviceworkers/fetch/requesturl/index.html create mode 100644 dom/workers/test/serviceworkers/fetch/requesturl/redirect.sjs create mode 100644 dom/workers/test/serviceworkers/fetch/requesturl/redirector.html create mode 100644 dom/workers/test/serviceworkers/fetch/requesturl/register.html create mode 100644 dom/workers/test/serviceworkers/fetch/requesturl/requesturl_test.js create mode 100644 dom/workers/test/serviceworkers/fetch/requesturl/secret.html create mode 100644 dom/workers/test/serviceworkers/fetch/requesturl/unregister.html create mode 100644 dom/workers/test/serviceworkers/fetch/sandbox/index.html create mode 100644 dom/workers/test/serviceworkers/fetch/sandbox/intercepted_index.html create mode 100644 dom/workers/test/serviceworkers/fetch/sandbox/register.html create mode 100644 dom/workers/test/serviceworkers/fetch/sandbox/sandbox_test.js create mode 100644 dom/workers/test/serviceworkers/fetch/sandbox/unregister.html create mode 100644 dom/workers/test/serviceworkers/fetch/upgrade-insecure/embedder.html create mode 100644 dom/workers/test/serviceworkers/fetch/upgrade-insecure/embedder.html^headers^ create mode 100644 dom/workers/test/serviceworkers/fetch/upgrade-insecure/image-20px.png create mode 100644 dom/workers/test/serviceworkers/fetch/upgrade-insecure/image-40px.png create mode 100644 dom/workers/test/serviceworkers/fetch/upgrade-insecure/image.html create mode 100644 dom/workers/test/serviceworkers/fetch/upgrade-insecure/realindex.html create mode 100644 dom/workers/test/serviceworkers/fetch/upgrade-insecure/register.html create mode 100644 dom/workers/test/serviceworkers/fetch/upgrade-insecure/unregister.html create mode 100644 dom/workers/test/serviceworkers/fetch/upgrade-insecure/upgrade-insecure_test.js create mode 100644 dom/workers/test/serviceworkers/fetch_event_worker.js create mode 100644 dom/workers/test/serviceworkers/file_blob_response_worker.js create mode 100644 dom/workers/test/serviceworkers/force_refresh_browser_worker.js create mode 100644 dom/workers/test/serviceworkers/force_refresh_worker.js create mode 100644 dom/workers/test/serviceworkers/gzip_redirect_worker.js create mode 100644 dom/workers/test/serviceworkers/header_checker.sjs create mode 100644 dom/workers/test/serviceworkers/hello.html create mode 100644 dom/workers/test/serviceworkers/importscript.sjs create mode 100644 dom/workers/test/serviceworkers/importscript_worker.js create mode 100644 dom/workers/test/serviceworkers/install_event_error_worker.js create mode 100644 dom/workers/test/serviceworkers/install_event_worker.js create mode 100644 dom/workers/test/serviceworkers/lorem_script.js create mode 100644 dom/workers/test/serviceworkers/match_all_advanced_worker.js create mode 100644 dom/workers/test/serviceworkers/match_all_client/match_all_client_id.html create mode 100644 dom/workers/test/serviceworkers/match_all_client_id_worker.js create mode 100644 dom/workers/test/serviceworkers/match_all_clients/match_all_controlled.html create mode 100644 dom/workers/test/serviceworkers/match_all_properties_worker.js create mode 100644 dom/workers/test/serviceworkers/match_all_worker.js create mode 100644 dom/workers/test/serviceworkers/message_posting_worker.js create mode 100644 dom/workers/test/serviceworkers/message_receiver.html create mode 100644 dom/workers/test/serviceworkers/mochitest.ini create mode 100644 dom/workers/test/serviceworkers/notification/listener.html create mode 100644 dom/workers/test/serviceworkers/notification/register.html create mode 100644 dom/workers/test/serviceworkers/notification/unregister.html create mode 100644 dom/workers/test/serviceworkers/notification_alt/register.html create mode 100644 dom/workers/test/serviceworkers/notification_alt/unregister.html create mode 100644 dom/workers/test/serviceworkers/notification_constructor_error.js create mode 100644 dom/workers/test/serviceworkers/notification_get_sw.js create mode 100644 dom/workers/test/serviceworkers/notificationclick-otherwindow.html create mode 100644 dom/workers/test/serviceworkers/notificationclick.html create mode 100644 dom/workers/test/serviceworkers/notificationclick.js create mode 100644 dom/workers/test/serviceworkers/notificationclick_focus.html create mode 100644 dom/workers/test/serviceworkers/notificationclick_focus.js create mode 100644 dom/workers/test/serviceworkers/notificationclose.html create mode 100644 dom/workers/test/serviceworkers/notificationclose.js create mode 100644 dom/workers/test/serviceworkers/notify_loaded.js create mode 100644 dom/workers/test/serviceworkers/opaque_intercept_worker.js create mode 100644 dom/workers/test/serviceworkers/openWindow_worker.js create mode 100644 dom/workers/test/serviceworkers/open_window/client.html create mode 100644 dom/workers/test/serviceworkers/parse_error_worker.js create mode 100644 dom/workers/test/serviceworkers/redirect.sjs create mode 100644 dom/workers/test/serviceworkers/redirect_post.sjs create mode 100644 dom/workers/test/serviceworkers/redirect_serviceworker.sjs create mode 100644 dom/workers/test/serviceworkers/register_https.html create mode 100644 dom/workers/test/serviceworkers/sanitize/example_check_and_unregister.html create mode 100644 dom/workers/test/serviceworkers/sanitize/frame.html create mode 100644 dom/workers/test/serviceworkers/sanitize/register.html create mode 100644 dom/workers/test/serviceworkers/sanitize_worker.js create mode 100644 dom/workers/test/serviceworkers/scope/scope_worker.js create mode 100644 dom/workers/test/serviceworkers/serviceworker.html create mode 100644 dom/workers/test/serviceworkers/serviceworker_not_sharedworker.js create mode 100644 dom/workers/test/serviceworkers/serviceworker_wrapper.js create mode 100644 dom/workers/test/serviceworkers/serviceworkerinfo_iframe.html create mode 100644 dom/workers/test/serviceworkers/serviceworkermanager_iframe.html create mode 100644 dom/workers/test/serviceworkers/serviceworkerregistrationinfo_iframe.html create mode 100644 dom/workers/test/serviceworkers/sharedWorker_fetch.js create mode 100644 dom/workers/test/serviceworkers/simpleregister/index.html create mode 100644 dom/workers/test/serviceworkers/simpleregister/ready.html create mode 100644 dom/workers/test/serviceworkers/skip_waiting_installed_worker.js create mode 100644 dom/workers/test/serviceworkers/skip_waiting_scope/index.html create mode 100644 dom/workers/test/serviceworkers/source_message_posting_worker.js create mode 100644 dom/workers/test/serviceworkers/strict_mode_warning.js create mode 100644 dom/workers/test/serviceworkers/sw_bad_mime_type.js create mode 100644 dom/workers/test/serviceworkers/sw_bad_mime_type.js^headers^ create mode 100644 dom/workers/test/serviceworkers/sw_clients/file_blob_upload_frame.html create mode 100644 dom/workers/test/serviceworkers/sw_clients/navigator.html create mode 100644 dom/workers/test/serviceworkers/sw_clients/refresher.html create mode 100644 dom/workers/test/serviceworkers/sw_clients/refresher_cached.html create mode 100644 dom/workers/test/serviceworkers/sw_clients/refresher_cached_compressed.html create mode 100644 dom/workers/test/serviceworkers/sw_clients/refresher_cached_compressed.html^headers^ create mode 100644 dom/workers/test/serviceworkers/sw_clients/refresher_compressed.html create mode 100644 dom/workers/test/serviceworkers/sw_clients/refresher_compressed.html^headers^ create mode 100644 dom/workers/test/serviceworkers/sw_clients/service_worker_controlled.html create mode 100644 dom/workers/test/serviceworkers/sw_clients/simple.html create mode 100644 dom/workers/test/serviceworkers/swa/worker_scope_different.js create mode 100644 dom/workers/test/serviceworkers/swa/worker_scope_different.js^headers^ create mode 100644 dom/workers/test/serviceworkers/swa/worker_scope_different2.js create mode 100644 dom/workers/test/serviceworkers/swa/worker_scope_different2.js^headers^ create mode 100644 dom/workers/test/serviceworkers/swa/worker_scope_precise.js create mode 100644 dom/workers/test/serviceworkers/swa/worker_scope_precise.js^headers^ create mode 100644 dom/workers/test/serviceworkers/swa/worker_scope_too_deep.js create mode 100644 dom/workers/test/serviceworkers/swa/worker_scope_too_deep.js^headers^ create mode 100644 dom/workers/test/serviceworkers/swa/worker_scope_too_narrow.js create mode 100644 dom/workers/test/serviceworkers/swa/worker_scope_too_narrow.js^headers^ create mode 100644 dom/workers/test/serviceworkers/test_bug1151916.html create mode 100644 dom/workers/test/serviceworkers/test_bug1240436.html create mode 100644 dom/workers/test/serviceworkers/test_claim.html create mode 100644 dom/workers/test/serviceworkers/test_claim_fetch.html create mode 100644 dom/workers/test/serviceworkers/test_claim_oninstall.html create mode 100644 dom/workers/test/serviceworkers/test_client_focus.html create mode 100644 dom/workers/test/serviceworkers/test_close.html create mode 100644 dom/workers/test/serviceworkers/test_controller.html create mode 100644 dom/workers/test/serviceworkers/test_cross_origin_url_after_redirect.html create mode 100644 dom/workers/test/serviceworkers/test_csp_upgrade-insecure_intercept.html create mode 100644 dom/workers/test/serviceworkers/test_empty_serviceworker.html create mode 100644 dom/workers/test/serviceworkers/test_error_reporting.html create mode 100644 dom/workers/test/serviceworkers/test_escapedSlashes.html create mode 100644 dom/workers/test/serviceworkers/test_eval_allowed.html create mode 100644 dom/workers/test/serviceworkers/test_eval_allowed.html^headers^ create mode 100644 dom/workers/test/serviceworkers/test_eventsource_intercept.html create mode 100644 dom/workers/test/serviceworkers/test_fetch_event.html create mode 100644 dom/workers/test/serviceworkers/test_fetch_integrity.html create mode 100644 dom/workers/test/serviceworkers/test_file_blob_response.html create mode 100644 dom/workers/test/serviceworkers/test_file_blob_upload.html create mode 100644 dom/workers/test/serviceworkers/test_force_refresh.html create mode 100644 dom/workers/test/serviceworkers/test_gzip_redirect.html create mode 100644 dom/workers/test/serviceworkers/test_hsts_upgrade_intercept.html create mode 100644 dom/workers/test/serviceworkers/test_https_fetch.html create mode 100644 dom/workers/test/serviceworkers/test_https_fetch_cloned_response.html create mode 100644 dom/workers/test/serviceworkers/test_https_origin_after_redirect.html create mode 100644 dom/workers/test/serviceworkers/test_https_origin_after_redirect_cached.html create mode 100644 dom/workers/test/serviceworkers/test_https_synth_fetch_from_cached_sw.html create mode 100644 dom/workers/test/serviceworkers/test_imagecache.html create mode 100644 dom/workers/test/serviceworkers/test_imagecache_max_age.html create mode 100644 dom/workers/test/serviceworkers/test_importscript.html create mode 100644 dom/workers/test/serviceworkers/test_importscript_mixedcontent.html create mode 100644 dom/workers/test/serviceworkers/test_install_event.html create mode 100644 dom/workers/test/serviceworkers/test_install_event_gc.html create mode 100644 dom/workers/test/serviceworkers/test_installation_simple.html create mode 100644 dom/workers/test/serviceworkers/test_match_all.html create mode 100644 dom/workers/test/serviceworkers/test_match_all_advanced.html create mode 100644 dom/workers/test/serviceworkers/test_match_all_client_id.html create mode 100644 dom/workers/test/serviceworkers/test_match_all_client_properties.html create mode 100644 dom/workers/test/serviceworkers/test_navigator.html create mode 100644 dom/workers/test/serviceworkers/test_not_intercept_plugin.html create mode 100644 dom/workers/test/serviceworkers/test_notification_constructor_error.html create mode 100644 dom/workers/test/serviceworkers/test_notification_get.html create mode 100644 dom/workers/test/serviceworkers/test_notificationclick-otherwindow.html create mode 100644 dom/workers/test/serviceworkers/test_notificationclick.html create mode 100644 dom/workers/test/serviceworkers/test_notificationclick_focus.html create mode 100644 dom/workers/test/serviceworkers/test_notificationclose.html create mode 100644 dom/workers/test/serviceworkers/test_opaque_intercept.html create mode 100644 dom/workers/test/serviceworkers/test_openWindow.html create mode 100644 dom/workers/test/serviceworkers/test_origin_after_redirect.html create mode 100644 dom/workers/test/serviceworkers/test_origin_after_redirect_cached.html create mode 100644 dom/workers/test/serviceworkers/test_origin_after_redirect_to_https.html create mode 100644 dom/workers/test/serviceworkers/test_origin_after_redirect_to_https_cached.html create mode 100644 dom/workers/test/serviceworkers/test_post_message.html create mode 100644 dom/workers/test/serviceworkers/test_post_message_advanced.html create mode 100644 dom/workers/test/serviceworkers/test_post_message_source.html create mode 100644 dom/workers/test/serviceworkers/test_privateBrowsing.html create mode 100644 dom/workers/test/serviceworkers/test_register_base.html create mode 100644 dom/workers/test/serviceworkers/test_register_https_in_http.html create mode 100644 dom/workers/test/serviceworkers/test_request_context.js create mode 100644 dom/workers/test/serviceworkers/test_request_context_audio.html create mode 100644 dom/workers/test/serviceworkers/test_request_context_beacon.html create mode 100644 dom/workers/test/serviceworkers/test_request_context_cache.html create mode 100644 dom/workers/test/serviceworkers/test_request_context_cspreport.html create mode 100644 dom/workers/test/serviceworkers/test_request_context_embed.html create mode 100644 dom/workers/test/serviceworkers/test_request_context_fetch.html create mode 100644 dom/workers/test/serviceworkers/test_request_context_font.html create mode 100644 dom/workers/test/serviceworkers/test_request_context_frame.html create mode 100644 dom/workers/test/serviceworkers/test_request_context_iframe.html create mode 100644 dom/workers/test/serviceworkers/test_request_context_image.html create mode 100644 dom/workers/test/serviceworkers/test_request_context_imagesrcset.html create mode 100644 dom/workers/test/serviceworkers/test_request_context_internal.html create mode 100644 dom/workers/test/serviceworkers/test_request_context_nestedworker.html create mode 100644 dom/workers/test/serviceworkers/test_request_context_nestedworkerinsharedworker.html create mode 100644 dom/workers/test/serviceworkers/test_request_context_object.html create mode 100644 dom/workers/test/serviceworkers/test_request_context_picture.html create mode 100644 dom/workers/test/serviceworkers/test_request_context_ping.html create mode 100644 dom/workers/test/serviceworkers/test_request_context_plugin.html create mode 100644 dom/workers/test/serviceworkers/test_request_context_script.html create mode 100644 dom/workers/test/serviceworkers/test_request_context_sharedworker.html create mode 100644 dom/workers/test/serviceworkers/test_request_context_style.html create mode 100644 dom/workers/test/serviceworkers/test_request_context_track.html create mode 100644 dom/workers/test/serviceworkers/test_request_context_video.html create mode 100644 dom/workers/test/serviceworkers/test_request_context_worker.html create mode 100644 dom/workers/test/serviceworkers/test_request_context_xhr.html create mode 100644 dom/workers/test/serviceworkers/test_request_context_xslt.html create mode 100644 dom/workers/test/serviceworkers/test_sandbox_intercept.html create mode 100644 dom/workers/test/serviceworkers/test_sanitize.html create mode 100644 dom/workers/test/serviceworkers/test_sanitize_domain.html create mode 100644 dom/workers/test/serviceworkers/test_scopes.html create mode 100644 dom/workers/test/serviceworkers/test_service_worker_allowed.html create mode 100644 dom/workers/test/serviceworkers/test_serviceworker_header.html create mode 100644 dom/workers/test/serviceworkers/test_serviceworker_interfaces.html create mode 100644 dom/workers/test/serviceworkers/test_serviceworker_interfaces.js create mode 100644 dom/workers/test/serviceworkers/test_serviceworker_not_sharedworker.html create mode 100644 dom/workers/test/serviceworkers/test_serviceworkerinfo.xul create mode 100644 dom/workers/test/serviceworkers/test_serviceworkermanager.xul create mode 100644 dom/workers/test/serviceworkers/test_serviceworkerregistrationinfo.xul create mode 100644 dom/workers/test/serviceworkers/test_skip_waiting.html create mode 100644 dom/workers/test/serviceworkers/test_strict_mode_warning.html create mode 100644 dom/workers/test/serviceworkers/test_third_party_iframes.html create mode 100644 dom/workers/test/serviceworkers/test_unregister.html create mode 100644 dom/workers/test/serviceworkers/test_unresolved_fetch_interception.html create mode 100644 dom/workers/test/serviceworkers/test_workerUnregister.html create mode 100644 dom/workers/test/serviceworkers/test_workerUpdate.html create mode 100644 dom/workers/test/serviceworkers/test_workerupdatefoundevent.html create mode 100644 dom/workers/test/serviceworkers/test_xslt.html create mode 100644 dom/workers/test/serviceworkers/thirdparty/iframe1.html create mode 100644 dom/workers/test/serviceworkers/thirdparty/iframe2.html create mode 100644 dom/workers/test/serviceworkers/thirdparty/register.html create mode 100644 dom/workers/test/serviceworkers/thirdparty/sw.js create mode 100644 dom/workers/test/serviceworkers/thirdparty/unregister.html create mode 100644 dom/workers/test/serviceworkers/unregister/index.html create mode 100644 dom/workers/test/serviceworkers/unregister/unregister.html create mode 100644 dom/workers/test/serviceworkers/unresolved_fetch_worker.js create mode 100644 dom/workers/test/serviceworkers/updatefoundevent.html create mode 100644 dom/workers/test/serviceworkers/worker.js create mode 100644 dom/workers/test/serviceworkers/worker2.js create mode 100644 dom/workers/test/serviceworkers/worker3.js create mode 100644 dom/workers/test/serviceworkers/workerUpdate/update.html create mode 100644 dom/workers/test/serviceworkers/worker_unregister.js create mode 100644 dom/workers/test/serviceworkers/worker_update.js create mode 100644 dom/workers/test/serviceworkers/worker_updatefoundevent.js create mode 100644 dom/workers/test/serviceworkers/worker_updatefoundevent2.js create mode 100644 dom/workers/test/serviceworkers/xslt/test.xml create mode 100644 dom/workers/test/serviceworkers/xslt/xslt.sjs create mode 100644 dom/workers/test/serviceworkers/xslt_worker.js 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/simpleThread_worker.js create mode 100644 dom/workers/test/suspend_iframe.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.xul create mode 100644 dom/workers/test/test_WorkerDebugger.postMessage.xul create mode 100644 dom/workers/test/test_WorkerDebugger.xul create mode 100644 dom/workers/test/test_WorkerDebuggerGlobalScope.createSandbox.xul create mode 100644 dom/workers/test/test_WorkerDebuggerGlobalScope.enterEventLoop.xul create mode 100644 dom/workers/test/test_WorkerDebuggerGlobalScope.reportError.xul create mode 100644 dom/workers/test/test_WorkerDebuggerGlobalScope.setImmediate.xul create mode 100644 dom/workers/test/test_WorkerDebuggerManager.xul create mode 100644 dom/workers/test/test_WorkerDebugger_console.xul create mode 100644 dom/workers/test/test_WorkerDebugger_frozen.xul create mode 100644 dom/workers/test/test_WorkerDebugger_promise.xul create mode 100644 dom/workers/test/test_WorkerDebugger_suspended.xul 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.xul 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_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.xul create mode 100644 dom/workers/test/test_chromeWorkerJSM.xul create mode 100644 dom/workers/test/test_clearTimeouts.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_consoleSharedWorkers.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_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_extension.xul create mode 100644 dom/workers/test/test_extensionBootstrap.xul create mode 100644 dom/workers/test/test_fibonacci.html create mode 100644 dom/workers/test/test_file.xul create mode 100644 dom/workers/test/test_fileBlobPosting.xul create mode 100644 dom/workers/test/test_fileBlobSubWorker.xul create mode 100644 dom/workers/test/test_filePosting.xul create mode 100644 dom/workers/test/test_fileReadSlice.xul create mode 100644 dom/workers/test/test_fileReader.html create mode 100644 dom/workers/test/test_fileReaderSync.xul create mode 100644 dom/workers/test/test_fileReaderSyncErrors.xul create mode 100644 dom/workers/test/test_fileSlice.xul create mode 100644 dom/workers/test/test_fileSubWorker.xul 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_jsversion.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.html create mode 100644 dom/workers/test/test_navigator.html create mode 100644 dom/workers/test/test_navigator_languages.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_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_simpleThread.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_webSocket_sharedWorker.html create mode 100644 dom/workers/test/test_websocket1.html create mode 100644 dom/workers/test/test_websocket2.html create mode 100644 dom/workers/test/test_websocket3.html create mode 100644 dom/workers/test/test_websocket4.html create mode 100644 dom/workers/test/test_websocket5.html create mode 100644 dom/workers/test/test_websocket_basic.html create mode 100644 dom/workers/test/test_websocket_https.html create mode 100644 dom/workers/test/test_websocket_loadgroup.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_workersDisabled.html create mode 100644 dom/workers/test/test_workersDisabled.xul 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/webSocket_sharedWorker.js create mode 100644 dom/workers/test/websocket_basic_worker.js create mode 100644 dom/workers/test/websocket_helpers.js create mode 100644 dom/workers/test/websocket_https.html create mode 100644 dom/workers/test/websocket_https_worker.js create mode 100644 dom/workers/test/websocket_loadgroup_worker.js create mode 100644 dom/workers/test/websocket_worker1.js create mode 100644 dom/workers/test/websocket_worker2.js create mode 100644 dom/workers/test/websocket_worker3.js create mode 100644 dom/workers/test/websocket_worker4.js create mode 100644 dom/workers/test/websocket_worker5.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_consoleAndBlobs.js create mode 100644 dom/workers/test/worker_driver.js create mode 100644 dom/workers/test/worker_fileReader.js 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_suspended.js create mode 100644 dom/workers/test/worker_wrapper.js create mode 100644 dom/workers/test/workersDisabled_worker.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_fileReader.js create mode 100644 dom/workers/test/xpcshell/test_workers.js create mode 100644 dom/workers/test/xpcshell/xpcshell.ini (limited to 'dom/workers') diff --git a/dom/workers/ChromeWorkerScope.cpp b/dom/workers/ChromeWorkerScope.cpp new file mode 100644 index 000000000..68ebebfc3 --- /dev/null +++ b/dom/workers/ChromeWorkerScope.cpp @@ -0,0 +1,74 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "ChromeWorkerScope.h" + +#include "jsapi.h" + +#include "nsXPCOM.h" +#include "nsNativeCharsetUtils.h" +#include "nsString.h" + +#include "WorkerPrivate.h" + +using namespace mozilla::dom; +USING_WORKERS_NAMESPACE + +namespace { + +#ifdef BUILD_CTYPES + +char* +UnicodeToNative(JSContext* aCx, const char16_t* aSource, size_t aSourceLen) +{ + nsDependentString unicode(aSource, aSourceLen); + + nsAutoCString native; + if (NS_FAILED(NS_CopyUnicodeToNative(unicode, native))) { + JS_ReportErrorASCII(aCx, "Could not convert string to native charset!"); + return nullptr; + } + + char* result = static_cast(JS_malloc(aCx, native.Length() + 1)); + if (!result) { + return nullptr; + } + + memcpy(result, native.get(), native.Length()); + result[native.Length()] = 0; + return result; +} + +#endif // BUILD_CTYPES + +} // namespace + +BEGIN_WORKERS_NAMESPACE + +bool +DefineChromeWorkerFunctions(JSContext* aCx, JS::Handle aGlobal) +{ + // Currently ctypes is the only special property given to ChromeWorkers. +#ifdef BUILD_CTYPES + { + JS::Rooted ctypes(aCx); + if (!JS_InitCTypesClass(aCx, aGlobal) || + !JS_GetProperty(aCx, aGlobal, "ctypes", &ctypes)) { + return false; + } + + static const JSCTypesCallbacks callbacks = { + UnicodeToNative + }; + + JS_SetCTypesCallbacks(ctypes.toObjectOrNull(), &callbacks); + } +#endif // BUILD_CTYPES + + return true; +} + +END_WORKERS_NAMESPACE diff --git a/dom/workers/ChromeWorkerScope.h b/dom/workers/ChromeWorkerScope.h new file mode 100644 index 000000000..306397b66 --- /dev/null +++ b/dom/workers/ChromeWorkerScope.h @@ -0,0 +1,19 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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/. */ + +#ifndef mozilla_dom_workers_chromeworkerscope_h__ +#define mozilla_dom_workers_chromeworkerscope_h__ + +#include "Workers.h" + +BEGIN_WORKERS_NAMESPACE + +bool +DefineChromeWorkerFunctions(JSContext* aCx, JS::Handle aGlobal); + +END_WORKERS_NAMESPACE + +#endif // mozilla_dom_workers_chromeworkerscope_h__ diff --git a/dom/workers/FileReaderSync.cpp b/dom/workers/FileReaderSync.cpp new file mode 100644 index 000000000..18efcb194 --- /dev/null +++ b/dom/workers/FileReaderSync.cpp @@ -0,0 +1,264 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "FileReaderSync.h" + +#include "jsfriendapi.h" +#include "mozilla/Unused.h" +#include "mozilla/Base64.h" +#include "mozilla/dom/EncodingUtils.h" +#include "mozilla/dom/File.h" +#include "nsContentUtils.h" +#include "mozilla/dom/FileReaderSyncBinding.h" +#include "nsCExternalHandlerService.h" +#include "nsComponentManagerUtils.h" +#include "nsCOMPtr.h" +#include "nsDOMClassInfoID.h" +#include "nsError.h" +#include "nsIConverterInputStream.h" +#include "nsIInputStream.h" +#include "nsISeekableStream.h" +#include "nsISupportsImpl.h" +#include "nsNetUtil.h" +#include "nsServiceManagerUtils.h" + +#include "RuntimeService.h" + +using namespace mozilla; +using namespace mozilla::dom; +using mozilla::dom::Optional; +using mozilla::dom::GlobalObject; + +// static +already_AddRefed +FileReaderSync::Constructor(const GlobalObject& aGlobal, ErrorResult& aRv) +{ + RefPtr frs = new FileReaderSync(); + + return frs.forget(); +} + +bool +FileReaderSync::WrapObject(JSContext* aCx, + JS::Handle aGivenProto, + JS::MutableHandle aReflector) +{ + return FileReaderSyncBinding::Wrap(aCx, this, aGivenProto, aReflector); +} + +void +FileReaderSync::ReadAsArrayBuffer(JSContext* aCx, + JS::Handle aScopeObj, + Blob& aBlob, + JS::MutableHandle aRetval, + ErrorResult& aRv) +{ + uint64_t blobSize = aBlob.GetSize(aRv); + if (NS_WARN_IF(aRv.Failed())) { + return; + } + + UniquePtr bufferData(js_pod_malloc(blobSize)); + if (!bufferData) { + aRv.Throw(NS_ERROR_OUT_OF_MEMORY); + return; + } + + nsCOMPtr stream; + aBlob.GetInternalStream(getter_AddRefs(stream), aRv); + if (NS_WARN_IF(aRv.Failed())) { + return; + } + + uint32_t numRead; + aRv = stream->Read(bufferData.get(), blobSize, &numRead); + if (NS_WARN_IF(aRv.Failed())) { + return; + } + NS_ASSERTION(numRead == blobSize, "failed to read data"); + + JSObject* arrayBuffer = JS_NewArrayBufferWithContents(aCx, blobSize, bufferData.get()); + if (!arrayBuffer) { + aRv.Throw(NS_ERROR_OUT_OF_MEMORY); + return; + } + // arrayBuffer takes the ownership when it is not null. Otherwise we + // need to release it explicitly. + mozilla::Unused << bufferData.release(); + + aRetval.set(arrayBuffer); +} + +void +FileReaderSync::ReadAsBinaryString(Blob& aBlob, + nsAString& aResult, + ErrorResult& aRv) +{ + nsCOMPtr stream; + aBlob.GetInternalStream(getter_AddRefs(stream), aRv); + if (NS_WARN_IF(aRv.Failed())) { + return; + } + + uint32_t numRead; + do { + char readBuf[4096]; + aRv = stream->Read(readBuf, sizeof(readBuf), &numRead); + if (NS_WARN_IF(aRv.Failed())) { + return; + } + + uint32_t oldLength = aResult.Length(); + AppendASCIItoUTF16(Substring(readBuf, readBuf + numRead), aResult); + if (aResult.Length() - oldLength != numRead) { + aRv.Throw(NS_ERROR_OUT_OF_MEMORY); + return; + } + } while (numRead > 0); +} + +void +FileReaderSync::ReadAsText(Blob& aBlob, + const Optional& aEncoding, + nsAString& aResult, + ErrorResult& aRv) +{ + nsCOMPtr stream; + aBlob.GetInternalStream(getter_AddRefs(stream), aRv); + if (NS_WARN_IF(aRv.Failed())) { + return; + } + + nsAutoCString encoding; + unsigned char sniffBuf[3] = { 0, 0, 0 }; + uint32_t numRead; + aRv = stream->Read(reinterpret_cast(sniffBuf), + sizeof(sniffBuf), &numRead); + if (NS_WARN_IF(aRv.Failed())) { + return; + } + + // The BOM sniffing is baked into the "decode" part of the Encoding + // Standard, which the File API references. + if (!nsContentUtils::CheckForBOM(sniffBuf, numRead, encoding)) { + // BOM sniffing failed. Try the API argument. + if (!aEncoding.WasPassed() || + !EncodingUtils::FindEncodingForLabel(aEncoding.Value(), + encoding)) { + // API argument failed. Try the type property of the blob. + nsAutoString type16; + aBlob.GetType(type16); + NS_ConvertUTF16toUTF8 type(type16); + nsAutoCString specifiedCharset; + bool haveCharset; + int32_t charsetStart, charsetEnd; + NS_ExtractCharsetFromContentType(type, + specifiedCharset, + &haveCharset, + &charsetStart, + &charsetEnd); + if (!EncodingUtils::FindEncodingForLabel(specifiedCharset, encoding)) { + // Type property failed. Use UTF-8. + encoding.AssignLiteral("UTF-8"); + } + } + } + + nsCOMPtr seekable = do_QueryInterface(stream); + if (!seekable) { + aRv.Throw(NS_ERROR_FAILURE); + return; + } + + // Seek to 0 because to undo the BOM sniffing advance. UTF-8 and UTF-16 + // decoders will swallow the BOM. + aRv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, 0); + if (NS_WARN_IF(aRv.Failed())) { + return; + } + + aRv = ConvertStream(stream, encoding.get(), aResult); + if (NS_WARN_IF(aRv.Failed())) { + return; + } +} + +void +FileReaderSync::ReadAsDataURL(Blob& aBlob, nsAString& aResult, + ErrorResult& aRv) +{ + nsAutoString scratchResult; + scratchResult.AssignLiteral("data:"); + + nsString contentType; + aBlob.GetType(contentType); + + if (contentType.IsEmpty()) { + scratchResult.AppendLiteral("application/octet-stream"); + } else { + scratchResult.Append(contentType); + } + scratchResult.AppendLiteral(";base64,"); + + nsCOMPtr stream; + aBlob.GetInternalStream(getter_AddRefs(stream), aRv); + if (NS_WARN_IF(aRv.Failed())){ + return; + } + + uint64_t size = aBlob.GetSize(aRv); + if (NS_WARN_IF(aRv.Failed())){ + return; + } + + nsCOMPtr bufferedStream; + aRv = NS_NewBufferedInputStream(getter_AddRefs(bufferedStream), stream, size); + if (NS_WARN_IF(aRv.Failed())){ + return; + } + + nsAutoString encodedData; + aRv = Base64EncodeInputStream(bufferedStream, encodedData, size); + if (NS_WARN_IF(aRv.Failed())){ + return; + } + + scratchResult.Append(encodedData); + + aResult = scratchResult; +} + +nsresult +FileReaderSync::ConvertStream(nsIInputStream *aStream, + const char *aCharset, + nsAString &aResult) +{ + nsCOMPtr converterStream = + do_CreateInstance("@mozilla.org/intl/converter-input-stream;1"); + NS_ENSURE_TRUE(converterStream, NS_ERROR_FAILURE); + + nsresult rv = converterStream->Init(aStream, aCharset, 8192, + nsIConverterInputStream::DEFAULT_REPLACEMENT_CHARACTER); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr unicharStream = + do_QueryInterface(converterStream); + NS_ENSURE_TRUE(unicharStream, NS_ERROR_FAILURE); + + uint32_t numChars; + nsString result; + while (NS_SUCCEEDED(unicharStream->ReadString(8192, result, &numChars)) && + numChars > 0) { + uint32_t oldLength = aResult.Length(); + aResult.Append(result); + if (aResult.Length() - oldLength != result.Length()) { + return NS_ERROR_OUT_OF_MEMORY; + } + } + + return rv; +} + diff --git a/dom/workers/FileReaderSync.h b/dom/workers/FileReaderSync.h new file mode 100644 index 000000000..db8f9d574 --- /dev/null +++ b/dom/workers/FileReaderSync.h @@ -0,0 +1,53 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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/. */ + +#ifndef mozilla_dom_filereadersync_h__ +#define mozilla_dom_filereadersync_h__ + +#include "Workers.h" + +class nsIInputStream; + +namespace mozilla { +class ErrorResult; + +namespace dom { +class Blob; +class GlobalObject; +template class Optional; + +class FileReaderSync final +{ + NS_INLINE_DECL_REFCOUNTING(FileReaderSync) + +private: + // Private destructor, to discourage deletion outside of Release(): + ~FileReaderSync() + { + } + + nsresult ConvertStream(nsIInputStream *aStream, const char *aCharset, + nsAString &aResult); + +public: + static already_AddRefed + Constructor(const GlobalObject& aGlobal, ErrorResult& aRv); + + bool WrapObject(JSContext* aCx, JS::Handle aGivenProto, JS::MutableHandle aReflector); + + void ReadAsArrayBuffer(JSContext* aCx, JS::Handle aScopeObj, + Blob& aBlob, JS::MutableHandle aRetval, + ErrorResult& aRv); + void ReadAsBinaryString(Blob& aBlob, nsAString& aResult, ErrorResult& aRv); + void ReadAsText(Blob& aBlob, const Optional& aEncoding, + nsAString& aResult, ErrorResult& aRv); + void ReadAsDataURL(Blob& aBlob, nsAString& aResult, ErrorResult& aRv); +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_filereadersync_h__ diff --git a/dom/workers/PServiceWorkerManager.ipdl b/dom/workers/PServiceWorkerManager.ipdl new file mode 100644 index 000000000..e7b97672d --- /dev/null +++ b/dom/workers/PServiceWorkerManager.ipdl @@ -0,0 +1,45 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +include protocol PBackground; + +include PBackgroundSharedTypes; +include ServiceWorkerRegistrarTypes; + +using mozilla::PrincipalOriginAttributes from "mozilla/ipc/BackgroundUtils.h"; + +namespace mozilla { +namespace dom { + +protocol PServiceWorkerManager +{ + manager PBackground; + +parent: + async Register(ServiceWorkerRegistrationData data); + + async Unregister(PrincipalInfo principalInfo, nsString scope); + + async PropagateSoftUpdate(PrincipalOriginAttributes originAttributes, + nsString scope); + async PropagateUnregister(PrincipalInfo principalInfo, nsString scope); + + async PropagateRemove(nsCString host); + + async PropagateRemoveAll(); + + async Shutdown(); + +child: + async NotifyRegister(ServiceWorkerRegistrationData data); + async NotifySoftUpdate(PrincipalOriginAttributes originAttributes, nsString scope); + async NotifyUnregister(PrincipalInfo principalInfo, nsString scope); + async NotifyRemove(nsCString host); + async NotifyRemoveAll(); + + async __delete__(); +}; + +} // namespace dom +} // namespace mozilla diff --git a/dom/workers/Principal.cpp b/dom/workers/Principal.cpp new file mode 100644 index 000000000..8fcd9ed10 --- /dev/null +++ b/dom/workers/Principal.cpp @@ -0,0 +1,51 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "Principal.h" + +#include "jsapi.h" +#include "mozilla/Assertions.h" + +BEGIN_WORKERS_NAMESPACE + +struct WorkerPrincipal final : public JSPrincipals +{ + bool write(JSContext* aCx, JSStructuredCloneWriter* aWriter) override { + MOZ_CRASH("WorkerPrincipal::write not implemented"); + return false; + } +}; + +JSPrincipals* +GetWorkerPrincipal() +{ + static WorkerPrincipal sPrincipal; + + /* + * To make sure the the principals refcount is initialized to one, atomically + * increment it on every pass though this function. If we discover this wasn't + * the first time, decrement it again. This avoids the need for + * synchronization. + */ + int32_t prevRefcount = sPrincipal.refcount++; + if (prevRefcount > 0) { + --sPrincipal.refcount; + } else { +#ifdef DEBUG + sPrincipal.debugToken = kJSPrincipalsDebugToken; +#endif + } + + return &sPrincipal; +} + +void +DestroyWorkerPrincipals(JSPrincipals* aPrincipals) +{ + MOZ_ASSERT_UNREACHABLE("Worker principals refcount should never fall below one"); +} + +END_WORKERS_NAMESPACE diff --git a/dom/workers/Principal.h b/dom/workers/Principal.h new file mode 100644 index 000000000..5bfe19443 --- /dev/null +++ b/dom/workers/Principal.h @@ -0,0 +1,22 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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/. */ + +#ifndef mozilla_dom_workers_principal_h__ +#define mozilla_dom_workers_principal_h__ + +#include "Workers.h" + +BEGIN_WORKERS_NAMESPACE + +JSPrincipals* +GetWorkerPrincipal(); + +void +DestroyWorkerPrincipals(JSPrincipals* aPrincipals); + +END_WORKERS_NAMESPACE + +#endif /* mozilla_dom_workers_principal_h__ */ diff --git a/dom/workers/Queue.h b/dom/workers/Queue.h new file mode 100644 index 000000000..c7a99158b --- /dev/null +++ b/dom/workers/Queue.h @@ -0,0 +1,203 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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/. */ + +#ifndef mozilla_dom_workers_queue_h__ +#define mozilla_dom_workers_queue_h__ + +#include "Workers.h" + +#include "mozilla/Mutex.h" +#include "nsTArray.h" + +BEGIN_WORKERS_NAMESPACE + +template +struct StorageWithTArray +{ + typedef AutoTArray StorageType; + + static void Reverse(StorageType& aStorage) + { + uint32_t length = aStorage.Length(); + for (uint32_t index = 0; index < length / 2; index++) { + uint32_t reverseIndex = length - 1 - index; + + T t1 = aStorage.ElementAt(index); + T t2 = aStorage.ElementAt(reverseIndex); + + aStorage.ReplaceElementsAt(index, 1, t2); + aStorage.ReplaceElementsAt(reverseIndex, 1, t1); + } + } + + static bool IsEmpty(const StorageType& aStorage) + { + return !!aStorage.IsEmpty(); + } + + static bool Push(StorageType& aStorage, const T& aEntry) + { + return !!aStorage.AppendElement(aEntry); + } + + static bool Pop(StorageType& aStorage, T& aEntry) + { + if (IsEmpty(aStorage)) { + return false; + } + + uint32_t index = aStorage.Length() - 1; + aEntry = aStorage.ElementAt(index); + aStorage.RemoveElementAt(index); + return true; + } + + static void Clear(StorageType& aStorage) + { + aStorage.Clear(); + } + + static void Compact(StorageType& aStorage) + { + aStorage.Compact(); + } +}; + +class LockingWithMutex +{ + mozilla::Mutex mMutex; + +protected: + LockingWithMutex() + : mMutex("LockingWithMutex::mMutex") + { } + + void Lock() + { + mMutex.Lock(); + } + + void Unlock() + { + mMutex.Unlock(); + } + + class AutoLock + { + LockingWithMutex& mHost; + + public: + explicit AutoLock(LockingWithMutex& aHost) + : mHost(aHost) + { + mHost.Lock(); + } + + ~AutoLock() + { + mHost.Unlock(); + } + }; + + friend class AutoLock; +}; + +class NoLocking +{ +protected: + void Lock() + { } + + void Unlock() + { } + + class AutoLock + { + public: + explicit AutoLock(NoLocking& aHost) + { } + + ~AutoLock() + { } + }; +}; + +template > +class Queue : public LockingPolicy +{ + typedef typename StoragePolicy::StorageType StorageType; + typedef typename LockingPolicy::AutoLock AutoLock; + + StorageType mStorage1; + StorageType mStorage2; + + StorageType* mFront; + StorageType* mBack; + +public: + Queue() + : mFront(&mStorage1), mBack(&mStorage2) + { } + + bool IsEmpty() + { + AutoLock lock(*this); + return StoragePolicy::IsEmpty(*mFront) && + StoragePolicy::IsEmpty(*mBack); + } + + bool Push(const T& aEntry) + { + AutoLock lock(*this); + return StoragePolicy::Push(*mBack, aEntry); + } + + bool Pop(T& aEntry) + { + AutoLock lock(*this); + if (StoragePolicy::IsEmpty(*mFront)) { + StoragePolicy::Compact(*mFront); + StoragePolicy::Reverse(*mBack); + StorageType* tmp = mFront; + mFront = mBack; + mBack = tmp; + } + return StoragePolicy::Pop(*mFront, aEntry); + } + + void Clear() + { + AutoLock lock(*this); + StoragePolicy::Clear(*mFront); + StoragePolicy::Clear(*mBack); + } + + // XXX Do we need this? + void Lock() + { + LockingPolicy::Lock(); + } + + // XXX Do we need this? + void Unlock() + { + LockingPolicy::Unlock(); + } + +private: + // Queue is not copyable. + Queue(const Queue&); + Queue & operator=(const Queue&); +}; + +END_WORKERS_NAMESPACE + +#endif /* mozilla_dom_workers_queue_h__ */ diff --git a/dom/workers/RegisterBindings.cpp b/dom/workers/RegisterBindings.cpp new file mode 100644 index 000000000..b6c1e9cfd --- /dev/null +++ b/dom/workers/RegisterBindings.cpp @@ -0,0 +1,55 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "WorkerPrivate.h" +#include "ChromeWorkerScope.h" +#include "RuntimeService.h" + +#include "jsapi.h" +#include "mozilla/dom/RegisterWorkerBindings.h" +#include "mozilla/dom/RegisterWorkerDebuggerBindings.h" +#include "mozilla/OSFileConstants.h" + +USING_WORKERS_NAMESPACE +using namespace mozilla::dom; + +bool +WorkerPrivate::RegisterBindings(JSContext* aCx, JS::Handle aGlobal) +{ + // Init Web IDL bindings + if (!RegisterWorkerBindings(aCx, aGlobal)) { + return false; + } + + if (IsChromeWorker()) { + if (!DefineChromeWorkerFunctions(aCx, aGlobal) || + !DefineOSFileConstants(aCx, aGlobal)) { + return false; + } + } + + if (!JS_DefineProfilingFunctions(aCx, aGlobal)) { + return false; + } + + return true; +} + +bool +WorkerPrivate::RegisterDebuggerBindings(JSContext* aCx, + JS::Handle aGlobal) +{ + // Init Web IDL bindings + if (!RegisterWorkerDebuggerBindings(aCx, aGlobal)) { + return false; + } + + if (!JS_DefineDebuggerObject(aCx, aGlobal)) { + return false; + } + + return true; +} diff --git a/dom/workers/RuntimeService.cpp b/dom/workers/RuntimeService.cpp new file mode 100644 index 000000000..d1d76e3d1 --- /dev/null +++ b/dom/workers/RuntimeService.cpp @@ -0,0 +1,2966 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "RuntimeService.h" + +#include "nsAutoPtr.h" +#include "nsIChannel.h" +#include "nsIContentSecurityPolicy.h" +#include "nsIDocument.h" +#include "nsIDOMChromeWindow.h" +#include "nsIEffectiveTLDService.h" +#include "nsIObserverService.h" +#include "nsIPrincipal.h" +#include "nsIScriptContext.h" +#include "nsIScriptError.h" +#include "nsIScriptSecurityManager.h" +#include "nsISupportsPriority.h" +#include "nsITimer.h" +#include "nsIURI.h" +#include "nsPIDOMWindow.h" + +#include +#include "BackgroundChild.h" +#include "GeckoProfiler.h" +#include "jsfriendapi.h" +#include "mozilla/ArrayUtils.h" +#include "mozilla/AsyncEventDispatcher.h" +#include "mozilla/Atomics.h" +#include "mozilla/CycleCollectedJSContext.h" +#include "mozilla/Telemetry.h" +#include "mozilla/TimeStamp.h" +#include "mozilla/dom/asmjscache/AsmJSCache.h" +#include "mozilla/dom/AtomList.h" +#include "mozilla/dom/BindingUtils.h" +#include "mozilla/dom/ErrorEventBinding.h" +#include "mozilla/dom/EventTargetBinding.h" +#include "mozilla/dom/MessageChannel.h" +#include "mozilla/dom/MessageEventBinding.h" +#include "mozilla/dom/WorkerBinding.h" +#include "mozilla/dom/ScriptSettings.h" +#include "mozilla/dom/IndexedDatabaseManager.h" +#include "mozilla/ipc/BackgroundChild.h" +#include "mozilla/DebugOnly.h" +#include "mozilla/Preferences.h" +#include "mozilla/dom/Navigator.h" +#include "nsContentUtils.h" +#include "nsCycleCollector.h" +#include "nsDOMJSUtils.h" +#include "nsIIPCBackgroundChildCreateCallback.h" +#include "nsISupportsImpl.h" +#include "nsLayoutStatics.h" +#include "nsNetUtil.h" +#include "nsServiceManagerUtils.h" +#include "nsThreadUtils.h" +#include "nsXPCOM.h" +#include "nsXPCOMPrivate.h" +#include "OSFileConstants.h" +#include "xpcpublic.h" + +#include "Principal.h" +#include "SharedWorker.h" +#include "WorkerDebuggerManager.h" +#include "WorkerPrivate.h" +#include "WorkerRunnable.h" +#include "WorkerScope.h" +#include "WorkerThread.h" +#include "prsystem.h" + +using namespace mozilla; +using namespace mozilla::dom; +using namespace mozilla::ipc; + +USING_WORKERS_NAMESPACE + +using mozilla::MutexAutoLock; +using mozilla::MutexAutoUnlock; +using mozilla::Preferences; + +// The size of the worker runtime heaps in bytes. May be changed via pref. +#define WORKER_DEFAULT_RUNTIME_HEAPSIZE 32 * 1024 * 1024 + +// The size of the generational GC nursery for workers, in bytes. +#define WORKER_DEFAULT_NURSERY_SIZE 1 * 1024 * 1024 + +// The size of the worker JS allocation threshold in MB. May be changed via pref. +#define WORKER_DEFAULT_ALLOCATION_THRESHOLD 30 + +// Half the size of the actual C stack, to be safe. +#define WORKER_CONTEXT_NATIVE_STACK_LIMIT 128 * sizeof(size_t) * 1024 + +// The maximum number of hardware concurrency, overridable via pref. +#define MAX_HARDWARE_CONCURRENCY 8 + +// The maximum number of threads to use for workers, overridable via pref. +#define MAX_WORKERS_PER_DOMAIN 512 + +static_assert(MAX_WORKERS_PER_DOMAIN >= 1, + "We should allow at least one worker per domain."); + +// The default number of seconds that close handlers will be allowed to run for +// content workers. +#define MAX_SCRIPT_RUN_TIME_SEC 10 + +// The number of seconds that idle threads can hang around before being killed. +#define IDLE_THREAD_TIMEOUT_SEC 30 + +// The maximum number of threads that can be idle at one time. +#define MAX_IDLE_THREADS 20 + +#define PREF_WORKERS_PREFIX "dom.workers." +#define PREF_WORKERS_MAX_PER_DOMAIN PREF_WORKERS_PREFIX "maxPerDomain" +#define PREF_WORKERS_MAX_HARDWARE_CONCURRENCY "dom.maxHardwareConcurrency" + +#define PREF_MAX_SCRIPT_RUN_TIME_CONTENT "dom.max_script_run_time" +#define PREF_MAX_SCRIPT_RUN_TIME_CHROME "dom.max_chrome_script_run_time" + +#define GC_REQUEST_OBSERVER_TOPIC "child-gc-request" +#define CC_REQUEST_OBSERVER_TOPIC "child-cc-request" +#define MEMORY_PRESSURE_OBSERVER_TOPIC "memory-pressure" + +#define BROADCAST_ALL_WORKERS(_func, ...) \ + PR_BEGIN_MACRO \ + AssertIsOnMainThread(); \ + \ + AutoTArray workers; \ + { \ + MutexAutoLock lock(mMutex); \ + \ + AddAllTopLevelWorkersToArray(workers); \ + } \ + \ + if (!workers.IsEmpty()) { \ + for (uint32_t index = 0; index < workers.Length(); index++) { \ + workers[index]-> _func (__VA_ARGS__); \ + } \ + } \ + PR_END_MACRO + +// Prefixes for observing preference changes. +#define PREF_JS_OPTIONS_PREFIX "javascript.options." +#define PREF_WORKERS_OPTIONS_PREFIX PREF_WORKERS_PREFIX "options." +#define PREF_MEM_OPTIONS_PREFIX "mem." +#define PREF_GCZEAL "gcZeal" + +namespace { + +const uint32_t kNoIndex = uint32_t(-1); + +uint32_t gMaxWorkersPerDomain = MAX_WORKERS_PER_DOMAIN; +uint32_t gMaxHardwareConcurrency = MAX_HARDWARE_CONCURRENCY; + +// Does not hold an owning reference. +RuntimeService* gRuntimeService = nullptr; + +// Only true during the call to Init. +bool gRuntimeServiceDuringInit = false; + +class LiteralRebindingCString : public nsDependentCString +{ +public: + template + void RebindLiteral(const char (&aStr)[N]) + { + Rebind(aStr, N-1); + } +}; + +template +struct PrefTraits; + +template <> +struct PrefTraits +{ + typedef bool PrefValueType; + + static const PrefValueType kDefaultValue = false; + + static inline PrefValueType + Get(const char* aPref) + { + AssertIsOnMainThread(); + return Preferences::GetBool(aPref); + } + + static inline bool + Exists(const char* aPref) + { + AssertIsOnMainThread(); + return Preferences::GetType(aPref) == nsIPrefBranch::PREF_BOOL; + } +}; + +template <> +struct PrefTraits +{ + typedef int32_t PrefValueType; + + static inline PrefValueType + Get(const char* aPref) + { + AssertIsOnMainThread(); + return Preferences::GetInt(aPref); + } + + static inline bool + Exists(const char* aPref) + { + AssertIsOnMainThread(); + return Preferences::GetType(aPref) == nsIPrefBranch::PREF_INT; + } +}; + +template +T +GetWorkerPref(const nsACString& aPref, + const T aDefault = PrefTraits::kDefaultValue) +{ + AssertIsOnMainThread(); + + typedef PrefTraits PrefHelper; + + T result; + + nsAutoCString prefName; + prefName.AssignLiteral(PREF_WORKERS_OPTIONS_PREFIX); + prefName.Append(aPref); + + if (PrefHelper::Exists(prefName.get())) { + result = PrefHelper::Get(prefName.get()); + } + else { + prefName.AssignLiteral(PREF_JS_OPTIONS_PREFIX); + prefName.Append(aPref); + + if (PrefHelper::Exists(prefName.get())) { + result = PrefHelper::Get(prefName.get()); + } + else { + result = aDefault; + } + } + + return result; +} + +// This fn creates a key for a SharedWorker that contains the name, script +// spec, and the serialized origin attributes: +// "name|scriptSpec^key1=val1&key2=val2&key3=val3" +void +GenerateSharedWorkerKey(const nsACString& aScriptSpec, + const nsACString& aName, + const PrincipalOriginAttributes& aAttrs, + nsCString& aKey) +{ + nsAutoCString suffix; + aAttrs.CreateSuffix(suffix); + + aKey.Truncate(); + aKey.SetCapacity(aName.Length() + aScriptSpec.Length() + suffix.Length() + 2); + aKey.Append(aName); + aKey.Append('|'); + aKey.Append(aScriptSpec); + aKey.Append(suffix); +} + +void +LoadContextOptions(const char* aPrefName, void* /* aClosure */) +{ + AssertIsOnMainThread(); + + RuntimeService* rts = RuntimeService::GetService(); + if (!rts) { + // May be shutting down, just bail. + return; + } + + const nsDependentCString prefName(aPrefName); + + // Several other pref branches will get included here so bail out if there is + // another callback that will handle this change. + if (StringBeginsWith(prefName, + NS_LITERAL_CSTRING(PREF_JS_OPTIONS_PREFIX + PREF_MEM_OPTIONS_PREFIX)) || + StringBeginsWith(prefName, + NS_LITERAL_CSTRING(PREF_WORKERS_OPTIONS_PREFIX + PREF_MEM_OPTIONS_PREFIX))) { + return; + } + +#ifdef JS_GC_ZEAL + if (prefName.EqualsLiteral(PREF_JS_OPTIONS_PREFIX PREF_GCZEAL) || + prefName.EqualsLiteral(PREF_WORKERS_OPTIONS_PREFIX PREF_GCZEAL)) { + return; + } +#endif + + // Context options. + JS::ContextOptions contextOptions; + contextOptions.setAsmJS(GetWorkerPref(NS_LITERAL_CSTRING("asmjs"))) + .setWasm(GetWorkerPref(NS_LITERAL_CSTRING("wasm"))) + .setThrowOnAsmJSValidationFailure(GetWorkerPref( + NS_LITERAL_CSTRING("throw_on_asmjs_validation_failure"))) + .setBaseline(GetWorkerPref(NS_LITERAL_CSTRING("baselinejit"))) + .setIon(GetWorkerPref(NS_LITERAL_CSTRING("ion"))) + .setNativeRegExp(GetWorkerPref(NS_LITERAL_CSTRING("native_regexp"))) + .setAsyncStack(GetWorkerPref(NS_LITERAL_CSTRING("asyncstack"))) + .setWerror(GetWorkerPref(NS_LITERAL_CSTRING("werror"))) + .setExtraWarnings(GetWorkerPref(NS_LITERAL_CSTRING("strict"))); + + RuntimeService::SetDefaultContextOptions(contextOptions); + + if (rts) { + rts->UpdateAllWorkerContextOptions(); + } +} + +#ifdef JS_GC_ZEAL +void +LoadGCZealOptions(const char* /* aPrefName */, void* /* aClosure */) +{ + AssertIsOnMainThread(); + + RuntimeService* rts = RuntimeService::GetService(); + if (!rts) { + // May be shutting down, just bail. + return; + } + + int32_t gczeal = GetWorkerPref(NS_LITERAL_CSTRING(PREF_GCZEAL), -1); + if (gczeal < 0) { + gczeal = 0; + } + + int32_t frequency = + GetWorkerPref(NS_LITERAL_CSTRING("gcZeal.frequency"), -1); + if (frequency < 0) { + frequency = JS_DEFAULT_ZEAL_FREQ; + } + + RuntimeService::SetDefaultGCZeal(uint8_t(gczeal), uint32_t(frequency)); + + if (rts) { + rts->UpdateAllWorkerGCZeal(); + } +} +#endif + +void +UpdateCommonJSGCMemoryOption(RuntimeService* aRuntimeService, + const nsACString& aPrefName, JSGCParamKey aKey) +{ + AssertIsOnMainThread(); + NS_ASSERTION(!aPrefName.IsEmpty(), "Empty pref name!"); + + int32_t prefValue = GetWorkerPref(aPrefName, -1); + uint32_t value = + (prefValue < 0 || prefValue >= 10000) ? 0 : uint32_t(prefValue); + + RuntimeService::SetDefaultJSGCSettings(aKey, value); + + if (aRuntimeService) { + aRuntimeService->UpdateAllWorkerMemoryParameter(aKey, value); + } +} + +void +UpdateOtherJSGCMemoryOption(RuntimeService* aRuntimeService, + JSGCParamKey aKey, uint32_t aValue) +{ + AssertIsOnMainThread(); + + RuntimeService::SetDefaultJSGCSettings(aKey, aValue); + + if (aRuntimeService) { + aRuntimeService->UpdateAllWorkerMemoryParameter(aKey, aValue); + } +} + + +void +LoadJSGCMemoryOptions(const char* aPrefName, void* /* aClosure */) +{ + AssertIsOnMainThread(); + + RuntimeService* rts = RuntimeService::GetService(); + + if (!rts) { + // May be shutting down, just bail. + return; + } + + NS_NAMED_LITERAL_CSTRING(jsPrefix, PREF_JS_OPTIONS_PREFIX); + NS_NAMED_LITERAL_CSTRING(workersPrefix, PREF_WORKERS_OPTIONS_PREFIX); + + const nsDependentCString fullPrefName(aPrefName); + + // Pull out the string that actually distinguishes the parameter we need to + // change. + nsDependentCSubstring memPrefName; + if (StringBeginsWith(fullPrefName, jsPrefix)) { + memPrefName.Rebind(fullPrefName, jsPrefix.Length()); + } + else if (StringBeginsWith(fullPrefName, workersPrefix)) { + memPrefName.Rebind(fullPrefName, workersPrefix.Length()); + } + else { + NS_ERROR("Unknown pref name!"); + return; + } + +#ifdef DEBUG + // During Init() we get called back with a branch string here, so there should + // be no just a "mem." pref here. + if (!rts) { + NS_ASSERTION(memPrefName.EqualsLiteral(PREF_MEM_OPTIONS_PREFIX), "Huh?!"); + } +#endif + + // If we're running in Init() then do this for every pref we care about. + // Otherwise we just want to update the parameter that changed. + for (uint32_t index = !gRuntimeServiceDuringInit + ? JSSettings::kGCSettingsArraySize - 1 : 0; + index < JSSettings::kGCSettingsArraySize; + index++) { + LiteralRebindingCString matchName; + + matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX "max"); + if (memPrefName == matchName || (gRuntimeServiceDuringInit && index == 0)) { + int32_t prefValue = GetWorkerPref(matchName, -1); + uint32_t value = (prefValue <= 0 || prefValue >= 0x1000) ? + uint32_t(-1) : + uint32_t(prefValue) * 1024 * 1024; + UpdateOtherJSGCMemoryOption(rts, JSGC_MAX_BYTES, value); + continue; + } + + matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX "high_water_mark"); + if (memPrefName == matchName || (gRuntimeServiceDuringInit && index == 1)) { + int32_t prefValue = GetWorkerPref(matchName, 128); + UpdateOtherJSGCMemoryOption(rts, JSGC_MAX_MALLOC_BYTES, + uint32_t(prefValue) * 1024 * 1024); + continue; + } + + matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX + "gc_high_frequency_time_limit_ms"); + if (memPrefName == matchName || (gRuntimeServiceDuringInit && index == 2)) { + UpdateCommonJSGCMemoryOption(rts, matchName, + JSGC_HIGH_FREQUENCY_TIME_LIMIT); + continue; + } + + matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX + "gc_low_frequency_heap_growth"); + if (memPrefName == matchName || (gRuntimeServiceDuringInit && index == 3)) { + UpdateCommonJSGCMemoryOption(rts, matchName, + JSGC_LOW_FREQUENCY_HEAP_GROWTH); + continue; + } + + matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX + "gc_high_frequency_heap_growth_min"); + if (memPrefName == matchName || (gRuntimeServiceDuringInit && index == 4)) { + UpdateCommonJSGCMemoryOption(rts, matchName, + JSGC_HIGH_FREQUENCY_HEAP_GROWTH_MIN); + continue; + } + + matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX + "gc_high_frequency_heap_growth_max"); + if (memPrefName == matchName || (gRuntimeServiceDuringInit && index == 5)) { + UpdateCommonJSGCMemoryOption(rts, matchName, + JSGC_HIGH_FREQUENCY_HEAP_GROWTH_MAX); + continue; + } + + matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX + "gc_high_frequency_low_limit_mb"); + if (memPrefName == matchName || (gRuntimeServiceDuringInit && index == 6)) { + UpdateCommonJSGCMemoryOption(rts, matchName, + JSGC_HIGH_FREQUENCY_LOW_LIMIT); + continue; + } + + matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX + "gc_high_frequency_high_limit_mb"); + if (memPrefName == matchName || (gRuntimeServiceDuringInit && index == 7)) { + UpdateCommonJSGCMemoryOption(rts, matchName, + JSGC_HIGH_FREQUENCY_HIGH_LIMIT); + continue; + } + + matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX + "gc_allocation_threshold_mb"); + if (memPrefName == matchName || (gRuntimeServiceDuringInit && index == 8)) { + UpdateCommonJSGCMemoryOption(rts, matchName, JSGC_ALLOCATION_THRESHOLD); + continue; + } + + matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX "gc_incremental_slice_ms"); + if (memPrefName == matchName || (gRuntimeServiceDuringInit && index == 9)) { + int32_t prefValue = GetWorkerPref(matchName, -1); + uint32_t value = + (prefValue <= 0 || prefValue >= 100000) ? 0 : uint32_t(prefValue); + UpdateOtherJSGCMemoryOption(rts, JSGC_SLICE_TIME_BUDGET, value); + continue; + } + + matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX "gc_dynamic_heap_growth"); + if (memPrefName == matchName || + (gRuntimeServiceDuringInit && index == 10)) { + bool prefValue = GetWorkerPref(matchName, false); + UpdateOtherJSGCMemoryOption(rts, JSGC_DYNAMIC_HEAP_GROWTH, + prefValue ? 0 : 1); + continue; + } + + matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX "gc_dynamic_mark_slice"); + if (memPrefName == matchName || + (gRuntimeServiceDuringInit && index == 11)) { + bool prefValue = GetWorkerPref(matchName, false); + UpdateOtherJSGCMemoryOption(rts, JSGC_DYNAMIC_MARK_SLICE, + prefValue ? 0 : 1); + continue; + } + + matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX "gc_min_empty_chunk_count"); + if (memPrefName == matchName || + (gRuntimeServiceDuringInit && index == 12)) { + UpdateCommonJSGCMemoryOption(rts, matchName, JSGC_MIN_EMPTY_CHUNK_COUNT); + continue; + } + + matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX "gc_max_empty_chunk_count"); + if (memPrefName == matchName || + (gRuntimeServiceDuringInit && index == 13)) { + UpdateCommonJSGCMemoryOption(rts, matchName, JSGC_MAX_EMPTY_CHUNK_COUNT); + continue; + } + + matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX "gc_compacting"); + if (memPrefName == matchName || + (gRuntimeServiceDuringInit && index == 14)) { + bool prefValue = GetWorkerPref(matchName, false); + UpdateOtherJSGCMemoryOption(rts, JSGC_COMPACTING_ENABLED, + prefValue ? 0 : 1); + continue; + } + + matchName.RebindLiteral(PREF_MEM_OPTIONS_PREFIX "gc_refresh_frame_slices_enabled"); + if (memPrefName == matchName || + (gRuntimeServiceDuringInit && index == 15)) { + bool prefValue = GetWorkerPref(matchName, false); + UpdateOtherJSGCMemoryOption(rts, JSGC_REFRESH_FRAME_SLICES_ENABLED, + prefValue ? 0 : 1); + continue; + } + +#ifdef DEBUG + nsAutoCString message("Workers don't support the 'mem."); + message.Append(memPrefName); + message.AppendLiteral("' preference!"); + NS_WARNING(message.get()); +#endif + } +} + +bool +InterruptCallback(JSContext* aCx) +{ + WorkerPrivate* worker = GetWorkerPrivateFromContext(aCx); + MOZ_ASSERT(worker); + + // Now is a good time to turn on profiling if it's pending. + profiler_js_operation_callback(); + + return worker->InterruptCallback(aCx); +} + +class LogViolationDetailsRunnable final : public WorkerMainThreadRunnable +{ + nsString mFileName; + uint32_t mLineNum; + +public: + LogViolationDetailsRunnable(WorkerPrivate* aWorker, + const nsString& aFileName, + uint32_t aLineNum) + : WorkerMainThreadRunnable(aWorker, + NS_LITERAL_CSTRING("RuntimeService :: LogViolationDetails")) + , mFileName(aFileName), mLineNum(aLineNum) + { + MOZ_ASSERT(aWorker); + } + + virtual bool MainThreadRun() override; + +private: + ~LogViolationDetailsRunnable() {} +}; + +bool +ContentSecurityPolicyAllows(JSContext* aCx) +{ + WorkerPrivate* worker = GetWorkerPrivateFromContext(aCx); + worker->AssertIsOnWorkerThread(); + + if (worker->GetReportCSPViolations()) { + nsString fileName; + uint32_t lineNum = 0; + + JS::AutoFilename file; + if (JS::DescribeScriptedCaller(aCx, &file, &lineNum) && file.get()) { + fileName = NS_ConvertUTF8toUTF16(file.get()); + } else { + MOZ_ASSERT(!JS_IsExceptionPending(aCx)); + } + + RefPtr runnable = + new LogViolationDetailsRunnable(worker, fileName, lineNum); + + ErrorResult rv; + runnable->Dispatch(rv); + if (NS_WARN_IF(rv.Failed())) { + rv.SuppressException(); + } + } + + return worker->IsEvalAllowed(); +} + +void +CTypesActivityCallback(JSContext* aCx, + js::CTypesActivityType aType) +{ + WorkerPrivate* worker = GetWorkerPrivateFromContext(aCx); + worker->AssertIsOnWorkerThread(); + + switch (aType) { + case js::CTYPES_CALL_BEGIN: + worker->BeginCTypesCall(); + break; + + case js::CTYPES_CALL_END: + worker->EndCTypesCall(); + break; + + case js::CTYPES_CALLBACK_BEGIN: + worker->BeginCTypesCallback(); + break; + + case js::CTYPES_CALLBACK_END: + worker->EndCTypesCallback(); + break; + + default: + MOZ_CRASH("Unknown type flag!"); + } +} + +static nsIPrincipal* +GetPrincipalForAsmJSCacheOp() +{ + WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate(); + if (!workerPrivate) { + return nullptr; + } + + // asmjscache::OpenEntryForX guarnatee to only access the given nsIPrincipal + // from the main thread. + return workerPrivate->GetPrincipalDontAssertMainThread(); +} + +static bool +AsmJSCacheOpenEntryForRead(JS::Handle aGlobal, + const char16_t* aBegin, + const char16_t* aLimit, + size_t* aSize, + const uint8_t** aMemory, + intptr_t *aHandle) +{ + nsIPrincipal* principal = GetPrincipalForAsmJSCacheOp(); + if (!principal) { + return false; + } + + return asmjscache::OpenEntryForRead(principal, aBegin, aLimit, aSize, aMemory, + aHandle); +} + +static JS::AsmJSCacheResult +AsmJSCacheOpenEntryForWrite(JS::Handle aGlobal, + bool aInstalled, + const char16_t* aBegin, + const char16_t* aEnd, + size_t aSize, + uint8_t** aMemory, + intptr_t* aHandle) +{ + nsIPrincipal* principal = GetPrincipalForAsmJSCacheOp(); + if (!principal) { + return JS::AsmJSCache_InternalError; + } + + return asmjscache::OpenEntryForWrite(principal, aInstalled, aBegin, aEnd, + aSize, aMemory, aHandle); +} + +class AsyncTaskWorkerHolder final : public WorkerHolder +{ + bool Notify(Status aStatus) override + { + // The async task must complete in bounded time and there is not (currently) + // a clean way to cancel it. Async tasks do not run arbitrary content. + return true; + } + +public: + WorkerPrivate* Worker() const + { + return mWorkerPrivate; + } +}; + +template +class AsyncTaskBase : public RunnableBase +{ + UniquePtr mHolder; + + // Disable the usual pre/post-dispatch thread assertions since we are + // dispatching from some random JS engine internal thread: + + bool PreDispatch(WorkerPrivate* aWorkerPrivate) override + { + return true; + } + + void PostDispatch(WorkerPrivate* aWorkerPrivate, bool aDispatchResult) override + { } + +protected: + explicit AsyncTaskBase(UniquePtr aHolder) + : RunnableBase(aHolder->Worker(), + WorkerRunnable::WorkerThreadUnchangedBusyCount) + , mHolder(Move(aHolder)) + { + MOZ_ASSERT(mHolder); + } + + ~AsyncTaskBase() + { + MOZ_ASSERT(!mHolder); + } + + void DestroyHolder() + { + MOZ_ASSERT(mHolder); + mHolder.reset(); + } + +public: + UniquePtr StealHolder() + { + return Move(mHolder); + } +}; + +class AsyncTaskRunnable final : public AsyncTaskBase +{ + JS::AsyncTask* mTask; + + ~AsyncTaskRunnable() + { + MOZ_ASSERT(!mTask); + } + + void PostDispatch(WorkerPrivate* aWorkerPrivate, bool aDispatchResult) override + { + // For the benefit of the destructor assert. + if (!aDispatchResult) { + mTask = nullptr; + } + } + +public: + AsyncTaskRunnable(UniquePtr aHolder, + JS::AsyncTask* aTask) + : AsyncTaskBase(Move(aHolder)) + , mTask(aTask) + { + MOZ_ASSERT(mTask); + } + + bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override + { + MOZ_ASSERT(aWorkerPrivate == mWorkerPrivate); + MOZ_ASSERT(aCx == mWorkerPrivate->GetJSContext()); + MOZ_ASSERT(mTask); + + AutoJSAPI jsapi; + jsapi.Init(); + + mTask->finish(mWorkerPrivate->GetJSContext()); + mTask = nullptr; // mTask may delete itself + + DestroyHolder(); + + return true; + } + + nsresult Cancel() override + { + MOZ_ASSERT(mTask); + + AutoJSAPI jsapi; + jsapi.Init(); + + mTask->cancel(mWorkerPrivate->GetJSContext()); + mTask = nullptr; // mTask may delete itself + + DestroyHolder(); + + return WorkerRunnable::Cancel(); + } +}; + +class AsyncTaskControlRunnable final + : public AsyncTaskBase +{ +public: + explicit AsyncTaskControlRunnable(UniquePtr aHolder) + : AsyncTaskBase(Move(aHolder)) + { } + + bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override + { + // See comment in FinishAsyncTaskCallback. + DestroyHolder(); + return true; + } +}; + +static bool +StartAsyncTaskCallback(JSContext* aCx, JS::AsyncTask* aTask) +{ + WorkerPrivate* worker = GetWorkerPrivateFromContext(aCx); + worker->AssertIsOnWorkerThread(); + + auto holder = MakeUnique(); + if (!holder->HoldWorker(worker, Status::Closing)) { + return false; + } + + // Matched by a UniquePtr in FinishAsyncTaskCallback which, by + // interface contract, must be called in the future. + aTask->user = holder.release(); + return true; +} + +static bool +FinishAsyncTaskCallback(JS::AsyncTask* aTask) +{ + // May execute either on the worker thread or a random JS-internal helper + // thread. + + // Match the release() in StartAsyncTaskCallback. + UniquePtr holder( + static_cast(aTask->user)); + + RefPtr r = new AsyncTaskRunnable(Move(holder), aTask); + + // WorkerRunnable::Dispatch() can fail during worker shutdown. In that case, + // report failure back to the JS engine but make sure to release the + // WorkerHolder on the worker thread using a control runnable. Control + // runables aren't suitable for calling AsyncTask::finish() since they are run + // via the interrupt callback which breaks JS run-to-completion. + if (!r->Dispatch()) { + RefPtr cr = + new AsyncTaskControlRunnable(r->StealHolder()); + + MOZ_ALWAYS_TRUE(cr->Dispatch()); + return false; + } + + return true; +} + +class WorkerJSContext; + +class WorkerThreadContextPrivate : private PerThreadAtomCache +{ + friend class WorkerJSContext; + + WorkerPrivate* mWorkerPrivate; + +public: + // This can't return null, but we can't lose the "Get" prefix in the name or + // it will be ambiguous with the WorkerPrivate class name. + WorkerPrivate* + GetWorkerPrivate() const + { + MOZ_ASSERT(!NS_IsMainThread()); + MOZ_ASSERT(mWorkerPrivate); + + return mWorkerPrivate; + } + +private: + explicit + WorkerThreadContextPrivate(WorkerPrivate* aWorkerPrivate) + : mWorkerPrivate(aWorkerPrivate) + { + MOZ_ASSERT(!NS_IsMainThread()); + + // Zero out the base class members. + memset(this, 0, sizeof(PerThreadAtomCache)); + + MOZ_ASSERT(mWorkerPrivate); + } + + ~WorkerThreadContextPrivate() + { + MOZ_ASSERT(!NS_IsMainThread()); + } + + WorkerThreadContextPrivate(const WorkerThreadContextPrivate&) = delete; + + WorkerThreadContextPrivate& + operator=(const WorkerThreadContextPrivate&) = delete; +}; + +bool +InitJSContextForWorker(WorkerPrivate* aWorkerPrivate, JSContext* aWorkerCx) +{ + aWorkerPrivate->AssertIsOnWorkerThread(); + NS_ASSERTION(!aWorkerPrivate->GetJSContext(), "Already has a context!"); + + JSSettings settings; + aWorkerPrivate->CopyJSSettings(settings); + + { + JS::UniqueChars defaultLocale = aWorkerPrivate->AdoptDefaultLocale(); + MOZ_ASSERT(defaultLocale, + "failure of a WorkerPrivate to have a default locale should " + "have made the worker fail to spawn"); + + if (!JS_SetDefaultLocale(aWorkerCx, defaultLocale.get())) { + NS_WARNING("failed to set workerCx's default locale"); + return false; + } + } + + JS::ContextOptionsRef(aWorkerCx) = settings.contextOptions; + + JSSettings::JSGCSettingsArray& gcSettings = settings.gcSettings; + + // This is the real place where we set the max memory for the runtime. + for (uint32_t index = 0; index < ArrayLength(gcSettings); index++) { + const JSSettings::JSGCSetting& setting = gcSettings[index]; + if (setting.IsSet()) { + NS_ASSERTION(setting.value, "Can't handle 0 values!"); + JS_SetGCParameter(aWorkerCx, setting.key, setting.value); + } + } + + JS_SetNativeStackQuota(aWorkerCx, WORKER_CONTEXT_NATIVE_STACK_LIMIT); + + // Security policy: + static const JSSecurityCallbacks securityCallbacks = { + ContentSecurityPolicyAllows + }; + JS_SetSecurityCallbacks(aWorkerCx, &securityCallbacks); + + // Set up the asm.js cache callbacks + static const JS::AsmJSCacheOps asmJSCacheOps = { + AsmJSCacheOpenEntryForRead, + asmjscache::CloseEntryForRead, + AsmJSCacheOpenEntryForWrite, + asmjscache::CloseEntryForWrite + }; + JS::SetAsmJSCacheOps(aWorkerCx, &asmJSCacheOps); + + JS::SetAsyncTaskCallbacks(aWorkerCx, StartAsyncTaskCallback, FinishAsyncTaskCallback); + + if (!JS::InitSelfHostedCode(aWorkerCx)) { + NS_WARNING("Could not init self-hosted code!"); + return false; + } + + JS_AddInterruptCallback(aWorkerCx, InterruptCallback); + + js::SetCTypesActivityCallback(aWorkerCx, CTypesActivityCallback); + +#ifdef JS_GC_ZEAL + JS_SetGCZeal(aWorkerCx, settings.gcZeal, settings.gcZealFrequency); +#endif + + return true; +} + +static bool +PreserveWrapper(JSContext *cx, JSObject *obj) +{ + MOZ_ASSERT(cx); + MOZ_ASSERT(obj); + MOZ_ASSERT(mozilla::dom::IsDOMObject(obj)); + + return mozilla::dom::TryPreserveWrapper(obj); +} + +JSObject* +Wrap(JSContext *cx, JS::HandleObject existing, JS::HandleObject obj) +{ + JSObject* targetGlobal = JS::CurrentGlobalOrNull(cx); + if (!IsDebuggerGlobal(targetGlobal) && !IsDebuggerSandbox(targetGlobal)) { + MOZ_CRASH("There should be no edges from the debuggee to the debugger."); + } + + JSObject* originGlobal = js::GetGlobalForObjectCrossCompartment(obj); + + const js::Wrapper* wrapper = nullptr; + if (IsDebuggerGlobal(originGlobal) || IsDebuggerSandbox(originGlobal)) { + wrapper = &js::CrossCompartmentWrapper::singleton; + } else { + wrapper = &js::OpaqueCrossCompartmentWrapper::singleton; + } + + if (existing) { + js::Wrapper::Renew(cx, existing, obj, wrapper); + } + return js::Wrapper::New(cx, obj, wrapper); +} + +static const JSWrapObjectCallbacks WrapObjectCallbacks = { + Wrap, + nullptr, +}; + +class MOZ_STACK_CLASS WorkerJSContext final : public mozilla::CycleCollectedJSContext +{ +public: + // The heap size passed here doesn't matter, we will change it later in the + // call to JS_SetGCParameter inside InitJSContextForWorker. + explicit WorkerJSContext(WorkerPrivate* aWorkerPrivate) + : mWorkerPrivate(aWorkerPrivate) + { + MOZ_ASSERT(aWorkerPrivate); + } + + ~WorkerJSContext() + { + JSContext* cx = MaybeContext(); + if (!cx) { + return; // Initialize() must have failed + } + + delete static_cast(JS_GetContextPrivate(cx)); + JS_SetContextPrivate(cx, nullptr); + + // The worker global should be unrooted and the shutdown cycle collection + // should break all remaining cycles. The superclass destructor will run + // the GC one final time and finalize any JSObjects that were participating + // in cycles that were broken during CC shutdown. + nsCycleCollector_shutdown(); + + // The CC is shut down, and the superclass destructor will GC, so make sure + // we don't try to CC again. + mWorkerPrivate = nullptr; + } + + nsresult Initialize(JSContext* aParentContext) + { + nsresult rv = + CycleCollectedJSContext::Initialize(aParentContext, + WORKER_DEFAULT_RUNTIME_HEAPSIZE, + WORKER_DEFAULT_NURSERY_SIZE); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + JSContext* cx = Context(); + + JS_SetContextPrivate(cx, new WorkerThreadContextPrivate(mWorkerPrivate)); + + js::SetPreserveWrapperCallback(cx, PreserveWrapper); + JS_InitDestroyPrincipalsCallback(cx, DestroyWorkerPrincipals); + JS_SetWrapObjectCallbacks(cx, &WrapObjectCallbacks); + if (mWorkerPrivate->IsDedicatedWorker()) { + JS_SetFutexCanWait(cx); + } + + return NS_OK; + } + + virtual void + PrepareForForgetSkippable() override + { + } + + virtual void + BeginCycleCollectionCallback() override + { + } + + virtual void + EndCycleCollectionCallback(CycleCollectorResults &aResults) override + { + } + + void + DispatchDeferredDeletion(bool aContinuation, bool aPurge) override + { + MOZ_ASSERT(!aContinuation); + + // Do it immediately, no need for asynchronous behavior here. + nsCycleCollector_doDeferredDeletion(); + } + + virtual void CustomGCCallback(JSGCStatus aStatus) override + { + if (!mWorkerPrivate) { + // We're shutting down, no need to do anything. + return; + } + + mWorkerPrivate->AssertIsOnWorkerThread(); + + if (aStatus == JSGC_END) { + nsCycleCollector_collect(nullptr); + } + } + + virtual void AfterProcessTask(uint32_t aRecursionDepth) override + { + // Only perform the Promise microtask checkpoint on the outermost event + // loop. Don't run it, for example, during sync XHR or importScripts. + if (aRecursionDepth == 2) { + CycleCollectedJSContext::AfterProcessTask(aRecursionDepth); + } else if (aRecursionDepth > 2) { + AutoDisableMicroTaskCheckpoint disableMicroTaskCheckpoint; + CycleCollectedJSContext::AfterProcessTask(aRecursionDepth); + } + } + + virtual void DispatchToMicroTask(already_AddRefed aRunnable) override + { + RefPtr runnable(aRunnable); + + MOZ_ASSERT(!NS_IsMainThread()); + MOZ_ASSERT(runnable); + + std::queue>* microTaskQueue = nullptr; + + JSContext* cx = GetCurrentThreadJSContext(); + NS_ASSERTION(cx, "This should never be null!"); + + JS::Rooted global(cx, JS::CurrentGlobalOrNull(cx)); + NS_ASSERTION(global, "This should never be null!"); + + // On worker threads, if the current global is the worker global, we use the + // main promise micro task queue. Otherwise, the current global must be + // either the debugger global or a debugger sandbox, and we use the debugger + // promise micro task queue instead. + if (IsWorkerGlobal(global)) { + microTaskQueue = &mPromiseMicroTaskQueue; + } else { + MOZ_ASSERT(IsDebuggerGlobal(global) || IsDebuggerSandbox(global)); + + microTaskQueue = &mDebuggerPromiseMicroTaskQueue; + } + + microTaskQueue->push(runnable.forget()); + } + +private: + WorkerPrivate* mWorkerPrivate; +}; + +class WorkerThreadPrimaryRunnable final : public Runnable +{ + WorkerPrivate* mWorkerPrivate; + RefPtr mThread; + JSContext* mParentContext; + + class FinishedRunnable final : public Runnable + { + RefPtr mThread; + + public: + explicit FinishedRunnable(already_AddRefed aThread) + : mThread(aThread) + { + MOZ_ASSERT(mThread); + } + + NS_DECL_ISUPPORTS_INHERITED + + private: + ~FinishedRunnable() + { } + + NS_DECL_NSIRUNNABLE + }; + +public: + WorkerThreadPrimaryRunnable(WorkerPrivate* aWorkerPrivate, + WorkerThread* aThread, + JSContext* aParentContext) + : mWorkerPrivate(aWorkerPrivate), mThread(aThread), mParentContext(aParentContext) + { + MOZ_ASSERT(aWorkerPrivate); + MOZ_ASSERT(aThread); + } + + NS_DECL_ISUPPORTS_INHERITED + +private: + ~WorkerThreadPrimaryRunnable() + { } + + NS_DECL_NSIRUNNABLE +}; + +class WorkerTaskRunnable final : public WorkerRunnable +{ + RefPtr mTask; + +public: + WorkerTaskRunnable(WorkerPrivate* aWorkerPrivate, WorkerTask* aTask) + : WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount), mTask(aTask) + { + MOZ_ASSERT(aTask); + } + +private: + virtual bool + PreDispatch(WorkerPrivate* aWorkerPrivate) override + { + // May be called on any thread! + return true; + } + + virtual void + PostDispatch(WorkerPrivate* aWorkerPrivate, bool aDispatchResult) override + { + // May be called on any thread! + } + + virtual bool + WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override + { + return mTask->RunTask(aCx); + } +}; + +void +PrefLanguagesChanged(const char* /* aPrefName */, void* /* aClosure */) +{ + AssertIsOnMainThread(); + + nsTArray languages; + Navigator::GetAcceptLanguages(languages); + + RuntimeService* runtime = RuntimeService::GetService(); + if (runtime) { + runtime->UpdateAllWorkerLanguages(languages); + } +} + +void +AppNameOverrideChanged(const char* /* aPrefName */, void* /* aClosure */) +{ + AssertIsOnMainThread(); + + const nsAdoptingString& override = + mozilla::Preferences::GetString("general.appname.override"); + + RuntimeService* runtime = RuntimeService::GetService(); + if (runtime) { + runtime->UpdateAppNameOverridePreference(override); + } +} + +void +AppVersionOverrideChanged(const char* /* aPrefName */, void* /* aClosure */) +{ + AssertIsOnMainThread(); + + const nsAdoptingString& override = + mozilla::Preferences::GetString("general.appversion.override"); + + RuntimeService* runtime = RuntimeService::GetService(); + if (runtime) { + runtime->UpdateAppVersionOverridePreference(override); + } +} + +void +PlatformOverrideChanged(const char* /* aPrefName */, void* /* aClosure */) +{ + AssertIsOnMainThread(); + + const nsAdoptingString& override = + mozilla::Preferences::GetString("general.platform.override"); + + RuntimeService* runtime = RuntimeService::GetService(); + if (runtime) { + runtime->UpdatePlatformOverridePreference(override); + } +} + +class BackgroundChildCallback final + : public nsIIPCBackgroundChildCreateCallback +{ +public: + BackgroundChildCallback() + { + AssertIsOnMainThread(); + } + + NS_DECL_ISUPPORTS + +private: + ~BackgroundChildCallback() + { + AssertIsOnMainThread(); + } + + virtual void + ActorCreated(PBackgroundChild* aActor) override + { + AssertIsOnMainThread(); + MOZ_ASSERT(aActor); + } + + virtual void + ActorFailed() override + { + AssertIsOnMainThread(); + MOZ_CRASH("Unable to connect PBackground actor for the main thread!"); + } +}; + +NS_IMPL_ISUPPORTS(BackgroundChildCallback, nsIIPCBackgroundChildCreateCallback) + +} /* anonymous namespace */ + +BEGIN_WORKERS_NAMESPACE + +void +CancelWorkersForWindow(nsPIDOMWindowInner* aWindow) +{ + AssertIsOnMainThread(); + RuntimeService* runtime = RuntimeService::GetService(); + if (runtime) { + runtime->CancelWorkersForWindow(aWindow); + } +} + +void +FreezeWorkersForWindow(nsPIDOMWindowInner* aWindow) +{ + AssertIsOnMainThread(); + RuntimeService* runtime = RuntimeService::GetService(); + if (runtime) { + runtime->FreezeWorkersForWindow(aWindow); + } +} + +void +ThawWorkersForWindow(nsPIDOMWindowInner* aWindow) +{ + AssertIsOnMainThread(); + RuntimeService* runtime = RuntimeService::GetService(); + if (runtime) { + runtime->ThawWorkersForWindow(aWindow); + } +} + +void +SuspendWorkersForWindow(nsPIDOMWindowInner* aWindow) +{ + AssertIsOnMainThread(); + RuntimeService* runtime = RuntimeService::GetService(); + if (runtime) { + runtime->SuspendWorkersForWindow(aWindow); + } +} + +void +ResumeWorkersForWindow(nsPIDOMWindowInner* aWindow) +{ + AssertIsOnMainThread(); + RuntimeService* runtime = RuntimeService::GetService(); + if (runtime) { + runtime->ResumeWorkersForWindow(aWindow); + } +} + +WorkerCrossThreadDispatcher::WorkerCrossThreadDispatcher( + WorkerPrivate* aWorkerPrivate) +: mMutex("WorkerCrossThreadDispatcher::mMutex"), + mWorkerPrivate(aWorkerPrivate) +{ + MOZ_ASSERT(aWorkerPrivate); +} + +bool +WorkerCrossThreadDispatcher::PostTask(WorkerTask* aTask) +{ + MOZ_ASSERT(aTask); + + MutexAutoLock lock(mMutex); + + if (!mWorkerPrivate) { + NS_WARNING("Posted a task to a WorkerCrossThreadDispatcher that is no " + "longer accepting tasks!"); + return false; + } + + RefPtr runnable = + new WorkerTaskRunnable(mWorkerPrivate, aTask); + return runnable->Dispatch(); +} + +WorkerPrivate* +GetWorkerPrivateFromContext(JSContext* aCx) +{ + MOZ_ASSERT(!NS_IsMainThread()); + MOZ_ASSERT(aCx); + + void* cxPrivate = JS_GetContextPrivate(aCx); + if (!cxPrivate) { + return nullptr; + } + + return + static_cast(cxPrivate)->GetWorkerPrivate(); +} + +WorkerPrivate* +GetCurrentThreadWorkerPrivate() +{ + MOZ_ASSERT(!NS_IsMainThread()); + + CycleCollectedJSContext* ccjscx = CycleCollectedJSContext::Get(); + if (!ccjscx) { + return nullptr; + } + + JSContext* cx = ccjscx->Context(); + MOZ_ASSERT(cx); + + void* cxPrivate = JS_GetContextPrivate(cx); + if (!cxPrivate) { + // This can happen if the nsCycleCollector_shutdown() in ~WorkerJSContext() + // triggers any calls to GetCurrentThreadWorkerPrivate(). At this stage + // CycleCollectedJSContext::Get() will still return a context, but + // the context private has already been cleared. + return nullptr; + } + + return + static_cast(cxPrivate)->GetWorkerPrivate(); +} + +bool +IsCurrentThreadRunningChromeWorker() +{ + return GetCurrentThreadWorkerPrivate()->UsesSystemPrincipal(); +} + +JSContext* +GetCurrentThreadJSContext() +{ + WorkerPrivate* wp = GetCurrentThreadWorkerPrivate(); + if (!wp) { + return nullptr; + } + return wp->GetJSContext(); +} + +JSObject* +GetCurrentThreadWorkerGlobal() +{ + WorkerPrivate* wp = GetCurrentThreadWorkerPrivate(); + if (!wp) { + return nullptr; + } + WorkerGlobalScope* scope = wp->GlobalScope(); + if (!scope) { + return nullptr; + } + return scope->GetGlobalJSObject(); +} + +END_WORKERS_NAMESPACE + +struct RuntimeService::IdleThreadInfo +{ + RefPtr mThread; + mozilla::TimeStamp mExpirationTime; +}; + +// This is only touched on the main thread. Initialized in Init() below. +JSSettings RuntimeService::sDefaultJSSettings; +bool RuntimeService::sDefaultPreferences[WORKERPREF_COUNT] = { false }; + +RuntimeService::RuntimeService() +: mMutex("RuntimeService::mMutex"), mObserved(false), + mShuttingDown(false), mNavigatorPropertiesLoaded(false) +{ + AssertIsOnMainThread(); + NS_ASSERTION(!gRuntimeService, "More than one service!"); +} + +RuntimeService::~RuntimeService() +{ + AssertIsOnMainThread(); + + // gRuntimeService can be null if Init() fails. + NS_ASSERTION(!gRuntimeService || gRuntimeService == this, + "More than one service!"); + + gRuntimeService = nullptr; +} + +// static +RuntimeService* +RuntimeService::GetOrCreateService() +{ + AssertIsOnMainThread(); + + if (!gRuntimeService) { + // The observer service now owns us until shutdown. + gRuntimeService = new RuntimeService(); + if (NS_FAILED(gRuntimeService->Init())) { + NS_WARNING("Failed to initialize!"); + gRuntimeService->Cleanup(); + gRuntimeService = nullptr; + return nullptr; + } + } + + return gRuntimeService; +} + +// static +RuntimeService* +RuntimeService::GetService() +{ + return gRuntimeService; +} + +bool +RuntimeService::RegisterWorker(WorkerPrivate* aWorkerPrivate) +{ + aWorkerPrivate->AssertIsOnParentThread(); + + WorkerPrivate* parent = aWorkerPrivate->GetParent(); + if (!parent) { + AssertIsOnMainThread(); + + if (mShuttingDown) { + return false; + } + } + + const bool isServiceWorker = aWorkerPrivate->IsServiceWorker(); + const bool isSharedWorker = aWorkerPrivate->IsSharedWorker(); + const bool isDedicatedWorker = aWorkerPrivate->IsDedicatedWorker(); + if (isServiceWorker) { + AssertIsOnMainThread(); + Telemetry::Accumulate(Telemetry::SERVICE_WORKER_SPAWN_ATTEMPTS, 1); + } + + nsCString sharedWorkerScriptSpec; + if (isSharedWorker) { + AssertIsOnMainThread(); + + nsCOMPtr scriptURI = aWorkerPrivate->GetResolvedScriptURI(); + NS_ASSERTION(scriptURI, "Null script URI!"); + + nsresult rv = scriptURI->GetSpec(sharedWorkerScriptSpec); + if (NS_FAILED(rv)) { + NS_WARNING("GetSpec failed?!"); + return false; + } + + NS_ASSERTION(!sharedWorkerScriptSpec.IsEmpty(), "Empty spec!"); + } + + bool exemptFromPerDomainMax = false; + if (isServiceWorker) { + AssertIsOnMainThread(); + exemptFromPerDomainMax = Preferences::GetBool("dom.serviceWorkers.exemptFromPerDomainMax", + false); + } + + const nsCString& domain = aWorkerPrivate->Domain(); + + WorkerDomainInfo* domainInfo; + bool queued = false; + { + MutexAutoLock lock(mMutex); + + if (!mDomainMap.Get(domain, &domainInfo)) { + NS_ASSERTION(!parent, "Shouldn't have a parent here!"); + + domainInfo = new WorkerDomainInfo(); + domainInfo->mDomain = domain; + mDomainMap.Put(domain, domainInfo); + } + + queued = gMaxWorkersPerDomain && + domainInfo->ActiveWorkerCount() >= gMaxWorkersPerDomain && + !domain.IsEmpty() && + !exemptFromPerDomainMax; + + if (queued) { + domainInfo->mQueuedWorkers.AppendElement(aWorkerPrivate); + + // Worker spawn gets queued due to hitting max workers per domain + // limit so let's log a warning. + WorkerPrivate::ReportErrorToConsole("HittingMaxWorkersPerDomain2"); + + if (isServiceWorker) { + Telemetry::Accumulate(Telemetry::SERVICE_WORKER_SPAWN_GETS_QUEUED, 1); + } else if (isSharedWorker) { + Telemetry::Accumulate(Telemetry::SHARED_WORKER_SPAWN_GETS_QUEUED, 1); + } else if (isDedicatedWorker) { + Telemetry::Accumulate(Telemetry::DEDICATED_WORKER_SPAWN_GETS_QUEUED, 1); + } + } + else if (parent) { + domainInfo->mChildWorkerCount++; + } + else if (isServiceWorker) { + domainInfo->mActiveServiceWorkers.AppendElement(aWorkerPrivate); + } + else { + domainInfo->mActiveWorkers.AppendElement(aWorkerPrivate); + } + + if (isSharedWorker) { + const nsCString& sharedWorkerName = aWorkerPrivate->WorkerName(); + nsAutoCString key; + GenerateSharedWorkerKey(sharedWorkerScriptSpec, sharedWorkerName, + aWorkerPrivate->GetOriginAttributes(), key); + MOZ_ASSERT(!domainInfo->mSharedWorkerInfos.Get(key)); + + SharedWorkerInfo* sharedWorkerInfo = + new SharedWorkerInfo(aWorkerPrivate, sharedWorkerScriptSpec, + sharedWorkerName); + domainInfo->mSharedWorkerInfos.Put(key, sharedWorkerInfo); + } + } + + // From here on out we must call UnregisterWorker if something fails! + if (parent) { + if (!parent->AddChildWorker(aWorkerPrivate)) { + UnregisterWorker(aWorkerPrivate); + return false; + } + } + else { + if (!mNavigatorPropertiesLoaded) { + Navigator::AppName(mNavigatorProperties.mAppName, + false /* aUsePrefOverriddenValue */); + if (NS_FAILED(Navigator::GetAppVersion(mNavigatorProperties.mAppVersion, + false /* aUsePrefOverriddenValue */)) || + NS_FAILED(Navigator::GetPlatform(mNavigatorProperties.mPlatform, + false /* aUsePrefOverriddenValue */))) { + UnregisterWorker(aWorkerPrivate); + return false; + } + + // The navigator overridden properties should have already been read. + + Navigator::GetAcceptLanguages(mNavigatorProperties.mLanguages); + mNavigatorPropertiesLoaded = true; + } + + nsPIDOMWindowInner* window = aWorkerPrivate->GetWindow(); + + if (!isServiceWorker) { + // Service workers are excluded since their lifetime is separate from + // that of dom windows. + nsTArray* windowArray; + if (!mWindowMap.Get(window, &windowArray)) { + windowArray = new nsTArray(1); + mWindowMap.Put(window, windowArray); + } + + if (!windowArray->Contains(aWorkerPrivate)) { + windowArray->AppendElement(aWorkerPrivate); + } else { + MOZ_ASSERT(aWorkerPrivate->IsSharedWorker()); + } + } + } + + if (!queued && !ScheduleWorker(aWorkerPrivate)) { + return false; + } + + if (isServiceWorker) { + AssertIsOnMainThread(); + Telemetry::Accumulate(Telemetry::SERVICE_WORKER_WAS_SPAWNED, 1); + } + return true; +} + +void +RuntimeService::RemoveSharedWorker(WorkerDomainInfo* aDomainInfo, + WorkerPrivate* aWorkerPrivate) +{ + for (auto iter = aDomainInfo->mSharedWorkerInfos.Iter(); + !iter.Done(); + iter.Next()) { + SharedWorkerInfo* data = iter.UserData(); + if (data->mWorkerPrivate == aWorkerPrivate) { +#ifdef DEBUG + nsAutoCString key; + GenerateSharedWorkerKey(data->mScriptSpec, data->mName, + aWorkerPrivate->GetOriginAttributes(), key); + MOZ_ASSERT(iter.Key() == key); +#endif + iter.Remove(); + break; + } + } +} + +void +RuntimeService::UnregisterWorker(WorkerPrivate* aWorkerPrivate) +{ + aWorkerPrivate->AssertIsOnParentThread(); + + WorkerPrivate* parent = aWorkerPrivate->GetParent(); + if (!parent) { + AssertIsOnMainThread(); + } + + const nsCString& domain = aWorkerPrivate->Domain(); + + WorkerPrivate* queuedWorker = nullptr; + { + MutexAutoLock lock(mMutex); + + WorkerDomainInfo* domainInfo; + if (!mDomainMap.Get(domain, &domainInfo)) { + NS_ERROR("Don't have an entry for this domain!"); + } + + // Remove old worker from everywhere. + uint32_t index = domainInfo->mQueuedWorkers.IndexOf(aWorkerPrivate); + if (index != kNoIndex) { + // Was queued, remove from the list. + domainInfo->mQueuedWorkers.RemoveElementAt(index); + } + else if (parent) { + MOZ_ASSERT(domainInfo->mChildWorkerCount, "Must be non-zero!"); + domainInfo->mChildWorkerCount--; + } + else if (aWorkerPrivate->IsServiceWorker()) { + MOZ_ASSERT(domainInfo->mActiveServiceWorkers.Contains(aWorkerPrivate), + "Don't know about this worker!"); + domainInfo->mActiveServiceWorkers.RemoveElement(aWorkerPrivate); + } + else { + MOZ_ASSERT(domainInfo->mActiveWorkers.Contains(aWorkerPrivate), + "Don't know about this worker!"); + domainInfo->mActiveWorkers.RemoveElement(aWorkerPrivate); + } + + if (aWorkerPrivate->IsSharedWorker()) { + RemoveSharedWorker(domainInfo, aWorkerPrivate); + } + + // See if there's a queued worker we can schedule. + if (domainInfo->ActiveWorkerCount() < gMaxWorkersPerDomain && + !domainInfo->mQueuedWorkers.IsEmpty()) { + queuedWorker = domainInfo->mQueuedWorkers[0]; + domainInfo->mQueuedWorkers.RemoveElementAt(0); + + if (queuedWorker->GetParent()) { + domainInfo->mChildWorkerCount++; + } + else if (queuedWorker->IsServiceWorker()) { + domainInfo->mActiveServiceWorkers.AppendElement(queuedWorker); + } + else { + domainInfo->mActiveWorkers.AppendElement(queuedWorker); + } + } + + if (domainInfo->HasNoWorkers()) { + MOZ_ASSERT(domainInfo->mQueuedWorkers.IsEmpty()); + mDomainMap.Remove(domain); + } + } + + if (aWorkerPrivate->IsServiceWorker()) { + AssertIsOnMainThread(); + Telemetry::AccumulateTimeDelta(Telemetry::SERVICE_WORKER_LIFE_TIME, + aWorkerPrivate->CreationTimeStamp()); + } + + if (aWorkerPrivate->IsSharedWorker() || + aWorkerPrivate->IsServiceWorker()) { + AssertIsOnMainThread(); + aWorkerPrivate->CloseAllSharedWorkers(); + } + + if (parent) { + parent->RemoveChildWorker(aWorkerPrivate); + } + else if (aWorkerPrivate->IsSharedWorker()) { + AssertIsOnMainThread(); + + for (auto iter = mWindowMap.Iter(); !iter.Done(); iter.Next()) { + nsAutoPtr>& workers = iter.Data(); + MOZ_ASSERT(workers.get()); + + if (workers->RemoveElement(aWorkerPrivate)) { + MOZ_ASSERT(!workers->Contains(aWorkerPrivate), + "Added worker more than once!"); + + if (workers->IsEmpty()) { + iter.Remove(); + } + } + } + } + else if (aWorkerPrivate->IsDedicatedWorker()) { + // May be null. + nsPIDOMWindowInner* window = aWorkerPrivate->GetWindow(); + + nsTArray* windowArray; + MOZ_ALWAYS_TRUE(mWindowMap.Get(window, &windowArray)); + + MOZ_ALWAYS_TRUE(windowArray->RemoveElement(aWorkerPrivate)); + + if (windowArray->IsEmpty()) { + mWindowMap.Remove(window); + } + } + + if (queuedWorker && !ScheduleWorker(queuedWorker)) { + UnregisterWorker(queuedWorker); + } +} + +bool +RuntimeService::ScheduleWorker(WorkerPrivate* aWorkerPrivate) +{ + if (!aWorkerPrivate->Start()) { + // This is ok, means that we didn't need to make a thread for this worker. + return true; + } + + RefPtr thread; + { + MutexAutoLock lock(mMutex); + if (!mIdleThreadArray.IsEmpty()) { + uint32_t index = mIdleThreadArray.Length() - 1; + mIdleThreadArray[index].mThread.swap(thread); + mIdleThreadArray.RemoveElementAt(index); + } + } + + const WorkerThreadFriendKey friendKey; + + if (!thread) { + thread = WorkerThread::Create(friendKey); + if (!thread) { + UnregisterWorker(aWorkerPrivate); + return false; + } + } + + int32_t priority = aWorkerPrivate->IsChromeWorker() ? + nsISupportsPriority::PRIORITY_NORMAL : + nsISupportsPriority::PRIORITY_LOW; + + if (NS_FAILED(thread->SetPriority(priority))) { + NS_WARNING("Could not set the thread's priority!"); + } + + JSContext* cx = CycleCollectedJSContext::Get()->Context(); + nsCOMPtr runnable = + new WorkerThreadPrimaryRunnable(aWorkerPrivate, thread, + JS_GetParentContext(cx)); + if (NS_FAILED(thread->DispatchPrimaryRunnable(friendKey, runnable.forget()))) { + UnregisterWorker(aWorkerPrivate); + return false; + } + + return true; +} + +// static +void +RuntimeService::ShutdownIdleThreads(nsITimer* aTimer, void* /* aClosure */) +{ + AssertIsOnMainThread(); + + RuntimeService* runtime = RuntimeService::GetService(); + NS_ASSERTION(runtime, "This should never be null!"); + + NS_ASSERTION(aTimer == runtime->mIdleThreadTimer, "Wrong timer!"); + + // Cheat a little and grab all threads that expire within one second of now. + TimeStamp now = TimeStamp::NowLoRes() + TimeDuration::FromSeconds(1); + + TimeStamp nextExpiration; + + AutoTArray, 20> expiredThreads; + { + MutexAutoLock lock(runtime->mMutex); + + for (uint32_t index = 0; index < runtime->mIdleThreadArray.Length(); + index++) { + IdleThreadInfo& info = runtime->mIdleThreadArray[index]; + if (info.mExpirationTime > now) { + nextExpiration = info.mExpirationTime; + break; + } + + RefPtr* thread = expiredThreads.AppendElement(); + thread->swap(info.mThread); + } + + if (!expiredThreads.IsEmpty()) { + runtime->mIdleThreadArray.RemoveElementsAt(0, expiredThreads.Length()); + } + } + + if (!nextExpiration.IsNull()) { + TimeDuration delta = nextExpiration - TimeStamp::NowLoRes(); + uint32_t delay(delta > TimeDuration(0) ? delta.ToMilliseconds() : 0); + + // Reschedule the timer. + MOZ_ALWAYS_SUCCEEDS( + aTimer->InitWithFuncCallback(ShutdownIdleThreads, + nullptr, + delay, + nsITimer::TYPE_ONE_SHOT)); + } + + for (uint32_t index = 0; index < expiredThreads.Length(); index++) { + if (NS_FAILED(expiredThreads[index]->Shutdown())) { + NS_WARNING("Failed to shutdown thread!"); + } + } +} + +nsresult +RuntimeService::Init() +{ + AssertIsOnMainThread(); + + nsLayoutStatics::AddRef(); + + // Make sure PBackground actors are connected as soon as possible for the main + // thread in case workers clone remote blobs here. + if (!BackgroundChild::GetForCurrentThread()) { + RefPtr callback = new BackgroundChildCallback(); + if (!BackgroundChild::GetOrCreateForCurrentThread(callback)) { + MOZ_CRASH("Unable to connect PBackground actor for the main thread!"); + } + } + + // Initialize JSSettings. + if (!sDefaultJSSettings.gcSettings[0].IsSet()) { + sDefaultJSSettings.contextOptions = JS::ContextOptions(); + sDefaultJSSettings.chrome.maxScriptRuntime = -1; + sDefaultJSSettings.chrome.compartmentOptions.behaviors().setVersion(JSVERSION_LATEST); + sDefaultJSSettings.content.maxScriptRuntime = MAX_SCRIPT_RUN_TIME_SEC; +#ifdef JS_GC_ZEAL + sDefaultJSSettings.gcZealFrequency = JS_DEFAULT_ZEAL_FREQ; + sDefaultJSSettings.gcZeal = 0; +#endif + SetDefaultJSGCSettings(JSGC_MAX_BYTES, WORKER_DEFAULT_RUNTIME_HEAPSIZE); + SetDefaultJSGCSettings(JSGC_ALLOCATION_THRESHOLD, + WORKER_DEFAULT_ALLOCATION_THRESHOLD); + } + + mIdleThreadTimer = do_CreateInstance(NS_TIMER_CONTRACTID); + NS_ENSURE_STATE(mIdleThreadTimer); + + nsCOMPtr obs = services::GetObserverService(); + NS_ENSURE_TRUE(obs, NS_ERROR_FAILURE); + + nsresult rv = + obs->AddObserver(this, NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID, false); + NS_ENSURE_SUCCESS(rv, rv); + + rv = obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false); + NS_ENSURE_SUCCESS(rv, rv); + + mObserved = true; + + if (NS_FAILED(obs->AddObserver(this, GC_REQUEST_OBSERVER_TOPIC, false))) { + NS_WARNING("Failed to register for GC request notifications!"); + } + + if (NS_FAILED(obs->AddObserver(this, CC_REQUEST_OBSERVER_TOPIC, false))) { + NS_WARNING("Failed to register for CC request notifications!"); + } + + if (NS_FAILED(obs->AddObserver(this, MEMORY_PRESSURE_OBSERVER_TOPIC, + false))) { + NS_WARNING("Failed to register for memory pressure notifications!"); + } + + if (NS_FAILED(obs->AddObserver(this, NS_IOSERVICE_OFFLINE_STATUS_TOPIC, false))) { + NS_WARNING("Failed to register for offline notification event!"); + } + + MOZ_ASSERT(!gRuntimeServiceDuringInit, "This should be false!"); + gRuntimeServiceDuringInit = true; + + if (NS_FAILED(Preferences::RegisterCallback( + LoadJSGCMemoryOptions, + PREF_JS_OPTIONS_PREFIX PREF_MEM_OPTIONS_PREFIX, + nullptr)) || + NS_FAILED(Preferences::RegisterCallbackAndCall( + LoadJSGCMemoryOptions, + PREF_WORKERS_OPTIONS_PREFIX PREF_MEM_OPTIONS_PREFIX, + nullptr)) || +#ifdef JS_GC_ZEAL + NS_FAILED(Preferences::RegisterCallback( + LoadGCZealOptions, + PREF_JS_OPTIONS_PREFIX PREF_GCZEAL, + nullptr)) || +#endif + +#define WORKER_SIMPLE_PREF(name, getter, NAME) \ + NS_FAILED(Preferences::RegisterCallbackAndCall( \ + WorkerPrefChanged, \ + name, \ + reinterpret_cast(WORKERPREF_##NAME))) || +#define WORKER_PREF(name, callback) \ + NS_FAILED(Preferences::RegisterCallbackAndCall( \ + callback, \ + name, \ + nullptr)) || +#include "WorkerPrefs.h" +#undef WORKER_SIMPLE_PREF +#undef WORKER_PREF + + NS_FAILED(Preferences::RegisterCallbackAndCall( + LoadContextOptions, + PREF_WORKERS_OPTIONS_PREFIX, + nullptr)) || + NS_FAILED(Preferences::RegisterCallback(LoadContextOptions, + PREF_JS_OPTIONS_PREFIX, + nullptr))) { + NS_WARNING("Failed to register pref callbacks!"); + } + + MOZ_ASSERT(gRuntimeServiceDuringInit, "Should be true!"); + gRuntimeServiceDuringInit = false; + + // We assume atomic 32bit reads/writes. If this assumption doesn't hold on + // some wacky platform then the worst that could happen is that the close + // handler will run for a slightly different amount of time. + if (NS_FAILED(Preferences::AddIntVarCache( + &sDefaultJSSettings.content.maxScriptRuntime, + PREF_MAX_SCRIPT_RUN_TIME_CONTENT, + MAX_SCRIPT_RUN_TIME_SEC)) || + NS_FAILED(Preferences::AddIntVarCache( + &sDefaultJSSettings.chrome.maxScriptRuntime, + PREF_MAX_SCRIPT_RUN_TIME_CHROME, -1))) { + NS_WARNING("Failed to register timeout cache!"); + } + + int32_t maxPerDomain = Preferences::GetInt(PREF_WORKERS_MAX_PER_DOMAIN, + MAX_WORKERS_PER_DOMAIN); + gMaxWorkersPerDomain = std::max(0, maxPerDomain); + + int32_t maxHardwareConcurrency = + Preferences::GetInt(PREF_WORKERS_MAX_HARDWARE_CONCURRENCY, + MAX_HARDWARE_CONCURRENCY); + gMaxHardwareConcurrency = std::max(0, maxHardwareConcurrency); + + rv = InitOSFileConstants(); + if (NS_FAILED(rv)) { + return rv; + } + + if (NS_WARN_IF(!IndexedDatabaseManager::GetOrCreate())) { + return NS_ERROR_UNEXPECTED; + } + + return NS_OK; +} + +void +RuntimeService::Shutdown() +{ + AssertIsOnMainThread(); + + MOZ_ASSERT(!mShuttingDown); + // That's it, no more workers. + mShuttingDown = true; + + nsCOMPtr obs = services::GetObserverService(); + NS_WARNING_ASSERTION(obs, "Failed to get observer service?!"); + + // Tell anyone that cares that they're about to lose worker support. + if (obs && NS_FAILED(obs->NotifyObservers(nullptr, WORKERS_SHUTDOWN_TOPIC, + nullptr))) { + NS_WARNING("NotifyObservers failed!"); + } + + { + MutexAutoLock lock(mMutex); + + AutoTArray workers; + AddAllTopLevelWorkersToArray(workers); + + if (!workers.IsEmpty()) { + // Cancel all top-level workers. + { + MutexAutoUnlock unlock(mMutex); + + for (uint32_t index = 0; index < workers.Length(); index++) { + if (!workers[index]->Kill()) { + NS_WARNING("Failed to cancel worker!"); + } + } + } + } + } +} + +// This spins the event loop until all workers are finished and their threads +// have been joined. +void +RuntimeService::Cleanup() +{ + AssertIsOnMainThread(); + + nsCOMPtr obs = services::GetObserverService(); + NS_WARNING_ASSERTION(obs, "Failed to get observer service?!"); + + if (mIdleThreadTimer) { + if (NS_FAILED(mIdleThreadTimer->Cancel())) { + NS_WARNING("Failed to cancel idle timer!"); + } + mIdleThreadTimer = nullptr; + } + + { + MutexAutoLock lock(mMutex); + + AutoTArray workers; + AddAllTopLevelWorkersToArray(workers); + + if (!workers.IsEmpty()) { + nsIThread* currentThread = NS_GetCurrentThread(); + NS_ASSERTION(currentThread, "This should never be null!"); + + // Shut down any idle threads. + if (!mIdleThreadArray.IsEmpty()) { + AutoTArray, 20> idleThreads; + + uint32_t idleThreadCount = mIdleThreadArray.Length(); + idleThreads.SetLength(idleThreadCount); + + for (uint32_t index = 0; index < idleThreadCount; index++) { + NS_ASSERTION(mIdleThreadArray[index].mThread, "Null thread!"); + idleThreads[index].swap(mIdleThreadArray[index].mThread); + } + + mIdleThreadArray.Clear(); + + MutexAutoUnlock unlock(mMutex); + + for (uint32_t index = 0; index < idleThreadCount; index++) { + if (NS_FAILED(idleThreads[index]->Shutdown())) { + NS_WARNING("Failed to shutdown thread!"); + } + } + } + + // And make sure all their final messages have run and all their threads + // have joined. + while (mDomainMap.Count()) { + MutexAutoUnlock unlock(mMutex); + + if (!NS_ProcessNextEvent(currentThread)) { + NS_WARNING("Something bad happened!"); + break; + } + } + } + } + + NS_ASSERTION(!mWindowMap.Count(), "All windows should have been released!"); + + if (mObserved) { + if (NS_FAILED(Preferences::UnregisterCallback(LoadContextOptions, + PREF_JS_OPTIONS_PREFIX, + nullptr)) || + NS_FAILED(Preferences::UnregisterCallback(LoadContextOptions, + PREF_WORKERS_OPTIONS_PREFIX, + nullptr)) || + +#define WORKER_SIMPLE_PREF(name, getter, NAME) \ + NS_FAILED(Preferences::UnregisterCallback( \ + WorkerPrefChanged, \ + name, \ + reinterpret_cast(WORKERPREF_##NAME))) || +#define WORKER_PREF(name, callback) \ + NS_FAILED(Preferences::UnregisterCallback( \ + callback, \ + name, \ + nullptr)) || +#include "WorkerPrefs.h" +#undef WORKER_SIMPLE_PREF +#undef WORKER_PREF + +#ifdef JS_GC_ZEAL + NS_FAILED(Preferences::UnregisterCallback( + LoadGCZealOptions, + PREF_JS_OPTIONS_PREFIX PREF_GCZEAL, + nullptr)) || +#endif + NS_FAILED(Preferences::UnregisterCallback( + LoadJSGCMemoryOptions, + PREF_JS_OPTIONS_PREFIX PREF_MEM_OPTIONS_PREFIX, + nullptr)) || + NS_FAILED(Preferences::UnregisterCallback( + LoadJSGCMemoryOptions, + PREF_WORKERS_OPTIONS_PREFIX PREF_MEM_OPTIONS_PREFIX, + nullptr))) { + NS_WARNING("Failed to unregister pref callbacks!"); + } + + if (obs) { + if (NS_FAILED(obs->RemoveObserver(this, GC_REQUEST_OBSERVER_TOPIC))) { + NS_WARNING("Failed to unregister for GC request notifications!"); + } + + if (NS_FAILED(obs->RemoveObserver(this, CC_REQUEST_OBSERVER_TOPIC))) { + NS_WARNING("Failed to unregister for CC request notifications!"); + } + + if (NS_FAILED(obs->RemoveObserver(this, + MEMORY_PRESSURE_OBSERVER_TOPIC))) { + NS_WARNING("Failed to unregister for memory pressure notifications!"); + } + + if (NS_FAILED(obs->RemoveObserver(this, + NS_IOSERVICE_OFFLINE_STATUS_TOPIC))) { + NS_WARNING("Failed to unregister for offline notification event!"); + } + obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID); + obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID); + mObserved = false; + } + } + + CleanupOSFileConstants(); + nsLayoutStatics::Release(); +} + +void +RuntimeService::AddAllTopLevelWorkersToArray(nsTArray& aWorkers) +{ + for (auto iter = mDomainMap.Iter(); !iter.Done(); iter.Next()) { + + WorkerDomainInfo* aData = iter.UserData(); + +#ifdef DEBUG + for (uint32_t index = 0; index < aData->mActiveWorkers.Length(); index++) { + MOZ_ASSERT(!aData->mActiveWorkers[index]->GetParent(), + "Shouldn't have a parent in this list!"); + } + for (uint32_t index = 0; index < aData->mActiveServiceWorkers.Length(); index++) { + MOZ_ASSERT(!aData->mActiveServiceWorkers[index]->GetParent(), + "Shouldn't have a parent in this list!"); + } +#endif + + aWorkers.AppendElements(aData->mActiveWorkers); + aWorkers.AppendElements(aData->mActiveServiceWorkers); + + // These might not be top-level workers... + for (uint32_t index = 0; index < aData->mQueuedWorkers.Length(); index++) { + WorkerPrivate* worker = aData->mQueuedWorkers[index]; + if (!worker->GetParent()) { + aWorkers.AppendElement(worker); + } + } + } +} + +void +RuntimeService::GetWorkersForWindow(nsPIDOMWindowInner* aWindow, + nsTArray& aWorkers) +{ + AssertIsOnMainThread(); + + nsTArray* workers; + if (mWindowMap.Get(aWindow, &workers)) { + NS_ASSERTION(!workers->IsEmpty(), "Should have been removed!"); + aWorkers.AppendElements(*workers); + } + else { + NS_ASSERTION(aWorkers.IsEmpty(), "Should be empty!"); + } +} + +void +RuntimeService::CancelWorkersForWindow(nsPIDOMWindowInner* aWindow) +{ + AssertIsOnMainThread(); + + nsTArray workers; + GetWorkersForWindow(aWindow, workers); + + if (!workers.IsEmpty()) { + for (uint32_t index = 0; index < workers.Length(); index++) { + WorkerPrivate*& worker = workers[index]; + + if (worker->IsSharedWorker()) { + worker->CloseSharedWorkersForWindow(aWindow); + } else { + worker->Cancel(); + } + } + } +} + +void +RuntimeService::FreezeWorkersForWindow(nsPIDOMWindowInner* aWindow) +{ + AssertIsOnMainThread(); + MOZ_ASSERT(aWindow); + + nsTArray workers; + GetWorkersForWindow(aWindow, workers); + + for (uint32_t index = 0; index < workers.Length(); index++) { + workers[index]->Freeze(aWindow); + } +} + +void +RuntimeService::ThawWorkersForWindow(nsPIDOMWindowInner* aWindow) +{ + AssertIsOnMainThread(); + MOZ_ASSERT(aWindow); + + nsTArray workers; + GetWorkersForWindow(aWindow, workers); + + for (uint32_t index = 0; index < workers.Length(); index++) { + workers[index]->Thaw(aWindow); + } +} + +void +RuntimeService::SuspendWorkersForWindow(nsPIDOMWindowInner* aWindow) +{ + AssertIsOnMainThread(); + MOZ_ASSERT(aWindow); + + nsTArray workers; + GetWorkersForWindow(aWindow, workers); + + for (uint32_t index = 0; index < workers.Length(); index++) { + workers[index]->ParentWindowPaused(); + } +} + +void +RuntimeService::ResumeWorkersForWindow(nsPIDOMWindowInner* aWindow) +{ + AssertIsOnMainThread(); + MOZ_ASSERT(aWindow); + + nsTArray workers; + GetWorkersForWindow(aWindow, workers); + + for (uint32_t index = 0; index < workers.Length(); index++) { + workers[index]->ParentWindowResumed(); + } +} + +nsresult +RuntimeService::CreateSharedWorker(const GlobalObject& aGlobal, + const nsAString& aScriptURL, + const nsACString& aName, + SharedWorker** aSharedWorker) +{ + AssertIsOnMainThread(); + + nsCOMPtr window = do_QueryInterface(aGlobal.GetAsSupports()); + MOZ_ASSERT(window); + + JSContext* cx = aGlobal.Context(); + + WorkerLoadInfo loadInfo; + nsresult rv = WorkerPrivate::GetLoadInfo(cx, window, nullptr, aScriptURL, + false, + WorkerPrivate::OverrideLoadGroup, + WorkerTypeShared, &loadInfo); + NS_ENSURE_SUCCESS(rv, rv); + + return CreateSharedWorkerFromLoadInfo(cx, &loadInfo, aScriptURL, aName, + aSharedWorker); +} + +nsresult +RuntimeService::CreateSharedWorkerFromLoadInfo(JSContext* aCx, + WorkerLoadInfo* aLoadInfo, + const nsAString& aScriptURL, + const nsACString& aName, + SharedWorker** aSharedWorker) +{ + AssertIsOnMainThread(); + MOZ_ASSERT(aLoadInfo); + MOZ_ASSERT(aLoadInfo->mResolvedScriptURI); + + RefPtr workerPrivate; + { + MutexAutoLock lock(mMutex); + + WorkerDomainInfo* domainInfo; + SharedWorkerInfo* sharedWorkerInfo; + + nsCString scriptSpec; + nsresult rv = aLoadInfo->mResolvedScriptURI->GetSpec(scriptSpec); + NS_ENSURE_SUCCESS(rv, rv); + + MOZ_ASSERT(aLoadInfo->mPrincipal); + nsAutoCString key; + GenerateSharedWorkerKey(scriptSpec, aName, + BasePrincipal::Cast(aLoadInfo->mPrincipal)->OriginAttributesRef(), key); + + if (mDomainMap.Get(aLoadInfo->mDomain, &domainInfo) && + domainInfo->mSharedWorkerInfos.Get(key, &sharedWorkerInfo)) { + workerPrivate = sharedWorkerInfo->mWorkerPrivate; + } + } + + // Keep a reference to the window before spawning the worker. If the worker is + // a Shared/Service worker and the worker script loads and executes before + // the SharedWorker object itself is created before then WorkerScriptLoaded() + // will reset the loadInfo's window. + nsCOMPtr window = aLoadInfo->mWindow; + + // shouldAttachToWorkerPrivate tracks whether our SharedWorker should actually + // get attached to the WorkerPrivate we're using. It will become false if the + // WorkerPrivate already exists and its secure context state doesn't match + // what we want for the new SharedWorker. + bool shouldAttachToWorkerPrivate = true; + bool created = false; + ErrorResult rv; + if (!workerPrivate) { + workerPrivate = + WorkerPrivate::Constructor(aCx, aScriptURL, false, + WorkerTypeShared, aName, aLoadInfo, rv); + NS_ENSURE_TRUE(workerPrivate, rv.StealNSResult()); + + created = true; + } else { + // Check whether the secure context state matches. The current compartment + // of aCx is the compartment of the SharedWorker constructor that was + // invoked, which is the compartment of the document that will be hooked up + // to the worker, so that's what we want to check. + shouldAttachToWorkerPrivate = + workerPrivate->IsSecureContext() == + JS_GetIsSecureContext(js::GetContextCompartment(aCx)); + + // If we're attaching to an existing SharedWorker private, then we + // must update the overriden load group to account for our document's + // load group. + if (shouldAttachToWorkerPrivate) { + workerPrivate->UpdateOverridenLoadGroup(aLoadInfo->mLoadGroup); + } + } + + // We don't actually care about this MessageChannel, but we use it to 'steal' + // its 2 connected ports. + nsCOMPtr global = do_QueryInterface(window); + RefPtr channel = MessageChannel::Constructor(global, rv); + if (NS_WARN_IF(rv.Failed())) { + return rv.StealNSResult(); + } + + RefPtr sharedWorker = new SharedWorker(window, workerPrivate, + channel->Port1()); + + if (!shouldAttachToWorkerPrivate) { + // We're done here. Just queue up our error event and return our + // dead-on-arrival SharedWorker. + RefPtr errorEvent = + new AsyncEventDispatcher(sharedWorker, NS_LITERAL_STRING("error"), false); + errorEvent->PostDOMEvent(); + sharedWorker.forget(aSharedWorker); + return NS_OK; + } + + if (!workerPrivate->RegisterSharedWorker(sharedWorker, channel->Port2())) { + NS_WARNING("Worker is unreachable, this shouldn't happen!"); + sharedWorker->Close(); + return NS_ERROR_FAILURE; + } + + // This is normally handled in RegisterWorker, but that wasn't called if the + // worker already existed. + if (!created) { + nsTArray* windowArray; + if (!mWindowMap.Get(window, &windowArray)) { + windowArray = new nsTArray(1); + mWindowMap.Put(window, windowArray); + } + + if (!windowArray->Contains(workerPrivate)) { + windowArray->AppendElement(workerPrivate); + } + } + + sharedWorker.forget(aSharedWorker); + return NS_OK; +} + +void +RuntimeService::ForgetSharedWorker(WorkerPrivate* aWorkerPrivate) +{ + AssertIsOnMainThread(); + MOZ_ASSERT(aWorkerPrivate); + MOZ_ASSERT(aWorkerPrivate->IsSharedWorker()); + + MutexAutoLock lock(mMutex); + + WorkerDomainInfo* domainInfo; + if (mDomainMap.Get(aWorkerPrivate->Domain(), &domainInfo)) { + RemoveSharedWorker(domainInfo, aWorkerPrivate); + } +} + +void +RuntimeService::NoteIdleThread(WorkerThread* aThread) +{ + AssertIsOnMainThread(); + MOZ_ASSERT(aThread); + + bool shutdownThread = mShuttingDown; + bool scheduleTimer = false; + + if (!shutdownThread) { + static TimeDuration timeout = + TimeDuration::FromSeconds(IDLE_THREAD_TIMEOUT_SEC); + + TimeStamp expirationTime = TimeStamp::NowLoRes() + timeout; + + MutexAutoLock lock(mMutex); + + uint32_t previousIdleCount = mIdleThreadArray.Length(); + + if (previousIdleCount < MAX_IDLE_THREADS) { + IdleThreadInfo* info = mIdleThreadArray.AppendElement(); + info->mThread = aThread; + info->mExpirationTime = expirationTime; + + scheduleTimer = previousIdleCount == 0; + } else { + shutdownThread = true; + } + } + + MOZ_ASSERT_IF(shutdownThread, !scheduleTimer); + MOZ_ASSERT_IF(scheduleTimer, !shutdownThread); + + // Too many idle threads, just shut this one down. + if (shutdownThread) { + MOZ_ALWAYS_SUCCEEDS(aThread->Shutdown()); + } else if (scheduleTimer) { + MOZ_ALWAYS_SUCCEEDS( + mIdleThreadTimer->InitWithFuncCallback(ShutdownIdleThreads, + nullptr, + IDLE_THREAD_TIMEOUT_SEC * 1000, + nsITimer::TYPE_ONE_SHOT)); + } +} + +void +RuntimeService::UpdateAllWorkerContextOptions() +{ + BROADCAST_ALL_WORKERS(UpdateContextOptions, sDefaultJSSettings.contextOptions); +} + +void +RuntimeService::UpdateAppNameOverridePreference(const nsAString& aValue) +{ + AssertIsOnMainThread(); + mNavigatorProperties.mAppNameOverridden = aValue; +} + +void +RuntimeService::UpdateAppVersionOverridePreference(const nsAString& aValue) +{ + AssertIsOnMainThread(); + mNavigatorProperties.mAppVersionOverridden = aValue; +} + +void +RuntimeService::UpdatePlatformOverridePreference(const nsAString& aValue) +{ + AssertIsOnMainThread(); + mNavigatorProperties.mPlatformOverridden = aValue; +} + +void +RuntimeService::UpdateAllWorkerPreference(WorkerPreference aPref, bool aValue) +{ + BROADCAST_ALL_WORKERS(UpdatePreference, aPref, aValue); +} + +void +RuntimeService::UpdateAllWorkerLanguages(const nsTArray& aLanguages) +{ + MOZ_ASSERT(NS_IsMainThread()); + + mNavigatorProperties.mLanguages = aLanguages; + BROADCAST_ALL_WORKERS(UpdateLanguages, aLanguages); +} + +void +RuntimeService::UpdateAllWorkerMemoryParameter(JSGCParamKey aKey, + uint32_t aValue) +{ + BROADCAST_ALL_WORKERS(UpdateJSWorkerMemoryParameter, aKey, aValue); +} + +#ifdef JS_GC_ZEAL +void +RuntimeService::UpdateAllWorkerGCZeal() +{ + BROADCAST_ALL_WORKERS(UpdateGCZeal, sDefaultJSSettings.gcZeal, + sDefaultJSSettings.gcZealFrequency); +} +#endif + +void +RuntimeService::GarbageCollectAllWorkers(bool aShrinking) +{ + BROADCAST_ALL_WORKERS(GarbageCollect, aShrinking); +} + +void +RuntimeService::CycleCollectAllWorkers() +{ + BROADCAST_ALL_WORKERS(CycleCollect, /* dummy = */ false); +} + +void +RuntimeService::SendOfflineStatusChangeEventToAllWorkers(bool aIsOffline) +{ + BROADCAST_ALL_WORKERS(OfflineStatusChangeEvent, aIsOffline); +} + +void +RuntimeService::MemoryPressureAllWorkers() +{ + BROADCAST_ALL_WORKERS(MemoryPressure, /* dummy = */ false); +} + +uint32_t +RuntimeService::ClampedHardwareConcurrency() const +{ + // This needs to be atomic, because multiple workers, and even mainthread, + // could race to initialize it at once. + static Atomic clampedHardwareConcurrency; + + // No need to loop here: if compareExchange fails, that just means that some + // other worker has initialized numberOfProcessors, so we're good to go. + if (!clampedHardwareConcurrency) { + int32_t numberOfProcessors = PR_GetNumberOfProcessors(); + if (numberOfProcessors <= 0) { + numberOfProcessors = 1; // Must be one there somewhere + } + uint32_t clampedValue = std::min(uint32_t(numberOfProcessors), + gMaxHardwareConcurrency); + clampedHardwareConcurrency.compareExchange(0, clampedValue); + } + + return clampedHardwareConcurrency; +} + +// nsISupports +NS_IMPL_ISUPPORTS(RuntimeService, nsIObserver) + +// nsIObserver +NS_IMETHODIMP +RuntimeService::Observe(nsISupports* aSubject, const char* aTopic, + const char16_t* aData) +{ + AssertIsOnMainThread(); + + if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) { + Shutdown(); + return NS_OK; + } + if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID)) { + Cleanup(); + return NS_OK; + } + if (!strcmp(aTopic, GC_REQUEST_OBSERVER_TOPIC)) { + GarbageCollectAllWorkers(/* shrinking = */ false); + return NS_OK; + } + if (!strcmp(aTopic, CC_REQUEST_OBSERVER_TOPIC)) { + CycleCollectAllWorkers(); + return NS_OK; + } + if (!strcmp(aTopic, MEMORY_PRESSURE_OBSERVER_TOPIC)) { + GarbageCollectAllWorkers(/* shrinking = */ true); + CycleCollectAllWorkers(); + MemoryPressureAllWorkers(); + return NS_OK; + } + if (!strcmp(aTopic, NS_IOSERVICE_OFFLINE_STATUS_TOPIC)) { + SendOfflineStatusChangeEventToAllWorkers(NS_IsOffline()); + return NS_OK; + } + + NS_NOTREACHED("Unknown observer topic!"); + return NS_OK; +} + +/* static */ void +RuntimeService::WorkerPrefChanged(const char* aPrefName, void* aClosure) +{ + AssertIsOnMainThread(); + + const WorkerPreference key = + static_cast(reinterpret_cast(aClosure)); + + switch (key) { +#define WORKER_SIMPLE_PREF(name, getter, NAME) case WORKERPREF_##NAME: +#define WORKER_PREF(name, callback) +#include "WorkerPrefs.h" +#undef WORKER_SIMPLE_PREF +#undef WORKER_PREF + sDefaultPreferences[key] = Preferences::GetBool(aPrefName, false); + break; + + default: + MOZ_ASSERT_UNREACHABLE("Invalid pref key"); + break; + } + + RuntimeService* rts = RuntimeService::GetService(); + if (rts) { + rts->UpdateAllWorkerPreference(key, sDefaultPreferences[key]); + } +} + +void +RuntimeService::JSVersionChanged(const char* /* aPrefName */, void* /* aClosure */) +{ + AssertIsOnMainThread(); + + bool useLatest = Preferences::GetBool("dom.workers.latestJSVersion", false); + JS::CompartmentOptions& options = sDefaultJSSettings.content.compartmentOptions; + options.behaviors().setVersion(useLatest ? JSVERSION_LATEST : JSVERSION_DEFAULT); +} + +bool +LogViolationDetailsRunnable::MainThreadRun() +{ + AssertIsOnMainThread(); + + nsIContentSecurityPolicy* csp = mWorkerPrivate->GetCSP(); + if (csp) { + NS_NAMED_LITERAL_STRING(scriptSample, + "Call to eval() or related function blocked by CSP."); + if (mWorkerPrivate->GetReportCSPViolations()) { + csp->LogViolationDetails(nsIContentSecurityPolicy::VIOLATION_TYPE_EVAL, + mFileName, scriptSample, mLineNum, + EmptyString(), EmptyString()); + } + } + + return true; +} + +NS_IMPL_ISUPPORTS_INHERITED0(WorkerThreadPrimaryRunnable, Runnable) + +NS_IMETHODIMP +WorkerThreadPrimaryRunnable::Run() +{ + using mozilla::ipc::BackgroundChild; + + char stackBaseGuess; + + PR_SetCurrentThreadName("DOM Worker"); + + nsAutoCString threadName; + threadName.AssignLiteral("DOM Worker '"); + threadName.Append(NS_LossyConvertUTF16toASCII(mWorkerPrivate->ScriptURL())); + threadName.Append('\''); + + profiler_register_thread(threadName.get(), &stackBaseGuess); + + // Note: SynchronouslyCreateForCurrentThread() must be called prior to + // mWorkerPrivate->SetThread() in order to avoid accidentally consuming + // worker messages here. + if (NS_WARN_IF(!BackgroundChild::SynchronouslyCreateForCurrentThread())) { + // XXX need to fire an error at parent. + // Failed in creating BackgroundChild: probably in shutdown. Continue to run + // without BackgroundChild created. + } + + class MOZ_STACK_CLASS SetThreadHelper final + { + // Raw pointer: this class is on the stack. + WorkerPrivate* mWorkerPrivate; + + public: + SetThreadHelper(WorkerPrivate* aWorkerPrivate, WorkerThread* aThread) + : mWorkerPrivate(aWorkerPrivate) + { + MOZ_ASSERT(aWorkerPrivate); + MOZ_ASSERT(aThread); + + mWorkerPrivate->SetThread(aThread); + } + + ~SetThreadHelper() + { + if (mWorkerPrivate) { + mWorkerPrivate->SetThread(nullptr); + } + } + + void Nullify() + { + MOZ_ASSERT(mWorkerPrivate); + mWorkerPrivate->SetThread(nullptr); + mWorkerPrivate = nullptr; + } + }; + + SetThreadHelper threadHelper(mWorkerPrivate, mThread); + + mWorkerPrivate->AssertIsOnWorkerThread(); + + { + nsCycleCollector_startup(); + + WorkerJSContext context(mWorkerPrivate); + nsresult rv = context.Initialize(mParentContext); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + JSContext* cx = context.Context(); + + if (!InitJSContextForWorker(mWorkerPrivate, cx)) { + // XXX need to fire an error at parent. + NS_ERROR("Failed to create context!"); + return NS_ERROR_FAILURE; + } + + { +#ifdef MOZ_ENABLE_PROFILER_SPS + PseudoStack* stack = mozilla_get_pseudo_stack(); + if (stack) { + stack->sampleContext(cx); + } +#endif + + { + JSAutoRequest ar(cx); + + mWorkerPrivate->DoRunLoop(cx); + // The AutoJSAPI in DoRunLoop should have reported any exceptions left + // on cx. Note that we still need the JSAutoRequest above because + // AutoJSAPI on workers does NOT enter a request! + MOZ_ASSERT(!JS_IsExceptionPending(cx)); + } + + BackgroundChild::CloseForCurrentThread(); + +#ifdef MOZ_ENABLE_PROFILER_SPS + if (stack) { + stack->sampleContext(nullptr); + } +#endif + } + + // There may still be runnables on the debugger event queue that hold a + // strong reference to the debugger global scope. These runnables are not + // visible to the cycle collector, so we need to make sure to clear the + // debugger event queue before we try to destroy the context. If we don't, + // the garbage collector will crash. + mWorkerPrivate->ClearDebuggerEventQueue(); + + // Perform a full GC. This will collect the main worker global and CC, + // which should break all cycles that touch JS. + JS_GC(cx); + + // Before shutting down the cycle collector we need to do one more pass + // through the event loop to clean up any C++ objects that need deferred + // cleanup. + mWorkerPrivate->ClearMainEventQueue(WorkerPrivate::WorkerRan); + + // Now WorkerJSContext goes out of scope and its destructor will shut + // down the cycle collector. This breaks any remaining cycles and collects + // any remaining C++ objects. + } + + threadHelper.Nullify(); + + mWorkerPrivate->ScheduleDeletion(WorkerPrivate::WorkerRan); + + // It is no longer safe to touch mWorkerPrivate. + mWorkerPrivate = nullptr; + + // Now recycle this thread. + nsCOMPtr mainThread = do_GetMainThread(); + MOZ_ASSERT(mainThread); + + RefPtr finishedRunnable = + new FinishedRunnable(mThread.forget()); + MOZ_ALWAYS_SUCCEEDS(mainThread->Dispatch(finishedRunnable, + NS_DISPATCH_NORMAL)); + + profiler_unregister_thread(); + return NS_OK; +} + +NS_IMPL_ISUPPORTS_INHERITED0(WorkerThreadPrimaryRunnable::FinishedRunnable, + Runnable) + +NS_IMETHODIMP +WorkerThreadPrimaryRunnable::FinishedRunnable::Run() +{ + AssertIsOnMainThread(); + + RefPtr thread; + mThread.swap(thread); + + RuntimeService* rts = RuntimeService::GetService(); + if (rts) { + rts->NoteIdleThread(thread); + } + else if (thread->ShutdownRequired()) { + MOZ_ALWAYS_SUCCEEDS(thread->Shutdown()); + } + + return NS_OK; +} diff --git a/dom/workers/RuntimeService.h b/dom/workers/RuntimeService.h new file mode 100644 index 000000000..2e5cc1dad --- /dev/null +++ b/dom/workers/RuntimeService.h @@ -0,0 +1,287 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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/. */ + +#ifndef mozilla_dom_workers_runtimeservice_h__ +#define mozilla_dom_workers_runtimeservice_h__ + +#include "Workers.h" + +#include "nsIObserver.h" + +#include "mozilla/dom/BindingDeclarations.h" +#include "nsClassHashtable.h" +#include "nsHashKeys.h" +#include "nsTArray.h" + +class nsITimer; +class nsPIDOMWindowInner; + +BEGIN_WORKERS_NAMESPACE + +class SharedWorker; +class WorkerThread; + +class RuntimeService final : public nsIObserver +{ + struct SharedWorkerInfo + { + WorkerPrivate* mWorkerPrivate; + nsCString mScriptSpec; + nsCString mName; + + SharedWorkerInfo(WorkerPrivate* aWorkerPrivate, + const nsACString& aScriptSpec, + const nsACString& aName) + : mWorkerPrivate(aWorkerPrivate), mScriptSpec(aScriptSpec), mName(aName) + { } + }; + + struct WorkerDomainInfo + { + nsCString mDomain; + nsTArray mActiveWorkers; + nsTArray mActiveServiceWorkers; + nsTArray mQueuedWorkers; + nsClassHashtable mSharedWorkerInfos; + uint32_t mChildWorkerCount; + + WorkerDomainInfo() + : mActiveWorkers(1), mChildWorkerCount(0) + { } + + uint32_t + ActiveWorkerCount() const + { + return mActiveWorkers.Length() + + mChildWorkerCount; + } + + uint32_t + ActiveServiceWorkerCount() const + { + return mActiveServiceWorkers.Length(); + } + + bool + HasNoWorkers() const + { + return ActiveWorkerCount() == 0 && + ActiveServiceWorkerCount() == 0; + } + }; + + struct IdleThreadInfo; + + mozilla::Mutex mMutex; + + // Protected by mMutex. + nsClassHashtable mDomainMap; + + // Protected by mMutex. + nsTArray mIdleThreadArray; + + // *Not* protected by mMutex. + nsClassHashtable, + nsTArray > mWindowMap; + + // Only used on the main thread. + nsCOMPtr mIdleThreadTimer; + + static JSSettings sDefaultJSSettings; + static bool sDefaultPreferences[WORKERPREF_COUNT]; + +public: + struct NavigatorProperties + { + nsString mAppName; + nsString mAppNameOverridden; + nsString mAppVersion; + nsString mAppVersionOverridden; + nsString mPlatform; + nsString mPlatformOverridden; + nsTArray mLanguages; + }; + +private: + NavigatorProperties mNavigatorProperties; + + // True when the observer service holds a reference to this object. + bool mObserved; + bool mShuttingDown; + bool mNavigatorPropertiesLoaded; + +public: + NS_DECL_ISUPPORTS + NS_DECL_NSIOBSERVER + + static RuntimeService* + GetOrCreateService(); + + static RuntimeService* + GetService(); + + bool + RegisterWorker(WorkerPrivate* aWorkerPrivate); + + void + UnregisterWorker(WorkerPrivate* aWorkerPrivate); + + void + RemoveSharedWorker(WorkerDomainInfo* aDomainInfo, + WorkerPrivate* aWorkerPrivate); + + void + CancelWorkersForWindow(nsPIDOMWindowInner* aWindow); + + void + FreezeWorkersForWindow(nsPIDOMWindowInner* aWindow); + + void + ThawWorkersForWindow(nsPIDOMWindowInner* aWindow); + + void + SuspendWorkersForWindow(nsPIDOMWindowInner* aWindow); + + void + ResumeWorkersForWindow(nsPIDOMWindowInner* aWindow); + + nsresult + CreateSharedWorker(const GlobalObject& aGlobal, + const nsAString& aScriptURL, + const nsACString& aName, + SharedWorker** aSharedWorker); + + void + ForgetSharedWorker(WorkerPrivate* aWorkerPrivate); + + const NavigatorProperties& + GetNavigatorProperties() const + { + return mNavigatorProperties; + } + + void + NoteIdleThread(WorkerThread* aThread); + + static void + GetDefaultJSSettings(JSSettings& aSettings) + { + AssertIsOnMainThread(); + aSettings = sDefaultJSSettings; + } + + static void + GetDefaultPreferences(bool aPreferences[WORKERPREF_COUNT]) + { + AssertIsOnMainThread(); + memcpy(aPreferences, sDefaultPreferences, WORKERPREF_COUNT * sizeof(bool)); + } + + static void + SetDefaultContextOptions(const JS::ContextOptions& aContextOptions) + { + AssertIsOnMainThread(); + sDefaultJSSettings.contextOptions = aContextOptions; + } + + void + UpdateAppNameOverridePreference(const nsAString& aValue); + + void + UpdateAppVersionOverridePreference(const nsAString& aValue); + + void + UpdatePlatformOverridePreference(const nsAString& aValue); + + void + UpdateAllWorkerContextOptions(); + + void + UpdateAllWorkerLanguages(const nsTArray& aLanguages); + + void + UpdateAllWorkerPreference(WorkerPreference aPref, bool aValue); + + static void + SetDefaultJSGCSettings(JSGCParamKey aKey, uint32_t aValue) + { + AssertIsOnMainThread(); + sDefaultJSSettings.ApplyGCSetting(aKey, aValue); + } + + void + UpdateAllWorkerMemoryParameter(JSGCParamKey aKey, uint32_t aValue); + +#ifdef JS_GC_ZEAL + static void + SetDefaultGCZeal(uint8_t aGCZeal, uint32_t aFrequency) + { + AssertIsOnMainThread(); + sDefaultJSSettings.gcZeal = aGCZeal; + sDefaultJSSettings.gcZealFrequency = aFrequency; + } + + void + UpdateAllWorkerGCZeal(); +#endif + + void + GarbageCollectAllWorkers(bool aShrinking); + + void + CycleCollectAllWorkers(); + + void + SendOfflineStatusChangeEventToAllWorkers(bool aIsOffline); + + void + MemoryPressureAllWorkers(); + + uint32_t ClampedHardwareConcurrency() const; + +private: + RuntimeService(); + ~RuntimeService(); + + nsresult + Init(); + + void + Shutdown(); + + void + Cleanup(); + + void + AddAllTopLevelWorkersToArray(nsTArray& aWorkers); + + void + GetWorkersForWindow(nsPIDOMWindowInner* aWindow, + nsTArray& aWorkers); + + bool + ScheduleWorker(WorkerPrivate* aWorkerPrivate); + + static void + ShutdownIdleThreads(nsITimer* aTimer, void* aClosure); + + static void + WorkerPrefChanged(const char* aPrefName, void* aClosure); + + static void + JSVersionChanged(const char* aPrefName, void* aClosure); + + nsresult + CreateSharedWorkerFromLoadInfo(JSContext* aCx, + WorkerLoadInfo* aLoadInfo, + const nsAString& aScriptURL, + const nsACString& aName, + SharedWorker** aSharedWorker); +}; + +END_WORKERS_NAMESPACE + +#endif /* mozilla_dom_workers_runtimeservice_h__ */ diff --git a/dom/workers/ScriptLoader.cpp b/dom/workers/ScriptLoader.cpp new file mode 100644 index 000000000..46545e737 --- /dev/null +++ b/dom/workers/ScriptLoader.cpp @@ -0,0 +1,2290 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "ScriptLoader.h" + +#include "nsIChannel.h" +#include "nsIContentPolicy.h" +#include "nsIContentSecurityPolicy.h" +#include "nsIDocShell.h" +#include "nsIDOMDocument.h" +#include "nsIHttpChannel.h" +#include "nsIHttpChannelInternal.h" +#include "nsIInputStreamPump.h" +#include "nsIIOService.h" +#include "nsIProtocolHandler.h" +#include "nsIScriptError.h" +#include "nsIScriptSecurityManager.h" +#include "nsIStreamLoader.h" +#include "nsIStreamListenerTee.h" +#include "nsIThreadRetargetableRequest.h" +#include "nsIURI.h" + +#include "jsapi.h" +#include "jsfriendapi.h" +#include "nsError.h" +#include "nsContentPolicyUtils.h" +#include "nsContentUtils.h" +#include "nsDocShellCID.h" +#include "nsISupportsPrimitives.h" +#include "nsNetUtil.h" +#include "nsIPipe.h" +#include "nsIOutputStream.h" +#include "nsPrintfCString.h" +#include "nsScriptLoader.h" +#include "nsString.h" +#include "nsStreamUtils.h" +#include "nsTArray.h" +#include "nsThreadUtils.h" +#include "nsXPCOM.h" +#include "xpcpublic.h" + +#include "mozilla/Assertions.h" +#include "mozilla/LoadContext.h" +#include "mozilla/Maybe.h" +#include "mozilla/ipc/BackgroundUtils.h" +#include "mozilla/dom/CacheBinding.h" +#include "mozilla/dom/cache/CacheTypes.h" +#include "mozilla/dom/cache/Cache.h" +#include "mozilla/dom/cache/CacheStorage.h" +#include "mozilla/dom/ChannelInfo.h" +#include "mozilla/dom/Exceptions.h" +#include "mozilla/dom/InternalResponse.h" +#include "mozilla/dom/nsCSPService.h" +#include "mozilla/dom/nsCSPUtils.h" +#include "mozilla/dom/Promise.h" +#include "mozilla/dom/PromiseNativeHandler.h" +#include "mozilla/dom/Response.h" +#include "mozilla/dom/ScriptSettings.h" +#include "mozilla/dom/SRILogHelper.h" +#include "mozilla/UniquePtr.h" +#include "Principal.h" +#include "WorkerHolder.h" +#include "WorkerPrivate.h" +#include "WorkerRunnable.h" +#include "WorkerScope.h" + +#define MAX_CONCURRENT_SCRIPTS 1000 + +USING_WORKERS_NAMESPACE + +using namespace mozilla; +using namespace mozilla::dom; +using mozilla::dom::cache::Cache; +using mozilla::dom::cache::CacheStorage; +using mozilla::ipc::PrincipalInfo; + +namespace { + +nsIURI* +GetBaseURI(bool aIsMainScript, WorkerPrivate* aWorkerPrivate) +{ + MOZ_ASSERT(aWorkerPrivate); + nsIURI* baseURI; + WorkerPrivate* parentWorker = aWorkerPrivate->GetParent(); + if (aIsMainScript) { + if (parentWorker) { + baseURI = parentWorker->GetBaseURI(); + NS_ASSERTION(baseURI, "Should have been set already!"); + } + else { + // May be null. + baseURI = aWorkerPrivate->GetBaseURI(); + } + } + else { + baseURI = aWorkerPrivate->GetBaseURI(); + NS_ASSERTION(baseURI, "Should have been set already!"); + } + + return baseURI; +} + +nsresult +ChannelFromScriptURL(nsIPrincipal* principal, + nsIURI* baseURI, + nsIDocument* parentDoc, + nsILoadGroup* loadGroup, + nsIIOService* ios, + nsIScriptSecurityManager* secMan, + const nsAString& aScriptURL, + bool aIsMainScript, + WorkerScriptType aWorkerScriptType, + nsContentPolicyType aContentPolicyType, + nsLoadFlags aLoadFlags, + bool aDefaultURIEncoding, + nsIChannel** aChannel) +{ + AssertIsOnMainThread(); + + nsresult rv; + nsCOMPtr uri; + + if (aDefaultURIEncoding) { + rv = NS_NewURI(getter_AddRefs(uri), aScriptURL, nullptr, baseURI); + } else { + rv = nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(uri), + aScriptURL, parentDoc, + baseURI); + } + + if (NS_FAILED(rv)) { + return NS_ERROR_DOM_SYNTAX_ERR; + } + + // If we have the document, use it. Unfortunately, for dedicated workers + // 'parentDoc' ends up being the parent document, which is not the document + // that we want to use. So make sure to avoid using 'parentDoc' in that + // situation. + if (parentDoc && parentDoc->NodePrincipal() != principal) { + parentDoc = nullptr; + } + + aLoadFlags |= nsIChannel::LOAD_CLASSIFY_URI; + uint32_t secFlags = aIsMainScript ? nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_IS_BLOCKED + : nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS; + + if (aWorkerScriptType == DebuggerScript) { + // A DebuggerScript needs to be a local resource like chrome: or resource: + bool isUIResource = false; + rv = NS_URIChainHasFlags(uri, nsIProtocolHandler::URI_IS_UI_RESOURCE, + &isUIResource); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (!isUIResource) { + return NS_ERROR_DOM_SECURITY_ERR; + } + + secFlags |= nsILoadInfo::SEC_ALLOW_CHROME; + } + + // Note: this is for backwards compatibility and goes against spec. + // We should find a better solution. + bool isData = false; + if (aIsMainScript && NS_SUCCEEDED(uri->SchemeIs("data", &isData)) && isData) { + secFlags = nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL; + } + + nsCOMPtr channel; + // If we have the document, use it. Unfortunately, for dedicated workers + // 'parentDoc' ends up being the parent document, which is not the document + // that we want to use. So make sure to avoid using 'parentDoc' in that + // situation. + if (parentDoc && parentDoc->NodePrincipal() == principal) { + rv = NS_NewChannel(getter_AddRefs(channel), + uri, + parentDoc, + secFlags, + aContentPolicyType, + loadGroup, + nullptr, // aCallbacks + aLoadFlags, + ios); + } else { + // We must have a loadGroup with a load context for the principal to + // traverse the channel correctly. + MOZ_ASSERT(loadGroup); + MOZ_ASSERT(NS_LoadGroupMatchesPrincipal(loadGroup, principal)); + + rv = NS_NewChannel(getter_AddRefs(channel), + uri, + principal, + secFlags, + aContentPolicyType, + loadGroup, + nullptr, // aCallbacks + aLoadFlags, + ios); + } + + NS_ENSURE_SUCCESS(rv, rv); + + if (nsCOMPtr httpChannel = do_QueryInterface(channel)) { + rv = nsContentUtils::SetFetchReferrerURIWithPolicy(principal, parentDoc, + httpChannel, mozilla::net::RP_Default); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + } + + channel.forget(aChannel); + return rv; +} + +struct ScriptLoadInfo +{ + ScriptLoadInfo() + : mScriptTextBuf(nullptr) + , mScriptTextLength(0) + , mLoadResult(NS_ERROR_NOT_INITIALIZED) + , mLoadingFinished(false) + , mExecutionScheduled(false) + , mExecutionResult(false) + , mCacheStatus(Uncached) + { } + + ~ScriptLoadInfo() + { + if (mScriptTextBuf) { + js_free(mScriptTextBuf); + } + } + + bool + ReadyToExecute() + { + return !mChannel && NS_SUCCEEDED(mLoadResult) && !mExecutionScheduled; + } + + nsString mURL; + + // This full URL string is populated only if this object is used in a + // ServiceWorker. + nsString mFullURL; + + // This promise is set only when the script is for a ServiceWorker but + // it's not in the cache yet. The promise is resolved when the full body is + // stored into the cache. mCachePromise will be set to nullptr after + // resolution. + RefPtr mCachePromise; + + // The reader stream the cache entry should be filled from, for those cases + // when we're going to have an mCachePromise. + nsCOMPtr mCacheReadStream; + + nsCOMPtr mChannel; + char16_t* mScriptTextBuf; + size_t mScriptTextLength; + + nsresult mLoadResult; + bool mLoadingFinished; + bool mExecutionScheduled; + bool mExecutionResult; + + enum CacheStatus { + // By default a normal script is just loaded from the network. But for + // ServiceWorkers, we have to check if the cache contains the script and + // load it from the cache. + Uncached, + + WritingToCache, + + ReadingFromCache, + + // This script has been loaded from the ServiceWorker cache. + Cached, + + // This script must be stored in the ServiceWorker cache. + ToBeCached, + + // Something went wrong or the worker went away. + Cancel + }; + + CacheStatus mCacheStatus; + + Maybe mMutedErrorFlag; + + bool Finished() const + { + return mLoadingFinished && !mCachePromise && !mChannel; + } +}; + +class ScriptLoaderRunnable; + +class ScriptExecutorRunnable final : public MainThreadWorkerSyncRunnable +{ + ScriptLoaderRunnable& mScriptLoader; + bool mIsWorkerScript; + uint32_t mFirstIndex; + uint32_t mLastIndex; + +public: + ScriptExecutorRunnable(ScriptLoaderRunnable& aScriptLoader, + nsIEventTarget* aSyncLoopTarget, + bool aIsWorkerScript, + uint32_t aFirstIndex, + uint32_t aLastIndex); + +private: + ~ScriptExecutorRunnable() + { } + + virtual bool + IsDebuggerRunnable() const override; + + virtual bool + PreRun(WorkerPrivate* aWorkerPrivate) override; + + virtual bool + WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override; + + virtual void + PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate, bool aRunResult) + override; + + nsresult + Cancel() override; + + void + ShutdownScriptLoader(JSContext* aCx, + WorkerPrivate* aWorkerPrivate, + bool aResult, + bool aMutedError); + + void LogExceptionToConsole(JSContext* aCx, + WorkerPrivate* WorkerPrivate); +}; + +class CacheScriptLoader; + +class CacheCreator final : public PromiseNativeHandler +{ +public: + NS_DECL_ISUPPORTS + + explicit CacheCreator(WorkerPrivate* aWorkerPrivate) + : mCacheName(aWorkerPrivate->ServiceWorkerCacheName()) + , mOriginAttributes(aWorkerPrivate->GetOriginAttributes()) + { + MOZ_ASSERT(aWorkerPrivate->IsServiceWorker()); + MOZ_ASSERT(aWorkerPrivate->LoadScriptAsPartOfLoadingServiceWorkerScript()); + AssertIsOnMainThread(); + } + + void + AddLoader(CacheScriptLoader* aLoader) + { + AssertIsOnMainThread(); + MOZ_ASSERT(!mCacheStorage); + mLoaders.AppendElement(aLoader); + } + + virtual void + ResolvedCallback(JSContext* aCx, JS::Handle aValue) override; + + virtual void + RejectedCallback(JSContext* aCx, JS::Handle aValue) override; + + // Try to load from cache with aPrincipal used for cache access. + nsresult + Load(nsIPrincipal* aPrincipal); + + Cache* + Cache_() const + { + AssertIsOnMainThread(); + MOZ_ASSERT(mCache); + return mCache; + } + + nsIGlobalObject* + Global() const + { + AssertIsOnMainThread(); + MOZ_ASSERT(mSandboxGlobalObject); + return mSandboxGlobalObject; + } + + void + DeleteCache(); + +private: + ~CacheCreator() + { + } + + nsresult + CreateCacheStorage(nsIPrincipal* aPrincipal); + + void + FailLoaders(nsresult aRv); + + RefPtr mCache; + RefPtr mCacheStorage; + nsCOMPtr mSandboxGlobalObject; + nsTArray> mLoaders; + + nsString mCacheName; + PrincipalOriginAttributes mOriginAttributes; +}; + +NS_IMPL_ISUPPORTS0(CacheCreator) + +class CacheScriptLoader final : public PromiseNativeHandler + , public nsIStreamLoaderObserver +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_NSISTREAMLOADEROBSERVER + + CacheScriptLoader(WorkerPrivate* aWorkerPrivate, ScriptLoadInfo& aLoadInfo, + uint32_t aIndex, bool aIsWorkerScript, + ScriptLoaderRunnable* aRunnable) + : mLoadInfo(aLoadInfo) + , mIndex(aIndex) + , mRunnable(aRunnable) + , mIsWorkerScript(aIsWorkerScript) + , mFailed(false) + { + MOZ_ASSERT(aWorkerPrivate->IsServiceWorker()); + mBaseURI = GetBaseURI(mIsWorkerScript, aWorkerPrivate); + AssertIsOnMainThread(); + } + + void + Fail(nsresult aRv); + + void + Load(Cache* aCache); + + virtual void + ResolvedCallback(JSContext* aCx, JS::Handle aValue) override; + + virtual void + RejectedCallback(JSContext* aCx, JS::Handle aValue) override; + +private: + ~CacheScriptLoader() + { + AssertIsOnMainThread(); + } + + ScriptLoadInfo& mLoadInfo; + uint32_t mIndex; + RefPtr mRunnable; + bool mIsWorkerScript; + bool mFailed; + nsCOMPtr mPump; + nsCOMPtr mBaseURI; + mozilla::dom::ChannelInfo mChannelInfo; + UniquePtr mPrincipalInfo; +}; + +NS_IMPL_ISUPPORTS(CacheScriptLoader, nsIStreamLoaderObserver) + +class CachePromiseHandler final : public PromiseNativeHandler +{ +public: + NS_DECL_ISUPPORTS + + CachePromiseHandler(ScriptLoaderRunnable* aRunnable, + ScriptLoadInfo& aLoadInfo, + uint32_t aIndex) + : mRunnable(aRunnable) + , mLoadInfo(aLoadInfo) + , mIndex(aIndex) + { + AssertIsOnMainThread(); + MOZ_ASSERT(mRunnable); + } + + virtual void + ResolvedCallback(JSContext* aCx, JS::Handle aValue) override; + + virtual void + RejectedCallback(JSContext* aCx, JS::Handle aValue) override; + +private: + ~CachePromiseHandler() + { + AssertIsOnMainThread(); + } + + RefPtr mRunnable; + ScriptLoadInfo& mLoadInfo; + uint32_t mIndex; +}; + +NS_IMPL_ISUPPORTS0(CachePromiseHandler) + +class LoaderListener final : public nsIStreamLoaderObserver + , public nsIRequestObserver +{ +public: + NS_DECL_ISUPPORTS + + LoaderListener(ScriptLoaderRunnable* aRunnable, uint32_t aIndex) + : mRunnable(aRunnable) + , mIndex(aIndex) + { + MOZ_ASSERT(mRunnable); + } + + NS_IMETHOD + OnStreamComplete(nsIStreamLoader* aLoader, nsISupports* aContext, + nsresult aStatus, uint32_t aStringLen, + const uint8_t* aString) override; + + NS_IMETHOD + OnStartRequest(nsIRequest* aRequest, nsISupports* aContext) override; + + NS_IMETHOD + OnStopRequest(nsIRequest* aRequest, nsISupports* aContext, + nsresult aStatusCode) override + { + // Nothing to do here! + return NS_OK; + } + +private: + ~LoaderListener() {} + + RefPtr mRunnable; + uint32_t mIndex; +}; + +NS_IMPL_ISUPPORTS(LoaderListener, nsIStreamLoaderObserver, nsIRequestObserver) + +class ScriptLoaderHolder; + +class ScriptLoaderRunnable final : public nsIRunnable +{ + friend class ScriptExecutorRunnable; + friend class ScriptLoaderHolder; + friend class CachePromiseHandler; + friend class CacheScriptLoader; + friend class LoaderListener; + + WorkerPrivate* mWorkerPrivate; + nsCOMPtr mSyncLoopTarget; + nsTArray mLoadInfos; + RefPtr mCacheCreator; + bool mIsMainScript; + WorkerScriptType mWorkerScriptType; + bool mCanceled; + bool mCanceledMainThread; + ErrorResult& mRv; + +public: + NS_DECL_THREADSAFE_ISUPPORTS + + ScriptLoaderRunnable(WorkerPrivate* aWorkerPrivate, + nsIEventTarget* aSyncLoopTarget, + nsTArray& aLoadInfos, + bool aIsMainScript, + WorkerScriptType aWorkerScriptType, + ErrorResult& aRv) + : mWorkerPrivate(aWorkerPrivate), mSyncLoopTarget(aSyncLoopTarget), + mIsMainScript(aIsMainScript), mWorkerScriptType(aWorkerScriptType), + mCanceled(false), mCanceledMainThread(false), mRv(aRv) + { + aWorkerPrivate->AssertIsOnWorkerThread(); + MOZ_ASSERT(aSyncLoopTarget); + MOZ_ASSERT_IF(aIsMainScript, aLoadInfos.Length() == 1); + + mLoadInfos.SwapElements(aLoadInfos); + } + +private: + ~ScriptLoaderRunnable() + { } + + NS_IMETHOD + Run() override + { + AssertIsOnMainThread(); + + nsresult rv = RunInternal(); + if (NS_WARN_IF(NS_FAILED(rv))) { + CancelMainThread(rv); + } + + return NS_OK; + } + + void + LoadingFinished(uint32_t aIndex, nsresult aRv) + { + AssertIsOnMainThread(); + MOZ_ASSERT(aIndex < mLoadInfos.Length()); + ScriptLoadInfo& loadInfo = mLoadInfos[aIndex]; + + loadInfo.mLoadResult = aRv; + + MOZ_ASSERT(!loadInfo.mLoadingFinished); + loadInfo.mLoadingFinished = true; + + MaybeExecuteFinishedScripts(aIndex); + } + + void + MaybeExecuteFinishedScripts(uint32_t aIndex) + { + AssertIsOnMainThread(); + MOZ_ASSERT(aIndex < mLoadInfos.Length()); + ScriptLoadInfo& loadInfo = mLoadInfos[aIndex]; + + // We execute the last step if we don't have a pending operation with the + // cache and the loading is completed. + if (loadInfo.Finished()) { + ExecuteFinishedScripts(); + } + } + + nsresult + OnStreamComplete(nsIStreamLoader* aLoader, uint32_t aIndex, + nsresult aStatus, uint32_t aStringLen, + const uint8_t* aString) + { + AssertIsOnMainThread(); + MOZ_ASSERT(aIndex < mLoadInfos.Length()); + + nsresult rv = OnStreamCompleteInternal(aLoader, aStatus, aStringLen, + aString, mLoadInfos[aIndex]); + LoadingFinished(aIndex, rv); + return NS_OK; + } + + nsresult + OnStartRequest(nsIRequest* aRequest, uint32_t aIndex) + { + AssertIsOnMainThread(); + MOZ_ASSERT(aIndex < mLoadInfos.Length()); + + // If one load info cancels or hits an error, it can race with the start + // callback coming from another load info. + if (mCanceledMainThread || !mCacheCreator) { + aRequest->Cancel(NS_ERROR_FAILURE); + return NS_ERROR_FAILURE; + } + + ScriptLoadInfo& loadInfo = mLoadInfos[aIndex]; + + nsCOMPtr channel = do_QueryInterface(aRequest); + MOZ_ASSERT(channel == loadInfo.mChannel); + + // We synthesize the result code, but its never exposed to content. + RefPtr ir = + new mozilla::dom::InternalResponse(200, NS_LITERAL_CSTRING("OK")); + ir->SetBody(loadInfo.mCacheReadStream, InternalResponse::UNKNOWN_BODY_SIZE); + // Drop our reference to the stream now that we've passed it along, so it + // doesn't hang around once the cache is done with it and keep data alive. + loadInfo.mCacheReadStream = nullptr; + + // Set the channel info of the channel on the response so that it's + // saved in the cache. + ir->InitChannelInfo(channel); + + // Save the principal of the channel since its URI encodes the script URI + // rather than the ServiceWorkerRegistrationInfo URI. + nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager(); + NS_ASSERTION(ssm, "Should never be null!"); + + nsCOMPtr channelPrincipal; + nsresult rv = ssm->GetChannelResultPrincipal(channel, getter_AddRefs(channelPrincipal)); + if (NS_WARN_IF(NS_FAILED(rv))) { + channel->Cancel(rv); + return rv; + } + + UniquePtr principalInfo(new PrincipalInfo()); + rv = PrincipalToPrincipalInfo(channelPrincipal, principalInfo.get()); + if (NS_WARN_IF(NS_FAILED(rv))) { + channel->Cancel(rv); + return rv; + } + + ir->SetPrincipalInfo(Move(principalInfo)); + + RefPtr response = + new mozilla::dom::Response(mCacheCreator->Global(), ir); + + mozilla::dom::RequestOrUSVString request; + + MOZ_ASSERT(!loadInfo.mFullURL.IsEmpty()); + request.SetAsUSVString().Rebind(loadInfo.mFullURL.Data(), + loadInfo.mFullURL.Length()); + + ErrorResult error; + RefPtr cachePromise = + mCacheCreator->Cache_()->Put(request, *response, error); + if (NS_WARN_IF(error.Failed())) { + nsresult rv = error.StealNSResult(); + channel->Cancel(rv); + return rv; + } + + RefPtr promiseHandler = + new CachePromiseHandler(this, loadInfo, aIndex); + cachePromise->AppendNativeHandler(promiseHandler); + + loadInfo.mCachePromise.swap(cachePromise); + loadInfo.mCacheStatus = ScriptLoadInfo::WritingToCache; + + return NS_OK; + } + + bool + Notify(Status aStatus) + { + mWorkerPrivate->AssertIsOnWorkerThread(); + + if (aStatus >= Terminating && !mCanceled) { + mCanceled = true; + + MOZ_ALWAYS_SUCCEEDS( + NS_DispatchToMainThread(NewRunnableMethod(this, + &ScriptLoaderRunnable::CancelMainThreadWithBindingAborted))); + } + + return true; + } + + bool + IsMainWorkerScript() const + { + return mIsMainScript && mWorkerScriptType == WorkerScript; + } + + void + CancelMainThreadWithBindingAborted() + { + CancelMainThread(NS_BINDING_ABORTED); + } + + void + CancelMainThread(nsresult aCancelResult) + { + AssertIsOnMainThread(); + + if (mCanceledMainThread) { + return; + } + + mCanceledMainThread = true; + + if (mCacheCreator) { + MOZ_ASSERT(mWorkerPrivate->IsServiceWorker()); + DeleteCache(); + } + + // Cancel all the channels that were already opened. + for (uint32_t index = 0; index < mLoadInfos.Length(); index++) { + ScriptLoadInfo& loadInfo = mLoadInfos[index]; + + // If promise or channel is non-null, their failures will lead to + // LoadingFinished being called. + bool callLoadingFinished = true; + + if (loadInfo.mCachePromise) { + MOZ_ASSERT(mWorkerPrivate->IsServiceWorker()); + loadInfo.mCachePromise->MaybeReject(aCancelResult); + loadInfo.mCachePromise = nullptr; + callLoadingFinished = false; + } + + if (loadInfo.mChannel) { + if (NS_SUCCEEDED(loadInfo.mChannel->Cancel(aCancelResult))) { + callLoadingFinished = false; + } else { + NS_WARNING("Failed to cancel channel!"); + } + } + + if (callLoadingFinished && !loadInfo.Finished()) { + LoadingFinished(index, aCancelResult); + } + } + + ExecuteFinishedScripts(); + } + + void + DeleteCache() + { + AssertIsOnMainThread(); + + if (!mCacheCreator) { + return; + } + + mCacheCreator->DeleteCache(); + mCacheCreator = nullptr; + } + + nsresult + RunInternal() + { + AssertIsOnMainThread(); + + if (IsMainWorkerScript() && mWorkerPrivate->IsServiceWorker()) { + mWorkerPrivate->SetLoadingWorkerScript(true); + } + + if (!mWorkerPrivate->IsServiceWorker() || + !mWorkerPrivate->LoadScriptAsPartOfLoadingServiceWorkerScript()) { + for (uint32_t index = 0, len = mLoadInfos.Length(); index < len; + ++index) { + nsresult rv = LoadScript(index); + if (NS_WARN_IF(NS_FAILED(rv))) { + LoadingFinished(index, rv); + return rv; + } + } + + return NS_OK; + } + + MOZ_ASSERT(!mCacheCreator); + mCacheCreator = new CacheCreator(mWorkerPrivate); + + for (uint32_t index = 0, len = mLoadInfos.Length(); index < len; ++index) { + RefPtr loader = + new CacheScriptLoader(mWorkerPrivate, mLoadInfos[index], index, + IsMainWorkerScript(), this); + mCacheCreator->AddLoader(loader); + } + + // The worker may have a null principal on first load, but in that case its + // parent definitely will have one. + nsIPrincipal* principal = mWorkerPrivate->GetPrincipal(); + if (!principal) { + WorkerPrivate* parentWorker = mWorkerPrivate->GetParent(); + MOZ_ASSERT(parentWorker, "Must have a parent!"); + principal = parentWorker->GetPrincipal(); + } + + nsresult rv = mCacheCreator->Load(principal); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + return NS_OK; + } + + nsresult + LoadScript(uint32_t aIndex) + { + AssertIsOnMainThread(); + MOZ_ASSERT(aIndex < mLoadInfos.Length()); + + WorkerPrivate* parentWorker = mWorkerPrivate->GetParent(); + + // Figure out which principal to use. + nsIPrincipal* principal = mWorkerPrivate->GetPrincipal(); + nsCOMPtr loadGroup = mWorkerPrivate->GetLoadGroup(); + if (!principal) { + NS_ASSERTION(parentWorker, "Must have a principal!"); + NS_ASSERTION(mIsMainScript, "Must have a principal for importScripts!"); + + principal = parentWorker->GetPrincipal(); + loadGroup = parentWorker->GetLoadGroup(); + } + NS_ASSERTION(principal, "This should never be null here!"); + MOZ_ASSERT(NS_LoadGroupMatchesPrincipal(loadGroup, principal)); + + // Figure out our base URI. + nsCOMPtr baseURI = GetBaseURI(mIsMainScript, mWorkerPrivate); + + // May be null. + nsCOMPtr parentDoc = mWorkerPrivate->GetDocument(); + + nsCOMPtr channel; + if (IsMainWorkerScript()) { + // May be null. + channel = mWorkerPrivate->ForgetWorkerChannel(); + } + + nsCOMPtr ios(do_GetIOService()); + + nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager(); + NS_ASSERTION(secMan, "This should never be null!"); + + ScriptLoadInfo& loadInfo = mLoadInfos[aIndex]; + nsresult& rv = loadInfo.mLoadResult; + + nsLoadFlags loadFlags = nsIRequest::LOAD_NORMAL; + + // Get the top-level worker. + WorkerPrivate* topWorkerPrivate = mWorkerPrivate; + WorkerPrivate* parent = topWorkerPrivate->GetParent(); + while (parent) { + topWorkerPrivate = parent; + parent = topWorkerPrivate->GetParent(); + } + + // If the top-level worker is a dedicated worker and has a window, and the + // window has a docshell, the caching behavior of this worker should match + // that of that docshell. + if (topWorkerPrivate->IsDedicatedWorker()) { + nsCOMPtr window = topWorkerPrivate->GetWindow(); + if (window) { + nsCOMPtr docShell = window->GetDocShell(); + if (docShell) { + nsresult rv = docShell->GetDefaultLoadFlags(&loadFlags); + NS_ENSURE_SUCCESS(rv, rv); + } + } + } + + // If we are loading a script for a ServiceWorker then we must not + // try to intercept it. If the interception matches the current + // ServiceWorker's scope then we could deadlock the load. + if (mWorkerPrivate->IsServiceWorker()) { + loadFlags |= nsIChannel::LOAD_BYPASS_SERVICE_WORKER; + } + + if (!channel) { + // Only top level workers' main script use the document charset for the + // script uri encoding. Otherwise, default encoding (UTF-8) is applied. + bool useDefaultEncoding = !(!parentWorker && IsMainWorkerScript()); + rv = ChannelFromScriptURL(principal, baseURI, parentDoc, loadGroup, ios, + secMan, loadInfo.mURL, IsMainWorkerScript(), + mWorkerScriptType, + mWorkerPrivate->ContentPolicyType(), loadFlags, + useDefaultEncoding, + getter_AddRefs(channel)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + } + + // We need to know which index we're on in OnStreamComplete so we know + // where to put the result. + RefPtr listener = new LoaderListener(this, aIndex); + + // We don't care about progress so just use the simple stream loader for + // OnStreamComplete notification only. + nsCOMPtr loader; + rv = NS_NewStreamLoader(getter_AddRefs(loader), listener); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (loadInfo.mCacheStatus != ScriptLoadInfo::ToBeCached) { + rv = channel->AsyncOpen2(loader); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + } else { + nsCOMPtr writer; + + // In case we return early. + loadInfo.mCacheStatus = ScriptLoadInfo::Cancel; + + rv = NS_NewPipe(getter_AddRefs(loadInfo.mCacheReadStream), + getter_AddRefs(writer), 0, + UINT32_MAX, // unlimited size to avoid writer WOULD_BLOCK case + true, false); // non-blocking reader, blocking writer + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + nsCOMPtr tee = + do_CreateInstance(NS_STREAMLISTENERTEE_CONTRACTID); + rv = tee->Init(loader, writer, listener); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + nsresult rv = channel->AsyncOpen2(tee); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + } + + loadInfo.mChannel.swap(channel); + + return NS_OK; + } + + nsresult + OnStreamCompleteInternal(nsIStreamLoader* aLoader, nsresult aStatus, + uint32_t aStringLen, const uint8_t* aString, + ScriptLoadInfo& aLoadInfo) + { + AssertIsOnMainThread(); + + if (!aLoadInfo.mChannel) { + return NS_BINDING_ABORTED; + } + + aLoadInfo.mChannel = nullptr; + + if (NS_FAILED(aStatus)) { + return aStatus; + } + + NS_ASSERTION(aString, "This should never be null!"); + + nsCOMPtr request; + nsresult rv = aLoader->GetRequest(getter_AddRefs(request)); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr channel = do_QueryInterface(request); + MOZ_ASSERT(channel); + + nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager(); + NS_ASSERTION(ssm, "Should never be null!"); + + nsCOMPtr channelPrincipal; + rv = ssm->GetChannelResultPrincipal(channel, getter_AddRefs(channelPrincipal)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + nsIPrincipal* principal = mWorkerPrivate->GetPrincipal(); + if (!principal) { + WorkerPrivate* parentWorker = mWorkerPrivate->GetParent(); + MOZ_ASSERT(parentWorker, "Must have a parent!"); + principal = parentWorker->GetPrincipal(); + } + + // We don't mute the main worker script becase we've already done + // same-origin checks on them so we should be able to see their errors. + // Note that for data: url, where we allow it through the same-origin check + // but then give it a different origin. + aLoadInfo.mMutedErrorFlag.emplace(IsMainWorkerScript() + ? false + : !principal->Subsumes(channelPrincipal)); + + // Make sure we're not seeing the result of a 404 or something by checking + // the 'requestSucceeded' attribute on the http channel. + nsCOMPtr httpChannel = do_QueryInterface(request); + nsAutoCString tCspHeaderValue, tCspROHeaderValue, tRPHeaderCValue; + + if (httpChannel) { + bool requestSucceeded; + rv = httpChannel->GetRequestSucceeded(&requestSucceeded); + NS_ENSURE_SUCCESS(rv, rv); + + if (!requestSucceeded) { + return NS_ERROR_NOT_AVAILABLE; + } + + httpChannel->GetResponseHeader( + NS_LITERAL_CSTRING("content-security-policy"), + tCspHeaderValue); + + httpChannel->GetResponseHeader( + NS_LITERAL_CSTRING("content-security-policy-report-only"), + tCspROHeaderValue); + + httpChannel->GetResponseHeader( + NS_LITERAL_CSTRING("referrer-policy"), + tRPHeaderCValue); + } + + // May be null. + nsIDocument* parentDoc = mWorkerPrivate->GetDocument(); + + // Use the regular nsScriptLoader for this grunt work! Should be just fine + // because we're running on the main thread. + // Unlike + + + This is page 1. + + diff --git a/dom/workers/test/WorkerDebugger_frozen_iframe2.html b/dom/workers/test/WorkerDebugger_frozen_iframe2.html new file mode 100644 index 000000000..96d5c56eb --- /dev/null +++ b/dom/workers/test/WorkerDebugger_frozen_iframe2.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 000000000..371d2c064 --- /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 000000000..371d2c064 --- /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 000000000..7d7eaf532 --- /dev/null +++ b/dom/workers/test/WorkerDebugger_promise_debugger.js @@ -0,0 +1,30 @@ +"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 (event) { + if (event.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(); + }); + }; + 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 000000000..a77737af5 --- /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 (event) { + if (event.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 000000000..5ad97d4c5 --- /dev/null +++ b/dom/workers/test/WorkerDebugger_sharedWorker.js @@ -0,0 +1,11 @@ +"use strict"; + +self.onconnect = function (event) { + event.ports[0].onmessage = function (event) { + switch (event.data) { + case "close": + close(); + break; + } + }; +}; diff --git a/dom/workers/test/WorkerDebugger_suspended_debugger.js b/dom/workers/test/WorkerDebugger_suspended_debugger.js new file mode 100644 index 000000000..2ed4e16c4 --- /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 000000000..c096be7e4 --- /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 000000000..f301ac1e8 --- /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 000000000..86431b7f8 --- /dev/null +++ b/dom/workers/test/WorkerTest.jsm @@ -0,0 +1,17 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +this.EXPORTED_SYMBOLS = [ + "WorkerTest" +]; + +this.WorkerTest = { + go: function(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 000000000..7a1df54bd --- /dev/null +++ b/dom/workers/test/WorkerTest_badworker.js @@ -0,0 +1,7 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +onmessage = function(event) { + throw "Shouldn't be able to read this!"; +} diff --git a/dom/workers/test/WorkerTest_subworker.js b/dom/workers/test/WorkerTest_subworker.js new file mode 100644 index 000000000..3e9242ed6 --- /dev/null +++ b/dom/workers/test/WorkerTest_subworker.js @@ -0,0 +1,43 @@ +/** + * 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.xul", + "WorkerTest_badworker.js"); + + let mochitestURL = event.data.replace("test_chromeWorkerJSM.xul", + "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. + let worker = new ChromeWorker(mochitestURL); + worker.onmessage = function(event) { + throw event.data; + }; + worker.onerror = function(event) { + event.preventDefault(); + + // And we shouldn't be able to make a regular Worker to a non-chrome URL. + worker = new Worker(mochitestURL); + worker.onmessage = function(event) { + throw event.data; + }; + worker.onerror = function(event) { + event.preventDefault(); + postMessage("Done"); + }; + worker.postMessage("Hi"); + }; + worker.postMessage("Hi"); +}; diff --git a/dom/workers/test/WorkerTest_worker.js b/dom/workers/test/WorkerTest_worker.js new file mode 100644 index 000000000..445408f8f --- /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(event) { + postMessage(event.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 000000000..680ca904b --- /dev/null +++ b/dom/workers/test/atob_worker.js @@ -0,0 +1,46 @@ +/** + * 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/browser.ini b/dom/workers/test/browser.ini new file mode 100644 index 000000000..ae1e27d13 --- /dev/null +++ b/dom/workers/test/browser.ini @@ -0,0 +1,12 @@ +[DEFAULT] +support-files = + bug1047663_tab.html + bug1047663_worker.sjs + frame_script.js + head.js + !/dom/base/test/file_empty.html + !/dom/base/test/file_bug945152.jar + +[browser_bug1047663.js] +[browser_bug1104623.js] +run-if = buildapp == 'browser' diff --git a/dom/workers/test/browser_bug1047663.js b/dom/workers/test/browser_bug1047663.js new file mode 100644 index 000000000..8fa2c5358 --- /dev/null +++ b/dom/workers/test/browser_bug1047663.js @@ -0,0 +1,50 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +"use strict"; + +const TAB_URL = EXAMPLE_URL + "bug1047663_tab.html"; +const WORKER_URL = EXAMPLE_URL + "bug1047663_worker.sjs"; + +function test() { + waitForExplicitFinish(); + + Task.spawn(function* () { + let tab = yield 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. + yield createWorkerInTab(tab, WORKER_URL); + let message = yield postMessageToWorkerInTab(tab, WORKER_URL, "ping"); + is(message, "one"); + yield 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. + yield createWorkerInTab(tab, WORKER_URL); + message = yield postMessageToWorkerInTab(tab, WORKER_URL, "ping"); + is(message, "one"); + yield terminateWorkerInTab(tab, WORKER_URL); + + // Disable the cache in this tab. This should also disable the cache for all + // workers in this tab. + yield 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. + yield createWorkerInTab(tab, WORKER_URL); + message = yield postMessageToWorkerInTab(tab, WORKER_URL, "ping"); + is(message, "two"); + yield 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 000000000..64a8eeb9c --- /dev/null +++ b/dom/workers/test/browser_bug1104623.js @@ -0,0 +1,53 @@ +/* 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 = gBrowser.addTab(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/bug1014466_data1.txt b/dom/workers/test/bug1014466_data1.txt new file mode 100644 index 000000000..a32a4347a --- /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 000000000..4d40154ce --- /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 000000000..beb324f0f --- /dev/null +++ b/dom/workers/test/bug1014466_worker.js @@ -0,0 +1,64 @@ +/** + * 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: 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(event) { + ok(false, "Got an error event: " + event); + 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 000000000..7f810d893 --- /dev/null +++ b/dom/workers/test/bug1020226_frame.html @@ -0,0 +1,20 @@ + + + + + + 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 000000000..868b1a2f2 --- /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 000000000..62ab9be7d --- /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 000000000..a39bf4474 --- /dev/null +++ b/dom/workers/test/bug1047663_worker.sjs @@ -0,0 +1,40 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +"use strict"; + +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"); + + // 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 000000000..a0fcd3f60 --- /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 000000000..d3df38870 --- /dev/null +++ b/dom/workers/test/bug1062920_worker.js @@ -0,0 +1,6 @@ +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_worker.js b/dom/workers/test/bug1063538_worker.js new file mode 100644 index 000000000..dc53dd289 --- /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 gJar = "jar:http://example.org/tests/dom/base/test/file_bug945152.jar!/data_big.txt"; +var xhr = new XMLHttpRequest({mozAnon: true, mozSystem: true}); +var progressFired = false; + +xhr.onloadend = function(e) { + postMessage({type: 'finish', progressFired: progressFired }); + self.close(); +}; + +xhr.onprogress = function(e) { + if (e.loaded > 0) { + progressFired = true; + xhr.abort(); + } +}; + +onmessage = function(e) { + xhr.open("GET", gJar, true); + xhr.send(); +} diff --git a/dom/workers/test/bug1104064_worker.js b/dom/workers/test/bug1104064_worker.js new file mode 100644 index 000000000..5e627c86f --- /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 000000000..988eea106 --- /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 000000000..cabd40686 --- /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 000000000..126b9c901 --- /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 000000000..a14ed2810 --- /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(e) { + port.postMessage(eval(e.data)); + }; +}); diff --git a/dom/workers/test/chrome.ini b/dom/workers/test/chrome.ini new file mode 100644 index 000000000..c01a20b2b --- /dev/null +++ b/dom/workers/test/chrome.ini @@ -0,0 +1,86 @@ +[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_iframe1.html + WorkerDebugger_frozen_iframe2.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.js + 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 + workersDisabled_worker.js + +[test_WorkerDebugger.initialize.xul] +[test_WorkerDebugger.postMessage.xul] +[test_WorkerDebugger.xul] +[test_WorkerDebuggerGlobalScope.createSandbox.xul] +[test_WorkerDebuggerGlobalScope.enterEventLoop.xul] +[test_WorkerDebuggerGlobalScope.reportError.xul] +skip-if = (os == 'linux') # Bug 1244697 +[test_WorkerDebuggerGlobalScope.setImmediate.xul] +[test_WorkerDebuggerManager.xul] +skip-if = (os == 'linux') # Bug 1244409 +[test_WorkerDebugger_console.xul] +[test_WorkerDebugger_frozen.xul] +[test_WorkerDebugger_promise.xul] +[test_WorkerDebugger_suspended.xul] +[test_chromeWorker.xul] +[test_chromeWorkerJSM.xul] +[test_extension.xul] +[test_extensionBootstrap.xul] +[test_file.xul] +[test_fileBlobPosting.xul] +[test_fileBlobSubWorker.xul] +[test_filePosting.xul] +[test_fileReadSlice.xul] +[test_fileReaderSync.xul] +[test_fileReaderSyncErrors.xul] +[test_fileSlice.xul] +[test_fileSubWorker.xul] +[test_workersDisabled.xul] +[test_bug1062920.xul] +[test_sharedWorker_privateBrowsing.html] diff --git a/dom/workers/test/chromeWorker_subworker.js b/dom/workers/test/chromeWorker_subworker.js new file mode 100644 index 000000000..5ad1a9923 --- /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 000000000..5738a9ae5 --- /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(event) { + postMessage(event.data); + } + worker.postMessage(event.data); +} diff --git a/dom/workers/test/clearTimeouts_worker.js b/dom/workers/test/clearTimeouts_worker.js new file mode 100644 index 000000000..b471515b3 --- /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 000000000..aaf104af1 --- /dev/null +++ b/dom/workers/test/consoleReplaceable_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: '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 000000000..6b5f9d8a1 --- /dev/null +++ b/dom/workers/test/console_worker.js @@ -0,0 +1,109 @@ +/** + * 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("b", "group"); + } + + 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(event) { + postMessage(event.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 000000000..7e092ec8c --- /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": 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 000000000..6ad0d550f --- /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 000000000..6d896bc46 --- /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 000000000..6d1f0f0a7 --- /dev/null +++ b/dom/workers/test/crashtests/1228456.html @@ -0,0 +1,14 @@ + + + diff --git a/dom/workers/test/crashtests/779707.html b/dom/workers/test/crashtests/779707.html new file mode 100644 index 000000000..97a8113da --- /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 000000000..5f4667850 --- /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 000000000..a7518d3c2 --- /dev/null +++ b/dom/workers/test/crashtests/crashtests.list @@ -0,0 +1,5 @@ +load 779707.html +load 943516.html +load 1153636.html +load 1158031.html +load 1228456.html diff --git a/dom/workers/test/csp_worker.js b/dom/workers/test/csp_worker.js new file mode 100644 index 000000000..63b3adeaf --- /dev/null +++ b/dom/workers/test/csp_worker.js @@ -0,0 +1,28 @@ +/** + * 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 000000000..7b835bf2a --- /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 000000000..52e802ac7 --- /dev/null +++ b/dom/workers/test/dom_worker_helper.js @@ -0,0 +1,176 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var { classes: Cc, interfaces: Ci, utils: Cu } = Components; + +Cu.import("resource://gre/modules/AddonManager.jsm"); +Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/Task.jsm"); +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); + +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() { + let e = wdm.getWorkerDebuggerEnumerator(); + while (e.hasMoreElements()) { + let dbg = e.getNext().QueryInterface(Ci.nsIWorkerDebugger); + yield dbg; + } +} + +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: function (dbg) { + dump("FAK " + dbg.url + "\n"); + 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: function (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: function () { + ok(true, "Debugger should be closed."); + dbg.removeListener(this); + resolve(); + } + }); + }); +} + +function waitForDebuggerError(dbg) { + return new Promise(function (resolve) { + dbg.addListener({ + onError: function (filename, lineno, message) { + dbg.removeListener(this); + resolve(new Error(message, filename, lineno)); + } + }); + }); +} + +function waitForDebuggerMessage(dbg, message) { + return new Promise(function (resolve) { + dbg.addListener({ + onMessage: function (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, false); + resolve(); + }; + window.addEventListener("message", onmessage, false); + }); +} + +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/empty.html b/dom/workers/test/empty.html new file mode 100644 index 000000000..e69de29bb diff --git a/dom/workers/test/errorPropagation_iframe.html b/dom/workers/test/errorPropagation_iframe.html new file mode 100644 index 000000000..c5f688487 --- /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 000000000..84f5916e0 --- /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: message, filename: filename, lineno: lineno } + }); + return true; + } +}; + +onmessage = function(event) { + var workerId = parseInt(event.data); + + if (workerId > 1) { + var worker = new Worker("errorPropagation_worker.js"); + + worker.onmessage = function(event) { + postMessage(event.data); + }; + + var seenWorkerError; + worker.onerror = function(event) { + if (!seenWorkerError) { + seenWorkerError = true; + postMessage({ + type: "worker", + data: { + message: event.message, + filename: event.filename, + lineno: event.lineno + } + }); + event.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 000000000..1c372104c --- /dev/null +++ b/dom/workers/test/errorwarning_worker.js @@ -0,0 +1,42 @@ +/** + * 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 000000000..915f60c93 --- /dev/null +++ b/dom/workers/test/eventDispatch_worker.js @@ -0,0 +1,67 @@ +/** + * 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/extensions/bootstrap/bootstrap.js b/dom/workers/test/extensions/bootstrap/bootstrap.js new file mode 100644 index 000000000..acb0a204b --- /dev/null +++ b/dom/workers/test/extensions/bootstrap/bootstrap.js @@ -0,0 +1,141 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var Ci = Components.interfaces; +var Cu = Components.utils; + +Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); + +function testForExpectedSymbols(stage, data) { + const expectedSymbols = [ "Worker", "ChromeWorker" ]; + for (var symbol of expectedSymbols) { + Services.prefs.setBoolPref("workertest.bootstrap." + stage + "." + symbol, + symbol in this); + } +} + +var gWorkerAndCallback = { + _ensureStarted: function() { + if (!this._worker) { + throw new Error("Not yet started!"); + } + }, + + start: function(data) { + if (!this._worker) { + this._worker = new Worker("chrome://workerbootstrap/content/worker.js"); + this._worker.onerror = function(event) { + Cu.reportError(event.message); + event.preventDefault(); + }; + } + }, + + stop: function() { + if (this._worker) { + this._worker.terminate(); + delete this._worker; + } + }, + + set callback(val) { + this._ensureStarted(); + var callback = val.QueryInterface(Ci.nsIObserver); + if (this._callback != callback) { + if (callback) { + this._worker.onmessage = function(event) { + callback.observe(this, event.type, event.data); + }; + this._worker.onerror = function(event) { + callback.observe(this, event.type, event.message); + event.preventDefault(); + }; + } + else { + this._worker.onmessage = null; + this._worker.onerror = null; + } + this._callback = callback; + } + }, + + get callback() { + return this._callback; + }, + + postMessage: function(data) { + this._ensureStarted(); + this._worker.postMessage(data); + }, + + terminate: function() { + this._ensureStarted(); + this._worker.terminate(); + delete this._callback; + } +}; + +function WorkerTestBootstrap() { +} +WorkerTestBootstrap.prototype = { + observe: function(subject, topic, data) { + + gWorkerAndCallback.callback = subject; + + switch (topic) { + case "postMessage": + gWorkerAndCallback.postMessage(data); + break; + + case "terminate": + gWorkerAndCallback.terminate(); + break; + + default: + throw new Error("Unknown worker command"); + } + }, + + QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]) +}; + +var gFactory = { + register: function() { + var registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar); + + var classID = Components.ID("{36b5df0b-8dcf-4aa2-9c45-c51d871295f9}"); + var description = "WorkerTestBootstrap"; + var contractID = "@mozilla.org/test/workertestbootstrap;1"; + var factory = XPCOMUtils._getFactory(WorkerTestBootstrap); + + registrar.registerFactory(classID, description, contractID, factory); + + this.unregister = function() { + registrar.unregisterFactory(classID, factory); + delete this.unregister; + }; + } +}; + +function install(data, reason) { + testForExpectedSymbols("install"); +} + +function startup(data, reason) { + testForExpectedSymbols("startup"); + gFactory.register(); + gWorkerAndCallback.start(data); +} + +function shutdown(data, reason) { + testForExpectedSymbols("shutdown"); + gWorkerAndCallback.stop(); + gFactory.unregister(); +} + +function uninstall(data, reason) { + testForExpectedSymbols("uninstall"); +} diff --git a/dom/workers/test/extensions/bootstrap/install.rdf b/dom/workers/test/extensions/bootstrap/install.rdf new file mode 100644 index 000000000..fdd9638cd --- /dev/null +++ b/dom/workers/test/extensions/bootstrap/install.rdf @@ -0,0 +1,31 @@ + + + + + + WorkerTestBootstrap + Worker functions for use in testing. + Mozilla + 2016.03.09 + workerbootstrap-test@mozilla.org + 2 + true + + + + {ec8030f7-c20a-464f-9b0e-13a3a9e97384} + 45.0 + * + + + + + + {aa3c5121-dab2-40e2-81ca-7ea25febc110} + 45.0 + * + + + + diff --git a/dom/workers/test/extensions/bootstrap/jar.mn b/dom/workers/test/extensions/bootstrap/jar.mn new file mode 100644 index 000000000..a9c625103 --- /dev/null +++ b/dom/workers/test/extensions/bootstrap/jar.mn @@ -0,0 +1,3 @@ +workerbootstrap.jar: +% content workerbootstrap %content/ + content/worker.js (worker.js) diff --git a/dom/workers/test/extensions/bootstrap/moz.build b/dom/workers/test/extensions/bootstrap/moz.build new file mode 100644 index 000000000..aec5c249c --- /dev/null +++ b/dom/workers/test/extensions/bootstrap/moz.build @@ -0,0 +1,20 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +XPI_NAME = 'workerbootstrap' + +JAR_MANIFESTS += ['jar.mn'] +USE_EXTENSION_MANIFEST = True +NO_JS_MANIFEST = True + +FINAL_TARGET_FILES += [ + 'bootstrap.js', + 'install.rdf', +] + +TEST_HARNESS_FILES.testing.mochitest.extensions += [ + 'workerbootstrap-test@mozilla.org.xpi', +] diff --git a/dom/workers/test/extensions/bootstrap/worker.js b/dom/workers/test/extensions/bootstrap/worker.js new file mode 100644 index 000000000..7346fc142 --- /dev/null +++ b/dom/workers/test/extensions/bootstrap/worker.js @@ -0,0 +1,7 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +onmessage = function(event) { + postMessage(event.data); +} diff --git a/dom/workers/test/extensions/bootstrap/workerbootstrap-test@mozilla.org.xpi b/dom/workers/test/extensions/bootstrap/workerbootstrap-test@mozilla.org.xpi new file mode 100644 index 000000000..2dab975db Binary files /dev/null and b/dom/workers/test/extensions/bootstrap/workerbootstrap-test@mozilla.org.xpi differ diff --git a/dom/workers/test/extensions/moz.build b/dom/workers/test/extensions/moz.build new file mode 100644 index 000000000..51cf80fa2 --- /dev/null +++ b/dom/workers/test/extensions/moz.build @@ -0,0 +1,7 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +DIRS += ['bootstrap', 'traditional'] diff --git a/dom/workers/test/extensions/traditional/WorkerTest.js b/dom/workers/test/extensions/traditional/WorkerTest.js new file mode 100644 index 000000000..5890c0d4c --- /dev/null +++ b/dom/workers/test/extensions/traditional/WorkerTest.js @@ -0,0 +1,122 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +const Cc = Components.classes; +const Ci = Components.interfaces; +const Cu = Components.utils; + +Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); + +var gWorkerAndCallback = { + _worker: null, + _callback: null, + + _ensureStarted: function() { + if (!this._worker) { + throw new Error("Not yet started!"); + } + }, + + start: function() { + if (!this._worker) { + var worker = new Worker("chrome://worker/content/worker.js"); + worker.onerror = function(event) { + Cu.reportError(event.message); + event.preventDefault(); + }; + + this._worker = worker; + } + }, + + stop: function() { + if (this._worker) { + try { + this.terminate(); + } + catch(e) { + Cu.reportError(e); + } + this._worker = null; + } + }, + + set callback(val) { + this._ensureStarted(); + if (val) { + var callback = val.QueryInterface(Ci.nsIWorkerTestCallback); + if (this.callback != callback) { + this._worker.onmessage = function(event) { + callback.onmessage(event.data); + }; + this._worker.onerror = function(event) { + callback.onerror(event.message); + event.preventDefault(); + }; + this._callback = callback; + } + } + else { + this._worker.onmessage = null; + this._worker.onerror = null; + this._callback = null; + } + }, + + get callback() { + return this._callback; + }, + + postMessage: function(data) { + this._ensureStarted(); + this._worker.postMessage(data); + }, + + terminate: function() { + this._ensureStarted(); + this._worker.terminate(); + this.callback = null; + } +}; + +function WorkerTest() { +} +WorkerTest.prototype = { + observe: function(subject, topic, data) { + switch(topic) { + case "profile-after-change": + gWorkerAndCallback.start(); + Services.obs.addObserver(this, "profile-before-change", false); + break; + case "profile-before-change": + gWorkerAndCallback.stop(); + break; + default: + Cu.reportError("Unknown topic: " + topic); + } + }, + + set callback(val) { + gWorkerAndCallback.callback = val; + }, + + get callback() { + return gWorkerAndCallback.callback; + }, + + postMessage: function(message) { + gWorkerAndCallback.postMessage(message); + }, + + terminate: function() { + gWorkerAndCallback.terminate(); + }, + + QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver, Ci.nsIWorkerTest]), + classID: Components.ID("{3b52b935-551f-4606-ba4c-decc18b67bfd}") +}; + +this.NSGetFactory = XPCOMUtils.generateNSGetFactory([WorkerTest]); diff --git a/dom/workers/test/extensions/traditional/WorkerTest.manifest b/dom/workers/test/extensions/traditional/WorkerTest.manifest new file mode 100644 index 000000000..a5a32fb06 --- /dev/null +++ b/dom/workers/test/extensions/traditional/WorkerTest.manifest @@ -0,0 +1,3 @@ +component {3b52b935-551f-4606-ba4c-decc18b67bfd} WorkerTest.js +contract @mozilla.org/test/workertest;1 {3b52b935-551f-4606-ba4c-decc18b67bfd} +category profile-after-change WorkerTest @mozilla.org/test/workertest;1 diff --git a/dom/workers/test/extensions/traditional/install.rdf b/dom/workers/test/extensions/traditional/install.rdf new file mode 100644 index 000000000..00fc0f441 --- /dev/null +++ b/dom/workers/test/extensions/traditional/install.rdf @@ -0,0 +1,30 @@ + + + + + + WorkerTest + Worker functions for use in testing. + Mozilla + 2016.03.09 + worker-test@mozilla.org + 2 + + + + {ec8030f7-c20a-464f-9b0e-13a3a9e97384} + 45.0 + * + + + + + + {aa3c5121-dab2-40e2-81ca-7ea25febc110} + 45.0 + * + + + + diff --git a/dom/workers/test/extensions/traditional/jar.mn b/dom/workers/test/extensions/traditional/jar.mn new file mode 100644 index 000000000..421ee55a0 --- /dev/null +++ b/dom/workers/test/extensions/traditional/jar.mn @@ -0,0 +1,3 @@ +worker.jar: +% content worker %content/ + content/worker.js (worker.js) diff --git a/dom/workers/test/extensions/traditional/moz.build b/dom/workers/test/extensions/traditional/moz.build new file mode 100644 index 000000000..d0920420d --- /dev/null +++ b/dom/workers/test/extensions/traditional/moz.build @@ -0,0 +1,30 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +XPIDL_SOURCES += [ + 'nsIWorkerTest.idl', +] + +XPIDL_MODULE = 'WorkerTest' + +EXTRA_COMPONENTS += [ + 'WorkerTest.js', + 'WorkerTest.manifest', +] + +XPI_NAME = 'worker' + +JAR_MANIFESTS += ['jar.mn'] +USE_EXTENSION_MANIFEST = True +NO_JS_MANIFEST = True + +FINAL_TARGET_FILES += [ + 'install.rdf', +] + +TEST_HARNESS_FILES.testing.mochitest.extensions += [ + 'worker-test@mozilla.org.xpi', +] diff --git a/dom/workers/test/extensions/traditional/nsIWorkerTest.idl b/dom/workers/test/extensions/traditional/nsIWorkerTest.idl new file mode 100644 index 000000000..32a952038 --- /dev/null +++ b/dom/workers/test/extensions/traditional/nsIWorkerTest.idl @@ -0,0 +1,23 @@ +/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */ +/* vim: set ts=2 et sw=2 tw=40: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsISupports.idl" + +[scriptable, uuid(10f8ebdf-1373-4640-9c34-53dee99f526f)] +interface nsIWorkerTestCallback : nsISupports +{ + void onmessage(in DOMString data); + void onerror(in DOMString data); +}; + +[scriptable, uuid(887a0614-a0f0-4c0e-80e0-cf31e6d4e286)] +interface nsIWorkerTest : nsISupports +{ + void postMessage(in DOMString data); + void terminate(); + + attribute nsIWorkerTestCallback callback; +}; diff --git a/dom/workers/test/extensions/traditional/worker-test@mozilla.org.xpi b/dom/workers/test/extensions/traditional/worker-test@mozilla.org.xpi new file mode 100644 index 000000000..8d2386894 Binary files /dev/null and b/dom/workers/test/extensions/traditional/worker-test@mozilla.org.xpi differ diff --git a/dom/workers/test/extensions/traditional/worker.js b/dom/workers/test/extensions/traditional/worker.js new file mode 100644 index 000000000..7346fc142 --- /dev/null +++ b/dom/workers/test/extensions/traditional/worker.js @@ -0,0 +1,7 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +onmessage = function(event) { + postMessage(event.data); +} diff --git a/dom/workers/test/fibonacci_worker.js b/dom/workers/test/fibonacci_worker.js new file mode 100644 index 000000000..fa35385e7 --- /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(event) { + results.push(parseInt(event.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 000000000..2dc8cd12d --- /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(event) { + postMessage(event.data); + } + + worker.onerror = function(event) { + postMessage(undefined); + } +}; diff --git a/dom/workers/test/fileBlob_worker.js b/dom/workers/test/fileBlob_worker.js new file mode 100644 index 000000000..2f7a31714 --- /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 000000000..2a24b2a40 --- /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 000000000..2c10b7d76 --- /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 000000000..f79ecc6f0 --- /dev/null +++ b/dom/workers/test/fileReaderSyncErrors_worker.js @@ -0,0 +1,74 @@ +/** + * 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 000000000..4a37409d5 --- /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 000000000..d0c6364e2 --- /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 000000000..21fbc3454 --- /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(event) { + postMessage(event.data); + } + + worker.onerror = function(event) { + 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 000000000..239968069 --- /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_worker.js b/dom/workers/test/file_worker.js new file mode 100644 index 000000000..23233b8ac --- /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.lastModifiedDate = file.lastModifiedDate; + + postMessage(rtnObj); +}; diff --git a/dom/workers/test/fileapi_chromeScript.js b/dom/workers/test/fileapi_chromeScript.js new file mode 100644 index 000000000..614b556ed --- /dev/null +++ b/dom/workers/test/fileapi_chromeScript.js @@ -0,0 +1,29 @@ +var { classes: Cc, interfaces: Ci, utils: Cu } = Components; +Cu.importGlobalProperties(["File"]); + +var fileNum = 1; + +function createFileWithData(fileData) { + var willDelete = fileData === null; + var dirSvc = Cc["@mozilla.org/file/directory_service;1"].getService(Ci.nsIProperties); + var testFile = dirSvc.get("ProfD", Ci.nsIFile); + testFile.append("fileAPItestfile" + fileNum); + fileNum++; + var outStream = Cc["@mozilla.org/network/file-output-stream;1"].createInstance(Ci.nsIFileOutputStream); + outStream.init(testFile, 0x02 | 0x08 | 0x20, // write, create, truncate + 0o666, 0); + if (willDelete) { + fileData = "some irrelevant test data\n"; + } + outStream.write(fileData, fileData.length); + outStream.close(); + var domFile = File.createFromNsIFile(testFile); + if (willDelete) { + testFile.remove(/* recursive: */ false); + } + return domFile; +} + +addMessageListener("files.open", function (message) { + sendAsyncMessage("files.opened", message.map(createFileWithData)); +}); diff --git a/dom/workers/test/foreign.js b/dom/workers/test/foreign.js new file mode 100644 index 000000000..33c982fa8 --- /dev/null +++ b/dom/workers/test/foreign.js @@ -0,0 +1 @@ +response = "bad"; diff --git a/dom/workers/test/frame_script.js b/dom/workers/test/frame_script.js new file mode 100644 index 000000000..ffc384416 --- /dev/null +++ b/dom/workers/test/frame_script.js @@ -0,0 +1,72 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +"use strict"; + +const { interfaces: Ci } = Components; + +let workers = {}; + +let methods = { + /** + * Create a worker with the given `url` in this tab. + */ + createWorker: function (url) { + dump("Frame script: creating worker with url '" + url + "'\n"); + + workers[url] = new content.Worker(url); + return Promise.resolve(); + }, + + /** + * Terminate the worker with the given `url` in this tab. + */ + terminateWorker: function (url) { + dump("Frame script: terminating worker with url '" + url + "'\n"); + + workers[url].terminate(); + delete workers[url]; + return Promise.resolve(); + }, + + /** + * Post the given `message` to the worker with the given `url` in this tab. + */ + postMessageToWorker: function (url, message) { + dump("Frame script: posting message to worker with url '" + url + "'\n"); + + let worker = workers[url]; + worker.postMessage(message); + return new Promise(function (resolve) { + worker.onmessage = function (event) { + worker.onmessage = null; + resolve(event.data); + }; + }); + }, + + /** + * Disable the cache for this tab. + */ + disableCache: function () { + docShell.defaultLoadFlags = Ci.nsIRequest.LOAD_BYPASS_CACHE + | Ci.nsIRequest.INHIBIT_CACHING; + } +}; + +addMessageListener("jsonrpc", function (event) { + let { id, method, params } = event.data; + Promise.resolve().then(function () { + return methods[method].apply(undefined, params); + }).then(function (result) { + sendAsyncMessage("jsonrpc", { + id: id, + result: result + }); + }).catch(function (error) { + sendAsyncMessage("jsonrpc", { + id: id, + error: error.toString() + }); + }); +}); diff --git a/dom/workers/test/gtest/TestReadWrite.cpp b/dom/workers/test/gtest/TestReadWrite.cpp new file mode 100644 index 000000000..d59888e24 --- /dev/null +++ b/dom/workers/test/gtest/TestReadWrite.cpp @@ -0,0 +1,499 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "gtest/gtest.h" +#include "mozilla/BasePrincipal.h" +#include "mozilla/dom/ServiceWorkerRegistrar.h" +#include "mozilla/dom/ServiceWorkerRegistrarTypes.h" +#include "mozilla/ipc/PBackgroundSharedTypes.h" + +#include "nsAppDirectoryServiceDefs.h" +#include "nsIFile.h" +#include "nsIOutputStream.h" +#include "nsNetUtil.h" +#include "nsPrintfCString.h" + +using namespace mozilla::dom; +using namespace mozilla::ipc; + +class ServiceWorkerRegistrarTest : public ServiceWorkerRegistrar +{ +public: + ServiceWorkerRegistrarTest() + { +#if defined(DEBUG) || !defined(RELEASE_OR_BETA) + nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, + getter_AddRefs(mProfileDir)); + MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv)); +#else + NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, + getter_AddRefs(mProfileDir)); +#endif + MOZ_DIAGNOSTIC_ASSERT(mProfileDir); + } + + nsresult TestReadData() { return ReadData(); } + nsresult TestWriteData() { return WriteData(); } + void TestDeleteData() { DeleteData(); } + + void TestRegisterServiceWorker(const ServiceWorkerRegistrationData& aData) + { + RegisterServiceWorkerInternal(aData); + } + + nsTArray& TestGetData() { return mData; } +}; + +already_AddRefed +GetFile() +{ + nsCOMPtr file; + nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, + getter_AddRefs(file)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return nullptr; + } + + file->Append(NS_LITERAL_STRING(SERVICEWORKERREGISTRAR_FILE)); + return file.forget(); +} + +bool +CreateFile(const nsACString& aData) +{ + nsCOMPtr file = GetFile(); + + nsCOMPtr stream; + nsresult rv = NS_NewLocalFileOutputStream(getter_AddRefs(stream), file); + if (NS_WARN_IF(NS_FAILED(rv))) { + return false; + } + + uint32_t count; + rv = stream->Write(aData.Data(), aData.Length(), &count); + if (NS_WARN_IF(NS_FAILED(rv))) { + return false; + } + + if (count != aData.Length()) { + return false; + } + + return true; +} + +TEST(ServiceWorkerRegistrar, TestNoFile) +{ + nsCOMPtr file = GetFile(); + ASSERT_TRUE(file) << "GetFile must return a nsIFIle"; + + bool exists; + nsresult rv = file->Exists(&exists); + ASSERT_EQ(NS_OK, rv) << "nsIFile::Exists cannot fail"; + + if (exists) { + rv = file->Remove(false); + ASSERT_EQ(NS_OK, rv) << "nsIFile::Remove cannot fail"; + } + + RefPtr swr = new ServiceWorkerRegistrarTest; + + rv = swr->TestReadData(); + ASSERT_EQ(NS_OK, rv) << "ReadData() should not fail"; + + const nsTArray& data = swr->TestGetData(); + ASSERT_EQ((uint32_t)0, data.Length()) << "No data should be found in an empty file"; +} + +TEST(ServiceWorkerRegistrar, TestEmptyFile) +{ + ASSERT_TRUE(CreateFile(EmptyCString())) << "CreateFile should not fail"; + + RefPtr swr = new ServiceWorkerRegistrarTest; + + nsresult rv = swr->TestReadData(); + ASSERT_NE(NS_OK, rv) << "ReadData() should fail if the file is empty"; + + const nsTArray& data = swr->TestGetData(); + ASSERT_EQ((uint32_t)0, data.Length()) << "No data should be found in an empty file"; +} + +TEST(ServiceWorkerRegistrar, TestRightVersionFile) +{ + ASSERT_TRUE(CreateFile(NS_LITERAL_CSTRING(SERVICEWORKERREGISTRAR_VERSION "\n"))) << "CreateFile should not fail"; + + RefPtr swr = new ServiceWorkerRegistrarTest; + + nsresult rv = swr->TestReadData(); + ASSERT_EQ(NS_OK, rv) << "ReadData() should not fail when the version is correct"; + + const nsTArray& data = swr->TestGetData(); + ASSERT_EQ((uint32_t)0, data.Length()) << "No data should be found in an empty file"; +} + +TEST(ServiceWorkerRegistrar, TestWrongVersionFile) +{ + ASSERT_TRUE(CreateFile(NS_LITERAL_CSTRING(SERVICEWORKERREGISTRAR_VERSION "bla\n"))) << "CreateFile should not fail"; + + RefPtr swr = new ServiceWorkerRegistrarTest; + + nsresult rv = swr->TestReadData(); + ASSERT_NE(NS_OK, rv) << "ReadData() should fail when the version is not correct"; + + const nsTArray& data = swr->TestGetData(); + ASSERT_EQ((uint32_t)0, data.Length()) << "No data should be found in an empty file"; +} + +TEST(ServiceWorkerRegistrar, TestReadData) +{ + nsAutoCString buffer(SERVICEWORKERREGISTRAR_VERSION "\n"); + + buffer.Append("^appId=123&inBrowser=1\n"); + buffer.Append("scope 0\ncurrentWorkerURL 0\ncacheName 0\n"); + buffer.Append(SERVICEWORKERREGISTRAR_TERMINATOR "\n"); + + buffer.Append("\n"); + buffer.Append("scope 1\ncurrentWorkerURL 1\ncacheName 1\n"); + buffer.Append(SERVICEWORKERREGISTRAR_TERMINATOR "\n"); + + ASSERT_TRUE(CreateFile(buffer)) << "CreateFile should not fail"; + + RefPtr swr = new ServiceWorkerRegistrarTest; + + nsresult rv = swr->TestReadData(); + ASSERT_EQ(NS_OK, rv) << "ReadData() should not fail"; + + const nsTArray& data = swr->TestGetData(); + ASSERT_EQ((uint32_t)2, data.Length()) << "2 entries should be found"; + + const mozilla::ipc::PrincipalInfo& info0 = data[0].principal(); + ASSERT_EQ(info0.type(), mozilla::ipc::PrincipalInfo::TContentPrincipalInfo) << "First principal must be content"; + const mozilla::ipc::ContentPrincipalInfo& cInfo0 = data[0].principal(); + + nsAutoCString suffix0; + cInfo0.attrs().CreateSuffix(suffix0); + + ASSERT_STREQ("^appId=123&inBrowser=1", suffix0.get()); + ASSERT_STREQ("scope 0", cInfo0.spec().get()); + ASSERT_STREQ("scope 0", data[0].scope().get()); + ASSERT_STREQ("currentWorkerURL 0", data[0].currentWorkerURL().get()); + ASSERT_STREQ("cacheName 0", NS_ConvertUTF16toUTF8(data[0].cacheName()).get()); + + const mozilla::ipc::PrincipalInfo& info1 = data[1].principal(); + ASSERT_EQ(info1.type(), mozilla::ipc::PrincipalInfo::TContentPrincipalInfo) << "First principal must be content"; + const mozilla::ipc::ContentPrincipalInfo& cInfo1 = data[1].principal(); + + nsAutoCString suffix1; + cInfo1.attrs().CreateSuffix(suffix1); + + ASSERT_STREQ("", suffix1.get()); + ASSERT_STREQ("scope 1", cInfo1.spec().get()); + ASSERT_STREQ("scope 1", data[1].scope().get()); + ASSERT_STREQ("currentWorkerURL 1", data[1].currentWorkerURL().get()); + ASSERT_STREQ("cacheName 1", NS_ConvertUTF16toUTF8(data[1].cacheName()).get()); +} + +TEST(ServiceWorkerRegistrar, TestDeleteData) +{ + ASSERT_TRUE(CreateFile(NS_LITERAL_CSTRING("Foobar"))) << "CreateFile should not fail"; + + RefPtr swr = new ServiceWorkerRegistrarTest; + + swr->TestDeleteData(); + + nsCOMPtr file = GetFile(); + + bool exists; + nsresult rv = file->Exists(&exists); + ASSERT_EQ(NS_OK, rv) << "nsIFile::Exists cannot fail"; + + ASSERT_FALSE(exists) << "The file should not exist after a DeleteData()."; +} + +TEST(ServiceWorkerRegistrar, TestWriteData) +{ + { + RefPtr swr = new ServiceWorkerRegistrarTest; + + for (int i = 0; i < 10; ++i) { + ServiceWorkerRegistrationData reg; + + reg.scope() = nsPrintfCString("scope write %d", i); + reg.currentWorkerURL() = nsPrintfCString("currentWorkerURL write %d", i); + reg.cacheName() = + NS_ConvertUTF8toUTF16(nsPrintfCString("cacheName write %d", i)); + + nsAutoCString spec; + spec.AppendPrintf("spec write %d", i); + reg.principal() = + mozilla::ipc::ContentPrincipalInfo(mozilla::PrincipalOriginAttributes(i, i % 2), + mozilla::void_t(), spec); + + swr->TestRegisterServiceWorker(reg); + } + + nsresult rv = swr->TestWriteData(); + ASSERT_EQ(NS_OK, rv) << "WriteData() should not fail"; + } + + RefPtr swr = new ServiceWorkerRegistrarTest; + + nsresult rv = swr->TestReadData(); + ASSERT_EQ(NS_OK, rv) << "ReadData() should not fail"; + + const nsTArray& data = swr->TestGetData(); + ASSERT_EQ((uint32_t)10, data.Length()) << "10 entries should be found"; + + for (int i = 0; i < 10; ++i) { + nsAutoCString test; + + ASSERT_EQ(data[i].principal().type(), mozilla::ipc::PrincipalInfo::TContentPrincipalInfo); + const mozilla::ipc::ContentPrincipalInfo& cInfo = data[i].principal(); + + mozilla::PrincipalOriginAttributes attrs(i, i % 2); + nsAutoCString suffix, expectSuffix; + attrs.CreateSuffix(expectSuffix); + cInfo.attrs().CreateSuffix(suffix); + + ASSERT_STREQ(expectSuffix.get(), suffix.get()); + + test.AppendPrintf("scope write %d", i); + ASSERT_STREQ(test.get(), cInfo.spec().get()); + + test.Truncate(); + test.AppendPrintf("scope write %d", i); + ASSERT_STREQ(test.get(), data[i].scope().get()); + + test.Truncate(); + test.AppendPrintf("currentWorkerURL write %d", i); + ASSERT_STREQ(test.get(), data[i].currentWorkerURL().get()); + + test.Truncate(); + test.AppendPrintf("cacheName write %d", i); + ASSERT_STREQ(test.get(), NS_ConvertUTF16toUTF8(data[i].cacheName()).get()); + } +} + +TEST(ServiceWorkerRegistrar, TestVersion2Migration) +{ + nsAutoCString buffer("2" "\n"); + + buffer.Append("^appId=123&inBrowser=1\n"); + buffer.Append("spec 0\nscope 0\nscriptSpec 0\ncurrentWorkerURL 0\nactiveCache 0\nwaitingCache 0\n"); + buffer.Append(SERVICEWORKERREGISTRAR_TERMINATOR "\n"); + + buffer.Append("\n"); + buffer.Append("spec 1\nscope 1\nscriptSpec 1\ncurrentWorkerURL 1\nactiveCache 1\nwaitingCache 1\n"); + buffer.Append(SERVICEWORKERREGISTRAR_TERMINATOR "\n"); + + ASSERT_TRUE(CreateFile(buffer)) << "CreateFile should not fail"; + + RefPtr swr = new ServiceWorkerRegistrarTest; + + nsresult rv = swr->TestReadData(); + ASSERT_EQ(NS_OK, rv) << "ReadData() should not fail"; + + const nsTArray& data = swr->TestGetData(); + ASSERT_EQ((uint32_t)2, data.Length()) << "2 entries should be found"; + + const mozilla::ipc::PrincipalInfo& info0 = data[0].principal(); + ASSERT_EQ(info0.type(), mozilla::ipc::PrincipalInfo::TContentPrincipalInfo) << "First principal must be content"; + const mozilla::ipc::ContentPrincipalInfo& cInfo0 = data[0].principal(); + + nsAutoCString suffix0; + cInfo0.attrs().CreateSuffix(suffix0); + + ASSERT_STREQ("^appId=123&inBrowser=1", suffix0.get()); + ASSERT_STREQ("scope 0", cInfo0.spec().get()); + ASSERT_STREQ("scope 0", data[0].scope().get()); + ASSERT_STREQ("currentWorkerURL 0", data[0].currentWorkerURL().get()); + ASSERT_STREQ("activeCache 0", NS_ConvertUTF16toUTF8(data[0].cacheName()).get()); + + const mozilla::ipc::PrincipalInfo& info1 = data[1].principal(); + ASSERT_EQ(info1.type(), mozilla::ipc::PrincipalInfo::TContentPrincipalInfo) << "First principal must be content"; + const mozilla::ipc::ContentPrincipalInfo& cInfo1 = data[1].principal(); + + nsAutoCString suffix1; + cInfo1.attrs().CreateSuffix(suffix1); + + ASSERT_STREQ("", suffix1.get()); + ASSERT_STREQ("scope 1", cInfo1.spec().get()); + ASSERT_STREQ("scope 1", data[1].scope().get()); + ASSERT_STREQ("currentWorkerURL 1", data[1].currentWorkerURL().get()); + ASSERT_STREQ("activeCache 1", NS_ConvertUTF16toUTF8(data[1].cacheName()).get()); +} + +TEST(ServiceWorkerRegistrar, TestVersion3Migration) +{ + nsAutoCString buffer("3" "\n"); + + buffer.Append("^appId=123&inBrowser=1\n"); + buffer.Append("spec 0\nscope 0\ncurrentWorkerURL 0\ncacheName 0\n"); + buffer.Append(SERVICEWORKERREGISTRAR_TERMINATOR "\n"); + + buffer.Append("\n"); + buffer.Append("spec 1\nscope 1\ncurrentWorkerURL 1\ncacheName 1\n"); + buffer.Append(SERVICEWORKERREGISTRAR_TERMINATOR "\n"); + + ASSERT_TRUE(CreateFile(buffer)) << "CreateFile should not fail"; + + RefPtr swr = new ServiceWorkerRegistrarTest; + + nsresult rv = swr->TestReadData(); + ASSERT_EQ(NS_OK, rv) << "ReadData() should not fail"; + + const nsTArray& data = swr->TestGetData(); + ASSERT_EQ((uint32_t)2, data.Length()) << "2 entries should be found"; + + const mozilla::ipc::PrincipalInfo& info0 = data[0].principal(); + ASSERT_EQ(info0.type(), mozilla::ipc::PrincipalInfo::TContentPrincipalInfo) << "First principal must be content"; + const mozilla::ipc::ContentPrincipalInfo& cInfo0 = data[0].principal(); + + nsAutoCString suffix0; + cInfo0.attrs().CreateSuffix(suffix0); + + ASSERT_STREQ("^appId=123&inBrowser=1", suffix0.get()); + ASSERT_STREQ("scope 0", cInfo0.spec().get()); + ASSERT_STREQ("scope 0", data[0].scope().get()); + ASSERT_STREQ("currentWorkerURL 0", data[0].currentWorkerURL().get()); + ASSERT_STREQ("cacheName 0", NS_ConvertUTF16toUTF8(data[0].cacheName()).get()); + + const mozilla::ipc::PrincipalInfo& info1 = data[1].principal(); + ASSERT_EQ(info1.type(), mozilla::ipc::PrincipalInfo::TContentPrincipalInfo) << "First principal must be content"; + const mozilla::ipc::ContentPrincipalInfo& cInfo1 = data[1].principal(); + + nsAutoCString suffix1; + cInfo1.attrs().CreateSuffix(suffix1); + + ASSERT_STREQ("", suffix1.get()); + ASSERT_STREQ("scope 1", cInfo1.spec().get()); + ASSERT_STREQ("scope 1", data[1].scope().get()); + ASSERT_STREQ("currentWorkerURL 1", data[1].currentWorkerURL().get()); + ASSERT_STREQ("cacheName 1", NS_ConvertUTF16toUTF8(data[1].cacheName()).get()); +} + +TEST(ServiceWorkerRegistrar, TestDedupeRead) +{ + nsAutoCString buffer("3" "\n"); + + // unique entries + buffer.Append("^appId=123&inBrowser=1\n"); + buffer.Append("spec 0\nscope 0\ncurrentWorkerURL 0\ncacheName 0\n"); + buffer.Append(SERVICEWORKERREGISTRAR_TERMINATOR "\n"); + + buffer.Append("\n"); + buffer.Append("spec 1\nscope 1\ncurrentWorkerURL 1\ncacheName 1\n"); + buffer.Append(SERVICEWORKERREGISTRAR_TERMINATOR "\n"); + + // dupe entries + buffer.Append("^appId=123&inBrowser=1\n"); + buffer.Append("spec 1\nscope 0\ncurrentWorkerURL 0\ncacheName 0\n"); + buffer.Append(SERVICEWORKERREGISTRAR_TERMINATOR "\n"); + + buffer.Append("^appId=123&inBrowser=1\n"); + buffer.Append("spec 2\nscope 0\ncurrentWorkerURL 0\ncacheName 0\n"); + buffer.Append(SERVICEWORKERREGISTRAR_TERMINATOR "\n"); + + buffer.Append("\n"); + buffer.Append("spec 3\nscope 1\ncurrentWorkerURL 1\ncacheName 1\n"); + buffer.Append(SERVICEWORKERREGISTRAR_TERMINATOR "\n"); + + ASSERT_TRUE(CreateFile(buffer)) << "CreateFile should not fail"; + + RefPtr swr = new ServiceWorkerRegistrarTest; + + nsresult rv = swr->TestReadData(); + ASSERT_EQ(NS_OK, rv) << "ReadData() should not fail"; + + const nsTArray& data = swr->TestGetData(); + ASSERT_EQ((uint32_t)2, data.Length()) << "2 entries should be found"; + + const mozilla::ipc::PrincipalInfo& info0 = data[0].principal(); + ASSERT_EQ(info0.type(), mozilla::ipc::PrincipalInfo::TContentPrincipalInfo) << "First principal must be content"; + const mozilla::ipc::ContentPrincipalInfo& cInfo0 = data[0].principal(); + + nsAutoCString suffix0; + cInfo0.attrs().CreateSuffix(suffix0); + + ASSERT_STREQ("^appId=123&inBrowser=1", suffix0.get()); + ASSERT_STREQ("scope 0", cInfo0.spec().get()); + ASSERT_STREQ("scope 0", data[0].scope().get()); + ASSERT_STREQ("currentWorkerURL 0", data[0].currentWorkerURL().get()); + ASSERT_STREQ("cacheName 0", NS_ConvertUTF16toUTF8(data[0].cacheName()).get()); + + const mozilla::ipc::PrincipalInfo& info1 = data[1].principal(); + ASSERT_EQ(info1.type(), mozilla::ipc::PrincipalInfo::TContentPrincipalInfo) << "First principal must be content"; + const mozilla::ipc::ContentPrincipalInfo& cInfo1 = data[1].principal(); + + nsAutoCString suffix1; + cInfo1.attrs().CreateSuffix(suffix1); + + ASSERT_STREQ("", suffix1.get()); + ASSERT_STREQ("scope 1", cInfo1.spec().get()); + ASSERT_STREQ("scope 1", data[1].scope().get()); + ASSERT_STREQ("currentWorkerURL 1", data[1].currentWorkerURL().get()); + ASSERT_STREQ("cacheName 1", NS_ConvertUTF16toUTF8(data[1].cacheName()).get()); +} + +TEST(ServiceWorkerRegistrar, TestDedupeWrite) +{ + { + RefPtr swr = new ServiceWorkerRegistrarTest; + + for (int i = 0; i < 10; ++i) { + ServiceWorkerRegistrationData reg; + + reg.scope() = NS_LITERAL_CSTRING("scope write dedupe"); + reg.currentWorkerURL() = nsPrintfCString("currentWorkerURL write %d", i); + reg.cacheName() = + NS_ConvertUTF8toUTF16(nsPrintfCString("cacheName write %d", i)); + + nsAutoCString spec; + spec.AppendPrintf("spec write dedupe/%d", i); + reg.principal() = + mozilla::ipc::ContentPrincipalInfo(mozilla::PrincipalOriginAttributes(0, false), + mozilla::void_t(), spec); + + swr->TestRegisterServiceWorker(reg); + } + + nsresult rv = swr->TestWriteData(); + ASSERT_EQ(NS_OK, rv) << "WriteData() should not fail"; + } + + RefPtr swr = new ServiceWorkerRegistrarTest; + + nsresult rv = swr->TestReadData(); + ASSERT_EQ(NS_OK, rv) << "ReadData() should not fail"; + + // Duplicate entries should be removed. + const nsTArray& data = swr->TestGetData(); + ASSERT_EQ((uint32_t)1, data.Length()) << "1 entry should be found"; + + ASSERT_EQ(data[0].principal().type(), mozilla::ipc::PrincipalInfo::TContentPrincipalInfo); + const mozilla::ipc::ContentPrincipalInfo& cInfo = data[0].principal(); + + mozilla::PrincipalOriginAttributes attrs(0, false); + nsAutoCString suffix, expectSuffix; + attrs.CreateSuffix(expectSuffix); + cInfo.attrs().CreateSuffix(suffix); + + // Last entry passed to RegisterServiceWorkerInternal() should overwrite + // previous values. So expect "9" in values here. + ASSERT_STREQ(expectSuffix.get(), suffix.get()); + ASSERT_STREQ("scope write dedupe", cInfo.spec().get()); + ASSERT_STREQ("scope write dedupe", data[0].scope().get()); + ASSERT_STREQ("currentWorkerURL write 9", data[0].currentWorkerURL().get()); + ASSERT_STREQ("cacheName write 9", + NS_ConvertUTF16toUTF8(data[0].cacheName()).get()); +} + +int main(int argc, char** argv) { + ::testing::InitGoogleTest(&argc, argv); + + int rv = RUN_ALL_TESTS(); + return rv; +} diff --git a/dom/workers/test/gtest/moz.build b/dom/workers/test/gtest/moz.build new file mode 100644 index 000000000..5f1f185a9 --- /dev/null +++ b/dom/workers/test/gtest/moz.build @@ -0,0 +1,13 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, you can obtain one at http://mozilla.org/MPL/2.0/. + +UNIFIED_SOURCES = [ + 'TestReadWrite.cpp', +] + +include('/ipc/chromium/chromium-config.mozbuild') + +FINAL_LIBRARY = 'xul-gtest' diff --git a/dom/workers/test/head.js b/dom/workers/test/head.js new file mode 100644 index 000000000..5f0c5c26e --- /dev/null +++ b/dom/workers/test/head.js @@ -0,0 +1,91 @@ +/* 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 = "http://example.com/browser/dom/workers/test/"; +const FRAME_SCRIPT_URL = getRootDirectory(gTestPath) + "frame_script.js"; + +/** + * Add a tab with given `url`, and load a frame script in it. Returns a promise + * that will be resolved when the tab finished loading. + */ +function addTab(url) { + let tab = gBrowser.addTab(TAB_URL); + gBrowser.selectedTab = tab; + let linkedBrowser = tab.linkedBrowser; + linkedBrowser.messageManager.loadFrameScript(FRAME_SCRIPT_URL, false); + return new Promise(function (resolve) { + linkedBrowser.addEventListener("load", function onload() { + linkedBrowser.removeEventListener("load", onload, true); + resolve(tab); + }, true); + }); +} + +/** + * Remove the given `tab`. + */ +function removeTab(tab) { + gBrowser.removeTab(tab); +} + +let nextId = 0; + +/** + * Send a JSON RPC request to the frame script in the given `tab`, invoking the + * given `method` with the given `params`. Returns a promise that will be + * resolved with the result of the invocation. + */ +function jsonrpc(tab, method, params) { + let currentId = nextId++; + let messageManager = tab.linkedBrowser.messageManager; + messageManager.sendAsyncMessage("jsonrpc", { + id: currentId, + method: method, + params: params + }); + return new Promise(function (resolve, reject) { + messageManager.addMessageListener("jsonrpc", function listener(event) { + let { id, result, error } = event.data; + if (id !== currentId) { + return; + } + messageManager.removeMessageListener("jsonrpc", listener); + if (error) { + reject(error); + return; + } + resolve(result); + }); + }); +} + +/** + * Create a worker with the given `url` in the given `tab`. + */ +function createWorkerInTab(tab, url) { + return jsonrpc(tab, "createWorker", [url]); +} + +/** + * Terminate the worker with the given `url` in the given `tab`. + */ +function terminateWorkerInTab(tab, url) { + return jsonrpc(tab, "terminateWorker", [url]); +} + +/** + * Post the given `message` to the worker with the given `url` in the given + * `tab`. + */ +function postMessageToWorkerInTab(tab, url, message) { + return jsonrpc(tab, "postMessageToWorker", [url, message]); +} + +/** + * Disable the cache in the given `tab`. + */ +function disableCacheInTab(tab) { + return jsonrpc(tab, "disableCache", []); +} diff --git a/dom/workers/test/importForeignScripts_worker.js b/dom/workers/test/importForeignScripts_worker.js new file mode 100644 index 000000000..5faa29c31 --- /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(e) { + if (e.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 000000000..ebf2d3b14 --- /dev/null +++ b/dom/workers/test/importScripts_3rdParty_worker.js @@ -0,0 +1,82 @@ +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 000000000..82933b091 --- /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 000000000..7176ce838 --- /dev/null +++ b/dom/workers/test/importScripts_worker.js @@ -0,0 +1,64 @@ +/** + * 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/plain,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 000000000..9c33588c4 --- /dev/null +++ b/dom/workers/test/importScripts_worker_imported1.js @@ -0,0 +1,10 @@ +/** + * 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 000000000..3aafb60be --- /dev/null +++ b/dom/workers/test/importScripts_worker_imported2.js @@ -0,0 +1,10 @@ +/** + * 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 000000000..c54be3e5f --- /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 000000000..82f8708c5 --- /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 000000000..a98255388 --- /dev/null +++ b/dom/workers/test/instanceof_worker.js @@ -0,0 +1,12 @@ +/** + * 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/json_worker.js b/dom/workers/test/json_worker.js new file mode 100644 index 000000000..f35e14cf2 --- /dev/null +++ b/dom/workers/test/json_worker.js @@ -0,0 +1,338 @@ +/** + * 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 makeCrazyNested(obj, count) { + var innermostobj; + for (var i = 0; i < count; i++) { + obj.foo = { bar: 5 } + innermostobj = obj.foo; + obj = innermostobj; + } + return innermostobj; +} + +var crazyNestedObject = {}; +makeCrazyNested(crazyNestedObject, 100); + +var crazyCyclicalObject = {}; +var innermost = makeCrazyNested(crazyCyclicalObject, 1000); +innermost.baz = crazyCyclicalObject; + +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 (var index in typedArrayWithValues) { + typedArrayWithValues[index] = index; +} + +var typedArrayWithFunBuffer = new Int8Array(4); +for (var 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: crazyNestedObject, + jsonValue: JSON.stringify(crazyNestedObject) + }, + { + type: "object", + value: crazyCyclicalObject, + }, + { + 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: 238573459843702923492399923049 + }, + { + type: "number", + shouldEqual: true, + value: -238573459843702923492399923049 + }, + { + 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: function (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: function(a) { alert(b); } }, + exception: true + }, + { + type: "object", + value: xhr, + exception: true + }, + { + type: "number", + value: xhr.readyState, + shouldEqual: true + }, + { + type: "object", + value: { xhr: xhr }, + exception: true + }, + { + type: "object", + value: self, + exception: true + }, + { + type: "object", + value: { p: ArrayBuffer.prototype }, + exception: true + }, + { + type: "string", + shouldEqual: true, + value: "testFinished" + } +]; + +for (var 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 (var 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/jsversion_worker.js b/dom/workers/test/jsversion_worker.js new file mode 100644 index 000000000..c66b72977 --- /dev/null +++ b/dom/workers/test/jsversion_worker.js @@ -0,0 +1,14 @@ +onmessage = function(evt) { + if (evt.data != 0) { + var worker = new Worker('jsversion_worker.js'); + worker.onmessage = function(evt) { + postMessage(evt.data); + } + + worker.postMessage(evt.data - 1); + return; + } + + let foo = 'bar'; + postMessage(true); +} diff --git a/dom/workers/test/loadEncoding_worker.js b/dom/workers/test/loadEncoding_worker.js new file mode 100644 index 000000000..5e4047844 --- /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 000000000..dd35ec8a3 --- /dev/null +++ b/dom/workers/test/location_worker.js @@ -0,0 +1,11 @@ +/** + * 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": string, "value": 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 000000000..f132dd8c1 --- /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/mochitest.ini b/dom/workers/test/mochitest.ini new file mode 100644 index 000000000..d4848819c --- /dev/null +++ b/dom/workers/test/mochitest.ini @@ -0,0 +1,227 @@ +[DEFAULT] +support-files = + WorkerTest_badworker.js + atob_worker.js + bug978260_worker.js + bug1014466_data1.txt + bug1014466_data2.txt + bug1014466_worker.js + bug1020226_worker.js + bug1020226_frame.html + bug998474_worker.js + bug1063538_worker.js + clearTimeouts_worker.js + content_worker.js + console_worker.js + consoleReplaceable_worker.js + csp_worker.js + csp_worker.js^headers^ + 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 + jsversion_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_console.js + sharedWorker_sharedWorker.js + simpleThread_worker.js + suspend_iframe.html + suspend_worker.js + terminate_worker.js + test_csp.html^headers^ + test_csp.js + referrer_worker.html + threadErrors_worker1.js + threadErrors_worker2.js + threadErrors_worker3.js + threadErrors_worker4.js + threadTimeouts_worker.js + throwingOnerror_worker.js + timeoutTracing_worker.js + transferable_worker.js + websocket_basic_worker.js + websocket_loadgroup_worker.js + websocket_worker1.js + websocket_worker2.js + websocket_worker3.js + websocket_worker4.js + websocket_worker5.js + websocket_helpers.js + workersDisabled_worker.js + test_worker_interfaces.js + worker_driver.js + worker_wrapper.js + bug1060621_worker.js + bug1062920_worker.js + webSocket_sharedWorker.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 + websocket_https.html + websocket_https_worker.js + worker_fileReader.js + fileapi_chromeScript.js + importScripts_3rdParty_worker.js + worker_bug1278777.js + worker_setTimeoutWith0.js + worker_bug1301094.js + script_createFile.js + worker_suspended.js + window_suspended.html + !/dom/base/test/file_bug945152.jar + !/dom/base/test/file_websocket_basic_wsh.py + !/dom/base/test/file_websocket_hello_wsh.py + !/dom/base/test/file_websocket_http_resource.txt + !/dom/base/test/file_websocket_permessage_deflate_disabled_wsh.py + !/dom/base/test/file_websocket_permessage_deflate_params_wsh.py + !/dom/base/test/file_websocket_permessage_deflate_rejected_wsh.py + !/dom/base/test/file_websocket_permessage_deflate_wsh.py + !/dom/base/test/file_websocket_wsh.py + !/dom/base/test/websocket_helpers.js + !/dom/base/test/websocket_tests.js + !/dom/tests/mochitest/notification/MockServices.js + !/dom/tests/mochitest/notification/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 + +[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] +[test_bug1104064.html] +[test_bug1132395.html] +skip-if = true # bug 1176225 +[test_bug1132924.html] +[test_chromeWorker.html] +[test_clearTimeouts.html] +[test_console.html] +[test_consoleAndBlobs.html] +[test_consoleReplaceable.html] +[test_consoleSharedWorkers.html] +[test_contentWorker.html] +[test_csp.html] +[test_dataURLWorker.html] +[test_errorPropagation.html] +[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_jsversion.html] +[test_loadEncoding.html] +[test_loadError.html] +[test_location.html] +[test_longThread.html] +[test_multi_sharedWorker.html] +[test_multi_sharedWorker_lifetimes.html] +[test_navigator.html] +[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] +[test_resolveWorker.html] +[test_resolveWorker-assignment.html] +[test_rvals.html] +[test_sharedWorker.html] +[test_simpleThread.html] +[test_suspend.html] +[test_terminate.html] +[test_threadErrors.html] +[test_threadTimeouts.html] +[test_throwingOnerror.html] +[test_timeoutTracing.html] +[test_transferable.html] +[test_websocket1.html] +skip-if = toolkit == 'android' #bug 982828 +[test_websocket2.html] +skip-if = toolkit == 'android' #bug 982828 +[test_websocket3.html] +skip-if = toolkit == 'android' #bug 982828 +[test_websocket4.html] +skip-if = toolkit == 'android' #bug 982828 +[test_websocket5.html] +skip-if = toolkit == 'android' #bug 982828 +[test_websocket_basic.html] +skip-if = toolkit == 'android' #bug 982828 +[test_websocket_https.html] +[test_websocket_loadgroup.html] +skip-if = toolkit == 'android' #bug 982828 +[test_webSocket_sharedWorker.html] +skip-if = toolkit == 'android' #bug 982828 +[test_worker_interfaces.html] +[test_workersDisabled.html] +[test_referrer.html] +[test_referrer_header_worker.html] +[test_importScripts_3rdparty.html] +[test_sharedWorker_ports.html] +[test_sharedWorker_lifetime.html] +[test_fileReader.html] +[test_navigator_workers_hardwareConcurrency.html] +[test_bug1278777.html] +[test_setTimeoutWith0.html] +[test_bug1301094.html] +[test_subworkers_suspended.html] +[test_bug1317725.html] diff --git a/dom/workers/test/multi_sharedWorker_frame.html b/dom/workers/test/multi_sharedWorker_frame.html new file mode 100644 index 000000000..6c0dfe591 --- /dev/null +++ b/dom/workers/test/multi_sharedWorker_frame.html @@ -0,0 +1,52 @@ + + + + + Test for SharedWorker + + + + + diff --git a/dom/workers/test/multi_sharedWorker_sharedWorker.js b/dom/workers/test/multi_sharedWorker_sharedWorker.js new file mode 100644 index 000000000..47a7ae04f --- /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) { + var data = { + type: "connect" + }; + + registeredPorts.forEach(function(registeredPort) { + registeredPort.postMessage(data); + }); + } + + port.onmessage = function(event) { + switch (event.data.command) { + case "start": + break; + + case "error": + throw new Error("Expected"); + + case "store": + storedData = event.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: message, + filename: filename, + lineno: 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/navigator_languages_worker.js b/dom/workers/test/navigator_languages_worker.js new file mode 100644 index 000000000..53aa4d39e --- /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 000000000..63853aef1 --- /dev/null +++ b/dom/workers/test/navigator_worker.js @@ -0,0 +1,79 @@ +/** + * 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", + "hardwareConcurrency", + { name: "storage", nightly: true }, +]; + +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) { + 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 000000000..46e6226f7 --- /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 000000000..f69acaeda --- /dev/null +++ b/dom/workers/test/notification_permission_worker.js @@ -0,0 +1,56 @@ +function info(message) { + dump("INFO: " + message + "\n"); +} + +function ok(test, message) { + postMessage({ type: 'ok', test: test, message: message }); +} + +function is(a, b, message) { + postMessage({ type: 'is', test1: a, test2: b, message: 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 (e) { + ok(false, "Test threw exception! " + nextTest + " " + e); + 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 000000000..cb55f8358 --- /dev/null +++ b/dom/workers/test/notification_worker.js @@ -0,0 +1,93 @@ +function ok(test, message) { + postMessage({ type: 'ok', test: test, message: message }); +} + +function is(a, b, message) { + postMessage({ type: 'is', test1: a, test2: b, message: 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 (e) { + ok(false, "Test threw exception! " + nextTest + " " + e); + 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 000000000..829bf43d3 --- /dev/null +++ b/dom/workers/test/notification_worker_child-child.js @@ -0,0 +1,92 @@ +function ok(test, message) { + postMessage({ type: 'ok', test: test, message: message }); +} + +function is(a, b, message) { + postMessage({ type: 'is', test1: a, test2: b, message: 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 (e) { + ok(false, "Test threw exception! " + nextTest + " " + e); + 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 000000000..bb28c520d --- /dev/null +++ b/dom/workers/test/notification_worker_child-parent.js @@ -0,0 +1,26 @@ +function ok(test, message) { + postMessage({ type: 'ok', test: test, message: message }); +} + +function is(a, b, message) { + postMessage({ type: 'is', test1: a, test2: b, message: 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 000000000..5a302b15e --- /dev/null +++ b/dom/workers/test/onLine_worker.js @@ -0,0 +1,65 @@ +/* + * 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 000000000..dc28fac45 --- /dev/null +++ b/dom/workers/test/onLine_worker_child.js @@ -0,0 +1,75 @@ +/* + * 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: test, message: 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 000000000..4800457d3 --- /dev/null +++ b/dom/workers/test/onLine_worker_head.js @@ -0,0 +1,43 @@ +/* + * 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: test, message: 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 000000000..5b0a8478b --- /dev/null +++ b/dom/workers/test/promise_worker.js @@ -0,0 +1,856 @@ +function ok(a, msg) { + dump("OK: " + !!a + " => " + a + " " + msg + "\n"); + postMessage({type: 'status', status: !!a, msg: a + ": " + msg }); +} + +function todo(a, msg) { + dump("TODO: " + !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(resolve, reject) { + ok(true, "Nested promise is executed"); + resolve(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(resolve, reject) { + ok(true, "Nested promise is executed"); + resolve(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(r1, r2) { + ok(true, "Nested nested promise is executed"); + r1(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 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 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: function(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 000000000..1a61ec9d1 --- /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 000000000..e6656d68f --- /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 000000000..1a5e24a59 --- /dev/null +++ b/dom/workers/test/redirect_to_foreign.sjs @@ -0,0 +1,4 @@ +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 000000000..dcfd1fe37 --- /dev/null +++ b/dom/workers/test/referrer.sjs @@ -0,0 +1,15 @@ +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.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 000000000..550fefb8a --- /dev/null +++ b/dom/workers/test/referrer_test_server.sjs @@ -0,0 +1,101 @@ +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 000000000..5693fc340 --- /dev/null +++ b/dom/workers/test/referrer_worker.html @@ -0,0 +1,145 @@ + + + + + + + + diff --git a/dom/workers/test/rvals_worker.js b/dom/workers/test/rvals_worker.js new file mode 100644 index 000000000..51f9ae723 --- /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 000000000..aee7df10e --- /dev/null +++ b/dom/workers/test/script_createFile.js @@ -0,0 +1,15 @@ +var { classes: Cc, interfaces: Ci, utils: Cu } = Components; +Cu.importGlobalProperties(["File", "Directory"]); + +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(Components.interfaces.nsIFile.FILE_TYPE, 0o600); + + sendAsyncMessage("file.opened", { + data: File.createFromNsIFile(tmpFile) + }); +}); diff --git a/dom/workers/test/serviceworkers/activate_event_error_worker.js b/dom/workers/test/serviceworkers/activate_event_error_worker.js new file mode 100644 index 000000000..a5f159d35 --- /dev/null +++ b/dom/workers/test/serviceworkers/activate_event_error_worker.js @@ -0,0 +1,4 @@ +// Worker that errors on receiving an activate event. +onactivate = function(e) { + undefined.doSomething; +} diff --git a/dom/workers/test/serviceworkers/blocking_install_event_worker.js b/dom/workers/test/serviceworkers/blocking_install_event_worker.js new file mode 100644 index 000000000..0bc6f7b7f --- /dev/null +++ b/dom/workers/test/serviceworkers/blocking_install_event_worker.js @@ -0,0 +1,23 @@ +function postMessageToTest(msg) { + return clients.matchAll({ includeUncontrolled: true }) + .then(list => { + for (var client of list) { + if (client.url.endsWith('test_install_event_gc.html')) { + client.postMessage(msg); + break; + } + } + }); +} + +addEventListener('install', evt => { + // This must be a simple promise to trigger the CC failure. + evt.waitUntil(new Promise(function() { })); + postMessageToTest({ type: 'INSTALL_EVENT' }); +}); + +addEventListener('message', evt => { + if (evt.data.type === 'ping') { + postMessageToTest({ type: 'pong' }); + } +}); diff --git a/dom/workers/test/serviceworkers/browser.ini b/dom/workers/test/serviceworkers/browser.ini new file mode 100644 index 000000000..c0aae30d6 --- /dev/null +++ b/dom/workers/test/serviceworkers/browser.ini @@ -0,0 +1,10 @@ +[DEFAULT] +support-files = + browser_base_force_refresh.html + browser_cached_force_refresh.html + download/window.html + download/worker.js + force_refresh_browser_worker.js + +[browser_force_refresh.js] +[browser_download.js] diff --git a/dom/workers/test/serviceworkers/browser_base_force_refresh.html b/dom/workers/test/serviceworkers/browser_base_force_refresh.html new file mode 100644 index 000000000..1b0d2defe --- /dev/null +++ b/dom/workers/test/serviceworkers/browser_base_force_refresh.html @@ -0,0 +1,30 @@ + + + + + + + + + diff --git a/dom/workers/test/serviceworkers/browser_cached_force_refresh.html b/dom/workers/test/serviceworkers/browser_cached_force_refresh.html new file mode 100644 index 000000000..33bd8cdaa --- /dev/null +++ b/dom/workers/test/serviceworkers/browser_cached_force_refresh.html @@ -0,0 +1,64 @@ + + + + + + + + + diff --git a/dom/workers/test/serviceworkers/browser_download.js b/dom/workers/test/serviceworkers/browser_download.js new file mode 100644 index 000000000..bd4da10c9 --- /dev/null +++ b/dom/workers/test/serviceworkers/browser_download.js @@ -0,0 +1,83 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +Cu.import('resource://gre/modules/Services.jsm'); +var Downloads = Cu.import("resource://gre/modules/Downloads.jsm", {}).Downloads; +var DownloadsCommon = Cu.import("resource:///modules/DownloadsCommon.jsm", {}).DownloadsCommon; +Cu.import('resource://gre/modules/NetUtil.jsm'); + +var gTestRoot = getRootDirectory(gTestPath).replace("chrome://mochitests/content/", + "http://mochi.test:8888/") + +function getFile(aFilename) { + if (aFilename.startsWith('file:')) { + var url = NetUtil.newURI(aFilename).QueryInterface(Ci.nsIFileURL); + return url.file.clone(); + } + + var file = Cc['@mozilla.org/file/local;1'].createInstance(Ci.nsIFile); + file.initWithPath(aFilename); + return file; +} + +function windowObserver(win, topic) { + if (topic !== 'domwindowopened') { + return; + } + + win.addEventListener('load', function onLoadWindow() { + win.removeEventListener('load', onLoadWindow, false); + if (win.document.documentURI === + 'chrome://mozapps/content/downloads/unknownContentType.xul') { + executeSoon(function() { + var button = win.document.documentElement.getButton('accept'); + button.disabled = false; + win.document.documentElement.acceptDialog(); + }); + } + }, false); +} + +function test() { + waitForExplicitFinish(); + + Services.ww.registerNotification(windowObserver); + + SpecialPowers.pushPrefEnv({'set': [['dom.serviceWorkers.enabled', true], + ['dom.serviceWorkers.exemptFromPerDomainMax', true], + ['dom.serviceWorkers.testing.enabled', true]]}, + function() { + var url = gTestRoot + 'download/window.html'; + var tab = gBrowser.addTab(); + gBrowser.selectedTab = tab; + + Downloads.getList(Downloads.ALL).then(function(downloadList) { + var downloadListener; + + function downloadVerifier(aDownload) { + if (aDownload.succeeded) { + var file = getFile(aDownload.target.path); + ok(file.exists(), 'download completed'); + is(file.fileSize, 33, 'downloaded file has correct size'); + file.remove(false); + DownloadsCommon.removeAndFinalizeDownload(aDownload); + + downloadList.removeView(downloadListener); + gBrowser.removeTab(tab); + Services.ww.unregisterNotification(windowObserver); + + executeSoon(finish); + } + } + + downloadListener = { + onDownloadAdded: downloadVerifier, + onDownloadChanged: downloadVerifier + }; + + return downloadList.addView(downloadListener); + }).then(function() { + gBrowser.loadURI(url); + }); + }); +} diff --git a/dom/workers/test/serviceworkers/browser_force_refresh.js b/dom/workers/test/serviceworkers/browser_force_refresh.js new file mode 100644 index 000000000..a2c9c871c --- /dev/null +++ b/dom/workers/test/serviceworkers/browser_force_refresh.js @@ -0,0 +1,91 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +var gTestRoot = getRootDirectory(gTestPath).replace("chrome://mochitests/content/", + "http://mochi.test:8888/") + +function refresh() { + EventUtils.synthesizeKey('R', { accelKey: true }); +} + +function forceRefresh() { + EventUtils.synthesizeKey('R', { accelKey: true, shiftKey: true }); +} + +function frameScript() { + function eventHandler(event) { + sendAsyncMessage("test:event", {type: event.type, detail: event.detail}); + } + + // These are tab-local, so no need to unregister them. + addEventListener('base-load', eventHandler, true, true); + addEventListener('base-register', eventHandler, true, true); + addEventListener('base-sw-ready', eventHandler, true, true); + addEventListener('cached-load', eventHandler, true, true); + addEventListener('cached-failure', eventHandler, true, true); +} + +function test() { + waitForExplicitFinish(); + SpecialPowers.pushPrefEnv({'set': [['dom.serviceWorkers.enabled', true], + ['dom.serviceWorkers.exemptFromPerDomainMax', true], + ['dom.serviceWorkers.testing.enabled', true], + ['dom.caches.enabled', true], + ['browser.cache.disk.enable', false], + ['browser.cache.memory.enable', false]]}, + function() { + var url = gTestRoot + 'browser_base_force_refresh.html'; + var tab = gBrowser.addTab(); + gBrowser.selectedTab = tab; + + tab.linkedBrowser.messageManager.loadFrameScript("data:,(" + encodeURIComponent(frameScript) + ")()", true); + gBrowser.loadURI(url); + + function done() { + tab.linkedBrowser.messageManager.removeMessageListener("test:event", eventHandler); + + gBrowser.removeTab(tab); + executeSoon(finish); + } + + var maxCacheLoadCount = 3; + var cachedLoadCount = 0; + var baseLoadCount = 0; + + function eventHandler(msg) { + if (msg.data.type === 'base-load') { + baseLoadCount += 1; + if (cachedLoadCount === maxCacheLoadCount) { + is(baseLoadCount, 2, 'cached load should occur before second base load'); + return done(); + } + if (baseLoadCount !== 1) { + ok(false, 'base load without cached load should only occur once'); + return done(); + } + } else if (msg.data.type === 'base-register') { + ok(!cachedLoadCount, 'cached load should not occur before base register'); + is(baseLoadCount, 1, 'register should occur after first base load'); + } else if (msg.data.type === 'base-sw-ready') { + ok(!cachedLoadCount, 'cached load should not occur before base ready'); + is(baseLoadCount, 1, 'ready should occur after first base load'); + refresh(); + } else if (msg.data.type === 'cached-load') { + ok(cachedLoadCount < maxCacheLoadCount, 'cached load should not occur too many times'); + is(baseLoadCount, 1, 'cache load occur after first base load'); + cachedLoadCount += 1; + if (cachedLoadCount < maxCacheLoadCount) { + return refresh(); + } + forceRefresh(); + } else if (msg.data.type === 'cached-failure') { + ok(false, 'failure: ' + msg.data.detail); + done(); + } + + return; + } + + tab.linkedBrowser.messageManager.addMessageListener("test:event", eventHandler); + }); +} diff --git a/dom/workers/test/serviceworkers/bug1151916_driver.html b/dom/workers/test/serviceworkers/bug1151916_driver.html new file mode 100644 index 000000000..e540ad239 --- /dev/null +++ b/dom/workers/test/serviceworkers/bug1151916_driver.html @@ -0,0 +1,53 @@ + + + + + diff --git a/dom/workers/test/serviceworkers/bug1151916_worker.js b/dom/workers/test/serviceworkers/bug1151916_worker.js new file mode 100644 index 000000000..06585e8e7 --- /dev/null +++ b/dom/workers/test/serviceworkers/bug1151916_worker.js @@ -0,0 +1,13 @@ +onactivate = function(e) { + e.waitUntil(self.caches.open("default-cache").then(function(cache) { + var response = new Response("Hi there"); + return cache.put("madeup.txt", response); + })); +} + +onfetch = function(e) { + if (e.request.url.match(/madeup.txt$/)) { + var p = self.caches.match("madeup.txt", { cacheName: "default-cache" }); + e.respondWith(p); + } +} diff --git a/dom/workers/test/serviceworkers/bug1240436_worker.js b/dom/workers/test/serviceworkers/bug1240436_worker.js new file mode 100644 index 000000000..5a588aedf --- /dev/null +++ b/dom/workers/test/serviceworkers/bug1240436_worker.js @@ -0,0 +1,2 @@ +// a contains a ZERO WIDTH JOINER (0x200D) +var a = "â€"; \ No newline at end of file diff --git a/dom/workers/test/serviceworkers/chrome.ini b/dom/workers/test/serviceworkers/chrome.ini new file mode 100644 index 000000000..e064e7fd0 --- /dev/null +++ b/dom/workers/test/serviceworkers/chrome.ini @@ -0,0 +1,16 @@ +[DEFAULT] +skip-if = os == 'android' +support-files = + chrome_helpers.js + empty.js + serviceworker.html + serviceworkerinfo_iframe.html + serviceworkermanager_iframe.html + serviceworkerregistrationinfo_iframe.html + worker.js + worker2.js + +[test_privateBrowsing.html] +[test_serviceworkerinfo.xul] +[test_serviceworkermanager.xul] +[test_serviceworkerregistrationinfo.xul] diff --git a/dom/workers/test/serviceworkers/chrome_helpers.js b/dom/workers/test/serviceworkers/chrome_helpers.js new file mode 100644 index 000000000..a438333e2 --- /dev/null +++ b/dom/workers/test/serviceworkers/chrome_helpers.js @@ -0,0 +1,74 @@ +let { classes: Cc, interfaces: Ci, utils: Cu } = Components; + +Cu.import("resource://gre/modules/Task.jsm"); + +let swm = Cc["@mozilla.org/serviceworkers/manager;1"]. + getService(Ci.nsIServiceWorkerManager); + +let EXAMPLE_URL = "https://example.com/chrome/dom/workers/test/serviceworkers/"; + +function waitForIframeLoad(iframe) { + return new Promise(function (resolve) { + iframe.onload = resolve; + }); +} + +function waitForRegister(scope, callback) { + return new Promise(function (resolve) { + let listener = { + onRegister: function (registration) { + if (registration.scope !== scope) { + return; + } + swm.removeListener(listener); + resolve(callback ? callback(registration) : registration); + } + }; + swm.addListener(listener); + }); +} + +function waitForUnregister(scope) { + return new Promise(function (resolve) { + let listener = { + onUnregister: function (registration) { + if (registration.scope !== scope) { + return; + } + swm.removeListener(listener); + resolve(registration); + } + }; + swm.addListener(listener); + }); +} + +function waitForServiceWorkerRegistrationChange(registration, callback) { + return new Promise(function (resolve) { + let listener = { + onChange: function () { + registration.removeListener(listener); + if (callback) { + callback(); + } + resolve(callback ? callback() : undefined); + } + }; + registration.addListener(listener); + }); +} + +function waitForServiceWorkerShutdown() { + return new Promise(function (resolve) { + let observer = { + observe: function (subject, topic, data) { + if (topic !== "service-worker-shutdown") { + return; + } + SpecialPowers.removeObserver(observer, "service-worker-shutdown"); + resolve(); + } + }; + SpecialPowers.addObserver(observer, "service-worker-shutdown", false); + }); +} diff --git a/dom/workers/test/serviceworkers/claim_clients/client.html b/dom/workers/test/serviceworkers/claim_clients/client.html new file mode 100644 index 000000000..eecfb294e --- /dev/null +++ b/dom/workers/test/serviceworkers/claim_clients/client.html @@ -0,0 +1,44 @@ + + + + + Bug 1130684 - claim client + + + + +

+ +

+
+
+
+
+
diff --git a/dom/workers/test/serviceworkers/claim_fetch_worker.js b/dom/workers/test/serviceworkers/claim_fetch_worker.js
new file mode 100644
index 000000000..ea62c37b8
--- /dev/null
+++ b/dom/workers/test/serviceworkers/claim_fetch_worker.js
@@ -0,0 +1,12 @@
+onfetch = function(e) {
+  if (e.request.url.indexOf("service_worker_controlled") >= 0) {
+    // pass through
+    e.respondWith(fetch(e.request));
+  } else {
+    e.respondWith(new Response("Fetch was intercepted"));
+  }
+}
+
+onmessage = function(e) {
+  clients.claim();
+}
diff --git a/dom/workers/test/serviceworkers/claim_oninstall_worker.js b/dom/workers/test/serviceworkers/claim_oninstall_worker.js
new file mode 100644
index 000000000..269afa721
--- /dev/null
+++ b/dom/workers/test/serviceworkers/claim_oninstall_worker.js
@@ -0,0 +1,7 @@
+oninstall = function(e) {
+  var claimFailedPromise = new Promise(function(resolve, reject) {
+    clients.claim().then(reject, () => resolve());
+  });
+
+  e.waitUntil(claimFailedPromise);
+}
diff --git a/dom/workers/test/serviceworkers/claim_worker_1.js b/dom/workers/test/serviceworkers/claim_worker_1.js
new file mode 100644
index 000000000..e5f6392d3
--- /dev/null
+++ b/dom/workers/test/serviceworkers/claim_worker_1.js
@@ -0,0 +1,28 @@
+onactivate = function(e) {
+  var result = {
+    resolve_value: false,
+    match_count_before: -1,
+    match_count_after: -1,
+    message: "claim_worker_1"
+  };
+
+  self.clients.matchAll().then(function(matched) {
+    // should be 0
+    result.match_count_before = matched.length;
+  }).then(function() {
+    var claimPromise = self.clients.claim().then(function(ret) {
+      result.resolve_value = ret;
+    });
+
+    return claimPromise.then(self.clients.matchAll().then(function(matched) {
+      // should be 2
+      result.match_count_after = matched.length;
+      for (i = 0; i < matched.length; i++) {
+        matched[i].postMessage(result);
+      }
+      if (result.match_count_after !== 2) {
+        dump("ERROR: claim_worker_1 failed to capture clients.\n");
+      }
+    }));
+  });
+}
diff --git a/dom/workers/test/serviceworkers/claim_worker_2.js b/dom/workers/test/serviceworkers/claim_worker_2.js
new file mode 100644
index 000000000..be8281d34
--- /dev/null
+++ b/dom/workers/test/serviceworkers/claim_worker_2.js
@@ -0,0 +1,27 @@
+onactivate = function(e) {
+  var result = {
+    resolve_value: false,
+    match_count_before: -1,
+    match_count_after: -1,
+    message: "claim_worker_2"
+  };
+
+  self.clients.matchAll().then(function(matched) {
+    // should be 0
+    result.match_count_before = matched.length;
+  }).then(function() {
+    var claimPromise = self.clients.claim().then(function(ret) {
+      result.resolve_value = ret;
+    });
+
+    return claimPromise.then(self.clients.matchAll().then(function(matched) {
+      // should be 1
+      result.match_count_after = matched.length;
+      if (result.match_count_after === 1) {
+        matched[0].postMessage(result);
+      } else {
+        dump("ERROR: claim_worker_2 failed to capture clients.\n");
+      }
+    }));
+  });
+}
diff --git a/dom/workers/test/serviceworkers/close_test.js b/dom/workers/test/serviceworkers/close_test.js
new file mode 100644
index 000000000..6138d6421
--- /dev/null
+++ b/dom/workers/test/serviceworkers/close_test.js
@@ -0,0 +1,19 @@
+function ok(v, msg) {
+  client.postMessage({status: "ok", result: !!v, message: msg});
+}
+
+var client;
+onmessage = function(e) {
+  if (e.data.message == "start") {
+    self.clients.matchAll().then(function(clients) {
+      client = clients[0];
+      try {
+        close();
+        ok(false, "close() should throw");
+      } catch (e) {
+        ok(e.name === "InvalidAccessError", "close() should throw InvalidAccessError");
+      }
+      client.postMessage({status: "done"});
+    });
+  }
+}
diff --git a/dom/workers/test/serviceworkers/controller/index.html b/dom/workers/test/serviceworkers/controller/index.html
new file mode 100644
index 000000000..740e24f15
--- /dev/null
+++ b/dom/workers/test/serviceworkers/controller/index.html
@@ -0,0 +1,74 @@
+
+
+
+
+  Bug 94048 - test install event.
+  
+  
+
+
+

+ +

+
+
+
+
+
+
diff --git a/dom/workers/test/serviceworkers/create_another_sharedWorker.html b/dom/workers/test/serviceworkers/create_another_sharedWorker.html
new file mode 100644
index 000000000..f49194fa5
--- /dev/null
+++ b/dom/workers/test/serviceworkers/create_another_sharedWorker.html
@@ -0,0 +1,6 @@
+
+Shared workers: create antoehr sharedworekr client
+
Hello World
+ diff --git a/dom/workers/test/serviceworkers/download/window.html b/dom/workers/test/serviceworkers/download/window.html new file mode 100644 index 000000000..7d7893e0e --- /dev/null +++ b/dom/workers/test/serviceworkers/download/window.html @@ -0,0 +1,46 @@ + + + + + + + + + diff --git a/dom/workers/test/serviceworkers/download/worker.js b/dom/workers/test/serviceworkers/download/worker.js new file mode 100644 index 000000000..fe46d1a3b --- /dev/null +++ b/dom/workers/test/serviceworkers/download/worker.js @@ -0,0 +1,30 @@ +addEventListener('install', function(evt) { + evt.waitUntil(self.skipWaiting()); +}); + +addEventListener('activate', function(evt) { + // We claim the current clients in order to ensure that we have an + // active client when we call unregister in the fetch handler. Otherwise + // the unregister() can kill the current worker before returning a + // response. + evt.waitUntil(clients.claim()); +}); + +addEventListener('fetch', function(evt) { + // This worker may live long enough to receive a fetch event from the next + // test. Just pass such requests through to the network. + if (evt.request.url.indexOf('fake_download') === -1) { + return; + } + + // We should only get a single download fetch event. Automatically unregister. + evt.respondWith(registration.unregister().then(function() { + return new Response('service worker generated download', { + headers: { + 'Content-Disposition': 'attachment; filename="fake_download.bin"', + // fake encoding header that should have no effect + 'Content-Encoding': 'gzip', + } + }); + })); +}); diff --git a/dom/workers/test/serviceworkers/empty.js b/dom/workers/test/serviceworkers/empty.js new file mode 100644 index 000000000..e69de29bb diff --git a/dom/workers/test/serviceworkers/error_reporting_helpers.js b/dom/workers/test/serviceworkers/error_reporting_helpers.js new file mode 100644 index 000000000..fbc4ca6fc --- /dev/null +++ b/dom/workers/test/serviceworkers/error_reporting_helpers.js @@ -0,0 +1,68 @@ +"use strict"; + +/** + * Helpers for use in tests that want to verify that localized error messages + * are logged during the test. Because most of our errors (ex: + * ServiceWorkerManager) generate nsIScriptError instances with flattened + * strings (the interpolated arguments aren't kept around), we load the string + * bundle and use it to derive the exact string message we expect for the given + * payload. + **/ + +let stringBundleService = + SpecialPowers.Cc["@mozilla.org/intl/stringbundle;1"] + .getService(SpecialPowers.Ci.nsIStringBundleService); +let localizer = + stringBundleService.createBundle("chrome://global/locale/dom/dom.properties"); + +/** + * Start monitoring the console for the given localized error message string(s) + * with the given arguments to be logged. Call before running code that will + * generate the console message. Pair with a call to + * `wait_for_expected_message` invoked after the message should have been + * generated. + * + * Multiple error messages can be expected, just repeat the msgId and args + * argument pair as needed. + * + * @param {String} msgId + * The localization message identifier used in the properties file. + * @param {String[]} args + * The list of formatting arguments we expect the error to be generated with. + * @return {Object} Promise/handle to pass to wait_for_expected_message. + */ +function expect_console_message(/* msgId, args, ... */) { + let expectations = []; + // process repeated paired arguments of: msgId, args + for (let i = 0; i < arguments.length; i += 2) { + let msgId = arguments[i]; + let args = arguments[i + 1]; + expectations.push({ + errorMessage: localizer.formatStringFromName(msgId, args, args.length) + }); + } + return new Promise(resolve => { + SimpleTest.monitorConsole(resolve, expectations); + }); +} +let expect_console_messages = expect_console_message; + +/** + * Stop monitoring the console, returning a Promise that will be resolved when + * the sentinel console message sent through the async data path has been + * received. The Promise will not reject on failure; instead a mochitest + * failure will have been generated by ok(false)/equivalent by the time it is + * resolved. + */ +function wait_for_expected_message(expectedPromise) { + SimpleTest.endMonitorConsole(); + return expectedPromise; +} + +/** + * Derive an absolute URL string from a relative URL to simplify error message + * argument generation. + */ +function make_absolute_url(relUrl) { + return new URL(relUrl, window.location).href; +} diff --git a/dom/workers/test/serviceworkers/eval_worker.js b/dom/workers/test/serviceworkers/eval_worker.js new file mode 100644 index 000000000..c60f2f637 --- /dev/null +++ b/dom/workers/test/serviceworkers/eval_worker.js @@ -0,0 +1 @@ +eval('1+1'); diff --git a/dom/workers/test/serviceworkers/eventsource/eventsource.resource b/dom/workers/test/serviceworkers/eventsource/eventsource.resource new file mode 100644 index 000000000..eb62cbd4c --- /dev/null +++ b/dom/workers/test/serviceworkers/eventsource/eventsource.resource @@ -0,0 +1,22 @@ +:this file must be enconded in utf8 +:and its Content-Type must be equal to text/event-stream + +retry:500 +data: 2 +unknow: unknow + +event: other_event_name +retry:500 +data: 2 +unknow: unknow + +event: click +retry:500 + +event: blur +retry:500 + +event:keypress +retry:500 + + diff --git a/dom/workers/test/serviceworkers/eventsource/eventsource.resource^headers^ b/dom/workers/test/serviceworkers/eventsource/eventsource.resource^headers^ new file mode 100644 index 000000000..5b88be7c3 --- /dev/null +++ b/dom/workers/test/serviceworkers/eventsource/eventsource.resource^headers^ @@ -0,0 +1,3 @@ +Content-Type: text/event-stream +Cache-Control: no-cache, must-revalidate +Access-Control-Allow-Origin: * diff --git a/dom/workers/test/serviceworkers/eventsource/eventsource_cors_response.html b/dom/workers/test/serviceworkers/eventsource/eventsource_cors_response.html new file mode 100644 index 000000000..7c6f7302f --- /dev/null +++ b/dom/workers/test/serviceworkers/eventsource/eventsource_cors_response.html @@ -0,0 +1,75 @@ + + + + + Bug 1182103 - Test EventSource scenarios with fetch interception + + + + + diff --git a/dom/workers/test/serviceworkers/eventsource/eventsource_cors_response_intercept_worker.js b/dom/workers/test/serviceworkers/eventsource/eventsource_cors_response_intercept_worker.js new file mode 100644 index 000000000..579e9f568 --- /dev/null +++ b/dom/workers/test/serviceworkers/eventsource/eventsource_cors_response_intercept_worker.js @@ -0,0 +1,20 @@ +// Cross origin request +var prefix = 'http://example.com/tests/dom/workers/test/serviceworkers/eventsource/'; + +self.importScripts('eventsource_worker_helper.js'); + +self.addEventListener('fetch', function (event) { + var request = event.request; + var url = new URL(request.url); + + if (url.pathname !== '/tests/dom/workers/test/serviceworkers/eventsource/eventsource.resource') { + return; + } + + ok(request.mode === 'cors', 'EventSource should make a CORS request'); + ok(request.cache === 'no-store', 'EventSource should make a no-store request'); + var fetchRequest = new Request(prefix + 'eventsource.resource', { mode: 'cors'}); + event.respondWith(fetch(fetchRequest).then((fetchResponse) => { + return fetchResponse; + })); +}); diff --git a/dom/workers/test/serviceworkers/eventsource/eventsource_mixed_content_cors_response.html b/dom/workers/test/serviceworkers/eventsource/eventsource_mixed_content_cors_response.html new file mode 100644 index 000000000..f6ae0d96f --- /dev/null +++ b/dom/workers/test/serviceworkers/eventsource/eventsource_mixed_content_cors_response.html @@ -0,0 +1,75 @@ + + + + + Bug 1182103 - Test EventSource scenarios with fetch interception + + + + + diff --git a/dom/workers/test/serviceworkers/eventsource/eventsource_mixed_content_cors_response_intercept_worker.js b/dom/workers/test/serviceworkers/eventsource/eventsource_mixed_content_cors_response_intercept_worker.js new file mode 100644 index 000000000..187d0bc6f --- /dev/null +++ b/dom/workers/test/serviceworkers/eventsource/eventsource_mixed_content_cors_response_intercept_worker.js @@ -0,0 +1,19 @@ +var prefix = 'http://example.com/tests/dom/workers/test/serviceworkers/eventsource/'; + +self.importScripts('eventsource_worker_helper.js'); + +self.addEventListener('fetch', function (event) { + var request = event.request; + var url = new URL(request.url); + + if (url.pathname !== '/tests/dom/workers/test/serviceworkers/eventsource/eventsource.resource') { + return; + } + + ok(request.mode === 'cors', 'EventSource should make a CORS request'); + ok(request.cache === 'no-store', 'EventSource should make a no-store request'); + var fetchRequest = new Request(prefix + 'eventsource.resource', { mode: 'cors'}); + event.respondWith(fetch(fetchRequest).then((fetchResponse) => { + return fetchResponse; + })); +}); diff --git a/dom/workers/test/serviceworkers/eventsource/eventsource_opaque_response.html b/dom/workers/test/serviceworkers/eventsource/eventsource_opaque_response.html new file mode 100644 index 000000000..f92811e63 --- /dev/null +++ b/dom/workers/test/serviceworkers/eventsource/eventsource_opaque_response.html @@ -0,0 +1,75 @@ + + + + + Bug 1182103 - Test EventSource scenarios with fetch interception + + + + + diff --git a/dom/workers/test/serviceworkers/eventsource/eventsource_opaque_response_intercept_worker.js b/dom/workers/test/serviceworkers/eventsource/eventsource_opaque_response_intercept_worker.js new file mode 100644 index 000000000..45a80e324 --- /dev/null +++ b/dom/workers/test/serviceworkers/eventsource/eventsource_opaque_response_intercept_worker.js @@ -0,0 +1,20 @@ +// Cross origin request +var prefix = 'http://example.com/tests/dom/workers/test/serviceworkers/eventsource/'; + +self.importScripts('eventsource_worker_helper.js'); + +self.addEventListener('fetch', function (event) { + var request = event.request; + var url = new URL(request.url); + + if (url.pathname !== '/tests/dom/workers/test/serviceworkers/eventsource/eventsource.resource') { + return; + } + + ok(request.mode === 'cors', 'EventSource should make a CORS request'); + ok(request.cache === 'no-store', 'EventSource should make a no-store request'); + var fetchRequest = new Request(prefix + 'eventsource.resource', { mode: 'no-cors'}); + event.respondWith(fetch(fetchRequest).then((fetchResponse) => { + return fetchResponse; + })); +}); diff --git a/dom/workers/test/serviceworkers/eventsource/eventsource_register_worker.html b/dom/workers/test/serviceworkers/eventsource/eventsource_register_worker.html new file mode 100644 index 000000000..59e8e92ab --- /dev/null +++ b/dom/workers/test/serviceworkers/eventsource/eventsource_register_worker.html @@ -0,0 +1,27 @@ + + + + + Bug 1182103 - Test EventSource scenarios with fetch interception + + + + + diff --git a/dom/workers/test/serviceworkers/eventsource/eventsource_synthetic_response.html b/dom/workers/test/serviceworkers/eventsource/eventsource_synthetic_response.html new file mode 100644 index 000000000..d9f380e2f --- /dev/null +++ b/dom/workers/test/serviceworkers/eventsource/eventsource_synthetic_response.html @@ -0,0 +1,75 @@ + + + + + Bug 1182103 - Test EventSource scenarios with fetch interception + + + + + diff --git a/dom/workers/test/serviceworkers/eventsource/eventsource_synthetic_response_intercept_worker.js b/dom/workers/test/serviceworkers/eventsource/eventsource_synthetic_response_intercept_worker.js new file mode 100644 index 000000000..8692f9186 --- /dev/null +++ b/dom/workers/test/serviceworkers/eventsource/eventsource_synthetic_response_intercept_worker.js @@ -0,0 +1,24 @@ +self.importScripts('eventsource_worker_helper.js'); + +self.addEventListener('fetch', function (event) { + var request = event.request; + var url = new URL(request.url); + + if (url.pathname !== '/tests/dom/workers/test/serviceworkers/eventsource/eventsource.resource') { + return; + } + + ok(request.mode === 'cors', 'EventSource should make a CORS request'); + var headerList = { + 'Content-Type': 'text/event-stream', + 'Cache-Control': 'no-cache, must-revalidate' + }; + var headers = new Headers(headerList); + var init = { + headers: headers, + mode: 'cors' + }; + var body = 'data: data0\r\r'; + var response = new Response(body, init); + event.respondWith(response); +}); diff --git a/dom/workers/test/serviceworkers/eventsource/eventsource_worker_helper.js b/dom/workers/test/serviceworkers/eventsource/eventsource_worker_helper.js new file mode 100644 index 000000000..6d5dbb024 --- /dev/null +++ b/dom/workers/test/serviceworkers/eventsource/eventsource_worker_helper.js @@ -0,0 +1,12 @@ +function ok(aCondition, aMessage) { + return new Promise(function(resolve, reject) { + self.clients.matchAll().then(function(res) { + if (!res.length) { + reject(); + return; + } + res[0].postMessage({status: "callback", data: "ok", condition: aCondition, message: aMessage}); + resolve(); + }); + }); +} diff --git a/dom/workers/test/serviceworkers/fetch.js b/dom/workers/test/serviceworkers/fetch.js new file mode 100644 index 000000000..38d20a638 --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch.js @@ -0,0 +1,11 @@ +addEventListener('fetch', function(event) { + if (event.request.url.indexOf("fail.html") !== -1) { + event.respondWith(fetch("hello.html", {"integrity": "abc"})); + } else if (event.request.url.indexOf("fake.html") !== -1) { + event.respondWith(fetch("hello.html")); + } +}); + +addEventListener("activate", function(event) { + event.waitUntil(clients.claim()); +}); diff --git a/dom/workers/test/serviceworkers/fetch/context/beacon.sjs b/dom/workers/test/serviceworkers/fetch/context/beacon.sjs new file mode 100644 index 000000000..8401bc29b --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/context/beacon.sjs @@ -0,0 +1,43 @@ +/* + * This is based on dom/tests/mochitest/beacon/beacon-originheader-handler.sjs. + */ + +function handleRequest(request, response) +{ + response.setHeader("Cache-Control", "no-cache", false); + response.setHeader("Content-Type", "text/plain", false); + + // case XHR-REQUEST: the xhr-request tries to query the + // stored context from the beacon request. + if (request.queryString == "queryContext") { + var context = getState("interceptContext"); + // if the beacon already stored the context - return. + if (context) { + response.write(context); + setState("interceptContext", ""); + return; + } + // otherwise wait for the beacon request + response.processAsync(); + setObjectState("sw-xhr-response", response); + return; + } + + // case BEACON-REQUEST: get the beacon context and + // store the context on the server. + var context = request.queryString; + setState("interceptContext", context); + + // if there is an xhr-request waiting, return the context now. + try{ + getObjectState("sw-xhr-response", function(xhrResponse) { + if (!xhrResponse) { + return; + } + setState("interceptContext", ""); + xhrResponse.write(context); + xhrResponse.finish(); + }); + } catch(e) { + } +} diff --git a/dom/workers/test/serviceworkers/fetch/context/context_test.js b/dom/workers/test/serviceworkers/fetch/context/context_test.js new file mode 100644 index 000000000..b98d2ab3c --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/context/context_test.js @@ -0,0 +1,135 @@ +self.addEventListener("fetch", function(event) { + if (event.request.url.indexOf("index.html") >= 0 || + event.request.url.indexOf("register.html") >= 0 || + event.request.url.indexOf("unregister.html") >= 0 || + event.request.url.indexOf("ping.html") >= 0 || + event.request.url.indexOf("xml.xml") >= 0 || + event.request.url.indexOf("csp-violate.sjs") >= 0) { + // Handle pass-through requests + event.respondWith(fetch(event.request)); + } else if (event.request.url.indexOf("fetch.txt") >= 0) { + var body = event.request.context == "fetch" ? + "so fetch" : "so unfetch"; + event.respondWith(new Response(body)); + } else if (event.request.url.indexOf("img.jpg") >= 0) { + if (event.request.context == "image") { + event.respondWith(fetch("realimg.jpg")); + } + } else if (event.request.url.indexOf("responsive.jpg") >= 0) { + if (event.request.context == "imageset") { + event.respondWith(fetch("realimg.jpg")); + } + } else if (event.request.url.indexOf("audio.ogg") >= 0) { + if (event.request.context == "audio") { + event.respondWith(fetch("realaudio.ogg")); + } + } else if (event.request.url.indexOf("video.ogg") >= 0) { + if (event.request.context == "video") { + event.respondWith(fetch("realaudio.ogg")); + } + } else if (event.request.url.indexOf("beacon.sjs") >= 0) { + if (event.request.url.indexOf("queryContext") == -1) { + event.respondWith(fetch("beacon.sjs?" + event.request.context)); + } else { + event.respondWith(fetch(event.request)); + } + } else if (event.request.url.indexOf("csp-report.sjs") >= 0) { + respondToServiceWorker(event, "csp-report"); + } else if (event.request.url.indexOf("embed") >= 0) { + respondToServiceWorker(event, "embed"); + } else if (event.request.url.indexOf("object") >= 0) { + respondToServiceWorker(event, "object"); + } else if (event.request.url.indexOf("font") >= 0) { + respondToServiceWorker(event, "font"); + } else if (event.request.url.indexOf("iframe") >= 0) { + if (event.request.context == "iframe") { + event.respondWith(fetch("context_test.js")); + } + } else if (event.request.url.indexOf("frame") >= 0) { + if (event.request.context == "frame") { + event.respondWith(fetch("context_test.js")); + } + } else if (event.request.url.indexOf("newwindow") >= 0) { + respondToServiceWorker(event, "newwindow"); + } else if (event.request.url.indexOf("ping") >= 0) { + respondToServiceWorker(event, "ping"); + } else if (event.request.url.indexOf("plugin") >= 0) { + respondToServiceWorker(event, "plugin"); + } else if (event.request.url.indexOf("script.js") >= 0) { + if (event.request.context == "script") { + event.respondWith(new Response("")); + } + } else if (event.request.url.indexOf("style.css") >= 0) { + respondToServiceWorker(event, "style"); + } else if (event.request.url.indexOf("track") >= 0) { + respondToServiceWorker(event, "track"); + } else if (event.request.url.indexOf("xhr") >= 0) { + if (event.request.context == "xmlhttprequest") { + event.respondWith(new Response("")); + } + } else if (event.request.url.indexOf("xslt") >= 0) { + respondToServiceWorker(event, "xslt"); + } else if (event.request.url.indexOf("myworker") >= 0) { + if (event.request.context == "worker") { + event.respondWith(fetch("worker.js")); + } + } else if (event.request.url.indexOf("myparentworker") >= 0) { + if (event.request.context == "worker") { + event.respondWith(fetch("parentworker.js")); + } + } else if (event.request.url.indexOf("mysharedworker") >= 0) { + if (event.request.context == "sharedworker") { + event.respondWith(fetch("sharedworker.js")); + } + } else if (event.request.url.indexOf("myparentsharedworker") >= 0) { + if (event.request.context == "sharedworker") { + event.respondWith(fetch("parentsharedworker.js")); + } + } else if (event.request.url.indexOf("cache") >= 0) { + var cache; + var origContext = event.request.context; + event.respondWith(caches.open("cache") + .then(function(c) { + cache = c; + // Store the Request in the cache. + return cache.put(event.request, new Response("fake")); + }).then(function() { + // Read it back. + return cache.keys(event.request); + }).then(function(res) { + var req = res[0]; + // Check to see if the context remained the same. + var success = req.context === origContext; + return clients.matchAll() + .then(function(clients) { + // Report it back to the main page. + clients.forEach(function(c) { + c.postMessage({data: "cache", success: success}); + }); + })}).then(function() { + // Cleanup. + return caches.delete("cache"); + }).then(function() { + return new Response("ack"); + })); + } + // Fail any request that we don't know about. + try { + event.respondWith(Promise.reject(event.request.url)); + dump("Fetch event received invalid context value " + event.request.context + + " for " + event.request.url + "\n"); + } catch(e) { + // Eat up the possible InvalidStateError exception that we may get if some + // code above has called respondWith too. + } +}); + +function respondToServiceWorker(event, data) { + event.respondWith(clients.matchAll() + .then(function(clients) { + clients.forEach(function(c) { + c.postMessage({data: data, context: event.request.context}); + }); + return new Response("ack"); + })); +} diff --git a/dom/workers/test/serviceworkers/fetch/context/csp-violate.sjs b/dom/workers/test/serviceworkers/fetch/context/csp-violate.sjs new file mode 100644 index 000000000..4c3e76d15 --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/context/csp-violate.sjs @@ -0,0 +1,6 @@ +function handleRequest(request, response) +{ + response.setHeader("Content-Security-Policy", "default-src 'none'; report-uri /tests/dom/workers/test/serviceworkers/fetch/context/csp-report.sjs", false); + response.setHeader("Content-Type", "text/html", false); + response.write(""); +} diff --git a/dom/workers/test/serviceworkers/fetch/context/index.html b/dom/workers/test/serviceworkers/fetch/context/index.html new file mode 100644 index 000000000..c6dfef99c --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/context/index.html @@ -0,0 +1,422 @@ + + diff --git a/dom/workers/test/serviceworkers/fetch/context/parentsharedworker.js b/dom/workers/test/serviceworkers/fetch/context/parentsharedworker.js new file mode 100644 index 000000000..eac8d5e71 --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/context/parentsharedworker.js @@ -0,0 +1,8 @@ +onconnect = function(e) { + e.ports[0].start(); + var worker = new Worker("myworker?shared"); + worker.onmessage = function(e2) { + e.ports[0].postMessage(e2.data); + self.close(); + }; +}; diff --git a/dom/workers/test/serviceworkers/fetch/context/parentworker.js b/dom/workers/test/serviceworkers/fetch/context/parentworker.js new file mode 100644 index 000000000..839fb6640 --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/context/parentworker.js @@ -0,0 +1,4 @@ +var worker = new Worker("myworker"); +worker.onmessage = function(e) { + postMessage(e.data); +}; diff --git a/dom/workers/test/serviceworkers/fetch/context/ping.html b/dom/workers/test/serviceworkers/fetch/context/ping.html new file mode 100644 index 000000000..b1bebe41e --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/context/ping.html @@ -0,0 +1,7 @@ + + +link diff --git a/dom/workers/test/serviceworkers/fetch/context/realaudio.ogg b/dom/workers/test/serviceworkers/fetch/context/realaudio.ogg new file mode 100644 index 000000000..1a41623f8 Binary files /dev/null and b/dom/workers/test/serviceworkers/fetch/context/realaudio.ogg differ diff --git a/dom/workers/test/serviceworkers/fetch/context/realimg.jpg b/dom/workers/test/serviceworkers/fetch/context/realimg.jpg new file mode 100644 index 000000000..5b920f7c0 Binary files /dev/null and b/dom/workers/test/serviceworkers/fetch/context/realimg.jpg differ diff --git a/dom/workers/test/serviceworkers/fetch/context/register.html b/dom/workers/test/serviceworkers/fetch/context/register.html new file mode 100644 index 000000000..6528d0eae --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/context/register.html @@ -0,0 +1,14 @@ + + diff --git a/dom/workers/test/serviceworkers/fetch/context/sharedworker.js b/dom/workers/test/serviceworkers/fetch/context/sharedworker.js new file mode 100644 index 000000000..94dca5839 --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/context/sharedworker.js @@ -0,0 +1,5 @@ +onconnect = function(e) { + e.ports[0].start(); + e.ports[0].postMessage("ack"); + self.close(); +}; diff --git a/dom/workers/test/serviceworkers/fetch/context/unregister.html b/dom/workers/test/serviceworkers/fetch/context/unregister.html new file mode 100644 index 000000000..1f13508fa --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/context/unregister.html @@ -0,0 +1,12 @@ + + diff --git a/dom/workers/test/serviceworkers/fetch/context/worker.js b/dom/workers/test/serviceworkers/fetch/context/worker.js new file mode 100644 index 000000000..e26e5bc69 --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/context/worker.js @@ -0,0 +1 @@ +postMessage("ack"); diff --git a/dom/workers/test/serviceworkers/fetch/context/xml.xml b/dom/workers/test/serviceworkers/fetch/context/xml.xml new file mode 100644 index 000000000..69c64adf1 --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/context/xml.xml @@ -0,0 +1,3 @@ + + + diff --git a/dom/workers/test/serviceworkers/fetch/deliver-gzip.sjs b/dom/workers/test/serviceworkers/fetch/deliver-gzip.sjs new file mode 100644 index 000000000..abacdd2ad --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/deliver-gzip.sjs @@ -0,0 +1,17 @@ +function handleRequest(request, response) { + // The string "hello" repeated 10 times followed by newline. Compressed using gzip. + var bytes = [0x1f, 0x8b, 0x08, 0x08, 0x4d, 0xe2, 0xf9, 0x54, 0x00, 0x03, 0x68, + 0x65, 0x6c, 0x6c, 0x6f, 0x00, 0xcb, 0x48, 0xcd, 0xc9, 0xc9, 0xcf, + 0x20, 0x85, 0xe0, 0x02, 0x00, 0xf5, 0x4b, 0x38, 0xcf, 0x33, 0x00, + 0x00, 0x00]; + + response.setHeader("Content-Encoding", "gzip", false); + response.setHeader("Content-Length", "" + bytes.length, false); + response.setHeader("Content-Type", "text/plain", false); + + var bos = Components.classes["@mozilla.org/binaryoutputstream;1"] + .createInstance(Components.interfaces.nsIBinaryOutputStream); + bos.setOutputStream(response.bodyOutputStream); + + bos.writeByteArray(bytes, bytes.length); +} diff --git a/dom/workers/test/serviceworkers/fetch/fetch_tests.js b/dom/workers/test/serviceworkers/fetch/fetch_tests.js new file mode 100644 index 000000000..54da1b66e --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/fetch_tests.js @@ -0,0 +1,416 @@ +var origin = 'http://mochi.test:8888'; + +function fetchXHRWithMethod(name, method, onload, onerror, headers) { + expectAsyncResult(); + + onload = onload || function() { + my_ok(false, "XHR load should not complete successfully"); + finish(); + }; + onerror = onerror || function() { + my_ok(false, "XHR load for " + name + " should be intercepted successfully"); + finish(); + }; + + var x = new XMLHttpRequest(); + x.open(method, name, true); + x.onload = function() { onload(x) }; + x.onerror = function() { onerror(x) }; + headers = headers || []; + headers.forEach(function(header) { + x.setRequestHeader(header[0], header[1]); + }); + x.send(); +} + +var corsServerPath = '/tests/dom/security/test/cors/file_CrossSiteXHR_server.sjs'; +var corsServerURL = 'http://example.com' + corsServerPath; + +function redirectURL(hops) { + return hops[0].server + corsServerPath + "?hop=1&hops=" + + encodeURIComponent(hops.toSource()); +} + +function fetchXHR(name, onload, onerror, headers) { + return fetchXHRWithMethod(name, 'GET', onload, onerror, headers); +} + +fetchXHR('bare-synthesized.txt', function(xhr) { + my_ok(xhr.status == 200, "load should be successful"); + my_ok(xhr.responseText == "synthesized response body", "load should have synthesized response"); + finish(); +}); + +fetchXHR('test-respondwith-response.txt', function(xhr) { + my_ok(xhr.status == 200, "test-respondwith-response load should be successful"); + my_ok(xhr.responseText == "test-respondwith-response response body", "load should have response"); + finish(); +}); + +fetchXHR('synthesized-404.txt', function(xhr) { + my_ok(xhr.status == 404, "load should 404"); + my_ok(xhr.responseText == "synthesized response body", "404 load should have synthesized response"); + finish(); +}); + +fetchXHR('synthesized-headers.txt', function(xhr) { + my_ok(xhr.status == 200, "load should be successful"); + my_ok(xhr.getResponseHeader("X-Custom-Greeting") === "Hello", "custom header should be set"); + my_ok(xhr.responseText == "synthesized response body", "custom header load should have synthesized response"); + finish(); +}); + +fetchXHR('synthesized-redirect-real-file.txt', function(xhr) { +dump("Got status AARRGH " + xhr.status + " " + xhr.responseText + "\n"); + my_ok(xhr.status == 200, "load should be successful"); + my_ok(xhr.responseText == "This is a real file.\n", "Redirect to real file should complete."); + finish(); +}); + +fetchXHR('synthesized-redirect-twice-real-file.txt', function(xhr) { + my_ok(xhr.status == 200, "load should be successful"); + my_ok(xhr.responseText == "This is a real file.\n", "Redirect to real file (twice) should complete."); + finish(); +}); + +fetchXHR('synthesized-redirect-synthesized.txt', function(xhr) { + my_ok(xhr.status == 200, "synth+redirect+synth load should be successful"); + my_ok(xhr.responseText == "synthesized response body", "load should have redirected+synthesized response"); + finish(); +}); + +fetchXHR('synthesized-redirect-twice-synthesized.txt', function(xhr) { + my_ok(xhr.status == 200, "synth+redirect+synth (twice) load should be successful"); + my_ok(xhr.responseText == "synthesized response body", "load should have redirected+synthesized (twice) response"); + finish(); +}); + +fetchXHR('redirect.sjs', function(xhr) { + my_ok(xhr.status == 404, "redirected load should be uninterrupted"); + finish(); +}); + +fetchXHR('ignored.txt', function(xhr) { + my_ok(xhr.status == 404, "load should be uninterrupted"); + finish(); +}); + +fetchXHR('rejected.txt', null, function(xhr) { + my_ok(xhr.status == 0, "load should not complete"); + finish(); +}); + +fetchXHR('nonresponse.txt', null, function(xhr) { + my_ok(xhr.status == 0, "load should not complete"); + finish(); +}); + +fetchXHR('nonresponse2.txt', null, function(xhr) { + my_ok(xhr.status == 0, "load should not complete"); + finish(); +}); + +fetchXHR('nonpromise.txt', null, function(xhr) { + my_ok(xhr.status == 0, "load should not complete"); + finish(); +}); + +fetchXHR('headers.txt', function(xhr) { + my_ok(xhr.status == 200, "load should be successful"); + my_ok(xhr.responseText == "1", "request header checks should have passed"); + finish(); +}, null, [["X-Test1", "header1"], ["X-Test2", "header2"]]); + +fetchXHR('http://user:pass@mochi.test:8888/user-pass', function(xhr) { + my_ok(xhr.status == 200, "load should be successful"); + my_ok(xhr.responseText == 'http://user:pass@mochi.test:8888/user-pass', 'The username and password should be preserved'); + finish(); +}); + +var expectedUncompressedResponse = ""; +for (var i = 0; i < 10; ++i) { + expectedUncompressedResponse += "hello"; +} +expectedUncompressedResponse += "\n"; + +// ServiceWorker does not intercept, at which point the network request should +// be correctly decoded. +fetchXHR('deliver-gzip.sjs', function(xhr) { + my_ok(xhr.status == 200, "network gzip load should be successful"); + my_ok(xhr.responseText == expectedUncompressedResponse, "network gzip load should have synthesized response."); + my_ok(xhr.getResponseHeader("Content-Encoding") == "gzip", "network Content-Encoding should be gzip."); + my_ok(xhr.getResponseHeader("Content-Length") == "35", "network Content-Length should be of original gzipped file."); + finish(); +}); + +fetchXHR('hello.gz', function(xhr) { + my_ok(xhr.status == 200, "gzip load should be successful"); + my_ok(xhr.responseText == expectedUncompressedResponse, "gzip load should have synthesized response."); + my_ok(xhr.getResponseHeader("Content-Encoding") == "gzip", "Content-Encoding should be gzip."); + my_ok(xhr.getResponseHeader("Content-Length") == "35", "Content-Length should be of original gzipped file."); + finish(); +}); + +fetchXHR('hello-after-extracting.gz', function(xhr) { + my_ok(xhr.status == 200, "gzip load after extracting should be successful"); + my_ok(xhr.responseText == expectedUncompressedResponse, "gzip load after extracting should have synthesized response."); + my_ok(xhr.getResponseHeader("Content-Encoding") == "gzip", "Content-Encoding after extracting should be gzip."); + my_ok(xhr.getResponseHeader("Content-Length") == "35", "Content-Length after extracting should be of original gzipped file."); + finish(); +}); + +fetchXHR(corsServerURL + '?status=200&allowOrigin=*', function(xhr) { + my_ok(xhr.status == 200, "cross origin load with correct headers should be successful"); + my_ok(xhr.getResponseHeader("access-control-allow-origin") == null, "cors headers should be filtered out"); + finish(); +}); + +// Verify origin header is sent properly even when we have a no-intercept SW. +var uriOrigin = encodeURIComponent(origin); +fetchXHR('http://example.org' + corsServerPath + '?ignore&status=200&origin=' + uriOrigin + + '&allowOrigin=' + uriOrigin, function(xhr) { + my_ok(xhr.status == 200, "cross origin load with correct headers should be successful"); + my_ok(xhr.getResponseHeader("access-control-allow-origin") == null, "cors headers should be filtered out"); + finish(); +}); + +// Verify that XHR is considered CORS tainted even when original URL is same-origin +// redirected to cross-origin. +fetchXHR(redirectURL([{ server: origin }, + { server: 'http://example.org', + allowOrigin: origin }]), function(xhr) { + my_ok(xhr.status == 200, "cross origin load with correct headers should be successful"); + my_ok(xhr.getResponseHeader("access-control-allow-origin") == null, "cors headers should be filtered out"); + finish(); +}); + +// Test that CORS preflight requests cannot be intercepted. Performs a +// cross-origin XHR that the SW chooses not to intercept. This requires a +// preflight request, which the SW must not be allowed to intercept. +fetchXHR(corsServerURL + '?status=200&allowOrigin=*', null, function(xhr) { + my_ok(xhr.status == 0, "cross origin load with incorrect headers should be a failure"); + finish(); +}, [["X-Unsafe", "unsafe"]]); + +// Test that CORS preflight requests cannot be intercepted. Performs a +// cross-origin XHR that the SW chooses to intercept and respond with a +// cross-origin fetch. This requires a preflight request, which the SW must not +// be allowed to intercept. +fetchXHR('http://example.org' + corsServerPath + '?status=200&allowOrigin=*', null, function(xhr) { + my_ok(xhr.status == 0, "cross origin load with incorrect headers should be a failure"); + finish(); +}, [["X-Unsafe", "unsafe"]]); + +// Test that when the page fetches a url the controlling SW forces a redirect to +// another location. This other location fetch should also be intercepted by +// the SW. +fetchXHR('something.txt', function(xhr) { + my_ok(xhr.status == 200, "load should be successful"); + my_ok(xhr.responseText == "something else response body", "load should have something else"); + finish(); +}); + +// Test fetch will internally get it's SkipServiceWorker flag set. The request is +// made from the SW through fetch(). fetch() fetches a server-side JavaScript +// file that force a redirect. The redirect location fetch does not go through +// the SW. +fetchXHR('redirect_serviceworker.sjs', function(xhr) { + my_ok(xhr.status == 200, "load should be successful"); + my_ok(xhr.responseText == "// empty worker, always succeed!\n", "load should have redirection content"); + finish(); +}); + +fetchXHR('empty-header', function(xhr) { + my_ok(xhr.status == 200, "load should be successful"); + my_ok(xhr.responseText == "emptyheader", "load should have the expected content"); + finish(); +}, null, [["emptyheader", ""]]); + +expectAsyncResult(); +fetch('http://example.com/tests/dom/security/test/cors/file_CrossSiteXHR_server.sjs?status=200&allowOrigin=*') +.then(function(res) { + my_ok(res.ok, "Valid CORS request should receive valid response"); + my_ok(res.type == "cors", "Response type should be CORS"); + res.text().then(function(body) { + my_ok(body === "hello pass\n", "cors response body should match"); + finish(); + }); +}, function(e) { + my_ok(false, "CORS Fetch failed"); + finish(); +}); + +expectAsyncResult(); +fetch('http://example.com/tests/dom/security/test/cors/file_CrossSiteXHR_server.sjs?status=200', { mode: 'no-cors' }) +.then(function(res) { + my_ok(res.type == "opaque", "Response type should be opaque"); + my_ok(res.status == 0, "Status should be 0"); + res.text().then(function(body) { + my_ok(body === "", "opaque response body should be empty"); + finish(); + }); +}, function(e) { + my_ok(false, "no-cors Fetch failed"); + finish(); +}); + +expectAsyncResult(); +fetch('opaque-on-same-origin') +.then(function(res) { + my_ok(false, "intercepted opaque response for non no-cors request should fail."); + finish(); +}, function(e) { + my_ok(true, "intercepted opaque response for non no-cors request should fail."); + finish(); +}); + +expectAsyncResult(); +fetch('http://example.com/opaque-no-cors', { mode: "no-cors" }) +.then(function(res) { + my_ok(res.type == "opaque", "intercepted opaque response for no-cors request should have type opaque."); + finish(); +}, function(e) { + my_ok(false, "intercepted opaque response for no-cors request should pass."); + finish(); +}); + +expectAsyncResult(); +fetch('http://example.com/cors-for-no-cors', { mode: "no-cors" }) +.then(function(res) { + my_ok(res.type == "opaque", "intercepted non-opaque response for no-cors request should resolve to opaque response."); + finish(); +}, function(e) { + my_ok(false, "intercepted non-opaque response for no-cors request should resolve to opaque response. It should not fail."); + finish(); +}); + +function arrayBufferFromString(str) { + var arr = new Uint8Array(str.length); + for (var i = 0; i < str.length; ++i) { + arr[i] = str.charCodeAt(i); + } + return arr; +} + +expectAsyncResult(); +fetch(new Request('body-simple', {method: 'POST', body: 'my body'})) +.then(function(res) { + return res.text(); +}).then(function(body) { + my_ok(body == 'my bodymy body', "the body of the intercepted fetch should be visible in the SW"); + finish(); +}); + +expectAsyncResult(); +fetch(new Request('body-arraybufferview', {method: 'POST', body: arrayBufferFromString('my body')})) +.then(function(res) { + return res.text(); +}).then(function(body) { + my_ok(body == 'my bodymy body', "the ArrayBufferView body of the intercepted fetch should be visible in the SW"); + finish(); +}); + +expectAsyncResult(); +fetch(new Request('body-arraybuffer', {method: 'POST', body: arrayBufferFromString('my body').buffer})) +.then(function(res) { + return res.text(); +}).then(function(body) { + my_ok(body == 'my bodymy body', "the ArrayBuffer body of the intercepted fetch should be visible in the SW"); + finish(); +}); + +expectAsyncResult(); +var usp = new URLSearchParams(); +usp.set("foo", "bar"); +usp.set("baz", "qux"); +fetch(new Request('body-urlsearchparams', {method: 'POST', body: usp})) +.then(function(res) { + return res.text(); +}).then(function(body) { + my_ok(body == 'foo=bar&baz=quxfoo=bar&baz=qux', "the URLSearchParams body of the intercepted fetch should be visible in the SW"); + finish(); +}); + +expectAsyncResult(); +var fd = new FormData(); +fd.set("foo", "bar"); +fd.set("baz", "qux"); +fetch(new Request('body-formdata', {method: 'POST', body: fd})) +.then(function(res) { + return res.text(); +}).then(function(body) { + my_ok(body.indexOf("Content-Disposition: form-data; name=\"foo\"\r\n\r\nbar") < + body.indexOf("Content-Disposition: form-data; name=\"baz\"\r\n\r\nqux"), + "the FormData body of the intercepted fetch should be visible in the SW"); + finish(); +}); + +expectAsyncResult(); +fetch(new Request('body-blob', {method: 'POST', body: new Blob(new String('my body'))})) +.then(function(res) { + return res.text(); +}).then(function(body) { + my_ok(body == 'my bodymy body', "the Blob body of the intercepted fetch should be visible in the SW"); + finish(); +}); + +expectAsyncResult(); +fetch('interrupt.sjs') +.then(function(res) { + my_ok(true, "interrupted fetch succeeded"); + res.text().then(function(body) { + my_ok(false, "interrupted fetch shouldn't have complete body"); + finish(); + }, + function() { + my_ok(true, "interrupted fetch shouldn't have complete body"); + finish(); + }) +}, function(e) { + my_ok(false, "interrupted fetch failed"); + finish(); +}); + +['DELETE', 'GET', 'HEAD', 'OPTIONS', 'POST', 'PUT'].forEach(function(method) { + fetchXHRWithMethod('xhr-method-test.txt', method, function(xhr) { + my_ok(xhr.status == 200, method + " load should be successful"); + my_ok(xhr.responseText == ("intercepted " + method), method + " load should have synthesized response"); + finish(); + }); +}); + +expectAsyncResult(); +fetch(new Request('empty-header', {headers:{"emptyheader":""}})) +.then(function(res) { + return res.text(); +}).then(function(body) { + my_ok(body == "emptyheader", "The empty header was observed in the fetch event"); + finish(); +}, function(err) { + my_ok(false, "A promise was rejected with " + err); + finish(); +}); + +expectAsyncResult(); +fetch('fetchevent-extendable') +.then(function(res) { + return res.text(); +}).then(function(body) { + my_ok(body == "extendable", "FetchEvent inherits from ExtendableEvent"); + finish(); +}, function(err) { + my_ok(false, "A promise was rejected with " + err); + finish(); +}); + +expectAsyncResult(); +fetch('fetchevent-request') +.then(function(res) { + return res.text(); +}).then(function(body) { + my_ok(body == "non-nullable", "FetchEvent.request must be non-nullable"); + finish(); +}, function(err) { + my_ok(false, "A promise was rejected with " + err); + finish(); +}); diff --git a/dom/workers/test/serviceworkers/fetch/fetch_worker_script.js b/dom/workers/test/serviceworkers/fetch/fetch_worker_script.js new file mode 100644 index 000000000..61efb647c --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/fetch_worker_script.js @@ -0,0 +1,29 @@ +function my_ok(v, msg) { + postMessage({type: "ok", value: v, msg: msg}); +} + +function finish() { + postMessage('finish'); +} + +function expectAsyncResult() { + postMessage('expect'); +} + +expectAsyncResult(); +try { + var success = false; + importScripts("nonexistent_imported_script.js"); +} catch(x) { +} + +my_ok(success, "worker imported script should be intercepted"); +finish(); + +function check_intercepted_script() { + success = true; +} + +importScripts('fetch_tests.js') + +finish(); //corresponds to the gExpected increment before creating this worker diff --git a/dom/workers/test/serviceworkers/fetch/hsts/embedder.html b/dom/workers/test/serviceworkers/fetch/hsts/embedder.html new file mode 100644 index 000000000..c98555423 --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/hsts/embedder.html @@ -0,0 +1,7 @@ + + + diff --git a/dom/workers/test/serviceworkers/fetch/hsts/hsts_test.js b/dom/workers/test/serviceworkers/fetch/hsts/hsts_test.js new file mode 100644 index 000000000..ab54164ed --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/hsts/hsts_test.js @@ -0,0 +1,11 @@ +self.addEventListener("fetch", function(event) { + if (event.request.url.indexOf("index.html") >= 0) { + event.respondWith(fetch("realindex.html")); + } else if (event.request.url.indexOf("image-20px.png") >= 0) { + if (event.request.url.indexOf("https://") == 0) { + event.respondWith(fetch("image-40px.png")); + } else { + event.respondWith(Response.error()); + } + } +}); diff --git a/dom/workers/test/serviceworkers/fetch/hsts/image-20px.png b/dom/workers/test/serviceworkers/fetch/hsts/image-20px.png new file mode 100644 index 000000000..ae6a8a6b8 Binary files /dev/null and b/dom/workers/test/serviceworkers/fetch/hsts/image-20px.png differ diff --git a/dom/workers/test/serviceworkers/fetch/hsts/image-40px.png b/dom/workers/test/serviceworkers/fetch/hsts/image-40px.png new file mode 100644 index 000000000..fe391dc8a Binary files /dev/null and b/dom/workers/test/serviceworkers/fetch/hsts/image-40px.png differ diff --git a/dom/workers/test/serviceworkers/fetch/hsts/image.html b/dom/workers/test/serviceworkers/fetch/hsts/image.html new file mode 100644 index 000000000..cadbdef5a --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/hsts/image.html @@ -0,0 +1,13 @@ + + diff --git a/dom/workers/test/serviceworkers/fetch/hsts/realindex.html b/dom/workers/test/serviceworkers/fetch/hsts/realindex.html new file mode 100644 index 000000000..b3d1d527e --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/hsts/realindex.html @@ -0,0 +1,8 @@ + + diff --git a/dom/workers/test/serviceworkers/fetch/hsts/register.html b/dom/workers/test/serviceworkers/fetch/hsts/register.html new file mode 100644 index 000000000..bcdc146ae --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/hsts/register.html @@ -0,0 +1,14 @@ + + diff --git a/dom/workers/test/serviceworkers/fetch/hsts/register.html^headers^ b/dom/workers/test/serviceworkers/fetch/hsts/register.html^headers^ new file mode 100644 index 000000000..a46bf65bd --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/hsts/register.html^headers^ @@ -0,0 +1,2 @@ +Cache-Control: no-cache +Strict-Transport-Security: max-age=60 diff --git a/dom/workers/test/serviceworkers/fetch/hsts/unregister.html b/dom/workers/test/serviceworkers/fetch/hsts/unregister.html new file mode 100644 index 000000000..1f13508fa --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/hsts/unregister.html @@ -0,0 +1,12 @@ + + diff --git a/dom/workers/test/serviceworkers/fetch/https/clonedresponse/https_test.js b/dom/workers/test/serviceworkers/fetch/https/clonedresponse/https_test.js new file mode 100644 index 000000000..48f7b9307 --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/https/clonedresponse/https_test.js @@ -0,0 +1,15 @@ +self.addEventListener("install", function(event) { + event.waitUntil(caches.open("cache").then(function(cache) { + return cache.add("index.html"); + })); +}); + +self.addEventListener("fetch", function(event) { + if (event.request.url.indexOf("index.html") >= 0) { + event.respondWith(new Promise(function(resolve, reject) { + caches.match(event.request).then(function(response) { + resolve(response.clone()); + }); + })); + } +}); diff --git a/dom/workers/test/serviceworkers/fetch/https/clonedresponse/index.html b/dom/workers/test/serviceworkers/fetch/https/clonedresponse/index.html new file mode 100644 index 000000000..a43554844 --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/https/clonedresponse/index.html @@ -0,0 +1,4 @@ + + diff --git a/dom/workers/test/serviceworkers/fetch/https/clonedresponse/register.html b/dom/workers/test/serviceworkers/fetch/https/clonedresponse/register.html new file mode 100644 index 000000000..41774f70d --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/https/clonedresponse/register.html @@ -0,0 +1,14 @@ + + diff --git a/dom/workers/test/serviceworkers/fetch/https/clonedresponse/unregister.html b/dom/workers/test/serviceworkers/fetch/https/clonedresponse/unregister.html new file mode 100644 index 000000000..1f13508fa --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/https/clonedresponse/unregister.html @@ -0,0 +1,12 @@ + + diff --git a/dom/workers/test/serviceworkers/fetch/https/https_test.js b/dom/workers/test/serviceworkers/fetch/https/https_test.js new file mode 100644 index 000000000..6f87bb5ee --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/https/https_test.js @@ -0,0 +1,23 @@ +self.addEventListener("install", function(event) { + event.waitUntil(caches.open("cache").then(function(cache) { + var synth = new Response('', + {headers:{"Content-Type": "text/html"}}); + return Promise.all([ + cache.add("index.html"), + cache.put("synth-sw.html", synth), + ]); + })); +}); + +self.addEventListener("fetch", function(event) { + if (event.request.url.indexOf("index.html") >= 0) { + event.respondWith(caches.match(event.request)); + } else if (event.request.url.indexOf("synth-sw.html") >= 0) { + event.respondWith(caches.match(event.request)); + } else if (event.request.url.indexOf("synth-window.html") >= 0) { + event.respondWith(caches.match(event.request)); + } else if (event.request.url.indexOf("synth.html") >= 0) { + event.respondWith(new Response('', + {headers:{"Content-Type": "text/html"}})); + } +}); diff --git a/dom/workers/test/serviceworkers/fetch/https/index.html b/dom/workers/test/serviceworkers/fetch/https/index.html new file mode 100644 index 000000000..a43554844 --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/https/index.html @@ -0,0 +1,4 @@ + + diff --git a/dom/workers/test/serviceworkers/fetch/https/register.html b/dom/workers/test/serviceworkers/fetch/https/register.html new file mode 100644 index 000000000..fa666fe95 --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/https/register.html @@ -0,0 +1,20 @@ + + diff --git a/dom/workers/test/serviceworkers/fetch/https/unregister.html b/dom/workers/test/serviceworkers/fetch/https/unregister.html new file mode 100644 index 000000000..1f13508fa --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/https/unregister.html @@ -0,0 +1,12 @@ + + diff --git a/dom/workers/test/serviceworkers/fetch/imagecache-maxage/image-20px.png b/dom/workers/test/serviceworkers/fetch/imagecache-maxage/image-20px.png new file mode 100644 index 000000000..ae6a8a6b8 Binary files /dev/null and b/dom/workers/test/serviceworkers/fetch/imagecache-maxage/image-20px.png differ diff --git a/dom/workers/test/serviceworkers/fetch/imagecache-maxage/image-40px.png b/dom/workers/test/serviceworkers/fetch/imagecache-maxage/image-40px.png new file mode 100644 index 000000000..fe391dc8a Binary files /dev/null and b/dom/workers/test/serviceworkers/fetch/imagecache-maxage/image-40px.png differ diff --git a/dom/workers/test/serviceworkers/fetch/imagecache-maxage/index.html b/dom/workers/test/serviceworkers/fetch/imagecache-maxage/index.html new file mode 100644 index 000000000..426c27a73 --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/imagecache-maxage/index.html @@ -0,0 +1,29 @@ + + + + diff --git a/dom/workers/test/serviceworkers/fetch/imagecache-maxage/maxage_test.js b/dom/workers/test/serviceworkers/fetch/imagecache-maxage/maxage_test.js new file mode 100644 index 000000000..1922111df --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/imagecache-maxage/maxage_test.js @@ -0,0 +1,41 @@ +function synthesizeImage(suffix) { + // Serve image-20px for the first page, and image-40px for the second page. + return clients.matchAll().then(clients => { + var url = "image-20px.png"; + clients.forEach(client => { + if (client.url.indexOf("?new") > 0) { + url = "image-40px.png"; + } + client.postMessage({suffix: suffix, url: url}); + }); + return fetch(url); + }).then(response => { + return response.arrayBuffer(); + }).then(ab => { + var headers; + if (suffix == "") { + headers = { + "Content-Type": "image/png", + "Date": "Tue, 1 Jan 1990 01:02:03 GMT", + "Cache-Control": "max-age=1", + }; + } else { + headers = { + "Content-Type": "image/png", + "Cache-Control": "no-cache", + }; + } + return new Response(ab, { + status: 200, + headers: headers, + }); + }); +} + +self.addEventListener("fetch", function(event) { + if (event.request.url.indexOf("image.png") >= 0) { + event.respondWith(synthesizeImage("")); + } else if (event.request.url.indexOf("image2.png") >= 0) { + event.respondWith(synthesizeImage("2")); + } +}); diff --git a/dom/workers/test/serviceworkers/fetch/imagecache-maxage/register.html b/dom/workers/test/serviceworkers/fetch/imagecache-maxage/register.html new file mode 100644 index 000000000..af4dde2e2 --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/imagecache-maxage/register.html @@ -0,0 +1,14 @@ + + diff --git a/dom/workers/test/serviceworkers/fetch/imagecache-maxage/unregister.html b/dom/workers/test/serviceworkers/fetch/imagecache-maxage/unregister.html new file mode 100644 index 000000000..1f13508fa --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/imagecache-maxage/unregister.html @@ -0,0 +1,12 @@ + + diff --git a/dom/workers/test/serviceworkers/fetch/imagecache/image-20px.png b/dom/workers/test/serviceworkers/fetch/imagecache/image-20px.png new file mode 100644 index 000000000..ae6a8a6b8 Binary files /dev/null and b/dom/workers/test/serviceworkers/fetch/imagecache/image-20px.png differ diff --git a/dom/workers/test/serviceworkers/fetch/imagecache/image-40px.png b/dom/workers/test/serviceworkers/fetch/imagecache/image-40px.png new file mode 100644 index 000000000..fe391dc8a Binary files /dev/null and b/dom/workers/test/serviceworkers/fetch/imagecache/image-40px.png differ diff --git a/dom/workers/test/serviceworkers/fetch/imagecache/imagecache_test.js b/dom/workers/test/serviceworkers/fetch/imagecache/imagecache_test.js new file mode 100644 index 000000000..598d8213f --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/imagecache/imagecache_test.js @@ -0,0 +1,15 @@ +function synthesizeImage() { + return clients.matchAll().then(clients => { + var url = "image-40px.png"; + clients.forEach(client => { + client.postMessage(url); + }); + return fetch(url); + }); +} + +self.addEventListener("fetch", function(event) { + if (event.request.url.indexOf("image-20px.png") >= 0) { + event.respondWith(synthesizeImage()); + } +}); diff --git a/dom/workers/test/serviceworkers/fetch/imagecache/index.html b/dom/workers/test/serviceworkers/fetch/imagecache/index.html new file mode 100644 index 000000000..93b30f184 --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/imagecache/index.html @@ -0,0 +1,20 @@ + + + diff --git a/dom/workers/test/serviceworkers/fetch/imagecache/postmortem.html b/dom/workers/test/serviceworkers/fetch/imagecache/postmortem.html new file mode 100644 index 000000000..72a650d26 --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/imagecache/postmortem.html @@ -0,0 +1,9 @@ + + + diff --git a/dom/workers/test/serviceworkers/fetch/imagecache/register.html b/dom/workers/test/serviceworkers/fetch/imagecache/register.html new file mode 100644 index 000000000..f6d1eb382 --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/imagecache/register.html @@ -0,0 +1,16 @@ + + + + diff --git a/dom/workers/test/serviceworkers/fetch/imagecache/unregister.html b/dom/workers/test/serviceworkers/fetch/imagecache/unregister.html new file mode 100644 index 000000000..1f13508fa --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/imagecache/unregister.html @@ -0,0 +1,12 @@ + + diff --git a/dom/workers/test/serviceworkers/fetch/importscript-mixedcontent/https_test.js b/dom/workers/test/serviceworkers/fetch/importscript-mixedcontent/https_test.js new file mode 100644 index 000000000..0f08ba74e --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/importscript-mixedcontent/https_test.js @@ -0,0 +1,28 @@ +function sendResponseToParent(response) { + return ` + + + `; +} + +self.addEventListener("fetch", function(event) { + if (event.request.url.indexOf("index.html") >= 0) { + var response = "good"; + try { + importScripts("http://example.org/tests/dom/workers/test/foreign.js"); + } catch(e) { + dump("Got error " + e + " when importing the script\n"); + } + if (response === "good") { + try { + importScripts("/tests/dom/workers/test/redirect_to_foreign.sjs"); + } catch(e) { + dump("Got error " + e + " when importing the script\n"); + } + } + event.respondWith(new Response(sendResponseToParent(response), + {headers: {'Content-Type': 'text/html'}})); + } +}); diff --git a/dom/workers/test/serviceworkers/fetch/importscript-mixedcontent/register.html b/dom/workers/test/serviceworkers/fetch/importscript-mixedcontent/register.html new file mode 100644 index 000000000..41774f70d --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/importscript-mixedcontent/register.html @@ -0,0 +1,14 @@ + + diff --git a/dom/workers/test/serviceworkers/fetch/importscript-mixedcontent/unregister.html b/dom/workers/test/serviceworkers/fetch/importscript-mixedcontent/unregister.html new file mode 100644 index 000000000..1f13508fa --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/importscript-mixedcontent/unregister.html @@ -0,0 +1,12 @@ + + diff --git a/dom/workers/test/serviceworkers/fetch/index.html b/dom/workers/test/serviceworkers/fetch/index.html new file mode 100644 index 000000000..4db0fb139 --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/index.html @@ -0,0 +1,183 @@ + + + + + Bug 94048 - test install event. + + + + +

+ +
+

+
+
+
+
+
+
diff --git a/dom/workers/test/serviceworkers/fetch/interrupt.sjs b/dom/workers/test/serviceworkers/fetch/interrupt.sjs
new file mode 100644
index 000000000..f6fe870ef
--- /dev/null
+++ b/dom/workers/test/serviceworkers/fetch/interrupt.sjs
@@ -0,0 +1,20 @@
+function handleRequest(request, response) {
+  var body = "a";
+  for (var i = 0; i < 20; i++) {
+    body += body;
+  }
+
+  response.seizePower();
+  response.write("HTTP/1.1 200 OK\r\n")
+  var count = 10;
+  response.write("Content-Length: " + body.length * count + "\r\n");
+  response.write("Content-Type: text/plain; charset=utf-8\r\n");
+  response.write("Cache-Control: no-cache, must-revalidate\r\n");
+  response.write("\r\n");
+
+  for (var i = 0; i < count; i++) {
+    response.write(body);
+  }
+
+  throw Components.results.NS_BINDING_ABORTED;
+}
diff --git a/dom/workers/test/serviceworkers/fetch/origin/https/index-https.sjs b/dom/workers/test/serviceworkers/fetch/origin/https/index-https.sjs
new file mode 100644
index 000000000..7266925ea
--- /dev/null
+++ b/dom/workers/test/serviceworkers/fetch/origin/https/index-https.sjs
@@ -0,0 +1,4 @@
+function handleRequest(request, response) {
+  response.setStatusLine(null, 308, "Permanent Redirect");
+  response.setHeader("Location", "https://example.org/tests/dom/workers/test/serviceworkers/fetch/origin/https/realindex.html", false);
+}
diff --git a/dom/workers/test/serviceworkers/fetch/origin/https/origin_test.js b/dom/workers/test/serviceworkers/fetch/origin/https/origin_test.js
new file mode 100644
index 000000000..9839fc5f0
--- /dev/null
+++ b/dom/workers/test/serviceworkers/fetch/origin/https/origin_test.js
@@ -0,0 +1,29 @@
+var prefix = "/tests/dom/workers/test/serviceworkers/fetch/origin/https/";
+
+function addOpaqueRedirect(cache, file) {
+  return fetch(new Request(prefix + file, { redirect: "manual" })).then(function(response) {
+    return cache.put(prefix + file, response);
+  });
+}
+
+self.addEventListener("install", function(event) {
+  event.waitUntil(
+    self.caches.open("origin-cache")
+      .then(c => {
+        return addOpaqueRedirect(c, 'index-https.sjs');
+      })
+  );
+});
+
+self.addEventListener("fetch", function(event) {
+  if (event.request.url.indexOf("index-cached-https.sjs") >= 0) {
+    event.respondWith(
+      self.caches.open("origin-cache")
+        .then(c => {
+          return c.match(prefix + 'index-https.sjs');
+        })
+    );
+  } else {
+    event.respondWith(fetch(event.request));
+  }
+});
diff --git a/dom/workers/test/serviceworkers/fetch/origin/https/realindex.html b/dom/workers/test/serviceworkers/fetch/origin/https/realindex.html
new file mode 100644
index 000000000..87f348945
--- /dev/null
+++ b/dom/workers/test/serviceworkers/fetch/origin/https/realindex.html
@@ -0,0 +1,6 @@
+
+
diff --git a/dom/workers/test/serviceworkers/fetch/origin/https/realindex.html^headers^ b/dom/workers/test/serviceworkers/fetch/origin/https/realindex.html^headers^
new file mode 100644
index 000000000..5ed82fd06
--- /dev/null
+++ b/dom/workers/test/serviceworkers/fetch/origin/https/realindex.html^headers^
@@ -0,0 +1 @@
+Access-Control-Allow-Origin: https://example.com
diff --git a/dom/workers/test/serviceworkers/fetch/origin/https/register.html b/dom/workers/test/serviceworkers/fetch/origin/https/register.html
new file mode 100644
index 000000000..2e99adba5
--- /dev/null
+++ b/dom/workers/test/serviceworkers/fetch/origin/https/register.html
@@ -0,0 +1,14 @@
+
+
diff --git a/dom/workers/test/serviceworkers/fetch/origin/https/unregister.html b/dom/workers/test/serviceworkers/fetch/origin/https/unregister.html
new file mode 100644
index 000000000..1f13508fa
--- /dev/null
+++ b/dom/workers/test/serviceworkers/fetch/origin/https/unregister.html
@@ -0,0 +1,12 @@
+
+
diff --git a/dom/workers/test/serviceworkers/fetch/origin/index-to-https.sjs b/dom/workers/test/serviceworkers/fetch/origin/index-to-https.sjs
new file mode 100644
index 000000000..1cc916ff3
--- /dev/null
+++ b/dom/workers/test/serviceworkers/fetch/origin/index-to-https.sjs
@@ -0,0 +1,4 @@
+function handleRequest(request, response) {
+  response.setStatusLine(null, 308, "Permanent Redirect");
+  response.setHeader("Location", "https://example.org/tests/dom/workers/test/serviceworkers/fetch/origin/realindex.html", false);
+}
diff --git a/dom/workers/test/serviceworkers/fetch/origin/index.sjs b/dom/workers/test/serviceworkers/fetch/origin/index.sjs
new file mode 100644
index 000000000..a79588e76
--- /dev/null
+++ b/dom/workers/test/serviceworkers/fetch/origin/index.sjs
@@ -0,0 +1,4 @@
+function handleRequest(request, response) {
+  response.setStatusLine(null, 308, "Permanent Redirect");
+  response.setHeader("Location", "http://example.org/tests/dom/workers/test/serviceworkers/fetch/origin/realindex.html", false);
+}
diff --git a/dom/workers/test/serviceworkers/fetch/origin/origin_test.js b/dom/workers/test/serviceworkers/fetch/origin/origin_test.js
new file mode 100644
index 000000000..d2be9573b
--- /dev/null
+++ b/dom/workers/test/serviceworkers/fetch/origin/origin_test.js
@@ -0,0 +1,41 @@
+var prefix = "/tests/dom/workers/test/serviceworkers/fetch/origin/";
+
+function addOpaqueRedirect(cache, file) {
+  return fetch(new Request(prefix + file, { redirect: "manual" })).then(function(response) {
+    return cache.put(prefix + file, response);
+  });
+}
+
+self.addEventListener("install", function(event) {
+  event.waitUntil(
+    self.caches.open("origin-cache")
+      .then(c => {
+        return Promise.all(
+          [
+            addOpaqueRedirect(c, 'index.sjs'),
+            addOpaqueRedirect(c, 'index-to-https.sjs')
+          ]
+        );
+      })
+  );
+});
+
+self.addEventListener("fetch", function(event) {
+  if (event.request.url.indexOf("index-cached.sjs") >= 0) {
+    event.respondWith(
+      self.caches.open("origin-cache")
+        .then(c => {
+          return c.match(prefix + 'index.sjs');
+        })
+    );
+  } else if (event.request.url.indexOf("index-to-https-cached.sjs") >= 0) {
+    event.respondWith(
+      self.caches.open("origin-cache")
+        .then(c => {
+          return c.match(prefix + 'index-to-https.sjs');
+        })
+    );
+  } else {
+    event.respondWith(fetch(event.request));
+  }
+});
diff --git a/dom/workers/test/serviceworkers/fetch/origin/realindex.html b/dom/workers/test/serviceworkers/fetch/origin/realindex.html
new file mode 100644
index 000000000..87f348945
--- /dev/null
+++ b/dom/workers/test/serviceworkers/fetch/origin/realindex.html
@@ -0,0 +1,6 @@
+
+
diff --git a/dom/workers/test/serviceworkers/fetch/origin/realindex.html^headers^ b/dom/workers/test/serviceworkers/fetch/origin/realindex.html^headers^
new file mode 100644
index 000000000..3a6a85d89
--- /dev/null
+++ b/dom/workers/test/serviceworkers/fetch/origin/realindex.html^headers^
@@ -0,0 +1 @@
+Access-Control-Allow-Origin: http://mochi.test:8888
diff --git a/dom/workers/test/serviceworkers/fetch/origin/register.html b/dom/workers/test/serviceworkers/fetch/origin/register.html
new file mode 100644
index 000000000..2e99adba5
--- /dev/null
+++ b/dom/workers/test/serviceworkers/fetch/origin/register.html
@@ -0,0 +1,14 @@
+
+
diff --git a/dom/workers/test/serviceworkers/fetch/origin/unregister.html b/dom/workers/test/serviceworkers/fetch/origin/unregister.html
new file mode 100644
index 000000000..1f13508fa
--- /dev/null
+++ b/dom/workers/test/serviceworkers/fetch/origin/unregister.html
@@ -0,0 +1,12 @@
+
+
diff --git a/dom/workers/test/serviceworkers/fetch/plugin/plugins.html b/dom/workers/test/serviceworkers/fetch/plugin/plugins.html
new file mode 100644
index 000000000..78e31b3c2
--- /dev/null
+++ b/dom/workers/test/serviceworkers/fetch/plugin/plugins.html
@@ -0,0 +1,43 @@
+
+
diff --git a/dom/workers/test/serviceworkers/fetch/plugin/worker.js b/dom/workers/test/serviceworkers/fetch/plugin/worker.js
new file mode 100644
index 000000000..e97d06205
--- /dev/null
+++ b/dom/workers/test/serviceworkers/fetch/plugin/worker.js
@@ -0,0 +1,14 @@
+self.addEventListener("fetch", function(event) {
+  var resource = event.request.url.split('/').pop();
+  event.waitUntil(
+    clients.matchAll()
+           .then(clients => {
+             clients.forEach(client => {
+               if (client.url.includes("plugins.html")) {
+                 client.postMessage({context: event.request.context,
+                                     resource: resource});
+               }
+             });
+           })
+  );
+});
diff --git a/dom/workers/test/serviceworkers/fetch/real-file.txt b/dom/workers/test/serviceworkers/fetch/real-file.txt
new file mode 100644
index 000000000..3ca2088ec
--- /dev/null
+++ b/dom/workers/test/serviceworkers/fetch/real-file.txt
@@ -0,0 +1 @@
+This is a real file.
diff --git a/dom/workers/test/serviceworkers/fetch/redirect.sjs b/dom/workers/test/serviceworkers/fetch/redirect.sjs
new file mode 100644
index 000000000..dab558f4a
--- /dev/null
+++ b/dom/workers/test/serviceworkers/fetch/redirect.sjs
@@ -0,0 +1,4 @@
+function handleRequest(request, response) {
+  response.setStatusLine(request.httpVersion, 301, "Moved Permanently");
+  response.setHeader("Location", "synthesized-redirect-twice-real-file.txt");
+}
diff --git a/dom/workers/test/serviceworkers/fetch/requesturl/index.html b/dom/workers/test/serviceworkers/fetch/requesturl/index.html
new file mode 100644
index 000000000..bc3e400a9
--- /dev/null
+++ b/dom/workers/test/serviceworkers/fetch/requesturl/index.html
@@ -0,0 +1,7 @@
+
+
+
diff --git a/dom/workers/test/serviceworkers/fetch/requesturl/redirect.sjs b/dom/workers/test/serviceworkers/fetch/requesturl/redirect.sjs
new file mode 100644
index 000000000..7b92fec20
--- /dev/null
+++ b/dom/workers/test/serviceworkers/fetch/requesturl/redirect.sjs
@@ -0,0 +1,4 @@
+function handleRequest(request, response) {
+  response.setStatusLine(null, 308, "Permanent Redirect");
+  response.setHeader("Location", "http://example.org/tests/dom/workers/test/serviceworkers/fetch/requesturl/secret.html", false);
+}
diff --git a/dom/workers/test/serviceworkers/fetch/requesturl/redirector.html b/dom/workers/test/serviceworkers/fetch/requesturl/redirector.html
new file mode 100644
index 000000000..73bf4af49
--- /dev/null
+++ b/dom/workers/test/serviceworkers/fetch/requesturl/redirector.html
@@ -0,0 +1,2 @@
+
+
diff --git a/dom/workers/test/serviceworkers/fetch/requesturl/register.html b/dom/workers/test/serviceworkers/fetch/requesturl/register.html
new file mode 100644
index 000000000..19a2e022c
--- /dev/null
+++ b/dom/workers/test/serviceworkers/fetch/requesturl/register.html
@@ -0,0 +1,14 @@
+
+
diff --git a/dom/workers/test/serviceworkers/fetch/requesturl/requesturl_test.js b/dom/workers/test/serviceworkers/fetch/requesturl/requesturl_test.js
new file mode 100644
index 000000000..c8be3daf4
--- /dev/null
+++ b/dom/workers/test/serviceworkers/fetch/requesturl/requesturl_test.js
@@ -0,0 +1,17 @@
+addEventListener("fetch", event => {
+  var url = event.request.url;
+  var badURL = url.indexOf("secret.html") > -1;
+  event.respondWith(
+    new Promise(resolve => {
+      clients.matchAll().then(clients => {
+        for (var client of clients) {
+          if (client.url.indexOf("index.html") > -1) {
+            client.postMessage({status: "ok", result: !badURL, message: "Should not find a bad URL (" + url + ")"});
+            break;
+          }
+        }
+        resolve(fetch(event.request));
+      });
+    })
+  );
+});
diff --git a/dom/workers/test/serviceworkers/fetch/requesturl/secret.html b/dom/workers/test/serviceworkers/fetch/requesturl/secret.html
new file mode 100644
index 000000000..694c33635
--- /dev/null
+++ b/dom/workers/test/serviceworkers/fetch/requesturl/secret.html
@@ -0,0 +1,5 @@
+
+secret stuff
+
diff --git a/dom/workers/test/serviceworkers/fetch/requesturl/unregister.html b/dom/workers/test/serviceworkers/fetch/requesturl/unregister.html
new file mode 100644
index 000000000..1f13508fa
--- /dev/null
+++ b/dom/workers/test/serviceworkers/fetch/requesturl/unregister.html
@@ -0,0 +1,12 @@
+
+
diff --git a/dom/workers/test/serviceworkers/fetch/sandbox/index.html b/dom/workers/test/serviceworkers/fetch/sandbox/index.html
new file mode 100644
index 000000000..1094a3995
--- /dev/null
+++ b/dom/workers/test/serviceworkers/fetch/sandbox/index.html
@@ -0,0 +1,5 @@
+
+
diff --git a/dom/workers/test/serviceworkers/fetch/sandbox/intercepted_index.html b/dom/workers/test/serviceworkers/fetch/sandbox/intercepted_index.html
new file mode 100644
index 000000000..87261a495
--- /dev/null
+++ b/dom/workers/test/serviceworkers/fetch/sandbox/intercepted_index.html
@@ -0,0 +1,5 @@
+
+
diff --git a/dom/workers/test/serviceworkers/fetch/sandbox/register.html b/dom/workers/test/serviceworkers/fetch/sandbox/register.html
new file mode 100644
index 000000000..427b1a8da
--- /dev/null
+++ b/dom/workers/test/serviceworkers/fetch/sandbox/register.html
@@ -0,0 +1,14 @@
+
+
diff --git a/dom/workers/test/serviceworkers/fetch/sandbox/sandbox_test.js b/dom/workers/test/serviceworkers/fetch/sandbox/sandbox_test.js
new file mode 100644
index 000000000..1ed351794
--- /dev/null
+++ b/dom/workers/test/serviceworkers/fetch/sandbox/sandbox_test.js
@@ -0,0 +1,5 @@
+self.addEventListener("fetch", function(event) {
+  if (event.request.url.indexOf("index.html") >= 0) {
+    event.respondWith(fetch("intercepted_index.html"));
+  }
+});
diff --git a/dom/workers/test/serviceworkers/fetch/sandbox/unregister.html b/dom/workers/test/serviceworkers/fetch/sandbox/unregister.html
new file mode 100644
index 000000000..1f13508fa
--- /dev/null
+++ b/dom/workers/test/serviceworkers/fetch/sandbox/unregister.html
@@ -0,0 +1,12 @@
+
+
diff --git a/dom/workers/test/serviceworkers/fetch/upgrade-insecure/embedder.html b/dom/workers/test/serviceworkers/fetch/upgrade-insecure/embedder.html
new file mode 100644
index 000000000..6098a45dd
--- /dev/null
+++ b/dom/workers/test/serviceworkers/fetch/upgrade-insecure/embedder.html
@@ -0,0 +1,10 @@
+
+
+
diff --git a/dom/workers/test/serviceworkers/fetch/upgrade-insecure/embedder.html^headers^ b/dom/workers/test/serviceworkers/fetch/upgrade-insecure/embedder.html^headers^
new file mode 100644
index 000000000..602d9dc38
--- /dev/null
+++ b/dom/workers/test/serviceworkers/fetch/upgrade-insecure/embedder.html^headers^
@@ -0,0 +1 @@
+Content-Security-Policy: upgrade-insecure-requests
diff --git a/dom/workers/test/serviceworkers/fetch/upgrade-insecure/image-20px.png b/dom/workers/test/serviceworkers/fetch/upgrade-insecure/image-20px.png
new file mode 100644
index 000000000..ae6a8a6b8
Binary files /dev/null and b/dom/workers/test/serviceworkers/fetch/upgrade-insecure/image-20px.png differ
diff --git a/dom/workers/test/serviceworkers/fetch/upgrade-insecure/image-40px.png b/dom/workers/test/serviceworkers/fetch/upgrade-insecure/image-40px.png
new file mode 100644
index 000000000..fe391dc8a
Binary files /dev/null and b/dom/workers/test/serviceworkers/fetch/upgrade-insecure/image-40px.png differ
diff --git a/dom/workers/test/serviceworkers/fetch/upgrade-insecure/image.html b/dom/workers/test/serviceworkers/fetch/upgrade-insecure/image.html
new file mode 100644
index 000000000..34e24e35a
--- /dev/null
+++ b/dom/workers/test/serviceworkers/fetch/upgrade-insecure/image.html
@@ -0,0 +1,13 @@
+
+
diff --git a/dom/workers/test/serviceworkers/fetch/upgrade-insecure/realindex.html b/dom/workers/test/serviceworkers/fetch/upgrade-insecure/realindex.html
new file mode 100644
index 000000000..aaa255aad
--- /dev/null
+++ b/dom/workers/test/serviceworkers/fetch/upgrade-insecure/realindex.html
@@ -0,0 +1,4 @@
+
+
diff --git a/dom/workers/test/serviceworkers/fetch/upgrade-insecure/register.html b/dom/workers/test/serviceworkers/fetch/upgrade-insecure/register.html
new file mode 100644
index 000000000..6309b9b21
--- /dev/null
+++ b/dom/workers/test/serviceworkers/fetch/upgrade-insecure/register.html
@@ -0,0 +1,14 @@
+
+
diff --git a/dom/workers/test/serviceworkers/fetch/upgrade-insecure/unregister.html b/dom/workers/test/serviceworkers/fetch/upgrade-insecure/unregister.html
new file mode 100644
index 000000000..1f13508fa
--- /dev/null
+++ b/dom/workers/test/serviceworkers/fetch/upgrade-insecure/unregister.html
@@ -0,0 +1,12 @@
+
+
diff --git a/dom/workers/test/serviceworkers/fetch/upgrade-insecure/upgrade-insecure_test.js b/dom/workers/test/serviceworkers/fetch/upgrade-insecure/upgrade-insecure_test.js
new file mode 100644
index 000000000..ab54164ed
--- /dev/null
+++ b/dom/workers/test/serviceworkers/fetch/upgrade-insecure/upgrade-insecure_test.js
@@ -0,0 +1,11 @@
+self.addEventListener("fetch", function(event) {
+  if (event.request.url.indexOf("index.html") >= 0) {
+    event.respondWith(fetch("realindex.html"));
+  } else if (event.request.url.indexOf("image-20px.png") >= 0) {
+    if (event.request.url.indexOf("https://") == 0) {
+      event.respondWith(fetch("image-40px.png"));
+    } else {
+      event.respondWith(Response.error());
+    }
+  }
+});
diff --git a/dom/workers/test/serviceworkers/fetch_event_worker.js b/dom/workers/test/serviceworkers/fetch_event_worker.js
new file mode 100644
index 000000000..1caef71e8
--- /dev/null
+++ b/dom/workers/test/serviceworkers/fetch_event_worker.js
@@ -0,0 +1,337 @@
+var seenIndex = false;
+
+onfetch = function(ev) {
+  if (ev.request.url.includes("ignore")) {
+    return;
+  }
+
+  if (ev.request.url.includes("bare-synthesized.txt")) {
+    ev.respondWith(Promise.resolve(
+      new Response("synthesized response body", {})
+    ));
+  }
+
+  else if (ev.request.url.includes('file_CrossSiteXHR_server.sjs')) {
+    // N.B. this response would break the rules of CORS if it were allowed, but
+    //      this test relies upon the preflight request not being intercepted and
+    //      thus this response should not be used.
+    if (ev.request.method == 'OPTIONS') {
+      ev.respondWith(new Response('', {headers: {'Access-Control-Allow-Origin': '*',
+                                                 'Access-Control-Allow-Headers': 'X-Unsafe'}}))
+    } else if (ev.request.url.includes('example.org')) {
+      ev.respondWith(fetch(ev.request));
+    }
+  }
+
+  else if (ev.request.url.includes("synthesized-404.txt")) {
+    ev.respondWith(Promise.resolve(
+      new Response("synthesized response body", { status: 404 })
+    ));
+  }
+
+  else if (ev.request.url.includes("synthesized-headers.txt")) {
+    ev.respondWith(Promise.resolve(
+      new Response("synthesized response body", {
+        headers: {
+          "X-Custom-Greeting": "Hello"
+        }
+      })
+    ));
+  }
+
+  else if (ev.request.url.includes("test-respondwith-response.txt")) {
+    ev.respondWith(new Response("test-respondwith-response response body", {}));
+  }
+
+  else if (ev.request.url.includes("synthesized-redirect-real-file.txt")) {
+    ev.respondWith(Promise.resolve(
+      Response.redirect("fetch/real-file.txt")
+    ));
+  }
+
+  else if (ev.request.url.includes("synthesized-redirect-twice-real-file.txt")) {
+    ev.respondWith(Promise.resolve(
+      Response.redirect("synthesized-redirect-real-file.txt")
+    ));
+  }
+
+  else if (ev.request.url.includes("synthesized-redirect-synthesized.txt")) {
+    ev.respondWith(Promise.resolve(
+      Response.redirect("bare-synthesized.txt")
+    ));
+  }
+
+  else if (ev.request.url.includes("synthesized-redirect-twice-synthesized.txt")) {
+    ev.respondWith(Promise.resolve(
+      Response.redirect("synthesized-redirect-synthesized.txt")
+     ));
+   }
+
+  else if (ev.request.url.includes("rejected.txt")) {
+    ev.respondWith(Promise.reject());
+  }
+
+  else if (ev.request.url.includes("nonresponse.txt")) {
+    ev.respondWith(Promise.resolve(5));
+  }
+
+  else if (ev.request.url.includes("nonresponse2.txt")) {
+    ev.respondWith(Promise.resolve({}));
+  }
+
+  else if (ev.request.url.includes("nonpromise.txt")) {
+    try {
+      // This should coerce to Promise(5) instead of throwing
+      ev.respondWith(5);
+    } catch (e) {
+      // test is expecting failure, so return a success if we get a thrown
+      // exception
+      ev.respondWith(new Response('respondWith(5) threw ' + e));
+    }
+  }
+
+  else if (ev.request.url.includes("headers.txt")) {
+    var ok = true;
+    ok &= ev.request.headers.get("X-Test1") == "header1";
+    ok &= ev.request.headers.get("X-Test2") == "header2";
+    ev.respondWith(Promise.resolve(
+      new Response(ok.toString(), {})
+    ));
+  }
+
+  else if (ev.request.url.includes('user-pass')) {
+    ev.respondWith(new Response(ev.request.url));
+  }
+
+  else if (ev.request.url.includes("nonexistent_image.gif")) {
+    var imageAsBinaryString = atob("R0lGODlhAQABAIAAAAUEBAAAACwAAAAAAQABAAACAkQBADs");
+    var imageLength = imageAsBinaryString.length;
+
+    // If we just pass |imageAsBinaryString| to the Response constructor, an
+    // encoding conversion occurs that corrupts the image. Instead, we need to
+    // convert it to a typed array.
+    // typed array.
+    var imageAsArray = new Uint8Array(imageLength);
+    for (var i = 0; i < imageLength; ++i) {
+      imageAsArray[i] = imageAsBinaryString.charCodeAt(i);
+    }
+
+    ev.respondWith(Promise.resolve(
+      new Response(imageAsArray, { headers: { "Content-Type": "image/gif" } })
+    ));
+  }
+
+  else if (ev.request.url.includes("nonexistent_script.js")) {
+    ev.respondWith(Promise.resolve(
+      new Response("check_intercepted_script();", {})
+    ));
+  }
+
+  else if (ev.request.url.includes("nonexistent_stylesheet.css")) {
+    ev.respondWith(Promise.resolve(
+      new Response("#style-test { background-color: black !important; }", {
+        headers : {
+          "Content-Type": "text/css"
+        }
+      })
+    ));
+  }
+
+  else if (ev.request.url.includes("nonexistent_page.html")) {
+    ev.respondWith(Promise.resolve(
+      new Response("", {
+        headers : {
+          "Content-Type": "text/html"
+        }
+      })
+    ));
+  }
+
+  else if (ev.request.url.includes("navigate.html")) {
+    var navigateModeCorrectlyChecked = false;
+    var requests = [ // should not throw
+      new Request(ev.request),
+      new Request(ev.request, undefined),
+      new Request(ev.request, null),
+      new Request(ev.request, {}),
+      new Request(ev.request, {someUnrelatedProperty: 42}),
+    ];
+    try {
+      var request3 = new Request(ev.request, {method: "GET"}); // should throw
+    } catch(e) {
+      navigateModeCorrectlyChecked = requests[0].mode == "navigate";
+    }
+    if (navigateModeCorrectlyChecked) {
+      ev.respondWith(Promise.resolve(
+        new Response("", {
+          headers : {
+            "Content-Type": "text/html"
+          }
+        })
+      ));
+    }
+  }
+
+  else if (ev.request.url.includes("nonexistent_worker_script.js")) {
+    ev.respondWith(Promise.resolve(
+      new Response("postMessage('worker-intercept-success')", {})
+    ));
+  }
+
+  else if (ev.request.url.includes("nonexistent_imported_script.js")) {
+    ev.respondWith(Promise.resolve(
+      new Response("check_intercepted_script();", {})
+    ));
+  }
+
+  else if (ev.request.url.includes("deliver-gzip")) {
+    // Don't handle the request, this will make Necko perform a network request, at
+    // which point SetApplyConversion must be re-enabled, otherwise the request
+    // will fail.
+    return;
+  }
+
+  else if (ev.request.url.includes("hello.gz")) {
+    ev.respondWith(fetch("fetch/deliver-gzip.sjs"));
+  }
+
+  else if (ev.request.url.includes("hello-after-extracting.gz")) {
+    ev.respondWith(fetch("fetch/deliver-gzip.sjs").then(function(res) {
+      return res.text().then(function(body) {
+        return new Response(body, { status: res.status, statusText: res.statusText, headers: res.headers });
+      });
+    }));
+  }
+
+  else if (ev.request.url.includes('opaque-on-same-origin')) {
+    var url = 'http://example.com/tests/dom/security/test/cors/file_CrossSiteXHR_server.sjs?status=200';
+    ev.respondWith(fetch(url, { mode: 'no-cors' }));
+  }
+
+  else if (ev.request.url.includes('opaque-no-cors')) {
+    if (ev.request.mode != "no-cors") {
+      ev.respondWith(Promise.reject());
+      return;
+    }
+
+    var url = 'http://example.com/tests/dom/security/test/cors/file_CrossSiteXHR_server.sjs?status=200';
+    ev.respondWith(fetch(url, { mode: ev.request.mode }));
+  }
+
+  else if (ev.request.url.includes('cors-for-no-cors')) {
+    if (ev.request.mode != "no-cors") {
+      ev.respondWith(Promise.reject());
+      return;
+    }
+
+    var url = 'http://example.com/tests/dom/security/test/cors/file_CrossSiteXHR_server.sjs?status=200&allowOrigin=*';
+    ev.respondWith(fetch(url));
+  }
+
+  else if (ev.request.url.includes('example.com')) {
+    ev.respondWith(fetch(ev.request));
+  }
+
+  else if (ev.request.url.includes("index.html")) {
+    if (seenIndex) {
+        var body = "";
+        ev.respondWith(new Response(body, {headers: {'Content-Type': 'text/html'}}));
+    } else {
+      seenIndex = true;
+      ev.respondWith(fetch(ev.request.url));
+    }
+  }
+
+  else if (ev.request.url.includes("body-")) {
+    ev.respondWith(ev.request.text().then(function (body) {
+      return new Response(body + body);
+    }));
+  }
+
+  else if (ev.request.url.includes('something.txt')) {
+    ev.respondWith(Response.redirect('fetch/somethingelse.txt'));
+  }
+
+  else if (ev.request.url.includes('somethingelse.txt')) {
+    ev.respondWith(new Response('something else response body', {}));
+  }
+
+  else if (ev.request.url.includes('redirect_serviceworker.sjs')) {
+    // The redirect_serviceworker.sjs server-side JavaScript file redirects to
+    // 'http://mochi.test:8888/tests/dom/workers/test/serviceworkers/worker.js'
+    // The redirected fetch should not go through the SW since the original
+    // fetch was initiated from a SW.
+    ev.respondWith(fetch('redirect_serviceworker.sjs'));
+  }
+
+  else if (ev.request.url.includes('load_cross_origin_xml_document_synthetic.xml')) {
+    if (ev.request.mode != 'same-origin') {
+      ev.respondWith(Promise.reject());
+      return;
+    }
+
+    ev.respondWith(Promise.resolve(
+      new Response("body", { headers: {'Content-Type': 'text/xtml'}})
+    ));
+  }
+
+  else if (ev.request.url.includes('load_cross_origin_xml_document_cors.xml')) {
+    if (ev.request.mode != 'same-origin') {
+      ev.respondWith(Promise.reject());
+      return;
+    }
+
+    var url = 'http://example.com/tests/dom/security/test/cors/file_CrossSiteXHR_server.sjs?status=200&allowOrigin=*';
+    ev.respondWith(fetch(url, { mode: 'cors' }));
+  }
+
+  else if (ev.request.url.includes('load_cross_origin_xml_document_opaque.xml')) {
+    if (ev.request.mode != 'same-origin') {
+      Promise.resolve(
+        new Response("Invalid Request mode", { headers: {'Content-Type': 'text/xtml'}})
+      );
+      return;
+    }
+
+    var url = 'http://example.com/tests/dom/security/test/cors/file_CrossSiteXHR_server.sjs?status=200';
+    ev.respondWith(fetch(url, { mode: 'no-cors' }));
+  }
+
+  else if (ev.request.url.includes('xhr-method-test.txt')) {
+    ev.respondWith(new Response('intercepted ' + ev.request.method));
+  }
+
+  else if (ev.request.url.includes('empty-header')) {
+    if (!ev.request.headers.has("emptyheader") ||
+        ev.request.headers.get("emptyheader") !== "") {
+      ev.respondWith(Promise.reject());
+      return;
+    }
+    ev.respondWith(new Response("emptyheader"));
+  }
+
+  else if (ev.request.url.includes('fetchevent-extendable')) {
+    if (ev instanceof ExtendableEvent) {
+      ev.respondWith(new Response("extendable"));
+    } else {
+      ev.respondWith(Promise.reject());
+    }
+  }
+
+  else if (ev.request.url.includes('fetchevent-request')) {
+    var threw = false;
+    try {
+      new FetchEvent("foo");
+    } catch(e) {
+      if (e.name == "TypeError") {
+        threw = true;
+      }
+    } finally {
+      ev.respondWith(new Response(threw ? "non-nullable" : "nullable"));
+    }
+  }
+};
diff --git a/dom/workers/test/serviceworkers/file_blob_response_worker.js b/dom/workers/test/serviceworkers/file_blob_response_worker.js
new file mode 100644
index 000000000..4b4379d0b
--- /dev/null
+++ b/dom/workers/test/serviceworkers/file_blob_response_worker.js
@@ -0,0 +1,38 @@
+function makeFileBlob(obj) {
+  return new Promise(function(resolve, reject) {
+    var request = indexedDB.open('file_blob_response_worker', 1);
+    request.onerror = reject;
+    request.onupgradeneeded = function(evt) {
+      var db = evt.target.result;
+      db.onerror = reject;
+
+      var objectStore = db.createObjectStore('test', { autoIncrement: true });
+      var index = objectStore.createIndex('test', 'index');
+    };
+
+    request.onsuccess = function(evt) {
+      var db = evt.target.result;
+      db.onerror = reject;
+
+      var blob = new Blob([JSON.stringify(obj)],
+                          { type: 'application/json' });
+      var data = { blob: blob, index: 5 };
+
+      objectStore = db.transaction('test', 'readwrite').objectStore('test');
+      objectStore.add(data).onsuccess = function(evt) {
+        var key = evt.target.result;
+        objectStore = db.transaction('test').objectStore('test');
+        objectStore.get(key).onsuccess = function(evt) {
+          resolve(evt.target.result.blob);
+        };
+      };
+    };
+  });
+}
+
+self.addEventListener('fetch', function(evt) {
+  var result = { value: 'success' };
+  evt.respondWith(makeFileBlob(result).then(function(blob) {
+    return new Response(blob)
+  }));
+});
diff --git a/dom/workers/test/serviceworkers/force_refresh_browser_worker.js b/dom/workers/test/serviceworkers/force_refresh_browser_worker.js
new file mode 100644
index 000000000..96d9d0f17
--- /dev/null
+++ b/dom/workers/test/serviceworkers/force_refresh_browser_worker.js
@@ -0,0 +1,34 @@
+var name = 'browserRefresherCache';
+
+self.addEventListener('install', function(event) {
+  event.waitUntil(
+    Promise.all([caches.open(name),
+                 fetch('./browser_cached_force_refresh.html')]).then(function(results) {
+      var cache = results[0];
+      var response = results[1];
+      return cache.put('./browser_base_force_refresh.html', response);
+    })
+  );
+});
+
+self.addEventListener('fetch', function (event) {
+  event.respondWith(
+    caches.open(name).then(function(cache) {
+      return cache.match(event.request);
+    }).then(function(response) {
+      return response || fetch(event.request);
+    })
+  );
+});
+
+self.addEventListener('message', function (event) {
+  if (event.data.type === 'GET_UNCONTROLLED_CLIENTS') {
+    event.waitUntil(clients.matchAll({ includeUncontrolled: true })
+      .then(function(clientList) {
+        var resultList = clientList.map(function(c) {
+          return { url: c.url, frameType: c.frameType };
+        });
+        event.source.postMessage({ type: 'CLIENTS', detail: resultList });
+      }));
+  }
+});
diff --git a/dom/workers/test/serviceworkers/force_refresh_worker.js b/dom/workers/test/serviceworkers/force_refresh_worker.js
new file mode 100644
index 000000000..f0752d0cb
--- /dev/null
+++ b/dom/workers/test/serviceworkers/force_refresh_worker.js
@@ -0,0 +1,33 @@
+var name = 'refresherCache';
+
+self.addEventListener('install', function(event) {
+  event.waitUntil(
+    Promise.all([caches.open(name),
+                 fetch('./sw_clients/refresher_cached.html'),
+                 fetch('./sw_clients/refresher_cached_compressed.html')]).then(function(results) {
+      var cache = results[0];
+      var response = results[1];
+      var compressed = results[2];
+      return Promise.all([cache.put('./sw_clients/refresher.html', response),
+                          cache.put('./sw_clients/refresher_compressed.html', compressed)]);
+    })
+  );
+});
+
+self.addEventListener('fetch', function (event) {
+  event.respondWith(
+    caches.open(name).then(function(cache) {
+      return cache.match(event.request);
+    }).then(function(response) {
+      // If this is one of our primary cached responses, then the window
+      // must have generated the request via a normal window reload.  That
+      // should be detectable in the event.request.cache attribute.
+      if (response && event.request.cache !== 'no-cache') {
+        dump('### ### FetchEvent.request.cache is "' + event.request.cache +
+             '" instead of expected "no-cache"\n');
+        return Response.error();
+      }
+      return response || fetch(event.request);
+    })
+  );
+});
diff --git a/dom/workers/test/serviceworkers/gzip_redirect_worker.js b/dom/workers/test/serviceworkers/gzip_redirect_worker.js
new file mode 100644
index 000000000..72aeba222
--- /dev/null
+++ b/dom/workers/test/serviceworkers/gzip_redirect_worker.js
@@ -0,0 +1,13 @@
+self.addEventListener('fetch', function (event) {
+  if (!event.request.url.endsWith('sw_clients/does_not_exist.html')) {
+    return;
+  }
+
+  event.respondWith(new Response('', {
+    status: 301,
+    statusText: 'Moved Permanently',
+    headers: {
+      'Location': 'refresher_compressed.html'
+    }
+  }));
+});
diff --git a/dom/workers/test/serviceworkers/header_checker.sjs b/dom/workers/test/serviceworkers/header_checker.sjs
new file mode 100644
index 000000000..706104103
--- /dev/null
+++ b/dom/workers/test/serviceworkers/header_checker.sjs
@@ -0,0 +1,9 @@
+function handleRequest(request, response) {
+  if (request.getHeader("Service-Worker") === "script") {
+    response.setStatusLine("1.1", 200, "OK");
+    response.setHeader("Content-Type", "text/javascript");
+    response.write("// empty");
+  } else {
+    response.setStatusLine("1.1", 404, "Not Found");
+  }
+}
diff --git a/dom/workers/test/serviceworkers/hello.html b/dom/workers/test/serviceworkers/hello.html
new file mode 100644
index 000000000..97eb03c90
--- /dev/null
+++ b/dom/workers/test/serviceworkers/hello.html
@@ -0,0 +1,9 @@
+
+
+  
+    
+  
+  
+    Hello.
+  
+
diff --git a/dom/workers/test/serviceworkers/importscript.sjs b/dom/workers/test/serviceworkers/importscript.sjs
new file mode 100644
index 000000000..6d177a734
--- /dev/null
+++ b/dom/workers/test/serviceworkers/importscript.sjs
@@ -0,0 +1,11 @@
+function handleRequest(request, response) {
+  if (request.queryString == 'clearcounter') {
+    setState('counter', '');
+  } else if (!getState('counter')) {
+    response.setHeader("Content-Type", "application/javascript", false);
+    response.write("callByScript();");
+    setState('counter', '1');
+  } else {
+    response.write("no cache no party!");
+  }
+}
diff --git a/dom/workers/test/serviceworkers/importscript_worker.js b/dom/workers/test/serviceworkers/importscript_worker.js
new file mode 100644
index 000000000..3cddec194
--- /dev/null
+++ b/dom/workers/test/serviceworkers/importscript_worker.js
@@ -0,0 +1,37 @@
+var counter = 0;
+function callByScript() {
+  ++counter;
+}
+
+// Use multiple scripts in this load to verify we support that case correctly.
+// See bug 1249351 for a case where we broke this.
+importScripts('lorem_script.js', 'importscript.sjs');
+
+importScripts('importscript.sjs');
+
+var missingScriptFailed = false;
+try {
+  importScripts(['there-is-nothing-here.js']);
+} catch(e) {
+  missingScriptFailed = true;
+}
+
+onmessage = function(e) {
+  self.clients.matchAll().then(function(res) {
+    if (!res.length) {
+      dump("ERROR: no clients are currently controlled.\n");
+    }
+
+    if (!missingScriptFailed) {
+      res[0].postMessage("KO");
+    }
+
+    try {
+      importScripts(['importscript.sjs']);
+      res[0].postMessage("KO");
+      return;
+    } catch(e) {}
+
+    res[0].postMessage(counter == 2 ? "OK" : "KO");
+  });
+};
diff --git a/dom/workers/test/serviceworkers/install_event_error_worker.js b/dom/workers/test/serviceworkers/install_event_error_worker.js
new file mode 100644
index 000000000..c06d648b8
--- /dev/null
+++ b/dom/workers/test/serviceworkers/install_event_error_worker.js
@@ -0,0 +1,4 @@
+// Worker that errors on receiving an install event.
+oninstall = function(e) {
+  undefined.doSomething;
+};
diff --git a/dom/workers/test/serviceworkers/install_event_worker.js b/dom/workers/test/serviceworkers/install_event_worker.js
new file mode 100644
index 000000000..f965d28aa
--- /dev/null
+++ b/dom/workers/test/serviceworkers/install_event_worker.js
@@ -0,0 +1,3 @@
+oninstall = function(e) {
+  dump("Got install event\n");
+}
diff --git a/dom/workers/test/serviceworkers/lorem_script.js b/dom/workers/test/serviceworkers/lorem_script.js
new file mode 100644
index 000000000..5502a44da
--- /dev/null
+++ b/dom/workers/test/serviceworkers/lorem_script.js
@@ -0,0 +1,8 @@
+var lorem_str = `
+Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor
+incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis
+nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
+consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum
+dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident,
+sunt in culpa qui officia deserunt mollit anim id est laborum.
+`
diff --git a/dom/workers/test/serviceworkers/match_all_advanced_worker.js b/dom/workers/test/serviceworkers/match_all_advanced_worker.js
new file mode 100644
index 000000000..3721aedfe
--- /dev/null
+++ b/dom/workers/test/serviceworkers/match_all_advanced_worker.js
@@ -0,0 +1,5 @@
+onmessage = function(e) {
+  self.clients.matchAll().then(function(clients) {
+    e.source.postMessage(clients.length);
+  });
+}
diff --git a/dom/workers/test/serviceworkers/match_all_client/match_all_client_id.html b/dom/workers/test/serviceworkers/match_all_client/match_all_client_id.html
new file mode 100644
index 000000000..7ac6fc9d0
--- /dev/null
+++ b/dom/workers/test/serviceworkers/match_all_client/match_all_client_id.html
@@ -0,0 +1,31 @@
+
+
+
+
+  Bug 1139425 - controlled page
+
+
+
+
+
+
diff --git a/dom/workers/test/serviceworkers/match_all_client_id_worker.js b/dom/workers/test/serviceworkers/match_all_client_id_worker.js
new file mode 100644
index 000000000..a7d9ff594
--- /dev/null
+++ b/dom/workers/test/serviceworkers/match_all_client_id_worker.js
@@ -0,0 +1,28 @@
+onmessage = function(e) {
+  dump("MatchAllClientIdWorker:" + e.data + "\n");
+  var id = [];
+  var iterations = 5;
+  var counter = 0;
+
+  for (var i = 0; i < iterations; i++) {
+    self.clients.matchAll().then(function(res) {
+      if (!res.length) {
+        dump("ERROR: no clients are currently controlled.\n");
+      }
+
+      client = res[0];
+      id[counter] = client.id;
+      counter++;
+      if (counter >= iterations) {
+        var response = true;
+        for (var index = 1; index < iterations; index++) {
+          if (id[0] != id[index]) {
+            response = false;
+            break;
+          }
+        }
+        client.postMessage(response);
+      }
+    });
+  }
+}
diff --git a/dom/workers/test/serviceworkers/match_all_clients/match_all_controlled.html b/dom/workers/test/serviceworkers/match_all_clients/match_all_controlled.html
new file mode 100644
index 000000000..25317b9fc
--- /dev/null
+++ b/dom/workers/test/serviceworkers/match_all_clients/match_all_controlled.html
@@ -0,0 +1,65 @@
+
+
+
+
+  Bug 1058311 - controlled page
+
+
+
+
+
+
diff --git a/dom/workers/test/serviceworkers/match_all_properties_worker.js b/dom/workers/test/serviceworkers/match_all_properties_worker.js
new file mode 100644
index 000000000..f007a5ce8
--- /dev/null
+++ b/dom/workers/test/serviceworkers/match_all_properties_worker.js
@@ -0,0 +1,20 @@
+onmessage = function(e) {
+  dump("MatchAllPropertiesWorker:" + e.data + "\n");
+  self.clients.matchAll().then(function(res) {
+    if (!res.length) {
+      dump("ERROR: no clients are currently controlled.\n");
+    }
+
+    for (i = 0; i < res.length; i++) {
+      client = res[i];
+      response = {
+        id: client.id,
+        url: client.url,
+        visibilityState: client.visibilityState,
+        focused: client.focused,
+        frameType: client.frameType
+      };
+      client.postMessage(response);
+    }
+  });
+}
diff --git a/dom/workers/test/serviceworkers/match_all_worker.js b/dom/workers/test/serviceworkers/match_all_worker.js
new file mode 100644
index 000000000..9d1c8c363
--- /dev/null
+++ b/dom/workers/test/serviceworkers/match_all_worker.js
@@ -0,0 +1,10 @@
+function loop() {
+  self.clients.matchAll().then(function(result) {
+    setTimeout(loop, 0);
+  });
+}
+
+onactivate = function(e) {
+  // spam matchAll until the worker is closed.
+  loop();
+}
diff --git a/dom/workers/test/serviceworkers/message_posting_worker.js b/dom/workers/test/serviceworkers/message_posting_worker.js
new file mode 100644
index 000000000..26db99775
--- /dev/null
+++ b/dom/workers/test/serviceworkers/message_posting_worker.js
@@ -0,0 +1,8 @@
+onmessage = function(e) {
+  self.clients.matchAll().then(function(res) {
+    if (!res.length) {
+      dump("ERROR: no clients are currently controlled.\n");
+    }
+    res[0].postMessage(e.data);
+  });
+};
diff --git a/dom/workers/test/serviceworkers/message_receiver.html b/dom/workers/test/serviceworkers/message_receiver.html
new file mode 100644
index 000000000..82cb587c7
--- /dev/null
+++ b/dom/workers/test/serviceworkers/message_receiver.html
@@ -0,0 +1,6 @@
+
+
diff --git a/dom/workers/test/serviceworkers/mochitest.ini b/dom/workers/test/serviceworkers/mochitest.ini
new file mode 100644
index 000000000..29ac9e036
--- /dev/null
+++ b/dom/workers/test/serviceworkers/mochitest.ini
@@ -0,0 +1,317 @@
+[DEFAULT]
+
+support-files =
+  worker.js
+  worker2.js
+  worker3.js
+  fetch_event_worker.js
+  parse_error_worker.js
+  activate_event_error_worker.js
+  install_event_worker.js
+  install_event_error_worker.js
+  simpleregister/index.html
+  simpleregister/ready.html
+  controller/index.html
+  unregister/index.html
+  unregister/unregister.html
+  workerUpdate/update.html
+  sw_clients/simple.html
+  sw_clients/service_worker_controlled.html
+  match_all_worker.js
+  match_all_advanced_worker.js
+  worker_unregister.js
+  worker_update.js
+  message_posting_worker.js
+  fetch/index.html
+  fetch/fetch_worker_script.js
+  fetch/fetch_tests.js
+  fetch/deliver-gzip.sjs
+  fetch/redirect.sjs
+  fetch/real-file.txt
+  fetch/context/index.html
+  fetch/context/register.html
+  fetch/context/unregister.html
+  fetch/context/context_test.js
+  fetch/context/realimg.jpg
+  fetch/context/realaudio.ogg
+  fetch/context/beacon.sjs
+  fetch/context/csp-violate.sjs
+  fetch/context/ping.html
+  fetch/context/worker.js
+  fetch/context/parentworker.js
+  fetch/context/sharedworker.js
+  fetch/context/parentsharedworker.js
+  fetch/context/xml.xml
+  fetch/hsts/hsts_test.js
+  fetch/hsts/embedder.html
+  fetch/hsts/image.html
+  fetch/hsts/image-20px.png
+  fetch/hsts/image-40px.png
+  fetch/hsts/realindex.html
+  fetch/hsts/register.html
+  fetch/hsts/register.html^headers^
+  fetch/hsts/unregister.html
+  fetch/https/index.html
+  fetch/https/register.html
+  fetch/https/unregister.html
+  fetch/https/https_test.js
+  fetch/https/clonedresponse/index.html
+  fetch/https/clonedresponse/register.html
+  fetch/https/clonedresponse/unregister.html
+  fetch/https/clonedresponse/https_test.js
+  fetch/imagecache/image-20px.png
+  fetch/imagecache/image-40px.png
+  fetch/imagecache/imagecache_test.js
+  fetch/imagecache/index.html
+  fetch/imagecache/postmortem.html
+  fetch/imagecache/register.html
+  fetch/imagecache/unregister.html
+  fetch/imagecache-maxage/index.html
+  fetch/imagecache-maxage/image-20px.png
+  fetch/imagecache-maxage/image-40px.png
+  fetch/imagecache-maxage/maxage_test.js
+  fetch/imagecache-maxage/register.html
+  fetch/imagecache-maxage/unregister.html
+  fetch/importscript-mixedcontent/register.html
+  fetch/importscript-mixedcontent/unregister.html
+  fetch/importscript-mixedcontent/https_test.js
+  fetch/interrupt.sjs
+  fetch/origin/index.sjs
+  fetch/origin/index-to-https.sjs
+  fetch/origin/realindex.html
+  fetch/origin/realindex.html^headers^
+  fetch/origin/register.html
+  fetch/origin/unregister.html
+  fetch/origin/origin_test.js
+  fetch/origin/https/index-https.sjs
+  fetch/origin/https/realindex.html
+  fetch/origin/https/realindex.html^headers^
+  fetch/origin/https/register.html
+  fetch/origin/https/unregister.html
+  fetch/origin/https/origin_test.js
+  fetch/requesturl/index.html
+  fetch/requesturl/redirect.sjs
+  fetch/requesturl/redirector.html
+  fetch/requesturl/register.html
+  fetch/requesturl/requesturl_test.js
+  fetch/requesturl/secret.html
+  fetch/requesturl/unregister.html
+  fetch/sandbox/index.html
+  fetch/sandbox/intercepted_index.html
+  fetch/sandbox/register.html
+  fetch/sandbox/unregister.html
+  fetch/sandbox/sandbox_test.js
+  fetch/upgrade-insecure/upgrade-insecure_test.js
+  fetch/upgrade-insecure/embedder.html
+  fetch/upgrade-insecure/embedder.html^headers^
+  fetch/upgrade-insecure/image.html
+  fetch/upgrade-insecure/image-20px.png
+  fetch/upgrade-insecure/image-40px.png
+  fetch/upgrade-insecure/realindex.html
+  fetch/upgrade-insecure/register.html
+  fetch/upgrade-insecure/unregister.html
+  match_all_properties_worker.js
+  match_all_clients/match_all_controlled.html
+  test_serviceworker_interfaces.js
+  serviceworker_wrapper.js
+  message_receiver.html
+  close_test.js
+  serviceworker_not_sharedworker.js
+  match_all_client/match_all_client_id.html
+  match_all_client_id_worker.js
+  source_message_posting_worker.js
+  scope/scope_worker.js
+  redirect_serviceworker.sjs
+  importscript.sjs
+  importscript_worker.js
+  bug1151916_worker.js
+  bug1151916_driver.html
+  bug1240436_worker.js
+  notificationclick.html
+  notificationclick-otherwindow.html
+  notificationclick.js
+  notificationclick_focus.html
+  notificationclick_focus.js
+  notificationclose.html
+  notificationclose.js
+  worker_updatefoundevent.js
+  worker_updatefoundevent2.js
+  updatefoundevent.html
+  empty.js
+  notification_constructor_error.js
+  notification_get_sw.js
+  notification/register.html
+  notification/unregister.html
+  notification/listener.html
+  notification_alt/register.html
+  notification_alt/unregister.html
+  sanitize/frame.html
+  sanitize/register.html
+  sanitize/example_check_and_unregister.html
+  sanitize_worker.js
+  swa/worker_scope_different.js
+  swa/worker_scope_different.js^headers^
+  swa/worker_scope_different2.js
+  swa/worker_scope_different2.js^headers^
+  swa/worker_scope_precise.js
+  swa/worker_scope_precise.js^headers^
+  swa/worker_scope_too_deep.js
+  swa/worker_scope_too_deep.js^headers^
+  swa/worker_scope_too_narrow.js
+  swa/worker_scope_too_narrow.js^headers^
+  claim_oninstall_worker.js
+  claim_worker_1.js
+  claim_worker_2.js
+  claim_clients/client.html
+  claim_fetch_worker.js
+  force_refresh_worker.js
+  sw_clients/refresher.html
+  sw_clients/refresher_compressed.html
+  sw_clients/refresher_compressed.html^headers^
+  sw_clients/refresher_cached.html
+  sw_clients/refresher_cached_compressed.html
+  sw_clients/refresher_cached_compressed.html^headers^
+  strict_mode_warning.js
+  skip_waiting_installed_worker.js
+  skip_waiting_scope/index.html
+  thirdparty/iframe1.html
+  thirdparty/iframe2.html
+  thirdparty/register.html
+  thirdparty/unregister.html
+  thirdparty/sw.js
+  register_https.html
+  gzip_redirect_worker.js
+  sw_clients/navigator.html
+  eval_worker.js
+  test_eval_allowed.html^headers^
+  opaque_intercept_worker.js
+  notify_loaded.js
+  test_request_context.js
+  fetch/plugin/worker.js
+  fetch/plugin/plugins.html
+  eventsource/*
+  sw_clients/file_blob_upload_frame.html
+  redirect_post.sjs
+  xslt_worker.js
+  xslt/*
+  unresolved_fetch_worker.js
+  header_checker.sjs
+  openWindow_worker.js
+  redirect.sjs
+  open_window/client.html
+  lorem_script.js
+  file_blob_response_worker.js
+  !/dom/security/test/cors/file_CrossSiteXHR_server.sjs
+  !/dom/tests/mochitest/notification/MockServices.js
+  !/dom/tests/mochitest/notification/NotificationTest.js
+  blocking_install_event_worker.js
+  sw_bad_mime_type.js
+  sw_bad_mime_type.js^headers^
+  error_reporting_helpers.js
+  fetch.js
+  hello.html
+  create_another_sharedWorker.html
+  sharedWorker_fetch.js
+
+[test_bug1151916.html]
+[test_bug1240436.html]
+[test_claim.html]
+[test_claim_fetch.html]
+[test_claim_oninstall.html]
+[test_close.html]
+[test_controller.html]
+[test_cross_origin_url_after_redirect.html]
+[test_csp_upgrade-insecure_intercept.html]
+[test_empty_serviceworker.html]
+[test_error_reporting.html]
+[test_escapedSlashes.html]
+[test_eval_allowed.html]
+[test_eventsource_intercept.html]
+[test_fetch_event.html]
+skip-if = (debug && e10s) # Bug 1262224
+[test_fetch_integrity.html]
+[test_file_blob_response.html]
+[test_file_blob_upload.html]
+[test_force_refresh.html]
+[test_gzip_redirect.html]
+[test_hsts_upgrade_intercept.html]
+[test_https_fetch.html]
+[test_https_fetch_cloned_response.html]
+[test_https_origin_after_redirect.html]
+[test_https_origin_after_redirect_cached.html]
+[test_https_synth_fetch_from_cached_sw.html]
+[test_imagecache.html]
+[test_imagecache_max_age.html]
+[test_importscript.html]
+[test_importscript_mixedcontent.html]
+tags = mcb
+[test_install_event.html]
+[test_install_event_gc.html]
+[test_installation_simple.html]
+[test_match_all.html]
+[test_match_all_advanced.html]
+[test_match_all_client_id.html]
+[test_match_all_client_properties.html]
+[test_navigator.html]
+[test_not_intercept_plugin.html]
+[test_notification_constructor_error.html]
+[test_notification_get.html]
+[test_notificationclick.html]
+[test_notificationclick_focus.html]
+[test_notificationclick-otherwindow.html]
+[test_notificationclose.html]
+[test_opaque_intercept.html]
+[test_openWindow.html]
+tags = openwindow
+[test_origin_after_redirect.html]
+[test_origin_after_redirect_cached.html]
+[test_origin_after_redirect_to_https.html]
+[test_origin_after_redirect_to_https_cached.html]
+[test_post_message.html]
+[test_post_message_advanced.html]
+[test_post_message_source.html]
+[test_register_base.html]
+[test_register_https_in_http.html]
+[test_request_context_audio.html]
+[test_request_context_beacon.html]
+[test_request_context_cache.html]
+[test_request_context_cspreport.html]
+[test_request_context_embed.html]
+[test_request_context_fetch.html]
+[test_request_context_font.html]
+[test_request_context_frame.html]
+[test_request_context_iframe.html]
+[test_request_context_image.html]
+[test_request_context_imagesrcset.html]
+[test_request_context_internal.html]
+[test_request_context_nestedworker.html]
+[test_request_context_nestedworkerinsharedworker.html]
+[test_request_context_object.html]
+[test_request_context_picture.html]
+[test_request_context_ping.html]
+[test_request_context_plugin.html]
+[test_request_context_script.html]
+[test_request_context_sharedworker.html]
+[test_request_context_style.html]
+[test_request_context_track.html]
+[test_request_context_video.html]
+[test_request_context_worker.html]
+[test_request_context_xhr.html]
+[test_request_context_xslt.html]
+[test_sandbox_intercept.html]
+[test_scopes.html]
+[test_sanitize.html]
+[test_sanitize_domain.html]
+[test_service_worker_allowed.html]
+[test_serviceworker_header.html]
+[test_serviceworker_interfaces.html]
+[test_serviceworker_not_sharedworker.html]
+[test_skip_waiting.html]
+[test_strict_mode_warning.html]
+[test_third_party_iframes.html]
+[test_unregister.html]
+[test_unresolved_fetch_interception.html]
+[test_workerUnregister.html]
+[test_workerUpdate.html]
+[test_workerupdatefoundevent.html]
+[test_xslt.html]
diff --git a/dom/workers/test/serviceworkers/notification/listener.html b/dom/workers/test/serviceworkers/notification/listener.html
new file mode 100644
index 000000000..1c6e282ec
--- /dev/null
+++ b/dom/workers/test/serviceworkers/notification/listener.html
@@ -0,0 +1,27 @@
+
+
+
+
+  Bug 1114554 - proxy to forward messages from SW to test
+
+
+
+
+
+
diff --git a/dom/workers/test/serviceworkers/notification/register.html b/dom/workers/test/serviceworkers/notification/register.html
new file mode 100644
index 000000000..b7df73bed
--- /dev/null
+++ b/dom/workers/test/serviceworkers/notification/register.html
@@ -0,0 +1,11 @@
+
+
diff --git a/dom/workers/test/serviceworkers/notification/unregister.html b/dom/workers/test/serviceworkers/notification/unregister.html
new file mode 100644
index 000000000..d5a141f83
--- /dev/null
+++ b/dom/workers/test/serviceworkers/notification/unregister.html
@@ -0,0 +1,12 @@
+
+
diff --git a/dom/workers/test/serviceworkers/notification_alt/register.html b/dom/workers/test/serviceworkers/notification_alt/register.html
new file mode 100644
index 000000000..b7df73bed
--- /dev/null
+++ b/dom/workers/test/serviceworkers/notification_alt/register.html
@@ -0,0 +1,11 @@
+
+
diff --git a/dom/workers/test/serviceworkers/notification_alt/unregister.html b/dom/workers/test/serviceworkers/notification_alt/unregister.html
new file mode 100644
index 000000000..d5a141f83
--- /dev/null
+++ b/dom/workers/test/serviceworkers/notification_alt/unregister.html
@@ -0,0 +1,12 @@
+
+
diff --git a/dom/workers/test/serviceworkers/notification_constructor_error.js b/dom/workers/test/serviceworkers/notification_constructor_error.js
new file mode 100644
index 000000000..644dba480
--- /dev/null
+++ b/dom/workers/test/serviceworkers/notification_constructor_error.js
@@ -0,0 +1 @@
+new Notification("Hi there");
diff --git a/dom/workers/test/serviceworkers/notification_get_sw.js b/dom/workers/test/serviceworkers/notification_get_sw.js
new file mode 100644
index 000000000..540c9d93c
--- /dev/null
+++ b/dom/workers/test/serviceworkers/notification_get_sw.js
@@ -0,0 +1,49 @@
+function postAll(data) {
+  self.clients.matchAll().then(function(clients) {
+    if (clients.length == 0) {
+      dump("***************** NO CLIENTS FOUND! Test messages are being lost *******************\n");
+    }
+    clients.forEach(function(client) {
+      client.postMessage(data);
+    });
+  });
+}
+
+function ok(a, msg) {
+  postAll({type: 'status', status: !!a, msg: a + ": " + msg });
+}
+
+function is(a, b, msg) {
+  postAll({type: 'status', status: a === b, msg: a + " === " + b + ": " + msg });
+}
+
+function done() {
+  postAll({type: 'finish'});
+}
+
+onmessage = function(e) {
+dump("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% MESSAGE " + e.data + "\n");
+  var start;
+  if (e.data == 'create') {
+    start = registration.showNotification("This is a title");
+  } else {
+    start = Promise.resolve();
+  }
+
+  start.then(function() {
+  dump("CALLING getNotification\n");
+    registration.getNotifications().then(function(notifications) {
+  dump("RECD getNotification\n");
+      is(notifications.length, 1, "There should be one stored notification");
+      var notification = notifications[0];
+      if (!notification) {
+        done();
+        return;
+      }
+      ok(notification instanceof Notification, "Should be a Notification");
+      is(notification.title, "This is a title", "Title should match");
+      notification.close();
+      done();
+    });
+  });
+}
diff --git a/dom/workers/test/serviceworkers/notificationclick-otherwindow.html b/dom/workers/test/serviceworkers/notificationclick-otherwindow.html
new file mode 100644
index 000000000..f64e82aab
--- /dev/null
+++ b/dom/workers/test/serviceworkers/notificationclick-otherwindow.html
@@ -0,0 +1,30 @@
+
+
+
+
+  Bug 1114554 - controlled page
+
+
+
+
+
+
diff --git a/dom/workers/test/serviceworkers/notificationclick.html b/dom/workers/test/serviceworkers/notificationclick.html
new file mode 100644
index 000000000..448764a1c
--- /dev/null
+++ b/dom/workers/test/serviceworkers/notificationclick.html
@@ -0,0 +1,27 @@
+
+
+
+
+  Bug 1114554 - controlled page
+
+
+
+
+
+
diff --git a/dom/workers/test/serviceworkers/notificationclick.js b/dom/workers/test/serviceworkers/notificationclick.js
new file mode 100644
index 000000000..39e7adb81
--- /dev/null
+++ b/dom/workers/test/serviceworkers/notificationclick.js
@@ -0,0 +1,19 @@
+// Any copyright is dedicated to the Public Domain.
+// http://creativecommons.org/publicdomain/zero/1.0/
+//
+onnotificationclick = function(e) {
+  self.clients.matchAll().then(function(clients) {
+    if (clients.length === 0) {
+      dump("********************* CLIENTS LIST EMPTY! Test will timeout! ***********************\n");
+      return;
+    }
+
+    clients.forEach(function(client) {
+      client.postMessage({ result: e.notification.data &&
+                                   e.notification.data['complex'] &&
+                                   e.notification.data['complex'][0] == "jsval" &&
+                                   e.notification.data['complex'][1] == 5 });
+
+    });
+  });
+}
diff --git a/dom/workers/test/serviceworkers/notificationclick_focus.html b/dom/workers/test/serviceworkers/notificationclick_focus.html
new file mode 100644
index 000000000..0152d397f
--- /dev/null
+++ b/dom/workers/test/serviceworkers/notificationclick_focus.html
@@ -0,0 +1,28 @@
+
+
+
+
+  Bug 1144660 - controlled page
+
+
+
+
+
+
diff --git a/dom/workers/test/serviceworkers/notificationclick_focus.js b/dom/workers/test/serviceworkers/notificationclick_focus.js
new file mode 100644
index 000000000..5fb73651e
--- /dev/null
+++ b/dom/workers/test/serviceworkers/notificationclick_focus.js
@@ -0,0 +1,40 @@
+// Any copyright is dedicated to the Public Domain.
+// http://creativecommons.org/publicdomain/zero/1.0/
+//
+
+function promisifyTimerFocus(client, delay) {
+  return new Promise(function(resolve, reject) {
+    setTimeout(function() {
+      client.focus().then(resolve, reject);
+    }, delay);
+  });
+}
+
+onnotificationclick = function(e) {
+  e.waitUntil(self.clients.matchAll().then(function(clients) {
+    if (clients.length === 0) {
+      dump("********************* CLIENTS LIST EMPTY! Test will timeout! ***********************\n");
+      return Promise.resolve();
+    }
+
+    var immediatePromise = clients[0].focus();
+    var withinTimeout = promisifyTimerFocus(clients[0], 100);
+
+    var afterTimeout = promisifyTimerFocus(clients[0], 2000).then(function() {
+      throw "Should have failed!";
+    }, function() {
+      return Promise.resolve();
+    });
+
+    return Promise.all([immediatePromise, withinTimeout, afterTimeout]).then(function() {
+      clients.forEach(function(client) {
+        client.postMessage({ok: true});
+      });
+    }).catch(function(e) {
+      dump("Error " + e + "\n");
+      clients.forEach(function(client) {
+        client.postMessage({ok: false});
+      });
+    });
+  }));
+}
diff --git a/dom/workers/test/serviceworkers/notificationclose.html b/dom/workers/test/serviceworkers/notificationclose.html
new file mode 100644
index 000000000..10c8da453
--- /dev/null
+++ b/dom/workers/test/serviceworkers/notificationclose.html
@@ -0,0 +1,37 @@
+
+
+
+
+  Bug 1265841 - controlled page
+
+
+
+
+
+
diff --git a/dom/workers/test/serviceworkers/notificationclose.js b/dom/workers/test/serviceworkers/notificationclose.js
new file mode 100644
index 000000000..d48218075
--- /dev/null
+++ b/dom/workers/test/serviceworkers/notificationclose.js
@@ -0,0 +1,19 @@
+// Any copyright is dedicated to the Public Domain.
+// http://creativecommons.org/publicdomain/zero/1.0/
+//
+onnotificationclose = function(e) {
+  self.clients.matchAll().then(function(clients) {
+    if (clients.length === 0) {
+      dump("********************* CLIENTS LIST EMPTY! Test will timeout! ***********************\n");
+      return;
+    }
+
+    clients.forEach(function(client) {
+      client.postMessage({ result: e.notification.data &&
+                                   e.notification.data['complex'] &&
+                                   e.notification.data['complex'][0] == "jsval" &&
+                                   e.notification.data['complex'][1] == 5 });
+
+    });
+  });
+}
diff --git a/dom/workers/test/serviceworkers/notify_loaded.js b/dom/workers/test/serviceworkers/notify_loaded.js
new file mode 100644
index 000000000..d07573b2c
--- /dev/null
+++ b/dom/workers/test/serviceworkers/notify_loaded.js
@@ -0,0 +1 @@
+parent.postMessage('SCRIPT_LOADED', '*');
diff --git a/dom/workers/test/serviceworkers/opaque_intercept_worker.js b/dom/workers/test/serviceworkers/opaque_intercept_worker.js
new file mode 100644
index 000000000..d593be783
--- /dev/null
+++ b/dom/workers/test/serviceworkers/opaque_intercept_worker.js
@@ -0,0 +1,25 @@
+var name = 'opaqueInterceptCache';
+
+// Cross origin request to ensure that an opaque response is used
+var prefix = 'http://example.com/tests/dom/workers/test/serviceworkers/'
+
+self.addEventListener('install', function(event) {
+  var request = new Request(prefix + 'notify_loaded.js', { mode: 'no-cors' });
+  event.waitUntil(
+    Promise.all([caches.open(name), fetch(request)]).then(function(results) {
+      var cache = results[0];
+      var response = results[1];
+      return cache.put('./sw_clients/does_not_exist.js', response);
+    })
+  );
+});
+
+self.addEventListener('fetch', function (event) {
+  event.respondWith(
+    caches.open(name).then(function(cache) {
+      return cache.match(event.request);
+    }).then(function(response) {
+      return response || fetch(event.request);
+    })
+  );
+});
diff --git a/dom/workers/test/serviceworkers/openWindow_worker.js b/dom/workers/test/serviceworkers/openWindow_worker.js
new file mode 100644
index 000000000..46c0f998e
--- /dev/null
+++ b/dom/workers/test/serviceworkers/openWindow_worker.js
@@ -0,0 +1,116 @@
+// the worker won't shut down between events because we increased
+// the timeout values.
+var client;
+var window_count = 0;
+var expected_window_count = 7;
+var resolve_got_all_windows = null;
+var got_all_windows = new Promise(function(res, rej) {
+  resolve_got_all_windows = res;
+});
+
+// |expected_window_count| needs to be updated for every new call that's
+// expected to actually open a new window regardless of what |clients.openWindow|
+// returns.
+function testForUrl(url, throwType, clientProperties, resultsArray) {
+  return clients.openWindow(url)
+    .then(function(e) {
+      if (throwType != null) {
+        resultsArray.push({
+          result: false,
+          message: "openWindow should throw " + throwType
+        });
+      } else if (clientProperties) {
+        resultsArray.push({
+          result: (e instanceof WindowClient),
+          message: "openWindow should resolve to a WindowClient"
+        });
+        resultsArray.push({
+          result: e.url == clientProperties.url,
+          message: "Client url should be " + clientProperties.url
+        });
+        // Add more properties
+      } else {
+        resultsArray.push({
+          result: e == null,
+          message: "Open window should resolve to null. Got: " + e
+        });
+      }
+    })
+    .catch(function(err) {
+      if (throwType == null) {
+        resultsArray.push({
+          result: false,
+          message: "Unexpected throw: " + err
+        });
+      } else {
+        resultsArray.push({
+          result: err.toString().indexOf(throwType) >= 0,
+          message: "openWindow should throw: " + err
+        });
+      }
+    })
+}
+
+onmessage = function(event) {
+  if (event.data == "testNoPopup") {
+    client = event.source;
+
+    var results = [];
+    var promises = [];
+    promises.push(testForUrl("about:blank", "TypeError", null, results));
+    promises.push(testForUrl("http://example.com", "InvalidAccessError", null, results));
+    promises.push(testForUrl("_._*`InvalidURL", "InvalidAccessError", null, results));
+    Promise.all(promises).then(function(e) {
+      client.postMessage(results);
+    });
+  }
+  if (event.data == "NEW_WINDOW") {
+    window_count += 1;
+    if (window_count == expected_window_count) {
+      resolve_got_all_windows();
+    }
+  }
+
+  if (event.data == "CHECK_NUMBER_OF_WINDOWS") {
+    got_all_windows.then(function() {
+      return clients.matchAll();
+    }).then(function(cl) {
+      event.source.postMessage({result: cl.length == expected_window_count,
+                                message: "The number of windows is correct."});
+      for (i = 0; i < cl.length; i++) {
+        cl[i].postMessage("CLOSE");
+      }
+    });
+  }
+}
+
+onnotificationclick = function(e) {
+  var results = [];
+  var promises = [];
+
+  var redirect = "http://mochi.test:8888/tests/dom/workers/test/serviceworkers/redirect.sjs?"
+  var redirect_xorigin = "http://example.com/tests/dom/workers/test/serviceworkers/redirect.sjs?"
+  var same_origin = "http://mochi.test:8888/tests/dom/workers/test/serviceworkers/open_window/client.html"
+  var different_origin = "http://example.com/tests/dom/workers/test/serviceworkers/open_window/client.html"
+
+
+  promises.push(testForUrl("about:blank", "TypeError", null, results));
+  promises.push(testForUrl(different_origin, null, null, results));
+  promises.push(testForUrl(same_origin, null, {url: same_origin}, results));
+  promises.push(testForUrl("open_window/client.html", null, {url: same_origin}, results));
+
+  // redirect tests
+  promises.push(testForUrl(redirect + "open_window/client.html", null,
+			   {url: same_origin}, results));
+  promises.push(testForUrl(redirect + different_origin, null, null, results));
+
+  promises.push(testForUrl(redirect_xorigin + "open_window/client.html", null,
+			   null, results));
+  promises.push(testForUrl(redirect_xorigin + same_origin, null,
+			   {url: same_origin}, results));
+
+  Promise.all(promises).then(function(e) {
+    client.postMessage(results);
+  });
+}
+
diff --git a/dom/workers/test/serviceworkers/open_window/client.html b/dom/workers/test/serviceworkers/open_window/client.html
new file mode 100644
index 000000000..82b93ff7e
--- /dev/null
+++ b/dom/workers/test/serviceworkers/open_window/client.html
@@ -0,0 +1,48 @@
+
+
+
+
+  Bug 1172870 - page opened by ServiceWorkerClients.OpenWindow
+
+
+

+ +

+
+
+
+
+
diff --git a/dom/workers/test/serviceworkers/parse_error_worker.js b/dom/workers/test/serviceworkers/parse_error_worker.js
new file mode 100644
index 000000000..b6a8ef0a1
--- /dev/null
+++ b/dom/workers/test/serviceworkers/parse_error_worker.js
@@ -0,0 +1,2 @@
+// intentional parse error.
+var foo = {;
diff --git a/dom/workers/test/serviceworkers/redirect.sjs b/dom/workers/test/serviceworkers/redirect.sjs
new file mode 100644
index 000000000..b6249cadf
--- /dev/null
+++ b/dom/workers/test/serviceworkers/redirect.sjs
@@ -0,0 +1,5 @@
+function handleRequest(request, response)
+{
+  response.setStatusLine(request.httpVersion, 301, "Moved Permanently");
+  response.setHeader("Location", request.queryString, false);
+}
diff --git a/dom/workers/test/serviceworkers/redirect_post.sjs b/dom/workers/test/serviceworkers/redirect_post.sjs
new file mode 100644
index 000000000..8b805be63
--- /dev/null
+++ b/dom/workers/test/serviceworkers/redirect_post.sjs
@@ -0,0 +1,35 @@
+const CC = Components.Constructor;
+const BinaryInputStream = CC("@mozilla.org/binaryinputstream;1",
+                             "nsIBinaryInputStream",
+                             "setInputStream");
+
+function handleRequest(request, response)
+{
+  var query = {};
+  request.queryString.split('&').forEach(function (val) {
+    var [name, value] = val.split('=');
+    query[name] = unescape(value);
+  });
+
+  var bodyStream = new BinaryInputStream(request.bodyInputStream);
+  var bodyBytes = [];
+  while ((bodyAvail = bodyStream.available()) > 0)
+    Array.prototype.push.apply(bodyBytes, bodyStream.readByteArray(bodyAvail));
+
+  var body = decodeURIComponent(
+    escape(String.fromCharCode.apply(null, bodyBytes)));
+
+  var currentHop = query.hop ? parseInt(query.hop) : 0;
+
+  var obj = JSON.parse(body);
+  if (currentHop < obj.hops) {
+    var newURL = '/tests/dom/workers/test/serviceworkers/redirect_post.sjs?hop=' +
+                 (1 + currentHop);
+    response.setStatusLine(null, 307, 'redirect');
+    response.setHeader('Location', newURL);
+    return;
+  }
+
+  response.setHeader('Content-Type', 'application/json');
+  response.write(body);
+}
diff --git a/dom/workers/test/serviceworkers/redirect_serviceworker.sjs b/dom/workers/test/serviceworkers/redirect_serviceworker.sjs
new file mode 100644
index 000000000..9d3a0a2cd
--- /dev/null
+++ b/dom/workers/test/serviceworkers/redirect_serviceworker.sjs
@@ -0,0 +1,4 @@
+function handleRequest(request, response) {
+  response.setStatusLine("1.1", 302, "Found");
+  response.setHeader("Location", "http://mochi.test:8888/tests/dom/workers/test/serviceworkers/worker.js");
+}
diff --git a/dom/workers/test/serviceworkers/register_https.html b/dom/workers/test/serviceworkers/register_https.html
new file mode 100644
index 000000000..d14d8c380
--- /dev/null
+++ b/dom/workers/test/serviceworkers/register_https.html
@@ -0,0 +1,21 @@
+
+
diff --git a/dom/workers/test/serviceworkers/sanitize/example_check_and_unregister.html b/dom/workers/test/serviceworkers/sanitize/example_check_and_unregister.html
new file mode 100644
index 000000000..4de8f317b
--- /dev/null
+++ b/dom/workers/test/serviceworkers/sanitize/example_check_and_unregister.html
@@ -0,0 +1,23 @@
+
+
+
diff --git a/dom/workers/test/serviceworkers/sanitize/frame.html b/dom/workers/test/serviceworkers/sanitize/frame.html
new file mode 100644
index 000000000..b4bf7a1ff
--- /dev/null
+++ b/dom/workers/test/serviceworkers/sanitize/frame.html
@@ -0,0 +1,11 @@
+
+
diff --git a/dom/workers/test/serviceworkers/sanitize/register.html b/dom/workers/test/serviceworkers/sanitize/register.html
new file mode 100644
index 000000000..f1edd27be
--- /dev/null
+++ b/dom/workers/test/serviceworkers/sanitize/register.html
@@ -0,0 +1,10 @@
+
+
+
diff --git a/dom/workers/test/serviceworkers/sanitize_worker.js b/dom/workers/test/serviceworkers/sanitize_worker.js
new file mode 100644
index 000000000..66495e186
--- /dev/null
+++ b/dom/workers/test/serviceworkers/sanitize_worker.js
@@ -0,0 +1,5 @@
+onfetch = function(e) {
+  if (e.request.url.indexOf("intercept-this") != -1) {
+    e.respondWith(new Response("intercepted"));
+  }
+}
diff --git a/dom/workers/test/serviceworkers/scope/scope_worker.js b/dom/workers/test/serviceworkers/scope/scope_worker.js
new file mode 100644
index 000000000..4164e7a24
--- /dev/null
+++ b/dom/workers/test/serviceworkers/scope/scope_worker.js
@@ -0,0 +1,2 @@
+// This worker is used to test if calling register() without a scope argument
+// leads to scope being relative to service worker script.
diff --git a/dom/workers/test/serviceworkers/serviceworker.html b/dom/workers/test/serviceworkers/serviceworker.html
new file mode 100644
index 000000000..11edd001a
--- /dev/null
+++ b/dom/workers/test/serviceworkers/serviceworker.html
@@ -0,0 +1,12 @@
+
+
+  
+    
+    
+  
+  
+    This is a test page.
+  
+
diff --git a/dom/workers/test/serviceworkers/serviceworker_not_sharedworker.js b/dom/workers/test/serviceworkers/serviceworker_not_sharedworker.js
new file mode 100644
index 000000000..077da2366
--- /dev/null
+++ b/dom/workers/test/serviceworkers/serviceworker_not_sharedworker.js
@@ -0,0 +1,21 @@
+function OnMessage(e)
+{
+  if (e.data.msg == "whoareyou") {
+    if ("ServiceWorker" in self) {
+      self.clients.matchAll().then(function(clients) {
+        clients[0].postMessage({result: "serviceworker"});
+      });
+    } else {
+      port.postMessage({result: "sharedworker"});
+    }
+  }
+};
+
+var port;
+onconnect = function(e) {
+  port = e.ports[0];
+  port.onmessage = OnMessage;
+  port.start();
+};
+
+onmessage = OnMessage;
diff --git a/dom/workers/test/serviceworkers/serviceworker_wrapper.js b/dom/workers/test/serviceworkers/serviceworker_wrapper.js
new file mode 100644
index 000000000..6a7ec0c0f
--- /dev/null
+++ b/dom/workers/test/serviceworkers/serviceworker_wrapper.js
@@ -0,0 +1,101 @@
+// Any copyright is dedicated to the Public Domain.
+// http://creativecommons.org/publicdomain/zero/1.0/
+//
+// ServiceWorker equivalent of worker_wrapper.js.
+
+var client;
+
+function ok(a, msg) {
+  dump("OK: " + !!a + "  =>  " + a + ": " + msg + "\n");
+  client.postMessage({type: 'status', status: !!a, msg: a + ": " + msg });
+}
+
+function is(a, b, msg) {
+  dump("IS: " + (a===b) + "  =>  " + a + " | " + b + ": " + msg + "\n");
+  client.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() {
+  client.postMessage({ type: 'finish' });
+}
+
+function workerTestGetVersion(cb) {
+  addEventListener('message', function workerTestGetVersionCB(e) {
+    if (e.data.type !== 'returnVersion') {
+      return;
+    }
+    removeEventListener('message', workerTestGetVersionCB);
+    cb(e.data.result);
+  });
+  client.postMessage({
+    type: 'getVersion'
+  });
+}
+
+function workerTestGetUserAgent(cb) {
+  addEventListener('message', function workerTestGetUserAgentCB(e) {
+    if (e.data.type !== 'returnUserAgent') {
+      return;
+    }
+    removeEventListener('message', workerTestGetUserAgentCB);
+    cb(e.data.result);
+  });
+  client.postMessage({
+    type: 'getUserAgent'
+  });
+}
+
+function workerTestGetOSCPU(cb) {
+  addEventListener('message', function workerTestGetOSCPUCB(e) {
+    if (e.data.type !== 'returnOSCPU') {
+      return;
+    }
+    removeEventListener('message', workerTestGetOSCPUCB);
+    cb(e.data.result);
+  });
+  client.postMessage({
+    type: 'getOSCPU'
+  });
+}
+
+function workerTestGetStorageManager(cb) {
+  addEventListener('message', function workerTestGetStorageManagerCB(e) {
+    if (e.data.type !== 'returnStorageManager') {
+      return;
+    }
+    removeEventListener('message', workerTestGetStorageManagerCB);
+    cb(e.data.result);
+  });
+  client.postMessage({
+    type: 'getStorageManager'
+  });
+}
+
+addEventListener('message', function workerWrapperOnMessage(e) {
+  removeEventListener('message', workerWrapperOnMessage);
+  var data = e.data;
+  self.clients.matchAll().then(function(clients) {
+    client = clients[0];
+    try {
+      importScripts(data.script);
+    } catch(e) {
+      client.postMessage({
+        type: 'status',
+        status: false,
+        msg: 'worker failed to import ' + data.script + "; error: " + e.message
+      });
+    }
+  });
+});
diff --git a/dom/workers/test/serviceworkers/serviceworkerinfo_iframe.html b/dom/workers/test/serviceworkers/serviceworkerinfo_iframe.html
new file mode 100644
index 000000000..a0a2de760
--- /dev/null
+++ b/dom/workers/test/serviceworkers/serviceworkerinfo_iframe.html
@@ -0,0 +1,26 @@
+
+
+  
+    
+    
+  
+  
+    This is a test page.
+  
+
diff --git a/dom/workers/test/serviceworkers/serviceworkermanager_iframe.html b/dom/workers/test/serviceworkers/serviceworkermanager_iframe.html
new file mode 100644
index 000000000..0df93da96
--- /dev/null
+++ b/dom/workers/test/serviceworkers/serviceworkermanager_iframe.html
@@ -0,0 +1,34 @@
+
+
+  
+    
+    
+  
+  
+    This is a test page.
+  
+
diff --git a/dom/workers/test/serviceworkers/serviceworkerregistrationinfo_iframe.html b/dom/workers/test/serviceworkers/serviceworkerregistrationinfo_iframe.html
new file mode 100644
index 000000000..f093d38db
--- /dev/null
+++ b/dom/workers/test/serviceworkers/serviceworkerregistrationinfo_iframe.html
@@ -0,0 +1,30 @@
+
+
+  
+    
+    
+  
+  
+    This is a test page.
+  
+
diff --git a/dom/workers/test/serviceworkers/sharedWorker_fetch.js b/dom/workers/test/serviceworkers/sharedWorker_fetch.js
new file mode 100644
index 000000000..4970a1fd2
--- /dev/null
+++ b/dom/workers/test/serviceworkers/sharedWorker_fetch.js
@@ -0,0 +1,29 @@
+var clients = new Array();
+clients.length = 0;
+
+var broadcast = function(message) {
+  var length = clients.length;
+  for (var i = 0; i < length; i++) {
+    port = clients[i];
+    port.postMessage(message);
+  }
+}
+
+onconnect = function(e) {
+  clients.push(e.ports[0]);
+  if (clients.length == 1) {
+    clients[0].postMessage("Connected");
+  } else if (clients.length == 2) {
+    broadcast("BothConnected");
+    clients[0].onmessage = function(e) {
+      if (e.data == "StartFetchWithWrongIntegrity") {
+        // The fetch will succeed because the integrity value is invalid and we
+        // are looking for the console message regarding the bad integrity value.
+        fetch("SharedWorker_SRIFailed.html", {"integrity": "abc"}).then(
+            function () {
+                clients[0].postMessage('SRI_failed');
+            });
+      }
+    }
+  }
+}
diff --git a/dom/workers/test/serviceworkers/simpleregister/index.html b/dom/workers/test/serviceworkers/simpleregister/index.html
new file mode 100644
index 000000000..2c0eb5345
--- /dev/null
+++ b/dom/workers/test/serviceworkers/simpleregister/index.html
@@ -0,0 +1,51 @@
+
+  
+  
+      
+  
+
diff --git a/dom/workers/test/serviceworkers/simpleregister/ready.html b/dom/workers/test/serviceworkers/simpleregister/ready.html
new file mode 100644
index 000000000..3afc4bfdb
--- /dev/null
+++ b/dom/workers/test/serviceworkers/simpleregister/ready.html
@@ -0,0 +1,15 @@
+
+  
+  
+    
+  
+
+
diff --git a/dom/workers/test/serviceworkers/skip_waiting_installed_worker.js b/dom/workers/test/serviceworkers/skip_waiting_installed_worker.js
new file mode 100644
index 000000000..68573f100
--- /dev/null
+++ b/dom/workers/test/serviceworkers/skip_waiting_installed_worker.js
@@ -0,0 +1,6 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+self.addEventListener('install', evt => {
+  evt.waitUntil(self.skipWaiting());
+});
diff --git a/dom/workers/test/serviceworkers/skip_waiting_scope/index.html b/dom/workers/test/serviceworkers/skip_waiting_scope/index.html
new file mode 100644
index 000000000..b8a64d512
--- /dev/null
+++ b/dom/workers/test/serviceworkers/skip_waiting_scope/index.html
@@ -0,0 +1,37 @@
+
+
+
+
+  Bug 1131352 - Add ServiceWorkerGlobalScope skipWaiting()
+  
+  
+
+
+

+ +

+
+
+
+
diff --git a/dom/workers/test/serviceworkers/source_message_posting_worker.js b/dom/workers/test/serviceworkers/source_message_posting_worker.js
new file mode 100644
index 000000000..36ce951fe
--- /dev/null
+++ b/dom/workers/test/serviceworkers/source_message_posting_worker.js
@@ -0,0 +1,16 @@
+onmessage = function(e) {
+  if (!e.source) {
+    dump("ERROR: message doesn't have a source.");
+  }
+
+  if (!(e instanceof ExtendableMessageEvent)) {
+    e.source.postMessage("ERROR. event is not an extendable message event.");
+  }
+
+  // The client should be a window client
+  if (e.source instanceof  WindowClient) {
+    e.source.postMessage(e.data);
+  } else {
+    e.source.postMessage("ERROR. source is not a window client.");
+  }
+};
diff --git a/dom/workers/test/serviceworkers/strict_mode_warning.js b/dom/workers/test/serviceworkers/strict_mode_warning.js
new file mode 100644
index 000000000..38418de3d
--- /dev/null
+++ b/dom/workers/test/serviceworkers/strict_mode_warning.js
@@ -0,0 +1,4 @@
+function f() {
+  return 1;
+  return 2;
+}
diff --git a/dom/workers/test/serviceworkers/sw_bad_mime_type.js b/dom/workers/test/serviceworkers/sw_bad_mime_type.js
new file mode 100644
index 000000000..f371807db
--- /dev/null
+++ b/dom/workers/test/serviceworkers/sw_bad_mime_type.js
@@ -0,0 +1 @@
+// I need some contents.
diff --git a/dom/workers/test/serviceworkers/sw_bad_mime_type.js^headers^ b/dom/workers/test/serviceworkers/sw_bad_mime_type.js^headers^
new file mode 100644
index 000000000..a1f9e38d9
--- /dev/null
+++ b/dom/workers/test/serviceworkers/sw_bad_mime_type.js^headers^
@@ -0,0 +1 @@
+Content-Type: text/plain
diff --git a/dom/workers/test/serviceworkers/sw_clients/file_blob_upload_frame.html b/dom/workers/test/serviceworkers/sw_clients/file_blob_upload_frame.html
new file mode 100644
index 000000000..e594c514d
--- /dev/null
+++ b/dom/workers/test/serviceworkers/sw_clients/file_blob_upload_frame.html
@@ -0,0 +1,77 @@
+
+
+
+
+  test file blob upload with SW interception
+
+
+

+ +

+
+
+
+
+
diff --git a/dom/workers/test/serviceworkers/sw_clients/navigator.html b/dom/workers/test/serviceworkers/sw_clients/navigator.html
new file mode 100644
index 000000000..f6019bf28
--- /dev/null
+++ b/dom/workers/test/serviceworkers/sw_clients/navigator.html
@@ -0,0 +1,35 @@
+
+
+
+
+  Bug 982726 - test match_all not crashing
+  
+
+
+

+ +

+
+
+
+
+
diff --git a/dom/workers/test/serviceworkers/sw_clients/refresher.html b/dom/workers/test/serviceworkers/sw_clients/refresher.html
new file mode 100644
index 000000000..054f6bfc8
--- /dev/null
+++ b/dom/workers/test/serviceworkers/sw_clients/refresher.html
@@ -0,0 +1,39 @@
+
+
+
+
+  Bug 982726 - test match_all not crashing
+  
+  
+  
+
+
+

+ +

+
+
+
+
+
diff --git a/dom/workers/test/serviceworkers/sw_clients/refresher_cached.html b/dom/workers/test/serviceworkers/sw_clients/refresher_cached.html
new file mode 100644
index 000000000..3ec0cc427
--- /dev/null
+++ b/dom/workers/test/serviceworkers/sw_clients/refresher_cached.html
@@ -0,0 +1,38 @@
+
+
+
+
+  Bug 982726 - test match_all not crashing
+  
+  
+
+
+

+ +

+
+
+
+
+
diff --git a/dom/workers/test/serviceworkers/sw_clients/refresher_cached_compressed.html b/dom/workers/test/serviceworkers/sw_clients/refresher_cached_compressed.html
new file mode 100644
index 000000000..55e97ac24
Binary files /dev/null and b/dom/workers/test/serviceworkers/sw_clients/refresher_cached_compressed.html differ
diff --git a/dom/workers/test/serviceworkers/sw_clients/refresher_cached_compressed.html^headers^ b/dom/workers/test/serviceworkers/sw_clients/refresher_cached_compressed.html^headers^
new file mode 100644
index 000000000..4204d8601
--- /dev/null
+++ b/dom/workers/test/serviceworkers/sw_clients/refresher_cached_compressed.html^headers^
@@ -0,0 +1,2 @@
+Content-Type: text/html
+Content-Encoding: gzip
diff --git a/dom/workers/test/serviceworkers/sw_clients/refresher_compressed.html b/dom/workers/test/serviceworkers/sw_clients/refresher_compressed.html
new file mode 100644
index 000000000..7a45bcafa
Binary files /dev/null and b/dom/workers/test/serviceworkers/sw_clients/refresher_compressed.html differ
diff --git a/dom/workers/test/serviceworkers/sw_clients/refresher_compressed.html^headers^ b/dom/workers/test/serviceworkers/sw_clients/refresher_compressed.html^headers^
new file mode 100644
index 000000000..4204d8601
--- /dev/null
+++ b/dom/workers/test/serviceworkers/sw_clients/refresher_compressed.html^headers^
@@ -0,0 +1,2 @@
+Content-Type: text/html
+Content-Encoding: gzip
diff --git a/dom/workers/test/serviceworkers/sw_clients/service_worker_controlled.html b/dom/workers/test/serviceworkers/sw_clients/service_worker_controlled.html
new file mode 100644
index 000000000..e0d7bce57
--- /dev/null
+++ b/dom/workers/test/serviceworkers/sw_clients/service_worker_controlled.html
@@ -0,0 +1,38 @@
+
+
+
+
+  controlled page
+  
+
+
+
+
+
+
diff --git a/dom/workers/test/serviceworkers/sw_clients/simple.html b/dom/workers/test/serviceworkers/sw_clients/simple.html
new file mode 100644
index 000000000..3e4d7deca
--- /dev/null
+++ b/dom/workers/test/serviceworkers/sw_clients/simple.html
@@ -0,0 +1,30 @@
+
+
+
+
+  Bug 982726 - test match_all not crashing
+  
+  
+
+
+

+ +

+
+
+
+
+
diff --git a/dom/workers/test/serviceworkers/swa/worker_scope_different.js b/dom/workers/test/serviceworkers/swa/worker_scope_different.js
new file mode 100644
index 000000000..e69de29bb
diff --git a/dom/workers/test/serviceworkers/swa/worker_scope_different.js^headers^ b/dom/workers/test/serviceworkers/swa/worker_scope_different.js^headers^
new file mode 100644
index 000000000..e85a7f09d
--- /dev/null
+++ b/dom/workers/test/serviceworkers/swa/worker_scope_different.js^headers^
@@ -0,0 +1 @@
+Service-Worker-Allowed: different/path
diff --git a/dom/workers/test/serviceworkers/swa/worker_scope_different2.js b/dom/workers/test/serviceworkers/swa/worker_scope_different2.js
new file mode 100644
index 000000000..e69de29bb
diff --git a/dom/workers/test/serviceworkers/swa/worker_scope_different2.js^headers^ b/dom/workers/test/serviceworkers/swa/worker_scope_different2.js^headers^
new file mode 100644
index 000000000..e37307d66
--- /dev/null
+++ b/dom/workers/test/serviceworkers/swa/worker_scope_different2.js^headers^
@@ -0,0 +1 @@
+Service-Worker-Allowed: /different/path
diff --git a/dom/workers/test/serviceworkers/swa/worker_scope_precise.js b/dom/workers/test/serviceworkers/swa/worker_scope_precise.js
new file mode 100644
index 000000000..e69de29bb
diff --git a/dom/workers/test/serviceworkers/swa/worker_scope_precise.js^headers^ b/dom/workers/test/serviceworkers/swa/worker_scope_precise.js^headers^
new file mode 100644
index 000000000..30b053055
--- /dev/null
+++ b/dom/workers/test/serviceworkers/swa/worker_scope_precise.js^headers^
@@ -0,0 +1 @@
+Service-Worker-Allowed: /tests/dom/workers/test/serviceworkers/swa
diff --git a/dom/workers/test/serviceworkers/swa/worker_scope_too_deep.js b/dom/workers/test/serviceworkers/swa/worker_scope_too_deep.js
new file mode 100644
index 000000000..e69de29bb
diff --git a/dom/workers/test/serviceworkers/swa/worker_scope_too_deep.js^headers^ b/dom/workers/test/serviceworkers/swa/worker_scope_too_deep.js^headers^
new file mode 100644
index 000000000..b2056fc4a
--- /dev/null
+++ b/dom/workers/test/serviceworkers/swa/worker_scope_too_deep.js^headers^
@@ -0,0 +1 @@
+Service-Worker-Allowed: /tests/dom/workers/test/serviceworkers/swa/deep/way/too/specific
diff --git a/dom/workers/test/serviceworkers/swa/worker_scope_too_narrow.js b/dom/workers/test/serviceworkers/swa/worker_scope_too_narrow.js
new file mode 100644
index 000000000..e69de29bb
diff --git a/dom/workers/test/serviceworkers/swa/worker_scope_too_narrow.js^headers^ b/dom/workers/test/serviceworkers/swa/worker_scope_too_narrow.js^headers^
new file mode 100644
index 000000000..22add13bf
--- /dev/null
+++ b/dom/workers/test/serviceworkers/swa/worker_scope_too_narrow.js^headers^
@@ -0,0 +1 @@
+Service-Worker-Allowed: /tests/dom/workers
diff --git a/dom/workers/test/serviceworkers/test_bug1151916.html b/dom/workers/test/serviceworkers/test_bug1151916.html
new file mode 100644
index 000000000..92811775b
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_bug1151916.html
@@ -0,0 +1,105 @@
+
+
+
+
+  Bug 1151916 - Test principal is set on cached serviceworkers
+  
+  
+
+
+
+

+
+

+
+
+
+
+
diff --git a/dom/workers/test/serviceworkers/test_bug1240436.html b/dom/workers/test/serviceworkers/test_bug1240436.html
new file mode 100644
index 000000000..e93535840
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_bug1240436.html
@@ -0,0 +1,34 @@
+
+
+
+
+  Test for encoding of service workers
+  
+  
+
+
+

+ +

+
+
+
+
diff --git a/dom/workers/test/serviceworkers/test_claim.html b/dom/workers/test/serviceworkers/test_claim.html
new file mode 100644
index 000000000..d7015850f
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_claim.html
@@ -0,0 +1,172 @@
+
+
+
+
+  Bug 1130684 - Test service worker clients claim onactivate 
+  
+  
+
+
+

+ +

+
+
+
+
+
diff --git a/dom/workers/test/serviceworkers/test_claim_fetch.html b/dom/workers/test/serviceworkers/test_claim_fetch.html
new file mode 100644
index 000000000..8db6d304e
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_claim_fetch.html
@@ -0,0 +1,98 @@
+
+
+
+
+  Bug 1130684 - Test fetch events are intercepted after claim 
+  
+  
+
+
+

+ +

+
+
+
+
+
diff --git a/dom/workers/test/serviceworkers/test_claim_oninstall.html b/dom/workers/test/serviceworkers/test_claim_oninstall.html
new file mode 100644
index 000000000..4605cfb76
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_claim_oninstall.html
@@ -0,0 +1,78 @@
+
+
+
+
+  Bug 1130684 - Test service worker clients.claim oninstall
+  
+  
+
+
+

+ +

+
+
+
+
+
diff --git a/dom/workers/test/serviceworkers/test_client_focus.html b/dom/workers/test/serviceworkers/test_client_focus.html
new file mode 100644
index 000000000..b0bf43bb3
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_client_focus.html
@@ -0,0 +1,96 @@
+
+
+
+
+  Bug 1130686 - Test service worker client.focus 
+  
+  
+
+
+
+

+
+

+
+
+
+
+
diff --git a/dom/workers/test/serviceworkers/test_close.html b/dom/workers/test/serviceworkers/test_close.html
new file mode 100644
index 000000000..d2f72b9ef
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_close.html
@@ -0,0 +1,64 @@
+
+
+
+
+  Bug 1131353 - test WorkerGlobalScope.close() on service workers
+  
+  
+
+
+

+ +

+
+
+
+
diff --git a/dom/workers/test/serviceworkers/test_controller.html b/dom/workers/test/serviceworkers/test_controller.html
new file mode 100644
index 000000000..789d7746d
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_controller.html
@@ -0,0 +1,84 @@
+
+
+
+
+  Bug 1002570 - test controller instance.
+  
+  
+
+
+

+ +

+
+
+
+
+
diff --git a/dom/workers/test/serviceworkers/test_cross_origin_url_after_redirect.html b/dom/workers/test/serviceworkers/test_cross_origin_url_after_redirect.html
new file mode 100644
index 000000000..e56bb84ca
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_cross_origin_url_after_redirect.html
@@ -0,0 +1,50 @@
+
+
+
+
+  Test access to a cross origin Request.url property from a service worker for a redirected intercepted iframe
+  
+  
+
+
+

+ +

+
+
+
+
diff --git a/dom/workers/test/serviceworkers/test_csp_upgrade-insecure_intercept.html b/dom/workers/test/serviceworkers/test_csp_upgrade-insecure_intercept.html
new file mode 100644
index 000000000..fe4cb991c
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_csp_upgrade-insecure_intercept.html
@@ -0,0 +1,55 @@
+
+
+
+
+  Test that a CSP upgraded request can be intercepted by a service worker
+  
+  
+
+
+

+
+ +
+

+
+
+
+
diff --git a/dom/workers/test/serviceworkers/test_empty_serviceworker.html b/dom/workers/test/serviceworkers/test_empty_serviceworker.html
new file mode 100644
index 000000000..e42951896
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_empty_serviceworker.html
@@ -0,0 +1,46 @@
+
+
+
+
+  Test that registering an empty service worker works
+  
+  
+
+
+

+ +

+
+
+
+
diff --git a/dom/workers/test/serviceworkers/test_error_reporting.html b/dom/workers/test/serviceworkers/test_error_reporting.html
new file mode 100644
index 000000000..619d602e8
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_error_reporting.html
@@ -0,0 +1,76 @@
+
+
+
+  Test Error Reporting of Service Worker Failures
+  
+  
+  
+  
+  
+
+
+
+
+
+
+
diff --git a/dom/workers/test/serviceworkers/test_escapedSlashes.html b/dom/workers/test/serviceworkers/test_escapedSlashes.html
new file mode 100644
index 000000000..001c66024
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_escapedSlashes.html
@@ -0,0 +1,102 @@
+
+
+
+
+  Test for escaped slashes in navigator.serviceWorker.register
+  
+  
+  
+
+
+

+ +

+
+
+
diff --git a/dom/workers/test/serviceworkers/test_eval_allowed.html b/dom/workers/test/serviceworkers/test_eval_allowed.html
new file mode 100644
index 000000000..bfe8ac280
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_eval_allowed.html
@@ -0,0 +1,51 @@
+
+
+
+
+  Bug 1160458 - CSP activated by default in Service Workers
+  
+  
+
+
+

+ +

+
+
+
+
diff --git a/dom/workers/test/serviceworkers/test_eval_allowed.html^headers^ b/dom/workers/test/serviceworkers/test_eval_allowed.html^headers^
new file mode 100644
index 000000000..51ffaa71d
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_eval_allowed.html^headers^
@@ -0,0 +1 @@
+Content-Security-Policy: "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self'"
diff --git a/dom/workers/test/serviceworkers/test_eventsource_intercept.html b/dom/workers/test/serviceworkers/test_eventsource_intercept.html
new file mode 100644
index 000000000..55df62bb7
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_eventsource_intercept.html
@@ -0,0 +1,103 @@
+
+
+
+
+  Bug 1182103 - Test EventSource scenarios with fetch interception
+  
+  
+
+
+

+ +

+
+
+
+
diff --git a/dom/workers/test/serviceworkers/test_fetch_event.html b/dom/workers/test/serviceworkers/test_fetch_event.html
new file mode 100644
index 000000000..764be87b1
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_fetch_event.html
@@ -0,0 +1,83 @@
+
+
+
+
+  Bug 94048 - test install event.
+  
+  
+
+
+

+ +

+
+
+
+
+
diff --git a/dom/workers/test/serviceworkers/test_fetch_integrity.html b/dom/workers/test/serviceworkers/test_fetch_integrity.html
new file mode 100644
index 000000000..50eb05581
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_fetch_integrity.html
@@ -0,0 +1,178 @@
+
+
+
+   Test fetch.integrity on console report for serviceWorker and sharedWorker 
+  
+  
+  
+  
+  
+
+
+
+
+
+
diff --git a/dom/workers/test/serviceworkers/test_file_blob_response.html b/dom/workers/test/serviceworkers/test_file_blob_response.html
new file mode 100644
index 000000000..6db0656c6
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_file_blob_response.html
@@ -0,0 +1,86 @@
+
+
+
+
+  Bug 1253777 - Test interception using file blob response body
+  
+  
+
+
+

+ +

+
+
+
+
+
diff --git a/dom/workers/test/serviceworkers/test_file_blob_upload.html b/dom/workers/test/serviceworkers/test_file_blob_upload.html
new file mode 100644
index 000000000..30a31eb7e
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_file_blob_upload.html
@@ -0,0 +1,145 @@
+
+
+
+
+  Bug 1203680 - Test interception of file blob uploads
+  
+  
+
+
+

+ +

+
+
+
+
+
diff --git a/dom/workers/test/serviceworkers/test_force_refresh.html b/dom/workers/test/serviceworkers/test_force_refresh.html
new file mode 100644
index 000000000..05caf0e6a
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_force_refresh.html
@@ -0,0 +1,84 @@
+
+
+
+
+  Bug 982726 - Test service worker post message 
+  
+  
+
+
+

+ +

+
+
+
+
diff --git a/dom/workers/test/serviceworkers/test_gzip_redirect.html b/dom/workers/test/serviceworkers/test_gzip_redirect.html
new file mode 100644
index 000000000..7ac92122c
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_gzip_redirect.html
@@ -0,0 +1,84 @@
+
+
+
+
+  Bug 982726 - Test service worker post message 
+  
+  
+
+
+

+ +

+
+
+
+
diff --git a/dom/workers/test/serviceworkers/test_hsts_upgrade_intercept.html b/dom/workers/test/serviceworkers/test_hsts_upgrade_intercept.html
new file mode 100644
index 000000000..dfce406b8
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_hsts_upgrade_intercept.html
@@ -0,0 +1,66 @@
+
+
+
+
+  Test that an HSTS upgraded request can be intercepted by a service worker
+  
+  
+
+
+

+
+ +
+

+
+
+
+
diff --git a/dom/workers/test/serviceworkers/test_https_fetch.html b/dom/workers/test/serviceworkers/test_https_fetch.html
new file mode 100644
index 000000000..e990200f8
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_https_fetch.html
@@ -0,0 +1,62 @@
+
+
+
+
+  Bug 1133763 - test fetch event in HTTPS origins
+  
+  
+
+
+

+ +

+
+
+
+
diff --git a/dom/workers/test/serviceworkers/test_https_fetch_cloned_response.html b/dom/workers/test/serviceworkers/test_https_fetch_cloned_response.html
new file mode 100644
index 000000000..1cf1dbef1
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_https_fetch_cloned_response.html
@@ -0,0 +1,56 @@
+
+
+
+
+  Bug 1133763 - test fetch event in HTTPS origins with a cloned response
+  
+  
+
+
+

+ +

+
+
+
+
diff --git a/dom/workers/test/serviceworkers/test_https_origin_after_redirect.html b/dom/workers/test/serviceworkers/test_https_origin_after_redirect.html
new file mode 100644
index 000000000..3878a1df6
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_https_origin_after_redirect.html
@@ -0,0 +1,57 @@
+
+
+
+
+  Test the origin of a redirected response from a service worker
+  
+  
+
+
+

+ +

+
+
+
+
diff --git a/dom/workers/test/serviceworkers/test_https_origin_after_redirect_cached.html b/dom/workers/test/serviceworkers/test_https_origin_after_redirect_cached.html
new file mode 100644
index 000000000..81a1d1da0
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_https_origin_after_redirect_cached.html
@@ -0,0 +1,57 @@
+
+
+
+
+  Test the origin of a redirected response from a service worker
+  
+  
+
+
+

+ +

+
+
+
+
diff --git a/dom/workers/test/serviceworkers/test_https_synth_fetch_from_cached_sw.html b/dom/workers/test/serviceworkers/test_https_synth_fetch_from_cached_sw.html
new file mode 100644
index 000000000..7bf3b352a
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_https_synth_fetch_from_cached_sw.html
@@ -0,0 +1,69 @@
+
+
+
+
+  Bug 1156847 - test fetch event generating a synthesized response in HTTPS origins from a cached SW
+  
+  
+
+
+

+
+ +
+

+
+
+
+
diff --git a/dom/workers/test/serviceworkers/test_imagecache.html b/dom/workers/test/serviceworkers/test_imagecache.html
new file mode 100644
index 000000000..8627b54af
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_imagecache.html
@@ -0,0 +1,55 @@
+
+
+
+
+  Bug 1202085 - Test that images from different controllers don't cached together
+  
+  
+
+
+

+
+ +
+

+
+
+
+
diff --git a/dom/workers/test/serviceworkers/test_imagecache_max_age.html b/dom/workers/test/serviceworkers/test_imagecache_max_age.html
new file mode 100644
index 000000000..eb3c1f166
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_imagecache_max_age.html
@@ -0,0 +1,71 @@
+
+
+
+
+  Test that the image cache respects a synthesized image's Cache headers
+  
+  
+
+
+

+
+ +
+

+
+
+
+
diff --git a/dom/workers/test/serviceworkers/test_importscript.html b/dom/workers/test/serviceworkers/test_importscript.html
new file mode 100644
index 000000000..5d2d5b352
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_importscript.html
@@ -0,0 +1,72 @@
+
+
+
+
+  Test service worker - script cache policy
+  
+  
+
+
+
+ + + + diff --git a/dom/workers/test/serviceworkers/test_importscript_mixedcontent.html b/dom/workers/test/serviceworkers/test_importscript_mixedcontent.html new file mode 100644 index 000000000..a659af92b --- /dev/null +++ b/dom/workers/test/serviceworkers/test_importscript_mixedcontent.html @@ -0,0 +1,53 @@ + + + + + Bug 1198078 - test that we respect mixed content blocking in importScript() inside service workers + + + + +

+ +

+
+
+
+
diff --git a/dom/workers/test/serviceworkers/test_install_event.html b/dom/workers/test/serviceworkers/test_install_event.html
new file mode 100644
index 000000000..0e59510fe
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_install_event.html
@@ -0,0 +1,144 @@
+
+
+
+
+  Bug 94048 - test install event.
+  
+  
+
+
+

+ +

+
+
+
+
+
diff --git a/dom/workers/test/serviceworkers/test_install_event_gc.html b/dom/workers/test/serviceworkers/test_install_event_gc.html
new file mode 100644
index 000000000..ccccd2b43
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_install_event_gc.html
@@ -0,0 +1,121 @@
+
+
+
+
+  Test install event being GC'd before waitUntil fulfills
+  
+  
+
+
+
+
+
+
diff --git a/dom/workers/test/serviceworkers/test_installation_simple.html b/dom/workers/test/serviceworkers/test_installation_simple.html
new file mode 100644
index 000000000..1b0d6c947
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_installation_simple.html
@@ -0,0 +1,212 @@
+
+
+
+
+  Bug 930348 - test stub Navigator ServiceWorker utilities.
+  
+  
+
+
+

+ +

+
+
+
+
+
diff --git a/dom/workers/test/serviceworkers/test_match_all.html b/dom/workers/test/serviceworkers/test_match_all.html
new file mode 100644
index 000000000..f4e65a730
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_match_all.html
@@ -0,0 +1,80 @@
+
+
+
+
+  Bug 982726 - test match_all not crashing
+  
+  
+
+
+

+ +

+
+
+
+
+
diff --git a/dom/workers/test/serviceworkers/test_match_all_advanced.html b/dom/workers/test/serviceworkers/test_match_all_advanced.html
new file mode 100644
index 000000000..a458ed70b
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_match_all_advanced.html
@@ -0,0 +1,100 @@
+
+
+
+
+  Bug 982726 - Test matchAll with multiple clients
+  
+  
+
+
+

+ +

+
+
+
+
+
diff --git a/dom/workers/test/serviceworkers/test_match_all_client_id.html b/dom/workers/test/serviceworkers/test_match_all_client_id.html
new file mode 100644
index 000000000..4c8ed9673
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_match_all_client_id.html
@@ -0,0 +1,91 @@
+
+
+
+
+  Bug 1058311 - Test matchAll client id 
+  
+  
+
+
+

+ +

+
+
+
+
diff --git a/dom/workers/test/serviceworkers/test_match_all_client_properties.html b/dom/workers/test/serviceworkers/test_match_all_client_properties.html
new file mode 100644
index 000000000..14e3445a4
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_match_all_client_properties.html
@@ -0,0 +1,97 @@
+
+
+
+
+  Bug 1058311 - Test matchAll clients properties 
+  
+  
+
+
+

+ +

+
+
+
+
diff --git a/dom/workers/test/serviceworkers/test_navigator.html b/dom/workers/test/serviceworkers/test_navigator.html
new file mode 100644
index 000000000..164f41bcd
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_navigator.html
@@ -0,0 +1,40 @@
+
+
+
+
+  Bug 930348 - test stub Navigator ServiceWorker utilities.
+  
+  
+
+
+

+ +

+
+
+
+
+
diff --git a/dom/workers/test/serviceworkers/test_not_intercept_plugin.html b/dom/workers/test/serviceworkers/test_not_intercept_plugin.html
new file mode 100644
index 000000000..a90e068d3
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_not_intercept_plugin.html
@@ -0,0 +1,78 @@
+
+
+
+
+  Bug 1187766 - Test loading plugins scenarios with fetch interception.
+  
+  
+
+
+

+ +

+
+
+
+
+
diff --git a/dom/workers/test/serviceworkers/test_notification_constructor_error.html b/dom/workers/test/serviceworkers/test_notification_constructor_error.html
new file mode 100644
index 000000000..6a8ecf8c0
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_notification_constructor_error.html
@@ -0,0 +1,52 @@
+
+
+
+
+  Bug XXXXXXX - Check that Notification constructor throws in ServiceWorkerGlobalScope
+  
+  
+  
+  
+
+
+

+ +

+
+
+
+
+
diff --git a/dom/workers/test/serviceworkers/test_notification_get.html b/dom/workers/test/serviceworkers/test_notification_get.html
new file mode 100644
index 000000000..dbb312e7b
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_notification_get.html
@@ -0,0 +1,213 @@
+
+
+
+  ServiceWorkerRegistration.getNotifications() on main thread and worker thread.
+  
+  
+  
+  
+
+
+

+ +

+
+
+
diff --git a/dom/workers/test/serviceworkers/test_notificationclick-otherwindow.html b/dom/workers/test/serviceworkers/test_notificationclick-otherwindow.html
new file mode 100644
index 000000000..4a785be9a
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_notificationclick-otherwindow.html
@@ -0,0 +1,62 @@
+
+
+
+
+  Bug 1114554 - Test ServiceWorkerGlobalScope.notificationclick event.
+  
+  
+  
+  
+
+
+Bug 1114554
+

+ +
+
+ + + diff --git a/dom/workers/test/serviceworkers/test_notificationclick.html b/dom/workers/test/serviceworkers/test_notificationclick.html new file mode 100644 index 000000000..d5c3ecf8b --- /dev/null +++ b/dom/workers/test/serviceworkers/test_notificationclick.html @@ -0,0 +1,62 @@ + + + + + Bug 1114554 - Test ServiceWorkerGlobalScope.notificationclick event. + + + + + + +Bug 1114554 +

+ +
+
+ + + diff --git a/dom/workers/test/serviceworkers/test_notificationclick_focus.html b/dom/workers/test/serviceworkers/test_notificationclick_focus.html new file mode 100644 index 000000000..81d6e269c --- /dev/null +++ b/dom/workers/test/serviceworkers/test_notificationclick_focus.html @@ -0,0 +1,63 @@ + + + + + Bug 1144660 - Test client.focus() permissions on notification click + + + + + + +Bug 1114554 +

+ +
+
+ + + diff --git a/dom/workers/test/serviceworkers/test_notificationclose.html b/dom/workers/test/serviceworkers/test_notificationclose.html new file mode 100644 index 000000000..3b81132c4 --- /dev/null +++ b/dom/workers/test/serviceworkers/test_notificationclose.html @@ -0,0 +1,62 @@ + + + + + Bug 1265841 - Test ServiceWorkerGlobalScope.notificationclose event. + + + + + + +Bug 1265841 +

+ +
+
+ + + diff --git a/dom/workers/test/serviceworkers/test_opaque_intercept.html b/dom/workers/test/serviceworkers/test_opaque_intercept.html new file mode 100644 index 000000000..5cb12e518 --- /dev/null +++ b/dom/workers/test/serviceworkers/test_opaque_intercept.html @@ -0,0 +1,85 @@ + + + + + Bug 982726 - Test service worker post message + + + + +

+ +

+
+
+
+
diff --git a/dom/workers/test/serviceworkers/test_openWindow.html b/dom/workers/test/serviceworkers/test_openWindow.html
new file mode 100644
index 000000000..2417648b9
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_openWindow.html
@@ -0,0 +1,117 @@
+
+
+
+
+  Bug 1172870 - Test clients.openWindow
+  
+  
+  
+  
+
+
+Bug 1172870
+

+ +
+
+ + + diff --git a/dom/workers/test/serviceworkers/test_origin_after_redirect.html b/dom/workers/test/serviceworkers/test_origin_after_redirect.html new file mode 100644 index 000000000..b68537d9d --- /dev/null +++ b/dom/workers/test/serviceworkers/test_origin_after_redirect.html @@ -0,0 +1,58 @@ + + + + + Test the origin of a redirected response from a service worker + + + + +

+ +

+
+
+
+
diff --git a/dom/workers/test/serviceworkers/test_origin_after_redirect_cached.html b/dom/workers/test/serviceworkers/test_origin_after_redirect_cached.html
new file mode 100644
index 000000000..69644abfa
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_origin_after_redirect_cached.html
@@ -0,0 +1,58 @@
+
+
+
+
+  Test the origin of a redirected response from a service worker
+  
+  
+
+
+

+ +

+
+
+
+
diff --git a/dom/workers/test/serviceworkers/test_origin_after_redirect_to_https.html b/dom/workers/test/serviceworkers/test_origin_after_redirect_to_https.html
new file mode 100644
index 000000000..dcac11aea
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_origin_after_redirect_to_https.html
@@ -0,0 +1,57 @@
+
+
+
+
+  Test the origin of a redirected response from a service worker
+  
+  
+
+
+

+ +

+
+
+
+
diff --git a/dom/workers/test/serviceworkers/test_origin_after_redirect_to_https_cached.html b/dom/workers/test/serviceworkers/test_origin_after_redirect_to_https_cached.html
new file mode 100644
index 000000000..3922fdb90
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_origin_after_redirect_to_https_cached.html
@@ -0,0 +1,58 @@
+
+
+
+
+  Test the origin of a redirected response from a service worker
+  
+  
+
+
+

+ +

+
+
+
+
diff --git a/dom/workers/test/serviceworkers/test_post_message.html b/dom/workers/test/serviceworkers/test_post_message.html
new file mode 100644
index 000000000..e366423cb
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_post_message.html
@@ -0,0 +1,80 @@
+
+
+
+
+  Bug 982726 - Test service worker post message 
+  
+  
+
+
+

+ +

+
+
+
+
+
diff --git a/dom/workers/test/serviceworkers/test_post_message_advanced.html b/dom/workers/test/serviceworkers/test_post_message_advanced.html
new file mode 100644
index 000000000..8ea0d2300
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_post_message_advanced.html
@@ -0,0 +1,109 @@
+
+
+
+
+  Bug 982726 - Test service worker post message advanced 
+  
+  
+
+
+

+ +

+
+
+
+
+
diff --git a/dom/workers/test/serviceworkers/test_post_message_source.html b/dom/workers/test/serviceworkers/test_post_message_source.html
new file mode 100644
index 000000000..543f64b4a
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_post_message_source.html
@@ -0,0 +1,68 @@
+
+
+
+
+  Bug 1142015 - Test service worker post message source 
+  
+  
+
+
+

+ +

+
+
+
+
+
diff --git a/dom/workers/test/serviceworkers/test_privateBrowsing.html b/dom/workers/test/serviceworkers/test_privateBrowsing.html
new file mode 100644
index 000000000..976337711
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_privateBrowsing.html
@@ -0,0 +1,113 @@
+
+
+  Test for ServiceWorker - Private Browsing
+  
+  
+
+
+
+
+
+
diff --git a/dom/workers/test/serviceworkers/test_register_base.html b/dom/workers/test/serviceworkers/test_register_base.html
new file mode 100644
index 000000000..58b08d27a
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_register_base.html
@@ -0,0 +1,41 @@
+
+
+
+
+  Test that registering a service worker uses the docuemnt URI for the secure origin check
+  
+  
+  
+
+
+

+ +

+
+
+
diff --git a/dom/workers/test/serviceworkers/test_register_https_in_http.html b/dom/workers/test/serviceworkers/test_register_https_in_http.html
new file mode 100644
index 000000000..2c8e998f7
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_register_https_in_http.html
@@ -0,0 +1,46 @@
+
+
+
+
+  Bug 1172948 - Test that registering a service worker from inside an HTTPS iframe embedded in an HTTP iframe doesn't work
+  
+  
+
+
+

+ +

+
+
+
diff --git a/dom/workers/test/serviceworkers/test_request_context.js b/dom/workers/test/serviceworkers/test_request_context.js
new file mode 100644
index 000000000..aebba79a7
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_request_context.js
@@ -0,0 +1,75 @@
+// Copied from /dom/plugins/test/mochitest/utils.js
+function getTestPlugin(pluginName) {
+  var ph = SpecialPowers.Cc["@mozilla.org/plugin/host;1"]
+                                 .getService(SpecialPowers.Ci.nsIPluginHost);
+  var tags = ph.getPluginTags();
+  var name = pluginName || "Test Plug-in";
+  for (var tag of tags) {
+    if (tag.name == name) {
+      return tag;
+    }
+  }
+
+  ok(false, "Could not find plugin tag with plugin name '" + name + "'");
+  return null;
+}
+function setTestPluginEnabledState(newEnabledState, pluginName) {
+  var oldEnabledState = SpecialPowers.setTestPluginEnabledState(newEnabledState, pluginName);
+  if (!oldEnabledState) {
+    return;
+  }
+  var plugin = getTestPlugin(pluginName);
+  while (plugin.enabledState != newEnabledState) {
+    // Run a nested event loop to wait for the preference change to
+    // propagate to the child. Yuck!
+    SpecialPowers.Services.tm.currentThread.processNextEvent(true);
+  }
+  SimpleTest.registerCleanupFunction(function() {
+    SpecialPowers.setTestPluginEnabledState(oldEnabledState, pluginName);
+  });
+}
+setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
+
+function isMulet() {
+  try {
+    return SpecialPowers.getBoolPref("b2g.is_mulet");
+  } catch(e) {
+    return false;
+  }
+}
+
+var iframe;
+function runTest() {
+  iframe = document.querySelector("iframe");
+  iframe.src = "/tests/dom/workers/test/serviceworkers/fetch/context/register.html";
+  window.onmessage = function(e) {
+    if (e.data.status == "ok") {
+      ok(e.data.result, e.data.message);
+    } else if (e.data.status == "todo") {
+      todo(e.data.result, e.data.message);
+    } else if (e.data.status == "registrationdone") {
+      iframe.src = "/tests/dom/workers/test/serviceworkers/fetch/context/index.html?" + gTest;
+    } else if (e.data.status == "done") {
+      iframe.src = "/tests/dom/workers/test/serviceworkers/fetch/context/unregister.html";
+    } else if (e.data.status == "unregistrationdone") {
+      window.onmessage = null;
+      ok(true, "Test finished successfully");
+      SimpleTest.finish();
+    }
+  };
+}
+
+SimpleTest.waitForExplicitFinish();
+onload = function() {
+  SpecialPowers.pushPrefEnv({"set": [
+    ["beacon.enabled", true],
+    ["browser.send_pings", true],
+    ["browser.send_pings.max_per_link", -1],
+    ["dom.caches.enabled", true],
+    ["dom.requestcontext.enabled", true],
+    ["dom.serviceWorkers.exemptFromPerDomainMax", true],
+    ["dom.serviceWorkers.enabled", true],
+    ["dom.serviceWorkers.openWindow.enabled", true],
+    ["dom.serviceWorkers.testing.enabled", true],
+  ]}, runTest);
+};
diff --git a/dom/workers/test/serviceworkers/test_request_context_audio.html b/dom/workers/test/serviceworkers/test_request_context_audio.html
new file mode 100644
index 000000000..929a24428
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_request_context_audio.html
@@ -0,0 +1,19 @@
+
+
+
+
+  Bug 1121157 - Test that Request objects passed to FetchEvent have the correct context
+  
+  
+  
+
+
+
+
+
+
diff --git a/dom/workers/test/serviceworkers/test_request_context_beacon.html b/dom/workers/test/serviceworkers/test_request_context_beacon.html
new file mode 100644
index 000000000..ce44214d6
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_request_context_beacon.html
@@ -0,0 +1,19 @@
+
+
+
+
+  Bug 1121157 - Test that Request objects passed to FetchEvent have the correct context
+  
+  
+  
+
+
+
+
+
+
diff --git a/dom/workers/test/serviceworkers/test_request_context_cache.html b/dom/workers/test/serviceworkers/test_request_context_cache.html
new file mode 100644
index 000000000..3d62baabc
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_request_context_cache.html
@@ -0,0 +1,19 @@
+
+
+
+
+  Bug 1121157 - Test that Request objects passed to FetchEvent have the correct context
+  
+  
+  
+
+
+
+
+
+
diff --git a/dom/workers/test/serviceworkers/test_request_context_cspreport.html b/dom/workers/test/serviceworkers/test_request_context_cspreport.html
new file mode 100644
index 000000000..a27e79303
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_request_context_cspreport.html
@@ -0,0 +1,19 @@
+
+
+
+
+  Bug 1121157 - Test that Request objects passed to FetchEvent have the correct context
+  
+  
+  
+
+
+
+
+
+
diff --git a/dom/workers/test/serviceworkers/test_request_context_embed.html b/dom/workers/test/serviceworkers/test_request_context_embed.html
new file mode 100644
index 000000000..f8d374246
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_request_context_embed.html
@@ -0,0 +1,19 @@
+
+
+
+
+  Bug 1121157 - Test that Request objects passed to FetchEvent have the correct context
+  
+  
+  
+
+
+
+
+
+
diff --git a/dom/workers/test/serviceworkers/test_request_context_fetch.html b/dom/workers/test/serviceworkers/test_request_context_fetch.html
new file mode 100644
index 000000000..94de8be31
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_request_context_fetch.html
@@ -0,0 +1,19 @@
+
+
+
+
+  Bug 1121157 - Test that Request objects passed to FetchEvent have the correct context
+  
+  
+  
+
+
+
+
+
+
diff --git a/dom/workers/test/serviceworkers/test_request_context_font.html b/dom/workers/test/serviceworkers/test_request_context_font.html
new file mode 100644
index 000000000..d81a0686b
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_request_context_font.html
@@ -0,0 +1,19 @@
+
+
+
+
+  Bug 1121157 - Test that Request objects passed to FetchEvent have the correct context
+  
+  
+  
+
+
+
+
+
+
diff --git a/dom/workers/test/serviceworkers/test_request_context_frame.html b/dom/workers/test/serviceworkers/test_request_context_frame.html
new file mode 100644
index 000000000..d5dc1f745
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_request_context_frame.html
@@ -0,0 +1,19 @@
+
+
+
+
+  Bug 1121157 - Test that Request objects passed to FetchEvent have the correct context
+  
+  
+  
+
+
+
+
+
+
diff --git a/dom/workers/test/serviceworkers/test_request_context_iframe.html b/dom/workers/test/serviceworkers/test_request_context_iframe.html
new file mode 100644
index 000000000..d3b0675e0
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_request_context_iframe.html
@@ -0,0 +1,19 @@
+
+
+
+
+  Bug 1121157 - Test that Request objects passed to FetchEvent have the correct context
+  
+  
+  
+
+
+
+
+
+
diff --git a/dom/workers/test/serviceworkers/test_request_context_image.html b/dom/workers/test/serviceworkers/test_request_context_image.html
new file mode 100644
index 000000000..810063da4
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_request_context_image.html
@@ -0,0 +1,19 @@
+
+
+
+
+  Bug 1121157 - Test that Request objects passed to FetchEvent have the correct context
+  
+  
+  
+
+
+
+
+
+
diff --git a/dom/workers/test/serviceworkers/test_request_context_imagesrcset.html b/dom/workers/test/serviceworkers/test_request_context_imagesrcset.html
new file mode 100644
index 000000000..95b2b7214
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_request_context_imagesrcset.html
@@ -0,0 +1,19 @@
+
+
+
+
+  Bug 1121157 - Test that Request objects passed to FetchEvent have the correct context
+  
+  
+  
+
+
+
+
+
+
diff --git a/dom/workers/test/serviceworkers/test_request_context_internal.html b/dom/workers/test/serviceworkers/test_request_context_internal.html
new file mode 100644
index 000000000..45f454495
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_request_context_internal.html
@@ -0,0 +1,19 @@
+
+
+
+
+  Bug 1121157 - Test that Request objects passed to FetchEvent have the correct context
+  
+  
+  
+
+
+
+
+
+
diff --git a/dom/workers/test/serviceworkers/test_request_context_nestedworker.html b/dom/workers/test/serviceworkers/test_request_context_nestedworker.html
new file mode 100644
index 000000000..226de691b
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_request_context_nestedworker.html
@@ -0,0 +1,19 @@
+
+
+
+
+  Bug 1121157 - Test that Request objects passed to FetchEvent have the correct context
+  
+  
+  
+
+
+
+
+
+
diff --git a/dom/workers/test/serviceworkers/test_request_context_nestedworkerinsharedworker.html b/dom/workers/test/serviceworkers/test_request_context_nestedworkerinsharedworker.html
new file mode 100644
index 000000000..48934a57c
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_request_context_nestedworkerinsharedworker.html
@@ -0,0 +1,19 @@
+
+
+
+
+  Bug 1121157 - Test that Request objects passed to FetchEvent have the correct context
+  
+  
+  
+
+
+
+
+
+
diff --git a/dom/workers/test/serviceworkers/test_request_context_object.html b/dom/workers/test/serviceworkers/test_request_context_object.html
new file mode 100644
index 000000000..189c5adbb
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_request_context_object.html
@@ -0,0 +1,19 @@
+
+
+
+
+  Bug 1121157 - Test that Request objects passed to FetchEvent have the correct context
+  
+  
+  
+
+
+
+
+
+
diff --git a/dom/workers/test/serviceworkers/test_request_context_picture.html b/dom/workers/test/serviceworkers/test_request_context_picture.html
new file mode 100644
index 000000000..1b49e7eb9
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_request_context_picture.html
@@ -0,0 +1,19 @@
+
+
+
+
+  Bug 1121157 - Test that Request objects passed to FetchEvent have the correct context
+  
+  
+  
+
+
+
+
+
+
diff --git a/dom/workers/test/serviceworkers/test_request_context_ping.html b/dom/workers/test/serviceworkers/test_request_context_ping.html
new file mode 100644
index 000000000..460e9efb4
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_request_context_ping.html
@@ -0,0 +1,19 @@
+
+
+
+
+  Bug 1121157 - Test that Request objects passed to FetchEvent have the correct context
+  
+  
+  
+
+
+
+
+
+
diff --git a/dom/workers/test/serviceworkers/test_request_context_plugin.html b/dom/workers/test/serviceworkers/test_request_context_plugin.html
new file mode 100644
index 000000000..862e9d4a5
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_request_context_plugin.html
@@ -0,0 +1,19 @@
+
+
+
+
+  Bug 1121157 - Test that Request objects passed to FetchEvent have the correct context
+  
+  
+  
+
+
+
+
+
+
diff --git a/dom/workers/test/serviceworkers/test_request_context_script.html b/dom/workers/test/serviceworkers/test_request_context_script.html
new file mode 100644
index 000000000..ec560ef72
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_request_context_script.html
@@ -0,0 +1,19 @@
+
+
+
+
+  Bug 1121157 - Test that Request objects passed to FetchEvent have the correct context
+  
+  
+  
+
+
+
+
+
+
diff --git a/dom/workers/test/serviceworkers/test_request_context_sharedworker.html b/dom/workers/test/serviceworkers/test_request_context_sharedworker.html
new file mode 100644
index 000000000..93ccdf3ae
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_request_context_sharedworker.html
@@ -0,0 +1,19 @@
+
+
+
+
+  Bug 1121157 - Test that Request objects passed to FetchEvent have the correct context
+  
+  
+  
+
+
+
+
+
+
diff --git a/dom/workers/test/serviceworkers/test_request_context_style.html b/dom/workers/test/serviceworkers/test_request_context_style.html
new file mode 100644
index 000000000..b557d5c04
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_request_context_style.html
@@ -0,0 +1,19 @@
+
+
+
+
+  Bug 1121157 - Test that Request objects passed to FetchEvent have the correct context
+  
+  
+  
+
+
+
+
+
+
diff --git a/dom/workers/test/serviceworkers/test_request_context_track.html b/dom/workers/test/serviceworkers/test_request_context_track.html
new file mode 100644
index 000000000..1b161c4d0
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_request_context_track.html
@@ -0,0 +1,19 @@
+
+
+
+
+  Bug 1121157 - Test that Request objects passed to FetchEvent have the correct context
+  
+  
+  
+
+
+
+
+
+
diff --git a/dom/workers/test/serviceworkers/test_request_context_video.html b/dom/workers/test/serviceworkers/test_request_context_video.html
new file mode 100644
index 000000000..1886e31d7
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_request_context_video.html
@@ -0,0 +1,19 @@
+
+
+
+
+  Bug 1121157 - Test that Request objects passed to FetchEvent have the correct context
+  
+  
+  
+
+
+
+
+
+
diff --git a/dom/workers/test/serviceworkers/test_request_context_worker.html b/dom/workers/test/serviceworkers/test_request_context_worker.html
new file mode 100644
index 000000000..9de127304
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_request_context_worker.html
@@ -0,0 +1,19 @@
+
+
+
+
+  Bug 1121157 - Test that Request objects passed to FetchEvent have the correct context
+  
+  
+  
+
+
+
+
+
+
diff --git a/dom/workers/test/serviceworkers/test_request_context_xhr.html b/dom/workers/test/serviceworkers/test_request_context_xhr.html
new file mode 100644
index 000000000..ed0d60bf8
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_request_context_xhr.html
@@ -0,0 +1,19 @@
+
+
+
+
+  Bug 1121157 - Test that Request objects passed to FetchEvent have the correct context
+  
+  
+  
+
+
+
+
+
+
diff --git a/dom/workers/test/serviceworkers/test_request_context_xslt.html b/dom/workers/test/serviceworkers/test_request_context_xslt.html
new file mode 100644
index 000000000..a6d837b69
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_request_context_xslt.html
@@ -0,0 +1,19 @@
+
+
+
+
+  Bug 1121157 - Test that Request objects passed to FetchEvent have the correct context
+  
+  
+  
+
+
+
+
+
+
diff --git a/dom/workers/test/serviceworkers/test_sandbox_intercept.html b/dom/workers/test/serviceworkers/test_sandbox_intercept.html
new file mode 100644
index 000000000..273df53ff
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_sandbox_intercept.html
@@ -0,0 +1,50 @@
+
+
+
+
+  Bug 1142727 - Test that sandboxed iframes are not intercepted
+  
+  
+
+
+

+
+ +
+

+
+
+
+
diff --git a/dom/workers/test/serviceworkers/test_sanitize.html b/dom/workers/test/serviceworkers/test_sanitize.html
new file mode 100644
index 000000000..842cb38c3
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_sanitize.html
@@ -0,0 +1,87 @@
+
+
+
+
+  Bug 1080109 - Clear ServiceWorker registrations for all domains
+  
+  
+
+
+

+ +

+
+
+
+
+
diff --git a/dom/workers/test/serviceworkers/test_sanitize_domain.html b/dom/workers/test/serviceworkers/test_sanitize_domain.html
new file mode 100644
index 000000000..054b60f37
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_sanitize_domain.html
@@ -0,0 +1,90 @@
+
+
+
+
+  Bug 1080109 - Clear ServiceWorker registrations for specific domains
+  
+  
+
+
+

+ +

+
+
+
+
+
diff --git a/dom/workers/test/serviceworkers/test_scopes.html b/dom/workers/test/serviceworkers/test_scopes.html
new file mode 100644
index 000000000..2d8116f83
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_scopes.html
@@ -0,0 +1,121 @@
+
+
+
+
+  Bug 984048 - Test scope glob matching.
+  
+  
+
+
+

+ +

+
+
+
+
+
diff --git a/dom/workers/test/serviceworkers/test_service_worker_allowed.html b/dom/workers/test/serviceworkers/test_service_worker_allowed.html
new file mode 100644
index 000000000..eca94ebb4
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_service_worker_allowed.html
@@ -0,0 +1,74 @@
+
+
+
+
+  Test the Service-Worker-Allowed header
+  
+  
+
+
+
+ + + + diff --git a/dom/workers/test/serviceworkers/test_serviceworker_header.html b/dom/workers/test/serviceworkers/test_serviceworker_header.html new file mode 100644 index 000000000..ac5a6e49f --- /dev/null +++ b/dom/workers/test/serviceworkers/test_serviceworker_header.html @@ -0,0 +1,41 @@ + + + + + Test that service worker scripts are fetched with a Service-Worker: script header + + + + + +

+ +

+
+
+
diff --git a/dom/workers/test/serviceworkers/test_serviceworker_interfaces.html b/dom/workers/test/serviceworkers/test_serviceworker_interfaces.html
new file mode 100644
index 000000000..0130ca2d9
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_serviceworker_interfaces.html
@@ -0,0 +1,106 @@
+
+
+
+
+  Validate Interfaces Exposed to Service Workers
+  
+  
+  
+
+
+
+
+
diff --git a/dom/workers/test/serviceworkers/test_serviceworker_interfaces.js b/dom/workers/test/serviceworkers/test_serviceworker_interfaces.js
new file mode 100644
index 000000000..9dbfcc099
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_serviceworker_interfaces.js
@@ -0,0 +1,278 @@
+// 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",
+//   { 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.
+
+// IMPORTANT: Do not change this list without review from
+//            a JavaScript Engine peer!
+var ecmaGlobals =
+  [
+    "Array",
+    "ArrayBuffer",
+    "Boolean",
+    "DataView",
+    "Date",
+    "Error",
+    "EvalError",
+    "Float32Array",
+    "Float64Array",
+    "Function",
+    "Infinity",
+    "Int16Array",
+    "Int32Array",
+    "Int8Array",
+    "InternalError",
+    {name: "Intl", android: false},
+    "Iterator",
+    "JSON",
+    "Map",
+    "Math",
+    "NaN",
+    "Number",
+    "Object",
+    "Promise",
+    "Proxy",
+    "RangeError",
+    "ReferenceError",
+    "Reflect",
+    "RegExp",
+    "Set",
+    {name: "SharedArrayBuffer", release: false},
+    {name: "SIMD", nightly: true},
+    {name: "Atomics", release: false},
+    "StopIteration",
+    "String",
+    "Symbol",
+    "SyntaxError",
+    {name: "TypedObject", nightly: true},
+    "TypeError",
+    "Uint16Array",
+    "Uint32Array",
+    "Uint8Array",
+    "Uint8ClampedArray",
+    "URIError",
+    "WeakMap",
+    "WeakSet",
+  ];
+// 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!
+var interfaceNamesInGlobalScope =
+  [
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "Blob",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "BroadcastChannel",
+// 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!
+    "Client",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "Clients",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "Crypto",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "CustomEvent",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "Directory",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "DOMCursor",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "DOMError",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "DOMException",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "DOMRequest",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "DOMStringList",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "Event",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "EventTarget",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "ExtendableEvent",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "ExtendableMessageEvent",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "FetchEvent",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "File",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "FileReader",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "FileReaderSync",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "FormData",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "Headers",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "IDBCursor",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "IDBCursorWithValue",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "IDBDatabase",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "IDBFactory",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "IDBIndex",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "IDBKeyRange",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "IDBObjectStore",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "IDBOpenDBRequest",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "IDBRequest",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "IDBTransaction",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "IDBVersionChangeEvent",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "ImageBitmap",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "ImageBitmapRenderingContext",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "ImageData",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "MessageChannel",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "MessageEvent",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "MessagePort",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "Notification",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "NotificationEvent",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "Performance",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "PerformanceEntry",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "PerformanceMark",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "PerformanceMeasure",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    { name: "PerformanceObserver", nightly: true },
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    { name: "PerformanceObserverEntryList", nightly: true },
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "Request",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "Response",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "ServiceWorker",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "ServiceWorkerGlobalScope",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "ServiceWorkerRegistration",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    {name: "StorageManager", nightly: true},
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "SubtleCrypto",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "TextDecoder",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "TextEncoder",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "URL",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "URLSearchParams",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "WebSocket",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "WindowClient",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "WorkerGlobalScope",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "WorkerLocation",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "WorkerNavigator",
+// 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!
+
+function createInterfaceMap(version, userAgent) {
+  var isNightly = version.endsWith("a1");
+  var isRelease = !version.includes("a");
+  var isDesktop = !/Mobile|Tablet/.test(userAgent);
+  var isAndroid = !!navigator.userAgent.includes("Android");
+
+  var interfaceMap = {};
+
+  function addInterfaces(interfaces)
+  {
+    for (var entry of interfaces) {
+      if (typeof(entry) === "string") {
+        interfaceMap[entry] = true;
+      } else {
+        ok(!("pref" in entry), "Bogus pref annotation for " + entry.name);
+        if ((entry.nightly === !isNightly) ||
+            (entry.nightlyAndroid === !(isAndroid && isNightly) && isAndroid) ||
+            (entry.nonReleaseAndroid === !(isAndroid && !isRelease) && isAndroid) ||
+            (entry.desktop === !isDesktop) ||
+            (entry.android === !isAndroid && !entry.nonReleaseAndroid && !entry.nightlyAndroid) ||
+            (entry.release === !isRelease)) {
+          interfaceMap[entry.name] = false;
+        } else {
+          interfaceMap[entry.name] = true;
+        }
+      }
+    }
+  }
+
+  addInterfaces(ecmaGlobals);
+  addInterfaces(interfaceNamesInGlobalScope);
+
+  return interfaceMap;
+}
+
+function runTest(version, userAgent) {
+  var interfaceMap = createInterfaceMap(version, userAgent);
+  for (var name of Object.getOwnPropertyNames(self)) {
+    // An interface name should start with an upper case character.
+    if (!/^[A-Z]/.test(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 on the service worker? 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 self === interfaceMap[name],
+       name + " should " + (interfaceMap[name] ? "" : " NOT") + " be defined on the global scope");
+    if (!interfaceMap[name]) {
+      delete interfaceMap[name];
+    }
+  }
+  is(Object.keys(interfaceMap).length, 0,
+     "The following interface(s) are not enumerated: " + Object.keys(interfaceMap).join(", "));
+}
+
+workerTestGetVersion(function(version) {
+  workerTestGetUserAgent(function(userAgent) {
+    runTest(version, userAgent);
+    workerTestDone();
+  });
+});
diff --git a/dom/workers/test/serviceworkers/test_serviceworker_not_sharedworker.html b/dom/workers/test/serviceworkers/test_serviceworker_not_sharedworker.html
new file mode 100644
index 000000000..96dd9f159
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_serviceworker_not_sharedworker.html
@@ -0,0 +1,66 @@
+
+
+
+
+  Bug 1141274 - test that service workers and shared workers are separate
+  
+  
+
+
+

+ +

+
+
+
+
diff --git a/dom/workers/test/serviceworkers/test_serviceworkerinfo.xul b/dom/workers/test/serviceworkers/test_serviceworkerinfo.xul
new file mode 100644
index 000000000..96e4bb1c3
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_serviceworkerinfo.xul
@@ -0,0 +1,115 @@
+
+
+
+  
+
+  
+    

+ +

+    
+  
+  
diff --git a/dom/workers/test/serviceworkers/test_serviceworkermanager.xul b/dom/workers/test/serviceworkers/test_serviceworkermanager.xul new file mode 100644 index 000000000..ead935a3c --- /dev/null +++ b/dom/workers/test/serviceworkers/test_serviceworkermanager.xul @@ -0,0 +1,80 @@ + + + + + + +

+ +

+    
+  
+  
diff --git a/dom/workers/test/serviceworkers/test_serviceworkerregistrationinfo.xul b/dom/workers/test/serviceworkers/test_serviceworkerregistrationinfo.xul new file mode 100644 index 000000000..c879dc01b --- /dev/null +++ b/dom/workers/test/serviceworkers/test_serviceworkerregistrationinfo.xul @@ -0,0 +1,115 @@ + + + + + + +

+ +

+    
+  
+  
diff --git a/dom/workers/test/serviceworkers/test_skip_waiting.html b/dom/workers/test/serviceworkers/test_skip_waiting.html new file mode 100644 index 000000000..7707d6035 --- /dev/null +++ b/dom/workers/test/serviceworkers/test_skip_waiting.html @@ -0,0 +1,95 @@ + + + + + Bug 1131352 - Add ServiceWorkerGlobalScope skipWaiting() + + + + +

+ +

+
+
+
+
diff --git a/dom/workers/test/serviceworkers/test_strict_mode_warning.html b/dom/workers/test/serviceworkers/test_strict_mode_warning.html
new file mode 100644
index 000000000..5b66673b9
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_strict_mode_warning.html
@@ -0,0 +1,42 @@
+
+
+
+
+  Bug 1170550 - test registration of service worker scripts with a strict mode warning
+  
+  
+
+
+

+ +

+
+
+
+
diff --git a/dom/workers/test/serviceworkers/test_third_party_iframes.html b/dom/workers/test/serviceworkers/test_third_party_iframes.html
new file mode 100644
index 000000000..33e815379
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_third_party_iframes.html
@@ -0,0 +1,175 @@
+
+
+
+
+  
+  Bug 1152899 - Disallow the interception of third-party iframes using service workers when the third-party cookie preference is set
+  
+  
+
+
+

+ +

+
+
+
+
diff --git a/dom/workers/test/serviceworkers/test_unregister.html b/dom/workers/test/serviceworkers/test_unregister.html
new file mode 100644
index 000000000..8366f50c1
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_unregister.html
@@ -0,0 +1,138 @@
+
+
+
+
+  Bug 984048 - Test unregister
+  
+  
+
+
+

+ +

+
+
+
+
+
diff --git a/dom/workers/test/serviceworkers/test_unresolved_fetch_interception.html b/dom/workers/test/serviceworkers/test_unresolved_fetch_interception.html
new file mode 100644
index 000000000..7296a3ddf
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_unresolved_fetch_interception.html
@@ -0,0 +1,100 @@
+
+
+
+
+  Test for Bug 1188545
+  
+  
+  
+  
+  
+
+Mozilla Bug 118845
+

+ +
+
+ + + + diff --git a/dom/workers/test/serviceworkers/test_workerUnregister.html b/dom/workers/test/serviceworkers/test_workerUnregister.html new file mode 100644 index 000000000..947861c17 --- /dev/null +++ b/dom/workers/test/serviceworkers/test_workerUnregister.html @@ -0,0 +1,82 @@ + + + + + Bug 982728 - Test ServiceWorkerGlobalScope.unregister + + + + +
+ + + + + diff --git a/dom/workers/test/serviceworkers/test_workerUpdate.html b/dom/workers/test/serviceworkers/test_workerUpdate.html new file mode 100644 index 000000000..5621d6cb8 --- /dev/null +++ b/dom/workers/test/serviceworkers/test_workerUpdate.html @@ -0,0 +1,62 @@ + + + + + Bug 1065366 - Test ServiceWorkerGlobalScope.update + + + + +
+ + + + + diff --git a/dom/workers/test/serviceworkers/test_workerupdatefoundevent.html b/dom/workers/test/serviceworkers/test_workerupdatefoundevent.html new file mode 100644 index 000000000..3361eba08 --- /dev/null +++ b/dom/workers/test/serviceworkers/test_workerupdatefoundevent.html @@ -0,0 +1,85 @@ + + + + + Bug 1131327 - Test ServiceWorkerRegistration.onupdatefound on ServiceWorker + + + + +

+
+

+
+
+
+
+
diff --git a/dom/workers/test/serviceworkers/test_xslt.html b/dom/workers/test/serviceworkers/test_xslt.html
new file mode 100644
index 000000000..44270753b
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_xslt.html
@@ -0,0 +1,128 @@
+
+
+
+
+  Bug 1182113 - Test service worker XSLT interception
+  
+  
+
+
+

+
+

+
+
+
+
+
diff --git a/dom/workers/test/serviceworkers/thirdparty/iframe1.html b/dom/workers/test/serviceworkers/thirdparty/iframe1.html
new file mode 100644
index 000000000..43fe8c572
--- /dev/null
+++ b/dom/workers/test/serviceworkers/thirdparty/iframe1.html
@@ -0,0 +1,30 @@
+
+
+
+  
+  SW third party iframe test
+
+  
+
+
+
+
+  
+
+
+
diff --git a/dom/workers/test/serviceworkers/thirdparty/iframe2.html b/dom/workers/test/serviceworkers/thirdparty/iframe2.html
new file mode 100644
index 000000000..fac6a9395
--- /dev/null
+++ b/dom/workers/test/serviceworkers/thirdparty/iframe2.html
@@ -0,0 +1,7 @@
+
+
diff --git a/dom/workers/test/serviceworkers/thirdparty/register.html b/dom/workers/test/serviceworkers/thirdparty/register.html
new file mode 100644
index 000000000..59b8c5c41
--- /dev/null
+++ b/dom/workers/test/serviceworkers/thirdparty/register.html
@@ -0,0 +1,27 @@
+
+
diff --git a/dom/workers/test/serviceworkers/thirdparty/sw.js b/dom/workers/test/serviceworkers/thirdparty/sw.js
new file mode 100644
index 000000000..ca45698c8
--- /dev/null
+++ b/dom/workers/test/serviceworkers/thirdparty/sw.js
@@ -0,0 +1,14 @@
+self.addEventListener("fetch", function(event) {
+  dump("fetch " + event.request.url + "\n");
+  if (event.request.url.indexOf("iframe2.html") >= 0) {
+    var body =
+      "";
+    event.respondWith(new Response(body, {
+      headers: {'Content-Type': 'text/html'}
+    }));
+  }
+});
diff --git a/dom/workers/test/serviceworkers/thirdparty/unregister.html b/dom/workers/test/serviceworkers/thirdparty/unregister.html
new file mode 100644
index 000000000..2cb6ee0ce
--- /dev/null
+++ b/dom/workers/test/serviceworkers/thirdparty/unregister.html
@@ -0,0 +1,11 @@
+
+
diff --git a/dom/workers/test/serviceworkers/unregister/index.html b/dom/workers/test/serviceworkers/unregister/index.html
new file mode 100644
index 000000000..db23d2cb7
--- /dev/null
+++ b/dom/workers/test/serviceworkers/unregister/index.html
@@ -0,0 +1,26 @@
+
+
+
+
+  Bug 984048 - Test unregister
+  
+  
+
+
+

+ +

+
+
+
+
diff --git a/dom/workers/test/serviceworkers/unregister/unregister.html b/dom/workers/test/serviceworkers/unregister/unregister.html
new file mode 100644
index 000000000..6fda82026
--- /dev/null
+++ b/dom/workers/test/serviceworkers/unregister/unregister.html
@@ -0,0 +1,22 @@
+
+
+
+
+  Test worker::unregister
+  
+  
+
+
+
+
+
+
+
diff --git a/dom/workers/test/serviceworkers/unresolved_fetch_worker.js b/dom/workers/test/serviceworkers/unresolved_fetch_worker.js
new file mode 100644
index 000000000..71fbad781
--- /dev/null
+++ b/dom/workers/test/serviceworkers/unresolved_fetch_worker.js
@@ -0,0 +1,19 @@
+var keepPromiseAlive;
+onfetch = function(event) {
+  event.waitUntil(
+    clients.matchAll()
+           .then(clients => {
+             clients.forEach(client => {
+               client.postMessage("continue");
+             });
+           })
+  );
+
+  // Never resolve, and keep it alive on our global so it can't get GC'ed and
+  // make this test weird and intermittent.
+  event.respondWith((keepPromiseAlive = new Promise(function(res, rej) {})));
+}
+
+onactivate = function(event) {
+  event.waitUntil(clients.claim());
+}
diff --git a/dom/workers/test/serviceworkers/updatefoundevent.html b/dom/workers/test/serviceworkers/updatefoundevent.html
new file mode 100644
index 000000000..78088c7cd
--- /dev/null
+++ b/dom/workers/test/serviceworkers/updatefoundevent.html
@@ -0,0 +1,13 @@
+
+
+
+  Bug 1131327 - Test ServiceWorkerRegistration.onupdatefound on ServiceWorker
+
+
+
+
diff --git a/dom/workers/test/serviceworkers/worker.js b/dom/workers/test/serviceworkers/worker.js
new file mode 100644
index 000000000..2aba167d1
--- /dev/null
+++ b/dom/workers/test/serviceworkers/worker.js
@@ -0,0 +1 @@
+// empty worker, always succeed!
diff --git a/dom/workers/test/serviceworkers/worker2.js b/dom/workers/test/serviceworkers/worker2.js
new file mode 100644
index 000000000..3072d0817
--- /dev/null
+++ b/dom/workers/test/serviceworkers/worker2.js
@@ -0,0 +1 @@
+// worker2.js
diff --git a/dom/workers/test/serviceworkers/worker3.js b/dom/workers/test/serviceworkers/worker3.js
new file mode 100644
index 000000000..449fc2f97
--- /dev/null
+++ b/dom/workers/test/serviceworkers/worker3.js
@@ -0,0 +1 @@
+// worker3.js
diff --git a/dom/workers/test/serviceworkers/workerUpdate/update.html b/dom/workers/test/serviceworkers/workerUpdate/update.html
new file mode 100644
index 000000000..8f984ccc4
--- /dev/null
+++ b/dom/workers/test/serviceworkers/workerUpdate/update.html
@@ -0,0 +1,24 @@
+
+
+
+
+  Test worker::update
+  
+  
+
+
+
+
+
+
+
diff --git a/dom/workers/test/serviceworkers/worker_unregister.js b/dom/workers/test/serviceworkers/worker_unregister.js
new file mode 100644
index 000000000..7a3e764f4
--- /dev/null
+++ b/dom/workers/test/serviceworkers/worker_unregister.js
@@ -0,0 +1,16 @@
+onmessage = function(e) {
+  clients.matchAll().then(function(c) {
+    if (c.length === 0) {
+      // We cannot proceed.
+      return;
+    }
+
+    registration.unregister().then(function() {
+      c[0].postMessage('DONE');
+    }, function() {
+      c[0].postMessage('ERROR');
+    }).then(function() {
+      c[0].postMessage('FINISH');
+    });
+  });
+}
diff --git a/dom/workers/test/serviceworkers/worker_update.js b/dom/workers/test/serviceworkers/worker_update.js
new file mode 100644
index 000000000..9f3e55b18
--- /dev/null
+++ b/dom/workers/test/serviceworkers/worker_update.js
@@ -0,0 +1,19 @@
+// For now this test only calls update to verify that our registration
+// job queueing works properly when called from the worker thread. We should
+// test actual update scenarios with a SJS test.
+onmessage = function(e) {
+  self.registration.update().then(function(v) {
+    return v === undefined ? 'FINISH' : 'FAIL';
+  }).catch(function(e) {
+    return 'FAIL';
+  }).then(function(result) {
+    clients.matchAll().then(function(c) {
+      if (c.length == 0) {
+        dump("!!!!!!!!!!! WORKER HAS NO CLIENTS TO FINISH TEST !!!!!!!!!!!!\n");
+        return;
+      }
+
+      c[0].postMessage(result);
+    });
+  });
+}
diff --git a/dom/workers/test/serviceworkers/worker_updatefoundevent.js b/dom/workers/test/serviceworkers/worker_updatefoundevent.js
new file mode 100644
index 000000000..a297bf455
--- /dev/null
+++ b/dom/workers/test/serviceworkers/worker_updatefoundevent.js
@@ -0,0 +1,23 @@
+/**
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+onactivate = function(e) {
+  e.waitUntil(new Promise(function(resolve, reject) {
+    registration.onupdatefound = function(e) {
+      clients.matchAll().then(function(clients) {
+        if (!clients.length) {
+          reject("No clients found");
+        }
+
+        if (registration.scope.match(/updatefoundevent\.html$/)) {
+          clients[0].postMessage("finish");
+          resolve();
+        } else {
+          dump("Scope did not match");
+        }
+      }, reject);
+    }
+  }));
+}
diff --git a/dom/workers/test/serviceworkers/worker_updatefoundevent2.js b/dom/workers/test/serviceworkers/worker_updatefoundevent2.js
new file mode 100644
index 000000000..da4c592aa
--- /dev/null
+++ b/dom/workers/test/serviceworkers/worker_updatefoundevent2.js
@@ -0,0 +1 @@
+// Not useful.
diff --git a/dom/workers/test/serviceworkers/xslt/test.xml b/dom/workers/test/serviceworkers/xslt/test.xml
new file mode 100644
index 000000000..83c777633
--- /dev/null
+++ b/dom/workers/test/serviceworkers/xslt/test.xml
@@ -0,0 +1,6 @@
+
+
+
+  Example
+  Error
+
diff --git a/dom/workers/test/serviceworkers/xslt/xslt.sjs b/dom/workers/test/serviceworkers/xslt/xslt.sjs
new file mode 100644
index 000000000..db681ab50
--- /dev/null
+++ b/dom/workers/test/serviceworkers/xslt/xslt.sjs
@@ -0,0 +1,12 @@
+function handleRequest(request, response) {
+  response.setHeader("Content-Type", "application/xslt+xml", false);
+  response.setHeader("Access-Control-Allow-Origin", "*");
+
+  var body = request.queryString;
+  if (!body) {
+    response.setStatusLine(null, 500, "Invalid querystring");
+    return;
+  }
+
+  response.write(unescape(body));
+}
diff --git a/dom/workers/test/serviceworkers/xslt_worker.js b/dom/workers/test/serviceworkers/xslt_worker.js
new file mode 100644
index 000000000..bf9bdbc56
--- /dev/null
+++ b/dom/workers/test/serviceworkers/xslt_worker.js
@@ -0,0 +1,52 @@
+var testType = 'synthetic';
+
+var xslt = " " +
+           "" +
+           "  " +
+           "    " +
+           "      " +
+           "    " +
+           "  " +
+           "  " +
+           "";
+
+onfetch = function(event) {
+  if (event.request.url.includes('test.xsl')) {
+    if (testType == 'synthetic') {
+      if (event.request.mode != 'cors') {
+        event.respondWith(Response.error());
+        return;
+      }
+
+      event.respondWith(Promise.resolve(
+        new Response(xslt, { headers: {'Content-Type': 'application/xslt+xml'}})
+      ));
+    }
+    else if (testType == 'cors') {
+      if (event.request.mode != 'cors') {
+        event.respondWith(Response.error());
+        return;
+      }
+
+      var url = "http://example.com/tests/dom/workers/test/serviceworkers/xslt/xslt.sjs?" + escape(xslt);
+      event.respondWith(fetch(url, { mode: 'cors' }));
+    }
+    else if (testType == 'opaque') {
+      if (event.request.mode != 'cors') {
+        event.respondWith(Response.error());
+        return;
+      }
+
+      var url = "http://example.com/tests/dom/workers/test/serviceworkers/xslt/xslt.sjs?" + escape(xslt);
+      event.respondWith(fetch(url, { mode: 'no-cors' }));
+    }
+    else {
+      event.respondWith(Response.error());
+    }
+  }
+};
+
+onmessage = function(event) {
+  testType = event.data;
+};
diff --git a/dom/workers/test/sharedWorker_console.js b/dom/workers/test/sharedWorker_console.js
new file mode 100644
index 000000000..932235eb7
--- /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 000000000..3d9a837bb
--- /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 000000000..64672e6ab
--- /dev/null
+++ b/dom/workers/test/sharedWorker_ports.js
@@ -0,0 +1,24 @@
+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 000000000..9d7ec886f
--- /dev/null
+++ b/dom/workers/test/sharedWorker_privateBrowsing.js
@@ -0,0 +1,5 @@
+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 000000000..5e8e93392
--- /dev/null
+++ b/dom/workers/test/sharedWorker_sharedWorker.js
@@ -0,0 +1,93 @@
+/**
+ * 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);
+  }
+
+  // The expression closures should trigger a warning in debug builds, but NOT
+  // fire error events at us. If we ever actually remove expression closures
+  // (in bug 1083458), we'll need something else to test this case.
+  (function() "Expected console warning: expression closures are deprecated");
+
+  event.ports[0].onmessage = function(event) {
+    if (!(event instanceof MessageEvent)) {
+      throw new Error("'message' event is not a MessageEvent!");
+    }
+    if (!("ports" in event)) {
+      throw new Error("'message' event doesn't have a 'ports' property!");
+    }
+    if (event.ports === null) {
+      throw new Error("'message' event has a null 'ports' property!");
+    }
+    event.target.postMessage(event.data);
+    throw new Error(event.data);
+  };
+};
diff --git a/dom/workers/test/simpleThread_worker.js b/dom/workers/test/simpleThread_worker.js
new file mode 100644
index 000000000..543d8b3dd
--- /dev/null
+++ b/dom/workers/test/simpleThread_worker.js
@@ -0,0 +1,53 @@
+/**
+ * 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, false);
+      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/suspend_iframe.html b/dom/workers/test/suspend_iframe.html
new file mode 100644
index 000000000..86d9d6bc1
--- /dev/null
+++ b/dom/workers/test/suspend_iframe.html
@@ -0,0 +1,47 @@
+
+
+
+
+  
+  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 000000000..43eb24a7a --- /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 000000000..f1a49e032 --- /dev/null +++ b/dom/workers/test/terminate_worker.js @@ -0,0 +1,9 @@ +/** + * 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 000000000..e2e83a35e --- /dev/null +++ b/dom/workers/test/test_404.html @@ -0,0 +1,41 @@ + + + + + + Test for DOM Worker Threads + + + + +
+
+
+ + + diff --git a/dom/workers/test/test_WorkerDebugger.initialize.xul b/dom/workers/test/test_WorkerDebugger.initialize.xul new file mode 100644 index 000000000..9e40bb78c --- /dev/null +++ b/dom/workers/test/test_WorkerDebugger.initialize.xul @@ -0,0 +1,58 @@ + + + + + + + +

+ +

+  
+  
diff --git a/dom/workers/test/test_WorkerDebugger.postMessage.xul b/dom/workers/test/test_WorkerDebugger.postMessage.xul new file mode 100644 index 000000000..7affbed21 --- /dev/null +++ b/dom/workers/test/test_WorkerDebugger.postMessage.xul @@ -0,0 +1,61 @@ + + + + + + + +

+ +

+  
+  
diff --git a/dom/workers/test/test_WorkerDebugger.xul b/dom/workers/test/test_WorkerDebugger.xul new file mode 100644 index 000000000..f3397bd54 --- /dev/null +++ b/dom/workers/test/test_WorkerDebugger.xul @@ -0,0 +1,122 @@ + + + + + + + +

+ +

+  
+  
diff --git a/dom/workers/test/test_WorkerDebuggerGlobalScope.createSandbox.xul b/dom/workers/test/test_WorkerDebuggerGlobalScope.createSandbox.xul new file mode 100644 index 000000000..0440c482b --- /dev/null +++ b/dom/workers/test/test_WorkerDebuggerGlobalScope.createSandbox.xul @@ -0,0 +1,52 @@ + + + + + + + +

+ +

+  
+  
+ diff --git a/dom/workers/test/test_WorkerDebuggerGlobalScope.enterEventLoop.xul b/dom/workers/test/test_WorkerDebuggerGlobalScope.enterEventLoop.xul new file mode 100644 index 000000000..081395308 --- /dev/null +++ b/dom/workers/test/test_WorkerDebuggerGlobalScope.enterEventLoop.xul @@ -0,0 +1,126 @@ + + + + + + + +

+ +

+  
+  
diff --git a/dom/workers/test/test_WorkerDebuggerGlobalScope.reportError.xul b/dom/workers/test/test_WorkerDebuggerGlobalScope.reportError.xul new file mode 100644 index 000000000..88b078674 --- /dev/null +++ b/dom/workers/test/test_WorkerDebuggerGlobalScope.reportError.xul @@ -0,0 +1,98 @@ + + + + + + + +

+ +

+  
+  
+ diff --git a/dom/workers/test/test_WorkerDebuggerGlobalScope.setImmediate.xul b/dom/workers/test/test_WorkerDebuggerGlobalScope.setImmediate.xul new file mode 100644 index 000000000..3ecac681b --- /dev/null +++ b/dom/workers/test/test_WorkerDebuggerGlobalScope.setImmediate.xul @@ -0,0 +1,54 @@ + + + + + + + +

+ +

+  
+  
diff --git a/dom/workers/test/test_WorkerDebuggerManager.xul b/dom/workers/test/test_WorkerDebuggerManager.xul new file mode 100644 index 000000000..6807226bd --- /dev/null +++ b/dom/workers/test/test_WorkerDebuggerManager.xul @@ -0,0 +1,106 @@ + + + + + + + +

+ +

+  
+  
diff --git a/dom/workers/test/test_WorkerDebugger_console.xul b/dom/workers/test/test_WorkerDebugger_console.xul new file mode 100644 index 000000000..0852002ea --- /dev/null +++ b/dom/workers/test/test_WorkerDebugger_console.xul @@ -0,0 +1,97 @@ + + + + + + + +

+ +

+  
+  
diff --git a/dom/workers/test/test_WorkerDebugger_frozen.xul b/dom/workers/test/test_WorkerDebugger_frozen.xul new file mode 100644 index 000000000..6b22e7702 --- /dev/null +++ b/dom/workers/test/test_WorkerDebugger_frozen.xul @@ -0,0 +1,90 @@ + + + + + + + +

+ +

+    
+  
+  
diff --git a/dom/workers/test/test_WorkerDebugger_promise.xul b/dom/workers/test/test_WorkerDebugger_promise.xul new file mode 100644 index 000000000..24ed07133 --- /dev/null +++ b/dom/workers/test/test_WorkerDebugger_promise.xul @@ -0,0 +1,70 @@ + + + + + + + +

+ +

+  
+  
diff --git a/dom/workers/test/test_WorkerDebugger_suspended.xul b/dom/workers/test/test_WorkerDebugger_suspended.xul new file mode 100644 index 000000000..0ed8bb71a --- /dev/null +++ b/dom/workers/test/test_WorkerDebugger_suspended.xul @@ -0,0 +1,75 @@ + + + + + + + +

+ +

+  
+  
diff --git a/dom/workers/test/test_atob.html b/dom/workers/test/test_atob.html new file mode 100644 index 000000000..99419174c --- /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 000000000..e8cfaf19d --- /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 000000000..6e2a83f53 --- /dev/null +++ b/dom/workers/test/test_blobWorkers.html @@ -0,0 +1,32 @@ + + + + + + + + + + + + diff --git a/dom/workers/test/test_bug1002702.html b/dom/workers/test/test_bug1002702.html new file mode 100644 index 000000000..3db6d2580 --- /dev/null +++ b/dom/workers/test/test_bug1002702.html @@ -0,0 +1,27 @@ + + + + + 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 000000000..f746f35e6
--- /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 000000000..59f5a6185 --- /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 000000000..b6db2aeb4 --- /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 000000000..17b9d490f --- /dev/null +++ b/dom/workers/test/test_bug1036484.html @@ -0,0 +1,54 @@ + + + + + + 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 000000000..758bf996e --- /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 000000000..31061a2b1
--- /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.xul b/dom/workers/test/test_bug1062920.xul
new file mode 100644
index 000000000..635c1b9f9
--- /dev/null
+++ b/dom/workers/test/test_bug1062920.xul
@@ -0,0 +1,69 @@
+
+
+
+
+  
+
+  
+    

+ +

+  
+  
diff --git a/dom/workers/test/test_bug1063538.html b/dom/workers/test/test_bug1063538.html new file mode 100644 index 000000000..7c32b8ed3 --- /dev/null +++ b/dom/workers/test/test_bug1063538.html @@ -0,0 +1,49 @@ + + + + + + + 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 000000000..9f83fd007 --- /dev/null +++ b/dom/workers/test/test_bug1104064.html @@ -0,0 +1,28 @@ + + + + + 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 000000000..30ca9b0ae --- /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 000000000..8c54813ef --- /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 000000000..a91902d26 --- /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 000000000..ea396b32e --- /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 000000000..c23587318 --- /dev/null +++ b/dom/workers/test/test_bug1317725.html @@ -0,0 +1,62 @@ + + + + + Test for bug 1317725 + + + + + + + + + + + + + diff --git a/dom/workers/test/test_bug949946.html b/dom/workers/test/test_bug949946.html new file mode 100644 index 000000000..547bdbda4 --- /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 000000000..49e84c659
--- /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 000000000..892b42ef9 --- /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 000000000..644593949
--- /dev/null
+++ b/dom/workers/test/test_chromeWorker.html
@@ -0,0 +1,27 @@
+
+
+
+  Test for DOM Worker Threads
+  
+  
+
+
+
+
+
+ + + diff --git a/dom/workers/test/test_chromeWorker.xul b/dom/workers/test/test_chromeWorker.xul new file mode 100644 index 000000000..35df768be --- /dev/null +++ b/dom/workers/test/test_chromeWorker.xul @@ -0,0 +1,45 @@ + + + + + + + +

+ +

+  
+  
diff --git a/dom/workers/test/test_chromeWorkerJSM.xul b/dom/workers/test/test_chromeWorkerJSM.xul new file mode 100644 index 000000000..01a88bc2a --- /dev/null +++ b/dom/workers/test/test_chromeWorkerJSM.xul @@ -0,0 +1,56 @@ + + + + + + + +

+ +

+  
+  
diff --git a/dom/workers/test/test_clearTimeouts.html b/dom/workers/test/test_clearTimeouts.html new file mode 100644 index 000000000..d9bc6a9f9 --- /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_console.html b/dom/workers/test/test_console.html new file mode 100644 index 000000000..af82a7de0 --- /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 000000000..e765500fa --- /dev/null +++ b/dom/workers/test/test_consoleAndBlobs.html @@ -0,0 +1,43 @@ + + + + + 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 000000000..3886b679d --- /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_consoleSharedWorkers.html b/dom/workers/test/test_consoleSharedWorkers.html new file mode 100644 index 000000000..74e1ec742 --- /dev/null +++ b/dom/workers/test/test_consoleSharedWorkers.html @@ -0,0 +1,56 @@ + + + + + Test for console API in SharedWorker + + + + + + + diff --git a/dom/workers/test/test_contentWorker.html b/dom/workers/test/test_contentWorker.html new file mode 100644 index 000000000..e745ca4a0 --- /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 000000000..a24217f04 --- /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 000000000..1c9321079
--- /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 000000000..dcbcd8c3a
--- /dev/null
+++ b/dom/workers/test/test_csp.js
@@ -0,0 +1,48 @@
+/**
+ * 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, "Error: 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: uri, level: 3 })
+  worker.onmessage = function(event) {
+    is(event.data, "Error: 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 000000000..1ff72424f
--- /dev/null
+++ b/dom/workers/test/test_dataURLWorker.html
@@ -0,0 +1,31 @@
+
+
+
+  
+    
+    
+  
+  
+    
+  
+
+
diff --git a/dom/workers/test/test_errorPropagation.html b/dom/workers/test/test_errorPropagation.html
new file mode 100644
index 000000000..7e1aafe25
--- /dev/null
+++ b/dom/workers/test/test_errorPropagation.html
@@ -0,0 +1,66 @@
+
+
+
+  
+    
+    
+    
+  
+  
+    
+    
+  
+
+
diff --git a/dom/workers/test/test_errorwarning.html b/dom/workers/test/test_errorwarning.html
new file mode 100644
index 000000000..04523c839
--- /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 000000000..b3c3123f0 --- /dev/null +++ b/dom/workers/test/test_eventDispatch.html @@ -0,0 +1,33 @@ + + + + + + + + + + + + diff --git a/dom/workers/test/test_extension.xul b/dom/workers/test/test_extension.xul new file mode 100644 index 000000000..ba68ef03a --- /dev/null +++ b/dom/workers/test/test_extension.xul @@ -0,0 +1,55 @@ + + + + + + + +

+ +

+  
+  
diff --git a/dom/workers/test/test_extensionBootstrap.xul b/dom/workers/test/test_extensionBootstrap.xul new file mode 100644 index 000000000..18bdde1d3 --- /dev/null +++ b/dom/workers/test/test_extensionBootstrap.xul @@ -0,0 +1,66 @@ + + + + + + + +

+ +

+  
+  
diff --git a/dom/workers/test/test_fibonacci.html b/dom/workers/test/test_fibonacci.html new file mode 100644 index 000000000..d93eb12d4 --- /dev/null +++ b/dom/workers/test/test_fibonacci.html @@ -0,0 +1,52 @@ + + + + + + Test for DOM Worker Threads with Fibonacci + + + + +DOM Worker Threads Fibonacci +

+ +
+
+
+ + + diff --git a/dom/workers/test/test_file.xul b/dom/workers/test/test_file.xul new file mode 100644 index 000000000..800e88fbc --- /dev/null +++ b/dom/workers/test/test_file.xul @@ -0,0 +1,97 @@ + + + + + + + diff --git a/dom/workers/test/test_fileBlobPosting.xul b/dom/workers/test/test_fileBlobPosting.xul new file mode 100644 index 000000000..358054598 --- /dev/null +++ b/dom/workers/test/test_fileBlobPosting.xul @@ -0,0 +1,86 @@ + + + + + + + diff --git a/dom/workers/test/test_fileBlobSubWorker.xul b/dom/workers/test/test_fileBlobSubWorker.xul new file mode 100644 index 000000000..6a8dba636 --- /dev/null +++ b/dom/workers/test/test_fileBlobSubWorker.xul @@ -0,0 +1,98 @@ + + + + + + + diff --git a/dom/workers/test/test_filePosting.xul b/dom/workers/test/test_filePosting.xul new file mode 100644 index 000000000..ff8520d7e --- /dev/null +++ b/dom/workers/test/test_filePosting.xul @@ -0,0 +1,86 @@ + + + + + + + diff --git a/dom/workers/test/test_fileReadSlice.xul b/dom/workers/test/test_fileReadSlice.xul new file mode 100644 index 000000000..da2e16719 --- /dev/null +++ b/dom/workers/test/test_fileReadSlice.xul @@ -0,0 +1,94 @@ + + + + + + + diff --git a/dom/workers/test/test_fileReader.html b/dom/workers/test/test_fileReader.html new file mode 100644 index 000000000..26e73bdb6 --- /dev/null +++ b/dom/workers/test/test_fileReader.html @@ -0,0 +1,100 @@ + + + + Test for FileReader in workers + + + + + + + + diff --git a/dom/workers/test/test_fileReaderSync.xul b/dom/workers/test/test_fileReaderSync.xul new file mode 100644 index 000000000..de93c3ed7 --- /dev/null +++ b/dom/workers/test/test_fileReaderSync.xul @@ -0,0 +1,199 @@ + + + + + + + diff --git a/dom/workers/test/test_fileReaderSyncErrors.xul b/dom/workers/test/test_fileReaderSyncErrors.xul new file mode 100644 index 000000000..9e416a603 --- /dev/null +++ b/dom/workers/test/test_fileReaderSyncErrors.xul @@ -0,0 +1,84 @@ + + + + + + + diff --git a/dom/workers/test/test_fileSlice.xul b/dom/workers/test/test_fileSlice.xul new file mode 100644 index 000000000..31531da2e --- /dev/null +++ b/dom/workers/test/test_fileSlice.xul @@ -0,0 +1,106 @@ + + + + + + + diff --git a/dom/workers/test/test_fileSubWorker.xul b/dom/workers/test/test_fileSubWorker.xul new file mode 100644 index 000000000..94b41704a --- /dev/null +++ b/dom/workers/test/test_fileSubWorker.xul @@ -0,0 +1,99 @@ + + + + + + + diff --git a/dom/workers/test/test_importScripts.html b/dom/workers/test/test_importScripts.html new file mode 100644 index 000000000..718409ce3 --- /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 000000000..a3d73c5b5 --- /dev/null +++ b/dom/workers/test/test_importScripts_3rdparty.html @@ -0,0 +1,134 @@ + + + + + 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 000000000..0a7ce005c --- /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 000000000..f73b3b6a7 --- /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 000000000..3a495de09 --- /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_jsversion.html b/dom/workers/test/test_jsversion.html new file mode 100644 index 000000000..495b8a3fa --- /dev/null +++ b/dom/workers/test/test_jsversion.html @@ -0,0 +1,68 @@ + + + + + Test for JSVersion in workers - Bug 487070 + + + + +

+ +
+
+
+ + diff --git a/dom/workers/test/test_loadEncoding.html b/dom/workers/test/test_loadEncoding.html new file mode 100644 index 000000000..47e08f2f5 --- /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 000000000..dc109b796 --- /dev/null +++ b/dom/workers/test/test_loadError.html @@ -0,0 +1,77 @@ + + + + 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 000000000..cbd605307 --- /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 000000000..d23989f8b --- /dev/null +++ b/dom/workers/test/test_longThread.html @@ -0,0 +1,59 @@ + + + + + + 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 000000000..ba028302f --- /dev/null +++ b/dom/workers/test/test_multi_sharedWorker.html @@ -0,0 +1,242 @@ + + + + + Test for SharedWorker + + + + + + + + + diff --git a/dom/workers/test/test_multi_sharedWorker_lifetimes.html b/dom/workers/test/test_multi_sharedWorker_lifetimes.html new file mode 100644 index 000000000..a3f4dc9b5 --- /dev/null +++ b/dom/workers/test/test_multi_sharedWorker_lifetimes.html @@ -0,0 +1,156 @@ + + + + + Test for SharedWorker + + + + + + + + diff --git a/dom/workers/test/test_navigator.html b/dom/workers/test/test_navigator.html new file mode 100644 index 000000000..49d320967 --- /dev/null +++ b/dom/workers/test/test_navigator.html @@ -0,0 +1,68 @@ + + + + + + Test for DOM Worker Navigator + + + + +

+ +
+
+
+ + diff --git a/dom/workers/test/test_navigator_languages.html b/dom/workers/test/test_navigator_languages.html new file mode 100644 index 000000000..2111739d9 --- /dev/null +++ b/dom/workers/test/test_navigator_languages.html @@ -0,0 +1,53 @@ + + + + + + Test for DOM Worker Navigator.languages + + + + +

+ +
+
+
+ + 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 000000000..6fbbc75a9 --- /dev/null +++ b/dom/workers/test/test_navigator_workers_hardwareConcurrency.html @@ -0,0 +1,30 @@ + + + + + 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 000000000..51aeae6b0 --- /dev/null +++ b/dom/workers/test/test_newError.html @@ -0,0 +1,34 @@ + + + + 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 000000000..409d9dfd1 --- /dev/null +++ b/dom/workers/test/test_notification.html @@ -0,0 +1,50 @@ + + + + + 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 000000000..74a1e8e09 --- /dev/null +++ b/dom/workers/test/test_notification_child.html @@ -0,0 +1,49 @@ + + + + + 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 000000000..e51e060d3 --- /dev/null +++ b/dom/workers/test/test_notification_permission.html @@ -0,0 +1,51 @@ + + + + + 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 000000000..660835f82 --- /dev/null +++ b/dom/workers/test/test_onLine.html @@ -0,0 +1,64 @@ + + + + + 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 000000000..63f359f9d --- /dev/null +++ b/dom/workers/test/test_promise.html @@ -0,0 +1,44 @@ + + + + + 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 000000000..28caaaaa8
--- /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_recursion.html b/dom/workers/test/test_recursion.html new file mode 100644 index 000000000..888a607c4 --- /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 000000000..06471d978 --- /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 000000000..c3afec78e --- /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 000000000..d04c8b591
--- /dev/null
+++ b/dom/workers/test/test_referrer_header_worker.html
@@ -0,0 +1,39 @@
+
+
+
+
+  
+  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 000000000..b6733e010
--- /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 000000000..8e2ea5445
--- /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 000000000..eba858928
--- /dev/null
+++ b/dom/workers/test/test_rvals.html
@@ -0,0 +1,37 @@
+
+
+
+
+  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 000000000..4c0dacafb
--- /dev/null
+++ b/dom/workers/test/test_setTimeoutWith0.html
@@ -0,0 +1,20 @@
+
+
+  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 000000000..3d3d4e2c6
--- /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 000000000..169746892 --- /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 000000000..32698ab52 --- /dev/null +++ b/dom/workers/test/test_sharedWorker_ports.html @@ -0,0 +1,42 @@ + + + + + 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 000000000..b0eac5831 --- /dev/null +++ b/dom/workers/test/test_sharedWorker_privateBrowsing.html @@ -0,0 +1,101 @@ + + + Test for SharedWorker - Private Browsing + + + + + + + + diff --git a/dom/workers/test/test_simpleThread.html b/dom/workers/test/test_simpleThread.html new file mode 100644 index 000000000..0dad90312 --- /dev/null +++ b/dom/workers/test/test_simpleThread.html @@ -0,0 +1,75 @@ + + + + + + Test for DOM Worker Threads (Bug 437152) + + + + +DOM Worker Threads Bug 437152 +

+ +
+
+
+ + + diff --git a/dom/workers/test/test_subworkers_suspended.html b/dom/workers/test/test_subworkers_suspended.html new file mode 100644 index 000000000..063ccaa64 --- /dev/null +++ b/dom/workers/test/test_subworkers_suspended.html @@ -0,0 +1,135 @@ + + + + + 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 000000000..806b97f6c --- /dev/null +++ b/dom/workers/test/test_suspend.html @@ -0,0 +1,138 @@ + + + + + + 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 000000000..5d31bd165 --- /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 000000000..034a443e7 --- /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 000000000..0fa86935a --- /dev/null +++ b/dom/workers/test/test_threadTimeouts.html @@ -0,0 +1,61 @@ + + + + + + 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 000000000..7676541f7 --- /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 000000000..6770d36a1 --- /dev/null +++ b/dom/workers/test/test_timeoutTracing.html @@ -0,0 +1,48 @@ + + + + + + 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 000000000..a3deec12a --- /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_webSocket_sharedWorker.html b/dom/workers/test/test_webSocket_sharedWorker.html new file mode 100644 index 000000000..7609a164b --- /dev/null +++ b/dom/workers/test/test_webSocket_sharedWorker.html @@ -0,0 +1,30 @@ + + + + + Test for bug 1090183 + + + + + + + + + diff --git a/dom/workers/test/test_websocket1.html b/dom/workers/test/test_websocket1.html new file mode 100644 index 000000000..6c74af03c --- /dev/null +++ b/dom/workers/test/test_websocket1.html @@ -0,0 +1,46 @@ + + + + + Test for WebSocket object in workers + + + + +

+

+
+
+
+
diff --git a/dom/workers/test/test_websocket2.html b/dom/workers/test/test_websocket2.html
new file mode 100644
index 000000000..d774be5a2
--- /dev/null
+++ b/dom/workers/test/test_websocket2.html
@@ -0,0 +1,46 @@
+
+
+
+
+  Test for WebSocket object in workers
+  
+  
+
+
+

+

+
+
+
+
diff --git a/dom/workers/test/test_websocket3.html b/dom/workers/test/test_websocket3.html
new file mode 100644
index 000000000..0882cf313
--- /dev/null
+++ b/dom/workers/test/test_websocket3.html
@@ -0,0 +1,46 @@
+
+
+
+
+  Test for WebSocket object in workers
+  
+  
+
+
+

+

+
+
+
+
diff --git a/dom/workers/test/test_websocket4.html b/dom/workers/test/test_websocket4.html
new file mode 100644
index 000000000..8c6bef506
--- /dev/null
+++ b/dom/workers/test/test_websocket4.html
@@ -0,0 +1,46 @@
+
+
+
+
+  Test for WebSocket object in workers
+  
+  
+
+
+

+

+
+
+
+
diff --git a/dom/workers/test/test_websocket5.html b/dom/workers/test/test_websocket5.html
new file mode 100644
index 000000000..cadae3782
--- /dev/null
+++ b/dom/workers/test/test_websocket5.html
@@ -0,0 +1,46 @@
+
+
+
+
+  Test for WebSocket object in workers
+  
+  
+
+
+

+

+
+
+
+
diff --git a/dom/workers/test/test_websocket_basic.html b/dom/workers/test/test_websocket_basic.html
new file mode 100644
index 000000000..5c9f8bf10
--- /dev/null
+++ b/dom/workers/test/test_websocket_basic.html
@@ -0,0 +1,57 @@
+
+
+
+
+  Test for WebSocket object in workers
+  
+  
+
+
+

+ +

+
+
+
+
diff --git a/dom/workers/test/test_websocket_https.html b/dom/workers/test/test_websocket_https.html
new file mode 100644
index 000000000..aa94141fd
--- /dev/null
+++ b/dom/workers/test/test_websocket_https.html
@@ -0,0 +1,30 @@
+
+
+
+
+  Test that creating insecure websockets from https workers is not possible
+  
+  
+
+
+

+ +
+
+
+ + + diff --git a/dom/workers/test/test_websocket_loadgroup.html b/dom/workers/test/test_websocket_loadgroup.html new file mode 100644 index 000000000..3c65e262b --- /dev/null +++ b/dom/workers/test/test_websocket_loadgroup.html @@ -0,0 +1,61 @@ + + + + + Test for WebSocket object in workers + + + + +

+ +

+
+
+
+
diff --git a/dom/workers/test/test_worker_interfaces.html b/dom/workers/test/test_worker_interfaces.html
new file mode 100644
index 000000000..26af63e51
--- /dev/null
+++ b/dom/workers/test/test_worker_interfaces.html
@@ -0,0 +1,16 @@
+
+
+
+
+  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 000000000..e0647682c
--- /dev/null
+++ b/dom/workers/test/test_worker_interfaces.js
@@ -0,0 +1,291 @@
+// 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",
+//   { 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.
+
+// IMPORTANT: Do not change this list without review from
+//            a JavaScript Engine peer!
+var ecmaGlobals =
+  [
+    "Array",
+    "ArrayBuffer",
+    "Boolean",
+    "DataView",
+    "Date",
+    "Error",
+    "EvalError",
+    "Float32Array",
+    "Float64Array",
+    "Function",
+    "Infinity",
+    "Int16Array",
+    "Int32Array",
+    "Int8Array",
+    "InternalError",
+    {name: "Intl", android: false},
+    "Iterator",
+    "JSON",
+    "Map",
+    "Math",
+    "NaN",
+    "Number",
+    "Object",
+    "Promise",
+    "Proxy",
+    "RangeError",
+    "ReferenceError",
+    "Reflect",
+    "RegExp",
+    "Set",
+    {name: "SharedArrayBuffer", release: false},
+    {name: "SIMD", nightly: true},
+    {name: "Atomics", release: false},
+    "StopIteration",
+    "String",
+    "Symbol",
+    "SyntaxError",
+    {name: "TypedObject", nightly: true},
+    "TypeError",
+    "Uint16Array",
+    "Uint32Array",
+    "Uint8Array",
+    "Uint8ClampedArray",
+    "URIError",
+    "WeakMap",
+    "WeakSet",
+  ];
+// 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!
+var interfaceNamesInGlobalScope =
+  [
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "Blob",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "BroadcastChannel",
+// 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!
+    "Crypto",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "CustomEvent",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "DedicatedWorkerGlobalScope",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "Directory",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "DOMCursor",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "DOMError",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "DOMException",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "DOMRequest",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "DOMStringList",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "Event",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "EventTarget",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "File",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "FileReader",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "FileReaderSync",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "FormData",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "Headers",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "IDBCursor",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "IDBCursorWithValue",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "IDBDatabase",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "IDBFactory",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "IDBIndex",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "IDBKeyRange",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "IDBObjectStore",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "IDBOpenDBRequest",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "IDBRequest",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "IDBTransaction",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "IDBVersionChangeEvent",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "ImageBitmap",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "ImageBitmapRenderingContext",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "ImageData",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "MessageChannel",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "MessageEvent",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "MessagePort",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "Notification",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    { name: "OffscreenCanvas", disabled: true },
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "Performance",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "PerformanceEntry",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "PerformanceMark",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "PerformanceMeasure",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    { name: "PerformanceObserver", nightly: true },
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    { name: "PerformanceObserverEntryList", nightly: true },
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "Request",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "Response",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    {name: "StorageManager", nightly: true},
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "SubtleCrypto",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "TextDecoder",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "TextEncoder",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "XMLHttpRequest",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "XMLHttpRequestEventTarget",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "XMLHttpRequestUpload",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "URL",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "URLSearchParams",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    { name: "WebGLActiveInfo", disabled: true },
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    { name: "WebGLBuffer", disabled: true },
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    { name: "WebGLContextEvent", disabled: true },
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    { name: "WebGLFramebuffer", disabled: true },
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    { name: "WebGLProgram", disabled: true },
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    { name: "WebGLRenderbuffer", disabled: true },
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    { name: "WebGLRenderingContext", disabled: true },
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    { name: "WebGLShader", disabled: true },
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    { name: "WebGLShaderPrecisionFormat", disabled: true },
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    { name: "WebGLTexture", disabled: true },
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    { name: "WebGLUniformLocation", disabled: true },
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "WebSocket",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "Worker",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "WorkerGlobalScope",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "WorkerLocation",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "WorkerNavigator",
+// 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!
+
+function createInterfaceMap(version, userAgent) {
+  var isNightly = version.endsWith("a1");
+  var isRelease = !version.includes("a");
+  var isDesktop = !/Mobile|Tablet/.test(userAgent);
+  var isAndroid = !!navigator.userAgent.includes("Android");
+
+  var interfaceMap = {};
+
+  function addInterfaces(interfaces)
+  {
+    for (var entry of interfaces) {
+      if (typeof(entry) === "string") {
+        interfaceMap[entry] = true;
+      } else {
+        ok(!("pref" in entry), "Bogus pref annotation for " + entry.name);
+        if ((entry.nightly === !isNightly) ||
+            (entry.nightlyAndroid === !(isAndroid && isNightly) && isAndroid) ||
+            (entry.desktop === !isDesktop) ||
+            (entry.android === !isAndroid && !entry.nightlyAndroid) ||
+            (entry.release === !isRelease) ||
+            entry.disabled) {
+          interfaceMap[entry.name] = false;
+        } else {
+          interfaceMap[entry.name] = true;
+        }
+      }
+    }
+  }
+
+  addInterfaces(ecmaGlobals);
+  addInterfaces(interfaceNamesInGlobalScope);
+
+  return interfaceMap;
+}
+
+function runTest(version, userAgent) {
+  var interfaceMap = createInterfaceMap(version, userAgent);
+  for (var name of Object.getOwnPropertyNames(self)) {
+    // An interface name should start with an upper case character.
+    if (!/^[A-Z]/.test(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 on the worker? 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 self === interfaceMap[name],
+       name + " should " + (interfaceMap[name] ? "" : " NOT") + " be defined on the global scope");
+    if (!interfaceMap[name]) {
+      delete interfaceMap[name];
+    }
+  }
+  is(Object.keys(interfaceMap).length, 0,
+     "The following interface(s) are not enumerated: " + Object.keys(interfaceMap).join(", "));
+}
+
+workerTestGetVersion(function(version) {
+  workerTestGetUserAgent(function(userAgent) {
+    runTest(version, userAgent);
+    workerTestDone();
+  });
+});
diff --git a/dom/workers/test/test_workersDisabled.html b/dom/workers/test/test_workersDisabled.html
new file mode 100644
index 000000000..39a7fcc5b
--- /dev/null
+++ b/dom/workers/test/test_workersDisabled.html
@@ -0,0 +1,54 @@
+
+
+
+  
+    
+    
+  
+  
+    
+  
+
+
diff --git a/dom/workers/test/test_workersDisabled.xul b/dom/workers/test/test_workersDisabled.xul
new file mode 100644
index 000000000..1674f819f
--- /dev/null
+++ b/dom/workers/test/test_workersDisabled.xul
@@ -0,0 +1,49 @@
+
+
+
+  
+
+  
+    

+ +

+  
+  
diff --git a/dom/workers/test/threadErrors_worker1.js b/dom/workers/test/threadErrors_worker1.js new file mode 100644 index 000000000..c0ddade82 --- /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 000000000..0462d9668 --- /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 000000000..151ffbe5e --- /dev/null +++ b/dom/workers/test/threadErrors_worker3.js @@ -0,0 +1,9 @@ +/** + * 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 000000000..4518ce017 --- /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 000000000..7aaac03d2 --- /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 000000000..07e01787b --- /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 000000000..d62cc50c5 --- /dev/null +++ b/dom/workers/test/timeoutTracing_worker.js @@ -0,0 +1,13 @@ +/** + * 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 000000000..3caf6121b --- /dev/null +++ b/dom/workers/test/transferable_worker.js @@ -0,0 +1,23 @@ +/** + * 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: 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/webSocket_sharedWorker.js b/dom/workers/test/webSocket_sharedWorker.js new file mode 100644 index 000000000..7c8fa0e12 --- /dev/null +++ b/dom/workers/test/webSocket_sharedWorker.js @@ -0,0 +1,20 @@ +onconnect = function(evt) { + var ws = new WebSocket("ws://mochi.test:8888/tests/dom/base/test/file_websocket_hello"); + + ws.onopen = function(e) { + evt.ports[0].postMessage({type: 'status', status: true, msg: 'OnOpen called' }); + ws.send("data"); + } + + ws.onclose = function(e) {} + + ws.onerror = function(e) { + evt.ports[0].postMessage({type: 'status', status: false, msg: 'onerror called!'}); + } + + ws.onmessage = function(e) { + evt.ports[0].postMessage({type: 'status', status: e.data == 'Hello world!', msg: 'Wrong data'}); + ws.close(); + evt.ports[0].postMessage({type: 'finish' }); + } +} diff --git a/dom/workers/test/websocket_basic_worker.js b/dom/workers/test/websocket_basic_worker.js new file mode 100644 index 000000000..256af569a --- /dev/null +++ b/dom/workers/test/websocket_basic_worker.js @@ -0,0 +1,39 @@ +onmessage = function(event) { + if (event.data != 0) { + var worker = new Worker('websocket_basic_worker.js'); + worker.onmessage = function(event) { + postMessage(event.data); + } + + worker.postMessage(event.data - 1); + return; + } + + status = false; + try { + if ((WebSocket instanceof Object)) { + status = true; + } + } catch(e) { + } + + postMessage({type: 'status', status: status, msg: 'WebSocket object:' + WebSocket}); + + var ws = new WebSocket("ws://mochi.test:8888/tests/dom/base/test/file_websocket_hello"); + ws.onopen = function(e) { + postMessage({type: 'status', status: true, msg: 'OnOpen called' }); + ws.send("data"); + } + + ws.onclose = function(e) {} + + ws.onerror = function(e) { + postMessage({type: 'status', status: false, msg: 'onerror called!'}); + } + + ws.onmessage = function(e) { + postMessage({type: 'status', status: e.data == 'Hello world!', msg: 'Wrong data'}); + ws.close(); + postMessage({type: 'finish' }); + } +} diff --git a/dom/workers/test/websocket_helpers.js b/dom/workers/test/websocket_helpers.js new file mode 100644 index 000000000..c2dc3f969 --- /dev/null +++ b/dom/workers/test/websocket_helpers.js @@ -0,0 +1,19 @@ +function feedback() { + postMessage({type: 'feedback', msg: "executing test: " + (current_test+1) + " of " + tests.length + " tests." }); +} + +function ok(status, msg) { + postMessage({type: 'status', status: !!status, msg: msg}); +} + +function is(a, b, msg) { + ok(a === b, msg); +} + +function isnot(a, b, msg) { + ok(a != b, msg); +} + +function finish() { + postMessage({type: 'finish'}); +} diff --git a/dom/workers/test/websocket_https.html b/dom/workers/test/websocket_https.html new file mode 100644 index 000000000..549147a33 --- /dev/null +++ b/dom/workers/test/websocket_https.html @@ -0,0 +1,14 @@ + + diff --git a/dom/workers/test/websocket_https_worker.js b/dom/workers/test/websocket_https_worker.js new file mode 100644 index 000000000..2592ed6d0 --- /dev/null +++ b/dom/workers/test/websocket_https_worker.js @@ -0,0 +1,9 @@ +onmessage = function() { + var wsCreated = true; + try { + new WebSocket("ws://mochi.test:8888/tests/dom/base/test/file_websocket_hello"); + } catch(e) { + wsCreated = false; + } + postMessage(wsCreated ? "created" : "not created"); +}; diff --git a/dom/workers/test/websocket_loadgroup_worker.js b/dom/workers/test/websocket_loadgroup_worker.js new file mode 100644 index 000000000..21cf248bc --- /dev/null +++ b/dom/workers/test/websocket_loadgroup_worker.js @@ -0,0 +1,24 @@ +onmessage = function(event) { + if (event.data != 0) { + var worker = new Worker('websocket_loadgroup_worker.js'); + worker.onmessage = function(event) { + postMessage(event.data); + } + + worker.postMessage(event.data - 1); + return; + } + + var ws = new WebSocket("ws://mochi.test:8888/tests/dom/base/test/file_websocket_hello"); + ws.onopen = function(e) { + postMessage('opened'); + } + + ws.onclose = function(e) { + postMessage('closed'); + } + + ws.onerror = function(e) { + postMessage('error'); + } +} diff --git a/dom/workers/test/websocket_worker1.js b/dom/workers/test/websocket_worker1.js new file mode 100644 index 000000000..cc2b6e825 --- /dev/null +++ b/dom/workers/test/websocket_worker1.js @@ -0,0 +1,19 @@ +importScripts('../../../dom/base/test/websocket_helpers.js'); +importScripts('../../../dom/base/test/websocket_tests.js'); +importScripts('websocket_helpers.js'); + +var tests = [ + test1, // client tries to connect to a http scheme location; + test2, // assure serialization of the connections; + test3, // client tries to connect to an non-existent ws server; + test4, // client tries to connect using a relative url; + test5, // client uses an invalid protocol value; + test6, // counter and encoding check; + test7, // onmessage event origin property check + test8, // client calls close() and the server sends the close frame (with no + // code or reason) in acknowledgement; + test9, // client closes the connection before the ws connection is established; + test10, // client sends a message before the ws connection is established; +]; + +doTest(); diff --git a/dom/workers/test/websocket_worker2.js b/dom/workers/test/websocket_worker2.js new file mode 100644 index 000000000..9dd4ae685 --- /dev/null +++ b/dom/workers/test/websocket_worker2.js @@ -0,0 +1,19 @@ +importScripts('../../../dom/base/test/websocket_helpers.js'); +importScripts('../../../dom/base/test/websocket_tests.js'); +importScripts('websocket_helpers.js'); + +var tests = [ + test11, // a simple hello echo; + test12, // client sends a message containing unpaired surrogates + test13, //server sends an invalid message; + test14, // server sends the close frame, it doesn't close the tcp connection + // and it keeps sending normal ws messages; + test15, // server closes the tcp connection, but it doesn't send the close + // frame; + test16, // client calls close() and tries to send a message; + test18, // client tries to connect to an http resource; + test19, // server closes the tcp connection before establishing the ws + // connection; +]; + +doTest(); diff --git a/dom/workers/test/websocket_worker3.js b/dom/workers/test/websocket_worker3.js new file mode 100644 index 000000000..d2811294f --- /dev/null +++ b/dom/workers/test/websocket_worker3.js @@ -0,0 +1,17 @@ +importScripts('../../../dom/base/test/websocket_helpers.js'); +importScripts('../../../dom/base/test/websocket_tests.js'); +importScripts('websocket_helpers.js'); + +var tests = [ + test24, // server rejects sub-protocol string + test25, // ctor with valid empty sub-protocol array + test26, // ctor with invalid sub-protocol array containing 1 empty element + test27, // ctor with invalid sub-protocol array containing an empty element in + // list + test28, // ctor using valid 1 element sub-protocol array + test29, // ctor using all valid 5 element sub-protocol array + test30, // ctor using valid 1 element sub-protocol array with element server + // will reject +]; + +doTest(); diff --git a/dom/workers/test/websocket_worker4.js b/dom/workers/test/websocket_worker4.js new file mode 100644 index 000000000..030ee67f5 --- /dev/null +++ b/dom/workers/test/websocket_worker4.js @@ -0,0 +1,19 @@ +importScripts('../../../dom/base/test/websocket_helpers.js'); +importScripts('../../../dom/base/test/websocket_tests.js'); +importScripts('websocket_helpers.js'); + +var tests = [ + test31, // ctor using valid 2 element sub-protocol array with 1 element server + // will reject and one server will accept + test32, // ctor using invalid sub-protocol array that contains duplicate items + test33, // test for sending/receiving custom close code (but no close reason) + test34, // test for receiving custom close code and reason + test35, // test for sending custom close code and reason + test36, // negative test for sending out of range close code + test37, // negative test for too long of a close reason + test38, // ensure extensions attribute is defined + test39, // a basic wss:// connectivity test + test40, // negative test for wss:// with no cert +]; + +doTest(); diff --git a/dom/workers/test/websocket_worker5.js b/dom/workers/test/websocket_worker5.js new file mode 100644 index 000000000..41d6e9be0 --- /dev/null +++ b/dom/workers/test/websocket_worker5.js @@ -0,0 +1,13 @@ +importScripts('../../../dom/base/test/websocket_helpers.js'); +importScripts('../../../dom/base/test/websocket_tests.js'); +importScripts('websocket_helpers.js'); + +var tests = [ + test42, // non-char utf-8 sequences + test43, // Test setting binaryType attribute + test44, // Test sending/receving binary ArrayBuffer + test46, // Test that we don't dispatch incoming msgs once in CLOSING state + test47, // Make sure onerror/onclose aren't called during close() +]; + +doTest(); diff --git a/dom/workers/test/window_suspended.html b/dom/workers/test/window_suspended.html new file mode 100644 index 000000000..b482cbe09 --- /dev/null +++ b/dom/workers/test/window_suspended.html @@ -0,0 +1,5 @@ + diff --git a/dom/workers/test/worker_bug1278777.js b/dom/workers/test/worker_bug1278777.js new file mode 100644 index 000000000..c056d0254 --- /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 000000000..69cc25d23 --- /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_consoleAndBlobs.js b/dom/workers/test/worker_consoleAndBlobs.js new file mode 100644 index 000000000..41e8317ac --- /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 000000000..35eb17985 --- /dev/null +++ b/dom/workers/test/worker_driver.js @@ -0,0 +1,64 @@ +// 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 == 'getVersion') { + var result = SpecialPowers.Cc['@mozilla.org/xre/app-info;1'].getService(SpecialPowers.Ci.nsIXULAppInfo).version; + worker.postMessage({ + type: 'returnVersion', + result: result + }); + + } else if (event.data.type == 'getUserAgent') { + worker.postMessage({ + type: 'returnUserAgent', + result: navigator.userAgent + }); + } else if (event.data.type == 'getOSCPU') { + worker.postMessage({ + type: 'returnOSCPU', + result: navigator.oscpu + }); + } + } + + worker.onerror = function(event) { + ok(false, 'Worker had an error: ' + event.data); + SimpleTest.finish(); + }; + + worker.postMessage({ script: script }); +} diff --git a/dom/workers/test/worker_fileReader.js b/dom/workers/test/worker_fileReader.js new file mode 100644 index 000000000..f4e91b325 --- /dev/null +++ b/dom/workers/test/worker_fileReader.js @@ -0,0 +1,417 @@ +var testRanCounter = 0; +var expectedTestCount = 0; +var testSetupFinished = false; + +function ok(a, msg) { + postMessage({type: 'check', status: !!a, msg: msg }); +} + +function is(a, b, msg) { + ok(a === b, msg); +} + +function finish() { + postMessage({type: 'finish'}); +} + +function convertToUTF16(s) { + res = ""; + for (var i = 0; i < s.length; ++i) { + c = s.charCodeAt(i); + res += String.fromCharCode(c & 255, c >>> 8); + } + return res; +} + +function convertToUTF8(s) { + return unescape(encodeURIComponent(s)); +} + +function convertToDataURL(s) { + return "data:application/octet-stream;base64," + btoa(s); +} + +onmessage = function(message) { + is(FileReader.EMPTY, 0, "correct EMPTY value"); + is(FileReader.LOADING, 1, "correct LOADING value"); + is(FileReader.DONE, 2, "correct DONE value"); + + // List of blobs. + var asciiFile = message.data.blobs.shift(); + var binaryFile = message.data.blobs.shift(); + var nonExistingFile = message.data.blobs.shift(); + var utf8TextFile = message.data.blobs.shift(); + var utf16TextFile = message.data.blobs.shift(); + var emptyFile = message.data.blobs.shift(); + var dataUrlFile0 = message.data.blobs.shift(); + var dataUrlFile1 = message.data.blobs.shift(); + var dataUrlFile2 = message.data.blobs.shift(); + + // List of buffers for testing. + var testTextData = message.data.testTextData; + var testASCIIData = message.data.testASCIIData; + var testBinaryData = message.data.testBinaryData; + var dataurldata0 = message.data.dataurldata0; + var dataurldata1 = message.data.dataurldata1; + var dataurldata2 = message.data.dataurldata2; + + // Test that plain reading works and fires events as expected, both + // for text and binary reading + + var onloadHasRunText = false; + var onloadStartHasRunText = false; + r = new FileReader(); + is(r.readyState, FileReader.EMPTY, "correct initial text readyState"); + r.onload = getLoadHandler(testASCIIData, testASCIIData.length, "plain reading"); + r.addEventListener("load", function() { onloadHasRunText = true }, false); + r.addEventListener("loadstart", function() { onloadStartHasRunText = true }, false); + r.readAsText(asciiFile); + is(r.readyState, FileReader.LOADING, "correct loading text readyState"); + is(onloadHasRunText, false, "text loading must be async"); + is(onloadStartHasRunText, true, "text loadstart should fire sync"); + expectedTestCount++; + + var onloadHasRunBinary = false; + var onloadStartHasRunBinary = false; + r = new FileReader(); + is(r.readyState, FileReader.EMPTY, "correct initial binary readyState"); + r.addEventListener("load", function() { onloadHasRunBinary = true }, false); + r.addEventListener("loadstart", function() { onloadStartHasRunBinary = true }, false); + r.readAsBinaryString(binaryFile); + r.onload = getLoadHandler(testBinaryData, testBinaryData.length, "binary reading"); + is(r.readyState, FileReader.LOADING, "correct loading binary readyState"); + is(onloadHasRunBinary, false, "binary loading must be async"); + is(onloadStartHasRunBinary, true, "binary loadstart should fire sync"); + expectedTestCount++; + + var onloadHasRunArrayBuffer = false; + var onloadStartHasRunArrayBuffer = false; + r = new FileReader(); + is(r.readyState, FileReader.EMPTY, "correct initial arrayBuffer readyState"); + r.addEventListener("load", function() { onloadHasRunArrayBuffer = true }, false); + r.addEventListener("loadstart", function() { onloadStartHasRunArrayBuffer = true }, false); + r.readAsArrayBuffer(binaryFile); + r.onload = getLoadHandlerForArrayBuffer(testBinaryData, testBinaryData.length, "array buffer reading"); + is(r.readyState, FileReader.LOADING, "correct loading arrayBuffer readyState"); + is(onloadHasRunArrayBuffer, false, "arrayBuffer loading must be async"); + is(onloadStartHasRunArrayBuffer, true, "arrayBuffer loadstart should fire sync"); + expectedTestCount++; + + // Test a variety of encodings, and make sure they work properly + r = new FileReader(); + r.onload = getLoadHandler(testASCIIData, testASCIIData.length, "no encoding reading"); + r.readAsText(asciiFile, ""); + expectedTestCount++; + + r = new FileReader(); + r.onload = getLoadHandler(testASCIIData, testASCIIData.length, "iso8859 reading"); + r.readAsText(asciiFile, "iso-8859-1"); + expectedTestCount++; + + r = new FileReader(); + r.onload = getLoadHandler(testTextData, + convertToUTF8(testTextData).length, + "utf8 reading"); + r.readAsText(utf8TextFile, "utf8"); + expectedTestCount++; + + r = new FileReader(); + r.readAsText(utf16TextFile, "utf-16"); + r.onload = getLoadHandler(testTextData, + convertToUTF16(testTextData).length, + "utf16 reading"); + expectedTestCount++; + + // Test get result without reading + r = new FileReader(); + is(r.readyState, FileReader.EMPTY, + "readyState in test reader get result without reading"); + is(r.error, null, + "no error in test reader get result without reading"); + is(r.result, null, + "result in test reader get result without reading"); + + // Test loading an empty file works (and doesn't crash!) + r = new FileReader(); + r.onload = getLoadHandler("", 0, "empty no encoding reading"); + r.readAsText(emptyFile, ""); + expectedTestCount++; + + r = new FileReader(); + r.onload = getLoadHandler("", 0, "empty utf8 reading"); + r.readAsText(emptyFile, "utf8"); + expectedTestCount++; + + r = new FileReader(); + r.onload = getLoadHandler("", 0, "empty utf16 reading"); + r.readAsText(emptyFile, "utf-16"); + expectedTestCount++; + + r = new FileReader(); + r.onload = getLoadHandler("", 0, "empty binary string reading"); + r.readAsBinaryString(emptyFile); + expectedTestCount++; + + r = new FileReader(); + r.onload = getLoadHandlerForArrayBuffer("", 0, "empty array buffer reading"); + r.readAsArrayBuffer(emptyFile); + expectedTestCount++; + + r = new FileReader(); + r.onload = getLoadHandler(convertToDataURL(""), 0, "empt binary string reading"); + r.readAsDataURL(emptyFile); + expectedTestCount++; + + // Test reusing a FileReader to read multiple times + r = new FileReader(); + r.onload = getLoadHandler(testASCIIData, + testASCIIData.length, + "to-be-reused reading text") + var makeAnotherReadListener = function(event) { + r = event.target; + r.removeEventListener("load", makeAnotherReadListener, false); + r.onload = getLoadHandler(testASCIIData, + testASCIIData.length, + "reused reading text"); + r.readAsText(asciiFile); + }; + r.addEventListener("load", makeAnotherReadListener, false); + r.readAsText(asciiFile); + expectedTestCount += 2; + + r = new FileReader(); + r.onload = getLoadHandler(testBinaryData, + testBinaryData.length, + "to-be-reused reading binary") + var makeAnotherReadListener2 = function(event) { + r = event.target; + r.removeEventListener("load", makeAnotherReadListener2, false); + r.onload = getLoadHandler(testBinaryData, + testBinaryData.length, + "reused reading binary"); + r.readAsBinaryString(binaryFile); + }; + r.addEventListener("load", makeAnotherReadListener2, false); + r.readAsBinaryString(binaryFile); + expectedTestCount += 2; + + r = new FileReader(); + r.onload = getLoadHandler(convertToDataURL(testBinaryData), + testBinaryData.length, + "to-be-reused reading data url") + var makeAnotherReadListener3 = function(event) { + r = event.target; + r.removeEventListener("load", makeAnotherReadListener3, false); + r.onload = getLoadHandler(convertToDataURL(testBinaryData), + testBinaryData.length, + "reused reading data url"); + r.readAsDataURL(binaryFile); + }; + r.addEventListener("load", makeAnotherReadListener3, false); + r.readAsDataURL(binaryFile); + expectedTestCount += 2; + + r = new FileReader(); + r.onload = getLoadHandlerForArrayBuffer(testBinaryData, + testBinaryData.length, + "to-be-reused reading arrayBuffer") + var makeAnotherReadListener4 = function(event) { + r = event.target; + r.removeEventListener("load", makeAnotherReadListener4, false); + r.onload = getLoadHandlerForArrayBuffer(testBinaryData, + testBinaryData.length, + "reused reading arrayBuffer"); + r.readAsArrayBuffer(binaryFile); + }; + r.addEventListener("load", makeAnotherReadListener4, false); + r.readAsArrayBuffer(binaryFile); + expectedTestCount += 2; + + // Test first reading as ArrayBuffer then read as something else + // (BinaryString) and doesn't crash + r = new FileReader(); + r.onload = getLoadHandlerForArrayBuffer(testBinaryData, + testBinaryData.length, + "to-be-reused reading arrayBuffer") + var makeAnotherReadListener5 = function(event) { + r = event.target; + r.removeEventListener("load", makeAnotherReadListener5, false); + r.onload = getLoadHandler(testBinaryData, + testBinaryData.length, + "reused reading binary string"); + r.readAsBinaryString(binaryFile); + }; + r.addEventListener("load", makeAnotherReadListener5, false); + r.readAsArrayBuffer(binaryFile); + expectedTestCount += 2; + + //Test data-URI encoding on differing file sizes + is(dataurldata0.length % 3, 0, "Want to test data with length % 3 == 0"); + r = new FileReader(); + r.onload = getLoadHandler(convertToDataURL(dataurldata0), + dataurldata0.length, + "dataurl reading, %3 = 0"); + r.readAsDataURL(dataUrlFile0); + expectedTestCount++; + + is(dataurldata1.length % 3, 1, "Want to test data with length % 3 == 1"); + r = new FileReader(); + r.onload = getLoadHandler(convertToDataURL(dataurldata1), + dataurldata1.length, + "dataurl reading, %3 = 1"); + r.readAsDataURL(dataUrlFile1); + expectedTestCount++; + + is(dataurldata2.length % 3, 2, "Want to test data with length % 3 == 2"); + r = new FileReader(); + r.onload = getLoadHandler(convertToDataURL(dataurldata2), + dataurldata2.length, + "dataurl reading, %3 = 2"); + r.readAsDataURL(dataUrlFile2), + expectedTestCount++; + + + // Test abort() + var abortHasRun = false; + var loadEndHasRun = false; + r = new FileReader(); + r.onabort = function (event) { + is(abortHasRun, false, "abort should only fire once"); + is(loadEndHasRun, false, "loadend shouldn't have fired yet"); + abortHasRun = true; + is(event.target.readyState, FileReader.DONE, "should be DONE while firing onabort"); + is(event.target.error.name, "AbortError", "error set to AbortError for aborted reads"); + is(event.target.result, null, "file data should be null on aborted reads"); + } + r.onloadend = function (event) { + is(abortHasRun, true, "abort should fire before loadend"); + is(loadEndHasRun, false, "loadend should only fire once"); + loadEndHasRun = true; + is(event.target.readyState, FileReader.DONE, "should be DONE while firing onabort"); + is(event.target.error.name, "AbortError", "error set to AbortError for aborted reads"); + is(event.target.result, null, "file data should be null on aborted reads"); + } + r.onload = function() { ok(false, "load should not fire for aborted reads") }; + r.onerror = function() { ok(false, "error should not fire for aborted reads") }; + r.onprogress = function() { ok(false, "progress should not fire for aborted reads") }; + var abortThrew = false; + try { + r.abort(); + } catch(e) { + abortThrew = true; + } + is(abortThrew, true, "abort() must throw if not loading"); + is(abortHasRun, false, "abort() is a no-op unless loading"); + r.readAsText(asciiFile); + r.abort(); + is(abortHasRun, true, "abort should fire sync"); + is(loadEndHasRun, true, "loadend should fire sync"); + + // Test calling readAsX to cause abort() + var reuseAbortHasRun = false; + r = new FileReader(); + r.onabort = function (event) { + is(reuseAbortHasRun, false, "abort should only fire once"); + reuseAbortHasRun = true; + is(event.target.readyState, FileReader.DONE, "should be DONE while firing onabort"); + is(event.target.error.name, "AbortError", "error set to AbortError for aborted reads"); + is(event.target.result, null, "file data should be null on aborted reads"); + } + r.onload = function() { ok(false, "load should not fire for aborted reads") }; + var abortThrew = false; + try { + r.abort(); + } catch(e) { + abortThrew = true; + } + is(abortThrew, true, "abort() must throw if not loading"); + is(reuseAbortHasRun, false, "abort() is a no-op unless loading"); + r.readAsText(asciiFile); + r.readAsText(asciiFile); + is(reuseAbortHasRun, true, "abort should fire sync"); + r.onload = getLoadHandler(testASCIIData, testASCIIData.length, "reuse-as-abort reading"); + expectedTestCount++; + + + // Test reading from nonexistent files + r = new FileReader(); + var didThrow = false; + r.onerror = function (event) { + is(event.target.readyState, FileReader.DONE, "should be DONE while firing onerror"); + is(event.target.error.name, "NotFoundError", "error set to NotFoundError for nonexistent files"); + is(event.target.result, null, "file data should be null on aborted reads"); + testHasRun(); + }; + r.onload = function (event) { + is(false, "nonexistent file shouldn't load! (FIXME: bug 1122788)"); + testHasRun(); + }; + try { + r.readAsDataURL(nonExistingFile); + expectedTestCount++; + } catch(ex) { + didThrow = true; + } + // Once this test passes, we should test that onerror gets called and + // that the FileReader object is in the right state during that call. + is(didThrow, false, "shouldn't throw when opening nonexistent file, should fire error instead"); + + + function getLoadHandler(expectedResult, expectedLength, testName) { + return function (event) { + is(event.target.readyState, FileReader.DONE, + "readyState in test " + testName); + is(event.target.error, null, + "no error in test " + testName); + is(event.target.result, expectedResult, + "result in test " + testName); + is(event.lengthComputable, true, + "lengthComputable in test " + testName); + is(event.loaded, expectedLength, + "loaded in test " + testName); + is(event.total, expectedLength, + "total in test " + testName); + testHasRun(); + } + } + + function getLoadHandlerForArrayBuffer(expectedResult, expectedLength, testName) { + return function (event) { + is(event.target.readyState, FileReader.DONE, + "readyState in test " + testName); + is(event.target.error, null, + "no error in test " + testName); + is(event.lengthComputable, true, + "lengthComputable in test " + testName); + is(event.loaded, expectedLength, + "loaded in test " + testName); + is(event.total, expectedLength, + "total in test " + testName); + is(event.target.result.byteLength, expectedLength, + "array buffer size in test " + testName); + var u8v = new Uint8Array(event.target.result); + is(String.fromCharCode.apply(String, u8v), expectedResult, + "array buffer contents in test " + testName); + u8v = null; + is(event.target.result.byteLength, expectedLength, + "array buffer size after gc in test " + testName); + u8v = new Uint8Array(event.target.result); + is(String.fromCharCode.apply(String, u8v), expectedResult, + "array buffer contents after gc in test " + testName); + testHasRun(); + } + } + + function testHasRun() { + //alert(testRanCounter); + ++testRanCounter; + if (testRanCounter == expectedTestCount) { + is(testSetupFinished, true, "test setup should have finished; check for exceptions"); + is(onloadHasRunText, true, "onload text should have fired by now"); + is(onloadHasRunBinary, true, "onload binary should have fired by now"); + finish(); + } + } + + testSetupFinished = true; +} diff --git a/dom/workers/test/worker_referrer.js b/dom/workers/test/worker_referrer.js new file mode 100644 index 000000000..227a77caf --- /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 000000000..2d8e5e6c2 --- /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_suspended.js b/dom/workers/test/worker_suspended.js new file mode 100644 index 000000000..3b53fcea2 --- /dev/null +++ b/dom/workers/test/worker_suspended.js @@ -0,0 +1,31 @@ +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 000000000..c385803c4 --- /dev/null +++ b/dom/workers/test/worker_wrapper.js @@ -0,0 +1,99 @@ +// 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: permissions + }); +} + +function workerTestGetVersion(cb) { + addEventListener('message', function workerTestGetVersionCB(e) { + if (e.data.type !== 'returnVersion') { + return; + } + removeEventListener('message', workerTestGetVersionCB); + cb(e.data.result); + }); + postMessage({ + type: 'getVersion' + }); +} + +function workerTestGetUserAgent(cb) { + addEventListener('message', function workerTestGetUserAgentCB(e) { + if (e.data.type !== 'returnUserAgent') { + return; + } + removeEventListener('message', workerTestGetUserAgentCB); + cb(e.data.result); + }); + postMessage({ + type: 'getUserAgent' + }); +} + +function workerTestGetOSCPU(cb) { + addEventListener('message', function workerTestGetOSCPUCB(e) { + if (e.data.type !== 'returnOSCPU') { + return; + } + removeEventListener('message', workerTestGetOSCPUCB); + cb(e.data.result); + }); + postMessage({ + type: 'getOSCPU' + }); +} + +addEventListener('message', function workerWrapperOnMessage(e) { + removeEventListener('message', workerWrapperOnMessage); + var data = e.data; + try { + importScripts(data.script); + } catch(e) { + postMessage({ + type: 'status', + status: false, + msg: 'worker failed to import ' + data.script + "; error: " + e.message + }); + } +}); diff --git a/dom/workers/test/workersDisabled_worker.js b/dom/workers/test/workersDisabled_worker.js new file mode 100644 index 000000000..7346fc142 --- /dev/null +++ b/dom/workers/test/workersDisabled_worker.js @@ -0,0 +1,7 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ +onmessage = function(event) { + postMessage(event.data); +} diff --git a/dom/workers/test/xpcshell/data/chrome.manifest b/dom/workers/test/xpcshell/data/chrome.manifest new file mode 100644 index 000000000..611e81fd4 --- /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 000000000..9a83bb128 --- /dev/null +++ b/dom/workers/test/xpcshell/data/worker.js @@ -0,0 +1,7 @@ +/* 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 000000000..b27366d17 --- /dev/null +++ b/dom/workers/test/xpcshell/data/worker_fileReader.js @@ -0,0 +1,8 @@ +/* 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_fileReader.js b/dom/workers/test/xpcshell/test_fileReader.js new file mode 100644 index 000000000..0e283fbe0 --- /dev/null +++ b/dom/workers/test/xpcshell/test_fileReader.js @@ -0,0 +1,40 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +Components.utils.import("resource://gre/modules/Promise.jsm"); + +// 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 run_test() { + run_next_test(); +} + +function talk_with_worker(worker) { + let deferred = Promise.defer(); + worker.onmessage = function(event) { + let success = true; + if (event.data == "OK") { + deferred.resolve(); + } else { + success = false; + deferred.reject(event); + } + do_check_true(success); + worker.terminate(); + }; + worker.onerror = function(event) { + let error = new Error(event.message, event.filename, event.lineno); + worker.terminate(); + deferred.reject(error); + }; + worker.postMessage("START"); + return deferred.promise; +} + + +add_task(function test_chrome_worker() { + return talk_with_worker(new ChromeWorker(WORKER_SOURCE_URI)); +}); diff --git a/dom/workers/test/xpcshell/test_workers.js b/dom/workers/test/xpcshell/test_workers.js new file mode 100644 index 000000000..c06e62af3 --- /dev/null +++ b/dom/workers/test/xpcshell/test_workers.js @@ -0,0 +1,44 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +Components.utils.import("resource://gre/modules/Promise.jsm"); + +// 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 run_test() { + run_next_test(); +} + +function talk_with_worker(worker) { + let deferred = Promise.defer(); + worker.onmessage = function(event) { + let success = true; + if (event.data == "OK") { + deferred.resolve(); + } else { + success = false; + deferred.reject(event); + } + do_check_true(success); + worker.terminate(); + }; + worker.onerror = function(event) { + let error = new Error(event.message, event.filename, event.lineno); + worker.terminate(); + deferred.reject(error); + }; + worker.postMessage("START"); + return deferred.promise; +} + + +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 000000000..917b842f5 --- /dev/null +++ b/dom/workers/test/xpcshell/xpcshell.ini @@ -0,0 +1,11 @@ +[DEFAULT] +head = +tail = +skip-if = toolkit == 'android' +support-files = + data/worker.js + data/worker_fileReader.js + data/chrome.manifest + +[test_workers.js] +[test_fileReader.js] -- cgit v1.2.3