summaryrefslogtreecommitdiffstats
path: root/dom/workers/test
diff options
context:
space:
mode:
Diffstat (limited to 'dom/workers/test')
-rw-r--r--dom/workers/test/404_server.sjs10
-rw-r--r--dom/workers/test/WorkerDebugger.console_childWorker.js3
-rw-r--r--dom/workers/test/WorkerDebugger.console_debugger.js41
-rw-r--r--dom/workers/test/WorkerDebugger.console_worker.js10
-rw-r--r--dom/workers/test/WorkerDebugger.initialize_childWorker.js6
-rw-r--r--dom/workers/test/WorkerDebugger.initialize_debugger.js6
-rw-r--r--dom/workers/test/WorkerDebugger.initialize_worker.js9
-rw-r--r--dom/workers/test/WorkerDebugger.postMessage_childWorker.js3
-rw-r--r--dom/workers/test/WorkerDebugger.postMessage_debugger.js9
-rw-r--r--dom/workers/test/WorkerDebugger.postMessage_worker.js3
-rw-r--r--dom/workers/test/WorkerDebuggerGlobalScope.createSandbox_debugger.js9
-rw-r--r--dom/workers/test/WorkerDebuggerGlobalScope.createSandbox_sandbox.js9
-rw-r--r--dom/workers/test/WorkerDebuggerGlobalScope.createSandbox_worker.js3
-rw-r--r--dom/workers/test/WorkerDebuggerGlobalScope.enterEventLoop_childWorker.js14
-rw-r--r--dom/workers/test/WorkerDebuggerGlobalScope.enterEventLoop_debugger.js29
-rw-r--r--dom/workers/test/WorkerDebuggerGlobalScope.enterEventLoop_worker.js25
-rw-r--r--dom/workers/test/WorkerDebuggerGlobalScope.reportError_childWorker.js5
-rw-r--r--dom/workers/test/WorkerDebuggerGlobalScope.reportError_debugger.js12
-rw-r--r--dom/workers/test/WorkerDebuggerGlobalScope.reportError_worker.js11
-rw-r--r--dom/workers/test/WorkerDebuggerGlobalScope.setImmediate_debugger.js12
-rw-r--r--dom/workers/test/WorkerDebuggerGlobalScope.setImmediate_worker.js3
-rw-r--r--dom/workers/test/WorkerDebuggerManager_childWorker.js3
-rw-r--r--dom/workers/test/WorkerDebuggerManager_worker.js3
-rw-r--r--dom/workers/test/WorkerDebugger_childWorker.js3
-rw-r--r--dom/workers/test/WorkerDebugger_frozen_iframe1.html15
-rw-r--r--dom/workers/test/WorkerDebugger_frozen_iframe2.html15
-rw-r--r--dom/workers/test/WorkerDebugger_frozen_worker1.js5
-rw-r--r--dom/workers/test/WorkerDebugger_frozen_worker2.js5
-rw-r--r--dom/workers/test/WorkerDebugger_promise_debugger.js30
-rw-r--r--dom/workers/test/WorkerDebugger_promise_worker.js25
-rw-r--r--dom/workers/test/WorkerDebugger_sharedWorker.js11
-rw-r--r--dom/workers/test/WorkerDebugger_suspended_debugger.js6
-rw-r--r--dom/workers/test/WorkerDebugger_suspended_worker.js6
-rw-r--r--dom/workers/test/WorkerDebugger_worker.js8
-rw-r--r--dom/workers/test/WorkerTest.jsm17
-rw-r--r--dom/workers/test/WorkerTest_badworker.js7
-rw-r--r--dom/workers/test/WorkerTest_subworker.js43
-rw-r--r--dom/workers/test/WorkerTest_worker.js11
-rw-r--r--dom/workers/test/atob_worker.js46
-rw-r--r--dom/workers/test/browser.ini12
-rw-r--r--dom/workers/test/browser_bug1047663.js50
-rw-r--r--dom/workers/test/browser_bug1104623.js53
-rw-r--r--dom/workers/test/bug1014466_data1.txt1
-rw-r--r--dom/workers/test/bug1014466_data2.txt1
-rw-r--r--dom/workers/test/bug1014466_worker.js64
-rw-r--r--dom/workers/test/bug1020226_frame.html20
-rw-r--r--dom/workers/test/bug1020226_worker.js12
-rw-r--r--dom/workers/test/bug1047663_tab.html8
-rw-r--r--dom/workers/test/bug1047663_worker.sjs40
-rw-r--r--dom/workers/test/bug1060621_worker.js2
-rw-r--r--dom/workers/test/bug1062920_worker.js6
-rw-r--r--dom/workers/test/bug1063538_worker.js25
-rw-r--r--dom/workers/test/bug1104064_worker.js10
-rw-r--r--dom/workers/test/bug1132395_sharedWorker.js12
-rw-r--r--dom/workers/test/bug1132924_worker.js10
-rw-r--r--dom/workers/test/bug978260_worker.js7
-rw-r--r--dom/workers/test/bug998474_worker.js6
-rw-r--r--dom/workers/test/chrome.ini86
-rw-r--r--dom/workers/test/chromeWorker_subworker.js7
-rw-r--r--dom/workers/test/chromeWorker_worker.js20
-rw-r--r--dom/workers/test/clearTimeouts_worker.js12
-rw-r--r--dom/workers/test/consoleReplaceable_worker.js16
-rw-r--r--dom/workers/test/console_worker.js109
-rw-r--r--dom/workers/test/content_worker.js12
-rw-r--r--dom/workers/test/crashtests/1153636.html5
-rw-r--r--dom/workers/test/crashtests/1158031.html11
-rw-r--r--dom/workers/test/crashtests/1228456.html14
-rw-r--r--dom/workers/test/crashtests/779707.html19
-rw-r--r--dom/workers/test/crashtests/943516.html10
-rw-r--r--dom/workers/test/crashtests/crashtests.list5
-rw-r--r--dom/workers/test/csp_worker.js28
-rw-r--r--dom/workers/test/csp_worker.js^headers^1
-rw-r--r--dom/workers/test/dom_worker_helper.js176
-rw-r--r--dom/workers/test/empty.html0
-rw-r--r--dom/workers/test/errorPropagation_iframe.html55
-rw-r--r--dom/workers/test/errorPropagation_worker.js50
-rw-r--r--dom/workers/test/errorwarning_worker.js42
-rw-r--r--dom/workers/test/eventDispatch_worker.js67
-rw-r--r--dom/workers/test/extensions/bootstrap/bootstrap.js141
-rw-r--r--dom/workers/test/extensions/bootstrap/install.rdf31
-rw-r--r--dom/workers/test/extensions/bootstrap/jar.mn3
-rw-r--r--dom/workers/test/extensions/bootstrap/moz.build20
-rw-r--r--dom/workers/test/extensions/bootstrap/worker.js7
-rw-r--r--dom/workers/test/extensions/bootstrap/workerbootstrap-test@mozilla.org.xpibin0 -> 7202 bytes
-rw-r--r--dom/workers/test/extensions/moz.build7
-rw-r--r--dom/workers/test/extensions/traditional/WorkerTest.js122
-rw-r--r--dom/workers/test/extensions/traditional/WorkerTest.manifest3
-rw-r--r--dom/workers/test/extensions/traditional/install.rdf30
-rw-r--r--dom/workers/test/extensions/traditional/jar.mn3
-rw-r--r--dom/workers/test/extensions/traditional/moz.build30
-rw-r--r--dom/workers/test/extensions/traditional/nsIWorkerTest.idl23
-rw-r--r--dom/workers/test/extensions/traditional/worker-test@mozilla.org.xpibin0 -> 8333 bytes
-rw-r--r--dom/workers/test/extensions/traditional/worker.js7
-rw-r--r--dom/workers/test/fibonacci_worker.js24
-rw-r--r--dom/workers/test/fileBlobSubWorker_worker.js17
-rw-r--r--dom/workers/test/fileBlob_worker.js13
-rw-r--r--dom/workers/test/filePosting_worker.js3
-rw-r--r--dom/workers/test/fileReadSlice_worker.js16
-rw-r--r--dom/workers/test/fileReaderSyncErrors_worker.js74
-rw-r--r--dom/workers/test/fileReaderSync_worker.js25
-rw-r--r--dom/workers/test/fileSlice_worker.js27
-rw-r--r--dom/workers/test/fileSubWorker_worker.js17
-rw-r--r--dom/workers/test/file_bug1010784_worker.js9
-rw-r--r--dom/workers/test/file_worker.js16
-rw-r--r--dom/workers/test/fileapi_chromeScript.js29
-rw-r--r--dom/workers/test/foreign.js1
-rw-r--r--dom/workers/test/frame_script.js72
-rw-r--r--dom/workers/test/gtest/TestReadWrite.cpp499
-rw-r--r--dom/workers/test/gtest/moz.build13
-rw-r--r--dom/workers/test/head.js91
-rw-r--r--dom/workers/test/importForeignScripts_worker.js55
-rw-r--r--dom/workers/test/importScripts_3rdParty_worker.js82
-rw-r--r--dom/workers/test/importScripts_mixedcontent.html46
-rw-r--r--dom/workers/test/importScripts_worker.js64
-rw-r--r--dom/workers/test/importScripts_worker_imported1.js10
-rw-r--r--dom/workers/test/importScripts_worker_imported2.js10
-rw-r--r--dom/workers/test/importScripts_worker_imported3.js6
-rw-r--r--dom/workers/test/importScripts_worker_imported4.js6
-rw-r--r--dom/workers/test/instanceof_worker.js12
-rw-r--r--dom/workers/test/json_worker.js338
-rw-r--r--dom/workers/test/jsversion_worker.js14
-rw-r--r--dom/workers/test/loadEncoding_worker.js7
-rw-r--r--dom/workers/test/location_worker.js11
-rw-r--r--dom/workers/test/longThread_worker.js14
-rw-r--r--dom/workers/test/mochitest.ini227
-rw-r--r--dom/workers/test/multi_sharedWorker_frame.html52
-rw-r--r--dom/workers/test/multi_sharedWorker_sharedWorker.js72
-rw-r--r--dom/workers/test/navigator_languages_worker.js11
-rw-r--r--dom/workers/test/navigator_worker.js79
-rw-r--r--dom/workers/test/newError_worker.js5
-rw-r--r--dom/workers/test/notification_permission_worker.js56
-rw-r--r--dom/workers/test/notification_worker.js93
-rw-r--r--dom/workers/test/notification_worker_child-child.js92
-rw-r--r--dom/workers/test/notification_worker_child-parent.js26
-rw-r--r--dom/workers/test/onLine_worker.js65
-rw-r--r--dom/workers/test/onLine_worker_child.js75
-rw-r--r--dom/workers/test/onLine_worker_head.js43
-rw-r--r--dom/workers/test/promise_worker.js856
-rw-r--r--dom/workers/test/recursion_worker.js46
-rw-r--r--dom/workers/test/recursiveOnerror_worker.js11
-rw-r--r--dom/workers/test/redirect_to_foreign.sjs4
-rw-r--r--dom/workers/test/referrer.sjs15
-rw-r--r--dom/workers/test/referrer_test_server.sjs101
-rw-r--r--dom/workers/test/referrer_worker.html145
-rw-r--r--dom/workers/test/rvals_worker.js13
-rw-r--r--dom/workers/test/script_createFile.js15
-rw-r--r--dom/workers/test/serviceworkers/activate_event_error_worker.js4
-rw-r--r--dom/workers/test/serviceworkers/blocking_install_event_worker.js23
-rw-r--r--dom/workers/test/serviceworkers/browser.ini10
-rw-r--r--dom/workers/test/serviceworkers/browser_base_force_refresh.html30
-rw-r--r--dom/workers/test/serviceworkers/browser_cached_force_refresh.html64
-rw-r--r--dom/workers/test/serviceworkers/browser_download.js83
-rw-r--r--dom/workers/test/serviceworkers/browser_force_refresh.js91
-rw-r--r--dom/workers/test/serviceworkers/bug1151916_driver.html53
-rw-r--r--dom/workers/test/serviceworkers/bug1151916_worker.js13
-rw-r--r--dom/workers/test/serviceworkers/bug1240436_worker.js2
-rw-r--r--dom/workers/test/serviceworkers/chrome.ini16
-rw-r--r--dom/workers/test/serviceworkers/chrome_helpers.js74
-rw-r--r--dom/workers/test/serviceworkers/claim_clients/client.html44
-rw-r--r--dom/workers/test/serviceworkers/claim_fetch_worker.js12
-rw-r--r--dom/workers/test/serviceworkers/claim_oninstall_worker.js7
-rw-r--r--dom/workers/test/serviceworkers/claim_worker_1.js28
-rw-r--r--dom/workers/test/serviceworkers/claim_worker_2.js27
-rw-r--r--dom/workers/test/serviceworkers/close_test.js19
-rw-r--r--dom/workers/test/serviceworkers/controller/index.html74
-rw-r--r--dom/workers/test/serviceworkers/create_another_sharedWorker.html6
-rw-r--r--dom/workers/test/serviceworkers/download/window.html46
-rw-r--r--dom/workers/test/serviceworkers/download/worker.js30
-rw-r--r--dom/workers/test/serviceworkers/empty.js0
-rw-r--r--dom/workers/test/serviceworkers/error_reporting_helpers.js68
-rw-r--r--dom/workers/test/serviceworkers/eval_worker.js1
-rw-r--r--dom/workers/test/serviceworkers/eventsource/eventsource.resource22
-rw-r--r--dom/workers/test/serviceworkers/eventsource/eventsource.resource^headers^3
-rw-r--r--dom/workers/test/serviceworkers/eventsource/eventsource_cors_response.html75
-rw-r--r--dom/workers/test/serviceworkers/eventsource/eventsource_cors_response_intercept_worker.js20
-rw-r--r--dom/workers/test/serviceworkers/eventsource/eventsource_mixed_content_cors_response.html75
-rw-r--r--dom/workers/test/serviceworkers/eventsource/eventsource_mixed_content_cors_response_intercept_worker.js19
-rw-r--r--dom/workers/test/serviceworkers/eventsource/eventsource_opaque_response.html75
-rw-r--r--dom/workers/test/serviceworkers/eventsource/eventsource_opaque_response_intercept_worker.js20
-rw-r--r--dom/workers/test/serviceworkers/eventsource/eventsource_register_worker.html27
-rw-r--r--dom/workers/test/serviceworkers/eventsource/eventsource_synthetic_response.html75
-rw-r--r--dom/workers/test/serviceworkers/eventsource/eventsource_synthetic_response_intercept_worker.js24
-rw-r--r--dom/workers/test/serviceworkers/eventsource/eventsource_worker_helper.js12
-rw-r--r--dom/workers/test/serviceworkers/fetch.js11
-rw-r--r--dom/workers/test/serviceworkers/fetch/context/beacon.sjs43
-rw-r--r--dom/workers/test/serviceworkers/fetch/context/context_test.js135
-rw-r--r--dom/workers/test/serviceworkers/fetch/context/csp-violate.sjs6
-rw-r--r--dom/workers/test/serviceworkers/fetch/context/index.html422
-rw-r--r--dom/workers/test/serviceworkers/fetch/context/parentsharedworker.js8
-rw-r--r--dom/workers/test/serviceworkers/fetch/context/parentworker.js4
-rw-r--r--dom/workers/test/serviceworkers/fetch/context/ping.html7
-rw-r--r--dom/workers/test/serviceworkers/fetch/context/realaudio.oggbin0 -> 6416 bytes
-rw-r--r--dom/workers/test/serviceworkers/fetch/context/realimg.jpgbin0 -> 3595 bytes
-rw-r--r--dom/workers/test/serviceworkers/fetch/context/register.html14
-rw-r--r--dom/workers/test/serviceworkers/fetch/context/sharedworker.js5
-rw-r--r--dom/workers/test/serviceworkers/fetch/context/unregister.html12
-rw-r--r--dom/workers/test/serviceworkers/fetch/context/worker.js1
-rw-r--r--dom/workers/test/serviceworkers/fetch/context/xml.xml3
-rw-r--r--dom/workers/test/serviceworkers/fetch/deliver-gzip.sjs17
-rw-r--r--dom/workers/test/serviceworkers/fetch/fetch_tests.js416
-rw-r--r--dom/workers/test/serviceworkers/fetch/fetch_worker_script.js29
-rw-r--r--dom/workers/test/serviceworkers/fetch/hsts/embedder.html7
-rw-r--r--dom/workers/test/serviceworkers/fetch/hsts/hsts_test.js11
-rw-r--r--dom/workers/test/serviceworkers/fetch/hsts/image-20px.pngbin0 -> 87 bytes
-rw-r--r--dom/workers/test/serviceworkers/fetch/hsts/image-40px.pngbin0 -> 123 bytes
-rw-r--r--dom/workers/test/serviceworkers/fetch/hsts/image.html13
-rw-r--r--dom/workers/test/serviceworkers/fetch/hsts/realindex.html8
-rw-r--r--dom/workers/test/serviceworkers/fetch/hsts/register.html14
-rw-r--r--dom/workers/test/serviceworkers/fetch/hsts/register.html^headers^2
-rw-r--r--dom/workers/test/serviceworkers/fetch/hsts/unregister.html12
-rw-r--r--dom/workers/test/serviceworkers/fetch/https/clonedresponse/https_test.js15
-rw-r--r--dom/workers/test/serviceworkers/fetch/https/clonedresponse/index.html4
-rw-r--r--dom/workers/test/serviceworkers/fetch/https/clonedresponse/register.html14
-rw-r--r--dom/workers/test/serviceworkers/fetch/https/clonedresponse/unregister.html12
-rw-r--r--dom/workers/test/serviceworkers/fetch/https/https_test.js23
-rw-r--r--dom/workers/test/serviceworkers/fetch/https/index.html4
-rw-r--r--dom/workers/test/serviceworkers/fetch/https/register.html20
-rw-r--r--dom/workers/test/serviceworkers/fetch/https/unregister.html12
-rw-r--r--dom/workers/test/serviceworkers/fetch/imagecache-maxage/image-20px.pngbin0 -> 87 bytes
-rw-r--r--dom/workers/test/serviceworkers/fetch/imagecache-maxage/image-40px.pngbin0 -> 123 bytes
-rw-r--r--dom/workers/test/serviceworkers/fetch/imagecache-maxage/index.html29
-rw-r--r--dom/workers/test/serviceworkers/fetch/imagecache-maxage/maxage_test.js41
-rw-r--r--dom/workers/test/serviceworkers/fetch/imagecache-maxage/register.html14
-rw-r--r--dom/workers/test/serviceworkers/fetch/imagecache-maxage/unregister.html12
-rw-r--r--dom/workers/test/serviceworkers/fetch/imagecache/image-20px.pngbin0 -> 87 bytes
-rw-r--r--dom/workers/test/serviceworkers/fetch/imagecache/image-40px.pngbin0 -> 123 bytes
-rw-r--r--dom/workers/test/serviceworkers/fetch/imagecache/imagecache_test.js15
-rw-r--r--dom/workers/test/serviceworkers/fetch/imagecache/index.html20
-rw-r--r--dom/workers/test/serviceworkers/fetch/imagecache/postmortem.html9
-rw-r--r--dom/workers/test/serviceworkers/fetch/imagecache/register.html16
-rw-r--r--dom/workers/test/serviceworkers/fetch/imagecache/unregister.html12
-rw-r--r--dom/workers/test/serviceworkers/fetch/importscript-mixedcontent/https_test.js28
-rw-r--r--dom/workers/test/serviceworkers/fetch/importscript-mixedcontent/register.html14
-rw-r--r--dom/workers/test/serviceworkers/fetch/importscript-mixedcontent/unregister.html12
-rw-r--r--dom/workers/test/serviceworkers/fetch/index.html183
-rw-r--r--dom/workers/test/serviceworkers/fetch/interrupt.sjs20
-rw-r--r--dom/workers/test/serviceworkers/fetch/origin/https/index-https.sjs4
-rw-r--r--dom/workers/test/serviceworkers/fetch/origin/https/origin_test.js29
-rw-r--r--dom/workers/test/serviceworkers/fetch/origin/https/realindex.html6
-rw-r--r--dom/workers/test/serviceworkers/fetch/origin/https/realindex.html^headers^1
-rw-r--r--dom/workers/test/serviceworkers/fetch/origin/https/register.html14
-rw-r--r--dom/workers/test/serviceworkers/fetch/origin/https/unregister.html12
-rw-r--r--dom/workers/test/serviceworkers/fetch/origin/index-to-https.sjs4
-rw-r--r--dom/workers/test/serviceworkers/fetch/origin/index.sjs4
-rw-r--r--dom/workers/test/serviceworkers/fetch/origin/origin_test.js41
-rw-r--r--dom/workers/test/serviceworkers/fetch/origin/realindex.html6
-rw-r--r--dom/workers/test/serviceworkers/fetch/origin/realindex.html^headers^1
-rw-r--r--dom/workers/test/serviceworkers/fetch/origin/register.html14
-rw-r--r--dom/workers/test/serviceworkers/fetch/origin/unregister.html12
-rw-r--r--dom/workers/test/serviceworkers/fetch/plugin/plugins.html43
-rw-r--r--dom/workers/test/serviceworkers/fetch/plugin/worker.js14
-rw-r--r--dom/workers/test/serviceworkers/fetch/real-file.txt1
-rw-r--r--dom/workers/test/serviceworkers/fetch/redirect.sjs4
-rw-r--r--dom/workers/test/serviceworkers/fetch/requesturl/index.html7
-rw-r--r--dom/workers/test/serviceworkers/fetch/requesturl/redirect.sjs4
-rw-r--r--dom/workers/test/serviceworkers/fetch/requesturl/redirector.html2
-rw-r--r--dom/workers/test/serviceworkers/fetch/requesturl/register.html14
-rw-r--r--dom/workers/test/serviceworkers/fetch/requesturl/requesturl_test.js17
-rw-r--r--dom/workers/test/serviceworkers/fetch/requesturl/secret.html5
-rw-r--r--dom/workers/test/serviceworkers/fetch/requesturl/unregister.html12
-rw-r--r--dom/workers/test/serviceworkers/fetch/sandbox/index.html5
-rw-r--r--dom/workers/test/serviceworkers/fetch/sandbox/intercepted_index.html5
-rw-r--r--dom/workers/test/serviceworkers/fetch/sandbox/register.html14
-rw-r--r--dom/workers/test/serviceworkers/fetch/sandbox/sandbox_test.js5
-rw-r--r--dom/workers/test/serviceworkers/fetch/sandbox/unregister.html12
-rw-r--r--dom/workers/test/serviceworkers/fetch/upgrade-insecure/embedder.html10
-rw-r--r--dom/workers/test/serviceworkers/fetch/upgrade-insecure/embedder.html^headers^1
-rw-r--r--dom/workers/test/serviceworkers/fetch/upgrade-insecure/image-20px.pngbin0 -> 87 bytes
-rw-r--r--dom/workers/test/serviceworkers/fetch/upgrade-insecure/image-40px.pngbin0 -> 123 bytes
-rw-r--r--dom/workers/test/serviceworkers/fetch/upgrade-insecure/image.html13
-rw-r--r--dom/workers/test/serviceworkers/fetch/upgrade-insecure/realindex.html4
-rw-r--r--dom/workers/test/serviceworkers/fetch/upgrade-insecure/register.html14
-rw-r--r--dom/workers/test/serviceworkers/fetch/upgrade-insecure/unregister.html12
-rw-r--r--dom/workers/test/serviceworkers/fetch/upgrade-insecure/upgrade-insecure_test.js11
-rw-r--r--dom/workers/test/serviceworkers/fetch_event_worker.js337
-rw-r--r--dom/workers/test/serviceworkers/file_blob_response_worker.js38
-rw-r--r--dom/workers/test/serviceworkers/force_refresh_browser_worker.js34
-rw-r--r--dom/workers/test/serviceworkers/force_refresh_worker.js33
-rw-r--r--dom/workers/test/serviceworkers/gzip_redirect_worker.js13
-rw-r--r--dom/workers/test/serviceworkers/header_checker.sjs9
-rw-r--r--dom/workers/test/serviceworkers/hello.html9
-rw-r--r--dom/workers/test/serviceworkers/importscript.sjs11
-rw-r--r--dom/workers/test/serviceworkers/importscript_worker.js37
-rw-r--r--dom/workers/test/serviceworkers/install_event_error_worker.js4
-rw-r--r--dom/workers/test/serviceworkers/install_event_worker.js3
-rw-r--r--dom/workers/test/serviceworkers/lorem_script.js8
-rw-r--r--dom/workers/test/serviceworkers/match_all_advanced_worker.js5
-rw-r--r--dom/workers/test/serviceworkers/match_all_client/match_all_client_id.html31
-rw-r--r--dom/workers/test/serviceworkers/match_all_client_id_worker.js28
-rw-r--r--dom/workers/test/serviceworkers/match_all_clients/match_all_controlled.html65
-rw-r--r--dom/workers/test/serviceworkers/match_all_properties_worker.js20
-rw-r--r--dom/workers/test/serviceworkers/match_all_worker.js10
-rw-r--r--dom/workers/test/serviceworkers/message_posting_worker.js8
-rw-r--r--dom/workers/test/serviceworkers/message_receiver.html6
-rw-r--r--dom/workers/test/serviceworkers/mochitest.ini317
-rw-r--r--dom/workers/test/serviceworkers/notification/listener.html27
-rw-r--r--dom/workers/test/serviceworkers/notification/register.html11
-rw-r--r--dom/workers/test/serviceworkers/notification/unregister.html12
-rw-r--r--dom/workers/test/serviceworkers/notification_alt/register.html11
-rw-r--r--dom/workers/test/serviceworkers/notification_alt/unregister.html12
-rw-r--r--dom/workers/test/serviceworkers/notification_constructor_error.js1
-rw-r--r--dom/workers/test/serviceworkers/notification_get_sw.js49
-rw-r--r--dom/workers/test/serviceworkers/notificationclick-otherwindow.html30
-rw-r--r--dom/workers/test/serviceworkers/notificationclick.html27
-rw-r--r--dom/workers/test/serviceworkers/notificationclick.js19
-rw-r--r--dom/workers/test/serviceworkers/notificationclick_focus.html28
-rw-r--r--dom/workers/test/serviceworkers/notificationclick_focus.js40
-rw-r--r--dom/workers/test/serviceworkers/notificationclose.html37
-rw-r--r--dom/workers/test/serviceworkers/notificationclose.js19
-rw-r--r--dom/workers/test/serviceworkers/notify_loaded.js1
-rw-r--r--dom/workers/test/serviceworkers/opaque_intercept_worker.js25
-rw-r--r--dom/workers/test/serviceworkers/openWindow_worker.js116
-rw-r--r--dom/workers/test/serviceworkers/open_window/client.html48
-rw-r--r--dom/workers/test/serviceworkers/parse_error_worker.js2
-rw-r--r--dom/workers/test/serviceworkers/redirect.sjs5
-rw-r--r--dom/workers/test/serviceworkers/redirect_post.sjs35
-rw-r--r--dom/workers/test/serviceworkers/redirect_serviceworker.sjs4
-rw-r--r--dom/workers/test/serviceworkers/register_https.html21
-rw-r--r--dom/workers/test/serviceworkers/sanitize/example_check_and_unregister.html23
-rw-r--r--dom/workers/test/serviceworkers/sanitize/frame.html11
-rw-r--r--dom/workers/test/serviceworkers/sanitize/register.html10
-rw-r--r--dom/workers/test/serviceworkers/sanitize_worker.js5
-rw-r--r--dom/workers/test/serviceworkers/scope/scope_worker.js2
-rw-r--r--dom/workers/test/serviceworkers/serviceworker.html12
-rw-r--r--dom/workers/test/serviceworkers/serviceworker_not_sharedworker.js21
-rw-r--r--dom/workers/test/serviceworkers/serviceworker_wrapper.js101
-rw-r--r--dom/workers/test/serviceworkers/serviceworkerinfo_iframe.html26
-rw-r--r--dom/workers/test/serviceworkers/serviceworkermanager_iframe.html34
-rw-r--r--dom/workers/test/serviceworkers/serviceworkerregistrationinfo_iframe.html30
-rw-r--r--dom/workers/test/serviceworkers/sharedWorker_fetch.js29
-rw-r--r--dom/workers/test/serviceworkers/simpleregister/index.html51
-rw-r--r--dom/workers/test/serviceworkers/simpleregister/ready.html15
-rw-r--r--dom/workers/test/serviceworkers/skip_waiting_installed_worker.js6
-rw-r--r--dom/workers/test/serviceworkers/skip_waiting_scope/index.html37
-rw-r--r--dom/workers/test/serviceworkers/source_message_posting_worker.js16
-rw-r--r--dom/workers/test/serviceworkers/strict_mode_warning.js4
-rw-r--r--dom/workers/test/serviceworkers/sw_bad_mime_type.js1
-rw-r--r--dom/workers/test/serviceworkers/sw_bad_mime_type.js^headers^1
-rw-r--r--dom/workers/test/serviceworkers/sw_clients/file_blob_upload_frame.html77
-rw-r--r--dom/workers/test/serviceworkers/sw_clients/navigator.html35
-rw-r--r--dom/workers/test/serviceworkers/sw_clients/refresher.html39
-rw-r--r--dom/workers/test/serviceworkers/sw_clients/refresher_cached.html38
-rw-r--r--dom/workers/test/serviceworkers/sw_clients/refresher_cached_compressed.htmlbin0 -> 559 bytes
-rw-r--r--dom/workers/test/serviceworkers/sw_clients/refresher_cached_compressed.html^headers^2
-rw-r--r--dom/workers/test/serviceworkers/sw_clients/refresher_compressed.htmlbin0 -> 608 bytes
-rw-r--r--dom/workers/test/serviceworkers/sw_clients/refresher_compressed.html^headers^2
-rw-r--r--dom/workers/test/serviceworkers/sw_clients/service_worker_controlled.html38
-rw-r--r--dom/workers/test/serviceworkers/sw_clients/simple.html30
-rw-r--r--dom/workers/test/serviceworkers/swa/worker_scope_different.js0
-rw-r--r--dom/workers/test/serviceworkers/swa/worker_scope_different.js^headers^1
-rw-r--r--dom/workers/test/serviceworkers/swa/worker_scope_different2.js0
-rw-r--r--dom/workers/test/serviceworkers/swa/worker_scope_different2.js^headers^1
-rw-r--r--dom/workers/test/serviceworkers/swa/worker_scope_precise.js0
-rw-r--r--dom/workers/test/serviceworkers/swa/worker_scope_precise.js^headers^1
-rw-r--r--dom/workers/test/serviceworkers/swa/worker_scope_too_deep.js0
-rw-r--r--dom/workers/test/serviceworkers/swa/worker_scope_too_deep.js^headers^1
-rw-r--r--dom/workers/test/serviceworkers/swa/worker_scope_too_narrow.js0
-rw-r--r--dom/workers/test/serviceworkers/swa/worker_scope_too_narrow.js^headers^1
-rw-r--r--dom/workers/test/serviceworkers/test_bug1151916.html105
-rw-r--r--dom/workers/test/serviceworkers/test_bug1240436.html34
-rw-r--r--dom/workers/test/serviceworkers/test_claim.html172
-rw-r--r--dom/workers/test/serviceworkers/test_claim_fetch.html98
-rw-r--r--dom/workers/test/serviceworkers/test_claim_oninstall.html78
-rw-r--r--dom/workers/test/serviceworkers/test_client_focus.html96
-rw-r--r--dom/workers/test/serviceworkers/test_close.html64
-rw-r--r--dom/workers/test/serviceworkers/test_controller.html84
-rw-r--r--dom/workers/test/serviceworkers/test_cross_origin_url_after_redirect.html50
-rw-r--r--dom/workers/test/serviceworkers/test_csp_upgrade-insecure_intercept.html55
-rw-r--r--dom/workers/test/serviceworkers/test_empty_serviceworker.html46
-rw-r--r--dom/workers/test/serviceworkers/test_error_reporting.html76
-rw-r--r--dom/workers/test/serviceworkers/test_escapedSlashes.html102
-rw-r--r--dom/workers/test/serviceworkers/test_eval_allowed.html51
-rw-r--r--dom/workers/test/serviceworkers/test_eval_allowed.html^headers^1
-rw-r--r--dom/workers/test/serviceworkers/test_eventsource_intercept.html103
-rw-r--r--dom/workers/test/serviceworkers/test_fetch_event.html83
-rw-r--r--dom/workers/test/serviceworkers/test_fetch_integrity.html178
-rw-r--r--dom/workers/test/serviceworkers/test_file_blob_response.html86
-rw-r--r--dom/workers/test/serviceworkers/test_file_blob_upload.html145
-rw-r--r--dom/workers/test/serviceworkers/test_force_refresh.html84
-rw-r--r--dom/workers/test/serviceworkers/test_gzip_redirect.html84
-rw-r--r--dom/workers/test/serviceworkers/test_hsts_upgrade_intercept.html66
-rw-r--r--dom/workers/test/serviceworkers/test_https_fetch.html62
-rw-r--r--dom/workers/test/serviceworkers/test_https_fetch_cloned_response.html56
-rw-r--r--dom/workers/test/serviceworkers/test_https_origin_after_redirect.html57
-rw-r--r--dom/workers/test/serviceworkers/test_https_origin_after_redirect_cached.html57
-rw-r--r--dom/workers/test/serviceworkers/test_https_synth_fetch_from_cached_sw.html69
-rw-r--r--dom/workers/test/serviceworkers/test_imagecache.html55
-rw-r--r--dom/workers/test/serviceworkers/test_imagecache_max_age.html71
-rw-r--r--dom/workers/test/serviceworkers/test_importscript.html72
-rw-r--r--dom/workers/test/serviceworkers/test_importscript_mixedcontent.html53
-rw-r--r--dom/workers/test/serviceworkers/test_install_event.html144
-rw-r--r--dom/workers/test/serviceworkers/test_install_event_gc.html121
-rw-r--r--dom/workers/test/serviceworkers/test_installation_simple.html212
-rw-r--r--dom/workers/test/serviceworkers/test_match_all.html80
-rw-r--r--dom/workers/test/serviceworkers/test_match_all_advanced.html100
-rw-r--r--dom/workers/test/serviceworkers/test_match_all_client_id.html91
-rw-r--r--dom/workers/test/serviceworkers/test_match_all_client_properties.html97
-rw-r--r--dom/workers/test/serviceworkers/test_navigator.html40
-rw-r--r--dom/workers/test/serviceworkers/test_not_intercept_plugin.html78
-rw-r--r--dom/workers/test/serviceworkers/test_notification_constructor_error.html52
-rw-r--r--dom/workers/test/serviceworkers/test_notification_get.html213
-rw-r--r--dom/workers/test/serviceworkers/test_notificationclick-otherwindow.html62
-rw-r--r--dom/workers/test/serviceworkers/test_notificationclick.html62
-rw-r--r--dom/workers/test/serviceworkers/test_notificationclick_focus.html63
-rw-r--r--dom/workers/test/serviceworkers/test_notificationclose.html62
-rw-r--r--dom/workers/test/serviceworkers/test_opaque_intercept.html85
-rw-r--r--dom/workers/test/serviceworkers/test_openWindow.html117
-rw-r--r--dom/workers/test/serviceworkers/test_origin_after_redirect.html58
-rw-r--r--dom/workers/test/serviceworkers/test_origin_after_redirect_cached.html58
-rw-r--r--dom/workers/test/serviceworkers/test_origin_after_redirect_to_https.html57
-rw-r--r--dom/workers/test/serviceworkers/test_origin_after_redirect_to_https_cached.html58
-rw-r--r--dom/workers/test/serviceworkers/test_post_message.html80
-rw-r--r--dom/workers/test/serviceworkers/test_post_message_advanced.html109
-rw-r--r--dom/workers/test/serviceworkers/test_post_message_source.html68
-rw-r--r--dom/workers/test/serviceworkers/test_privateBrowsing.html113
-rw-r--r--dom/workers/test/serviceworkers/test_register_base.html41
-rw-r--r--dom/workers/test/serviceworkers/test_register_https_in_http.html46
-rw-r--r--dom/workers/test/serviceworkers/test_request_context.js75
-rw-r--r--dom/workers/test/serviceworkers/test_request_context_audio.html19
-rw-r--r--dom/workers/test/serviceworkers/test_request_context_beacon.html19
-rw-r--r--dom/workers/test/serviceworkers/test_request_context_cache.html19
-rw-r--r--dom/workers/test/serviceworkers/test_request_context_cspreport.html19
-rw-r--r--dom/workers/test/serviceworkers/test_request_context_embed.html19
-rw-r--r--dom/workers/test/serviceworkers/test_request_context_fetch.html19
-rw-r--r--dom/workers/test/serviceworkers/test_request_context_font.html19
-rw-r--r--dom/workers/test/serviceworkers/test_request_context_frame.html19
-rw-r--r--dom/workers/test/serviceworkers/test_request_context_iframe.html19
-rw-r--r--dom/workers/test/serviceworkers/test_request_context_image.html19
-rw-r--r--dom/workers/test/serviceworkers/test_request_context_imagesrcset.html19
-rw-r--r--dom/workers/test/serviceworkers/test_request_context_internal.html19
-rw-r--r--dom/workers/test/serviceworkers/test_request_context_nestedworker.html19
-rw-r--r--dom/workers/test/serviceworkers/test_request_context_nestedworkerinsharedworker.html19
-rw-r--r--dom/workers/test/serviceworkers/test_request_context_object.html19
-rw-r--r--dom/workers/test/serviceworkers/test_request_context_picture.html19
-rw-r--r--dom/workers/test/serviceworkers/test_request_context_ping.html19
-rw-r--r--dom/workers/test/serviceworkers/test_request_context_plugin.html19
-rw-r--r--dom/workers/test/serviceworkers/test_request_context_script.html19
-rw-r--r--dom/workers/test/serviceworkers/test_request_context_sharedworker.html19
-rw-r--r--dom/workers/test/serviceworkers/test_request_context_style.html19
-rw-r--r--dom/workers/test/serviceworkers/test_request_context_track.html19
-rw-r--r--dom/workers/test/serviceworkers/test_request_context_video.html19
-rw-r--r--dom/workers/test/serviceworkers/test_request_context_worker.html19
-rw-r--r--dom/workers/test/serviceworkers/test_request_context_xhr.html19
-rw-r--r--dom/workers/test/serviceworkers/test_request_context_xslt.html19
-rw-r--r--dom/workers/test/serviceworkers/test_sandbox_intercept.html50
-rw-r--r--dom/workers/test/serviceworkers/test_sanitize.html87
-rw-r--r--dom/workers/test/serviceworkers/test_sanitize_domain.html90
-rw-r--r--dom/workers/test/serviceworkers/test_scopes.html121
-rw-r--r--dom/workers/test/serviceworkers/test_service_worker_allowed.html74
-rw-r--r--dom/workers/test/serviceworkers/test_serviceworker_header.html41
-rw-r--r--dom/workers/test/serviceworkers/test_serviceworker_interfaces.html106
-rw-r--r--dom/workers/test/serviceworkers/test_serviceworker_interfaces.js278
-rw-r--r--dom/workers/test/serviceworkers/test_serviceworker_not_sharedworker.html66
-rw-r--r--dom/workers/test/serviceworkers/test_serviceworkerinfo.xul115
-rw-r--r--dom/workers/test/serviceworkers/test_serviceworkermanager.xul80
-rw-r--r--dom/workers/test/serviceworkers/test_serviceworkerregistrationinfo.xul115
-rw-r--r--dom/workers/test/serviceworkers/test_skip_waiting.html95
-rw-r--r--dom/workers/test/serviceworkers/test_strict_mode_warning.html42
-rw-r--r--dom/workers/test/serviceworkers/test_third_party_iframes.html175
-rw-r--r--dom/workers/test/serviceworkers/test_unregister.html138
-rw-r--r--dom/workers/test/serviceworkers/test_unresolved_fetch_interception.html100
-rw-r--r--dom/workers/test/serviceworkers/test_workerUnregister.html82
-rw-r--r--dom/workers/test/serviceworkers/test_workerUpdate.html62
-rw-r--r--dom/workers/test/serviceworkers/test_workerupdatefoundevent.html85
-rw-r--r--dom/workers/test/serviceworkers/test_xslt.html128
-rw-r--r--dom/workers/test/serviceworkers/thirdparty/iframe1.html30
-rw-r--r--dom/workers/test/serviceworkers/thirdparty/iframe2.html7
-rw-r--r--dom/workers/test/serviceworkers/thirdparty/register.html27
-rw-r--r--dom/workers/test/serviceworkers/thirdparty/sw.js14
-rw-r--r--dom/workers/test/serviceworkers/thirdparty/unregister.html11
-rw-r--r--dom/workers/test/serviceworkers/unregister/index.html26
-rw-r--r--dom/workers/test/serviceworkers/unregister/unregister.html22
-rw-r--r--dom/workers/test/serviceworkers/unresolved_fetch_worker.js19
-rw-r--r--dom/workers/test/serviceworkers/updatefoundevent.html13
-rw-r--r--dom/workers/test/serviceworkers/worker.js1
-rw-r--r--dom/workers/test/serviceworkers/worker2.js1
-rw-r--r--dom/workers/test/serviceworkers/worker3.js1
-rw-r--r--dom/workers/test/serviceworkers/workerUpdate/update.html24
-rw-r--r--dom/workers/test/serviceworkers/worker_unregister.js16
-rw-r--r--dom/workers/test/serviceworkers/worker_update.js19
-rw-r--r--dom/workers/test/serviceworkers/worker_updatefoundevent.js23
-rw-r--r--dom/workers/test/serviceworkers/worker_updatefoundevent2.js1
-rw-r--r--dom/workers/test/serviceworkers/xslt/test.xml6
-rw-r--r--dom/workers/test/serviceworkers/xslt/xslt.sjs12
-rw-r--r--dom/workers/test/serviceworkers/xslt_worker.js52
-rw-r--r--dom/workers/test/sharedWorker_console.js11
-rw-r--r--dom/workers/test/sharedWorker_lifetime.js5
-rw-r--r--dom/workers/test/sharedWorker_ports.js24
-rw-r--r--dom/workers/test/sharedWorker_privateBrowsing.js5
-rw-r--r--dom/workers/test/sharedWorker_sharedWorker.js93
-rw-r--r--dom/workers/test/simpleThread_worker.js53
-rw-r--r--dom/workers/test/suspend_iframe.html47
-rw-r--r--dom/workers/test/suspend_worker.js13
-rw-r--r--dom/workers/test/terminate_worker.js9
-rw-r--r--dom/workers/test/test_404.html41
-rw-r--r--dom/workers/test/test_WorkerDebugger.initialize.xul58
-rw-r--r--dom/workers/test/test_WorkerDebugger.postMessage.xul61
-rw-r--r--dom/workers/test/test_WorkerDebugger.xul122
-rw-r--r--dom/workers/test/test_WorkerDebuggerGlobalScope.createSandbox.xul52
-rw-r--r--dom/workers/test/test_WorkerDebuggerGlobalScope.enterEventLoop.xul126
-rw-r--r--dom/workers/test/test_WorkerDebuggerGlobalScope.reportError.xul98
-rw-r--r--dom/workers/test/test_WorkerDebuggerGlobalScope.setImmediate.xul54
-rw-r--r--dom/workers/test/test_WorkerDebuggerManager.xul106
-rw-r--r--dom/workers/test/test_WorkerDebugger_console.xul97
-rw-r--r--dom/workers/test/test_WorkerDebugger_frozen.xul90
-rw-r--r--dom/workers/test/test_WorkerDebugger_promise.xul70
-rw-r--r--dom/workers/test/test_WorkerDebugger_suspended.xul75
-rw-r--r--dom/workers/test/test_atob.html57
-rw-r--r--dom/workers/test/test_blobConstructor.html60
-rw-r--r--dom/workers/test/test_blobWorkers.html32
-rw-r--r--dom/workers/test/test_bug1002702.html27
-rw-r--r--dom/workers/test/test_bug1010784.html35
-rw-r--r--dom/workers/test/test_bug1014466.html42
-rw-r--r--dom/workers/test/test_bug1020226.html38
-rw-r--r--dom/workers/test/test_bug1036484.html54
-rw-r--r--dom/workers/test/test_bug1060621.html30
-rw-r--r--dom/workers/test/test_bug1062920.html70
-rw-r--r--dom/workers/test/test_bug1062920.xul69
-rw-r--r--dom/workers/test/test_bug1063538.html49
-rw-r--r--dom/workers/test/test_bug1104064.html28
-rw-r--r--dom/workers/test/test_bug1132395.html40
-rw-r--r--dom/workers/test/test_bug1132924.html28
-rw-r--r--dom/workers/test/test_bug1278777.html31
-rw-r--r--dom/workers/test/test_bug1301094.html69
-rw-r--r--dom/workers/test/test_bug1317725.html62
-rw-r--r--dom/workers/test/test_bug949946.html26
-rw-r--r--dom/workers/test/test_bug978260.html35
-rw-r--r--dom/workers/test/test_bug998474.html40
-rw-r--r--dom/workers/test/test_chromeWorker.html27
-rw-r--r--dom/workers/test/test_chromeWorker.xul45
-rw-r--r--dom/workers/test/test_chromeWorkerJSM.xul56
-rw-r--r--dom/workers/test/test_clearTimeouts.html31
-rw-r--r--dom/workers/test/test_console.html44
-rw-r--r--dom/workers/test/test_consoleAndBlobs.html43
-rw-r--r--dom/workers/test/test_consoleReplaceable.html44
-rw-r--r--dom/workers/test/test_consoleSharedWorkers.html56
-rw-r--r--dom/workers/test/test_contentWorker.html48
-rw-r--r--dom/workers/test/test_csp.html18
-rw-r--r--dom/workers/test/test_csp.html^headers^2
-rw-r--r--dom/workers/test/test_csp.js48
-rw-r--r--dom/workers/test/test_dataURLWorker.html31
-rw-r--r--dom/workers/test/test_errorPropagation.html66
-rw-r--r--dom/workers/test/test_errorwarning.html95
-rw-r--r--dom/workers/test/test_eventDispatch.html33
-rw-r--r--dom/workers/test/test_extension.xul55
-rw-r--r--dom/workers/test/test_extensionBootstrap.xul66
-rw-r--r--dom/workers/test/test_fibonacci.html52
-rw-r--r--dom/workers/test/test_file.xul97
-rw-r--r--dom/workers/test/test_fileBlobPosting.xul86
-rw-r--r--dom/workers/test/test_fileBlobSubWorker.xul98
-rw-r--r--dom/workers/test/test_filePosting.xul86
-rw-r--r--dom/workers/test/test_fileReadSlice.xul94
-rw-r--r--dom/workers/test/test_fileReader.html100
-rw-r--r--dom/workers/test/test_fileReaderSync.xul199
-rw-r--r--dom/workers/test/test_fileReaderSyncErrors.xul84
-rw-r--r--dom/workers/test/test_fileSlice.xul106
-rw-r--r--dom/workers/test/test_fileSubWorker.xul99
-rw-r--r--dom/workers/test/test_importScripts.html53
-rw-r--r--dom/workers/test/test_importScripts_3rdparty.html134
-rw-r--r--dom/workers/test/test_importScripts_mixedcontent.html50
-rw-r--r--dom/workers/test/test_instanceof.html40
-rw-r--r--dom/workers/test/test_json.html89
-rw-r--r--dom/workers/test/test_jsversion.html68
-rw-r--r--dom/workers/test/test_loadEncoding.html50
-rw-r--r--dom/workers/test/test_loadError.html77
-rw-r--r--dom/workers/test/test_location.html72
-rw-r--r--dom/workers/test/test_longThread.html59
-rw-r--r--dom/workers/test/test_multi_sharedWorker.html242
-rw-r--r--dom/workers/test/test_multi_sharedWorker_lifetimes.html156
-rw-r--r--dom/workers/test/test_navigator.html68
-rw-r--r--dom/workers/test/test_navigator_languages.html53
-rw-r--r--dom/workers/test/test_navigator_workers_hardwareConcurrency.html30
-rw-r--r--dom/workers/test/test_newError.html34
-rw-r--r--dom/workers/test/test_notification.html50
-rw-r--r--dom/workers/test/test_notification_child.html49
-rw-r--r--dom/workers/test/test_notification_permission.html51
-rw-r--r--dom/workers/test/test_onLine.html64
-rw-r--r--dom/workers/test/test_promise.html44
-rw-r--r--dom/workers/test/test_promise_resolved_with_string.html41
-rw-r--r--dom/workers/test/test_recursion.html69
-rw-r--r--dom/workers/test/test_recursiveOnerror.html44
-rw-r--r--dom/workers/test/test_referrer.html58
-rw-r--r--dom/workers/test/test_referrer_header_worker.html39
-rw-r--r--dom/workers/test/test_resolveWorker-assignment.html30
-rw-r--r--dom/workers/test/test_resolveWorker.html31
-rw-r--r--dom/workers/test/test_rvals.html37
-rw-r--r--dom/workers/test/test_setTimeoutWith0.html20
-rw-r--r--dom/workers/test/test_sharedWorker.html71
-rw-r--r--dom/workers/test/test_sharedWorker_lifetime.html30
-rw-r--r--dom/workers/test/test_sharedWorker_ports.html42
-rw-r--r--dom/workers/test/test_sharedWorker_privateBrowsing.html101
-rw-r--r--dom/workers/test/test_simpleThread.html75
-rw-r--r--dom/workers/test/test_subworkers_suspended.html135
-rw-r--r--dom/workers/test/test_suspend.html138
-rw-r--r--dom/workers/test/test_terminate.html100
-rw-r--r--dom/workers/test/test_threadErrors.html64
-rw-r--r--dom/workers/test/test_threadTimeouts.html61
-rw-r--r--dom/workers/test/test_throwingOnerror.html54
-rw-r--r--dom/workers/test/test_timeoutTracing.html48
-rw-r--r--dom/workers/test/test_transferable.html123
-rw-r--r--dom/workers/test/test_webSocket_sharedWorker.html30
-rw-r--r--dom/workers/test/test_websocket1.html46
-rw-r--r--dom/workers/test/test_websocket2.html46
-rw-r--r--dom/workers/test/test_websocket3.html46
-rw-r--r--dom/workers/test/test_websocket4.html46
-rw-r--r--dom/workers/test/test_websocket5.html46
-rw-r--r--dom/workers/test/test_websocket_basic.html57
-rw-r--r--dom/workers/test/test_websocket_https.html30
-rw-r--r--dom/workers/test/test_websocket_loadgroup.html61
-rw-r--r--dom/workers/test/test_worker_interfaces.html16
-rw-r--r--dom/workers/test/test_worker_interfaces.js291
-rw-r--r--dom/workers/test/test_workersDisabled.html54
-rw-r--r--dom/workers/test/test_workersDisabled.xul49
-rw-r--r--dom/workers/test/threadErrors_worker1.js8
-rw-r--r--dom/workers/test/threadErrors_worker2.js8
-rw-r--r--dom/workers/test/threadErrors_worker3.js9
-rw-r--r--dom/workers/test/threadErrors_worker4.js8
-rw-r--r--dom/workers/test/threadTimeouts_worker.js44
-rw-r--r--dom/workers/test/throwingOnerror_worker.js15
-rw-r--r--dom/workers/test/timeoutTracing_worker.js13
-rw-r--r--dom/workers/test/transferable_worker.js23
-rw-r--r--dom/workers/test/webSocket_sharedWorker.js20
-rw-r--r--dom/workers/test/websocket_basic_worker.js39
-rw-r--r--dom/workers/test/websocket_helpers.js19
-rw-r--r--dom/workers/test/websocket_https.html14
-rw-r--r--dom/workers/test/websocket_https_worker.js9
-rw-r--r--dom/workers/test/websocket_loadgroup_worker.js24
-rw-r--r--dom/workers/test/websocket_worker1.js19
-rw-r--r--dom/workers/test/websocket_worker2.js19
-rw-r--r--dom/workers/test/websocket_worker3.js17
-rw-r--r--dom/workers/test/websocket_worker4.js19
-rw-r--r--dom/workers/test/websocket_worker5.js13
-rw-r--r--dom/workers/test/window_suspended.html5
-rw-r--r--dom/workers/test/worker_bug1278777.js9
-rw-r--r--dom/workers/test/worker_bug1301094.js11
-rw-r--r--dom/workers/test/worker_consoleAndBlobs.js8
-rw-r--r--dom/workers/test/worker_driver.js64
-rw-r--r--dom/workers/test/worker_fileReader.js417
-rw-r--r--dom/workers/test/worker_referrer.js9
-rw-r--r--dom/workers/test/worker_setTimeoutWith0.js3
-rw-r--r--dom/workers/test/worker_suspended.js31
-rw-r--r--dom/workers/test/worker_wrapper.js99
-rw-r--r--dom/workers/test/workersDisabled_worker.js7
-rw-r--r--dom/workers/test/xpcshell/data/chrome.manifest1
-rw-r--r--dom/workers/test/xpcshell/data/worker.js7
-rw-r--r--dom/workers/test/xpcshell/data/worker_fileReader.js8
-rw-r--r--dom/workers/test/xpcshell/test_fileReader.js40
-rw-r--r--dom/workers/test/xpcshell/test_workers.js44
-rw-r--r--dom/workers/test/xpcshell/xpcshell.ini11
649 files changed, 28311 insertions, 0 deletions
diff --git a/dom/workers/test/404_server.sjs b/dom/workers/test/404_server.sjs
new file mode 100644
index 000000000..f5dff1abb
--- /dev/null
+++ b/dom/workers/test/404_server.sjs
@@ -0,0 +1,10 @@
+function handleRequest(request, response)
+{
+ response.setStatusLine(request.httpVersion, 404, "Not found");
+
+ // Any valid JS.
+ if (request.queryString == 'js') {
+ response.setHeader("Content-Type", "text/javascript", false);
+ response.write('4 + 4');
+ }
+}
diff --git a/dom/workers/test/WorkerDebugger.console_childWorker.js b/dom/workers/test/WorkerDebugger.console_childWorker.js
new file mode 100644
index 000000000..8cee6809e
--- /dev/null
+++ b/dom/workers/test/WorkerDebugger.console_childWorker.js
@@ -0,0 +1,3 @@
+"use strict";
+
+self.onmessage = function () {};
diff --git a/dom/workers/test/WorkerDebugger.console_debugger.js b/dom/workers/test/WorkerDebugger.console_debugger.js
new file mode 100644
index 000000000..662bd3520
--- /dev/null
+++ b/dom/workers/test/WorkerDebugger.console_debugger.js
@@ -0,0 +1,41 @@
+"use strict"
+
+function ok(a, msg) {
+ postMessage(JSON.stringify({ type: 'status', what: !!a, msg: msg }));
+}
+
+function is(a, b, msg) {
+ ok(a === b, msg);
+}
+
+function finish() {
+ postMessage(JSON.stringify({ type: 'finish' }));
+}
+
+function magic() {
+ console.log("Hello from the debugger script!");
+
+ var foo = retrieveConsoleEvents();
+ ok(Array.isArray(foo), "We received an array.");
+ ok(foo.length >= 2, "At least 2 messages.");
+
+ is(foo[0].arguments[0], "Can you see this console message?", "First message ok.");
+ is(foo[1].arguments[0], "Can you see this second console message?", "Second message ok.");
+
+ setConsoleEventHandler(function(consoleData) {
+ is(consoleData.arguments[0], "Random message.", "Random message ok!");
+
+ // The consoleEventHandler can be null.
+ setConsoleEventHandler(null);
+
+ finish();
+ });
+}
+
+this.onmessage = function (event) {
+ switch (event.data) {
+ case "do magic":
+ magic();
+ break;
+ }
+};
diff --git a/dom/workers/test/WorkerDebugger.console_worker.js b/dom/workers/test/WorkerDebugger.console_worker.js
new file mode 100644
index 000000000..2db43a3d7
--- /dev/null
+++ b/dom/workers/test/WorkerDebugger.console_worker.js
@@ -0,0 +1,10 @@
+"use strict";
+
+console.log("Can you see this console message?");
+console.warn("Can you see this second console message?");
+
+var worker = new Worker("WorkerDebugger.console_childWorker.js");
+
+setInterval(function() {
+ console.log("Random message.");
+}, 200);
diff --git a/dom/workers/test/WorkerDebugger.initialize_childWorker.js b/dom/workers/test/WorkerDebugger.initialize_childWorker.js
new file mode 100644
index 000000000..a85764bd9
--- /dev/null
+++ b/dom/workers/test/WorkerDebugger.initialize_childWorker.js
@@ -0,0 +1,6 @@
+"use strict";
+
+self.onmessage = function () {};
+
+debugger;
+postMessage("worker");
diff --git a/dom/workers/test/WorkerDebugger.initialize_debugger.js b/dom/workers/test/WorkerDebugger.initialize_debugger.js
new file mode 100644
index 000000000..f52e95b15
--- /dev/null
+++ b/dom/workers/test/WorkerDebugger.initialize_debugger.js
@@ -0,0 +1,6 @@
+"use strict";
+
+var dbg = new Debugger(global);
+dbg.onDebuggerStatement = function (frame) {
+ frame.eval("postMessage('debugger');");
+};
diff --git a/dom/workers/test/WorkerDebugger.initialize_worker.js b/dom/workers/test/WorkerDebugger.initialize_worker.js
new file mode 100644
index 000000000..5a24efd3a
--- /dev/null
+++ b/dom/workers/test/WorkerDebugger.initialize_worker.js
@@ -0,0 +1,9 @@
+"use strict";
+
+var worker = new Worker("WorkerDebugger.initialize_childWorker.js");
+worker.onmessage = function (event) {
+ postMessage("child:" + event.data);
+};
+
+debugger;
+postMessage("worker");
diff --git a/dom/workers/test/WorkerDebugger.postMessage_childWorker.js b/dom/workers/test/WorkerDebugger.postMessage_childWorker.js
new file mode 100644
index 000000000..8cee6809e
--- /dev/null
+++ b/dom/workers/test/WorkerDebugger.postMessage_childWorker.js
@@ -0,0 +1,3 @@
+"use strict";
+
+self.onmessage = function () {};
diff --git a/dom/workers/test/WorkerDebugger.postMessage_debugger.js b/dom/workers/test/WorkerDebugger.postMessage_debugger.js
new file mode 100644
index 000000000..4a231b7ab
--- /dev/null
+++ b/dom/workers/test/WorkerDebugger.postMessage_debugger.js
@@ -0,0 +1,9 @@
+"use strict"
+
+this.onmessage = function (event) {
+ switch (event.data) {
+ case "ping":
+ postMessage("pong");
+ break;
+ }
+};
diff --git a/dom/workers/test/WorkerDebugger.postMessage_worker.js b/dom/workers/test/WorkerDebugger.postMessage_worker.js
new file mode 100644
index 000000000..8ddf6cf86
--- /dev/null
+++ b/dom/workers/test/WorkerDebugger.postMessage_worker.js
@@ -0,0 +1,3 @@
+"use strict";
+
+var worker = new Worker("WorkerDebugger.postMessage_childWorker.js");
diff --git a/dom/workers/test/WorkerDebuggerGlobalScope.createSandbox_debugger.js b/dom/workers/test/WorkerDebuggerGlobalScope.createSandbox_debugger.js
new file mode 100644
index 000000000..908c9f316
--- /dev/null
+++ b/dom/workers/test/WorkerDebuggerGlobalScope.createSandbox_debugger.js
@@ -0,0 +1,9 @@
+"use strict";
+
+const SANDBOX_URL = "WorkerDebuggerGlobalScope.createSandbox_sandbox.js";
+
+var prototype = {
+ self: this,
+};
+var sandbox = createSandbox(SANDBOX_URL, prototype);
+loadSubScript(SANDBOX_URL, sandbox);
diff --git a/dom/workers/test/WorkerDebuggerGlobalScope.createSandbox_sandbox.js b/dom/workers/test/WorkerDebuggerGlobalScope.createSandbox_sandbox.js
new file mode 100644
index 000000000..f94d65062
--- /dev/null
+++ b/dom/workers/test/WorkerDebuggerGlobalScope.createSandbox_sandbox.js
@@ -0,0 +1,9 @@
+"use strict";
+
+self.addEventListener("message", function(event) {
+ switch (event.data) {
+ case "ping":
+ self.postMessage("pong");
+ break;
+ }
+});
diff --git a/dom/workers/test/WorkerDebuggerGlobalScope.createSandbox_worker.js b/dom/workers/test/WorkerDebuggerGlobalScope.createSandbox_worker.js
new file mode 100644
index 000000000..8cee6809e
--- /dev/null
+++ b/dom/workers/test/WorkerDebuggerGlobalScope.createSandbox_worker.js
@@ -0,0 +1,3 @@
+"use strict";
+
+self.onmessage = function () {};
diff --git a/dom/workers/test/WorkerDebuggerGlobalScope.enterEventLoop_childWorker.js b/dom/workers/test/WorkerDebuggerGlobalScope.enterEventLoop_childWorker.js
new file mode 100644
index 000000000..b76f45a4d
--- /dev/null
+++ b/dom/workers/test/WorkerDebuggerGlobalScope.enterEventLoop_childWorker.js
@@ -0,0 +1,14 @@
+"use strict";
+
+function f() {
+ debugger;
+}
+
+self.onmessage = function (event) {
+ switch (event.data) {
+ case "ping":
+ debugger;
+ postMessage("pong");
+ break;
+ };
+};
diff --git a/dom/workers/test/WorkerDebuggerGlobalScope.enterEventLoop_debugger.js b/dom/workers/test/WorkerDebuggerGlobalScope.enterEventLoop_debugger.js
new file mode 100644
index 000000000..dcd4dbecd
--- /dev/null
+++ b/dom/workers/test/WorkerDebuggerGlobalScope.enterEventLoop_debugger.js
@@ -0,0 +1,29 @@
+"use strict";
+
+var frames = [];
+
+var dbg = new Debugger(global);
+dbg.onDebuggerStatement = function (frame) {
+ frames.push(frame);
+ postMessage("paused");
+ enterEventLoop();
+ frames.pop();
+ postMessage("resumed");
+};
+
+this.onmessage = function (event) {
+ switch (event.data) {
+ case "eval":
+ frames[frames.length - 1].eval("f()");
+ postMessage("evalled");
+ break;
+
+ case "ping":
+ postMessage("pong");
+ break;
+
+ case "resume":
+ leaveEventLoop();
+ break;
+ };
+};
diff --git a/dom/workers/test/WorkerDebuggerGlobalScope.enterEventLoop_worker.js b/dom/workers/test/WorkerDebuggerGlobalScope.enterEventLoop_worker.js
new file mode 100644
index 000000000..c43738516
--- /dev/null
+++ b/dom/workers/test/WorkerDebuggerGlobalScope.enterEventLoop_worker.js
@@ -0,0 +1,25 @@
+"use strict";
+
+function f() {
+ debugger;
+}
+
+var worker = new Worker("WorkerDebuggerGlobalScope.enterEventLoop_childWorker.js");
+
+worker.onmessage = function (event) {
+ postMessage("child:" + event.data);
+};
+
+self.onmessage = function (event) {
+ var message = event.data;
+ if (message.indexOf(":") >= 0) {
+ worker.postMessage(message.split(":")[1]);
+ return;
+ }
+ switch (message) {
+ case "ping":
+ debugger;
+ postMessage("pong");
+ break;
+ };
+};
diff --git a/dom/workers/test/WorkerDebuggerGlobalScope.reportError_childWorker.js b/dom/workers/test/WorkerDebuggerGlobalScope.reportError_childWorker.js
new file mode 100644
index 000000000..823e7c477
--- /dev/null
+++ b/dom/workers/test/WorkerDebuggerGlobalScope.reportError_childWorker.js
@@ -0,0 +1,5 @@
+"use strict";
+
+self.onerror = function () {
+ postMessage("error");
+}
diff --git a/dom/workers/test/WorkerDebuggerGlobalScope.reportError_debugger.js b/dom/workers/test/WorkerDebuggerGlobalScope.reportError_debugger.js
new file mode 100644
index 000000000..67ea08de5
--- /dev/null
+++ b/dom/workers/test/WorkerDebuggerGlobalScope.reportError_debugger.js
@@ -0,0 +1,12 @@
+"use strict";
+
+this.onmessage = function (event) {
+ switch (event.data) {
+ case "report":
+ reportError("reported");
+ break;
+ case "throw":
+ throw new Error("thrown");
+ break;
+ }
+};
diff --git a/dom/workers/test/WorkerDebuggerGlobalScope.reportError_worker.js b/dom/workers/test/WorkerDebuggerGlobalScope.reportError_worker.js
new file mode 100644
index 000000000..67ccfc2ca
--- /dev/null
+++ b/dom/workers/test/WorkerDebuggerGlobalScope.reportError_worker.js
@@ -0,0 +1,11 @@
+"use strict";
+
+var worker = new Worker("WorkerDebuggerGlobalScope.reportError_childWorker.js");
+
+worker.onmessage = function (event) {
+ postMessage("child:" + event.data);
+};
+
+self.onerror = function () {
+ postMessage("error");
+};
diff --git a/dom/workers/test/WorkerDebuggerGlobalScope.setImmediate_debugger.js b/dom/workers/test/WorkerDebuggerGlobalScope.setImmediate_debugger.js
new file mode 100644
index 000000000..b5075c70f
--- /dev/null
+++ b/dom/workers/test/WorkerDebuggerGlobalScope.setImmediate_debugger.js
@@ -0,0 +1,12 @@
+"use strict";
+
+this.onmessage = function (event) {
+ switch (event.data) {
+ case "ping":
+ setImmediate(function () {
+ postMessage("pong1");
+ });
+ postMessage("pong2");
+ break;
+ }
+};
diff --git a/dom/workers/test/WorkerDebuggerGlobalScope.setImmediate_worker.js b/dom/workers/test/WorkerDebuggerGlobalScope.setImmediate_worker.js
new file mode 100644
index 000000000..5a72b0f24
--- /dev/null
+++ b/dom/workers/test/WorkerDebuggerGlobalScope.setImmediate_worker.js
@@ -0,0 +1,3 @@
+"use strict"
+
+self.onmessage = function () {};
diff --git a/dom/workers/test/WorkerDebuggerManager_childWorker.js b/dom/workers/test/WorkerDebuggerManager_childWorker.js
new file mode 100644
index 000000000..8cee6809e
--- /dev/null
+++ b/dom/workers/test/WorkerDebuggerManager_childWorker.js
@@ -0,0 +1,3 @@
+"use strict";
+
+self.onmessage = function () {};
diff --git a/dom/workers/test/WorkerDebuggerManager_worker.js b/dom/workers/test/WorkerDebuggerManager_worker.js
new file mode 100644
index 000000000..0737d17eb
--- /dev/null
+++ b/dom/workers/test/WorkerDebuggerManager_worker.js
@@ -0,0 +1,3 @@
+"use strict";
+
+var worker = new Worker("WorkerDebuggerManager_childWorker.js");
diff --git a/dom/workers/test/WorkerDebugger_childWorker.js b/dom/workers/test/WorkerDebugger_childWorker.js
new file mode 100644
index 000000000..8cee6809e
--- /dev/null
+++ b/dom/workers/test/WorkerDebugger_childWorker.js
@@ -0,0 +1,3 @@
+"use strict";
+
+self.onmessage = function () {};
diff --git a/dom/workers/test/WorkerDebugger_frozen_iframe1.html b/dom/workers/test/WorkerDebugger_frozen_iframe1.html
new file mode 100644
index 000000000..591923121
--- /dev/null
+++ b/dom/workers/test/WorkerDebugger_frozen_iframe1.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="utf-8">
+ <script>
+ var worker = new Worker("WorkerDebugger_frozen_worker1.js");
+ worker.onmessage = function () {
+ parent.postMessage("ready", "*");
+ };
+ </script>
+ </head>
+ <body>
+ This is page 1.
+ </body>
+<html>
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 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="utf-8">
+ <script>
+ var worker = new Worker("WorkerDebugger_frozen_worker2.js");
+ worker.onmessage = function () {
+ parent.postMessage("ready", "*");
+ };
+ </script>
+ </head>
+ <body>
+ This is page 2.
+ </body>
+<html>
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 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1020226
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1020226</title>
+</head>
+<body>
+
+<script type="application/javascript">
+ var worker = new Worker("bug1020226_worker.js");
+ worker.onmessage = function(e) {
+ window.parent.postMessage("loaded", "*");
+ }
+</script>
+</body>
+</html>
+
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 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="utf-8"/>
+ </head>
+ <body>
+ </body>
+</html>
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 @@
+<script>
+
+new Worker("data:text/javascript;charset=UTF-8,self.addEventListener('',function(){},false);");
+
+</script>
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 @@
+<!DOCTYPE html>
+<script>
+
+function boom()
+{
+ var w = new Worker("data:text/javascript;charset=UTF-8,");
+ w.postMessage(new Blob([], {}));
+}
+
+</script>
+<body onload="boom();"></body>
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 @@
+<!DOCTYPE html>
+<script>
+
+function boom()
+{
+ var w;
+ for (var i = 0; i < 99; ++i) {
+ w = new SharedWorker("data:text/javascript;charset=UTF-8," + encodeURIComponent(i + ";"));
+ }
+ w.port.postMessage("");
+}
+
+</script>
+<body onload="boom();"></body>
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 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script>
+
+function boom()
+{
+ var x = new XMLHttpRequest();
+ x.open('GET', "data:text/plain,2", false);
+ x.send();
+
+ new Worker("data:text/javascript,3");
+}
+
+</script>
+</head>
+
+<body onload="boom();"></body>
+</html>
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 @@
+<!--
+Any copyright is dedicated to the Public Domain.
+http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE html>
+<script>
+// Using a DOM bindings object as a weak map key should not crash when attempting to
+// call the preserve wrapper callback.
+new Worker("data:text/javascript;charset=UTF-8,(new WeakMap()).set(self, 0);")
+</script>
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
--- /dev/null
+++ b/dom/workers/test/empty.html
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+ <meta charset="utf-8">
+ <body>
+ <script type="text/javascript">
+ var worker;
+
+ function start(workerCount, messageCallback) {
+ var seenWindowError;
+ window.onerror = function(message, filename, lineno) {
+ if (!seenWindowError) {
+ seenWindowError = true;
+ messageCallback({
+ type: "window",
+ data: { message: message, filename: filename, lineno: lineno }
+ });
+ return true;
+ }
+ };
+
+ worker = new Worker("errorPropagation_worker.js");
+
+ worker.onmessage = function(event) {
+ messageCallback(event.data);
+ };
+
+ var seenWorkerError;
+ worker.onerror = function(event) {
+ if (!seenWorkerError) {
+ seenWorkerError = true;
+ messageCallback({
+ type: "worker",
+ data: {
+ message: event.message,
+ filename: event.filename,
+ lineno: event.lineno
+ }
+ });
+ event.preventDefault();
+ }
+ };
+
+ worker.postMessage(workerCount);
+ }
+
+ function stop() {
+ worker.terminate();
+ }
+ </script>
+ </body>
+</html>
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 @@
+<?xml version="1.0"?>
+
+<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:em="http://www.mozilla.org/2004/em-rdf#">
+
+ <Description about="urn:mozilla:install-manifest">
+ <em:name>WorkerTestBootstrap</em:name>
+ <em:description>Worker functions for use in testing.</em:description>
+ <em:creator>Mozilla</em:creator>
+ <em:version>2016.03.09</em:version>
+ <em:id>workerbootstrap-test@mozilla.org</em:id>
+ <em:type>2</em:type>
+ <em:bootstrap>true</em:bootstrap>
+ <em:targetApplication>
+ <Description>
+ <!-- Firefox -->
+ <em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
+ <em:minVersion>45.0</em:minVersion>
+ <em:maxVersion>*</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+ <em:targetApplication>
+ <Description>
+ <!-- Fennec -->
+ <em:id>{aa3c5121-dab2-40e2-81ca-7ea25febc110}</em:id>
+ <em:minVersion>45.0</em:minVersion>
+ <em:maxVersion>*</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+ </Description>
+</RDF>
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
--- /dev/null
+++ b/dom/workers/test/extensions/bootstrap/workerbootstrap-test@mozilla.org.xpi
Binary files 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 @@
+<?xml version="1.0"?>
+
+<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:em="http://www.mozilla.org/2004/em-rdf#">
+
+ <Description about="urn:mozilla:install-manifest">
+ <em:name>WorkerTest</em:name>
+ <em:description>Worker functions for use in testing.</em:description>
+ <em:creator>Mozilla</em:creator>
+ <em:version>2016.03.09</em:version>
+ <em:id>worker-test@mozilla.org</em:id>
+ <em:type>2</em:type>
+ <em:targetApplication>
+ <Description>
+ <!-- Firefox -->
+ <em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
+ <em:minVersion>45.0</em:minVersion>
+ <em:maxVersion>*</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+ <em:targetApplication>
+ <Description>
+ <!-- Fennec -->
+ <em:id>{aa3c5121-dab2-40e2-81ca-7ea25febc110}</em:id>
+ <em:minVersion>45.0</em:minVersion>
+ <em:maxVersion>*</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+ </Description>
+</RDF>
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
--- /dev/null
+++ b/dom/workers/test/extensions/traditional/worker-test@mozilla.org.xpi
Binary files 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<ServiceWorkerRegistrationData>& TestGetData() { return mData; }
+};
+
+already_AddRefed<nsIFile>
+GetFile()
+{
+ nsCOMPtr<nsIFile> 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<nsIFile> file = GetFile();
+
+ nsCOMPtr<nsIOutputStream> 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<nsIFile> 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<ServiceWorkerRegistrarTest> swr = new ServiceWorkerRegistrarTest;
+
+ rv = swr->TestReadData();
+ ASSERT_EQ(NS_OK, rv) << "ReadData() should not fail";
+
+ const nsTArray<ServiceWorkerRegistrationData>& 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<ServiceWorkerRegistrarTest> swr = new ServiceWorkerRegistrarTest;
+
+ nsresult rv = swr->TestReadData();
+ ASSERT_NE(NS_OK, rv) << "ReadData() should fail if the file is empty";
+
+ const nsTArray<ServiceWorkerRegistrationData>& 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<ServiceWorkerRegistrarTest> swr = new ServiceWorkerRegistrarTest;
+
+ nsresult rv = swr->TestReadData();
+ ASSERT_EQ(NS_OK, rv) << "ReadData() should not fail when the version is correct";
+
+ const nsTArray<ServiceWorkerRegistrationData>& 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<ServiceWorkerRegistrarTest> swr = new ServiceWorkerRegistrarTest;
+
+ nsresult rv = swr->TestReadData();
+ ASSERT_NE(NS_OK, rv) << "ReadData() should fail when the version is not correct";
+
+ const nsTArray<ServiceWorkerRegistrationData>& 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<ServiceWorkerRegistrarTest> swr = new ServiceWorkerRegistrarTest;
+
+ nsresult rv = swr->TestReadData();
+ ASSERT_EQ(NS_OK, rv) << "ReadData() should not fail";
+
+ const nsTArray<ServiceWorkerRegistrationData>& 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<ServiceWorkerRegistrarTest> swr = new ServiceWorkerRegistrarTest;
+
+ swr->TestDeleteData();
+
+ nsCOMPtr<nsIFile> 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<ServiceWorkerRegistrarTest> 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<ServiceWorkerRegistrarTest> swr = new ServiceWorkerRegistrarTest;
+
+ nsresult rv = swr->TestReadData();
+ ASSERT_EQ(NS_OK, rv) << "ReadData() should not fail";
+
+ const nsTArray<ServiceWorkerRegistrationData>& 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<ServiceWorkerRegistrarTest> swr = new ServiceWorkerRegistrarTest;
+
+ nsresult rv = swr->TestReadData();
+ ASSERT_EQ(NS_OK, rv) << "ReadData() should not fail";
+
+ const nsTArray<ServiceWorkerRegistrationData>& 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<ServiceWorkerRegistrarTest> swr = new ServiceWorkerRegistrarTest;
+
+ nsresult rv = swr->TestReadData();
+ ASSERT_EQ(NS_OK, rv) << "ReadData() should not fail";
+
+ const nsTArray<ServiceWorkerRegistrationData>& 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<ServiceWorkerRegistrarTest> swr = new ServiceWorkerRegistrarTest;
+
+ nsresult rv = swr->TestReadData();
+ ASSERT_EQ(NS_OK, rv) << "ReadData() should not fail";
+
+ const nsTArray<ServiceWorkerRegistrationData>& 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<ServiceWorkerRegistrarTest> 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<ServiceWorkerRegistrarTest> swr = new ServiceWorkerRegistrarTest;
+
+ nsresult rv = swr->TestReadData();
+ ASSERT_EQ(NS_OK, rv) << "ReadData() should not fail";
+
+ // Duplicate entries should be removed.
+ const nsTArray<ServiceWorkerRegistrationData>& 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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE html>
+<script>
+ function ok(cond, msg) {
+ window.parent.postMessage({status: "ok", data: cond, msg: msg}, "*");
+ }
+ function finish() {
+ window.parent.postMessage({status: "done"}, "*");
+ }
+
+ function testSharedWorker() {
+ var sw = new SharedWorker("importForeignScripts_worker.js");
+ sw.port.onmessage = function(e) {
+ if (e.data == "finish") {
+ finish();
+ return;
+ }
+ ok(e.data === "good", "mixed content for shared workers is correctly blocked");
+ };
+
+ sw.onerror = function() {
+ ok(false, "Error on shared worker ");
+ };
+
+ sw.port.postMessage("start");
+ }
+
+ var worker = new Worker("importForeignScripts_worker.js");
+
+ worker.onmessage = function(e) {
+ if (e.data == "finish") {
+ testSharedWorker();
+ return;
+ }
+ ok(e.data === "good", "mixed content is correctly blocked");
+ }
+
+ worker.onerror = function() {
+ ok(false, "Error on worker");
+ }
+
+ worker.postMessage("start");
+</script>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>Test for SharedWorker</title>
+ </head>
+ <body>
+ <script type="text/javascript;version=1.7">
+ "use strict";
+
+ function debug(message) {
+ if (typeof(message) != "string") {
+ throw new Error("debug() only accepts strings!");
+ }
+ parent.postMessage(message, "*");
+ }
+
+ let worker;
+
+ window.addEventListener("message", function(event) {
+ if (!worker) {
+ worker = new SharedWorker("multi_sharedWorker_sharedWorker.js",
+ "FrameWorker");
+ worker.onerror = function(event) {
+ debug("Worker error: " + event.message);
+ event.preventDefault();
+
+ let data = {
+ type: "error",
+ message: event.message,
+ filename: event.filename,
+ lineno: event.lineno,
+ isErrorEvent: event instanceof ErrorEvent
+ };
+ parent.postMessage(data, "*");
+ };
+
+ worker.port.onmessage = function(event) {
+ debug("Worker message: " + JSON.stringify(event.data));
+ parent.postMessage(event.data, "*");
+ };
+ }
+
+ debug("Posting message: " + JSON.stringify(event.data));
+ worker.port.postMessage(event.data);
+ });
+ </script>
+ </body>
+</html>
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 @@
+<!DOCTYPE html>
+<html>
+<head>
+</head>
+<body onload="tests.next();">
+<script type="text/javascript;version=1.7">
+const SJS = "referrer_test_server.sjs?";
+const BASE_URL = "https://example.com/tests/dom/workers/test/" + SJS;
+const GET_RESULT = BASE_URL + 'ACTION=get-test-results';
+const RESET_STATE = BASE_URL + 'ACTION=resetState';
+
+function ok(val, message) {
+ val = val ? "true" : "false";
+ window.parent.postMessage("SimpleTest.ok(" + val + ", '" + message + "');", "*");
+}
+
+function info(val) {
+ window.parent.postMessage("SimpleTest.info(" + val + ");", "*");
+}
+
+function is(a, b, message) {
+ ok(a == b, message);
+}
+
+function finish() {
+ // Let window.onerror have a chance to fire
+ setTimeout(function() {
+ setTimeout(function() {
+ tests.close();
+ window.parent.postMessage("SimpleTest.finish();", "*");
+ }, 0);
+ }, 0);
+}
+
+var testCases = {
+ 'same-origin': { 'Referrer-Policy' : { 'default' : 'full',
+ 'origin' : 'origin',
+ 'origin-when-cross-origin' : 'full',
+ 'unsafe-url' : 'full',
+ 'same-origin' : 'full',
+ 'strict-origin' : 'origin',
+ 'strict-origin-when-cross-origin' : 'full',
+ 'no-referrer' : 'none',
+ 'unsafe-url, no-referrer' : 'none',
+ 'invalid' : 'full' }},
+
+ 'cross-origin': { 'Referrer-Policy' : { 'default' : 'full',
+ 'origin' : 'origin',
+ 'origin-when-cross-origin' : 'origin',
+ 'unsafe-url' : 'full',
+ 'same-origin' : 'none',
+ 'strict-origin' : 'origin',
+ 'strict-origin-when-cross-origin' : 'origin',
+ 'no-referrer' : 'none',
+ 'unsafe-url, no-referrer' : 'none',
+ 'invalid' : 'full' }},
+
+ // Downgrading in worker is blocked entirely without unblock option
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=1198078#c17
+ // Skip the downgrading test
+ /* 'downgrade': { 'Referrer-Policy' : { 'default' : 'full',
+ 'origin' : 'full',
+ 'origin-when-cross-origin"' : 'full',
+ 'unsafe-url' : 'full',
+ 'same-origin' : 'none',
+ 'strict-origin' : 'none',
+ 'strict-origin-when-cross-origin' : 'none',
+ 'no-referrer' : 'full',
+ 'unsafe-url, no-referrer' : 'none',
+ 'invalid' : 'full' }}, */
+
+
+};
+
+var advance = function() { tests.next(); };
+
+/**
+ * helper to perform an XHR
+ * to do checkIndividualResults and resetState
+ */
+function doXHR(aUrl, onSuccess, onFail) {
+ var xhr = new XMLHttpRequest({mozSystem: true});
+ xhr.responseType = "json";
+ xhr.onload = function () {
+ onSuccess(xhr);
+ };
+ xhr.onerror = function () {
+ onFail(xhr);
+ };
+ xhr.open('GET', aUrl, true);
+ xhr.send(null);
+}
+
+
+function resetState() {
+ doXHR(RESET_STATE,
+ advance,
+ function(xhr) {
+ ok(false, "error in reset state");
+ finish();
+ });
+}
+
+function checkIndividualResults(aType, aPolicy, aExpected) {
+ var onload = xhr => {
+ var results = xhr.response;
+ dump(JSON.stringify(xhr.response));
+ // test id equals type + "-" + policy
+ // Ex: same-origin-default
+ var id = aType + "-" + aPolicy;
+ ok(id in results, id + " tests have to be performed.");
+ is(results[id].policy, aExpected, id + ' --- ' + results[id].policy + ' (' + results[id].referrer + ')');
+ advance();
+ };
+ var onerror = xhr => {
+ ok(false, "Can't get results from the counter server.");
+ finish();
+ };
+ doXHR(GET_RESULT, onload, onerror);
+}
+
+var tests = (function() {
+
+ for (var type in testCases) {
+ for (var policy in testCases[type]['Referrer-Policy']) {
+ yield resetState();
+ var searchParams = new URLSearchParams();
+ searchParams.append("TYPE", type);
+ searchParams.append("ACTION", "test");
+ searchParams.append("Referrer-Policy", policy);
+ var worker = new Worker(BASE_URL + searchParams.toString());
+ worker.onmessage = function () {
+ advance();
+ };
+ yield worker.postMessage(42);
+ yield checkIndividualResults(type, policy, escape(testCases[type]['Referrer-Policy'][policy]));
+ }
+ }
+
+ // complete. Be sure to yield so we don't call this twice.
+ yield finish();
+})();
+</script>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+</head>
+<body>
+<script type="text/javascript">
+addEventListener('load', function(event) {
+ navigator.serviceWorker.register('force_refresh_browser_worker.js').then(function(swr) {
+ if (!swr) {
+ return;
+ }
+ var custom = new Event('base-register', { bubbles: true });
+ document.dispatchEvent(custom);
+ });
+
+ navigator.serviceWorker.ready.then(function() {
+ var custom = new Event('base-sw-ready', { bubbles: true });
+ document.dispatchEvent(custom);
+ });
+
+ var custom = new Event('base-load', { bubbles: true });
+ document.dispatchEvent(custom);
+});
+</script>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+</head>
+<body>
+<script type="text/javascript">
+function ok(exp, msg) {
+ if (!exp) {
+ throw(msg);
+ }
+}
+
+function is(actual, expected, msg) {
+ if (actual !== expected) {
+ throw('got "' + actual + '", but expected "' + expected + '" - ' + msg);
+ }
+}
+
+function fail(err) {
+ var custom = new CustomEvent('cached-failure', {
+ bubbles: true,
+ detail: err
+ });
+ document.dispatchEvent(custom);
+}
+
+function getUncontrolledClients(sw) {
+ return new Promise(function(resolve, reject) {
+ navigator.serviceWorker.addEventListener('message', function onMsg(evt) {
+ if (evt.data.type === 'CLIENTS') {
+ navigator.serviceWorker.removeEventListener('message', onMsg);
+ resolve(evt.data.detail);
+ }
+ });
+ sw.postMessage({ type: 'GET_UNCONTROLLED_CLIENTS' })
+ });
+}
+
+addEventListener('load', function(event) {
+ if (!navigator.serviceWorker.controller) {
+ return fail(window.location.href + ' is not controlled!');
+ }
+
+ getUncontrolledClients(navigator.serviceWorker.controller)
+ .then(function(clientList) {
+ is(clientList.length, 1, 'should only have one client');
+ is(clientList[0].url, window.location.href,
+ 'client url should match current window');
+ is(clientList[0].frameType, 'top-level',
+ 'client should be a top-level window');
+ var custom = new Event('cached-load', { bubbles: true });
+ document.dispatchEvent(custom);
+ })
+ .catch(function(err) {
+ fail(err);
+ });
+});
+</script>
+</body>
+</html>
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 @@
+<html>
+ <body>
+ <script language="javascript">
+ function fail(msg) {
+ window.parent.postMessage({ status: "failed", message: msg }, "*");
+ }
+
+ function success(msg) {
+ window.parent.postMessage({ status: "success", message: msg }, "*");
+ }
+
+ if (!window.parent) {
+ dump("This file must be embedded in an iframe!");
+ }
+
+ navigator.serviceWorker.getRegistration()
+ .then(function(reg) {
+ if (!reg) {
+ navigator.serviceWorker.ready.then(function(reg) {
+ if (reg.active.state == "activating") {
+ reg.active.onstatechange = function(e) {
+ reg.active.onstatechange = null;
+ if (reg.active.state == "activated") {
+ success("Registered and activated");
+ }
+ }
+ } else {
+ success("Registered and activated");
+ }
+ });
+ navigator.serviceWorker.register("bug1151916_worker.js",
+ { scope: "." });
+ } else {
+ // Simply force the sw to load a resource and touch self.caches.
+ if (!reg.active) {
+ fail("no-active-worker");
+ return;
+ }
+
+ fetch("madeup.txt").then(function(res) {
+ res.text().then(function(v) {
+ if (v == "Hi there") {
+ success("Loaded from cache");
+ } else {
+ fail("Response text did not match");
+ }
+ }, fail);
+ }, fail);
+ }
+ }, fail);
+ </script>
+ </body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1130684 - claim client </title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+<script class="testbody" type="text/javascript">
+
+ if (!parent) {
+ info("This page shouldn't be launched directly!");
+ }
+
+ window.onload = function() {
+ parent.postMessage("READY", "*");
+ }
+
+ navigator.serviceWorker.oncontrollerchange = function() {
+ parent.postMessage({
+ event: "controllerchange",
+ controller: (navigator.serviceWorker.controller !== null)
+ }, "*");
+ }
+
+ navigator.serviceWorker.onmessage = function(e) {
+ parent.postMessage({
+ event: "message",
+ data: e.data
+ }, "*");
+ }
+
+</script>
+</pre>
+</body>
+</html>
+
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 94048 - test install event.</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+<script class="testbody" type="text/javascript">
+
+ // Make sure to use good, unique messages, since the actual expression will not show up in test results.
+ function my_ok(result, msg) {
+ parent.postMessage({status: "ok", result: result, message: msg}, "*");
+ }
+
+ function finish() {
+ parent.postMessage({status: "done"}, "*");
+ }
+
+ navigator.serviceWorker.ready.then(function(swr) {
+ my_ok(swr.scope.match(/serviceworkers\/control$/),
+ "This page should be controlled by upper level registration");
+ my_ok(swr.installing == undefined,
+ "Upper level registration should not have a installing worker.");
+ if (navigator.serviceWorker.controller) {
+ // We are controlled.
+ // Register a new worker for this sub-scope. After that, controller should still be for upper level, but active should change to be this scope's.
+ navigator.serviceWorker.register("../worker2.js", { scope: "./" }).then(function(e) {
+ my_ok("installing" in e, "ServiceWorkerRegistration.installing exists.");
+ my_ok(e.installing instanceof ServiceWorker, "ServiceWorkerRegistration.installing is a ServiceWorker.");
+
+ my_ok("waiting" in e, "ServiceWorkerRegistration.waiting exists.");
+ my_ok("active" in e, "ServiceWorkerRegistration.active exists.");
+
+ my_ok(e.installing &&
+ e.installing.scriptURL.match(/worker2.js$/),
+ "Installing is serviceworker/controller");
+
+ my_ok("scope" in e, "ServiceWorkerRegistration.scope exists.");
+ my_ok(e.scope.match(/serviceworkers\/controller\/$/), "Scope is serviceworker/controller " + e.scope);
+
+ my_ok("unregister" in e, "ServiceWorkerRegistration.unregister exists.");
+
+ my_ok(navigator.serviceWorker.controller.scriptURL.match(/worker\.js$/),
+ "Controller is still worker.js");
+
+ e.unregister().then(function(result) {
+ my_ok(result, "Unregistering the SW should succeed");
+ finish();
+ }, function(e) {
+ dump("Error unregistering the SW: " + e + "\n");
+ });
+ });
+ } else {
+ my_ok(false, "Should've been controlled!");
+ finish();
+ }
+ }).catch(function(e) {
+ my_ok(false, "Some test threw an error " + e);
+ finish();
+ });
+</script>
+</pre>
+</body>
+</html>
+
+
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 @@
+<!DOCTYPE HTML>
+<title>Shared workers: create antoehr sharedworekr client</title>
+<pre id=log>Hello World</pre>
+<script>
+ var worker = new SharedWorker('sharedWorker_fetch.js');
+</script>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+</head>
+<body>
+<script type="text/javascript">
+
+function wait_until_controlled() {
+ return new Promise(function(resolve) {
+ if (navigator.serviceWorker.controller) {
+ return resolve();
+ }
+ navigator.serviceWorker.addEventListener('controllerchange', function onController() {
+ if (navigator.serviceWorker.controller) {
+ navigator.serviceWorker.removeEventListener('controllerchange', onController);
+ return resolve();
+ }
+ });
+ });
+}
+addEventListener('load', function(event) {
+ var registration;
+ navigator.serviceWorker.register('worker.js').then(function(swr) {
+ registration = swr;
+
+ // While the iframe below is a navigation, we still wait until we are
+ // controlled here. We want an active client to hold the service worker
+ // alive since it calls unregister() on itself.
+ return wait_until_controlled();
+
+ }).then(function() {
+ var frame = document.createElement('iframe');
+ document.body.appendChild(frame);
+ frame.src = 'fake_download';
+
+ // The service worker is unregistered in the fetch event. The window and
+ // frame are cleaned up from the browser chrome script.
+ });
+});
+</script>
+</body>
+</html>
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
--- /dev/null
+++ b/dom/workers/test/serviceworkers/empty.js
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1182103 - Test EventSource scenarios with fetch interception</title>
+ <script type="text/javascript">
+
+ var prefix = "http://mochi.test:8888/tests/dom/workers/test/serviceworkers/eventsource/";
+
+ function ok(aCondition, aMessage) {
+ parent.postMessage({status: "callback", data: "ok", condition: aCondition, message: aMessage}, "*");
+ }
+
+ function doUnregister() {
+ navigator.serviceWorker.getRegistration().then(swr => {
+ swr.unregister().then(function(result) {
+ ok(result, "Unregister should return true.");
+ parent.postMessage({status: "callback", data: "done"}, "*");
+ }, function(e) {
+ ok(false, "Unregistering the SW failed with " + e);
+ });
+ });
+ }
+
+ function doEventSource() {
+ var source = new EventSource(prefix + "eventsource.resource");
+ source.onmessage = function(e) {
+ source.onmessage = null;
+ source.close();
+ ok(true, "EventSource should work with cors responses");
+ doUnregister();
+ };
+ source.onerror = function(error) {
+ source.onerror = null;
+ source.close();
+ ok(false, "Something went wrong");
+ };
+ }
+
+ function onLoad() {
+ if (!parent) {
+ dump("eventsource/eventsource_cors_response.html shouldn't be launched directly!");
+ }
+
+ window.addEventListener("message", function onMessage(e) {
+ if (e.data.status === "callback") {
+ switch(e.data.data) {
+ case "eventsource":
+ doEventSource();
+ window.removeEventListener("message", onMessage);
+ break;
+ default:
+ ok(false, "Something went wrong")
+ break
+ }
+ }
+ });
+
+ navigator.serviceWorker.ready.then(function() {
+ parent.postMessage({status: "callback", data: "ready"}, "*");
+ });
+
+ navigator.serviceWorker.addEventListener("message", function(event) {
+ parent.postMessage(event.data, "*");
+ });
+ }
+
+ </script>
+</head>
+<body onload="onLoad()">
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1182103 - Test EventSource scenarios with fetch interception</title>
+ <script type="text/javascript">
+
+ var prefix = "https://example.com/tests/dom/workers/test/serviceworkers/eventsource/";
+
+ function ok(aCondition, aMessage) {
+ parent.postMessage({status: "callback", data: "ok", condition: aCondition, message: aMessage}, "*");
+ }
+
+ function doUnregister() {
+ navigator.serviceWorker.getRegistration().then(swr => {
+ swr.unregister().then(function(result) {
+ ok(result, "Unregister should return true.");
+ parent.postMessage({status: "callback", data: "done"}, "*");
+ }, function(e) {
+ ok(false, "Unregistering the SW failed with " + e);
+ });
+ });
+ }
+
+ function doEventSource() {
+ var source = new EventSource(prefix + "eventsource.resource");
+ source.onmessage = function(e) {
+ source.onmessage = null;
+ source.close();
+ ok(false, "Something went wrong");
+ };
+ source.onerror = function(error) {
+ source.onerror = null;
+ source.close();
+ ok(true, "EventSource should not work with mixed content cors responses");
+ doUnregister();
+ };
+ }
+
+ function onLoad() {
+ if (!parent) {
+ dump("eventsource/eventsource_cors_response.html shouldn't be launched directly!");
+ }
+
+ window.addEventListener("message", function onMessage(e) {
+ if (e.data.status === "callback") {
+ switch(e.data.data) {
+ case "eventsource":
+ doEventSource();
+ window.removeEventListener("message", onMessage);
+ break;
+ default:
+ ok(false, "Something went wrong")
+ break
+ }
+ }
+ });
+
+ navigator.serviceWorker.ready.then(function() {
+ parent.postMessage({status: "callback", data: "ready"}, "*");
+ });
+
+ navigator.serviceWorker.addEventListener("message", function(event) {
+ parent.postMessage(event.data, "*");
+ });
+ }
+
+ </script>
+</head>
+<body onload="onLoad()">
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1182103 - Test EventSource scenarios with fetch interception</title>
+ <script type="text/javascript">
+
+ var prefix = "http://mochi.test:8888/tests/dom/workers/test/serviceworkers/eventsource/";
+
+ function ok(aCondition, aMessage) {
+ parent.postMessage({status: "callback", data: "ok", condition: aCondition, message: aMessage}, "*");
+ }
+
+ function doUnregister() {
+ navigator.serviceWorker.getRegistration().then(swr => {
+ swr.unregister().then(function(result) {
+ ok(result, "Unregister should return true.");
+ parent.postMessage({status: "callback", data: "done"}, "*");
+ }, function(e) {
+ ok(false, "Unregistering the SW failed with " + e);
+ });
+ });
+ }
+
+ function doEventSource() {
+ var source = new EventSource(prefix + "eventsource.resource");
+ source.onmessage = function(e) {
+ source.onmessage = null;
+ source.close();
+ ok(false, "Something went wrong");
+ };
+ source.onerror = function(error) {
+ source.onerror = null;
+ source.close();
+ ok(true, "EventSource should not work with opaque responses");
+ doUnregister();
+ };
+ }
+
+ function onLoad() {
+ if (!parent) {
+ dump("eventsource/eventsource_opaque_response.html shouldn't be launched directly!");
+ }
+
+ window.addEventListener("message", function onMessage(e) {
+ if (e.data.status === "callback") {
+ switch(e.data.data) {
+ case "eventsource":
+ doEventSource();
+ window.removeEventListener("message", onMessage);
+ break;
+ default:
+ ok(false, "Something went wrong")
+ break
+ }
+ }
+ });
+
+ navigator.serviceWorker.ready.then(function() {
+ parent.postMessage({status: "callback", data: "ready"}, "*");
+ });
+
+ navigator.serviceWorker.addEventListener("message", function(event) {
+ parent.postMessage(event.data, "*");
+ });
+ }
+
+ </script>
+</head>
+<body onload="onLoad()">
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1182103 - Test EventSource scenarios with fetch interception</title>
+ <script type="text/javascript">
+
+ function getURLParam (aTarget, aValue) {
+ return decodeURI(aTarget.search.replace(new RegExp("^(?:.*[&\\?]" + encodeURI(aValue).replace(/[\.\+\*]/g, "\\$&") + "(?:\\=([^&]*))?)?.*$", "i"), "$1"));
+ }
+
+ function onLoad() {
+ navigator.serviceWorker.ready.then(function() {
+ parent.postMessage({status: "callback", data: "done"}, "*");
+ });
+
+ navigator.serviceWorker.register(getURLParam(document.location, "script"), {scope: "."});
+ }
+
+ </script>
+</head>
+<body onload="onLoad()">
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1182103 - Test EventSource scenarios with fetch interception</title>
+ <script type="text/javascript">
+
+ var prefix = "http://mochi.test:8888/tests/dom/workers/test/serviceworkers/eventsource/";
+
+ function ok(aCondition, aMessage) {
+ parent.postMessage({status: "callback", data: "ok", condition: aCondition, message: aMessage}, "*");
+ }
+
+ function doUnregister() {
+ navigator.serviceWorker.getRegistration().then(swr => {
+ swr.unregister().then(function(result) {
+ ok(result, "Unregister should return true.");
+ parent.postMessage({status: "callback", data: "done"}, "*");
+ }, function(e) {
+ ok(false, "Unregistering the SW failed with " + e);
+ });
+ });
+ }
+
+ function doEventSource() {
+ var source = new EventSource(prefix + "eventsource.resource");
+ source.onmessage = function(e) {
+ source.onmessage = null;
+ source.close();
+ ok(true, "EventSource should work with synthetic responses");
+ doUnregister();
+ };
+ source.onerror = function(error) {
+ source.onmessage = null;
+ source.close();
+ ok(false, "Something went wrong");
+ };
+ }
+
+ function onLoad() {
+ if (!parent) {
+ dump("eventsource/eventsource_synthetic_response.html shouldn't be launched directly!");
+ }
+
+ window.addEventListener("message", function onMessage(e) {
+ if (e.data.status === "callback") {
+ switch(e.data.data) {
+ case "eventsource":
+ doEventSource();
+ window.removeEventListener("message", onMessage);
+ break;
+ default:
+ ok(false, "Something went wrong")
+ break
+ }
+ }
+ });
+
+ navigator.serviceWorker.ready.then(function() {
+ parent.postMessage({status: "callback", data: "ready"}, "*");
+ });
+
+ navigator.serviceWorker.addEventListener("message", function(event) {
+ parent.postMessage(event.data, "*");
+ });
+ }
+
+ </script>
+</head>
+<body onload="onLoad()">
+</body>
+</html>
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("<link rel=stylesheet href=style.css>");
+}
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 @@
+<!DOCTYPE html>
+<script>
+ var isAndroid = navigator.userAgent.includes("Android");
+ var isB2G = !isAndroid && /Mobile|Tablet/.test(navigator.userAgent);
+
+ function ok(v, msg) {
+ window.parent.postMessage({status: "ok", result: !!v, message: msg}, "*");
+ }
+
+ function is(a, b, msg) {
+ ok(a === b, msg + ", expected '" + b + "', got '" + a + "'");
+ }
+
+ function todo(v, msg) {
+ window.parent.postMessage({status: "todo", result: !!v, message: msg}, "*");
+ }
+
+ function finish() {
+ window.parent.postMessage({status: "done"}, "*");
+ }
+
+ function testFetch() {
+ return fetch("fetch.txt").then(function(r) {
+ return r.text();
+ }).then(function(body) {
+ is(body, "so fetch", "A fetch() Request should have the 'fetch' context");
+ });
+ }
+
+ function testImage() {
+ return new Promise(function(resolve, reject) {
+ var img = document.createElement("img");
+ img.src = "img.jpg";
+ // The service worker will respond with an existing image only if the
+ // Request has the correct context, otherwise the Promise will get
+ // rejected and the test will fail.
+ img.onload = resolve;
+ img.onerror = reject;
+ });
+ }
+
+ function testImageSrcSet() {
+ return new Promise(function(resolve, reject) {
+ var img = document.createElement("img");
+ img.srcset = "responsive.jpg 100w";
+ // The service worker will respond with an existing image only if the
+ // Request has the correct context, otherwise the Promise will get
+ // rejected and the test will fail.
+ img.onload = resolve;
+ img.onerror = reject;
+ });
+ }
+
+ function testPicture() {
+ return new Promise(function(resolve, reject) {
+ var pic = document.createElement("picture");
+ var img = document.createElement("img");
+ pic.appendChild(img);
+ img.src = "responsive.jpg?picture";
+ // The service worker will respond with an existing image only if the
+ // Request has the correct context, otherwise the Promise will get
+ // rejected and the test will fail.
+ img.onload = resolve;
+ img.onerror = reject;
+ });
+ }
+
+ function testAudio() {
+ return new Promise(function(resolve, reject) {
+ var audio = document.createElement("audio");
+ audio.src = "audio.ogg";
+ audio.preload = "metadata";
+ // The service worker will respond with an existing audio only if the
+ // Request has the correct context, otherwise the Promise will get
+ // rejected and the test will fail.
+ audio.onloadedmetadata = resolve;
+ audio.onerror = reject;
+ });
+ }
+
+ function testVideo() {
+ return new Promise(function(resolve, reject) {
+ var video = document.createElement("video");
+ video.src = "video.ogg";
+ video.preload = "metadata";
+ // The service worker will respond with an existing video only if the
+ // Request has the correct context, otherwise the Promise will get
+ // rejected and the test will fail.
+ video.onloadedmetadata = resolve;
+ video.onerror = reject;
+ });
+ }
+
+ function testBeacon() {
+ ok(navigator.sendBeacon("beacon.sjs"), "Sending the beacon should succeed");
+ // query the context from beacon.sjs
+ return fetch("beacon.sjs?queryContext")
+ .then(function(r) {
+ return r.text();
+ }).then(function(body) {
+ is(body, "beacon", "The context for the intercepted beacon should be correct");
+ });
+ }
+
+ function testCSPReport() {
+ return new Promise(function(resolve, reject) {
+ var iframe = document.createElement("iframe");
+ iframe.src = "csp-violate.sjs";
+ document.documentElement.appendChild(iframe);
+ navigator.serviceWorker.addEventListener("message", function onMessage(e) {
+ if (e.data.data == "csp-report") {
+ is(e.data.context, "cspreport", "Expected the cspreport context on a CSP violation report");
+ navigator.serviceWorker.removeEventListener("message", onMessage);
+ resolve();
+ }
+ }, false);
+ });
+ }
+
+ function testEmbed() {
+ return Promise.resolve().then(function() {
+ todo(false, "<embed> tag is not currently intercepted. See Bug 1168676.");
+ });
+
+ return new Promise(function(resolve, reject) {
+ var embed = document.createElement("embed");
+ embed.src = "embed";
+ document.documentElement.appendChild(embed);
+ navigator.serviceWorker.addEventListener("message", function onMessage(e) {
+ if (e.data.data == "embed") {
+ is(e.data.context, "embed", "Expected the object context on an embed");
+ navigator.serviceWorker.removeEventListener("message", onMessage);
+ resolve();
+ }
+ }, false);
+ });
+ }
+
+ function testObject() {
+ return Promise.resolve().then(function() {
+ todo(false, "<object> tag is not currently intercepted. See Bug 1168676");
+ });
+
+ return new Promise(function(resolve, reject) {
+ var object = document.createElement("object");
+ object.data = "object";
+ document.documentElement.appendChild(object);
+ navigator.serviceWorker.addEventListener("message", function onMessage(e) {
+ if (e.data.data == "object") {
+ is(e.data.context, "object", "Expected the object context on an object");
+ navigator.serviceWorker.removeEventListener("message", onMessage);
+ resolve();
+ }
+ }, false);
+ });
+ }
+
+ function testFont() {
+ return new Promise(function(resolve, reject) {
+ var css = '@font-face { font-family: "sw-font"; src: url("font"); }';
+ css += '* { font-family: "sw-font"; }';
+ var style = document.createElement("style");
+ style.appendChild(document.createTextNode(css));
+ document.documentElement.appendChild(style);
+ navigator.serviceWorker.addEventListener("message", function onMessage(e) {
+ if (e.data.data == "font") {
+ is(e.data.context, "font", "Expected the font context on an font");
+ navigator.serviceWorker.removeEventListener("message", onMessage);
+ resolve();
+ }
+ }, false);
+ });
+ }
+
+ function testIFrame() {
+ return new Promise(function(resolve, reject) {
+ var iframe = document.createElement("iframe");
+ iframe.src = "iframe";
+ document.documentElement.appendChild(iframe);
+ // The service worker will respond with an existing document only if the
+ // Request has the correct context, otherwise the Promise will get
+ // rejected and the test will fail.
+ iframe.onload = resolve;
+ iframe.onerror = reject;
+ });
+ }
+
+ function testFrame() {
+ return new Promise(function(resolve, reject) {
+ var frame = document.createElement("frame");
+ frame.src = "frame";
+ document.documentElement.appendChild(frame);
+ // The service worker will respond with an existing document only if the
+ // Request has the correct context, otherwise the Promise will get
+ // rejected and the test will fail.
+ frame.onload = resolve;
+ frame.onerror = reject;
+ });
+ }
+
+ function testInternal() {
+ if (isB2G) {
+ // We can't open new windows on b2g, so skip this part of the test there.
+ return Promise.resolve();
+ }
+ return new Promise(function(resolve, reject) {
+ // Test this with a new window opened through script. There are of course
+ // other possible ways of testing this too.
+ var win = window.open("newwindow", "_blank", "width=100,height=100");
+ navigator.serviceWorker.addEventListener("message", function onMessage(e) {
+ if (e.data.data == "newwindow") {
+ is(e.data.context, "internal", "Expected the internal context on a newly opened window");
+ navigator.serviceWorker.removeEventListener("message", onMessage);
+ win.close();
+ resolve();
+ }
+ }, false);
+ });
+ }
+
+ function testPing() {
+ return new Promise(function(resolve, reject) {
+ var iframe = document.createElement("iframe");
+ iframe.src = "ping.html";
+ document.documentElement.appendChild(iframe);
+ navigator.serviceWorker.addEventListener("message", function onMessage(e) {
+ if (e.data.data == "ping") {
+ is(e.data.context, "ping", "Expected the ping context on an anchor ping");
+ navigator.serviceWorker.removeEventListener("message", onMessage);
+ resolve();
+ }
+ }, false);
+ });
+ }
+
+ function testPlugin() {
+ return Promise.resolve().then(function() {
+ todo(false, "plugins are not currently intercepted. See Bug 1168676.");
+ });
+
+ var isMobile = /Mobile|Tablet/.test(navigator.userAgent);
+ if (isMobile || parent.isMulet()) {
+ // We can't use plugins on mobile, so skip this part of the test there.
+ return Promise.resolve();
+ }
+
+ return new Promise(function(resolve, reject) {
+ var embed = document.createElement("embed");
+ embed.type = "application/x-test";
+ embed.setAttribute("posturl", "plugin");
+ embed.setAttribute("postmode", "stream");
+ embed.setAttribute("streammode", "normal");
+ embed.setAttribute("src", "fetch.txt");
+ document.documentElement.appendChild(embed);
+ navigator.serviceWorker.addEventListener("message", function onMessage(e) {
+ if (e.data.data == "plugin") {
+ is(e.data.context, "plugin", "Expected the plugin context on a request coming from a plugin");
+ navigator.serviceWorker.removeEventListener("message", onMessage);
+ // Without this, the test leaks in e10s!
+ embed.parentNode.removeChild(embed);
+ resolve();
+ }
+ }, false);
+ });
+ }
+
+ function testScript() {
+ return new Promise(function(resolve, reject) {
+ var script = document.createElement("script");
+ script.src = "script.js";
+ document.documentElement.appendChild(script);
+ // The service worker will respond with an existing script only if the
+ // Request has the correct context, otherwise the Promise will get
+ // rejected and the test will fail.
+ script.onload = resolve;
+ script.onerror = reject;
+ });
+ }
+
+ function testStyle() {
+ return new Promise(function(resolve, reject) {
+ var link = document.createElement("link");
+ link.rel = "stylesheet";
+ link.href = "style.css";
+ document.documentElement.appendChild(link);
+ navigator.serviceWorker.addEventListener("message", function onMessage(e) {
+ if (e.data.data == "style") {
+ is(e.data.context, "style", "Expected the style context on a request coming from a stylesheet");
+ navigator.serviceWorker.removeEventListener("message", onMessage);
+ resolve();
+ }
+ }, false);
+ });
+ }
+
+ function testTrack() {
+ return new Promise(function(resolve, reject) {
+ var video = document.createElement("video");
+ var track = document.createElement("track");
+ track.src = "track";
+ video.appendChild(track);
+ document.documentElement.appendChild(video);
+ navigator.serviceWorker.addEventListener("message", function onMessage(e) {
+ if (e.data.data == "track") {
+ is(e.data.context, "track", "Expected the track context on a request coming from a track");
+ navigator.serviceWorker.removeEventListener("message", onMessage);
+ resolve();
+ }
+ }, false);
+ });
+ }
+
+ function testXHR() {
+ return new Promise(function(resolve, reject) {
+ var xhr = new XMLHttpRequest();
+ xhr.open("get", "xhr", true);
+ xhr.send();
+ // The service worker will respond with an existing resource only if the
+ // Request has the correct context, otherwise the Promise will get
+ // rejected and the test will fail.
+ xhr.onload = resolve;
+ xhr.onerror = reject;
+ });
+ }
+
+ function testXSLT() {
+ return new Promise(function(resolve, reject) {
+ var iframe = document.createElement("iframe");
+ iframe.src = "xml.xml";
+ document.documentElement.appendChild(iframe);
+ navigator.serviceWorker.addEventListener("message", function onMessage(e) {
+ if (e.data.data == "xslt") {
+ is(e.data.context, "xslt", "Expected the xslt context on an XSLT stylesheet");
+ navigator.serviceWorker.removeEventListener("message", onMessage);
+ // Without this, the test leaks in e10s!
+ iframe.parentNode.removeChild(iframe);
+ resolve();
+ }
+ }, false);
+ });
+ }
+
+ function testWorker() {
+ return new Promise(function(resolve, reject) {
+ var worker = new Worker("myworker");
+ worker.onmessage = function(e) {
+ if (e.data == "ack") {
+ worker.terminate();
+ resolve();
+ }
+ };
+ worker.onerror = reject;
+ });
+ }
+
+ function testNestedWorker() {
+ return new Promise(function(resolve, reject) {
+ var worker = new Worker("myparentworker");
+ worker.onmessage = function(e) {
+ if (e.data == "ack") {
+ worker.terminate();
+ resolve();
+ }
+ };
+ worker.onerror = reject;
+ });
+ }
+
+ function testSharedWorker() {
+ return new Promise(function(resolve, reject) {
+ var worker = new SharedWorker("mysharedworker");
+ worker.port.start();
+ worker.port.onmessage = function(e) {
+ if (e.data == "ack") {
+ resolve();
+ }
+ };
+ worker.onerror = reject;
+ });
+ }
+
+ function testNestedWorkerInSharedWorker() {
+ return new Promise(function(resolve, reject) {
+ var worker = new SharedWorker("myparentsharedworker");
+ worker.port.start();
+ worker.port.onmessage = function(e) {
+ if (e.data == "ack") {
+ resolve();
+ }
+ };
+ worker.onerror = reject;
+ });
+ }
+
+ function testCache() {
+ return new Promise(function(resolve, reject) {
+ // Issue an XHR that will be intercepted by the SW in order to start off
+ // the test with a RequestContext value that is not the default ("fetch").
+ // This needs to run inside a fetch event handler because synthesized
+ // RequestContext objects can only have the "fetch" context, and we'd
+ // prefer to test the more general case of some other RequestContext value.
+ var xhr = new XMLHttpRequest();
+ xhr.open("get", "cache", true);
+ xhr.send();
+ navigator.serviceWorker.addEventListener("message", function onMessage(e) {
+ if (e.data.data == "cache") {
+ ok(e.data.success, "The RequestContext can be persisted in the cache.");
+ navigator.serviceWorker.removeEventListener("message", onMessage);
+ resolve();
+ }
+ }, false);
+ });
+ }
+
+ var testName = location.search.substr(1);
+ window[testName]().then(function() {
+ finish();
+ }, function(e) {
+ ok(false, "A promise was rejected: " + e);
+ finish();
+ });
+</script>
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 @@
+<!DOCTYPE html>
+<script>
+ onload = function() {
+ document.querySelector("a").click();
+ };
+</script>
+<a ping="ping" href="fetch.txt">link</a>
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
--- /dev/null
+++ b/dom/workers/test/serviceworkers/fetch/context/realaudio.ogg
Binary files 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
--- /dev/null
+++ b/dom/workers/test/serviceworkers/fetch/context/realimg.jpg
Binary files 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 @@
+<!DOCTYPE html>
+<script>
+ function ok(v, msg) {
+ window.parent.postMessage({status: "ok", result: !!v, message: msg}, "*");
+ }
+
+ function done(reg) {
+ ok(reg.active, "The active worker should be available.");
+ window.parent.postMessage({status: "registrationdone"}, "*");
+ }
+
+ navigator.serviceWorker.ready.then(done);
+ navigator.serviceWorker.register("context_test.js", {scope: "."});
+</script>
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 @@
+<!DOCTYPE html>
+<script>
+ navigator.serviceWorker.getRegistration(".").then(function(registration) {
+ registration.unregister().then(function(success) {
+ if (success) {
+ window.parent.postMessage({status: "unregistrationdone"}, "*");
+ }
+ }, function(e) {
+ dump("Unregistering the SW failed with " + e + "\n");
+ });
+ });
+</script>
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 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="xslt"?>
+<root/>
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 === "<res>hello pass</res>\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 @@
+<!DOCTYPE html>
+<script>
+ window.onmessage = function(e) {
+ window.parent.postMessage(e.data, "*");
+ };
+</script>
+<iframe src="http://example.com/tests/dom/workers/test/serviceworkers/fetch/hsts/index.html"></iframe>
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
--- /dev/null
+++ b/dom/workers/test/serviceworkers/fetch/hsts/image-20px.png
Binary files 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
--- /dev/null
+++ b/dom/workers/test/serviceworkers/fetch/hsts/image-40px.png
Binary files 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 @@
+<!DOCTYPE html>
+<script>
+onload=function(){
+ var img = new Image();
+ img.src = "http://example.com/tests/dom/workers/test/serviceworkers/fetch/hsts/image-20px.png";
+ img.onload = function() {
+ window.parent.postMessage({status: "image", data: img.width}, "*");
+ };
+ img.onerror = function() {
+ window.parent.postMessage({status: "image", data: "error"}, "*");
+ };
+};
+</script>
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 @@
+<!DOCTYPE html>
+<script>
+ var securityInfoPresent = !!SpecialPowers.wrap(document).docShell.currentDocumentChannel.securityInfo;
+ window.parent.postMessage({status: "protocol",
+ data: location.protocol,
+ securityInfoPresent: securityInfoPresent},
+ "*");
+</script>
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 @@
+<!DOCTYPE html>
+<script>
+ function ok(v, msg) {
+ window.parent.postMessage({status: "ok", result: !!v, message: msg}, "*");
+ }
+
+ function done(reg) {
+ ok(reg.active, "The active worker should be available.");
+ window.parent.postMessage({status: "registrationdone"}, "*");
+ }
+
+ navigator.serviceWorker.ready.then(done);
+ navigator.serviceWorker.register("hsts_test.js", {scope: "."});
+</script>
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 @@
+<!DOCTYPE html>
+<script>
+ navigator.serviceWorker.getRegistration(".").then(function(registration) {
+ registration.unregister().then(function(success) {
+ if (success) {
+ window.parent.postMessage({status: "unregistrationdone"}, "*");
+ }
+ }, function(e) {
+ dump("Unregistering the SW failed with " + e + "\n");
+ });
+ });
+</script>
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 @@
+<!DOCTYPE html>
+<script>
+ window.parent.postMessage({status: "done"}, "*");
+</script>
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 @@
+<!DOCTYPE html>
+<script>
+ function ok(v, msg) {
+ window.parent.postMessage({status: "ok", result: !!v, message: msg}, "*");
+ }
+
+ function done(reg) {
+ ok(reg.active, "The active worker should be available.");
+ window.parent.postMessage({status: "registrationdone"}, "*");
+ }
+
+ navigator.serviceWorker.ready.then(done);
+ navigator.serviceWorker.register("https_test.js", {scope: "."});
+</script>
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 @@
+<!DOCTYPE html>
+<script>
+ navigator.serviceWorker.getRegistration(".").then(function(registration) {
+ registration.unregister().then(function(success) {
+ if (success) {
+ window.parent.postMessage({status: "unregistrationdone"}, "*");
+ }
+ }, function(e) {
+ dump("Unregistering the SW failed with " + e + "\n");
+ });
+ });
+</script>
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('<!DOCTYPE html><script>window.parent.postMessage({status: "done-synth-sw"}, "*");</script>',
+ {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('<!DOCTYPE html><script>window.parent.postMessage({status: "done-synth"}, "*");</script>',
+ {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 @@
+<!DOCTYPE html>
+<script>
+ window.parent.postMessage({status: "done"}, "*");
+</script>
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 @@
+<!DOCTYPE html>
+<script>
+ function ok(v, msg) {
+ window.parent.postMessage({status: "ok", result: !!v, message: msg}, "*");
+ }
+
+ function done(reg) {
+ ok(reg.active, "The active worker should be available.");
+ window.parent.postMessage({status: "registrationdone"}, "*");
+ }
+
+ navigator.serviceWorker.ready.then(reg => {
+ return window.caches.open("cache").then(function(cache) {
+ var synth = new Response('<!DOCTYPE html><script>window.parent.postMessage({status: "done-synth-window"}, "*");</scri' + 'pt>',
+ {headers:{"Content-Type": "text/html"}});
+ return cache.put('synth-window.html', synth).then(_ => done(reg));
+ });
+ });
+ navigator.serviceWorker.register("https_test.js", {scope: "."});
+</script>
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 @@
+<!DOCTYPE html>
+<script>
+ navigator.serviceWorker.getRegistration(".").then(function(registration) {
+ registration.unregister().then(function(success) {
+ if (success) {
+ window.parent.postMessage({status: "unregistrationdone"}, "*");
+ }
+ }, function(e) {
+ dump("Unregistering the SW failed with " + e + "\n");
+ });
+ });
+</script>
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
--- /dev/null
+++ b/dom/workers/test/serviceworkers/fetch/imagecache-maxage/image-20px.png
Binary files 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
--- /dev/null
+++ b/dom/workers/test/serviceworkers/fetch/imagecache-maxage/image-40px.png
Binary files 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 @@
+<!DOCTYPE html>
+<script>
+var width, url, width2, url2;
+function maybeReport() {
+ if (width !== undefined && url !== undefined &&
+ width2 !== undefined && url2 !== undefined) {
+ window.parent.postMessage({status: "result",
+ width: width,
+ width2: width2,
+ url: url,
+ url2: url2}, "*");
+ }
+}
+onload = function() {
+ width = document.querySelector("img").width;
+ width2 = document.querySelector("img").width;
+ maybeReport();
+};
+navigator.serviceWorker.onmessage = function(event) {
+ if (event.data.suffix == "2") {
+ url2 = event.data.url;
+ } else {
+ url = event.data.url;
+ }
+ maybeReport();
+};
+</script>
+<img src="image.png">
+<img src="image2.png">
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 @@
+<!DOCTYPE html>
+<script>
+ function ok(v, msg) {
+ window.parent.postMessage({status: "ok", result: !!v, message: msg}, "*");
+ }
+
+ function done(reg) {
+ ok(reg.active, "The active worker should be available.");
+ window.parent.postMessage({status: "registrationdone"}, "*");
+ }
+
+ navigator.serviceWorker.ready.then(done);
+ navigator.serviceWorker.register("maxage_test.js", {scope: "."});
+</script>
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 @@
+<!DOCTYPE html>
+<script>
+ navigator.serviceWorker.getRegistration(".").then(function(registration) {
+ registration.unregister().then(function(success) {
+ if (success) {
+ window.parent.postMessage({status: "unregistrationdone"}, "*");
+ }
+ }, function(e) {
+ dump("Unregistering the SW failed with " + e + "\n");
+ });
+ });
+</script>
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
--- /dev/null
+++ b/dom/workers/test/serviceworkers/fetch/imagecache/image-20px.png
Binary files 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
--- /dev/null
+++ b/dom/workers/test/serviceworkers/fetch/imagecache/image-40px.png
Binary files 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 @@
+<!DOCTYPE html>
+<script>
+var width, url;
+function maybeReport() {
+ if (width !== undefined && url !== undefined) {
+ window.parent.postMessage({status: "result",
+ width: width,
+ url: url}, "*");
+ }
+}
+onload = function() {
+ width = document.querySelector("img").width;
+ maybeReport();
+};
+navigator.serviceWorker.onmessage = function(event) {
+ url = event.data;
+ maybeReport();
+};
+</script>
+<img src="image-20px.png">
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 @@
+<!DOCTYPE html>
+<script>
+onload = function() {
+ var width = document.querySelector("img").width;
+ window.parent.postMessage({status: "postmortem",
+ width: width}, "*");
+};
+</script>
+<img src="image-20px.png">
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 @@
+<!DOCTYPE html>
+<!-- Load the image here to put it in the image cache -->
+<img src="image-20px.png">
+<script>
+ function ok(v, msg) {
+ window.parent.postMessage({status: "ok", result: !!v, message: msg}, "*");
+ }
+
+ function done(reg) {
+ ok(reg.active, "The active worker should be available.");
+ window.parent.postMessage({status: "registrationdone"}, "*");
+ }
+
+ navigator.serviceWorker.ready.then(done);
+ navigator.serviceWorker.register("imagecache_test.js", {scope: "."});
+</script>
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 @@
+<!DOCTYPE html>
+<script>
+ navigator.serviceWorker.getRegistration(".").then(function(registration) {
+ registration.unregister().then(function(success) {
+ if (success) {
+ window.parent.postMessage({status: "unregistrationdone"}, "*");
+ }
+ }, function(e) {
+ dump("Unregistering the SW failed with " + e + "\n");
+ });
+ });
+</script>
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 `
+ <!DOCTYPE html>
+ <script>
+ window.parent.postMessage({status: "done", data: "${response}"}, "*");
+ </script>
+ `;
+}
+
+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 @@
+<!DOCTYPE html>
+<script>
+ function ok(v, msg) {
+ window.parent.postMessage({status: "ok", result: !!v, message: msg}, "*");
+ }
+
+ function done(reg) {
+ ok(reg.active, "The active worker should be available.");
+ window.parent.postMessage({status: "registrationdone"}, "*");
+ }
+
+ navigator.serviceWorker.ready.then(done);
+ navigator.serviceWorker.register("https_test.js", {scope: "."});
+</script>
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 @@
+<!DOCTYPE html>
+<script>
+ navigator.serviceWorker.getRegistration(".").then(function(registration) {
+ registration.unregister().then(function(success) {
+ if (success) {
+ window.parent.postMessage({status: "unregistrationdone"}, "*");
+ }
+ }, function(e) {
+ dump("Unregistering the SW failed with " + e + "\n");
+ });
+ });
+</script>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 94048 - test install event.</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<div id="style-test" style="background-color: white"></div>
+<pre id="test"></pre>
+<script class="testbody" type="text/javascript">
+ function my_ok(result, msg) {
+ window.opener.postMessage({status: "ok", result: result, message: msg}, "*");
+ }
+
+ function check_intercepted_script() {
+ document.getElementById('intercepted-script').test_result =
+ document.currentScript == document.getElementById('intercepted-script');
+ }
+
+ function fetchXHR(name, onload, onerror, headers) {
+ gExpected++;
+
+ onload = onload || function() {
+ my_ok(false, "load should not complete successfully");
+ finish();
+ };
+ onerror = onerror || function() {
+ my_ok(false, "load should be intercepted successfully");
+ finish();
+ };
+
+ var x = new XMLHttpRequest();
+ x.open('GET', 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 gExpected = 0;
+ var gEncountered = 0;
+ function finish() {
+ gEncountered++;
+ if (gEncountered == gExpected) {
+ window.opener.postMessage({status: "done"}, "*");
+ }
+ }
+
+ function test_onload(creator, complete) {
+ gExpected++;
+ var elem = creator();
+ elem.onload = function() {
+ complete.call(elem);
+ finish();
+ };
+ elem.onerror = function() {
+ my_ok(false, elem.tagName + " load should complete successfully");
+ finish();
+ };
+ document.body.appendChild(elem);
+ }
+
+ function expectAsyncResult() {
+ gExpected++;
+ }
+
+ my_ok(navigator.serviceWorker.controller != null, "should be controlled");
+</script>
+<script src="fetch_tests.js"></script>
+<script>
+ test_onload(function() {
+ var elem = document.createElement('img');
+ elem.src = "nonexistent_image.gifs";
+ elem.id = 'intercepted-img';
+ return elem;
+ }, function() {
+ my_ok(this.complete, "image should be complete");
+ my_ok(this.naturalWidth == 1 && this.naturalHeight == 1, "image should be 1x1 gif");
+ });
+
+ test_onload(function() {
+ var elem = document.createElement('script');
+ elem.id = 'intercepted-script';
+ elem.src = "nonexistent_script.js";
+ return elem;
+ }, function() {
+ my_ok(this.test_result, "script load should be intercepted");
+ });
+
+ test_onload(function() {
+ var elem = document.createElement('link');
+ elem.href = "nonexistent_stylesheet.css";
+ elem.rel = "stylesheet";
+ return elem;
+ }, function() {
+ var styled = document.getElementById('style-test');
+ my_ok(window.getComputedStyle(styled).backgroundColor == 'rgb(0, 0, 0)',
+ "stylesheet load should be intercepted");
+ });
+
+ test_onload(function() {
+ var elem = document.createElement('iframe');
+ elem.id = 'intercepted-iframe';
+ elem.src = "nonexistent_page.html";
+ return elem;
+ }, function() {
+ my_ok(this.test_result, "iframe load should be intercepted");
+ });
+
+ test_onload(function() {
+ var elem = document.createElement('iframe');
+ elem.id = 'intercepted-iframe-2';
+ elem.src = "navigate.html";
+ return elem;
+ }, function() {
+ my_ok(this.test_result, "iframe should successfully load");
+ });
+
+ gExpected++;
+ var xmlDoc = document.implementation.createDocument(null, null, null);
+ xmlDoc.load('load_cross_origin_xml_document_synthetic.xml');
+ xmlDoc.onload = function(evt) {
+ var content = new XMLSerializer().serializeToString(evt.target);
+ my_ok(!content.includes('parsererror'), "Load synthetic cross origin XML Document should be allowed");
+ finish();
+ };
+
+ gExpected++;
+ var xmlDoc = document.implementation.createDocument(null, null, null);
+ xmlDoc.load('load_cross_origin_xml_document_cors.xml');
+ xmlDoc.onload = function(evt) {
+ var content = new XMLSerializer().serializeToString(evt.target);
+ my_ok(!content.includes('parsererror'), "Load CORS cross origin XML Document should be allowed");
+ finish();
+ };
+
+ gExpected++;
+ var xmlDoc = document.implementation.createDocument(null, null, null);
+ xmlDoc.load('load_cross_origin_xml_document_opaque.xml');
+ xmlDoc.onload = function(evt) {
+ var content = new XMLSerializer().serializeToString(evt.target);
+ my_ok(content.includes('parsererror'), "Load opaque cross origin XML Document should not be allowed");
+ finish();
+ };
+
+ gExpected++;
+ var worker = new Worker('nonexistent_worker_script.js');
+ worker.onmessage = function(e) {
+ my_ok(e.data == "worker-intercept-success", "worker load intercepted");
+ finish();
+ };
+ worker.onerror = function() {
+ my_ok(false, "worker load should be intercepted");
+ };
+
+ gExpected++;
+ var worker = new Worker('fetch_worker_script.js');
+ worker.onmessage = function(e) {
+ if (e.data == "finish") {
+ finish();
+ } else if (e.data == "expect") {
+ gExpected++;
+ } else if (e.data.type == "ok") {
+ my_ok(e.data.value, "Fetch test on worker: " + e.data.msg);
+ }
+ };
+ worker.onerror = function() {
+ my_ok(false, "worker should not cause any errors");
+ };
+</script>
+</pre>
+</body>
+</html>
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 @@
+<!DOCTYPE html>
+<script>
+ window.opener.postMessage({status: "domain", data: document.domain}, "*");
+ window.opener.postMessage({status: "origin", data: location.origin}, "*");
+ window.opener.postMessage({status: "done"}, "*");
+</script>
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 @@
+<!DOCTYPE html>
+<script>
+ function ok(v, msg) {
+ window.parent.postMessage({status: "ok", result: !!v, message: msg}, "*");
+ }
+
+ function done(reg) {
+ ok(reg.active, "The active worker should be available.");
+ window.parent.postMessage({status: "registrationdone"}, "*");
+ }
+
+ navigator.serviceWorker.ready.then(done);
+ navigator.serviceWorker.register("origin_test.js", {scope: "."});
+</script>
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 @@
+<!DOCTYPE html>
+<script>
+ navigator.serviceWorker.getRegistration(".").then(function(registration) {
+ registration.unregister().then(function(success) {
+ if (success) {
+ window.parent.postMessage({status: "unregistrationdone"}, "*");
+ }
+ }, function(e) {
+ dump("Unregistering the SW failed with " + e + "\n");
+ });
+ });
+</script>
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 @@
+<!DOCTYPE html>
+<script>
+ window.opener.postMessage({status: "domain", data: document.domain}, "*");
+ window.opener.postMessage({status: "origin", data: location.origin}, "*");
+ window.opener.postMessage({status: "done"}, "*");
+</script>
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 @@
+<!DOCTYPE html>
+<script>
+ function ok(v, msg) {
+ window.parent.postMessage({status: "ok", result: !!v, message: msg}, "*");
+ }
+
+ function done(reg) {
+ ok(reg.active, "The active worker should be available.");
+ window.parent.postMessage({status: "registrationdone"}, "*");
+ }
+
+ navigator.serviceWorker.ready.then(done);
+ navigator.serviceWorker.register("origin_test.js", {scope: "."});
+</script>
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 @@
+<!DOCTYPE html>
+<script>
+ navigator.serviceWorker.getRegistration(".").then(function(registration) {
+ registration.unregister().then(function(success) {
+ if (success) {
+ window.parent.postMessage({status: "unregistrationdone"}, "*");
+ }
+ }, function(e) {
+ dump("Unregistering the SW failed with " + e + "\n");
+ });
+ });
+</script>
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 @@
+<!DOCTYPE html>
+<script>
+ var obj, embed;
+
+ function ok(v, msg) {
+ window.opener.postMessage({status: "ok", result: !!v, message: msg}, "*");
+ }
+
+ function finish() {
+ document.documentElement.removeChild(obj);
+ document.documentElement.removeChild(embed);
+ window.opener.postMessage({status: "done"}, "*");
+ }
+
+ function test_object() {
+ obj = document.createElement("object");
+ obj.setAttribute('data', "object");
+ document.documentElement.appendChild(obj);
+ }
+
+ function test_embed() {
+ embed = document.createElement("embed");
+ embed.setAttribute('src', "embed");
+ document.documentElement.appendChild(embed);
+ }
+
+ navigator.serviceWorker.addEventListener("message", function onMessage(e) {
+ if (e.data.context === "object") {
+ ok(false, "<object> should not be intercepted");
+ } else if (e.data.context === "embed") {
+ ok(false, "<embed> should not be intercepted");
+ } else if (e.data.context === "fetch" && e.data.resource === "foo.txt") {
+ navigator.serviceWorker.removeEventListener("message", onMessage);
+ finish();
+ }
+ }, false);
+
+ test_object();
+ test_embed();
+ // SW will definitely intercept fetch API, use this to see if plugins are
+ // intercepted before fetch().
+ fetch("foo.txt");
+</script>
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 @@
+<!DOCTYPE html>
+<script>
+ navigator.serviceWorker.onmessage = window.onmessage = e => {
+ window.parent.postMessage(e.data, "*");
+ };
+</script>
+<iframe src="redirector.html"></iframe>
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 @@
+<!DOCTYPE html>
+<meta http-equiv="refresh" content="3;URL=/tests/dom/workers/test/serviceworkers/fetch/requesturl/redirect.sjs">
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 @@
+<!DOCTYPE html>
+<script>
+ function ok(v, msg) {
+ window.parent.postMessage({status: "ok", result: !!v, message: msg}, "*");
+ }
+
+ function done(reg) {
+ ok(reg.active, "The active worker should be available.");
+ window.parent.postMessage({status: "registrationdone"}, "*");
+ }
+
+ navigator.serviceWorker.ready.then(done);
+ navigator.serviceWorker.register("requesturl_test.js", {scope: "."});
+</script>
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 @@
+<!DOCTYPE html>
+secret stuff
+<script>
+ window.parent.postMessage({status: "done"}, "*");
+</script>
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 @@
+<!DOCTYPE html>
+<script>
+ navigator.serviceWorker.getRegistration(".").then(function(registration) {
+ registration.unregister().then(function(success) {
+ if (success) {
+ window.parent.postMessage({status: "unregistrationdone"}, "*");
+ }
+ }, function(e) {
+ dump("Unregistering the SW failed with " + e + "\n");
+ });
+ });
+</script>
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 @@
+<!DOCTYPE html>
+<script>
+ window.parent.postMessage({status: "ok", result: true, message: "The iframe is not being intercepted"}, "*");
+ window.parent.postMessage({status: "done"}, "*");
+</script>
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 @@
+<!DOCTYPE html>
+<script>
+ window.parent.postMessage({status: "ok", result: false, message: "The iframe is being intercepted"}, "*");
+ window.parent.postMessage({status: "done"}, "*");
+</script>
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 @@
+<!DOCTYPE html>
+<script>
+ function ok(v, msg) {
+ window.parent.postMessage({status: "ok", result: !!v, message: msg}, "*");
+ }
+
+ function done(reg) {
+ ok(reg.active, "The active worker should be available.");
+ window.parent.postMessage({status: "registrationdone"}, "*");
+ }
+
+ navigator.serviceWorker.ready.then(done);
+ navigator.serviceWorker.register("sandbox_test.js", {scope: "."});
+</script>
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 @@
+<!DOCTYPE html>
+<script>
+ navigator.serviceWorker.getRegistration(".").then(function(registration) {
+ registration.unregister().then(function(success) {
+ if (success) {
+ window.parent.postMessage({status: "unregistrationdone"}, "*");
+ }
+ }, function(e) {
+ dump("Unregistering the SW failed with " + e + "\n");
+ });
+ });
+</script>
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 @@
+<!DOCTYPE html>
+<script>
+ window.onmessage = function(e) {
+ window.parent.postMessage(e.data, "*");
+ if (e.data.status == "protocol") {
+ document.querySelector("iframe").src = "image.html";
+ }
+ };
+</script>
+<iframe src="http://example.com/tests/dom/workers/test/serviceworkers/fetch/upgrade-insecure/index.html"></iframe>
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
--- /dev/null
+++ b/dom/workers/test/serviceworkers/fetch/upgrade-insecure/image-20px.png
Binary files 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
--- /dev/null
+++ b/dom/workers/test/serviceworkers/fetch/upgrade-insecure/image-40px.png
Binary files 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 @@
+<!DOCTYPE html>
+<script>
+onload=function(){
+ var img = new Image();
+ img.src = "http://example.com/tests/dom/workers/test/serviceworkers/fetch/upgrade-insecure/image-20px.png";
+ img.onload = function() {
+ window.parent.postMessage({status: "image", data: img.width}, "*");
+ };
+ img.onerror = function() {
+ window.parent.postMessage({status: "image", data: "error"}, "*");
+ };
+};
+</script>
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 @@
+<!DOCTYPE html>
+<script>
+ window.parent.postMessage({status: "protocol", data: location.protocol}, "*");
+</script>
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 @@
+<!DOCTYPE html>
+<script>
+ function ok(v, msg) {
+ window.parent.postMessage({status: "ok", result: !!v, message: msg}, "*");
+ }
+
+ function done(reg) {
+ ok(reg.active, "The active worker should be available.");
+ window.parent.postMessage({status: "registrationdone"}, "*");
+ }
+
+ navigator.serviceWorker.ready.then(done);
+ navigator.serviceWorker.register("upgrade-insecure_test.js", {scope: "."});
+</script>
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 @@
+<!DOCTYPE html>
+<script>
+ navigator.serviceWorker.getRegistration(".").then(function(registration) {
+ registration.unregister().then(function(success) {
+ if (success) {
+ window.parent.postMessage({status: "unregistrationdone"}, "*");
+ }
+ }, function(e) {
+ dump("Unregistering the SW failed with " + e + "\n");
+ });
+ });
+</script>
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("<script>window.frameElement.test_result = true;</script>", {
+ 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("<script>window.frameElement.test_result = true;</script>", {
+ 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 = "<script>" +
+ "opener.postMessage({status: 'ok', result: " + ev.isReload + "," +
+ "message: 'reload status should be indicated'}, '*');" +
+ "opener.postMessage({status: 'done'}, '*');" +
+ "</script>";
+ 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("<response>body</response>", { 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("<error>Invalid Request mode</error>", { 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 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="utf-8">
+ </head>
+ <body>
+ Hello.
+ </body>
+<html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1139425 - controlled page</title>
+<script class="testbody" type="text/javascript">
+ var testWindow = parent;
+ if (opener) {
+ testWindow = opener;
+ }
+
+ window.onload = function() {
+ navigator.serviceWorker.ready.then(function(swr) {
+ swr.active.postMessage("Start");
+ });
+ }
+
+ navigator.serviceWorker.onmessage = function(msg) {
+ // worker message;
+ testWindow.postMessage(msg.data, "*");
+ window.close();
+ };
+</script>
+
+</head>
+<body>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1058311 - controlled page</title>
+<script class="testbody" type="text/javascript">
+ var re = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/;
+ var frameType = "none";
+ var testWindow = parent;
+
+ if (parent != window) {
+ frameType = "nested";
+ } else if (opener) {
+ frameType = "auxiliary";
+ testWindow = opener;
+ } else if (parent != window) {
+ frameType = "top-level";
+ } else {
+ postResult(false, "Unexpected frameType");
+ }
+
+ window.onload = function() {
+ navigator.serviceWorker.ready.then(function(swr) {
+ swr.active.postMessage("Start");
+ });
+ }
+
+ function postResult(result, msg) {
+ response = {
+ result: result,
+ message: msg
+ };
+
+ testWindow.postMessage(response, "*");
+ }
+
+ navigator.serviceWorker.onmessage = function(msg) {
+ // worker message;
+ result = re.test(msg.data.id);
+ postResult(result, "Client id test");
+
+ result = msg.data.url == window.location;
+ postResult(result, "Client url test");
+
+ result = msg.data.visibilityState === document.visibilityState;
+ postResult(result, "Client visibility test. expected=" +document.visibilityState);
+
+ result = msg.data.focused === document.hasFocus();
+ postResult(result, "Client focus test. expected=" + document.hasFocus());
+
+ result = msg.data.frameType === frameType;
+ postResult(result, "Client frameType test. expected=" + frameType);
+
+ postResult(true, "DONE");
+ window.close();
+ };
+</script>
+
+</head>
+<body>
+</body>
+</html>
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 @@
+<!DOCTYPE html>
+<script>
+ navigator.serviceWorker.onmessage = function(e) {
+ window.parent.postMessage(e.data, "*");
+ };
+</script>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1114554 - proxy to forward messages from SW to test</title>
+<script class="testbody" type="text/javascript">
+ var testWindow = parent;
+ if (opener) {
+ testWindow = opener;
+ }
+
+ navigator.serviceWorker.onmessage = function(msg) {
+ // worker message;
+ testWindow.postMessage(msg.data, "*");
+ if (msg.data.type == 'finish') {
+ window.close();
+ }
+ };
+</script>
+
+</head>
+<body>
+</body>
+</html>
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 @@
+<!DOCTYPE html>
+<script>
+ function done() {
+ parent.callback();
+ }
+
+ navigator.serviceWorker.ready.then(done);
+ navigator.serviceWorker.register("../notification_get_sw.js", {scope: "."}).catch(function(e) {
+ dump("Registration failure " + e.message + "\n");
+ });
+</script>
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 @@
+<!DOCTYPE html>
+<script>
+ navigator.serviceWorker.getRegistration(".").then(function(registration) {
+ registration.unregister().then(function(success) {
+ if (success) {
+ window.parent.callback();
+ }
+ }, function(e) {
+ dump("Unregistering the SW failed with " + e + "\n");
+ });
+ });
+</script>
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 @@
+<!DOCTYPE html>
+<script>
+ function done() {
+ parent.callback();
+ }
+
+ navigator.serviceWorker.ready.then(done);
+ navigator.serviceWorker.register("../notification_get_sw.js", {scope: "."}).catch(function(e) {
+ dump("Registration failure " + e.message + "\n");
+ });
+</script>
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 @@
+<!DOCTYPE html>
+<script>
+ navigator.serviceWorker.getRegistration(".").then(function(registration) {
+ registration.unregister().then(function(success) {
+ if (success) {
+ window.parent.callback();
+ }
+ }, function(e) {
+ dump("Unregistering the SW failed with " + e + "\n");
+ });
+ });
+</script>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1114554 - controlled page</title>
+<script class="testbody" type="text/javascript">
+ var testWindow = parent;
+ if (opener) {
+ testWindow = opener;
+ }
+
+ navigator.serviceWorker.ready.then(function(swr) {
+ var ifr = document.createElement("iframe");
+ document.documentElement.appendChild(ifr);
+ ifr.contentWindow.ServiceWorkerRegistration.prototype.showNotification
+ .call(swr, "Hi there. The ServiceWorker should receive a click event for this.", { data: { complex: ["jsval", 5] }});
+ });
+
+ navigator.serviceWorker.onmessage = function(msg) {
+ testWindow.callback(msg.data.result);
+ };
+</script>
+
+</head>
+<body>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1114554 - controlled page</title>
+<script class="testbody" type="text/javascript">
+ var testWindow = parent;
+ if (opener) {
+ testWindow = opener;
+ }
+
+ navigator.serviceWorker.ready.then(function(swr) {
+ swr.showNotification("Hi there. The ServiceWorker should receive a click event for this.", { data: { complex: ["jsval", 5] }});
+ });
+
+ navigator.serviceWorker.onmessage = function(msg) {
+ testWindow.callback(msg.data.result);
+ };
+</script>
+
+</head>
+<body>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1144660 - controlled page</title>
+<script class="testbody" type="text/javascript">
+ var testWindow = parent;
+ if (opener) {
+ testWindow = opener;
+ }
+
+ navigator.serviceWorker.ready.then(function(swr) {
+ swr.showNotification("Hi there. The ServiceWorker should receive a click event for this.");
+ });
+
+ navigator.serviceWorker.onmessage = function(msg) {
+ dump("GOT Message " + JSON.stringify(msg.data) + "\n");
+ testWindow.callback(msg.data.ok);
+ };
+</script>
+
+</head>
+<body>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1265841 - controlled page</title>
+<script class="testbody" type="text/javascript">
+ var testWindow = parent;
+ if (opener) {
+ testWindow = opener;
+ }
+
+ navigator.serviceWorker.ready.then(function(swr) {
+ return swr.showNotification(
+ "Hi there. The ServiceWorker should receive a close event for this.",
+ { data: { complex: ["jsval", 5] }}).then(function() {
+ return swr;
+ });
+ }).then(function(swr) {
+ return swr.getNotifications();
+ }).then(function(notifications) {
+ notifications.forEach(function(notification) {
+ notification.close();
+ });
+ });
+
+ navigator.serviceWorker.onmessage = function(msg) {
+ testWindow.callback(msg.data.result);
+ };
+</script>
+
+</head>
+<body>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1172870 - page opened by ServiceWorkerClients.OpenWindow</title>
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+<script class="testbody" type="text/javascript">
+
+ window.onload = function() {
+ if (window.location == "http://mochi.test:8888/tests/dom/workers/test/serviceworkers/open_window/client.html") {
+ navigator.serviceWorker.ready.then(function(result) {
+ navigator.serviceWorker.onmessage = function(event) {
+ if (event.data !== "CLOSE") {
+ dump("ERROR: unexepected reply from the service worker.\n");
+ }
+ if (parent) {
+ parent.postMessage("CLOSE", "*");
+ }
+ window.close();
+ }
+ navigator.serviceWorker.controller.postMessage("NEW_WINDOW");
+ })
+ } else {
+ window.onmessage = function(event) {
+ if (event.data !== "CLOSE") {
+ dump("ERROR: unexepected reply from the iframe.\n");
+ }
+ window.close();
+ }
+
+ var iframe = document.createElement('iframe');
+ iframe.src = "http://mochi.test:8888/tests/dom/workers/test/serviceworkers/open_window/client.html";
+ document.body.appendChild(iframe);
+ }
+ }
+
+</script>
+</pre>
+</body>
+</html>
+
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 @@
+<!DOCTYPE html>
+<script>
+function ok(condition, message) {
+ parent.postMessage({type: "ok", status: condition, msg: message}, "*");
+}
+
+function done() {
+ parent.postMessage({type: "done"}, "*");
+}
+
+ok(location.protocol == "https:", "We should be loaded from HTTPS");
+
+navigator.serviceWorker.register("empty.js", {scope: "register-https"})
+ .then(reg => {
+ ok(false, "Registration should fail");
+ done();
+ }).catch(err => {
+ ok(err.name === "SecurityError", "Registration should fail with SecurityError");
+ done();
+ });
+</script>
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 @@
+<!DOCTYPE html>
+<script>
+ function done(exists) {
+ parent.postMessage(exists, '*');
+ }
+
+ function fail() {
+ parent.postMessage("FAIL", '*');
+ }
+
+ navigator.serviceWorker.getRegistration(".").then(function(reg) {
+ if (reg) {
+ reg.unregister().then(done.bind(undefined, true), fail);
+ } else {
+ dump("getRegistration() returned undefined registration\n");
+ done(false);
+ }
+ }, function(e) {
+ dump("getRegistration() failed\n");
+ fail();
+ });
+</script>
+
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 @@
+<!DOCTYPE html>
+<script>
+ fetch("intercept-this").then(function(r) {
+ if (!r.ok) {
+ return "FAIL";
+ }
+ return r.text();
+ }).then(function(body) {
+ parent.postMessage(body, '*');
+ });
+</script>
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 @@
+<!DOCTYPE html>
+<script>
+ function done() {
+ parent.postMessage('', '*');
+ }
+
+ navigator.serviceWorker.ready.then(done);
+ navigator.serviceWorker.register("../sanitize_worker.js", {scope: "."});
+</script>
+
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 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="utf-8">
+ <script>
+ navigator.serviceWorker.register("worker.js");
+ </script>
+ </head>
+ <body>
+ This is a test page.
+ </body>
+<html>
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 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="utf-8">
+ <script>
+ window.onmessage = function (event) {
+ if (event.data !== "register") {
+ return;
+ }
+ var promise = navigator.serviceWorker.register("worker.js");
+ window.onmessage = function (event) {
+ if (event.data !== "unregister") {
+ return;
+ }
+ promise.then(function (registration) {
+ registration.unregister();
+ });
+ window.onmessage = null;
+ };
+ };
+ </script>
+ </head>
+ <body>
+ This is a test page.
+ </body>
+<html>
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 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="utf-8">
+ <script>
+ window.onmessage = function (event) {
+ if (event.data !== "register") {
+ return;
+ }
+ var promise = navigator.serviceWorker.register("worker.js");
+ window.onmessage = function (event) {
+ if (event.data !== "register") {
+ return;
+ }
+ promise = promise.then(function (registration) {
+ return navigator.serviceWorker.register("worker2.js");
+ });
+ window.onmessage = function (event) {
+ if (event.data !== "unregister") {
+ return;
+ }
+ promise.then(function (registration) {
+ registration.unregister();
+ });
+ window.onmessage = null;
+ };
+ };
+ };
+ </script>
+ </head>
+ <body>
+ This is a test page.
+ </body>
+<html>
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 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="utf-8">
+ <script>
+ var reg;
+ window.onmessage = function (event) {
+ if (event.data !== "register") {
+ return;
+ }
+ var promise = navigator.serviceWorker.register("worker.js");
+ window.onmessage = function (event) {
+ if (event.data === "register") {
+ promise.then(function (registration) {
+ return navigator.serviceWorker.register("worker2.js")
+ .then(function(registration) {
+ reg = registration;
+ });
+ });
+ } else if (event.data === "unregister") {
+ reg.unregister();
+ }
+ };
+ };
+ </script>
+ </head>
+ <body>
+ This is a test page.
+ </body>
+<html>
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 @@
+<html>
+ <head></head>
+ <body>
+ <script type="text/javascript">
+ var expectedEvents = 2;
+ function eventReceived() {
+ window.parent.postMessage({ type: "check", status: expectedEvents > 0, msg: "updatefound received" }, "*");
+
+ if (--expectedEvents) {
+ window.parent.postMessage({ type: "finish" }, "*");
+ }
+ }
+
+ navigator.serviceWorker.getRegistrations().then(function(a) {
+ window.parent.postMessage({ type: "check", status: Array.isArray(a),
+ msg: "getRegistrations returns an array" }, "*");
+ window.parent.postMessage({ type: "check", status: a.length > 0,
+ msg: "getRegistrations returns an array with 1 item" }, "*");
+ for (var i = 0; i < a.length; ++i) {
+ window.parent.postMessage({ type: "check", status: a[i] instanceof ServiceWorkerRegistration,
+ msg: "getRegistrations returns an array of ServiceWorkerRegistration objects" }, "*");
+ if (a[i].scope.match(/simpleregister\//)) {
+ a[i].onupdatefound = function(e) {
+ eventReceived();
+ }
+ }
+ }
+ });
+
+ navigator.serviceWorker.getRegistration('http://mochi.test:8888/tests/dom/workers/test/serviceworkers/simpleregister/')
+ .then(function(a) {
+ window.parent.postMessage({ type: "check", status: a instanceof ServiceWorkerRegistration,
+ msg: "getRegistration returns a ServiceWorkerRegistration" }, "*");
+ a.onupdatefound = function(e) {
+ eventReceived();
+ }
+ });
+
+ navigator.serviceWorker.getRegistration('http://www.something_else.net/')
+ .then(function(a) {
+ window.parent.postMessage({ type: "check", status: false,
+ msg: "getRegistration should throw for security error!" }, "*");
+ }, function(a) {
+ window.parent.postMessage({ type: "check", status: true,
+ msg: "getRegistration should throw for security error!" }, "*");
+ });
+
+ window.parent.postMessage({ type: "ready" }, "*");
+ </script>
+ </body>
+</html>
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 @@
+<html>
+ <head></head>
+ <body>
+ <script type="text/javascript">
+
+ window.addEventListener('message', function(evt) {
+ navigator.serviceWorker.ready.then(function() {
+ evt.ports[0].postMessage("WOW!");
+ });
+ }, false);
+
+ </script>
+ </body>
+</html>
+
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1131352 - Add ServiceWorkerGlobalScope skipWaiting()</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+<script class="testbody" type="text/javascript">
+
+ if (!parent) {
+ info("skip_waiting_scope/index.html shouldn't be launched directly!");
+ }
+
+ navigator.serviceWorker.ready.then(function() {
+ parent.postMessage("READY", "*");
+ });
+
+ navigator.serviceWorker.oncontrollerchange = function() {
+ parent.postMessage({
+ event: "controllerchange",
+ controllerScriptURL: navigator.serviceWorker.controller &&
+ navigator.serviceWorker.controller.scriptURL
+ }, "*");
+ }
+
+</script>
+</pre>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>test file blob upload with SW interception</title>
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+<script class="testbody" type="text/javascript">
+
+if (!parent) {
+ dump("sw_clients/file_blob_upload_frame.html shouldn't be launched directly!");
+}
+
+function makeFileBlob(obj) {
+ return new Promise(function(resolve, reject) {
+
+ var request = indexedDB.open(window.location.pathname, 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);
+ };
+ };
+ };
+ });
+}
+
+navigator.serviceWorker.ready.then(function() {
+ parent.postMessage({ status: 'READY' }, '*');
+});
+
+var URL = '/tests/dom/workers/test/serviceworkers/redirect_post.sjs';
+
+addEventListener('message', function(evt) {
+ if (evt.data.type = 'TEST') {
+ makeFileBlob(evt.data.body).then(function(blob) {
+ return fetch(URL, { method: 'POST', body: blob });
+ }).then(function(response) {
+ return response.json();
+ }).then(function(result) {
+ parent.postMessage({ status: 'OK', result: result }, '*');
+ }).catch(function(e) {
+ parent.postMessage({ status: 'ERROR', result: e.toString() }, '*');
+ });
+ }
+});
+
+</script>
+</pre>
+</body>
+</html>
+
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 982726 - test match_all not crashing</title>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+<script class="testbody" type="text/javascript">
+
+ if (!parent) {
+ dump("sw_clients/navigator.html shouldn't be launched directly!\n");
+ }
+
+ window.addEventListener("message", function(event) {
+ if (event.data.type === "NAVIGATE") {
+ window.location = event.data.url;
+ }
+ });
+
+ navigator.serviceWorker.ready.then(function() {
+ parent.postMessage("NAVIGATOR_READY", "*");
+ });
+
+</script>
+</pre>
+</body>
+</html>
+
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 982726 - test match_all not crashing</title>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ <!-- some tests will intercept this bogus script request -->
+ <script type="text/javascript" src="does_not_exist.js"></script>
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+<script class="testbody" type="text/javascript">
+
+ if (!parent) {
+ dump("sw_clients/simple.html shouldn't be launched directly!");
+ }
+
+ window.addEventListener("message", function(event) {
+ if (event.data === "REFRESH") {
+ window.location.reload();
+ } else if (event.data === "FORCE_REFRESH") {
+ window.location.reload(true);
+ }
+ });
+
+ navigator.serviceWorker.ready.then(function() {
+ parent.postMessage("READY", "*");
+ });
+
+</script>
+</pre>
+</body>
+</html>
+
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 982726 - test match_all not crashing</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+<script class="testbody" type="text/javascript">
+
+ if (!parent) {
+ info("sw_clients/simple.html shouldn't be launched directly!");
+ }
+
+ window.addEventListener("message", function(event) {
+ if (event.data === "REFRESH") {
+ window.location.reload();
+ } else if (event.data === "FORCE_REFRESH") {
+ window.location.reload(true);
+ }
+ });
+
+ navigator.serviceWorker.ready.then(function() {
+ parent.postMessage("READY_CACHED", "*");
+ });
+
+</script>
+</pre>
+</body>
+</html>
+
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
--- /dev/null
+++ b/dom/workers/test/serviceworkers/sw_clients/refresher_cached_compressed.html
Binary files 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
--- /dev/null
+++ b/dom/workers/test/serviceworkers/sw_clients/refresher_compressed.html
Binary files 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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>controlled page</title>
+ <!--
+ Paged controlled by a service worker for testing matchAll().
+ See bug 982726, 1058311.
+ -->
+<script class="testbody" type="text/javascript">
+ function fail(msg) {
+ info("service_worker_controlled.html: " + msg);
+ opener.postMessage("FAIL", "*");
+ }
+
+ if (!parent) {
+ info("service_worker_controlled.html should not be launched directly!");
+ }
+
+ window.onload = function() {
+ navigator.serviceWorker.ready.then(function(swr) {
+ parent.postMessage("READY", "*");
+ });
+ }
+
+ navigator.serviceWorker.onmessage = function(msg) {
+ // forward message to the test page.
+ parent.postMessage(msg.data, "*");
+ };
+</script>
+
+</head>
+<body>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 982726 - test match_all not crashing</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+<script class="testbody" type="text/javascript">
+
+ if (!parent) {
+ info("sw_clients/simple.html shouldn't be launched directly!");
+ }
+
+ navigator.serviceWorker.ready.then(function() {
+ parent.postMessage("READY", "*");
+ });
+
+</script>
+</pre>
+</body>
+</html>
+
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
--- /dev/null
+++ b/dom/workers/test/serviceworkers/swa/worker_scope_different.js
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
--- /dev/null
+++ b/dom/workers/test/serviceworkers/swa/worker_scope_different2.js
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
--- /dev/null
+++ b/dom/workers/test/serviceworkers/swa/worker_scope_precise.js
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
--- /dev/null
+++ b/dom/workers/test/serviceworkers/swa/worker_scope_too_deep.js
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
--- /dev/null
+++ b/dom/workers/test/serviceworkers/swa/worker_scope_too_narrow.js
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1151916 - Test principal is set on cached serviceworkers</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+<!--
+ If the principal is not set, accessing self.caches in the worker will crash.
+-->
+</head>
+<body>
+<p id="display"></p>
+<div id="content"></div>
+<pre id="test"></pre>
+<script class="testbody" type="text/javascript">
+
+ var frame;
+
+ function listenForMessage() {
+ var p = new Promise(function(resolve, reject) {
+ window.onmessage = function(e) {
+ if (e.data.status == "failed") {
+ ok(false, "iframe had error " + e.data.message);
+ reject(e.data.message);
+ } else if (e.data.status == "success") {
+ ok(true, "iframe step success " + e.data.message);
+ resolve(e.data.message);
+ } else {
+ ok(false, "Unexpected message " + e.data);
+ reject();
+ }
+ }
+ });
+
+ return p;
+ }
+
+ // We have the iframe register for its own scope so that this page is not
+ // holding any references when we GC.
+ function register() {
+ var p = listenForMessage();
+
+ frame = document.createElement("iframe");
+ document.body.appendChild(frame);
+ frame.src = "bug1151916_driver.html";
+
+ return p;
+ }
+
+ function unloadFrame() {
+ frame.src = "about:blank";
+ frame.parentNode.removeChild(frame);
+ frame = null;
+ }
+
+ function gc() {
+ return new Promise(function(resolve) {
+ SpecialPowers.exactGC(resolve);
+ });
+ }
+
+ function testCaches() {
+ var p = listenForMessage();
+
+ frame = document.createElement("iframe");
+ document.body.appendChild(frame);
+ frame.src = "bug1151916_driver.html";
+
+ return p;
+ }
+
+ function unregister() {
+ return navigator.serviceWorker.getRegistration("./bug1151916_driver.html").then(function(reg) {
+ ok(reg instanceof ServiceWorkerRegistration, "Must have valid registration.");
+ return reg.unregister();
+ });
+ }
+
+ function runTest() {
+ register()
+ .then(unloadFrame)
+ .then(gc)
+ .then(testCaches)
+ .then(unregister)
+ .catch(function(e) {
+ ok(false, "Some test failed with error " + e);
+ }).then(SimpleTest.finish);
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ SpecialPowers.pushPrefEnv({"set": [
+ ["dom.serviceWorkers.exemptFromPerDomainMax", true],
+ ["dom.serviceWorkers.enabled", true],
+ ["dom.serviceWorkers.testing.enabled", true],
+ ["dom.caches.enabled", true],
+ ]}, runTest);
+</script>
+</pre>
+</body>
+</html>
+
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for encoding of service workers</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+<script class="testbody" type="text/javascript">
+ SimpleTest.waitForExplicitFinish();
+
+ function runTest() {
+ navigator.serviceWorker.register("bug1240436_worker.js")
+ .then(reg => reg.unregister())
+ .then(() => ok(true, "service worker register script succeed"))
+ .catch(err => ok(false, "service worker register script faled " + err))
+ .then(() => SimpleTest.finish());
+ }
+
+ SpecialPowers.pushPrefEnv({"set": [
+ ["dom.serviceWorkers.enabled", true],
+ ["dom.serviceWorkers.testing.enabled", true]
+ ]}, runTest);
+</script>
+</pre>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1130684 - Test service worker clients claim onactivate </title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+<script class="testbody" type="text/javascript">
+ var registration_1;
+ var registration_2;
+ var client;
+
+ function register_1() {
+ return navigator.serviceWorker.register("claim_worker_1.js",
+ { scope: "./" })
+ .then((swr) => registration_1 = swr);
+ }
+
+ function register_2() {
+ return navigator.serviceWorker.register("claim_worker_2.js",
+ { scope: "./claim_clients/client.html" })
+ .then((swr) => registration_2 = swr);
+ }
+
+ function unregister(reg) {
+ return reg.unregister().then(function(result) {
+ ok(result, "Unregister should return true.");
+ });
+ }
+
+ function createClient() {
+ var p = new Promise(function(res, rej) {
+ window.onmessage = function(e) {
+ if (e.data === "READY") {
+ res();
+ }
+ }
+ });
+
+ content = document.getElementById("content");
+ ok(content, "parent exists.");
+
+ client = document.createElement("iframe");
+ client.setAttribute('src', "claim_clients/client.html");
+ content.appendChild(client);
+
+ return p;
+ }
+
+ function testController() {
+ ok(navigator.serviceWorker.controller.scriptURL.match("claim_worker_1"),
+ "Controlling service worker has the correct url.");
+ }
+
+ function testClientWasClaimed(expected) {
+ var resolveClientMessage, resolveClientControllerChange;
+ var messageFromClient = new Promise(function(res, rej) {
+ resolveClientMessage = res;
+ });
+ var controllerChangeFromClient = new Promise(function(res, rej) {
+ resolveClientControllerChange = res;
+ });
+ window.onmessage = function(e) {
+ if (!e.data.event) {
+ ok(false, "Unknown message received: " + e.data);
+ }
+
+ if (e.data.event === "controllerchange") {
+ ok(e.data.controller,
+ "Client was claimed and received controllerchange event.");
+ resolveClientControllerChange();
+ }
+
+ if (e.data.event === "message") {
+ ok(e.data.data.resolve_value === undefined,
+ "Claim should resolve with undefined.");
+ ok(e.data.data.message === expected.message,
+ "Client received message from claiming worker.");
+ ok(e.data.data.match_count_before === expected.match_count_before,
+ "MatchAll clients count before claim should be " + expected.match_count_before);
+ ok(e.data.data.match_count_after === expected.match_count_after,
+ "MatchAll clients count after claim should be " + expected.match_count_after);
+ resolveClientMessage();
+ }
+ }
+
+ return Promise.all([messageFromClient, controllerChangeFromClient])
+ .then(() => window.onmessage = null);
+ }
+
+ function testClaimFirstWorker() {
+ // wait for the worker to control us
+ var controllerChange = new Promise(function(res, rej) {
+ navigator.serviceWorker.oncontrollerchange = function(e) {
+ ok(true, "controller changed event received.");
+ res();
+ };
+ });
+
+ var messageFromWorker = new Promise(function(res, rej) {
+ navigator.serviceWorker.onmessage = function(e) {
+ ok(e.data.resolve_value === undefined,
+ "Claim should resolve with undefined.");
+ ok(e.data.message === "claim_worker_1",
+ "Received message from claiming worker.");
+ ok(e.data.match_count_before === 0,
+ "Worker doesn't control any client before claim.");
+ ok(e.data.match_count_after === 2, "Worker should claim 2 clients.");
+ res();
+ }
+ });
+
+ var clientClaim = testClientWasClaimed({
+ message: "claim_worker_1",
+ match_count_before: 0,
+ match_count_after: 2
+ });
+
+ return Promise.all([controllerChange, messageFromWorker, clientClaim])
+ .then(testController);
+ }
+
+ function testClaimSecondWorker() {
+ navigator.serviceWorker.oncontrollerchange = function(e) {
+ ok(false, "Claim_worker_2 shouldn't claim this window.");
+ }
+
+ navigator.serviceWorker.onmessage = function(e) {
+ ok(false, "Claim_worker_2 shouldn't claim this window.");
+ }
+
+ var clientClaim = testClientWasClaimed({
+ message: "claim_worker_2",
+ match_count_before: 0,
+ match_count_after: 1
+ });
+
+ return clientClaim.then(testController);
+ }
+
+ function runTest() {
+ createClient()
+ .then(register_1)
+ .then(testClaimFirstWorker)
+ .then(register_2)
+ .then(testClaimSecondWorker)
+ .then(function() { return unregister(registration_1); })
+ .then(function() { return unregister(registration_2); })
+ .catch(function(e) {
+ ok(false, "Some test failed with error " + e);
+ }).then(SimpleTest.finish);
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ SpecialPowers.pushPrefEnv({"set": [
+ ["dom.serviceWorkers.exemptFromPerDomainMax", true],
+ ["dom.serviceWorkers.enabled", true],
+ ["dom.serviceWorkers.testing.enabled", true]
+ ]}, runTest);
+</script>
+</pre>
+</body>
+</html>
+
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1130684 - Test fetch events are intercepted after claim </title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+ <a ping="ping" href="fetch.txt">link</a>
+</div>
+<pre id="test"></pre>
+<script class="testbody" type="text/javascript">
+ var registration;
+
+ function register() {
+ return navigator.serviceWorker.register("claim_fetch_worker.js",
+ { scope: "./" })
+ .then((swr) => registration = swr);
+ }
+
+ function unregister() {
+ return registration.unregister().then(function(result) {
+ ok(result, "Unregister should return true.");
+ });
+ }
+
+ function createClient() {
+ var p = new Promise(function(res, rej){
+ window.onmessage = function(e) {
+ if(e.data === "READY") {
+ res();
+ }
+ }
+ });
+
+ var content = document.getElementById("content");
+ ok(content, "Parent exists.");
+
+ iframe = document.createElement("iframe");
+ iframe.setAttribute('src', "sw_clients/service_worker_controlled.html");
+ content.appendChild(iframe);
+
+ return p;
+ }
+
+ function testFetch(before) {
+ return fetch("fetch/real-file.txt").then(function(res) {
+ ok(res.ok, "Response should be valid.");
+ return res.text().then(function(body) {
+ if (before) {
+ ok(body !== "Fetch was intercepted", "Fetch events should not be intercepted.");
+ } else {
+ ok(body === "Fetch was intercepted", "Fetch events should be intercepted.");
+ }
+ });
+ });
+ }
+
+ function claimThisPage() {
+ ok(registration.active, "Worker is active.");
+ var p = new Promise(function (res, rej) {
+ navigator.serviceWorker.oncontrollerchange = res;
+ });
+
+ registration.active.postMessage("Claim");
+
+ return p;
+ }
+
+ function runTest() {
+ register()
+ .then(createClient)
+ .then(testFetch.bind(this, true))
+ .then(claimThisPage)
+ .then(testFetch.bind(this, false))
+ .then(unregister)
+ .catch(function(e) {
+ ok(false, "Some test failed with error " + e);
+ }).then(SimpleTest.finish);
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ SpecialPowers.pushPrefEnv({"set": [
+ ["dom.serviceWorkers.exemptFromPerDomainMax", true],
+ ["dom.serviceWorkers.enabled", true],
+ ["dom.serviceWorkers.testing.enabled", true]
+ ]}, runTest);
+</script>
+</pre>
+</body>
+</html>
+
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1130684 - Test service worker clients.claim oninstall</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+<script class="testbody" type="text/javascript">
+ var registration;
+
+ function register() {
+ return navigator.serviceWorker.register("claim_oninstall_worker.js",
+ { scope: "./" })
+ .then((swr) => registration = swr);
+ }
+
+
+ function unregister() {
+ return registration.unregister().then(function(result) {
+ ok(result, "Unregister should return true.");
+ });
+ }
+
+ function testClaim() {
+ ok(registration.installing, "Worker should be in installing state");
+
+ navigator.serviceWorker.oncontrollerchange = function() {
+ ok(false, "Claim should not succeed when the worker is not active.");
+ }
+
+ var p = new Promise(function(res, rej) {
+ var worker = registration.installing;
+ worker.onstatechange = function(e) {
+ if (worker.state === 'installed') {
+ is(worker, registration.waiting, "Worker should be in waiting state");
+ } else if (worker.state === 'activated') {
+ // The worker will become active only if claim will reject inside the
+ // install handler.
+ is(worker, registration.active,
+ "Claim should reject if the worker is not active");
+ ok(navigator.serviceWorker.controller === null, "Client is not controlled.");
+ e.target.onstatechange = null;
+ res();
+ }
+ }
+ });
+
+ return p;
+ }
+
+ function runTest() {
+ register()
+ .then(testClaim)
+ .then(unregister)
+ .catch(function(e) {
+ ok(false, "Some test failed with error " + e);
+ }).then(SimpleTest.finish);
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ SpecialPowers.pushPrefEnv({"set": [
+ ["dom.serviceWorkers.exemptFromPerDomainMax", true],
+ ["dom.serviceWorkers.enabled", true],
+ ["dom.serviceWorkers.testing.enabled", true]
+ ]}, runTest);
+</script>
+</pre>
+</body>
+</html>
+
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1130686 - Test service worker client.focus </title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+<!--
+ This test checks that client.focus is available.
+ Actual focusing is tested by test_notificationclick_focus.html since only notification events have permission to change focus.
+-->
+</head>
+<body>
+<p id="display"></p>
+<div id="content"></div>
+<pre id="test"></pre>
+<script class="testbody" type="text/javascript">
+ var registration;
+ var worker;
+
+ function start() {
+ return navigator.serviceWorker.register("client_focus_worker.js",
+ { scope: "./sw_clients/focus_stealing_client.html" })
+ .then((swr) => registration = swr);
+ }
+
+ function unregister() {
+ return registration.unregister().then(function(result) {
+ ok(result, "Unregister should return true.");
+ }, function(e) {
+ dump("Unregistering the SW failed with " + e + "\n");
+ });
+ }
+
+ function loseFocus() {
+ var p = new Promise(function(res, rej) {
+ window.onmessage = function(e) {
+ if (e.data == "READY") {
+ ok(true, "iframe created.");
+ iframe.contentWindow.focus();
+ }
+ }
+ window.onblur = function() {
+ ok(true, "blurred");
+ res();
+ }
+ });
+
+ content = document.getElementById("content");
+ ok(content, "parent exists.");
+
+ iframe = document.createElement("iframe");
+ content.appendChild(iframe);
+
+ iframe.setAttribute('src', "sw_clients/focus_stealing_client.html");
+ return p;
+ }
+
+ function testFocus() {
+ var p = new Promise(function(res, rej) {
+ navigator.serviceWorker.onmessage = function(e) {
+ ok(e.data, "client object is marked as focused.");
+ ok(document.hasFocus(), "document has focus.");
+ res();
+ }
+ });
+
+ ok(registration.active, "active worker exists.");
+ registration.active.postMessage("go");
+ return p;
+ }
+
+ function runTest() {
+ start()
+ .then(loseFocus)
+ .then(testFocus)
+ .then(unregister)
+ .catch(function(e) {
+ ok(false, "Some test failed with error " + e);
+ }).then(SimpleTest.finish);
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ SpecialPowers.pushPrefEnv({"set": [
+ ["dom.serviceWorkers.exemptFromPerDomainMax", true],
+ ["dom.serviceWorkers.enabled", true],
+ ["dom.serviceWorkers.testing.enabled", true],
+ ]}, runTest);
+</script>
+</pre>
+</body>
+</html>
+
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1131353 - test WorkerGlobalScope.close() on service workers</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+<iframe></iframe>
+</div>
+<pre id="test"></pre>
+<script class="testbody" type="text/javascript">
+
+ var iframe;
+ function runTest() {
+ navigator.serviceWorker.ready.then(setupSW);
+ navigator.serviceWorker.register("close_test.js", {scope: "."});
+
+ function setupSW(registration) {
+ var worker = registration.waiting ||
+ registration.active;
+ var iframe = document.createElement("iframe");
+ iframe.src = "message_receiver.html";
+ iframe.onload = function() {
+ worker.postMessage({ message: "start" });
+ };
+ document.body.appendChild(iframe);
+ }
+
+ window.onmessage = function(e) {
+ if (e.data.status == "ok") {
+ ok(e.data.result, e.data.message);
+ } else if (e.data.status == "done") {
+ navigator.serviceWorker.getRegistration().then(function(registration) {
+ registration.unregister().then(function(result) {
+ ok(result, "Unregistering the service worker should succeed");
+ SimpleTest.finish();
+ }, function(e) {
+ dump("Unregistering the SW failed with " + e + "\n");
+ SimpleTest.finish();
+ });
+ });
+ }
+ };
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ onload = function() {
+ SpecialPowers.pushPrefEnv({"set": [
+ ["dom.serviceWorkers.exemptFromPerDomainMax", true],
+ ["dom.serviceWorkers.enabled", true],
+ ["dom.serviceWorkers.testing.enabled", true],
+ ]}, runTest);
+ };
+</script>
+</pre>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1002570 - test controller instance.</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+<script class="testbody" type="text/javascript">
+
+ var content;
+ var iframe;
+ var registration;
+
+ function simpleRegister() {
+ // We use the control scope for the less specific registration. The window will register a worker on controller/
+ return navigator.serviceWorker.register("worker.js", { scope: "./control" })
+ .then(function(reg) {
+ registration = reg;
+ });;
+ }
+
+ function unregister() {
+ return registration.unregister().then(function(result) {
+ ok(result, "Unregister should return true.");
+ }, function(e) {
+ dump("Unregistering the SW failed: " + e + "\n");
+ });
+ }
+
+ function testController() {
+ var p = new Promise(function(resolve, reject) {
+ window.onmessage = function(e) {
+ if (e.data.status == "ok") {
+ ok(e.data.result, e.data.message);
+ } else if (e.data.status == "done") {
+ window.onmessage = null;
+ content.removeChild(iframe);
+ resolve();
+ }
+ }
+ });
+
+ content = document.getElementById("content");
+ ok(content, "Parent exists.");
+
+ iframe = document.createElement("iframe");
+ iframe.setAttribute('src', "controller/index.html");
+ content.appendChild(iframe);
+
+ return p;
+ }
+
+ // This document just flips the prefs and opens the iframe for the actual test.
+ function runTest() {
+ simpleRegister()
+ .then(testController)
+ .then(unregister)
+ .then(function() {
+ SimpleTest.finish();
+ }).catch(function(e) {
+ ok(false, "Some test failed with error " + e);
+ SimpleTest.finish();
+ });
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ SpecialPowers.pushPrefEnv({"set": [
+ ["dom.serviceWorkers.exemptFromPerDomainMax", true],
+ ["dom.serviceWorkers.enabled", true],
+ ["dom.serviceWorkers.testing.enabled", true]
+ ]}, runTest);
+</script>
+</pre>
+</body>
+</html>
+
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test access to a cross origin Request.url property from a service worker for a redirected intercepted iframe</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+<iframe></iframe>
+</div>
+<pre id="test"></pre>
+<script class="testbody" type="text/javascript">
+
+ var iframe;
+ function runTest() {
+ iframe = document.querySelector("iframe");
+ iframe.src = "/tests/dom/workers/test/serviceworkers/fetch/requesturl/register.html";
+ window.onmessage = function(e) {
+ if (e.data.status == "ok") {
+ ok(e.data.result, e.data.message);
+ } else if (e.data.status == "registrationdone") {
+ iframe.src = "/tests/dom/workers/test/serviceworkers/fetch/requesturl/index.html";
+ } else if (e.data.status == "done") {
+ iframe.src = "/tests/dom/workers/test/serviceworkers/fetch/requesturl/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": [
+ ["dom.serviceWorkers.exemptFromPerDomainMax", true],
+ ["dom.serviceWorkers.enabled", true],
+ ["dom.serviceWorkers.testing.enabled", true],
+ ]}, runTest);
+ };
+</script>
+</pre>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test that a CSP upgraded request can be intercepted by a service worker</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content">
+<iframe></iframe>
+</div>
+<pre id="test"></pre>
+<script class="testbody" type="text/javascript">
+
+ var iframe;
+ function runTest() {
+ iframe = document.querySelector("iframe");
+ iframe.src = "https://example.com/tests/dom/workers/test/serviceworkers/fetch/upgrade-insecure/register.html";
+ window.onmessage = function(e) {
+ if (e.data.status == "ok") {
+ ok(e.data.result, e.data.message);
+ } else if (e.data.status == "registrationdone") {
+ iframe.src = "https://example.com/tests/dom/workers/test/serviceworkers/fetch/upgrade-insecure/embedder.html";
+ } else if (e.data.status == "protocol") {
+ is(e.data.data, "https:", "Correct protocol expected");
+ } else if (e.data.status == "image") {
+ is(e.data.data, 40, "The image request was upgraded before interception");
+ iframe.src = "https://example.com/tests/dom/workers/test/serviceworkers/fetch/upgrade-insecure/unregister.html";
+ } else if (e.data.status == "unregistrationdone") {
+ window.onmessage = null;
+ SimpleTest.finish();
+ }
+ };
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ onload = function() {
+ SpecialPowers.pushPrefEnv({"set": [
+ ["dom.serviceWorkers.exemptFromPerDomainMax", true],
+ ["dom.serviceWorkers.enabled", true],
+ ["dom.serviceWorkers.testing.enabled", true],
+ // This is needed so that we can test upgrading a non-secure load inside an https iframe.
+ ["security.mixed_content.block_active_content", false],
+ ["security.mixed_content.block_display_content", false],
+ ]}, runTest);
+ };
+</script>
+</pre>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test that registering an empty service worker works</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test"></pre>
+<script class="testbody" type="text/javascript">
+
+ function runTest() {
+ navigator.serviceWorker.ready.then(done);
+ navigator.serviceWorker.register("empty.js", {scope: "."});
+ }
+
+ function done(registration) {
+ ok(registration.waiting || registration.active, "registration worked");
+ registration.unregister().then(function(success) {
+ ok(success, "unregister worked");
+ SimpleTest.finish();
+ }, function(e) {
+ dump("Unregistering the SW failed with " + e + "\n");
+ SimpleTest.finish();
+ });
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ onload = function() {
+ SpecialPowers.pushPrefEnv({"set": [
+ ["dom.serviceWorkers.exemptFromPerDomainMax", true],
+ ["dom.serviceWorkers.enabled", true],
+ ["dom.serviceWorkers.testing.enabled", true]
+ ]}, runTest);
+ };
+</script>
+</pre>
+</body>
+</html>
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 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test Error Reporting of Service Worker Failures</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/SpawnTask.js"></script>
+ <script src="error_reporting_helpers.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
+ <meta http-equiv="Content-type" content="text/html;charset=UTF-8">
+</head>
+<body>
+
+<script type="text/javascript">
+"use strict";
+
+/**
+ * Test that a bunch of service worker coding errors and failure modes that
+ * might otherwise be hard to diagnose are surfaced as console error messages.
+ * The driving use-case is minimizing cursing from a developer looking at a
+ * document in Firefox testing a page that involves service workers.
+ *
+ * This test assumes that errors will be reported via
+ * ServiceWorkerManager::ReportToAllClients and that that method is reliable and
+ * tested via some other file.
+ **/
+
+add_task(function setupPrefs() {
+ return SpecialPowers.pushPrefEnv({"set": [
+ ["dom.serviceWorkers.enabled", true],
+ ["dom.serviceWorkers.testing.enabled", true],
+ ["dom.caches.testing.enabled", true],
+ ]});
+});
+
+/**
+ * Ensure an error is logged during the initial registration of a SW when a 404
+ * is received.
+ */
+add_task(function* register_404() {
+ // Start monitoring for the error
+ let expectedMessage = expect_console_message(
+ "ServiceWorkerRegisterNetworkError",
+ [make_absolute_url("network_error/"), "404", make_absolute_url("404.js")]);
+
+ // Register, generating the 404 error. This will reject with a TypeError
+ // which we need to consume so it doesn't get thrown at our generator.
+ yield navigator.serviceWorker.register("404.js", { scope: "network_error/" })
+ .then(
+ () => { ok(false, "should have rejected"); },
+ (e) => { ok(e.name === "TypeError", "404 failed as expected"); });
+
+ yield wait_for_expected_message(expectedMessage);
+});
+
+/**
+ * Ensure an error is logged when the service worker is being served with a
+ * MIME type of text/plain rather than a JS type.
+ */
+add_task(function* register_bad_mime_type() {
+ let expectedMessage = expect_console_message(
+ "ServiceWorkerRegisterMimeTypeError",
+ [make_absolute_url("bad_mime_type/"), "text/plain",
+ make_absolute_url("sw_bad_mime_type.js")]);
+
+ // consume the expected rejection so it doesn't get thrown at us.
+ yield navigator.serviceWorker.register("sw_bad_mime_type.js", { scope: "bad_mime_type/" })
+ .then(
+ () => { ok(false, "should have rejected"); },
+ (e) => { ok(e.name === "SecurityError", "bad MIME type failed as expected"); });
+
+ yield wait_for_expected_message(expectedMessage);
+});
+</script>
+
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for escaped slashes in navigator.serviceWorker.register</title>
+ <script type="text/javascript" src="http://mochi.test:8888/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="http://mochi.test:8888/tests/SimpleTest/test.css" />
+ <base href="https://mozilla.org/">
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test"></pre>
+<script class="testbody" type="text/javascript">
+
+var tests = [
+ { status: true,
+ scriptURL: "a.js?foo%2fbar",
+ scopeURL: null },
+ { status: false,
+ scriptURL: "foo%2fbar",
+ scopeURL: null },
+ { status: true,
+ scriptURL: "a.js?foo%2Fbar",
+ scopeURL: null },
+ { status: false,
+ scriptURL: "foo%2Fbar",
+ scopeURL: null },
+ { status: true,
+ scriptURL: "a.js?foo%5cbar",
+ scopeURL: null },
+ { status: false,
+ scriptURL: "foo%5cbar",
+ scopeURL: null },
+ { status: true,
+ scriptURL: "a.js?foo%2Cbar",
+ scopeURL: null },
+ { status: false,
+ scriptURL: "foo%5Cbar",
+ scopeURL: null },
+ { status: true,
+ scriptURL: "ok.js",
+ scopeURL: "/scope?foo%2fbar"},
+ { status: false,
+ scriptURL: "ok.js",
+ scopeURL: "/foo%2fbar"},
+ { status: true,
+ scriptURL: "ok.js",
+ scopeURL: "/scope?foo%2Fbar"},
+ { status: false,
+ scriptURL: "ok.js",
+ scopeURL: "foo%2Fbar"},
+ { status: true,
+ scriptURL: "ok.js",
+ scopeURL: "/scope?foo%5cbar"},
+ { status: false,
+ scriptURL: "ok.js",
+ scopeURL: "foo%5cbar"},
+ { status: true,
+ scriptURL: "ok.js",
+ scopeURL: "/scope?foo%5Cbar"},
+ { status: false,
+ scriptURL: "ok.js",
+ scopeURL: "foo%5Cbar"},
+];
+
+function runTest() {
+ if (!tests.length) {
+ SimpleTest.finish();
+ return;
+ }
+
+ var test = tests.shift();
+ navigator.serviceWorker.register(test.scriptURL, test.scopeURL)
+ .then(reg => {
+ ok(false, "Register should fail");
+ }, err => {
+ if (!test.status) {
+ is(err.name, "TypeError", "Registration should fail with TypeError");
+ } else {
+ ok(test.status, "Register should fail");
+ }
+ })
+ .then(runTest);
+}
+
+SimpleTest.waitForExplicitFinish();
+onload = function() {
+ SpecialPowers.pushPrefEnv({"set": [
+ ["dom.serviceWorkers.exemptFromPerDomainMax", true],
+ ["dom.serviceWorkers.testing.enabled", true],
+ ["dom.serviceWorkers.enabled", true],
+ ]}, runTest);
+};
+
+</script>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1160458 - CSP activated by default in Service Workers</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+<script class="testbody" type="text/javascript">
+ function register() {
+ return navigator.serviceWorker.register("eval_worker.js");
+ }
+
+ function runTest() {
+ try {
+ eval("1");
+ ok(false, "should throw");
+ }
+ catch (ex) {
+ ok(true, "did throw");
+ }
+ register()
+ .then(function(swr) {
+ ok(true, "eval restriction didn't get inherited");
+ swr.unregister()
+ .then(function() {
+ SimpleTest.finish();
+ });
+ }).catch(function(e) {
+ ok(false, "Some test failed with error " + e);
+ SimpleTest.finish();
+ });
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ SpecialPowers.pushPrefEnv({"set": [
+ ["dom.serviceWorkers.exemptFromPerDomainMax", true],
+ ["dom.serviceWorkers.enabled", true],
+ ["dom.serviceWorkers.testing.enabled", true],
+ ]}, runTest);
+</script>
+</pre>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1182103 - Test EventSource scenarios with fetch interception</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+<script class="testbody" type="text/javascript">
+
+ function testFrame(src) {
+ return new Promise(function(resolve, reject) {
+ var iframe = document.createElement("iframe");
+ iframe.src = src;
+ window.onmessage = function(e) {
+ if (e.data.status == "callback") {
+ switch(e.data.data) {
+ case "ok":
+ ok(e.data.condition, e.data.message);
+ break;
+ case "ready":
+ iframe.contentWindow.postMessage({status: "callback", data: "eventsource"}, "*");
+ break;
+ case "done":
+ window.onmessage = null;
+ iframe.src = "about:blank";
+ document.body.removeChild(iframe);
+ iframe = null;
+ resolve();
+ break;
+ default:
+ ok(false, "Something went wrong");
+ break;
+ }
+ } else {
+ ok(false, "Something went wrong");
+ }
+ };
+ document.body.appendChild(iframe);
+ });
+ }
+
+ function runTest() {
+ Promise.resolve()
+ .then(() => {
+ info("Going to intercept and test opaque responses");
+ return testFrame("eventsource/eventsource_register_worker.html" +
+ "?script=eventsource_opaque_response_intercept_worker.js");
+ })
+ .then(() => {
+ return testFrame("eventsource/eventsource_opaque_response.html");
+ })
+ .then(() => {
+ info("Going to intercept and test cors responses");
+ return testFrame("eventsource/eventsource_register_worker.html" +
+ "?script=eventsource_cors_response_intercept_worker.js");
+ })
+ .then(() => {
+ return testFrame("eventsource/eventsource_cors_response.html");
+ })
+ .then(() => {
+ info("Going to intercept and test synthetic responses");
+ return testFrame("eventsource/eventsource_register_worker.html" +
+ "?script=eventsource_synthetic_response_intercept_worker.js");
+ })
+ .then(() => {
+ return testFrame("eventsource/eventsource_synthetic_response.html");
+ })
+ .then(() => {
+ info("Going to intercept and test mixed content cors responses");
+ return testFrame("https://example.com/tests/dom/workers/test/serviceworkers/" +
+ "eventsource/eventsource_register_worker.html" +
+ "?script=eventsource_mixed_content_cors_response_intercept_worker.js");
+ })
+ .then(() => {
+ return testFrame("https://example.com/tests/dom/workers/test/serviceworkers/" +
+ "eventsource/eventsource_mixed_content_cors_response.html");
+ })
+ .then(SimpleTest.finish)
+ .catch(function(e) {
+ ok(false, "Some test failed with error " + e);
+ SimpleTest.finish();
+ });
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ SpecialPowers.pushPrefEnv({"set": [
+ ["dom.serviceWorkers.exemptFromPerDomainMax", true],
+ ["dom.serviceWorkers.enabled", true],
+ ["dom.serviceWorkers.testing.enabled", true],
+ ["dom.caches.enabled", true],
+ ]}, runTest);
+</script>
+</pre>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 94048 - test install event.</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+<script class="testbody" type="text/javascript">
+ SimpleTest.requestCompleteLog();
+
+ var registration;
+ function simpleRegister() {
+ var p = navigator.serviceWorker.register("fetch_event_worker.js", { scope: "./fetch" });
+ return p.then(function(swr) {
+ registration = swr;
+ return new Promise(function(resolve) {
+ swr.installing.onstatechange = resolve;
+ });
+ });
+ }
+
+ function unregister() {
+ return registration.unregister().then(function(success) {
+ ok(success, "Service worker should be unregistered successfully");
+ }, function(e) {
+ dump("SW unregistration error: " + e + "\n");
+ });
+ }
+
+ function testController() {
+ var p = new Promise(function(resolve, reject) {
+ var reloaded = false;
+ window.onmessage = function(e) {
+ if (e.data.status == "ok") {
+ ok(e.data.result, e.data.message);
+ } else if (e.data.status == "done") {
+ if (reloaded) {
+ window.onmessage = null;
+ w.close();
+ resolve();
+ } else {
+ w.location.reload();
+ reloaded = true;
+ }
+ }
+ }
+ });
+
+ var w = window.open("fetch/index.html");
+ return p;
+ }
+
+ function runTest() {
+ simpleRegister()
+ .then(testController)
+ .then(unregister)
+ .then(function() {
+ SimpleTest.finish();
+ }).catch(function(e) {
+ ok(false, "Some test failed with error " + e);
+ SimpleTest.finish();
+ });
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ SpecialPowers.pushPrefEnv({"set": [
+ ["dom.serviceWorkers.exemptFromPerDomainMax", true],
+ ["dom.serviceWorkers.enabled", true],
+ ["dom.serviceWorkers.testing.enabled", true],
+ ]}, runTest);
+</script>
+</pre>
+</body>
+</html>
+
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 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title> Test fetch.integrity on console report for serviceWorker and sharedWorker </title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/SpawnTask.js"></script>
+ <script src="error_reporting_helpers.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
+ <meta http-equiv="Content-type" content="text/html;charset=UTF-8">
+</head>
+<body>
+<div id="content" style="display: none"></div>
+<script type="text/javascript">
+"use strict";
+
+let security_localizer =
+ stringBundleService.createBundle("chrome://global/locale/security/security.properties");
+
+function expect_security_console_message(/* msgId, args, ... */) {
+ let expectations = [];
+ // process repeated paired arguments of: msgId, args
+ for (let i = 0; i < arguments.length; i += 4) {
+ let msgId = arguments[i];
+ let args = arguments[i + 1];
+ let filename = arguments[i + 2];
+ let windowId = arguments[i + 3];
+ expectations.push({
+ errorMessage: security_localizer.formatStringFromName(msgId, args, args.length),
+ sourceName: filename,
+ windowID: windowId
+ });
+ }
+ return new Promise(resolve => {
+ SimpleTest.monitorConsole(resolve, expectations);
+ });
+}
+
+// (This doesn't really need to be its own task, but it allows the actual test
+// case to be self-contained.)
+add_task(function setupPrefs() {
+ return SpecialPowers.pushPrefEnv({"set": [
+ ["dom.serviceWorkers.enabled", true],
+ ["dom.serviceWorkers.testing.enabled", true],
+ ]});
+});
+
+add_task(function* test_integrity_serviceWorker() {
+ var filename = make_absolute_url("fetch.js");
+ var filename2 = make_absolute_url("fake.html");
+
+ // The SW will claim us once it activates; this is async, start listening now.
+ let waitForControlled = new Promise((resolve) => {
+ navigator.serviceWorker.oncontrollerchange = resolve;
+ });
+
+ let registration = yield navigator.serviceWorker.register("fetch.js",
+ { scope: "./" });
+ yield waitForControlled;
+
+ info("Test for mNavigationInterceptions.")
+ // The client_win will reload to another URL after opening filename2.
+ let client_win = window.open(filename2);
+
+ // XXX windowID should be innerWindowID
+ let mainWindowID = SpecialPowers.getDOMWindowUtils(window).outerWindowID;
+ let clientWindowID = SpecialPowers.getDOMWindowUtils(client_win).outerWindowID;
+ let expectedMessage = expect_security_console_message(
+ "MalformedIntegrityHash",
+ ["abc"],
+ filename,
+ mainWindowID,
+ "NoValidMetadata",
+ [""],
+ filename,
+ mainWindowID
+ );
+ let expectedMessage2 = expect_security_console_message(
+ "MalformedIntegrityHash",
+ ["abc"],
+ filename,
+ clientWindowID,
+ "NoValidMetadata",
+ [""],
+ filename,
+ clientWindowID
+ );
+
+ info("Test for mControlledDocuments and report error message to console.");
+ // The fetch will succeed because the integrity value is invalid and we are
+ // looking for the console message regarding the bad integrity value.
+ yield fetch("fail.html");
+
+ yield wait_for_expected_message(expectedMessage);
+
+ yield wait_for_expected_message(expectedMessage2);
+
+ yield registration.unregister();
+ client_win.close();
+});
+
+add_task(function* test_integrity_sharedWorker() {
+ var filename = make_absolute_url("sharedWorker_fetch.js");
+
+ info("Attch main window to a SharedWorker.");
+ let sharedWorker = new SharedWorker(filename);
+ let waitForConnected = new Promise((resolve) => {
+ sharedWorker.port.onmessage = function (e) {
+ if (e.data == "Connected") {
+ resolve();
+ } else {
+ reject();
+ }
+ }
+ });
+ yield waitForConnected;
+
+ info("Attch another window to the same SharedWorker.");
+ // Open another window and its also managed by the shared worker.
+ let client_win = window.open("create_another_sharedWorker.html");
+ let waitForBothConnected = new Promise((resolve) => {
+ sharedWorker.port.onmessage = function (e) {
+ if (e.data == "BothConnected") {
+ resolve();
+ } else {
+ reject();
+ }
+ }
+ });
+ yield waitForBothConnected;
+
+ // XXX windowID should be innerWindowID
+ let mainWindowID = SpecialPowers.getDOMWindowUtils(window).outerWindowID;
+ let clientWindowID = SpecialPowers.getDOMWindowUtils(client_win).outerWindowID;
+ let expectedMessage = expect_security_console_message(
+ "MalformedIntegrityHash",
+ ["abc"],
+ filename,
+ mainWindowID,
+ "NoValidMetadata",
+ [""],
+ filename,
+ mainWindowID
+ );
+ let expectedMessage2 = expect_security_console_message(
+ "MalformedIntegrityHash",
+ ["abc"],
+ filename,
+ clientWindowID,
+ "NoValidMetadata",
+ [""],
+ filename,
+ clientWindowID
+ );
+
+ info("Start to fetch a URL with wrong integrity.")
+ sharedWorker.port.start();
+ sharedWorker.port.postMessage("StartFetchWithWrongIntegrity");
+
+ let waitForSRIFailed = new Promise((resolve) => {
+ sharedWorker.port.onmessage = function (e) {
+ if (e.data == "SRI_failed") {
+ resolve();
+ } else {
+ reject();
+ }
+ }
+ });
+ yield waitForSRIFailed;
+
+ yield wait_for_expected_message(expectedMessage);
+
+ yield wait_for_expected_message(expectedMessage2);
+ client_win.close();
+});
+
+</script>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1253777 - Test interception using file blob response body</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+<script class="testbody" type="text/javascript">
+ var registration;
+ var scope = './file_blob_response/';
+ function start() {
+ return navigator.serviceWorker.register("file_blob_response_worker.js",
+ { scope: scope })
+ .then(function(swr) {
+ registration = swr;
+ return new Promise(function(resolve) {
+ registration.installing.onstatechange = function(evt) {
+ if (evt.target.state === 'activated') {
+ evt.target.onstate = null;
+ resolve();
+ }
+ }
+ });
+ });
+ }
+
+ function unregister() {
+ return registration.unregister().then(function(result) {
+ ok(result, "Unregister should return true.");
+ }, function(e) {
+ ok(false, "Unregistering the SW failed with " + e + "\n");
+ });
+ }
+
+ function withFrame(url) {
+ return new Promise(function(resolve, reject) {
+ var content = document.getElementById("content");
+ ok(content, "Parent exists.");
+
+ var frame = document.createElement("iframe");
+ frame.setAttribute('src', url);
+ content.appendChild(frame);
+
+ frame.addEventListener('load', function loadCallback(evt) {
+ frame.removeEventListener('load', loadCallback);
+ resolve(frame);
+ });
+ });
+ }
+
+ function runTest() {
+ start()
+ .then(function() {
+ return withFrame(scope + 'dummy.txt');
+ })
+ .then(function(frame) {
+ var result = JSON.parse(frame.contentWindow.document.body.textContent);
+ frame.remove();
+ is(result.value, 'success');
+ })
+ .catch(function(e) {
+ ok(false, "Some test failed with error " + e);
+ })
+ .then(unregister)
+ .then(SimpleTest.finish);
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ SpecialPowers.pushPrefEnv({"set": [
+ ["dom.serviceWorkers.exemptFromPerDomainMax", true],
+ ["dom.serviceWorkers.enabled", true],
+ ["dom.serviceWorkers.testing.enabled", true]
+ ]}, runTest);
+</script>
+</pre>
+</body>
+</html>
+
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1203680 - Test interception of file blob uploads</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+<script class="testbody" type="text/javascript">
+ var registration;
+ var iframe;
+ function start() {
+ return navigator.serviceWorker.register("empty.js",
+ { scope: "./sw_clients/" })
+ .then((swr) => registration = swr);
+ }
+
+ function unregister() {
+ if (iframe) {
+ iframe.remove();
+ iframe = null;
+ }
+
+ return registration.unregister().then(function(result) {
+ ok(result, "Unregister should return true.");
+ }, function(e) {
+ ok(false, "Unregistering the SW failed with " + e + "\n");
+ });
+ }
+
+ function withFrame() {
+ var content = document.getElementById("content");
+ ok(content, "Parent exists.");
+
+ iframe = document.createElement("iframe");
+ iframe.setAttribute('src', "sw_clients/file_blob_upload_frame.html");
+ content.appendChild(iframe);
+
+ return new Promise(function(resolve, reject) {
+ window.addEventListener('message', function readyCallback(evt) {
+ window.removeEventListener('message', readyCallback);
+ if (evt.data.status === 'READY') {
+ resolve();
+ } else {
+ reject(evt.data.result);
+ }
+ });
+ });
+ }
+
+ function postBlob(body) {
+ return new Promise(function(resolve, reject) {
+ window.addEventListener('message', function postBlobCallback(evt) {
+ window.removeEventListener('message', postBlobCallback);
+ if (evt.data.status === 'OK') {
+ is(JSON.stringify(body), JSON.stringify(evt.data.result),
+ 'body echoed back correctly');
+ resolve();
+ } else {
+ reject(evt.data.result);
+ }
+ });
+
+ iframe.contentWindow.postMessage({ type: 'TEST', body: body }, '*');
+ });
+ }
+
+ function generateMessage(length) {
+
+ var lorem =
+ 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis egestas '
+ 'vehicula tortor eget ultrices. Sed et luctus est. Nunc eu orci ligula. '
+ 'In vel ornare eros, eget lacinia diam. Praesent vel metus mattis, '
+ 'cursus nulla sit amet, rhoncus diam. Aliquam nulla tortor, aliquet et '
+ 'viverra non, dignissim vel tellus. Praesent sed ex in dolor aliquet '
+ 'aliquet. In at facilisis sem, et aliquet eros. Maecenas feugiat nisl '
+ 'quis elit blandit posuere. Duis viverra odio sed eros consectetur, '
+ 'viverra mattis ligula volutpat.';
+
+ var result = '';
+
+ while (result.length < length) {
+ var remaining = length - result.length;
+ if (remaining < lorem.length) {
+ result += lorem.slice(0, remaining);
+ } else {
+ result += lorem;
+ }
+ }
+
+ return result;
+ }
+
+ var smallBody = generateMessage(64);
+ var mediumBody = generateMessage(1024);
+
+ // TODO: Test large bodies over the default pipe size. Currently stalls
+ // due to bug 1134372.
+ //var largeBody = generateMessage(100 * 1024);
+
+ function runTest() {
+ start()
+ .then(withFrame)
+ .then(function() {
+ return postBlob({ hops: 0, message: smallBody });
+ })
+ .then(function() {
+ return postBlob({ hops: 1, message: smallBody });
+ })
+ .then(function() {
+ return postBlob({ hops: 10, message: smallBody });
+ })
+ .then(function() {
+ return postBlob({ hops: 0, message: mediumBody });
+ })
+ .then(function() {
+ return postBlob({ hops: 1, message: mediumBody });
+ })
+ .then(function() {
+ return postBlob({ hops: 10, message: mediumBody });
+ })
+ .then(unregister)
+ .catch(function(e) {
+ ok(false, "Some test failed with error " + e);
+ }).then(SimpleTest.finish);
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ SpecialPowers.pushPrefEnv({"set": [
+ ["dom.serviceWorkers.exemptFromPerDomainMax", true],
+ ["dom.serviceWorkers.enabled", true],
+ ["dom.serviceWorkers.testing.enabled", true]
+ ]}, runTest);
+</script>
+</pre>
+</body>
+</html>
+
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 982726 - Test service worker post message </title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+<script class="testbody" type="text/javascript">
+ var registration;
+ function start() {
+ return navigator.serviceWorker.register("force_refresh_worker.js",
+ { scope: "./sw_clients/" })
+ .then((swr) => registration = swr);
+ }
+
+ function unregister() {
+ return registration.unregister().then(function(result) {
+ ok(result, "Unregister should return true.");
+ }, function(e) {
+ dump("Unregistering the SW failed with " + e + "\n");
+ });
+ }
+
+
+ function testForceRefresh(swr) {
+ var p = new Promise(function(res, rej) {
+ var count = 0;
+ var cachedCount = 0;
+ window.onmessage = function(e) {
+ if (e.data === "READY") {
+ count += 1;
+ if (count == 2) {
+ is(cachedCount, 1, "should have received cached message before " +
+ "second non-cached message");
+ res();
+ }
+ iframe.contentWindow.postMessage("REFRESH", "*");
+ } else if (e.data === "READY_CACHED") {
+ cachedCount += 1;
+ is(count, 1, "should have received non-cached message before " +
+ "cached message");
+ iframe.contentWindow.postMessage("FORCE_REFRESH", "*");
+ }
+ }
+ });
+
+ var content = document.getElementById("content");
+ ok(content, "Parent exists.");
+
+ iframe = document.createElement("iframe");
+ iframe.setAttribute('src', "sw_clients/refresher_compressed.html");
+ content.appendChild(iframe);
+
+ return p.then(() => content.removeChild(iframe));
+ }
+
+ function runTest() {
+ start()
+ .then(testForceRefresh)
+ .then(unregister)
+ .catch(function(e) {
+ ok(false, "Some test failed with error " + e);
+ }).then(SimpleTest.finish);
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ SpecialPowers.pushPrefEnv({"set": [
+ ["dom.serviceWorkers.exemptFromPerDomainMax", true],
+ ["dom.serviceWorkers.enabled", true],
+ ["dom.serviceWorkers.testing.enabled", true],
+ ["dom.caches.enabled", true],
+ ]}, runTest);
+</script>
+</pre>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 982726 - Test service worker post message </title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+<script class="testbody" type="text/javascript">
+ var registration;
+ function start() {
+ return navigator.serviceWorker.register("gzip_redirect_worker.js",
+ { scope: "./sw_clients/" })
+ .then((swr) => registration = swr);
+ }
+
+ function unregister() {
+ return registration.unregister().then(function(result) {
+ ok(result, "Unregister should return true.");
+ }, function(e) {
+ dump("Unregistering the SW failed with " + e + "\n");
+ });
+ }
+
+
+ function testGzipRedirect(swr) {
+ var p = new Promise(function(res, rej) {
+ var navigatorReady = false;
+ var finalReady = false;
+
+ window.onmessage = function(e) {
+ if (e.data === "NAVIGATOR_READY") {
+ ok(!navigatorReady, "should only get navigator ready message once");
+ ok(!finalReady, "should get navigator ready before final redirect ready message");
+ navigatorReady = true;
+ iframe.contentWindow.postMessage({
+ type: "NAVIGATE",
+ url: "does_not_exist.html"
+ }, "*");
+ } else if (e.data === "READY") {
+ ok(navigatorReady, "should only get navigator ready message once");
+ ok(!finalReady, "should get final ready message only once");
+ finalReady = true;
+ res();
+ }
+ }
+ });
+
+ var content = document.getElementById("content");
+ ok(content, "Parent exists.");
+
+ iframe = document.createElement("iframe");
+ iframe.setAttribute('src', "sw_clients/navigator.html");
+ content.appendChild(iframe);
+
+ return p.then(() => content.removeChild(iframe));
+ }
+
+ function runTest() {
+ start()
+ .then(testGzipRedirect)
+ .then(unregister)
+ .catch(function(e) {
+ ok(false, "Some test failed with error " + e);
+ }).then(SimpleTest.finish);
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ SpecialPowers.pushPrefEnv({"set": [
+ ["dom.serviceWorkers.exemptFromPerDomainMax", true],
+ ["dom.serviceWorkers.enabled", true],
+ ["dom.serviceWorkers.testing.enabled", true],
+ ]}, runTest);
+</script>
+</pre>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test that an HSTS upgraded request can be intercepted by a service worker</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content">
+<iframe></iframe>
+</div>
+<pre id="test"></pre>
+<script class="testbody" type="text/javascript">
+
+ var iframe;
+ var framesLoaded = 0;
+ function runTest() {
+ iframe = document.querySelector("iframe");
+ iframe.src = "https://example.com/tests/dom/workers/test/serviceworkers/fetch/hsts/register.html";
+ window.onmessage = function(e) {
+ if (e.data.status == "ok") {
+ ok(e.data.result, e.data.message);
+ } else if (e.data.status == "registrationdone") {
+ iframe.src = "http://example.com/tests/dom/workers/test/serviceworkers/fetch/hsts/index.html";
+ } else if (e.data.status == "protocol") {
+ is(e.data.data, "https:", "Correct protocol expected");
+ ok(e.data.securityInfoPresent, "Security info present on intercepted value");
+ switch (++framesLoaded) {
+ case 1:
+ iframe.src = "https://example.com/tests/dom/workers/test/serviceworkers/fetch/hsts/embedder.html";
+ break;
+ case 2:
+ iframe.src = "https://example.com/tests/dom/workers/test/serviceworkers/fetch/hsts/image.html";
+ break;
+ }
+ } else if (e.data.status == "image") {
+ is(e.data.data, 40, "The image request was upgraded before interception");
+ iframe.src = "https://example.com/tests/dom/workers/test/serviceworkers/fetch/hsts/unregister.html";
+ } else if (e.data.status == "unregistrationdone") {
+ window.onmessage = null;
+ SpecialPowers.cleanUpSTSData("http://example.com");
+ SimpleTest.finish();
+ }
+ };
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ onload = function() {
+ SpecialPowers.pushPrefEnv({"set": [
+ ["dom.serviceWorkers.exemptFromPerDomainMax", true],
+ ["dom.serviceWorkers.enabled", true],
+ ["dom.serviceWorkers.testing.enabled", true],
+ // This is needed so that we can test upgrading a non-secure load inside an https iframe.
+ ["security.mixed_content.block_active_content", false],
+ ["security.mixed_content.block_display_content", false],
+ ]}, runTest);
+ };
+</script>
+</pre>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1133763 - test fetch event in HTTPS origins</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+<iframe></iframe>
+</div>
+<pre id="test"></pre>
+<script class="testbody" type="text/javascript">
+
+ var iframe;
+ function runTest() {
+ iframe = document.querySelector("iframe");
+ iframe.src = "https://example.com/tests/dom/workers/test/serviceworkers/fetch/https/register.html";
+ var ios;
+ window.onmessage = function(e) {
+ if (e.data.status == "ok") {
+ ok(e.data.result, e.data.message);
+ } else if (e.data.status == "registrationdone") {
+ ios = SpecialPowers.Cc["@mozilla.org/network/io-service;1"]
+ .getService(SpecialPowers.Ci.nsIIOService);
+ ios.offline = true;
+ iframe.src = "https://example.com/tests/dom/workers/test/serviceworkers/fetch/https/index.html";
+ } else if (e.data.status == "done") {
+ iframe.src = "https://example.com/tests/dom/workers/test/serviceworkers/fetch/https/synth-sw.html";
+ } else if (e.data.status == "done-synth-sw") {
+ iframe.src = "https://example.com/tests/dom/workers/test/serviceworkers/fetch/https/synth-window.html";
+ } else if (e.data.status == "done-synth-window") {
+ iframe.src = "https://example.com/tests/dom/workers/test/serviceworkers/fetch/https/synth.html";
+ } else if (e.data.status == "done-synth") {
+ ios.offline = false;
+ iframe.src = "https://example.com/tests/dom/workers/test/serviceworkers/fetch/https/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": [
+ ["dom.serviceWorkers.exemptFromPerDomainMax", true],
+ ["dom.serviceWorkers.enabled", true],
+ ["dom.serviceWorkers.testing.enabled", true],
+ ["dom.caches.enabled", true]
+ ]}, runTest);
+ };
+</script>
+</pre>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1133763 - test fetch event in HTTPS origins with a cloned response</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+<iframe></iframe>
+</div>
+<pre id="test"></pre>
+<script class="testbody" type="text/javascript">
+
+ var iframe;
+ function runTest() {
+ iframe = document.querySelector("iframe");
+ iframe.src = "https://example.com/tests/dom/workers/test/serviceworkers/fetch/https/clonedresponse/register.html";
+ var ios;
+ window.onmessage = function(e) {
+ if (e.data.status == "ok") {
+ ok(e.data.result, e.data.message);
+ } else if (e.data.status == "registrationdone") {
+ ios = SpecialPowers.Cc["@mozilla.org/network/io-service;1"]
+ .getService(SpecialPowers.Ci.nsIIOService);
+ ios.offline = true;
+ iframe.src = "https://example.com/tests/dom/workers/test/serviceworkers/fetch/https/clonedresponse/index.html";
+ } else if (e.data.status == "done") {
+ ios.offline = false;
+ iframe.src = "https://example.com/tests/dom/workers/test/serviceworkers/fetch/https/clonedresponse/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": [
+ ["dom.serviceWorkers.exemptFromPerDomainMax", true],
+ ["dom.serviceWorkers.enabled", true],
+ ["dom.serviceWorkers.testing.enabled", true],
+ ["dom.caches.enabled", true]
+ ]}, runTest);
+ };
+</script>
+</pre>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test the origin of a redirected response from a service worker</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+<iframe></iframe>
+</div>
+<pre id="test"></pre>
+<script class="testbody" type="text/javascript">
+
+ var iframe;
+ function runTest() {
+ iframe = document.querySelector("iframe");
+ iframe.src = "https://example.com/tests/dom/workers/test/serviceworkers/fetch/origin/https/register.html";
+ var win;
+ window.onmessage = function(e) {
+ if (e.data.status == "ok") {
+ ok(e.data.result, e.data.message);
+ } else if (e.data.status == "registrationdone") {
+ win = window.open("https://example.com/tests/dom/workers/test/serviceworkers/fetch/origin/https/index-https.sjs", "mywindow", "width=100,height=100");
+ } else if (e.data.status == "domain") {
+ is(e.data.data, "example.org", "Correct domain expected");
+ } else if (e.data.status == "origin") {
+ is(e.data.data, "https://example.org", "Correct origin expected");
+ } else if (e.data.status == "done") {
+ win.close();
+ iframe.src = "https://example.com/tests/dom/workers/test/serviceworkers/fetch/origin/https/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": [
+ ["dom.serviceWorkers.exemptFromPerDomainMax", true],
+ ["dom.serviceWorkers.enabled", true],
+ ["dom.serviceWorkers.testing.enabled", true],
+ ["dom.caches.enabled", true],
+ ]}, runTest);
+ };
+</script>
+</pre>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test the origin of a redirected response from a service worker</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+<iframe></iframe>
+</div>
+<pre id="test"></pre>
+<script class="testbody" type="text/javascript">
+
+ var iframe;
+ function runTest() {
+ iframe = document.querySelector("iframe");
+ iframe.src = "https://example.com/tests/dom/workers/test/serviceworkers/fetch/origin/https/register.html";
+ var win;
+ window.onmessage = function(e) {
+ if (e.data.status == "ok") {
+ ok(e.data.result, e.data.message);
+ } else if (e.data.status == "registrationdone") {
+ win = window.open("https://example.com/tests/dom/workers/test/serviceworkers/fetch/origin/https/index-cached-https.sjs", "mywindow", "width=100,height=100");
+ } else if (e.data.status == "domain") {
+ is(e.data.data, "example.org", "Correct domain expected");
+ } else if (e.data.status == "origin") {
+ is(e.data.data, "https://example.org", "Correct origin expected");
+ } else if (e.data.status == "done") {
+ win.close();
+ iframe.src = "https://example.com/tests/dom/workers/test/serviceworkers/fetch/origin/https/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": [
+ ["dom.serviceWorkers.exemptFromPerDomainMax", true],
+ ["dom.serviceWorkers.enabled", true],
+ ["dom.serviceWorkers.testing.enabled", true],
+ ["dom.caches.enabled", true],
+ ]}, runTest);
+ };
+</script>
+</pre>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1156847 - test fetch event generating a synthesized response in HTTPS origins from a cached SW</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" tyle="display: none">
+<iframe></iframe>
+</div>
+<pre id="test"></pre>
+<script class="testbody" type="text/javascript">
+
+ var iframe;
+ function runTest() {
+ iframe = document.querySelector("iframe");
+ iframe.src = "https://example.com/tests/dom/workers/test/serviceworkers/fetch/https/register.html";
+ var ios;
+ window.onmessage = function(e) {
+ if (e.data.status == "ok") {
+ ok(e.data.result, e.data.message);
+ } else if (e.data.status == "registrationdone") {
+ ios = SpecialPowers.Cc["@mozilla.org/network/io-service;1"]
+ .getService(SpecialPowers.Ci.nsIIOService);
+ ios.offline = true;
+
+ // In order to load synth.html from a cached service worker, we first
+ // remove the existing window that is keeping the service worker alive,
+ // and do a GC to ensure that the SW is destroyed. This way, when we
+ // load synth.html for the second time, we will first recreate the
+ // service worker from the cache. This is intended to test that we
+ // properly store and retrieve the security info from the cache.
+ iframe.parentNode.removeChild(iframe);
+ iframe = null;
+ SpecialPowers.exactGC(function() {
+ iframe = document.createElement("iframe");
+ iframe.src = "https://example.com/tests/dom/workers/test/serviceworkers/fetch/https/synth.html";
+ document.body.appendChild(iframe);
+ });
+ } else if (e.data.status == "done-synth") {
+ ios.offline = false;
+ iframe.src = "https://example.com/tests/dom/workers/test/serviceworkers/fetch/https/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": [
+ ["dom.serviceWorkers.exemptFromPerDomainMax", true],
+ ["dom.serviceWorkers.enabled", true],
+ ["dom.serviceWorkers.testing.enabled", true],
+ ["dom.caches.enabled", true]
+ ]}, runTest);
+ };
+</script>
+</pre>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1202085 - Test that images from different controllers don't cached together</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content">
+<iframe></iframe>
+</div>
+<pre id="test"></pre>
+<script class="testbody" type="text/javascript">
+
+ var iframe;
+ function runTest() {
+ iframe = document.querySelector("iframe");
+ iframe.src = "/tests/dom/workers/test/serviceworkers/fetch/imagecache/register.html";
+ window.onmessage = function(e) {
+ if (e.data.status == "ok") {
+ ok(e.data.result, e.data.message);
+ } else if (e.data.status == "registrationdone") {
+ iframe.src = "/tests/dom/workers/test/serviceworkers/fetch/imagecache/index.html";
+ } else if (e.data.status == "result") {
+ is(e.data.url, "image-40px.png", "Correct url expected");
+ is(e.data.width, 40, "Correct width expected");
+ iframe.src = "/tests/dom/workers/test/serviceworkers/fetch/imagecache/unregister.html";
+ } else if (e.data.status == "unregistrationdone") {
+ iframe.src = "/tests/dom/workers/test/serviceworkers/fetch/imagecache/postmortem.html";
+ } else if (e.data.status == "postmortem") {
+ is(e.data.width, 20, "Correct width expected");
+ window.onmessage = null;
+ ok(true, "Test finished successfully");
+ SimpleTest.finish();
+ }
+ };
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ onload = function() {
+ SpecialPowers.pushPrefEnv({"set": [
+ ["dom.serviceWorkers.exemptFromPerDomainMax", true],
+ ["dom.serviceWorkers.enabled", true],
+ ["dom.serviceWorkers.testing.enabled", true],
+ ]}, runTest);
+ };
+</script>
+</pre>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test that the image cache respects a synthesized image's Cache headers</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content">
+<iframe></iframe>
+</div>
+<pre id="test"></pre>
+<script class="testbody" type="text/javascript">
+
+ var iframe;
+ var framesLoaded = 0;
+ function runTest() {
+ iframe = document.querySelector("iframe");
+ iframe.src = "/tests/dom/workers/test/serviceworkers/fetch/imagecache-maxage/register.html";
+ window.onmessage = function(e) {
+ if (e.data.status == "ok") {
+ ok(e.data.result, e.data.message);
+ } else if (e.data.status == "registrationdone") {
+ iframe.src = "/tests/dom/workers/test/serviceworkers/fetch/imagecache-maxage/index.html";
+ } else if (e.data.status == "result") {
+ switch (++framesLoaded) {
+ case 1:
+ is(e.data.url, "image-20px.png", "Correct url expected");
+ is(e.data.url2, "image-20px.png", "Correct url expected");
+ is(e.data.width, 20, "Correct width expected");
+ is(e.data.width2, 20, "Correct width expected");
+ // Wait for 100ms so that the image gets expired.
+ setTimeout(function() {
+ iframe.src = "/tests/dom/workers/test/serviceworkers/fetch/imagecache-maxage/index.html?new"
+ }, 100);
+ break;
+ case 2:
+ is(e.data.url, "image-40px.png", "Correct url expected");
+ is(e.data.url2, "image-40px.png", "Correct url expected");
+ is(e.data.width, 40, "Correct width expected");
+ is(e.data.width2, 40, "Correct width expected");
+ iframe.src = "/tests/dom/workers/test/serviceworkers/fetch/imagecache-maxage/unregister.html";
+ break;
+ default:
+ ok(false, "This should never happen");
+ }
+ } else if (e.data.status == "unregistrationdone") {
+ window.onmessage = null;
+ SimpleTest.finish();
+ }
+ };
+ }
+
+ SimpleTest.requestFlakyTimeout("This test needs to simulate the passing of time");
+ SimpleTest.waitForExplicitFinish();
+ onload = function() {
+ SpecialPowers.pushPrefEnv({"set": [
+ ["dom.serviceWorkers.exemptFromPerDomainMax", true],
+ ["dom.serviceWorkers.enabled", true],
+ ["dom.serviceWorkers.testing.enabled", true],
+ ]}, runTest);
+ };
+</script>
+</pre>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test service worker - script cache policy</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<div id="content"></div>
+<script class="testbody" type="text/javascript">
+ function start() {
+ return navigator.serviceWorker.register("importscript_worker.js",
+ { scope: "./sw_clients/" })
+ .then((swr) => registration = swr);
+ }
+
+ function unregister() {
+ return fetch("importscript.sjs?clearcounter").then(function() {
+ return registration.unregister();
+ }).then(function(result) {
+ ok(result, "Unregister should return true.");
+ }, function(e) {
+ dump("Unregistering the SW failed with " + e + "\n");
+ });
+ }
+
+ function testPostMessage(swr) {
+ var p = new Promise(function(res, rej) {
+ window.onmessage = function(e) {
+ if (e.data === "READY") {
+ swr.active.postMessage("do magic");
+ return;
+ }
+
+ ok(e.data === "OK", "Worker posted the correct value: " + e.data);
+ res();
+ }
+ });
+
+ var content = document.getElementById("content");
+ ok(content, "Parent exists.");
+
+ iframe = document.createElement("iframe");
+ iframe.setAttribute('src', "sw_clients/service_worker_controlled.html");
+ content.appendChild(iframe);
+
+ return p.then(() => content.removeChild(iframe));
+ }
+
+ function runTest() {
+ start()
+ .then(testPostMessage)
+ .then(unregister)
+ .catch(function(e) {
+ ok(false, "Some test failed with error " + e);
+ }).then(SimpleTest.finish);
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ SpecialPowers.pushPrefEnv({"set": [
+ ["dom.serviceWorkers.exemptFromPerDomainMax", true],
+ ["dom.serviceWorkers.enabled", true],
+ ["dom.serviceWorkers.testing.enabled", true]
+ ]}, runTest);
+</script>
+</pre>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1198078 - test that we respect mixed content blocking in importScript() inside service workers</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+<iframe></iframe>
+</div>
+<pre id="test"></pre>
+<script class="testbody" type="text/javascript">
+
+ var iframe;
+ function runTest() {
+ iframe = document.querySelector("iframe");
+ iframe.src = "https://example.com/tests/dom/workers/test/serviceworkers/fetch/importscript-mixedcontent/register.html";
+ var ios;
+ window.onmessage = function(e) {
+ if (e.data.status == "ok") {
+ ok(e.data.result, e.data.message);
+ } else if (e.data.status == "registrationdone") {
+ iframe.src = "https://example.com/tests/dom/workers/test/serviceworkers/fetch/importscript-mixedcontent/index.html";
+ } else if (e.data.status == "done") {
+ ok(e.data.data, "good", "Mixed content blocking should work correctly for service workers");
+ iframe.src = "https://example.com/tests/dom/workers/test/serviceworkers/fetch/importscript-mixedcontent/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": [
+ ["dom.serviceWorkers.exemptFromPerDomainMax", true],
+ ["dom.serviceWorkers.enabled", true],
+ ["dom.serviceWorkers.testing.enabled", true],
+ ["security.mixed_content.block_active_content", false],
+ ]}, runTest);
+ };
+</script>
+</pre>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 94048 - test install event.</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+<script class="testbody" type="text/javascript">
+
+ function simpleRegister() {
+ var p = navigator.serviceWorker.register("worker.js", { scope: "./install_event" });
+ return p;
+ }
+
+ function nextRegister(reg) {
+ ok(reg instanceof ServiceWorkerRegistration, "reg should be a ServiceWorkerRegistration");
+ var p = navigator.serviceWorker.register("install_event_worker.js", { scope: "./install_event" });
+ return p.then(function(swr) {
+ ok(reg === swr, "register should resolve to the same registration object");
+ var update_found_promise = new Promise(function(resolve, reject) {
+ swr.addEventListener('updatefound', function(e) {
+ ok(true, "Received onupdatefound");
+ resolve();
+ });
+ });
+
+ var worker_activating = new Promise(function(res, reject) {
+ ok(swr.installing instanceof ServiceWorker, "There should be an installing worker if promise resolves.");
+ ok(swr.installing.state == "installing", "Installing worker's state should be 'installing'");
+ swr.installing.onstatechange = function(e) {
+ if (e.target.state == "activating") {
+ e.target.onstatechange = null;
+ res();
+ }
+ }
+ });
+
+ return Promise.all([update_found_promise, worker_activating]);
+ }, function(e) {
+ ok(false, "Unexpected Error in nextRegister! " + e);
+ });
+ }
+
+ function installError() {
+ // Silence worker errors so they don't cause the test to fail.
+ window.onerror = function(e) {}
+ return navigator.serviceWorker.register("install_event_error_worker.js", { scope: "./install_event" })
+ .then(function(swr) {
+ ok(swr.installing instanceof ServiceWorker, "There should be an installing worker if promise resolves.");
+ ok(swr.installing.state == "installing", "Installing worker's state should be 'installing'");
+ return new Promise(function(resolve, reject) {
+ swr.installing.onstatechange = function(e) {
+ ok(e.target.state == "redundant", "Installation of worker with error should fail.");
+ resolve();
+ }
+ });
+ }).then(function() {
+ return navigator.serviceWorker.getRegistration("./install_event").then(function(swr) {
+ var newest = swr.waiting || swr.active;
+ ok(newest, "Waiting or active worker should still exist");
+ ok(newest.scriptURL.match(/install_event_worker.js$/), "Previous worker should remain the newest worker");
+ });
+ });
+ }
+
+ function testActive(worker) {
+ is(worker.state, "activating", "Should be activating");
+ return new Promise(function(resolve, reject) {
+ worker.onstatechange = function(e) {
+ e.target.onstatechange = null;
+ is(e.target.state, "activated", "Activation of worker with error in activate event handler should still succeed.");
+ resolve();
+ }
+ });
+ }
+
+ function activateErrorShouldSucceed() {
+ // Silence worker errors so they don't cause the test to fail.
+ window.onerror = function() { }
+ return navigator.serviceWorker.register("activate_event_error_worker.js", { scope: "./activate_error" })
+ .then(function(swr) {
+ var p = new Promise(function(resolve, reject) {
+ ok(swr.installing.state == "installing", "activateErrorShouldSucceed(): Installing worker's state should be 'installing'");
+ swr.installing.onstatechange = function(e) {
+ e.target.onstatechange = null;
+ if (swr.waiting) {
+ swr.waiting.onstatechange = function(e) {
+ e.target.onstatechange = null;
+ testActive(swr.active).then(resolve, reject);
+ }
+ } else {
+ testActive(swr.active).then(resolve, reject);
+ }
+ }
+ });
+
+ return p.then(function() {
+ return Promise.resolve(swr);
+ });
+ }).then(function(swr) {
+ return swr.unregister();
+ });
+ }
+
+ function unregister() {
+ return navigator.serviceWorker.getRegistration("./install_event").then(function(reg) {
+ return reg.unregister();
+ });
+ }
+
+ function runTest() {
+ Promise.resolve()
+ .then(simpleRegister)
+ .then(nextRegister)
+ .then(installError)
+ .then(activateErrorShouldSucceed)
+ .then(unregister)
+ .then(function() {
+ SimpleTest.finish();
+ }).catch(function(e) {
+ ok(false, "Some test failed with error " + e);
+ SimpleTest.finish();
+ });
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ SpecialPowers.pushPrefEnv({"set": [
+ ["dom.serviceWorkers.exemptFromPerDomainMax", true],
+ ["dom.serviceWorkers.enabled", true],
+ ["dom.serviceWorkers.testing.enabled", true]
+ ]}, runTest);
+</script>
+</pre>
+</body>
+</html>
+
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test install event being GC'd before waitUntil fulfills</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<script class="testbody" type="text/javascript">
+var script = 'blocking_install_event_worker.js';
+var scope = 'sw_clients/simple.html?install-event-gc';
+var registration;
+
+function register() {
+ return navigator.serviceWorker.register(script, { scope: scope })
+ .then(swr => registration = swr);
+}
+
+function unregister() {
+ if (!registration) {
+ return;
+ }
+ return registration.unregister();
+}
+
+function waitForInstallEvent() {
+ return new Promise((resolve, reject) => {
+ navigator.serviceWorker.addEventListener('message', evt => {
+ if (evt.data.type === 'INSTALL_EVENT') {
+ resolve();
+ }
+ });
+ });
+}
+
+function gcWorker() {
+ return new Promise(function(resolve, reject) {
+ // We are able to trigger asynchronous garbage collection and cycle
+ // collection by emitting "child-cc-request" and "child-gc-request"
+ // observer notifications. The worker RuntimeService will translate
+ // these notifications into the appropriate operation on all known
+ // worker threads.
+ //
+ // In the failure case where GC/CC causes us to abort the installation,
+ // we will know something happened from the statechange event.
+ const statechangeHandler = evt => {
+ // Reject rather than resolving to avoid the possibility of us seeing
+ // an unrelated racing statechange somehow. Since in the success case we
+ // will still see a state change on termination, we do explicitly need to
+ // be removed on the success path.
+ ok(registration.installing, 'service worker is still installing?');
+ reject();
+ };
+ registration.installing.addEventListener('statechange', statechangeHandler);
+ // In the success case since the service worker installation is effectively
+ // hung, we instead depend on sending a 'ping' message to the service worker
+ // and hearing it 'pong' back. Since we issue our postMessage after we
+ // trigger the GC/CC, our 'ping' will only be processed after the GC/CC and
+ // therefore the pong will also strictly occur after the cycle collection.
+ navigator.serviceWorker.addEventListener('message', evt => {
+ if (evt.data.type === 'pong') {
+ registration.installing.removeEventListener(
+ 'statechange', statechangeHandler);
+ resolve();
+ }
+ });
+ // At the current time, the service worker will exist in our same process
+ // and notifyObservers is synchronous. However, in the future, service
+ // workers may end up in a separate process and in that case it will be
+ // appropriate to use notifyObserversInParentProcess or something like it.
+ // (notifyObserversInParentProcess is a synchronous IPC call to the parent
+ // process's main thread. IPDL PContent::CycleCollect is an async message.
+ // Ordering will be maintained if the postMessage goes via PContent as well,
+ // but that seems unlikely.)
+ SpecialPowers.notifyObservers(null, 'child-gc-request', null);
+ SpecialPowers.notifyObservers(null, 'child-cc-request', null);
+ SpecialPowers.notifyObservers(null, 'child-gc-request', null);
+ // (Only send the ping after we set the gc/cc/gc in motion.)
+ registration.installing.postMessage({ type: 'ping' });
+ });
+}
+
+function terminateWorker() {
+ return SpecialPowers.pushPrefEnv({
+ set: [
+ ["dom.serviceWorkers.idle_timeout", 0],
+ ["dom.serviceWorkers.idle_extended_timeout", 0]
+ ]
+ }).then(_ => {
+ registration.installing.postMessage({ type: 'RESET_TIMER' });
+ });
+}
+
+function runTest() {
+ Promise.all([
+ waitForInstallEvent(),
+ register()
+ ]).then(_ => ok(registration.installing, 'service worker is installing'))
+ .then(gcWorker)
+ .then(_ => ok(registration.installing, 'service worker is still installing'))
+ .then(terminateWorker)
+ .catch(e => ok(false, e))
+ .then(unregister)
+ .then(SimpleTest.finish);
+}
+
+SimpleTest.waitForExplicitFinish();
+SpecialPowers.pushPrefEnv({"set": [
+ ["dom.serviceWorkers.exemptFromPerDomainMax", true],
+ ["dom.serviceWorkers.enabled", true],
+ ["dom.serviceWorkers.testing.enabled", true],
+ ["dom.caches.enabled", true],
+]}, runTest);
+</script>
+</pre>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 930348 - test stub Navigator ServiceWorker utilities.</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+<script class="testbody" type="text/javascript">
+
+ function simpleRegister() {
+ var p = navigator.serviceWorker.register("worker.js", { scope: "simpleregister/" });
+ ok(p instanceof Promise, "register() should return a Promise");
+ return Promise.resolve();
+ }
+
+ function sameOriginWorker() {
+ p = navigator.serviceWorker.register("http://some-other-origin/worker.js");
+ return p.then(function(w) {
+ ok(false, "Worker from different origin should fail");
+ }, function(e) {
+ ok(e.name === "SecurityError", "Should fail with a SecurityError");
+ });
+ }
+
+ function sameOriginScope() {
+ p = navigator.serviceWorker.register("worker.js", { scope: "http://www.example.com/" });
+ return p.then(function(w) {
+ ok(false, "Worker controlling scope for different origin should fail");
+ }, function(e) {
+ ok(e.name === "SecurityError", "Should fail with a SecurityError");
+ });
+ }
+
+ function httpsOnly() {
+ var promise = new Promise(function(resolve) {
+ SpecialPowers.pushPrefEnv({'set': [["dom.serviceWorkers.testing.enabled", false]] }, resolve);
+ });
+
+ return promise.then(function() {
+ return navigator.serviceWorker.register("/worker.js");
+ }).then(function(w) {
+ ok(false, "non-HTTPS pages cannot register ServiceWorkers");
+ }, function(e) {
+ ok(e.name === "SecurityError", "Should fail with a SecurityError");
+ }).then(function() {
+ return new Promise((resolve) => SpecialPowers.popPrefEnv(resolve));
+ });
+ }
+
+ function realWorker() {
+ var p = navigator.serviceWorker.register("worker.js", { scope: "realworker" });
+ return p.then(function(wr) {
+ ok(wr instanceof ServiceWorkerRegistration, "Register a ServiceWorker");
+
+ info(wr.scope);
+ ok(wr.scope == (new URL("realworker", document.baseURI)).href, "Scope should match");
+ // active, waiting, installing should return valid worker instances
+ // because the registration is for the realworker scope, so the workers
+ // should be obtained for that scope and not for
+ // test_installation_simple.html
+ var worker = wr.installing;
+ ok(worker && wr.scope.match(/realworker$/) &&
+ worker.scriptURL.match(/worker.js$/), "Valid worker instance should be available.");
+ return wr.unregister().then(function(success) {
+ ok(success, "The worker should be unregistered successfully");
+ }, function(e) {
+ dump("Error unregistering the worker: " + e + "\n");
+ });
+ }, function(e) {
+ info("Error: " + e.name);
+ ok(false, "realWorker Registration should have succeeded!");
+ });
+ }
+
+ function networkError404() {
+ return navigator.serviceWorker.register("404.js", { scope: "network_error/"}).then(function(w) {
+ ok(false, "404 response should fail with TypeError");
+ }, function(e) {
+ ok(e.name === "TypeError", "404 response should fail with TypeError");
+ });
+ }
+
+ function redirectError() {
+ return navigator.serviceWorker.register("redirect_serviceworker.sjs", { scope: "redirect_error/" }).then(function(swr) {
+ ok(false, "redirection should fail");
+ }, function (e) {
+ ok(e.name === "SecurityError", "redirection should fail with SecurityError");
+ });
+ }
+
+ function parseError() {
+ var p = navigator.serviceWorker.register("parse_error_worker.js", { scope: "parse_error/" });
+ return p.then(function(wr) {
+ ok(false, "Registration should fail with parse error");
+ return navigator.serviceWorker.getRegistration("parse_error/").then(function(swr) {
+ // See https://github.com/slightlyoff/ServiceWorker/issues/547
+ is(swr, undefined, "A failed registration for a scope with no prior controllers should clear itself");
+ });
+ }, function(e) {
+ ok(e instanceof Error, "Registration should fail with parse error");
+ });
+ }
+
+ // FIXME(nsm): test for parse error when Update step doesn't happen (directly from register).
+
+ function updatefound() {
+ var frame = document.createElement("iframe");
+ frame.setAttribute("id", "simpleregister-frame");
+ frame.setAttribute("src", new URL("simpleregister/index.html", document.baseURI).href);
+ document.body.appendChild(frame);
+ var resolve, reject;
+ var p = new Promise(function(res, rej) {
+ resolve = res;
+ reject = rej;
+ });
+
+ var reg;
+ function continueTest() {
+ navigator.serviceWorker.register("worker2.js", { scope: "simpleregister/" })
+ .then(function(r) {
+ reg = r;
+ });;
+ }
+
+ window.onmessage = function(e) {
+ if (e.data.type == "ready") {
+ continueTest();
+ } else if (e.data.type == "finish") {
+ window.onmessage = null;
+ // We have to make frame navigate away, otherwise it will call
+ // MaybeStopControlling() when this document is unloaded. At that point
+ // the pref has been disabled, so the ServiceWorkerManager is not available.
+ frame.setAttribute("src", new URL("about:blank").href);
+ reg.unregister().then(function(success) {
+ ok(success, "The worker should be unregistered successfully");
+ resolve();
+ }, function(e) {
+ dump("Error unregistering the worker: " + e + "\n");
+ });
+ } else if (e.data.type == "check") {
+ ok(e.data.status, e.data.msg);
+ }
+ }
+ return p;
+ }
+
+ var readyPromiseResolved = false;
+
+ function readyPromise() {
+ var frame = document.createElement("iframe");
+ frame.setAttribute("id", "simpleregister-frame-ready");
+ frame.setAttribute("src", new URL("simpleregister/ready.html", document.baseURI).href);
+ document.body.appendChild(frame);
+
+ var channel = new MessageChannel();
+ frame.addEventListener('load', function() {
+ frame.contentWindow.postMessage('your port!', '*', [channel.port2]);
+ }, false);
+
+ channel.port1.onmessage = function() {
+ readyPromiseResolved = true;
+ }
+
+ return Promise.resolve();
+ }
+
+ function checkReadyPromise() {
+ ok(readyPromiseResolved, "The ready promise has been resolved!");
+ return Promise.resolve();
+ }
+
+ function runTest() {
+ simpleRegister()
+ .then(readyPromise)
+ .then(sameOriginWorker)
+ .then(sameOriginScope)
+ .then(httpsOnly)
+ .then(realWorker)
+ .then(networkError404)
+ .then(redirectError)
+ .then(parseError)
+ .then(updatefound)
+ .then(checkReadyPromise)
+ // put more tests here.
+ .then(function() {
+ SimpleTest.finish();
+ }).catch(function(e) {
+ ok(false, "Some test failed with error " + e);
+ SimpleTest.finish();
+ });
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ SpecialPowers.pushPrefEnv({"set": [
+ ["dom.serviceWorkers.exemptFromPerDomainMax", true],
+ ["dom.serviceWorkers.enabled", true],
+ ["dom.serviceWorkers.testing.enabled", true],
+ ["dom.caches.testing.enabled", true],
+ ]}, runTest);
+</script>
+</pre>
+</body>
+</html>
+
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 982726 - test match_all not crashing</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+<script class="testbody" type="text/javascript">
+ // match_all_worker will call matchAll until the worker shuts down.
+ // Test passes if the browser doesn't crash on leaked promise objects.
+ var registration;
+ var content;
+ var iframe;
+
+ function simpleRegister() {
+ return navigator.serviceWorker.register("match_all_worker.js",
+ { scope: "./sw_clients/" })
+ .then((swr) => registration = swr);
+ }
+
+ function closeAndUnregister() {
+ content.removeChild(iframe);
+
+ return registration.unregister().then(function(result) {
+ ok(result, "Unregister should return true.");
+ }, function(e) {
+ dump("Unregistering the SW failed with " + e + "\n");
+ });
+ }
+
+ function openClient() {
+ var p = new Promise(function(resolve, reject) {
+ window.onmessage = function(e) {
+ if (e.data === "READY") {
+ resolve();
+ }
+ }
+ });
+
+ content = document.getElementById("content");
+ ok(content, "Parent exists.");
+
+ iframe = document.createElement("iframe");
+ iframe.setAttribute('src', "sw_clients/simple.html");
+ content.appendChild(iframe);
+
+ return p;
+ }
+
+ function runTest() {
+ simpleRegister()
+ .then(openClient)
+ .then(closeAndUnregister)
+ .catch(function(e) {
+ ok(false, "Some test failed with error " + e);
+ }).then(function() {
+ ok(true, "Didn't crash on resolving matchAll promises while worker shuts down.");
+ SimpleTest.finish();
+ });
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ SpecialPowers.pushPrefEnv({"set": [
+ ["dom.serviceWorkers.exemptFromPerDomainMax", true],
+ ["dom.serviceWorkers.enabled", true],
+ ["dom.serviceWorkers.testing.enabled", true]
+ ]}, runTest);
+</script>
+</pre>
+</body>
+</html>
+
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 982726 - Test matchAll with multiple clients</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+<script class="testbody" type="text/javascript">
+ var client_iframes = [];
+ var registration;
+
+ function start() {
+ return navigator.serviceWorker.register("match_all_advanced_worker.js",
+ { scope: "./sw_clients/" }).then(function(swr) {
+ registration = swr;
+ window.onmessage = function (e) {
+ if (e.data === "READY") {
+ ok(registration.active, "Worker is active.");
+ registration.active.postMessage("RUN");
+ }
+ }
+ });
+ }
+
+ function unregister() {
+ return registration.unregister().then(function(result) {
+ ok(result, "Unregister should return true.");
+ }, function(e) {
+ dump("Unregistering the SW failed with " + e + "\n");
+ });
+ }
+
+
+ function testMatchAll() {
+ var p = new Promise(function(res, rej) {
+ navigator.serviceWorker.onmessage = function (e) {
+ ok(e.data === client_iframes.length, "MatchAll returned the correct number of clients.");
+ res();
+ }
+ });
+
+ content = document.getElementById("content");
+ ok(content, "Parent exists.");
+
+ iframe = document.createElement("iframe");
+ iframe.setAttribute('src', "sw_clients/service_worker_controlled.html");
+ content.appendChild(iframe);
+
+ client_iframes.push(iframe);
+ return p;
+ }
+
+ function removeAndTest() {
+ content = document.getElementById("content");
+ ok(content, "Parent exists.");
+
+ content.removeChild(client_iframes.pop());
+ content.removeChild(client_iframes.pop());
+
+ return testMatchAll();
+ }
+
+ function runTest() {
+ start()
+ .then(testMatchAll)
+ .then(testMatchAll)
+ .then(testMatchAll)
+ .then(removeAndTest)
+ .then(function(e) {
+ content = document.getElementById("content");
+ while (client_iframes.length) {
+ content.removeChild(client_iframes.pop());
+ }
+ }).then(unregister).catch(function(e) {
+ ok(false, "Some test failed with error " + e);
+ }).then(function() {
+ SimpleTest.finish();
+ });
+
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ SpecialPowers.pushPrefEnv({"set": [
+ ["dom.serviceWorkers.exemptFromPerDomainMax", true],
+ ["dom.serviceWorkers.enabled", true],
+ ["dom.serviceWorkers.testing.enabled", true]
+ ]}, runTest);
+</script>
+</pre>
+</body>
+</html>
+
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1058311 - Test matchAll client id </title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+<script class="testbody" type="text/javascript">
+ var registration;
+ var clientURL = "match_all_client/match_all_client_id.html";
+ function start() {
+ return navigator.serviceWorker.register("match_all_client_id_worker.js",
+ { scope: "./match_all_client/" })
+ .then((swr) => registration = swr);
+ }
+
+ function unregister() {
+ return registration.unregister().then(function(result) {
+ ok(result, "Unregister should return true.");
+ }, function(e) {
+ dump("Unregistering the SW failed with " + e + "\n");
+ });
+ }
+
+ function getMessageListener() {
+ return new Promise(function(res, rej) {
+ window.onmessage = function(e) {
+ ok(e.data, "Same client id for multiple calls.");
+ is(e.origin, "http://mochi.test:8888", "Event should have the correct origin");
+
+ if (!e.data) {
+ rej();
+ return;
+ }
+
+ info("DONE from: " + e.source);
+ res();
+ }
+ });
+ }
+
+ function testNestedWindow() {
+ var p = getMessageListener();
+
+ var content = document.getElementById("content");
+ ok(content, "Parent exists.");
+
+ iframe = document.createElement("iframe");
+
+ content.appendChild(iframe);
+ iframe.setAttribute('src', clientURL);
+
+ return p.then(() => content.removeChild(iframe));
+ }
+
+ function testAuxiliaryWindow() {
+ var p = getMessageListener();
+ var w = window.open(clientURL);
+
+ return p.then(() => w.close());
+ }
+
+ function runTest() {
+ info(window.opener == undefined);
+ start()
+ .then(testAuxiliaryWindow)
+ .then(testNestedWindow)
+ .then(unregister)
+ .catch(function(e) {
+ ok(false, "Some test failed with error " + e);
+ }).then(SimpleTest.finish);
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ SpecialPowers.pushPrefEnv({"set": [
+ ["dom.serviceWorkers.exemptFromPerDomainMax", true],
+ ["dom.serviceWorkers.enabled", true],
+ ["dom.serviceWorkers.testing.enabled", true]
+ ]}, runTest);
+</script>
+</pre>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1058311 - Test matchAll clients properties </title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+<script class="testbody" type="text/javascript">
+ var registration;
+ var clientURL = "match_all_clients/match_all_controlled.html";
+ function start() {
+ return navigator.serviceWorker.register("match_all_properties_worker.js",
+ { scope: "./match_all_clients/" })
+ .then((swr) => registration = swr);
+ }
+
+ function unregister() {
+ return registration.unregister().then(function(result) {
+ ok(result, "Unregister should return true.");
+ }, function(e) {
+ dump("Unregistering the SW failed with " + e + "\n");
+ });
+ }
+
+ function getMessageListener() {
+ return new Promise(function(res, rej) {
+ window.onmessage = function(e) {
+ if (e.data.message === undefined) {
+ info("rejecting promise");
+ rej();
+ return;
+ }
+
+ ok(e.data.result, e.data.message);
+
+ if (!e.data.result) {
+ rej();
+ }
+ if (e.data.message == "DONE") {
+ info("DONE from: " + e.source);
+ res();
+ }
+ }
+ });
+ }
+
+ function testNestedWindow() {
+ var p = getMessageListener();
+
+ var content = document.getElementById("content");
+ ok(content, "Parent exists.");
+
+ iframe = document.createElement("iframe");
+
+ content.appendChild(iframe);
+ iframe.setAttribute('src', clientURL);
+
+ return p.then(() => content.removeChild(iframe));
+ }
+
+ function testAuxiliaryWindow() {
+ var p = getMessageListener();
+ var w = window.open(clientURL);
+
+ return p.then(() => w.close());
+ }
+
+ function runTest() {
+ info("catalin");
+ info(window.opener == undefined);
+ start()
+ .then(testAuxiliaryWindow)
+ .then(testNestedWindow)
+ .then(unregister)
+ .catch(function(e) {
+ ok(false, "Some test failed with error " + e);
+ }).then(SimpleTest.finish);
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ SpecialPowers.pushPrefEnv({"set": [
+ ["dom.serviceWorkers.exemptFromPerDomainMax", true],
+ ["dom.serviceWorkers.enabled", true],
+ ["dom.serviceWorkers.testing.enabled", true]
+ ]}, runTest);
+</script>
+</pre>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 930348 - test stub Navigator ServiceWorker utilities.</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+<script class="testbody" type="text/javascript">
+
+ function checkEnabled() {
+ ok(navigator.serviceWorker, "navigator.serviceWorker should exist when ServiceWorkers are enabled.");
+ ok(typeof navigator.serviceWorker.register === "function", "navigator.serviceWorker.register() should be a function.");
+ ok(typeof navigator.serviceWorker.getRegistration === "function", "navigator.serviceWorker.getAll() should be a function.");
+ ok(typeof navigator.serviceWorker.getRegistrations === "function", "navigator.serviceWorker.getAll() should be a function.");
+ ok(navigator.serviceWorker.ready instanceof Promise, "navigator.serviceWorker.ready should be a Promise.");
+ ok(navigator.serviceWorker.controller === null, "There should be no controller worker for an uncontrolled document.");
+ }
+
+ SimpleTest.waitForExplicitFinish();
+
+ SpecialPowers.pushPrefEnv({"set": [
+ ["dom.serviceWorkers.exemptFromPerDomainMax", true],
+ ["dom.serviceWorkers.enabled", true]
+ ]}, function() {
+ checkEnabled();
+ SimpleTest.finish();
+ });
+</script>
+</pre>
+</body>
+</html>
+
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1187766 - Test loading plugins scenarios with fetch interception.</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+<script class="testbody" type="text/javascript">
+ SimpleTest.requestCompleteLog();
+
+ var registration;
+ function simpleRegister() {
+ var p = navigator.serviceWorker.register("./fetch/plugin/worker.js", { scope: "./fetch/plugin/" });
+ return p.then(function(swr) {
+ registration = swr;
+ return new Promise(function(resolve) {
+ swr.installing.onstatechange = resolve;
+ });
+ });
+ }
+
+ function unregister() {
+ return registration.unregister().then(function(success) {
+ ok(success, "Service worker should be unregistered successfully");
+ }, function(e) {
+ dump("SW unregistration error: " + e + "\n");
+ });
+ }
+
+ function testPlugins() {
+ var p = new Promise(function(resolve, reject) {
+ window.onmessage = function(e) {
+ if (e.data.status == "ok") {
+ ok(e.data.result, e.data.message);
+ } else if (e.data.status == "done") {
+ window.onmessage = null;
+ w.close();
+ resolve();
+ }
+ }
+ });
+
+ var w = window.open("fetch/plugin/plugins.html");
+ return p;
+ }
+
+ function runTest() {
+ simpleRegister()
+ .then(testPlugins)
+ .then(unregister)
+ .then(function() {
+ SimpleTest.finish();
+ }).catch(function(e) {
+ ok(false, "Some test failed with error " + e);
+ SimpleTest.finish();
+ });
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ SpecialPowers.pushPrefEnv({"set": [
+ ["dom.requestcontext.enabled", true],
+ ["dom.serviceWorkers.exemptFromPerDomainMax", true],
+ ["dom.serviceWorkers.enabled", true],
+ ["dom.serviceWorkers.testing.enabled", true],
+ ]}, runTest);
+</script>
+</pre>
+</body>
+</html>
+
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug XXXXXXX - Check that Notification constructor throws in ServiceWorkerGlobalScope</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="/tests/dom/tests/mochitest/notification/MockServices.js"></script>
+ <script type="text/javascript" src="/tests/dom/tests/mochitest/notification/NotificationTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+<script class="testbody" type="text/javascript">
+
+ function simpleRegister() {
+ return navigator.serviceWorker.register("notification_constructor_error.js", { scope: "notification_constructor_error/" }).then(function(swr) {
+ ok(false, "Registration should fail.");
+ }, function(e) {
+ is(e.name, 'TypeError', "Registration should fail with a TypeError.");
+ });
+ }
+
+ function runTest() {
+ MockServices.register();
+ simpleRegister()
+ .then(function() {
+ MockServices.unregister();
+ SimpleTest.finish();
+ }).catch(function(e) {
+ ok(false, "Some test failed with error " + e);
+ MockServices.unregister();
+ SimpleTest.finish();
+ });
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ SpecialPowers.pushPrefEnv({"set": [
+ ["dom.serviceWorkers.exemptFromPerDomainMax", true],
+ ["dom.serviceWorkers.enabled", true],
+ ["dom.serviceWorkers.testing.enabled", true],
+ ["notification.prompt.testing", true],
+ ]}, runTest);
+</script>
+</pre>
+</body>
+</html>
+
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 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>ServiceWorkerRegistration.getNotifications() on main thread and worker thread.</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="/tests/dom/tests/mochitest/notification/MockServices.js"></script>
+ <script type="text/javascript" src="/tests/dom/tests/mochitest/notification/NotificationTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+<script type="text/javascript">
+
+ SimpleTest.requestFlakyTimeout("untriaged");
+
+ function testFrame(src) {
+ return new Promise(function(resolve, reject) {
+ var iframe = document.createElement("iframe");
+ iframe.src = src;
+ window.callback = function(result) {
+ iframe.src = "about:blank";
+ document.body.removeChild(iframe);
+ iframe = null;
+ SpecialPowers.exactGC(function() {
+ resolve(result);
+ });
+ };
+ document.body.appendChild(iframe);
+ });
+ }
+
+ function registerSW() {
+ return testFrame('notification/register.html').then(function() {
+ ok(true, "Registered service worker.");
+ });
+ }
+
+ function unregisterSW() {
+ return testFrame('notification/unregister.html').then(function() {
+ ok(true, "Unregistered service worker.");
+ });
+ }
+
+ // To check that the scope is respected when retrieving notifications.
+ function registerAlternateSWAndAddNotification() {
+ return testFrame('notification_alt/register.html').then(function() {
+ ok(true, "Registered alternate service worker.");
+ return navigator.serviceWorker.getRegistration("./notification_alt/").then(function(reg) {
+ return reg.showNotification("This is a notification_alt");
+ });
+ });
+ }
+
+ function unregisterAlternateSWAndAddNotification() {
+ return testFrame('notification_alt/unregister.html').then(function() {
+ ok(true, "unregistered alternate service worker.");
+ });
+ }
+
+ function testDismiss() {
+ // Dismissed persistent notifications should be removed from the
+ // notification list.
+ var alertsService = SpecialPowers.Cc["@mozilla.org/alerts-service;1"]
+ .getService(SpecialPowers.Ci.nsIAlertsService);
+ return navigator.serviceWorker.getRegistration("./notification/")
+ .then(function(reg) {
+ return reg.showNotification(
+ "This is a notification that will be closed", { tag: "dismiss" })
+ .then(function() {
+ return reg;
+ });
+ }).then(function(reg) {
+ return reg.getNotifications()
+ .then(function(notifications) {
+ is(notifications.length, 1, "There should be one visible notification");
+ is(notifications[0].tag, "dismiss", "Tag should match");
+
+ // Simulate dismissing the notification by using the alerts service
+ // directly, instead of `Notification#close`.
+ var principal = SpecialPowers.wrap(document).nodePrincipal;
+ var id = principal.origin + "#tag:dismiss";
+ alertsService.closeAlert(id, principal);
+
+ return reg;
+ });
+ }).then(function(reg) {
+ return reg.getNotifications();
+ }).then(function(notifications) {
+ // Make sure dismissed notifications are no longer retrieved.
+ is(notifications.length, 0, "There should be no more stored notifications");
+ });
+ }
+
+ function testGet() {
+ // Non persistent notifications will not show up in getNotification().
+ var n = new Notification("Scope does not match");
+ var options = NotificationTest.payload;
+ return navigator.serviceWorker.getRegistration("./notification/")
+ .then(function(reg) {
+ return reg.showNotification("This is a title", options)
+ .then(function() {
+ return reg;
+ });
+ }).then(function(reg) {
+ return registerAlternateSWAndAddNotification().then(function() {
+ return reg;
+ });
+ }).then(function(reg) {
+ return reg.getNotifications();
+ }).then(function(notifications) {
+ is(notifications.length, 1, "There should be one stored notification");
+ var notification = notifications[0];
+ ok(notification instanceof Notification, "Should be a Notification");
+ is(notification.title, "This is a title", "Title should match");
+ for (var key in options) {
+ if (key === "data") {
+ ok(NotificationTest.customDataMatches(notification.data),
+ "data property should match");
+ continue;
+ }
+ is(notification[key], options[key], key + " property should match");
+ }
+ notification.close();
+ }).then(function() {
+ return navigator.serviceWorker.getRegistration("./notification/").then(function(reg) {
+ return reg.getNotifications();
+ });
+ }).then(function(notifications) {
+ // Make sure closed notifications are no longer retrieved.
+ is(notifications.length, 0, "There should be no more stored notifications");
+ }).catch(function(e) {
+ ok(false, "Something went wrong " + e.message);
+ }).then(unregisterAlternateSWAndAddNotification);
+ }
+
+ function testGetWorker() {
+ todo(false, "navigator.serviceWorker is not available on workers yet");
+ return Promise.resolve();
+ }
+
+ function waitForSWTests(reg, msg) {
+ return new Promise(function(resolve, reject) {
+ var content = document.getElementById("content");
+
+ iframe = document.createElement("iframe");
+
+ content.appendChild(iframe);
+ iframe.setAttribute('src', "notification/listener.html");
+
+ window.onmessage = function(e) {
+ if (e.data.type == 'status') {
+ ok(e.data.status, "Service worker test: " + e.data.msg);
+ } else if (e.data.type == 'finish') {
+ content.removeChild(iframe);
+ resolve();
+ }
+ }
+
+ iframe.onload = function(e) {
+ iframe.onload = null;
+ reg.active.postMessage(msg);
+ }
+ });
+ }
+
+ function testGetServiceWorker() {
+ return navigator.serviceWorker.getRegistration("./notification/")
+ .then(function(reg) {
+ return waitForSWTests(reg, 'create');
+ });
+ }
+
+ // Create a Notification here, make sure ServiceWorker sees it.
+ function testAcrossThreads() {
+ return navigator.serviceWorker.getRegistration("./notification/")
+ .then(function(reg) {
+ return reg.showNotification("This is a title")
+ .then(function() {
+ return reg;
+ });
+ }).then(function(reg) {
+ return waitForSWTests(reg, 'do-not-create');
+ });
+ }
+
+ SimpleTest.waitForExplicitFinish();
+
+ MockServices.register();
+ SpecialPowers.pushPrefEnv({"set": [
+ ["dom.serviceWorkers.exemptFromPerDomainMax", true],
+ ["dom.serviceWorkers.enabled", true],
+ ["dom.serviceWorkers.testing.enabled", true],
+ ["dom.webnotifications.workers.enabled", true],
+ ["dom.webnotifications.serviceworker.enabled", true],
+ ["notification.prompt.testing", true],
+ ]}, function() {
+ registerSW()
+ .then(testGet)
+ .then(testGetWorker)
+ .then(testGetServiceWorker)
+ .then(testAcrossThreads)
+ .then(testDismiss)
+ .then(unregisterSW)
+ .then(function() {
+ MockServices.unregister();
+ SimpleTest.finish();
+ });
+ });
+</script>
+</body>
+</html>
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 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=916893
+-->
+<head>
+ <title>Bug 1114554 - Test ServiceWorkerGlobalScope.notificationclick event.</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="/tests/dom/tests/mochitest/notification/MockServices.js"></script>
+ <script type="text/javascript" src="/tests/dom/tests/mochitest/notification/NotificationTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1114554">Bug 1114554</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+</pre>
+<script type="text/javascript">
+ SimpleTest.requestFlakyTimeout("Mock alert service dispatches show and click events.");
+
+ function testFrame(src) {
+ var iframe = document.createElement("iframe");
+ iframe.src = src;
+ window.callback = function(result) {
+ window.callback = null;
+ document.body.removeChild(iframe);
+ iframe = null;
+ ok(result, "Got notificationclick event with correct data.");
+ MockServices.unregister();
+ registration.unregister().then(function() {
+ SimpleTest.finish();
+ });
+ };
+ document.body.appendChild(iframe);
+ }
+
+ var registration;
+
+ function runTest() {
+ MockServices.register();
+ testFrame('notificationclick-otherwindow.html');
+ navigator.serviceWorker.register("notificationclick.js", { scope: "notificationclick-otherwindow.html" }).then(function(reg) {
+ registration = reg;
+ }, function(e) {
+ ok(false, "registration should have passed!");
+ });
+ };
+
+ SimpleTest.waitForExplicitFinish();
+ SpecialPowers.pushPrefEnv({"set": [
+ ["dom.serviceWorkers.exemptFromPerDomainMax", true],
+ ["dom.serviceWorkers.enabled", true],
+ ["dom.serviceWorkers.testing.enabled", true],
+ ["dom.webnotifications.workers.enabled", true],
+ ["dom.webnotifications.serviceworker.enabled", true],
+ ["notification.prompt.testing", true],
+ ]}, runTest);
+</script>
+</body>
+</html>
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 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=916893
+-->
+<head>
+ <title>Bug 1114554 - Test ServiceWorkerGlobalScope.notificationclick event.</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="/tests/dom/tests/mochitest/notification/MockServices.js"></script>
+ <script type="text/javascript" src="/tests/dom/tests/mochitest/notification/NotificationTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1114554">Bug 1114554</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+</pre>
+<script type="text/javascript">
+ SimpleTest.requestFlakyTimeout("Mock alert service dispatches show and click events.");
+
+ function testFrame(src) {
+ var iframe = document.createElement("iframe");
+ iframe.src = src;
+ window.callback = function(result) {
+ window.callback = null;
+ document.body.removeChild(iframe);
+ iframe = null;
+ ok(result, "Got notificationclick event with correct data.");
+ MockServices.unregister();
+ registration.unregister().then(function() {
+ SimpleTest.finish();
+ });
+ };
+ document.body.appendChild(iframe);
+ }
+
+ var registration;
+
+ function runTest() {
+ MockServices.register();
+ testFrame('notificationclick.html');
+ navigator.serviceWorker.register("notificationclick.js", { scope: "notificationclick.html" }).then(function(reg) {
+ registration = reg;
+ }, function(e) {
+ ok(false, "registration should have passed!");
+ });
+ };
+
+ SimpleTest.waitForExplicitFinish();
+ SpecialPowers.pushPrefEnv({"set": [
+ ["dom.serviceWorkers.exemptFromPerDomainMax", true],
+ ["dom.serviceWorkers.enabled", true],
+ ["dom.serviceWorkers.testing.enabled", true],
+ ["dom.webnotifications.workers.enabled", true],
+ ["dom.webnotifications.serviceworker.enabled", true],
+ ["notification.prompt.testing", true],
+ ]}, runTest);
+</script>
+</body>
+</html>
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 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=916893
+-->
+<head>
+ <title>Bug 1144660 - Test client.focus() permissions on notification click</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="/tests/dom/tests/mochitest/notification/MockServices.js"></script>
+ <script type="text/javascript" src="/tests/dom/tests/mochitest/notification/NotificationTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1114554">Bug 1114554</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+</pre>
+<script type="text/javascript">
+ SimpleTest.requestFlakyTimeout("Mock alert service dispatches show and click events.");
+
+ function testFrame(src) {
+ var iframe = document.createElement("iframe");
+ iframe.src = src;
+ window.callback = function(result) {
+ window.callback = null;
+ document.body.removeChild(iframe);
+ iframe = null;
+ ok(result, "All tests passed.");
+ MockServices.unregister();
+ registration.unregister().then(function() {
+ SimpleTest.finish();
+ });
+ };
+ document.body.appendChild(iframe);
+ }
+
+ var registration;
+
+ function runTest() {
+ MockServices.register();
+ testFrame('notificationclick_focus.html');
+ navigator.serviceWorker.register("notificationclick_focus.js", { scope: "notificationclick_focus.html" }).then(function(reg) {
+ registration = reg;
+ }, function(e) {
+ ok(false, "registration should have passed!");
+ });
+ };
+
+ SimpleTest.waitForExplicitFinish();
+ SpecialPowers.pushPrefEnv({"set": [
+ ["dom.serviceWorkers.exemptFromPerDomainMax", true],
+ ["dom.serviceWorkers.enabled", true],
+ ["dom.serviceWorkers.testing.enabled", true],
+ ["dom.webnotifications.workers.enabled", true],
+ ["dom.webnotifications.serviceworker.enabled", true],
+ ["notification.prompt.testing", true],
+ ["dom.disable_open_click_delay", 1000],
+ ]}, runTest);
+</script>
+</body>
+</html>
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 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1265841
+-->
+<head>
+ <title>Bug 1265841 - Test ServiceWorkerGlobalScope.notificationclose event.</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="/tests/dom/tests/mochitest/notification/MockServices.js"></script>
+ <script type="text/javascript" src="/tests/dom/tests/mochitest/notification/NotificationTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1265841">Bug 1265841</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+</pre>
+<script type="text/javascript">
+ SimpleTest.requestFlakyTimeout("Mock alert service dispatches show, click, and close events.");
+
+ function testFrame(src) {
+ var iframe = document.createElement("iframe");
+ iframe.src = src;
+ window.callback = function(result) {
+ window.callback = null;
+ document.body.removeChild(iframe);
+ iframe = null;
+ ok(result, "Got notificationclose event with correct data.");
+ MockServices.unregister();
+ registration.unregister().then(function() {
+ SimpleTest.finish();
+ });
+ };
+ document.body.appendChild(iframe);
+ }
+
+ var registration;
+
+ function runTest() {
+ MockServices.register();
+ testFrame('notificationclose.html');
+ navigator.serviceWorker.register("notificationclose.js", { scope: "notificationclose.html" }).then(function(reg) {
+ registration = reg;
+ }, function(e) {
+ ok(false, "registration should have passed!");
+ });
+ };
+
+ SimpleTest.waitForExplicitFinish();
+ SpecialPowers.pushPrefEnv({"set": [
+ ["dom.serviceWorkers.exemptFromPerDomainMax", true],
+ ["dom.serviceWorkers.enabled", true],
+ ["dom.serviceWorkers.testing.enabled", true],
+ ["dom.webnotifications.workers.enabled", true],
+ ["dom.webnotifications.serviceworker.enabled", true],
+ ["notification.prompt.testing", true],
+ ]}, runTest);
+</script>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 982726 - Test service worker post message </title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+<script class="testbody" type="text/javascript">
+ var registration;
+ function start() {
+ return navigator.serviceWorker.register("opaque_intercept_worker.js",
+ { scope: "./sw_clients/" })
+ .then((swr) => registration = swr);
+ }
+
+ function unregister() {
+ return registration.unregister().then(function(result) {
+ ok(result, "Unregister should return true.");
+ }, function(e) {
+ dump("Unregistering the SW failed with " + e + "\n");
+ });
+ }
+
+
+ function testOpaqueIntercept(swr) {
+ var p = new Promise(function(res, rej) {
+ var ready = false;
+ var scriptLoaded = false;
+ window.onmessage = function(e) {
+ if (e.data === "READY") {
+ ok(!ready, "ready message should only be received once");
+ ok(!scriptLoaded, "ready message should be received before script loaded");
+ if (ready) {
+ res();
+ return;
+ }
+ ready = true;
+ iframe.contentWindow.postMessage("REFRESH", "*");
+ } else if (e.data === "SCRIPT_LOADED") {
+ ok(ready, "script loaded should be received after ready");
+ ok(!scriptLoaded, "script loaded message should be received only once");
+ scriptLoaded = true;
+ res();
+ }
+ }
+ });
+
+ var content = document.getElementById("content");
+ ok(content, "Parent exists.");
+
+ iframe = document.createElement("iframe");
+ iframe.setAttribute('src', "sw_clients/refresher.html");
+ content.appendChild(iframe);
+
+ return p.then(() => content.removeChild(iframe));
+ }
+
+ function runTest() {
+ start()
+ .then(testOpaqueIntercept)
+ .then(unregister)
+ .catch(function(e) {
+ ok(false, "Some test failed with error " + e);
+ }).then(SimpleTest.finish);
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ SpecialPowers.pushPrefEnv({"set": [
+ ["dom.serviceWorkers.exemptFromPerDomainMax", true],
+ ["dom.serviceWorkers.enabled", true],
+ ["dom.serviceWorkers.testing.enabled", true],
+ ["dom.caches.enabled", true],
+ ]}, runTest);
+</script>
+</pre>
+</body>
+</html>
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 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1172870
+-->
+<head>
+ <title>Bug 1172870 - Test clients.openWindow</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="/tests/dom/tests/mochitest/notification/MockServices.js"></script>
+ <script type="text/javascript" src="/tests/dom/tests/mochitest/notification/NotificationTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1172870">Bug 1172870</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+</pre>
+<script type="text/javascript">
+ SimpleTest.requestFlakyTimeout("Mock alert service dispatches show and click events.");
+
+ function setup(ctx) {
+ MockServices.register();
+
+ return navigator.serviceWorker.register("openWindow_worker.js", {scope: "./"})
+ .then(function(swr) {
+ ok(swr, "Registration successful");
+ ctx.registration = swr;
+ return ctx;
+ });
+ }
+
+ function waitForActiveServiceWorker(ctx) {
+ return navigator.serviceWorker.ready.then(function(result) {
+ ok(ctx.registration.active, "Service Worker is active");
+ return ctx;
+ });
+ }
+
+ function setupMessageHandler(ctx) {
+ return new Promise(function(res, rej) {
+ navigator.serviceWorker.onmessage = function(event) {
+ navigator.serviceWorker.onmessage = null;
+ for (i = 0; i < event.data.length; i++) {
+ ok(event.data[i].result, event.data[i].message);
+ }
+ res(ctx);
+ }
+ });
+ }
+
+ function testPopupNotAllowed(ctx) {
+ var p = setupMessageHandler(ctx);
+ ok(ctx.registration.active, "Worker is active.");
+ ctx.registration.active.postMessage("testNoPopup");
+
+ return p;
+ }
+
+ function testPopupAllowed(ctx) {
+ var p = setupMessageHandler(ctx);
+ ctx.registration.showNotification("testPopup");
+
+ return p;
+ }
+
+ function checkNumberOfWindows(ctx) {
+ return new Promise(function(res, rej) {
+ navigator.serviceWorker.onmessage = function(event) {
+ navigator.serviceWorker.onmessage = null;
+ ok(event.data.result, event.data.message);
+ res(ctx);
+ }
+ ctx.registration.active.postMessage("CHECK_NUMBER_OF_WINDOWS");
+ });
+ }
+
+ function clear(ctx) {
+ MockServices.unregister();
+
+ return ctx.registration.unregister().then(function(result) {
+ ctx.registration = null;
+ ok(result, "Unregister was successful.");
+ });
+ }
+
+ function runTest() {
+ setup({})
+ .then(waitForActiveServiceWorker)
+ // Permission to allow popups persists for some time after a notification
+ // click event, so the order here is important.
+ .then(testPopupNotAllowed)
+ .then(testPopupAllowed)
+ .then(checkNumberOfWindows)
+ .then(clear)
+ .catch(function(e) {
+ ok(false, "Some test failed with error " + e);
+ }).then(SimpleTest.finish);
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ SpecialPowers.pushPrefEnv({"set": [
+ ["dom.serviceWorkers.exemptFromPerDomainMax", true],
+ ["dom.serviceWorkers.enabled", true],
+ ["dom.serviceWorkers.openWindow.enabled", true],
+ ["dom.serviceWorkers.testing.enabled", true],
+ ["dom.webnotifications.workers.enabled", true],
+ ["dom.webnotifications.serviceworker.enabled", true],
+ ["notification.prompt.testing", true],
+ ["dom.disable_open_click_delay", 1000],
+ ["dom.serviceWorkers.idle_timeout", 299999],
+ ["dom.serviceWorkers.idle_extended_timeout", 299999]
+ ]}, runTest);
+</script>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test the origin of a redirected response from a service worker</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+<iframe></iframe>
+</div>
+<pre id="test"></pre>
+<script class="testbody" type="text/javascript">
+
+ var iframe;
+ function runTest() {
+ iframe = document.querySelector("iframe");
+ iframe.src = "/tests/dom/workers/test/serviceworkers/fetch/origin/register.html";
+ var win;
+ window.onmessage = function(e) {
+ if (e.data.status == "ok") {
+ ok(e.data.result, e.data.message);
+ } else if (e.data.status == "registrationdone") {
+ win = window.open("/tests/dom/workers/test/serviceworkers/fetch/origin/index.sjs", "mywindow", "width=100,height=100");
+ } else if (e.data.status == "domain") {
+ is(e.data.data, "example.org", "Correct domain expected");
+ } else if (e.data.status == "origin") {
+ is(e.data.data, "http://example.org", "Correct origin expected");
+ } else if (e.data.status == "done") {
+ win.close();
+ iframe.src = "/tests/dom/workers/test/serviceworkers/fetch/origin/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": [
+ ["dom.serviceWorkers.exemptFromPerDomainMax", true],
+ ["dom.serviceWorkers.enabled", true],
+ ["dom.serviceWorkers.openWindow.enabled", true],
+ ["dom.serviceWorkers.testing.enabled", true],
+ ["dom.caches.enabled", true],
+ ]}, runTest);
+ };
+</script>
+</pre>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test the origin of a redirected response from a service worker</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+<iframe></iframe>
+</div>
+<pre id="test"></pre>
+<script class="testbody" type="text/javascript">
+
+ var iframe;
+ function runTest() {
+ iframe = document.querySelector("iframe");
+ iframe.src = "/tests/dom/workers/test/serviceworkers/fetch/origin/register.html";
+ var win;
+ window.onmessage = function(e) {
+ if (e.data.status == "ok") {
+ ok(e.data.result, e.data.message);
+ } else if (e.data.status == "registrationdone") {
+ win = window.open("/tests/dom/workers/test/serviceworkers/fetch/origin/index-cached.sjs", "mywindow", "width=100,height=100");
+ } else if (e.data.status == "domain") {
+ is(e.data.data, "example.org", "Correct domain expected");
+ } else if (e.data.status == "origin") {
+ is(e.data.data, "http://example.org", "Correct origin expected");
+ } else if (e.data.status == "done") {
+ win.close();
+ iframe.src = "/tests/dom/workers/test/serviceworkers/fetch/origin/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": [
+ ["dom.serviceWorkers.exemptFromPerDomainMax", true],
+ ["dom.serviceWorkers.enabled", true],
+ ["dom.serviceWorkers.openWindow.enabled", true],
+ ["dom.serviceWorkers.testing.enabled", true],
+ ["dom.caches.enabled", true],
+ ]}, runTest);
+ };
+</script>
+</pre>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test the origin of a redirected response from a service worker</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+<iframe></iframe>
+</div>
+<pre id="test"></pre>
+<script class="testbody" type="text/javascript">
+
+ var iframe;
+ function runTest() {
+ iframe = document.querySelector("iframe");
+ iframe.src = "/tests/dom/workers/test/serviceworkers/fetch/origin/register.html";
+ var win;
+ window.onmessage = function(e) {
+ if (e.data.status == "ok") {
+ ok(e.data.result, e.data.message);
+ } else if (e.data.status == "registrationdone") {
+ win = window.open("/tests/dom/workers/test/serviceworkers/fetch/origin/index-to-https.sjs", "mywindow", "width=100,height=100");
+ } else if (e.data.status == "domain") {
+ is(e.data.data, "example.org", "Correct domain expected");
+ } else if (e.data.status == "origin") {
+ is(e.data.data, "https://example.org", "Correct origin expected");
+ } else if (e.data.status == "done") {
+ win.close();
+ iframe.src = "/tests/dom/workers/test/serviceworkers/fetch/origin/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": [
+ ["dom.serviceWorkers.exemptFromPerDomainMax", true],
+ ["dom.serviceWorkers.enabled", true],
+ ["dom.serviceWorkers.testing.enabled", true],
+ ["dom.caches.enabled", true],
+ ]}, runTest);
+ };
+</script>
+</pre>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test the origin of a redirected response from a service worker</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+<iframe></iframe>
+</div>
+<pre id="test"></pre>
+<script class="testbody" type="text/javascript">
+
+ var iframe;
+ function runTest() {
+ iframe = document.querySelector("iframe");
+ iframe.src = "/tests/dom/workers/test/serviceworkers/fetch/origin/register.html";
+ var win;
+ window.onmessage = function(e) {
+ if (e.data.status == "ok") {
+ ok(e.data.result, e.data.message);
+ } else if (e.data.status == "registrationdone") {
+ win = window.open("/tests/dom/workers/test/serviceworkers/fetch/origin/index-to-https-cached.sjs", "mywindow", "width=100,height=100");
+ } else if (e.data.status == "domain") {
+ is(e.data.data, "example.org", "Correct domain expected");
+ } else if (e.data.status == "origin") {
+ is(e.data.data, "https://example.org", "Correct origin expected");
+ } else if (e.data.status == "done") {
+ win.close();
+ iframe.src = "/tests/dom/workers/test/serviceworkers/fetch/origin/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": [
+ ["dom.serviceWorkers.exemptFromPerDomainMax", true],
+ ["dom.serviceWorkers.enabled", true],
+ ["dom.serviceWorkers.openWindow.enabled", true],
+ ["dom.serviceWorkers.testing.enabled", true],
+ ["dom.caches.enabled", true],
+ ]}, runTest);
+ };
+</script>
+</pre>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 982726 - Test service worker post message </title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+<script class="testbody" type="text/javascript">
+ var magic_value = "MAGIC_VALUE_123";
+ var registration;
+ function start() {
+ return navigator.serviceWorker.register("message_posting_worker.js",
+ { scope: "./sw_clients/" })
+ .then((swr) => registration = swr);
+ }
+
+ function unregister() {
+ return registration.unregister().then(function(result) {
+ ok(result, "Unregister should return true.");
+ }, function(e) {
+ dump("Unregistering the SW failed with " + e + "\n");
+ });
+ }
+
+
+ function testPostMessage(swr) {
+ var p = new Promise(function(res, rej) {
+ window.onmessage = function(e) {
+ if (e.data === "READY") {
+ swr.active.postMessage(magic_value);
+ } else if (e.data === magic_value) {
+ ok(true, "Worker posted the correct value.");
+ res();
+ } else {
+ ok(false, "Wrong value. Expected: " + magic_value +
+ ", got: " + e.data);
+ res();
+ }
+ }
+ });
+
+ var content = document.getElementById("content");
+ ok(content, "Parent exists.");
+
+ iframe = document.createElement("iframe");
+ iframe.setAttribute('src', "sw_clients/service_worker_controlled.html");
+ content.appendChild(iframe);
+
+ return p.then(() => content.removeChild(iframe));
+ }
+
+ function runTest() {
+ start()
+ .then(testPostMessage)
+ .then(unregister)
+ .catch(function(e) {
+ ok(false, "Some test failed with error " + e);
+ }).then(SimpleTest.finish);
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ SpecialPowers.pushPrefEnv({"set": [
+ ["dom.serviceWorkers.exemptFromPerDomainMax", true],
+ ["dom.serviceWorkers.enabled", true],
+ ["dom.serviceWorkers.openWindow.enabled", true],
+ ["dom.serviceWorkers.testing.enabled", true]
+ ]}, runTest);
+</script>
+</pre>
+</body>
+</html>
+
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 982726 - Test service worker post message advanced </title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+<script class="testbody" type="text/javascript">
+ var registration;
+ var base = ["string", true, 42];
+ var blob = new Blob(["blob_content"]);
+ var file = new File(["file_content"], "file");
+ var obj = { body : "object_content" };
+
+ function readBlob(blob) {
+ return new Promise(function(resolve, reject) {
+ var reader = new FileReader();
+ reader.onloadend = () => resolve(reader.result);
+ reader.readAsText(blob);
+ });
+ }
+
+ function equals(v1, v2) {
+ return Promise.all([v1, v2]).then(function(val) {
+ ok(val[0] === val[1], "Values should match.");
+ });
+ }
+
+ function blob_equals(b1, b2) {
+ return equals(readBlob(b1), readBlob(b2));
+ }
+
+ function file_equals(f1, f2) {
+ return equals(f1.name, f2.name).then(blob_equals(f1, f2));
+ }
+
+ function obj_equals(o1, o2) {
+ return equals(o1.body, o2.body);
+ }
+
+ function start() {
+ return navigator.serviceWorker.register("message_posting_worker.js",
+ { scope: "./sw_clients/" })
+ .then((swr) => registration = swr);
+ }
+
+ function unregister() {
+ return registration.unregister().then(function(result) {
+ ok(result, "Unregister should return true.");
+ }, function(e) {
+ dump("Unregistering the SW failed with " + e + "\n");
+ });
+ }
+
+ function testPostMessageObject(obj, test) {
+ var p = new Promise(function(res, rej) {
+ window.onmessage = function(e) {
+ if (e.data === "READY") {
+ registration.active.postMessage(obj)
+ } else {
+ test(obj, e.data).then(res);
+ }
+ }
+ });
+
+ var content = document.getElementById("content");
+ ok(content, "Parent exists.");
+
+ iframe = document.createElement("iframe");
+ iframe.setAttribute('src', "sw_clients/service_worker_controlled.html");
+ content.appendChild(iframe);
+
+ return p.then(() => content.removeChild(iframe));
+ }
+
+ function runTest() {
+ start()
+ .then(testPostMessageObject.bind(this, base[0], equals))
+ .then(testPostMessageObject.bind(this, base[1], equals))
+ .then(testPostMessageObject.bind(this, base[2], equals))
+ .then(testPostMessageObject.bind(this, blob, blob_equals))
+ .then(testPostMessageObject.bind(this, file, file_equals))
+ .then(testPostMessageObject.bind(this, obj, obj_equals))
+ .then(unregister)
+ .catch(function(e) {
+ ok(false, "Some test failed with error " + e);
+ }).then(SimpleTest.finish);
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ SpecialPowers.pushPrefEnv({"set": [
+ ["dom.serviceWorkers.exemptFromPerDomainMax", true],
+ ["dom.serviceWorkers.enabled", true],
+ ["dom.serviceWorkers.openWindow.enabled", true],
+ ["dom.serviceWorkers.testing.enabled", true]
+ ]}, runTest);
+</script>
+</pre>
+</body>
+</html>
+
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1142015 - Test service worker post message source </title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+<script class="testbody" type="text/javascript">
+ var magic_value = "MAGIC_VALUE_RANDOM";
+ var registration;
+ function start() {
+ return navigator.serviceWorker.register("source_message_posting_worker.js",
+ { scope: "./nonexistent_scope/" })
+ .then((swr) => registration = swr);
+ }
+
+ function unregister() {
+ return registration.unregister().then(function(result) {
+ ok(result, "Unregister should return true.");
+ }, function(e) {
+ dump("Unregistering the SW failed with " + e + "\n");
+ });
+ }
+
+
+ function testPostMessage(swr) {
+ var p = new Promise(function(res, rej) {
+ navigator.serviceWorker.onmessage = function(e) {
+ ok(e.data === magic_value, "Worker posted the correct value.");
+ res();
+ }
+ });
+
+ ok(swr.installing, "Installing worker exists.");
+ swr.installing.postMessage(magic_value);
+ return p;
+ }
+
+
+ function runTest() {
+ start()
+ .then(testPostMessage)
+ .then(unregister)
+ .catch(function(e) {
+ ok(false, "Some test failed with error " + e);
+ }).then(SimpleTest.finish);
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ SpecialPowers.pushPrefEnv({"set": [
+ ["dom.serviceWorkers.exemptFromPerDomainMax", true],
+ ["dom.serviceWorkers.enabled", true],
+ ["dom.serviceWorkers.openWindow.enabled", true],
+ ["dom.serviceWorkers.testing.enabled", true]
+ ]}, runTest);
+</script>
+</pre>
+</body>
+</html>
+
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 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+ <title>Test for ServiceWorker - Private Browsing</title>
+ <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
+</head>
+<body>
+
+<script type="application/javascript">
+
+const Ci = Components.interfaces;
+var mainWindow;
+
+var contentPage = "http://mochi.test:8888/chrome/dom/workers/test/empty.html";
+var workerScope = "http://mochi.test:8888/chrome/dom/workers/test/serviceworkers/";
+var workerURL = workerScope + "worker.js";
+
+function testOnWindow(aIsPrivate, aCallback) {
+ var win = mainWindow.OpenBrowserWindow({private: aIsPrivate});
+ win.addEventListener("load", function onLoad() {
+ win.removeEventListener("load", onLoad, false);
+ win.addEventListener("DOMContentLoaded", function onInnerLoad() {
+ if (win.content.location.href != contentPage) {
+ win.gBrowser.loadURI(contentPage);
+ return;
+ }
+
+ win.removeEventListener("DOMContentLoaded", onInnerLoad, true);
+ SimpleTest.executeSoon(function() { aCallback(win); });
+ }, true);
+
+ if (!aIsPrivate) {
+ win.gBrowser.loadURI(contentPage);
+ }
+ }, true);
+}
+
+function setupWindow() {
+ mainWindow = window.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIWebNavigation)
+ .QueryInterface(Ci.nsIDocShellTreeItem)
+ .rootTreeItem
+ .QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIDOMWindow);
+ runTest();
+}
+
+var wN;
+var registration;
+var wP;
+
+function testPrivateWindow() {
+ testOnWindow(true, function(aWin) {
+ wP = aWin;
+ ok(!("serviceWorker" in wP.content.navigator), "ServiceWorkers are not available for private windows");
+ runTest();
+ });
+}
+
+function doTests() {
+ testOnWindow(false, function(aWin) {
+ wN = aWin;
+ ok("serviceWorker" in wN.content.navigator, "ServiceWorkers are available for normal windows");
+
+ wN.content.navigator.serviceWorker.register(workerURL,
+ { scope: workerScope })
+ .then(function(aRegistration) {
+ registration = aRegistration;
+ ok(registration, "Registering a service worker in a normal window should succeed");
+
+ // Bug 1255621: We should be able to load a controlled document in a private window.
+ testPrivateWindow();
+ }, function(aError) {
+ ok(false, "Error registering worker in normal window: " + aError);
+ testPrivateWindow();
+ });
+ });
+}
+
+var steps = [
+ setupWindow,
+ doTests
+];
+
+function cleanup() {
+ wN.close();
+ wP.close();
+
+ SimpleTest.finish();
+}
+
+function runTest() {
+ if (!steps.length) {
+ registration.unregister().then(cleanup, cleanup);
+
+ return;
+ }
+
+ var step = steps.shift();
+ step();
+}
+
+SimpleTest.waitForExplicitFinish();
+SpecialPowers.pushPrefEnv({"set": [
+ ["dom.serviceWorkers.enabled", true],
+ ["dom.serviceWorkers.testing.enabled", true],
+ ["browser.startup.page", 0],
+ ["browser.startup.homepage_override.mstone", "ignore"],
+]}, runTest);
+
+</script>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test that registering a service worker uses the docuemnt URI for the secure origin check</title>
+ <script type="text/javascript" src="http://mochi.test:8888/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="http://mochi.test:8888/tests/SimpleTest/test.css" />
+ <base href="https://mozilla.org/">
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test"></pre>
+<script class="testbody" type="text/javascript">
+
+ function runTest() {
+ navigator.serviceWorker.register("http://mochi.test:8888/tests/dom/workers/test/serviceworkers/empty.js")
+ .then(reg => {
+ ok(false, "Register should fail");
+ SimpleTest.finish();
+ }, err => {
+ is(err.name, "SecurityError", "Registration should fail with SecurityError");
+ SimpleTest.finish();
+ });
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ onload = function() {
+ SpecialPowers.pushPrefEnv({"set": [
+ ["dom.serviceWorkers.exemptFromPerDomainMax", true],
+ ["dom.serviceWorkers.enabled", true],
+ ["dom.serviceWorkers.openWindow.enabled", true],
+ ]}, runTest);
+ };
+</script>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1172948 - Test that registering a service worker from inside an HTTPS iframe embedded in an HTTP iframe doesn't work</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test"></pre>
+<script class="testbody" type="text/javascript">
+
+ function runTest() {
+ var iframe = document.createElement("iframe");
+ iframe.src = "https://example.com/tests/dom/workers/test/serviceworkers/register_https.html";
+ document.body.appendChild(iframe);
+
+ window.onmessage = event => {
+ switch (event.data.type) {
+ case "ok":
+ ok(event.data.status, event.data.msg);
+ break;
+ case "done":
+ SimpleTest.finish();
+ break;
+ }
+ };
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ onload = function() {
+ SpecialPowers.pushPrefEnv({"set": [
+ ["dom.serviceWorkers.exemptFromPerDomainMax", true],
+ ["dom.serviceWorkers.enabled", true],
+ ["dom.serviceWorkers.openWindow.enabled", true],
+ ]}, runTest);
+ };
+</script>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1121157 - Test that Request objects passed to FetchEvent have the correct context</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="test_request_context.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<iframe></iframe>
+<script type="text/javascript">
+var gTest = "testAudio";
+</script>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1121157 - Test that Request objects passed to FetchEvent have the correct context</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="test_request_context.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<iframe></iframe>
+<script type="text/javascript">
+var gTest = "testBeacon";
+</script>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1121157 - Test that Request objects passed to FetchEvent have the correct context</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="test_request_context.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<iframe></iframe>
+<script type="text/javascript">
+var gTest = "testCache";
+</script>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1121157 - Test that Request objects passed to FetchEvent have the correct context</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="test_request_context.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<iframe></iframe>
+<script type="text/javascript">
+var gTest = "testCSPReport";
+</script>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1121157 - Test that Request objects passed to FetchEvent have the correct context</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="test_request_context.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<iframe></iframe>
+<script type="text/javascript">
+var gTest = "testEmbed";
+</script>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1121157 - Test that Request objects passed to FetchEvent have the correct context</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="test_request_context.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<iframe></iframe>
+<script type="text/javascript">
+var gTest = "testFetch";
+</script>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1121157 - Test that Request objects passed to FetchEvent have the correct context</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="test_request_context.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<iframe></iframe>
+<script type="text/javascript">
+var gTest = "testFont";
+</script>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1121157 - Test that Request objects passed to FetchEvent have the correct context</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="test_request_context.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<iframe></iframe>
+<script type="text/javascript">
+var gTest = "testFrame";
+</script>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1121157 - Test that Request objects passed to FetchEvent have the correct context</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="test_request_context.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<iframe></iframe>
+<script type="text/javascript">
+var gTest = "testIFrame";
+</script>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1121157 - Test that Request objects passed to FetchEvent have the correct context</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="test_request_context.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<iframe></iframe>
+<script type="text/javascript">
+var gTest = "testImage";
+</script>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1121157 - Test that Request objects passed to FetchEvent have the correct context</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="test_request_context.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<iframe></iframe>
+<script type="text/javascript">
+var gTest = "testImageSrcSet";
+</script>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1121157 - Test that Request objects passed to FetchEvent have the correct context</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="test_request_context.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<iframe></iframe>
+<script type="text/javascript">
+var gTest = "testInternal";
+</script>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1121157 - Test that Request objects passed to FetchEvent have the correct context</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="test_request_context.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<iframe></iframe>
+<script type="text/javascript">
+var gTest = "testNestedWorker";
+</script>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1121157 - Test that Request objects passed to FetchEvent have the correct context</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="test_request_context.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<iframe></iframe>
+<script type="text/javascript">
+var gTest = "testNestedWorkerInSharedWorker";
+</script>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1121157 - Test that Request objects passed to FetchEvent have the correct context</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="test_request_context.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<iframe></iframe>
+<script type="text/javascript">
+var gTest = "testObject";
+</script>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1121157 - Test that Request objects passed to FetchEvent have the correct context</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="test_request_context.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<iframe></iframe>
+<script type="text/javascript">
+var gTest = "testPicture";
+</script>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1121157 - Test that Request objects passed to FetchEvent have the correct context</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="test_request_context.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<iframe></iframe>
+<script type="text/javascript">
+var gTest = "testPing";
+</script>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1121157 - Test that Request objects passed to FetchEvent have the correct context</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="test_request_context.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<iframe></iframe>
+<script type="text/javascript">
+var gTest = "testPlugin";
+</script>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1121157 - Test that Request objects passed to FetchEvent have the correct context</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="test_request_context.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<iframe></iframe>
+<script type="text/javascript">
+var gTest = "testScript";
+</script>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1121157 - Test that Request objects passed to FetchEvent have the correct context</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="test_request_context.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<iframe></iframe>
+<script type="text/javascript">
+var gTest = "testSharedWorker";
+</script>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1121157 - Test that Request objects passed to FetchEvent have the correct context</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="test_request_context.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<iframe></iframe>
+<script type="text/javascript">
+var gTest = "testStyle";
+</script>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1121157 - Test that Request objects passed to FetchEvent have the correct context</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="test_request_context.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<iframe></iframe>
+<script type="text/javascript">
+var gTest = "testTrack";
+</script>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1121157 - Test that Request objects passed to FetchEvent have the correct context</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="test_request_context.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<iframe></iframe>
+<script type="text/javascript">
+var gTest = "testVideo";
+</script>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1121157 - Test that Request objects passed to FetchEvent have the correct context</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="test_request_context.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<iframe></iframe>
+<script type="text/javascript">
+var gTest = "testWorker";
+</script>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1121157 - Test that Request objects passed to FetchEvent have the correct context</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="test_request_context.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<iframe></iframe>
+<script type="text/javascript">
+var gTest = "testXHR";
+</script>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1121157 - Test that Request objects passed to FetchEvent have the correct context</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="test_request_context.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<iframe></iframe>
+<script type="text/javascript">
+var gTest = "testXSLT";
+</script>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1142727 - Test that sandboxed iframes are not intercepted</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content">
+<iframe sandbox="allow-scripts allow-same-origin"></iframe>
+</div>
+<pre id="test"></pre>
+<script class="testbody" type="text/javascript">
+
+ var iframe;
+ function runTest() {
+ iframe = document.querySelector("iframe");
+ iframe.src = "/tests/dom/workers/test/serviceworkers/fetch/sandbox/register.html";
+ window.onmessage = function(e) {
+ if (e.data.status == "ok") {
+ ok(e.data.result, e.data.message);
+ } else if (e.data.status == "registrationdone") {
+ iframe.src = "/tests/dom/workers/test/serviceworkers/fetch/sandbox/index.html";
+ } else if (e.data.status == "done") {
+ iframe.src = "/tests/dom/workers/test/serviceworkers/fetch/sandbox/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": [
+ ["dom.serviceWorkers.exemptFromPerDomainMax", true],
+ ["dom.serviceWorkers.enabled", true],
+ ["dom.serviceWorkers.testing.enabled", true],
+ ]}, runTest);
+ };
+</script>
+</pre>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1080109 - Clear ServiceWorker registrations for all domains</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+<script class="testbody" type="text/javascript">
+
+ function start() {
+ const Cc = SpecialPowers.Cc;
+ const Ci = SpecialPowers.Ci;
+
+ function testNotIntercepted() {
+ testFrame("sanitize/frame.html").then(function(body) {
+ is(body, "FAIL", "Expected frame to not be controlled");
+ // No need to unregister since that already happened.
+ navigator.serviceWorker.getRegistration("sanitize/foo").then(function(reg) {
+ ok(reg === undefined, "There should no longer be a valid registration");
+ }, function(e) {
+ ok(false, "getRegistration() should not error");
+ }).then(function(e) {
+ SimpleTest.finish();
+ });
+ });
+ }
+
+ registerSW().then(function() {
+ return testFrame("sanitize/frame.html").then(function(body) {
+ is(body, "intercepted", "Expected serviceworker to intercept request");
+ });
+ }).then(function() {
+ return navigator.serviceWorker.getRegistration("sanitize/foo");
+ }).then(function(reg) {
+ reg.active.onstatechange = function(e) {
+ e.target.onstatechange = null;
+ ok(e.target.state, "redundant", "On clearing data, serviceworker should become redundant");
+ testNotIntercepted();
+ };
+ }).then(function() {
+ SpecialPowers.removeAllServiceWorkerData();
+ });
+ }
+
+ function testFrame(src) {
+ return new Promise(function(resolve, reject) {
+ var iframe = document.createElement("iframe");
+ iframe.src = src;
+ window.onmessage = function(message) {
+ window.onmessage = null;
+ iframe.src = "about:blank";
+ document.body.removeChild(iframe);
+ iframe = null;
+ SpecialPowers.exactGC(function() {
+ resolve(message.data);
+ });
+ };
+ document.body.appendChild(iframe);
+ });
+ }
+
+ function registerSW() {
+ return testFrame("sanitize/register.html");
+ }
+
+ SimpleTest.waitForExplicitFinish();
+
+ SpecialPowers.pushPrefEnv({"set": [
+ ["dom.serviceWorkers.exemptFromPerDomainMax", true],
+ ["dom.serviceWorkers.enabled", true],
+ ["dom.serviceWorkers.testing.enabled", true],
+ ]}, function() {
+ start();
+ });
+</script>
+</pre>
+</body>
+</html>
+
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1080109 - Clear ServiceWorker registrations for specific domains</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+<script class="testbody" type="text/javascript">
+
+ function start() {
+ const Cc = SpecialPowers.Cc;
+ const Ci = SpecialPowers.Ci;
+
+ function checkDomainRegistration(domain, exists) {
+ return testFrame("http://" + domain + "/tests/dom/workers/test/serviceworkers/sanitize/example_check_and_unregister.html").then(function(body) {
+ if (body === "FAIL") {
+ ok(false, "Error acquiring registration or unregistering for " + domain);
+ } else {
+ if (exists) {
+ ok(body === true, "Expected " + domain + " to still have a registration.");
+ } else {
+ ok(body === false, "Expected " + domain + " to have no registration.");
+ }
+ }
+ });
+ }
+
+ registerSW().then(function() {
+ return testFrame("http://example.com/tests/dom/workers/test/serviceworkers/sanitize/frame.html").then(function(body) {
+ is(body, "intercepted", "Expected serviceworker to intercept request");
+ });
+ }).then(function() {
+ SpecialPowers.removeServiceWorkerDataForExampleDomain();
+ }).then(function() {
+ return checkDomainRegistration("prefixexample.com", true /* exists */)
+ .then(function(e) {
+ return checkDomainRegistration("example.com", false /* exists */);
+ }).then(function(e) {
+ SimpleTest.finish();
+ });
+ })
+ }
+
+ function testFrame(src) {
+ return new Promise(function(resolve, reject) {
+ var iframe = document.createElement("iframe");
+ iframe.src = src;
+ window.onmessage = function(message) {
+ window.onmessage = null;
+ iframe.src = "about:blank";
+ document.body.removeChild(iframe);
+ iframe = null;
+ SpecialPowers.exactGC(function() {
+ resolve(message.data);
+ });
+ };
+ document.body.appendChild(iframe);
+ });
+ }
+
+ function registerSW() {
+ return testFrame("http://example.com/tests/dom/workers/test/serviceworkers/sanitize/register.html")
+ .then(function(e) {
+ // Register for prefixexample.com and then ensure it does not get unregistered.
+ return testFrame("http://prefixexample.com/tests/dom/workers/test/serviceworkers/sanitize/register.html");
+ });
+ }
+
+ SimpleTest.waitForExplicitFinish();
+
+ SpecialPowers.pushPrefEnv({"set": [
+ ["dom.serviceWorkers.exemptFromPerDomainMax", true],
+ ["dom.serviceWorkers.enabled", true],
+ ["dom.serviceWorkers.testing.enabled", true],
+ ]}, function() {
+ start();
+ });
+</script>
+</pre>
+</body>
+</html>
+
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 984048 - Test scope glob matching.</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+<script class="testbody" type="text/javascript">
+
+ var scriptsAndScopes = [
+ [ "worker.js", "./sub/dir/"],
+ [ "worker.js", "./sub/dir" ],
+ [ "worker.js", "./sub/dir.html" ],
+ [ "worker.js", "./sub/dir/a" ],
+ [ "worker.js", "./sub" ],
+ [ "worker.js", "./star*" ], // '*' has no special meaning
+ ];
+
+ function registerWorkers() {
+ var registerArray = [];
+ scriptsAndScopes.forEach(function(item) {
+ registerArray.push(navigator.serviceWorker.register(item[0], { scope: item[1] }));
+ });
+
+ // Check register()'s step 4 which uses script's url with "./" as the scope if no scope is passed.
+ // The other tests already check step 5.
+ registerArray.push(navigator.serviceWorker.register("scope/scope_worker.js"));
+
+ // Check that SW cannot be registered for a scope "above" the script's location.
+ registerArray.push(new Promise(function(resolve, reject) {
+ navigator.serviceWorker.register("scope/scope_worker.js", { scope: "./" })
+ .then(function() {
+ ok(false, "registration scope has to be inside service worker script scope.");
+ reject();
+ }, function() {
+ ok(true, "registration scope has to be inside service worker script scope.");
+ resolve();
+ });
+ }));
+ return Promise.all(registerArray);
+ }
+
+ function unregisterWorkers() {
+ var unregisterArray = [];
+ scriptsAndScopes.forEach(function(item) {
+ var p = navigator.serviceWorker.getRegistration(item[1]);
+ unregisterArray.push(p.then(function(reg) {
+ return reg.unregister();
+ }));
+ });
+
+ unregisterArray.push(navigator.serviceWorker.getRegistration("scope/").then(function (reg) {
+ return reg.unregister();
+ }));
+
+ return Promise.all(unregisterArray);
+ }
+
+ function testScopes() {
+ return new Promise(function(resolve, reject) {
+ var getScope = navigator.serviceWorker.getScopeForUrl.bind(navigator.serviceWorker);
+ var base = new URL(".", document.baseURI);
+
+ function p(s) {
+ return base + s;
+ }
+
+ function fail(fn) {
+ try {
+ getScope(p("index.html"));
+ ok(false, "No registration");
+ } catch(e) {
+ ok(true, "No registration");
+ }
+ }
+
+ ok(getScope(p("sub.html")) === p("sub"), "Scope should match");
+ ok(getScope(p("sub/dir.html")) === p("sub/dir.html"), "Scope should match");
+ ok(getScope(p("sub/dir")) === p("sub/dir"), "Scope should match");
+ ok(getScope(p("sub/dir/foo")) === p("sub/dir/"), "Scope should match");
+ ok(getScope(p("sub/dir/afoo")) === p("sub/dir/a"), "Scope should match");
+ ok(getScope(p("star*wars")) === p("star*"), "Scope should match");
+ ok(getScope(p("scope/some_file.html")) === p("scope/"), "Scope should match");
+ fail("index.html");
+ fail("sua.html");
+ fail("star/a.html");
+ resolve();
+ });
+ }
+
+ function runTest() {
+ registerWorkers()
+ .then(testScopes)
+ .then(unregisterWorkers)
+ .then(function() {
+ SimpleTest.finish();
+ }).catch(function(e) {
+ ok(false, "Some test failed with error " + e);
+ SimpleTest.finish();
+ });
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ SpecialPowers.pushPrefEnv({"set": [
+ ["dom.serviceWorkers.exemptFromPerDomainMax", true],
+ ["dom.serviceWorkers.enabled", true],
+ ["dom.serviceWorkers.testing.enabled", true]
+ ]}, runTest);
+</script>
+</pre>
+</body>
+</html>
+
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test the Service-Worker-Allowed header</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<div id="content"></div>
+<script class="testbody" type="text/javascript">
+ var gTests = [
+ "worker_scope_different.js",
+ "worker_scope_different2.js",
+ "worker_scope_too_deep.js",
+ ];
+
+ function testPermissiveHeader() {
+ // Make sure that this registration succeeds, as the prefix check should pass.
+ return navigator.serviceWorker.register("swa/worker_scope_too_narrow.js", {scope: "swa/"})
+ .then(swr => {
+ ok(true, "Registration should finish successfully");
+ return swr.unregister();
+ }, err => {
+ ok(false, "Unexpected error when registering the service worker: " + err);
+ });
+ }
+
+ function testPreciseHeader() {
+ // Make sure that this registration succeeds, as the prefix check should pass
+ // given that we parse the use the full pathname from this URL..
+ return navigator.serviceWorker.register("swa/worker_scope_precise.js", {scope: "swa/"})
+ .then(swr => {
+ ok(true, "Registration should finish successfully");
+ return swr.unregister();
+ }, err => {
+ ok(false, "Unexpected error when registering the service worker: " + err);
+ });
+ }
+
+ function runTest() {
+ Promise.all(gTests.map(testName => {
+ return new Promise((resolve, reject) => {
+ // Make sure that registration fails.
+ navigator.serviceWorker.register("swa/" + testName, {scope: "swa/"})
+ .then(reject, resolve);
+ });
+ })).then(values => {
+ values.forEach(error => {
+ is(error.name, "SecurityError", "Registration should fail");
+ });
+ Promise.all([
+ testPermissiveHeader(),
+ testPreciseHeader(),
+ ]).then(SimpleTest.finish, SimpleTest.finish);
+ }, (x) => {
+ ok(false, "Registration should not succeed, but it did");
+ SimpleTest.finish();
+ });
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ SpecialPowers.pushPrefEnv({"set": [
+ ["dom.serviceWorkers.enabled", true],
+ ["dom.serviceWorkers.testing.enabled", true],
+ ["dom.serviceWorkers.exemptFromPerDomainMax", true],
+ ]}, runTest);
+</script>
+</pre>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test that service worker scripts are fetched with a Service-Worker: script header</title>
+ <script type="text/javascript" src="http://mochi.test:8888/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="http://mochi.test:8888/tests/SimpleTest/test.css" />
+ <base href="https://mozilla.org/">
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test"></pre>
+<script class="testbody" type="text/javascript">
+
+ function runTest() {
+ navigator.serviceWorker.register("http://mochi.test:8888/tests/dom/workers/test/serviceworkers/header_checker.sjs")
+ .then(reg => {
+ ok(true, "Register should succeed");
+ reg.unregister().then(() => SimpleTest.finish());
+ }, err => {
+ ok(false, "Register should not fail");
+ SimpleTest.finish();
+ });
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ onload = function() {
+ SpecialPowers.pushPrefEnv({"set": [
+ ["dom.serviceWorkers.exemptFromPerDomainMax", true],
+ ["dom.serviceWorkers.testing.enabled", true],
+ ["dom.serviceWorkers.enabled", true],
+ ]}, runTest);
+ };
+</script>
+</body>
+</html>
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 @@
+<!-- Any copyright is dedicated to the Public Domain.
+ - http://creativecommons.org/publicdomain/zero/1.0/ -->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Validate Interfaces Exposed to Service Workers</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ <script type="text/javascript" src="../worker_driver.js"></script>
+</head>
+<body>
+<script class="testbody" type="text/javascript">
+
+ function setupSW(registration) {
+ var worker = registration.waiting ||
+ registration.active;
+ window.onmessage = function(event) {
+ if (event.data.type == 'finish') {
+ registration.unregister().then(function(success) {
+ ok(success, "The service worker should be unregistered successfully");
+
+ SimpleTest.finish();
+ }, function(e) {
+ dump("Unregistering the SW failed with " + e + "\n");
+ SimpleTest.finish();
+ });
+ } else if (event.data.type == 'status') {
+ ok(event.data.status, event.data.msg);
+
+ } else if (event.data.type == 'getPrefs') {
+ var result = {};
+ event.data.prefs.forEach(function(pref) {
+ result[pref] = SpecialPowers.Services.prefs.getBoolPref(pref);
+ });
+ worker.postMessage({
+ type: 'returnPrefs',
+ prefs: event.data.prefs,
+ result: result
+ });
+
+ } 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();
+ };
+
+ var iframe = document.createElement("iframe");
+ iframe.src = "message_receiver.html";
+ iframe.onload = function() {
+ worker.postMessage({ script: "test_serviceworker_interfaces.js" });
+ };
+ document.body.appendChild(iframe);
+ }
+
+ function runTest() {
+ navigator.serviceWorker.ready.then(setupSW);
+ navigator.serviceWorker.register("serviceworker_wrapper.js", {scope: "."});
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ onload = function() {
+ // The handling of "dom.caches.enabled" here is a bit complicated. What we
+ // want to happen is that Cache is always enabled in service workers. So
+ // if service workers are disabled by default we want to force on both
+ // service workers and "dom.caches.enabled". But if service workers are
+ // enabled by default, we do not want to mess with the "dom.caches.enabled"
+ // value, since that would defeat the purpose of the test. Use a subframe
+ // to decide whether service workers are enabled by default, so we don't
+ // force creation of our own Navigator object before our prefs are set.
+ var prefs = [
+ ["dom.serviceWorkers.exemptFromPerDomainMax", true],
+ ["dom.serviceWorkers.enabled", true],
+ ["dom.serviceWorkers.testing.enabled", true],
+ ];
+
+ var subframe = document.createElement("iframe");
+ document.body.appendChild(subframe);
+ if (!("serviceWorker" in subframe.contentWindow.navigator)) {
+ prefs.push(["dom.caches.enabled", true]);
+ }
+ subframe.remove();
+
+ SpecialPowers.pushPrefEnv({"set": prefs}, runTest);
+ };
+</script>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1141274 - test that service workers and shared workers are separate</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+<iframe></iframe>
+</div>
+<pre id="test"></pre>
+<script class="testbody" type="text/javascript">
+
+ var iframe;
+ const SCOPE = "http://mochi.test:8888/tests/dom/workers/test/serviceworkers/";
+ function runTest() {
+ navigator.serviceWorker.ready.then(setupSW);
+ navigator.serviceWorker.register("serviceworker_not_sharedworker.js",
+ {scope: SCOPE});
+ }
+
+ var sw, worker;
+ function setupSW(registration) {
+ sw = registration.waiting || registration.active;
+ worker = new SharedWorker("serviceworker_not_sharedworker.js", SCOPE);
+ worker.port.start();
+ iframe = document.querySelector("iframe");
+ iframe.src = "message_receiver.html";
+ iframe.onload = function() {
+ window.onmessage = function(e) {
+ is(e.data.result, "serviceworker", "We should be talking to a service worker");
+ window.onmessage = null;
+ worker.port.onmessage = function(e) {
+ is(e.data.result, "sharedworker", "We should be talking to a shared worker");
+ registration.unregister().then(function(success) {
+ ok(success, "unregister should succeed");
+ SimpleTest.finish();
+ }, function(e) {
+ dump("Unregistering the SW failed with " + e + "\n");
+ SimpleTest.finish();
+ });
+ };
+ worker.port.postMessage({msg: "whoareyou"});
+ };
+ sw.postMessage({msg: "whoareyou"});
+ };
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ onload = function() {
+ SpecialPowers.pushPrefEnv({"set": [
+ ["dom.serviceWorkers.exemptFromPerDomainMax", true],
+ ["dom.serviceWorkers.enabled", true],
+ ["dom.serviceWorkers.testing.enabled", true]
+ ]}, runTest);
+ };
+</script>
+</pre>
+</body>
+</html>
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 @@
+<?xml version="1.0"?>
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<window title="Test for ServiceWorkerInfo"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="test();">
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+ <script type="application/javascript" src="chrome_helpers.js"/>
+ <script type="application/javascript">
+ <![CDATA[
+
+ let IFRAME_URL = EXAMPLE_URL + "serviceworkerinfo_iframe.html";
+
+ function wait_for_active_worker(registration) {
+ ok(registration, "Registration is valid.");
+ return new Promise(function(res, rej) {
+ if (registration.activeWorker) {
+ res(registration);
+ return;
+ }
+ let listener = {
+ onChange: function() {
+ if (registration.activeWorker) {
+ registration.removeListener(listener);
+ res(registration);
+ }
+ }
+ }
+ registration.addListener(listener);
+ });
+ }
+
+ function test() {
+ SimpleTest.waitForExplicitFinish();
+
+ SpecialPowers.pushPrefEnv({'set': [
+ ["dom.serviceWorkers.enabled", true],
+ ["dom.serviceWorkers.idle_extended_timeout", 1000000],
+ ["dom.serviceWorkers.idle_timeout", 0],
+ ["dom.serviceWorkers.testing.enabled", true],
+ ]}, function () {
+ Task.spawn(function *() {
+ let iframe = $("iframe");
+ let promise = new Promise(function (resolve) {
+ iframe.onload = function () {
+ resolve();
+ };
+ });
+ iframe.src = IFRAME_URL;
+ yield promise;
+
+ info("Check that a service worker eventually shuts down.");
+ promise = Promise.all([
+ waitForRegister(EXAMPLE_URL),
+ waitForServiceWorkerShutdown()
+ ]);
+ iframe.contentWindow.postMessage("register", "*");
+ let [registration] = yield promise;
+
+ // Make sure the worker is active.
+ registration = yield wait_for_active_worker(registration);
+
+ let activeWorker = registration.activeWorker;
+ ok(activeWorker !== null, "Worker is not active!");
+ ok(activeWorker.debugger === null);
+
+ info("Attach a debugger to the service worker, and check that the " +
+ "service worker is restarted.");
+ activeWorker.attachDebugger();
+ let workerDebugger = activeWorker.debugger;
+ ok(workerDebugger !== null);
+
+ // Verify debugger properties
+ ok(workerDebugger.principal instanceof Ci.nsIPrincipal);
+ is(workerDebugger.url, EXAMPLE_URL + "worker.js");
+
+ info("Verify that getRegistrationByPrincipal return the same " +
+ "nsIServiceWorkerRegistrationInfo");
+ let reg = swm.getRegistrationByPrincipal(workerDebugger.principal,
+ workerDebugger.url);
+ is(reg, registration);
+
+ info("Check that getWorkerByID returns the same nsIWorkerDebugger");
+ is(activeWorker, reg.getWorkerByID(workerDebugger.serviceWorkerID));
+
+ info("Detach the debugger from the service worker, and check that " +
+ "the service worker eventually shuts down again.");
+ promise = waitForServiceWorkerShutdown();
+ activeWorker.detachDebugger();
+ yield promise;
+ ok(activeWorker.debugger === null);
+
+ promise = waitForUnregister(EXAMPLE_URL);
+ iframe.contentWindow.postMessage("unregister", "*");
+ registration = yield promise;
+
+ SimpleTest.finish();
+ });
+ });
+ }
+
+ ]]>
+ </script>
+
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <p id="display"></p>
+ <div id="content" style="display:none;"></div>
+ <pre id="test"></pre>
+ <iframe id="iframe"></iframe>
+ </body>
+ <label id="test-result"/>
+</window>
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 @@
+<?xml version="1.0"?>
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<window title="Test for ServiceWorkerManager"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="test();">
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+ <script type="application/javascript" src="chrome_helpers.js"/>
+ <script type="application/javascript">
+ <![CDATA[
+
+ let IFRAME_URL = EXAMPLE_URL + "serviceworkermanager_iframe.html";
+
+ function test() {
+ SimpleTest.waitForExplicitFinish();
+
+ SpecialPowers.pushPrefEnv({'set': [
+ ["dom.serviceWorkers.enabled", true],
+ ["dom.serviceWorkers.testing.enabled", true],
+ ]}, function () {
+ Task.spawn(function* () {
+ let registrations = swm.getAllRegistrations();
+ is(registrations.length, 0);
+
+ let iframe = $("iframe");
+ let promise = waitForIframeLoad(iframe);
+ iframe.src = IFRAME_URL;
+ yield promise;
+
+ info("Check that the service worker manager notifies its listeners " +
+ "when a service worker is registered.");
+ promise = waitForRegister(EXAMPLE_URL);
+ iframe.contentWindow.postMessage("register", "*");
+ let registration = yield promise;
+
+ registrations = swm.getAllRegistrations();
+ is(registrations.length, 1);
+ is(registrations.queryElementAt(0, Ci.nsIServiceWorkerRegistrationInfo),
+ registration);
+
+ info("Check that the service worker manager does not notify its " +
+ "listeners when a service worker is registered with the same " +
+ "scope as an existing registration.");
+ let listener = {
+ onRegister: function () {
+ ok(false, "Listener should not have been notified.");
+ }
+ };
+ swm.addListener(listener);
+ iframe.contentWindow.postMessage("register", "*");
+
+ info("Check that the service worker manager notifies its listeners " +
+ "when a service worker is unregistered.");
+ promise = waitForUnregister(EXAMPLE_URL);
+ iframe.contentWindow.postMessage("unregister", "*");
+ registration = yield promise;
+ swm.removeListener(listener);
+
+ registrations = swm.getAllRegistrations();
+ is(registrations.length, 0);
+
+ SimpleTest.finish();
+ });
+ });
+ }
+
+ ]]>
+ </script>
+
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <p id="display"></p>
+ <div id="content" style="display:none;"></div>
+ <pre id="test"></pre>
+ <iframe id="iframe"></iframe>
+ </body>
+ <label id="test-result"/>
+</window>
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 @@
+<?xml version="1.0"?>
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<window title="Test for ServiceWorkerRegistrationInfo"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="test();">
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+ <script type="application/javascript" src="chrome_helpers.js"/>
+ <script type="application/javascript">
+ <![CDATA[
+
+ let IFRAME_URL = EXAMPLE_URL + "serviceworkerregistrationinfo_iframe.html";
+
+ function test() {
+ SimpleTest.waitForExplicitFinish();
+
+ SpecialPowers.pushPrefEnv({'set': [
+ ["dom.serviceWorkers.enabled", true],
+ ["dom.serviceWorkers.testing.enabled", true],
+ ]}, function () {
+ Task.spawn(function* () {
+ let iframe = $("iframe");
+ let promise = waitForIframeLoad(iframe);
+ iframe.src = IFRAME_URL;
+ yield promise;
+
+ // The change handler is not guaranteed to be called within the same
+ // tick of the event loop as the one in which the change happened.
+ // Because of this, the exact state of the service worker registration
+ // is only known until the handler returns.
+ //
+ // Because then-handlers are resolved asynchronously, the following
+ // checks are done using callbacks, which are called synchronously
+ // when then handler is called. These callbacks can return a promise,
+ // which is used to resolve the promise returned by the function.
+
+ info("Check that a service worker registration notifies its " +
+ "listeners when its state changes.");
+ promise = waitForRegister(EXAMPLE_URL, function (registration) {
+ is(registration.scriptSpec, "");
+ ok(registration.installingWorker === null);
+ ok(registration.waitingWorker === null);
+ ok(registration.activeWorker === null);
+
+ return waitForServiceWorkerRegistrationChange(registration, function () {
+ is(registration.scriptSpec, EXAMPLE_URL + "worker.js");
+ ok(registration.installingWorker !== null);
+ is(registration.installingWorker.scriptSpec, EXAMPLE_URL + "worker.js");
+ ok(registration.waitingWorker === null);
+ ok(registration.activeWorker === null);
+
+ return waitForServiceWorkerRegistrationChange(registration, function () {
+ ok(registration.installingWorker === null);
+ ok(registration.waitingWorker !== null);
+ ok(registration.activeWorker === null);
+
+ return waitForServiceWorkerRegistrationChange(registration, function () {
+ ok(registration.installingWorker === null);
+ ok(registration.waitingWorker === null);
+ ok(registration.activeWorker !== null);
+
+ return registration;
+ });
+ });
+ });
+ });
+ iframe.contentWindow.postMessage("register", "*");
+ let registration = yield promise;
+
+ promise = waitForServiceWorkerRegistrationChange(registration, function () {
+ is(registration.scriptSpec, EXAMPLE_URL + "worker2.js");
+ ok(registration.installingWorker !== null);
+ is(registration.installingWorker.scriptSpec, EXAMPLE_URL + "worker2.js");
+ ok(registration.waitingWorker === null);
+ ok(registration.activeWorker !== null);
+
+ return waitForServiceWorkerRegistrationChange(registration, function () {
+ ok(registration.installingWorker === null);
+ ok(registration.waitingWorker !== null);
+ ok(registration.activeWorker !== null);
+
+ return waitForServiceWorkerRegistrationChange(registration, function () {
+ ok(registration.installingWorker === null);
+ ok(registration.waitingWorker === null);
+ ok(registration.activeWorker !== null);
+
+ return registration;
+ });
+ });
+ });
+ iframe.contentWindow.postMessage("register", "*");
+ yield promise;
+
+ iframe.contentWindow.postMessage("unregister", "*");
+ yield waitForUnregister(EXAMPLE_URL);
+
+ SimpleTest.finish();
+ });
+ });
+ }
+
+ ]]>
+ </script>
+
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <p id="display"></p>
+ <div id="content" style="display:none;"></div>
+ <pre id="test"></pre>
+ <iframe id="iframe"></iframe>
+ </body>
+ <label id="test-result"/>
+</window>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1131352 - Add ServiceWorkerGlobalScope skipWaiting()</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+<script class="testbody" type="text/javascript">
+ var registration, iframe, content;
+
+ function start() {
+ return navigator.serviceWorker.register("worker.js",
+ {scope: "./skip_waiting_scope/"});
+ }
+
+ function waitForActivated(swr) {
+ registration = swr;
+ var promise = new Promise(function(resolve, reject) {
+ window.onmessage = function(e) {
+ if (e.data === "READY") {
+ ok(true, "Active worker is activated now");
+ resolve();
+ } else {
+ ok(false, "Wrong value. Somenting went wrong");
+ resolve();
+ }
+ }
+ });
+
+ iframe = document.createElement("iframe");
+ iframe.setAttribute("src", "skip_waiting_scope/index.html");
+
+ content = document.getElementById("content");
+ content.appendChild(iframe);
+
+ return promise;
+ }
+
+ function checkWhetherItSkippedWaiting() {
+ var promise = new Promise(function(resolve, reject) {
+ window.onmessage = function (evt) {
+ if (evt.data.event === "controllerchange") {
+ ok(evt.data.controllerScriptURL.match("skip_waiting_installed_worker"),
+ "The controller changed after skiping the waiting step");
+ resolve();
+ } else {
+ ok(false, "Wrong value. Somenting went wrong");
+ resolve();
+ }
+ };
+ });
+
+ navigator.serviceWorker.register("skip_waiting_installed_worker.js",
+ {scope: "./skip_waiting_scope/"})
+ .then(swr => {
+ registration = swr;
+ });
+
+ return promise;
+ }
+
+ function clean() {
+ content.removeChild(iframe);
+
+ return registration.unregister();
+ }
+
+ function runTest() {
+ start()
+ .then(waitForActivated)
+ .then(checkWhetherItSkippedWaiting)
+ .then(clean)
+ .catch(function(e) {
+ ok(false, "Some test failed with error " + e);
+ }).then(SimpleTest.finish);
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ SpecialPowers.pushPrefEnv({"set": [
+ ["dom.serviceWorkers.exemptFromPerDomainMax", true],
+ ["dom.serviceWorkers.enabled", true],
+ ["dom.serviceWorkers.testing.enabled", true]
+ ]}, runTest);
+</script>
+</pre>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1170550 - test registration of service worker scripts with a strict mode warning</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test"></pre>
+<script class="testbody" type="text/javascript">
+
+ function runTest() {
+ navigator.serviceWorker
+ .register("strict_mode_warning.js", {scope: "strict_mode_warning"})
+ .then((reg) => {
+ ok(true, "Registration should not fail for warnings");
+ return reg.unregister();
+ })
+ .then(() => {
+ SimpleTest.finish();
+ });
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ onload = function() {
+ SpecialPowers.pushPrefEnv({"set": [
+ ["dom.serviceWorkers.exemptFromPerDomainMax", true],
+ ["dom.serviceWorkers.enabled", true],
+ ["dom.serviceWorkers.testing.enabled", true],
+ ]}, runTest);
+ };
+</script>
+</pre>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta http-equiv="Content-type" content="text/html;charset=UTF-8">
+ <title>Bug 1152899 - Disallow the interception of third-party iframes using service workers when the third-party cookie preference is set</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+<iframe></iframe>
+</div>
+<pre id="test"></pre>
+<script class="testbody" type="text/javascript;version=1.7">
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.requestLongerTimeout(2);
+
+let index = 0;
+function next() {
+ info("Step " + index);
+ if (index >= steps.length) {
+ SimpleTest.finish();
+ return;
+ }
+ try {
+ let i = index++;
+ steps[i]();
+ } catch(ex) {
+ ok(false, "Caught exception", ex);
+ }
+}
+
+onload = next;
+
+let iframe;
+let basePath = "/tests/dom/workers/test/serviceworkers/thirdparty/";
+let origin = window.location.protocol + "//" + window.location.host;
+let thirdPartyOrigin = "https://example.com";
+
+function testIframeLoaded() {
+ ok(true, "Iframe loaded");
+ iframe.removeEventListener("load", testIframeLoaded);
+ let message = {
+ source: "parent",
+ href: origin + basePath + "iframe2.html"
+ };
+ iframe.contentWindow.postMessage(message.toSource(), "*");
+}
+
+function loadThirdPartyIframe() {
+ let message = {
+ source: "parent",
+ href: thirdPartyOrigin + basePath + "iframe2.html"
+ }
+ iframe.contentWindow.postMessage(message.toSource(), "*");
+}
+
+function runTest(aExpectedResponses) {
+ iframe = document.querySelector("iframe");
+ iframe.src = thirdPartyOrigin + basePath + "register.html";
+ let responsesIndex = 0;
+ window.onmessage = function(e) {
+ let status = e.data.status;
+ let expected = aExpectedResponses[responsesIndex];
+ if (status == expected.status) {
+ ok(true, "Received expected " + expected.status);
+ if (expected.next) {
+ expected.next();
+ }
+ } else {
+ ok(false, "Expected " + expected.status + " got " + status);
+ }
+ responsesIndex++;
+ };
+}
+
+function testShouldIntercept(done) {
+ runTest([{
+ status: "ok"
+ }, {
+ status: "registrationdone",
+ next: function() {
+ iframe.addEventListener("load", testIframeLoaded);
+ iframe.src = origin + basePath + "iframe1.html";
+ }
+ }, {
+ status: "networkresponse",
+ next: loadThirdPartyIframe
+ }, {
+ status: "swresponse",
+ next: function() {
+ iframe.src = thirdPartyOrigin + basePath + "unregister.html";
+ }
+ }, {
+ status: "unregistrationdone",
+ next: function() {
+ window.onmessage = null;
+ ok(true, "Test finished successfully");
+ done();
+ }
+ }]);
+}
+
+function testShouldNotIntercept(done) {
+ runTest([{
+ status: "ok"
+ }, {
+ status: "registrationdone",
+ next: function() {
+ iframe.addEventListener("load", testIframeLoaded);
+ iframe.src = origin + basePath + "iframe1.html";
+ }
+ }, {
+ status: "networkresponse",
+ next: loadThirdPartyIframe
+ }, {
+ status: "networkresponse",
+ next: function() {
+ iframe.src = thirdPartyOrigin + basePath + "unregister.html";
+ }
+ }, {
+ status: "unregistrationdone",
+ next: function() {
+ window.onmessage = null;
+ ok(true, "Test finished successfully");
+ done();
+ }
+ }]);
+}
+
+const COOKIE_BEHAVIOR_ACCEPT = 0;
+const COOKIE_BEHAVIOR_REJECTFOREIGN = 1;
+const COOKIE_BEHAVIOR_REJECT = 2;
+const COOKIE_BEHAVIOR_LIMITFOREIGN = 3;
+
+let steps = [() => {
+ SpecialPowers.pushPrefEnv({"set": [
+ ["dom.serviceWorkers.exemptFromPerDomainMax", true],
+ ["dom.serviceWorkers.enabled", true],
+ ["dom.serviceWorkers.testing.enabled", true],
+ ["browser.dom.window.dump.enabled", true],
+ ["network.cookie.cookieBehavior", COOKIE_BEHAVIOR_ACCEPT]
+ ]}, next);
+}, () => {
+ testShouldIntercept(next);
+}, () => {
+ SpecialPowers.pushPrefEnv({"set": [
+ ["network.cookie.cookieBehavior", COOKIE_BEHAVIOR_REJECTFOREIGN]
+ ]}, next);
+}, () => {
+ testShouldNotIntercept(next);
+}, () => {
+ SpecialPowers.pushPrefEnv({"set": [
+ ["network.cookie.cookieBehavior", COOKIE_BEHAVIOR_REJECT]
+ ]}, next);
+}, () => {
+ testShouldIntercept(next);
+}, () => {
+ SpecialPowers.pushPrefEnv({"set": [
+ ["network.cookie.cookieBehavior", COOKIE_BEHAVIOR_LIMITFOREIGN]
+ ]}, next);
+}, () => {
+ testShouldIntercept(next);
+}];
+
+</script>
+</pre>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 984048 - Test unregister</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+<script class="testbody" type="text/javascript">
+
+ function simpleRegister() {
+ return navigator.serviceWorker.register("worker.js", { scope: "unregister/" }).then(function(swr) {
+ if (swr.installing) {
+ return new Promise(function(resolve, reject) {
+ swr.installing.onstatechange = function(e) {
+ if (swr.waiting) {
+ swr.waiting.onstatechange = function(e) {
+ if (swr.active) {
+ resolve();
+ } else if (swr.waiting && swr.waiting.state == "redundant") {
+ reject("Should not go into redundant");
+ }
+ }
+ } else {
+ if (swr.active) {
+ resolve();
+ } else {
+ reject("No waiting and no active!");
+ }
+ }
+ }
+ });
+ } else {
+ return Promise.reject("Installing should be non-null");
+ }
+ });
+ }
+
+ function testControlled() {
+ var testPromise = new Promise(function(res, rej) {
+ window.onmessage = function(e) {
+ if (!("controlled" in e.data)) {
+ ok(false, "Something went wrong.");
+ rej();
+ return;
+ }
+
+ ok(e.data.controlled, "New window should be controlled.");
+ res();
+ }
+ })
+
+ var div = document.getElementById("content");
+ ok(div, "Parent exists");
+
+ var ifr = document.createElement("iframe");
+ ifr.setAttribute('src', "unregister/index.html");
+ div.appendChild(ifr);
+
+ return testPromise.then(function() {
+ div.removeChild(ifr);
+ });
+ }
+
+ function unregister() {
+ return navigator.serviceWorker.getRegistration("unregister/")
+ .then(function(reg) {
+ if (!reg) {
+ info("Registration already removed");
+ return;
+ }
+
+ info("getRegistration() succeeded " + reg.scope);
+ return reg.unregister().then(function(v) {
+ ok(v, "Unregister should resolve to true");
+ }, function(e) {
+ ok(false, "Unregister failed with " + e.name);
+ });
+ });
+ }
+
+ function testUncontrolled() {
+ var testPromise = new Promise(function(res, rej) {
+ window.onmessage = function(e) {
+ if (!("controlled" in e.data)) {
+ ok(false, "Something went wrong.");
+ rej();
+ return;
+ }
+
+ ok(!e.data.controlled, "New window should not be controlled.");
+ res();
+ }
+ });
+
+ var div = document.getElementById("content");
+ ok(div, "Parent exists");
+
+ var ifr = document.createElement("iframe");
+ ifr.setAttribute('src', "unregister/index.html");
+ div.appendChild(ifr);
+
+ return testPromise.then(function() {
+ div.removeChild(ifr);
+ });
+ }
+
+ function runTest() {
+ simpleRegister()
+ .then(testControlled)
+ .then(unregister)
+ .then(testUncontrolled)
+ .then(function() {
+ SimpleTest.finish();
+ }).catch(function(e) {
+ ok(false, "Some test failed with error " + e);
+ SimpleTest.finish();
+ });
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ SpecialPowers.pushPrefEnv({"set": [
+ ["dom.serviceWorkers.exemptFromPerDomainMax", true],
+ ["dom.serviceWorkers.enabled", true],
+ ["dom.serviceWorkers.testing.enabled", true]
+ ]}, runTest);
+</script>
+</pre>
+</body>
+</html>
+
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 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+ Test that an unresolved respondWith promise will reset the channel when
+ the service worker is terminated due to idling, and that appropriate error
+ messages are logged for both the termination of the serice worker and the
+ resetting of the channel.
+ -->
+<head>
+ <title>Test for Bug 1188545</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/SpawnTask.js"></script>
+ <script src="error_reporting_helpers.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ <meta http-equiv="Content-type" content="text/html;charset=UTF-8">
+</head>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1188545">Mozilla Bug 118845</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+
+<script class="testbody" type="text/javascript">
+// (This doesn't really need to be its own task, but it allows the actual test
+// case to be self-contained.)
+add_task(function setupPrefs() {
+ return SpecialPowers.pushPrefEnv({"set": [
+ ["dom.serviceWorkers.enabled", true],
+ ["dom.serviceWorkers.testing.enabled", true],
+ ]});
+});
+
+add_task(function* grace_timeout_termination_with_interrupted_intercept() {
+ // Setup timeouts so that the service worker will go into grace timeout after
+ // a zero-length idle timeout.
+ yield SpecialPowers.pushPrefEnv({"set": [
+ ["dom.serviceWorkers.idle_timeout", 0],
+ ["dom.serviceWorkers.idle_extended_timeout", 299999]]});
+
+ // The SW will claim us once it activates; this is async, start listening now.
+ let waitForControlled = new Promise((resolve) => {
+ navigator.serviceWorker.oncontrollerchange = resolve;
+ });
+
+ let registration = yield navigator.serviceWorker.register(
+ "unresolved_fetch_worker.js", { scope: "./"} );
+ yield waitForControlled;
+ ok(navigator.serviceWorker.controller, "Controlled"); // double check!
+
+ // We want to make sure the SW is active and processing the fetch before we
+ // try and kill it. It sends us a message when it has done so.
+ let waitForFetchActive = new Promise((resolve) => {
+ navigator.serviceWorker.onmessage = resolve;
+ });
+
+ // Issue a fetch which the SW will respondWith() a never resolved promise.
+ // The fetch, however, will terminate when the SW is killed, so check that.
+ let hangingFetch = fetch("does_not_exist.html")
+ .then(() => { ok(false, "should have rejected "); },
+ () => { ok(true, "hung fetch terminates when worker dies"); });
+
+ yield waitForFetchActive;
+
+ let expectedMessage = expect_console_message(
+ // Termination error
+ "ServiceWorkerGraceTimeoutTermination",
+ [make_absolute_url("./")],
+ // The interception failure error generated by the RespondWithHandler
+ // destructor when it notices it didn't get a response before being
+ // destroyed. It logs via the intercepted channel nsIConsoleReportCollector
+ // that is eventually flushed to our document and its console.
+ "InterceptionFailedWithURL",
+ [make_absolute_url("does_not_exist.html")]
+ );
+
+ // Zero out the grace timeout too so the worker will get terminated after two
+ // zero-length timer firings. Note that we need to do something to get the
+ // SW to renew its keepalive for this to actually cause the timers to be
+ // rescheduled...
+ yield SpecialPowers.pushPrefEnv({"set": [
+ ["dom.serviceWorkers.idle_extended_timeout", 0]]});
+ // ...which we do by postMessaging it.
+ navigator.serviceWorker.controller.postMessage("doomity doom doom");
+
+ // Now wait for signs that the worker was terminated by the fetch failing.
+ yield hangingFetch;
+
+ // The worker should now be dead and the error logged, wait/assert.
+ yield wait_for_expected_message(expectedMessage);
+
+ // roll back all of our test case specific preferences and otherwise cleanup
+ yield SpecialPowers.popPrefEnv();
+ yield SpecialPowers.popPrefEnv();
+ yield registration.unregister();
+});
+</script>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 982728 - Test ServiceWorkerGlobalScope.unregister</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<div id="container"></div>
+<script class="testbody" type="text/javascript">
+
+ function simpleRegister() {
+ return navigator.serviceWorker.register("worker_unregister.js", { scope: "unregister/" }).then(function(swr) {
+ if (swr.installing) {
+ return new Promise(function(resolve, reject) {
+ swr.installing.onstatechange = function(e) {
+ if (swr.waiting) {
+ swr.waiting.onstatechange = function(e) {
+ if (swr.active) {
+ resolve();
+ } else if (swr.waiting && swr.waiting.state == "redundant") {
+ reject("Should not go into redundant");
+ }
+ }
+ } else {
+ if (swr.active) {
+ resolve();
+ } else {
+ reject("No waiting and no active!");
+ }
+ }
+ }
+ });
+ } else {
+ return Promise.reject("Installing should be non-null");
+ }
+ });
+ }
+
+ function waitForMessages(sw) {
+ var p = new Promise(function(resolve, reject) {
+ window.onmessage = function(e) {
+ if (e.data === "DONE") {
+ ok(true, "The worker has unregistered itself");
+ } else if (e.data === "ERROR") {
+ ok(false, "The worker has unregistered itself");
+ } else if (e.data === "FINISH") {
+ resolve();
+ }
+ }
+ });
+
+ var frame = document.createElement("iframe");
+ frame.setAttribute("src", "unregister/unregister.html");
+ document.body.appendChild(frame);
+
+ return p;
+ }
+
+ function runTest() {
+ simpleRegister().then(waitForMessages).catch(function(e) {
+ ok(false, "Something went wrong.");
+ }).then(function() {
+ SimpleTest.finish();
+ });
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ SpecialPowers.pushPrefEnv({"set": [
+ ["dom.serviceWorkers.exemptFromPerDomainMax", true],
+ ["dom.serviceWorkers.enabled", true],
+ ["dom.serviceWorkers.testing.enabled", true]
+ ]}, runTest);
+</script>
+</pre>
+</body>
+</html>
+
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1065366 - Test ServiceWorkerGlobalScope.update</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<div id="container"></div>
+<script class="testbody" type="text/javascript">
+
+ function simpleRegister() {
+ return navigator.serviceWorker.register("worker_update.js", { scope: "workerUpdate/" });
+ }
+
+ var registration;
+ function waitForMessages(sw) {
+ registration = sw;
+ var p = new Promise(function(resolve, reject) {
+ window.onmessage = function(e) {
+ if (e.data === "FINISH") {
+ ok(true, "The worker has updated itself");
+ resolve();
+ } else if (e.data === "FAIL") {
+ ok(false, "The worker failed to update itself");
+ resolve();
+ }
+ }
+ });
+
+ var frame = document.createElement("iframe");
+ frame.setAttribute("src", "workerUpdate/update.html");
+ document.body.appendChild(frame);
+
+ return p;
+ }
+
+ function runTest() {
+ simpleRegister().then(waitForMessages).catch(function(e) {
+ ok(false, "Something went wrong.");
+ }).then(function() {
+ return registration.unregister();
+ }).then(function() {
+ SimpleTest.finish();
+ });
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ SpecialPowers.pushPrefEnv({"set": [
+ ["dom.serviceWorkers.exemptFromPerDomainMax", true],
+ ["dom.serviceWorkers.enabled", true],
+ ["dom.serviceWorkers.testing.enabled", true]
+ ]}, runTest);
+</script>
+</pre>
+</body>
+</html>
+
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1131327 - Test ServiceWorkerRegistration.onupdatefound on ServiceWorker</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content"></div>
+<pre id="test"></pre>
+<script class="testbody" type="text/javascript">
+ var registration;
+ var promise;
+
+ function start() {
+ return navigator.serviceWorker.register("worker_updatefoundevent.js",
+ { scope: "./updatefoundevent.html" })
+ .then((swr) => registration = swr);
+ }
+
+ function startWaitForUpdateFound() {
+ registration.onupdatefound = function(e) {
+ }
+
+ promise = new Promise(function(resolve, reject) {
+ window.onmessage = function(e) {
+
+ if (e.data == "finish") {
+ ok(true, "Received updatefound");
+ resolve();
+ }
+ }
+ });
+
+ content = document.getElementById("content");
+ iframe = document.createElement("iframe");
+ content.appendChild(iframe);
+ iframe.setAttribute("src", "./updatefoundevent.html");
+
+ return Promise.resolve();
+ }
+
+ function registerNext() {
+ return navigator.serviceWorker.register("worker_updatefoundevent2.js",
+ { scope: "./updatefoundevent.html" });
+ }
+
+ function waitForUpdateFound() {
+ return promise;
+ }
+
+ function unregister() {
+ window.onmessage = null;
+ return registration.unregister().then(function(result) {
+ ok(result, "Unregister should return true.");
+ });
+ }
+
+ function runTest() {
+ start()
+ .then(startWaitForUpdateFound)
+ .then(registerNext)
+ .then(waitForUpdateFound)
+ .then(unregister)
+ .catch(function(e) {
+ ok(false, "Some test failed with error " + e);
+ }).then(SimpleTest.finish);
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ SpecialPowers.pushPrefEnv({"set": [
+ ["dom.serviceWorkers.exemptFromPerDomainMax", true],
+ ["dom.serviceWorkers.enabled", true],
+ ["dom.serviceWorkers.testing.enabled", true],
+ ]}, runTest);
+</script>
+</pre>
+</body>
+</html>
+
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1182113 - Test service worker XSLT interception</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content"></div>
+<pre id="test"></pre>
+<script class="testbody" type="text/javascript">
+ var registration;
+ var worker;
+
+ function start() {
+ return navigator.serviceWorker.register("xslt_worker.js",
+ { scope: "./" })
+ .then((swr) => {
+ registration = swr;
+
+ // Ensure the registration is active before continuing
+ var worker = registration.installing;
+ return new Promise((resolve) => {
+ if (worker.state === 'activated') {
+ resolve();
+ return;
+ }
+ worker.addEventListener('statechange', () => {
+ if (worker.state === 'activated') {
+ resolve();
+ }
+ });
+ });
+ });
+ }
+
+ function unregister() {
+ return registration.unregister().then(function(result) {
+ ok(result, "Unregister should return true.");
+ }, function(e) {
+ dump("Unregistering the SW failed with " + e + "\n");
+ });
+ }
+
+ function getXmlString(xmlObject) {
+ serializer = new XMLSerializer();
+ return serializer.serializeToString(iframe.contentDocument);
+ }
+
+ function synthetic() {
+ content = document.getElementById("content");
+ ok(content, "parent exists.");
+
+ iframe = document.createElement("iframe");
+ content.appendChild(iframe);
+
+ iframe.setAttribute('src', "xslt/test.xml");
+
+ var p = new Promise(function(res, rej) {
+ iframe.onload = function(e) {
+ dump("Set request mode\n");
+ registration.active.postMessage("synthetic");
+ xmlString = getXmlString(iframe.contentDocument);
+ ok(!xmlString.includes("Error"), "Load synthetic cross origin XSLT should be allowed");
+ res();
+ };
+ });
+
+ return p;
+ }
+
+ function cors() {
+ var p = new Promise(function(res, rej) {
+ iframe.onload = function(e) {
+ xmlString = getXmlString(iframe.contentDocument);
+ ok(!xmlString.includes("Error"), "Load CORS cross origin XSLT should be allowed");
+ res();
+ };
+ });
+
+ registration.active.postMessage("cors");
+ iframe.setAttribute('src', "xslt/test.xml");
+
+ return p;
+ }
+
+ function opaque() {
+ var p = new Promise(function(res, rej) {
+ iframe.onload = function(e) {
+ xmlString = getXmlString(iframe.contentDocument);
+ ok(xmlString.includes("Error"), "Load opaque cross origin XSLT should not be allowed");
+ res();
+ };
+ });
+
+ registration.active.postMessage("opaque");
+ iframe.setAttribute('src', "xslt/test.xml");
+
+ return p;
+ }
+
+ function runTest() {
+ start()
+ .then(synthetic)
+ .then(opaque)
+ .then(cors)
+ .then(unregister)
+ .catch(function(e) {
+ ok(false, "Some test failed with error " + e);
+ }).then(SimpleTest.finish);
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ SpecialPowers.pushPrefEnv({"set": [
+ ["dom.serviceWorkers.exemptFromPerDomainMax", true],
+ ["dom.serviceWorkers.enabled", true],
+ ["dom.serviceWorkers.testing.enabled", true],
+ ]}, runTest);
+</script>
+</pre>
+</body>
+</html>
+
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html>
+<head>
+ <meta http-equiv="Content-type" content="text/html;charset=UTF-8">
+ <title>SW third party iframe test</title>
+
+ <script type="text/javascript;version=1.7">
+ function messageListener(event) {
+ let message = eval(event.data);
+
+ dump("got message " + JSON.stringify(message) + "\n");
+ if (message.source == "parent") {
+ document.getElementById("iframe2").src = message.href;
+ }
+ else if (message.source == "iframe") {
+ parent.postMessage(event.data, "*");
+ }
+ }
+ </script>
+
+</head>
+
+<body onload="window.addEventListener('message', messageListener, false);">
+ <iframe id="iframe2"></iframe>
+</body>
+
+</html>
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 @@
+<!DOCTYPE html>
+<script>
+ window.parent.postMessage({
+ source: "iframe",
+ status: "networkresponse"
+ }, "*");
+</script>
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 @@
+<!DOCTYPE html>
+<script>
+ function ok(v, msg) {
+ window.parent.postMessage({status: "ok", result: !!v, message: msg}, "*");
+ }
+
+ var isDone = false;
+ function done(reg) {
+ if (!isDone) {
+ ok(reg.waiting || reg.active,
+ "Either active or waiting worker should be available.");
+ window.parent.postMessage({status: "registrationdone"}, "*");
+ isDone = true;
+ }
+ }
+
+ navigator.serviceWorker.register("sw.js", {scope: "."})
+ .then(function(registration) {
+ if (registration.installing) {
+ registration.installing.onstatechange = function(e) {
+ done(registration);
+ };
+ } else {
+ done(registration);
+ }
+ });
+</script>
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 =
+ "<script>" +
+ "window.parent.postMessage({" +
+ "source: 'iframe', status: 'swresponse'" +
+ "}, '*');" +
+ "</script>";
+ 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 @@
+<!DOCTYPE html>
+<script>
+ navigator.serviceWorker.getRegistration(".").then(function(registration) {
+ if(!registration) {
+ return;
+ }
+ registration.unregister().then(() => {
+ window.parent.postMessage({status: "unregistrationdone"}, "*");
+ });
+ });
+</script>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 984048 - Test unregister</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+<script class="testbody" type="text/javascript">
+
+ if (!parent) {
+ info("unregister/index.html should not to be launched directly!");
+ }
+
+ parent.postMessage({ controlled: !!navigator.serviceWorker.controller }, "*");
+</script>
+</pre>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test worker::unregister</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<script type="text/javascript">
+
+ navigator.serviceWorker.onmessage = function(e) { parent.postMessage(e.data, "*"); }
+ navigator.serviceWorker.controller.postMessage("GO");
+
+</script>
+</pre>
+</body>
+</html>
+
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 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Bug 1131327 - Test ServiceWorkerRegistration.onupdatefound on ServiceWorker</title>
+</head>
+<body>
+<script>
+ navigator.serviceWorker.onmessage = function(e) {
+ dump("NSM iframe got message " + e.data + "\n");
+ window.parent.postMessage(e.data, "*");
+ };
+</script>
+</body>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test worker::update</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<script type="text/javascript">
+
+ navigator.serviceWorker.onmessage = function(e) { parent.postMessage(e.data, "*"); }
+ navigator.serviceWorker.ready.then(function() {
+ navigator.serviceWorker.controller.postMessage("GO");
+ });
+
+</script>
+</pre>
+</body>
+</html>
+
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 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="test.xsl"?>
+<result>
+ <Title>Example</Title>
+ <Error>Error</Error>
+</result>
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 = "<?xml version=\"1.0\"?> " +
+ "<xsl:stylesheet version=\"1.0\"" +
+ " xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">" +
+ " <xsl:template match=\"node()|@*\">" +
+ " <xsl:copy>" +
+ " <xsl:apply-templates select=\"node()|@*\"/>" +
+ " </xsl:copy>" +
+ " </xsl:template>" +
+ " <xsl:template match=\"Error\"/>" +
+ "</xsl:stylesheet>";
+
+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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test for DOM Worker Threads Suspending</title>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<pre id="test">
+<div id="output"></div>
+<script class="testbody" type="text/javascript">
+
+ var output = document.getElementById("output");
+
+ var worker;
+
+ function terminateWorker() {
+ if (worker) {
+ worker.postMessage("stop");
+ worker = null;
+ }
+ }
+
+ function startWorker(messageCallback, errorCallback) {
+ var lastData;
+ worker = new Worker("suspend_worker.js");
+
+ worker.onmessage = function(event) {
+ output.textContent = (lastData ? lastData + " -> " : "") + event.data;
+ lastData = event.data;
+ messageCallback(event.data);
+ };
+
+ worker.onerror = function(event) {
+ this.terminate();
+ errorCallback(event.message);
+ };
+ }
+
+</script>
+</pre>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<!--
+Tests of DOM Worker Threads
+-->
+<head>
+ <title>Test for DOM Worker Threads</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+ var worker = new Worker("nonexistent_worker.js");
+
+ worker.onmessage = function(event) {
+ ok(false, "Shouldn't ever get a message!");
+ SimpleTest.finish();
+ }
+
+ worker.onerror = function(event) {
+ is(event.target, worker);
+ is(event.message, 'NetworkError: Failed to load worker script at "nonexistent_worker.js"');
+ event.preventDefault();
+ SimpleTest.finish();
+ };
+
+ worker.postMessage("dummy");
+
+ SimpleTest.waitForExplicitFinish();
+
+</script>
+</pre>
+</body>
+</html>
+
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 @@
+<?xml version="1.0"?>
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<window title="Test for WorkerDebugger.initialize"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="test();">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
+ <script type="application/javascript" src="dom_worker_helper.js"/>
+
+ <script type="application/javascript">
+ <![CDATA[
+
+ const WORKER_URL = "WorkerDebugger.initialize_worker.js";
+ const CHILD_WORKER_URL = "WorkerDebugger.initialize_childWorker.js";
+ const DEBUGGER_URL = BASE_URL + "WorkerDebugger.initialize_debugger.js";
+
+ function test() {
+ Task.spawn(function* () {
+ SimpleTest.waitForExplicitFinish();
+
+ info("Create a worker that creates a child worker, wait for their " +
+ "debuggers to be registered, and initialize them.");
+ let promise = waitForMultiple([
+ waitForRegister(WORKER_URL, DEBUGGER_URL),
+ waitForRegister(CHILD_WORKER_URL, DEBUGGER_URL)
+ ]);
+ let worker = new Worker(WORKER_URL);
+ yield promise;
+
+ info("Check that the debuggers are initialized before the workers " +
+ "start running.");
+ yield waitForMultiple([
+ waitForWorkerMessage(worker, "debugger"),
+ waitForWorkerMessage(worker, "worker"),
+ waitForWorkerMessage(worker, "child:debugger"),
+ waitForWorkerMessage(worker, "child:worker")
+ ]);
+
+ SimpleTest.finish();
+ });
+ }
+
+ ]]>
+ </script>
+
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <p id="display"></p>
+ <div id="content" style="display:none;"></div>
+ <pre id="test"></pre>
+ </body>
+ <label id="test-result"/>
+</window>
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 @@
+<?xml version="1.0"?>
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<window title="Test for WorkerDebugger.postMessage"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="test();">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
+ <script type="application/javascript" src="dom_worker_helper.js"/>
+
+ <script type="application/javascript">
+ <![CDATA[
+
+ const WORKER_URL = "WorkerDebugger.postMessage_worker.js";
+ const CHILD_WORKER_URL = "WorkerDebugger.postMessage_childWorker.js";
+ const DEBUGGER_URL = BASE_URL + "WorkerDebugger.postMessage_debugger.js";
+
+ function test() {
+ Task.spawn(function* () {
+ SimpleTest.waitForExplicitFinish();
+
+ info("Create a worker that creates a child worker, wait for their " +
+ "debuggers to be registered, and initialize them.");
+ let promise = waitForMultiple([
+ waitForRegister(WORKER_URL, DEBUGGER_URL),
+ waitForRegister(CHILD_WORKER_URL, DEBUGGER_URL)
+ ]);
+ let worker = new Worker(WORKER_URL);
+ let [dbg, childDbg] = yield promise;
+
+ info("Send a request to the worker debugger. This should cause the " +
+ "the worker debugger to send a response.");
+ promise = waitForDebuggerMessage(dbg, "pong");
+ dbg.postMessage("ping");
+ yield promise;
+
+ info("Send a request to the child worker debugger. This should cause " +
+ "the child worker debugger to send a response.");
+ promise = waitForDebuggerMessage(childDbg, "pong");
+ childDbg.postMessage("ping");
+ yield promise;
+
+ SimpleTest.finish();
+ });
+ }
+
+ ]]>
+ </script>
+
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <p id="display"></p>
+ <div id="content" style="display:none;"></div>
+ <pre id="test"></pre>
+ </body>
+ <label id="test-result"/>
+</window>
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 @@
+<?xml version="1.0"?>
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<window title="Test for WorkerDebugger"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="test();">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
+ <script type="application/javascript" src="dom_worker_helper.js"/>
+
+ <script type="application/javascript">
+ <![CDATA[
+
+ const WORKER_URL = "WorkerDebugger_worker.js";
+ const CHILD_WORKER_URL = "WorkerDebugger_childWorker.js";
+ const SHARED_WORKER_URL = "WorkerDebugger_sharedWorker.js";
+
+ function test() {
+ Task.spawn(function* () {
+ SimpleTest.waitForExplicitFinish();
+
+ info("Create a top-level chrome worker that creates a non-top-level " +
+ "content worker and wait for their debuggers to be registered.");
+ let promise = waitForMultiple([
+ waitForRegister(WORKER_URL),
+ waitForRegister(CHILD_WORKER_URL)
+ ]);
+ worker = new ChromeWorker(WORKER_URL);
+ let [dbg, childDbg] = yield promise;
+
+ info("Check that the top-level chrome worker debugger has the " +
+ "correct properties.");
+ is(dbg.isChrome, true,
+ "Chrome worker debugger should be chrome.");
+ is(dbg.parent, null,
+ "Top-level debugger should not have parent.");
+ is(dbg.type, Ci.nsIWorkerDebugger.TYPE_DEDICATED,
+ "Chrome worker debugger should be dedicated.");
+ is(dbg.window, window,
+ "Top-level dedicated worker debugger should have window.");
+
+ info("Check that the non-top-level content worker debugger has the " +
+ "correct properties.");
+ is(childDbg.isChrome, false,
+ "Content worker debugger should be content.");
+ is(childDbg.parent, dbg,
+ "Non-top-level worker debugger should have parent.");
+ is(childDbg.type, Ci.nsIWorkerDebugger.TYPE_DEDICATED,
+ "Content worker debugger should be dedicated.");
+ is(childDbg.window, null,
+ "Non-top-level worker debugger should not have window.");
+
+ info("Terminate the top-level chrome worker and the non-top-level " +
+ "content worker, and wait for their debuggers to be " +
+ "unregistered and closed.");
+ promise = waitForMultiple([
+ waitForUnregister(CHILD_WORKER_URL),
+ waitForDebuggerClose(childDbg),
+ waitForUnregister(WORKER_URL),
+ waitForDebuggerClose(dbg),
+ ]);
+ worker.terminate();
+ yield promise;
+
+ info("Create a shared worker and wait for its debugger to be " +
+ "registered");
+ promise = waitForRegister(SHARED_WORKER_URL);
+ worker = new SharedWorker(SHARED_WORKER_URL);
+ let sharedDbg = yield promise;
+
+ info("Check that the shared worker debugger has the correct " +
+ "properties.");
+ is(sharedDbg.isChrome, false,
+ "Shared worker debugger should be content.");
+ is(sharedDbg.parent, null,
+ "Shared worker debugger should not have parent.");
+ is(sharedDbg.type, Ci.nsIWorkerDebugger.TYPE_SHARED,
+ "Shared worker debugger should be shared.");
+ is(sharedDbg.window, null,
+ "Shared worker debugger should not have window.");
+
+ info("Create a shared worker with the same URL and check that its " +
+ "debugger is not registered again.");
+ let listener = {
+ onRegistered: function () {
+ ok(false,
+ "Shared worker debugger should not be registered again.");
+ },
+ };
+ wdm.addListener(listener);
+ worker = new SharedWorker(SHARED_WORKER_URL);
+
+ info("Send a message to the shared worker to tell it to close " +
+ "itself, and wait for its debugger to be closed.");
+ promise = waitForMultiple([
+ waitForUnregister(SHARED_WORKER_URL),
+ waitForDebuggerClose(sharedDbg)
+ ]);
+ worker.port.start();
+ worker.port.postMessage("close");
+ yield promise;
+
+ wdm.removeListener(listener);
+ SimpleTest.finish();
+ });
+ }
+
+ ]]>
+ </script>
+
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <p id="display"></p>
+ <div id="content" style="display:none;"></div>
+ <pre id="test"></pre>
+ </body>
+ <label id="test-result"/>
+</window>
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 @@
+<?xml version="1.0"?>
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<window title="Test for WorkerDebuggerGlobalScope.createSandbox"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="test();">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
+ <script type="application/javascript" src="dom_worker_helper.js"/>
+
+ <script type="application/javascript">
+ <![CDATA[
+
+ const WORKER_URL = "WorkerDebuggerGlobalScope.createSandbox_worker.js";
+ const DEBUGGER_URL = BASE_URL + "WorkerDebuggerGlobalScope.createSandbox_debugger.js";
+
+ function test() {
+ Task.spawn(function* () {
+ SimpleTest.waitForExplicitFinish();
+
+ info("Create a worker, wait for its debugger to be registered, and " +
+ "initialize it.");
+ let promise = waitForRegister(WORKER_URL, DEBUGGER_URL);
+ let worker = new Worker(WORKER_URL);
+ let dbg = yield promise;
+
+ info("Send a request to the worker debugger. This should cause the " +
+ "worker debugger to send a response from within a sandbox.");
+ promise = waitForDebuggerMessage(dbg, "pong");
+ dbg.postMessage("ping");
+ yield promise;
+
+ SimpleTest.finish();
+ });
+ }
+
+ ]]>
+ </script>
+
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <p id="display"></p>
+ <div id="content" style="display:none;"></div>
+ <pre id="test"></pre>
+ </body>
+ <label id="test-result"/>
+</window>
+
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 @@
+<?xml version="1.0"?>
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<window title="Test for WorkerDebuggerGlobalScope.enterEventLoop"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="test();">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
+ <script type="application/javascript" src="dom_worker_helper.js"/>
+
+ <script type="application/javascript">
+ <![CDATA[
+
+ const WORKER_URL = "WorkerDebuggerGlobalScope.enterEventLoop_worker.js";
+ const CHILD_WORKER_URL = "WorkerDebuggerGlobalScope.enterEventLoop_childWorker.js";
+ const DEBUGGER_URL = BASE_URL + "WorkerDebuggerGlobalScope.enterEventLoop_debugger.js";
+
+ function test() {
+ Task.spawn(function* () {
+ SimpleTest.waitForExplicitFinish();
+
+ info("Create a worker that creates a child worker, wait for their " +
+ "debuggers to be registered, and initialize them.");
+ let promise = waitForMultiple([
+ waitForRegister(WORKER_URL, DEBUGGER_URL),
+ waitForRegister(CHILD_WORKER_URL, DEBUGGER_URL)
+ ]);
+ let worker = new Worker(WORKER_URL);
+ let [dbg, childDbg] = yield promise;
+
+ info("Send a request to the child worker. This should cause the " +
+ "child worker debugger to enter a nested event loop.");
+ promise = waitForDebuggerMessage(childDbg, "paused");
+ worker.postMessage("child:ping");
+ yield promise;
+
+ info("Send a request to the child worker debugger. This should cause " +
+ "the child worker debugger to enter a second nested event loop.");
+ promise = waitForDebuggerMessage(childDbg, "paused");
+ childDbg.postMessage("eval");
+ yield promise;
+
+ info("Send a request to the child worker debugger. This should cause " +
+ "the child worker debugger to leave its second nested event " +
+ "loop. The child worker debugger should not send a response " +
+ "for its previous request until after it has left the nested " +
+ "event loop.");
+ promise = waitForMultiple([
+ waitForDebuggerMessage(childDbg, "resumed"),
+ waitForDebuggerMessage(childDbg, "evalled")
+ ]);
+ childDbg.postMessage("resume");
+ yield promise;
+
+ info("Send a request to the child worker debugger. This should cause " +
+ "the child worker debugger to leave its first nested event loop." +
+ "The child worker should not send a response for its earlier " +
+ "request until after the child worker debugger has left the " +
+ "nested event loop.");
+ promise = waitForMultiple([
+ waitForDebuggerMessage(childDbg, "resumed"),
+ waitForWorkerMessage(worker, "child:pong")
+ ]);
+ childDbg.postMessage("resume");
+ yield promise;
+
+ info("Send a request to the worker. This should cause the worker " +
+ "debugger to enter a nested event loop.");
+ promise = waitForDebuggerMessage(dbg, "paused");
+ worker.postMessage("ping");
+ yield promise;
+
+ info("Terminate the worker. This should not cause the worker " +
+ "debugger to terminate as well.");
+ worker.terminate();
+
+ worker.onmessage = function () {
+ ok(false, "Worker should have been terminated.");
+ };
+
+ info("Send a request to the worker debugger. This should cause the " +
+ "worker debugger to enter a second nested event loop.");
+ promise = waitForDebuggerMessage(dbg, "paused");
+ dbg.postMessage("eval");
+ yield promise;
+
+ info("Send a request to the worker debugger. This should cause the " +
+ "worker debugger to leave its second nested event loop. The " +
+ "worker debugger should not send a response for the previous " +
+ "request until after leaving the nested event loop.");
+ promise = waitForMultiple([
+ waitForDebuggerMessage(dbg, "resumed"),
+ waitForDebuggerMessage(dbg, "evalled")
+ ]);
+ dbg.postMessage("resume");
+ yield promise;
+
+ info("Send a request to the worker debugger. This should cause the " +
+ "worker debugger to leave its first nested event loop. The " +
+ "worker should not send a response for its earlier request, " +
+ "since it has been terminated.");
+ promise = waitForMultiple([
+ waitForDebuggerMessage(dbg, "resumed"),
+ ]);
+ dbg.postMessage("resume");
+ yield promise;
+
+ SimpleTest.finish();
+ });
+ }
+
+ ]]>
+ </script>
+
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <p id="display"></p>
+ <div id="content" style="display:none;"></div>
+ <pre id="test"></pre>
+ </body>
+ <label id="test-result"/>
+</window>
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 @@
+<?xml version="1.0"?>
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<window title="Test for WorkerDebuggerGlobalScope.reportError"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="test();">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
+ <script type="application/javascript" src="dom_worker_helper.js"/>
+
+ <script type="application/javascript">
+ <![CDATA[
+
+ const WORKER_URL = "WorkerDebuggerGlobalScope.reportError_worker.js";
+ const CHILD_WORKER_URL = "WorkerDebuggerGlobalScope.reportError_childWorker.js";
+ const DEBUGGER_URL = BASE_URL + "WorkerDebuggerGlobalScope.reportError_debugger.js";
+
+ function test() {
+ Task.spawn(function* () {
+ SimpleTest.waitForExplicitFinish();
+
+ info("Create a worker that creates a child worker, wait for their " +
+ "debuggers to be registered, and initialize them.");
+ let promise = waitForMultiple([
+ waitForRegister(WORKER_URL, DEBUGGER_URL),
+ waitForRegister(CHILD_WORKER_URL, DEBUGGER_URL)
+ ]);
+ let worker = new Worker(WORKER_URL);
+ let [dbg, childDbg] = yield promise;
+
+ worker.onmessage = function () {
+ ok(false, "Debugger error events should not be fired at workers.");
+ };
+
+ info("Send a request to the worker debugger. This should cause the " +
+ "worker debugger to report an error.");
+ promise = waitForDebuggerError(dbg);
+ dbg.postMessage("report");
+ let error = yield promise;
+ is(error.fileName, DEBUGGER_URL,
+ "fileName should be name of file from which error is reported.");
+ is(error.lineNumber, 6,
+ "lineNumber should be line number from which error is reported.");
+ is(error.message, "reported", "message should be reported.");
+
+ info("Send a request to the worker debugger. This should cause the " +
+ "worker debugger to throw an error.");
+ promise = waitForDebuggerError(dbg);
+ dbg.postMessage("throw");
+ error = yield promise;
+ is(error.fileName, DEBUGGER_URL,
+ "fileName should be name of file from which error is thrown");
+ is(error.lineNumber, 9,
+ "lineNumber should be line number from which error is thrown");
+ is(error.message, "Error: thrown", "message should be Error: thrown");
+
+ info("Send a reqeust to the child worker debugger. This should cause " +
+ "the child worker debugger to report an error.");
+ promise = waitForDebuggerError(childDbg);
+ childDbg.postMessage("report");
+ error = yield promise;
+ is(error.fileName, DEBUGGER_URL,
+ "fileName should be name of file from which error is reported.");
+ is(error.lineNumber, 6,
+ "lineNumber should be line number from which error is reported.");
+ is(error.message, "reported", "message should be reported.");
+
+ info("Send a message to the child worker debugger. This should cause " +
+ "the child worker debugger to throw an error.");
+ promise = waitForDebuggerError(childDbg);
+ childDbg.postMessage("throw");
+ error = yield promise;
+ is(error.fileName, DEBUGGER_URL,
+ "fileName should be name of file from which error is thrown");
+ is(error.lineNumber, 9,
+ "lineNumber should be line number from which error is thrown");
+ is(error.message, "Error: thrown", "message should be Error: thrown");
+
+ SimpleTest.finish();
+ });
+ }
+
+ ]]>
+ </script>
+
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <p id="display"></p>
+ <div id="content" style="display:none;"></div>
+ <pre id="test"></pre>
+ </body>
+ <label id="test-result"/>
+</window>
+
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 @@
+<?xml version="1.0"?>
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<window title="Test for WorkerDebuggerGlobalScope.setImmediate"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="test();">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
+ <script type="application/javascript" src="dom_worker_helper.js"/>
+
+ <script type="application/javascript">
+ <![CDATA[
+
+ const WORKER_URL = "WorkerDebuggerGlobalScope.setImmediate_worker.js";
+ const DEBUGGER_URL = BASE_URL + "WorkerDebuggerGlobalScope.setImmediate_debugger.js";
+
+ function test() {
+ Task.spawn(function* () {
+ SimpleTest.waitForExplicitFinish();
+
+ let promise = waitForRegister(WORKER_URL, DEBUGGER_URL);
+ let worker = new Worker(WORKER_URL);
+ let dbg = yield promise;
+
+ info("Send a request to the worker debugger. This should cause a " +
+ "the worker debugger to send two responses. The worker debugger " +
+ "should send the second response before the first one, since " +
+ "the latter is delayed until the next tick of the event loop.");
+ promise = waitForMultiple([
+ waitForDebuggerMessage(dbg, "pong2"),
+ waitForDebuggerMessage(dbg, "pong1")
+ ]);
+ dbg.postMessage("ping");
+ yield promise;
+
+ SimpleTest.finish();
+ });
+ }
+
+ ]]>
+ </script>
+
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <p id="display"></p>
+ <div id="content" style="display:none;"></div>
+ <pre id="test"></pre>
+ </body>
+ <label id="test-result"/>
+</window>
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 @@
+<?xml version="1.0"?>
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<window title="Test for WorkerDebuggerManager"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="test();">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
+ <script type="application/javascript" src="dom_worker_helper.js"/>
+
+ <script type="application/javascript">
+ <![CDATA[
+
+ const WORKER_URL = "WorkerDebuggerManager_worker.js";
+ const CHILD_WORKER_URL = "WorkerDebuggerManager_childWorker.js";
+
+ function test() {
+ Task.spawn(function* () {
+ SimpleTest.waitForExplicitFinish();
+
+ info("Check that worker debuggers are not enumerated before they are " +
+ "registered.");
+ ok(!findDebugger(WORKER_URL),
+ "Worker debugger should not be enumerated before it is registered.");
+ ok(!findDebugger(CHILD_WORKER_URL),
+ "Child worker debugger should not be enumerated before it is " +
+ "registered.");
+
+ info("Create a worker that creates a child worker, and wait for " +
+ "their debuggers to be registered.");
+ let promise = waitForMultiple([
+ waitForRegister(WORKER_URL),
+ waitForRegister(CHILD_WORKER_URL)
+ ]);
+ let worker = new Worker(WORKER_URL);
+ let [dbg, childDbg] = yield promise;
+
+ info("Check that worker debuggers are enumerated after they are " +
+ "registered.");
+ ok(findDebugger(WORKER_URL),
+ "Worker debugger should be enumerated after it is registered.");
+ ok(findDebugger(CHILD_WORKER_URL),
+ "Child worker debugger should be enumerated after it is " +
+ "registered.");
+
+ info("Check that worker debuggers are not closed before they are " +
+ "unregistered.");
+ is(dbg.isClosed, false,
+ "Worker debugger should not be closed before it is unregistered.");
+ is(childDbg.isClosed, false,
+ "Child worker debugger should not be closed before it is " +
+ "unregistered");
+
+ info("Terminate the worker and the child worker, and wait for their " +
+ "debuggers to be unregistered.");
+ promise = waitForMultiple([
+ waitForUnregister(CHILD_WORKER_URL),
+ waitForUnregister(WORKER_URL),
+ ]);
+ worker.terminate();
+ yield promise;
+
+ info("Check that worker debuggers are not enumerated after they are " +
+ "unregistered.");
+ ok(!findDebugger(WORKER_URL),
+ "Worker debugger should not be enumerated after it is " +
+ "unregistered.");
+ ok(!findDebugger(CHILD_WORKER_URL),
+ "Child worker debugger should not be enumerated after it is " +
+ "unregistered.");
+
+ info("Check that worker debuggers are closed after they are " +
+ "unregistered.");
+ is(dbg.isClosed, true,
+ "Worker debugger should be closed after it is unregistered.");
+ is(childDbg.isClosed, true,
+ "Child worker debugger should be closed after it is unregistered.");
+
+ info("Check that property accesses on worker debuggers throws " +
+ "after they are closed.");
+ assertThrows(() => dbg.url,
+ "Property accesses on worker debugger should throw " +
+ "after it is closed.");
+ assertThrows(() => childDbg.url,
+ "Property accesses on child worker debugger should " +
+ "throw after it is closed.");
+
+ SimpleTest.finish();
+ });
+ }
+
+ ]]>
+ </script>
+
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <p id="display"></p>
+ <div id="content" style="display:none;"></div>
+ <pre id="test"></pre>
+ </body>
+ <label id="test-result"/>
+</window>
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 @@
+<?xml version="1.0"?>
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<window title="Test for WorkerDebuggerGlobalScope.console methods"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="test();">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
+ <script type="application/javascript" src="dom_worker_helper.js"/>
+
+ <script type="application/javascript">
+ <![CDATA[
+
+ const WORKER_URL = "WorkerDebugger.console_worker.js";
+ const CHILD_WORKER_URL = "WorkerDebugger.console_childWorker.js";
+ const DEBUGGER_URL = BASE_URL + "WorkerDebugger.console_debugger.js";
+
+ consoleMessagesReceived = 0;
+ function test() {
+ function consoleListener() {
+ SpecialPowers.addObserver(this, "console-api-log-event", false);
+ }
+
+ consoleListener.prototype = {
+ observe: function(aSubject, aTopic, aData) {
+ if (aTopic == "console-api-log-event") {
+ var obj = aSubject.wrappedJSObject;
+ if (obj.arguments[0] == "Hello from the debugger script!" &&
+ !consoleMessagesReceived) {
+ consoleMessagesReceived++;
+ ok(true, "Something has been received");
+ SpecialPowers.removeObserver(this, "console-api-log-event");
+ }
+ }
+ }
+ }
+
+ var cl = new consoleListener();
+
+ Task.spawn(function* () {
+ SimpleTest.waitForExplicitFinish();
+
+ info("Create a worker that creates a child worker, wait for their " +
+ "debuggers to be registered, and initialize them.");
+ let promise = waitForMultiple([
+ waitForRegister(WORKER_URL, DEBUGGER_URL),
+ waitForRegister(CHILD_WORKER_URL, DEBUGGER_URL)
+ ]);
+ let worker = new Worker(WORKER_URL);
+ let [dbg, childDbg] = yield promise;
+
+ info("Send a request to the worker debugger. This should cause the " +
+ "the worker debugger to send a response.");
+ dbg.addListener({
+ onMessage: function(msg) {
+ try {
+ msg = JSON.parse(msg);
+ } catch(e) {
+ ok(false, "Something went wrong");
+ return;
+ }
+
+ if (msg.type == 'finish') {
+ ok(consoleMessagesReceived, "We received something via debugger console!");
+ dbg.removeListener(this);
+ SimpleTest.finish();
+ return;
+ }
+
+ if (msg.type == 'status') {
+ ok(msg.what, msg.msg);
+ return;
+ }
+
+ ok(false, "Something went wrong");
+ }
+ });
+
+ dbg.postMessage("do magic");
+ });
+ }
+
+ ]]>
+ </script>
+
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <p id="display"></p>
+ <div id="content" style="display:none;"></div>
+ <pre id="test"></pre>
+ </body>
+ <label id="test-result"/>
+</window>
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 @@
+<?xml version="1.0"?>
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<window title="Test for WorkerDebugger with frozen workers"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="test();">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
+ <script type="application/javascript" src="dom_worker_helper.js"/>
+
+ <script type="application/javascript">
+ <![CDATA[
+
+ const CACHE_SUBFRAMES = "browser.sessionhistory.cache_subframes";
+ const MAX_TOTAL_VIEWERS = "browser.sessionhistory.max_total_viewers";
+
+ const IFRAME1_URL = "WorkerDebugger_frozen_iframe1.html";
+ const IFRAME2_URL = "WorkerDebugger_frozen_iframe2.html";
+
+ const WORKER1_URL = "WorkerDebugger_frozen_worker1.js";
+ const WORKER2_URL = "WorkerDebugger_frozen_worker2.js";
+
+ function test() {
+ Task.spawn(function* () {
+ SimpleTest.waitForExplicitFinish();
+
+ var oldMaxTotalViewers = SpecialPowers.getIntPref(MAX_TOTAL_VIEWERS);
+
+ SpecialPowers.setBoolPref(CACHE_SUBFRAMES, true);
+ SpecialPowers.setIntPref(MAX_TOTAL_VIEWERS, 10);
+
+ let iframe = $("iframe");
+
+ let promise = waitForMultiple([
+ waitForRegister(WORKER1_URL),
+ waitForWindowMessage(window, "ready"),
+ ]);
+ iframe.src = IFRAME1_URL;
+ let [dbg1] = yield promise;
+ is(dbg1.isClosed, false,
+ "debugger for worker on page 1 should not be closed");
+
+ promise = waitForMultiple([
+ waitForUnregister(WORKER1_URL),
+ waitForDebuggerClose(dbg1),
+ waitForRegister(WORKER2_URL),
+ waitForWindowMessage(window, "ready"),
+ ]);
+ iframe.src = IFRAME2_URL;
+ let [,, dbg2] = yield promise;
+ is(dbg1.isClosed, true,
+ "debugger for worker on page 1 should be closed");
+ is(dbg2.isClosed, false,
+ "debugger for worker on page 2 should not be closed");
+
+ promise = Promise.all([
+ waitForUnregister(WORKER2_URL),
+ waitForDebuggerClose(dbg2),
+ waitForRegister(WORKER1_URL)
+ ]);
+ iframe.contentWindow.history.back();
+ [,, dbg1] = yield promise;
+ is(dbg1.isClosed, false,
+ "debugger for worker on page 1 should not be closed");
+ is(dbg2.isClosed, true,
+ "debugger for worker on page 2 should be closed");
+
+ SpecialPowers.clearUserPref(CACHE_SUBFRAMES);
+ SpecialPowers.setIntPref(MAX_TOTAL_VIEWERS, oldMaxTotalViewers);
+
+ SimpleTest.finish();
+ });
+ }
+
+ ]]>
+ </script>
+
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <p id="display"></p>
+ <div id="content" style="display:none;"></div>
+ <pre id="test"></pre>
+ <iframe id="iframe"></iframe>
+ </body>
+ <label id="test-result"/>
+</window>
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 @@
+<?xml version="1.0"?>
+<!--
+ Any copyright is dedicated to the Public Domain.
++ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<window title="Test for WorkerDebugger with DOM Promises"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="test();">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
+ <script type="application/javascript" src="dom_worker_helper.js"/>
+
+ <script type="application/javascript">
+ <![CDATA[
+
+ const WORKER_URL = "WorkerDebugger_promise_worker.js";
+ const DEBUGGER_URL = BASE_URL + "WorkerDebugger_promise_debugger.js";
+
+ function test() {
+ Task.spawn(function* () {
+ SimpleTest.waitForExplicitFinish();
+
+ let promise = waitForRegister(WORKER_URL, DEBUGGER_URL);
+ let worker = new Worker(WORKER_URL);
+ let dbg = yield promise;
+
+ info("Send a request to the worker. This should cause the worker " +
+ "to send a response.");
+ promise = waitForWorkerMessage(worker, "resolved");
+ worker.postMessage("resolve");
+ yield promise;
+
+ info("Send a request to the debugger. This should cause the debugger " +
+ "to send a response.");
+ promise = waitForDebuggerMessage(dbg, "resolved");
+ dbg.postMessage("resolve");
+ yield promise;
+
+ info("Send a request to the worker. This should cause the debugger " +
+ "to enter a nested event loop.");
+ promise = waitForDebuggerMessage(dbg, "paused");
+ worker.postMessage("pause");
+ yield promise;
+
+ info("Send a request to the debugger. This should cause the debugger " +
+ "to leave the nested event loop.");
+ promise = waitForMultiple([
+ waitForDebuggerMessage(dbg, "resumed"),
+ waitForWorkerMessage(worker, "resumed")
+ ]);
+ dbg.postMessage("resume");
+ yield promise;
+
+ SimpleTest.finish();
+ });
+ }
+
+ ]]>
+ </script>
+
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <p id="display"></p>
+ <div id="content" style="display:none;"></div>
+ <pre id="test"></pre>
+ </body>
+ <label id="test-result"/>
+</window>
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 @@
+<?xml version="1.0"?>
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<window title="Test for WorkerDebugger with suspended workers"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="test();">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
+ <script type="application/javascript" src="dom_worker_helper.js"/>
+
+ <script type="application/javascript">
+ <![CDATA[
+
+ const WORKER_URL = "WorkerDebugger_suspended_worker.js";
+ const DEBUGGER_URL = BASE_URL + "WorkerDebugger_suspended_debugger.js";
+
+ function test() {
+ Task.spawn(function* () {
+ SimpleTest.waitForExplicitFinish();
+
+ info("Create a worker, wait for its debugger to be registered, and " +
+ "initialize it.");
+ let promise = waitForRegister(WORKER_URL, DEBUGGER_URL);
+ let worker = new Worker(WORKER_URL);
+ let dbg = yield promise;
+
+ info("Send a request to the worker. This should cause both the " +
+ "worker and the worker debugger to send a response.");
+ promise = waitForMultiple([
+ waitForWorkerMessage(worker, "worker"),
+ waitForDebuggerMessage(dbg, "debugger")
+ ]);
+ worker.postMessage("ping");
+ yield promise;
+
+ info("Suspend the workers for this window, and send another request " +
+ "to the worker. This should cause only the worker debugger to " +
+ "send a response.");
+ let windowUtils = window.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIDOMWindowUtils);
+ windowUtils.suspendTimeouts();
+ function onmessage() {
+ ok(false, "The worker should not send a response.");
+ };
+ worker.addEventListener("message", onmessage);
+ promise = waitForDebuggerMessage(dbg, "debugger");
+ worker.postMessage("ping");
+ yield promise;
+ worker.removeEventListener("message", onmessage);
+
+ info("Resume the workers for this window. This should cause the " +
+ "worker to send a response to the previous request.");
+ promise = waitForWorkerMessage(worker, "worker");
+ windowUtils.resumeTimeouts();
+ yield promise;
+
+ SimpleTest.finish();
+ });
+ }
+
+ ]]>
+ </script>
+
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <p id="display"></p>
+ <div id="content" style="display:none;"></div>
+ <pre id="test"></pre>
+ </body>
+ <label id="test-result"/>
+</window>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for DOM Worker Threads</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test">
+<script src="atob_worker.js" language="javascript"></script>
+<script class="testbody" type="text/javascript">
+
+ var dataIndex = 0;
+
+ var worker = new Worker("atob_worker.js");
+ worker.onmessage = function(event) {
+ switch (event.data.type) {
+ case "done":
+ is(dataIndex, data.length, "Saw all values");
+ SimpleTest.finish();
+ return;
+ case "btoa":
+ is(btoa(data[dataIndex]), event.data.value,
+ "Good btoa value " + dataIndex);
+ break;
+ case "atob":
+ is(atob(btoa(data[dataIndex])) + "", event.data.value,
+ "Good round trip value " + dataIndex);
+ dataIndex++;
+ break;
+ default:
+ ok(false, "Worker posted a bad message: " + event.message);
+ worker.terminate();
+ SimpleTest.finish();
+ }
+ }
+
+ worker.onerror = function(event) {
+ ok(false, "Worker threw an error: " + event.message);
+ worker.terminate();
+ SimpleTest.finish();
+ }
+
+ worker.postMessage("go");
+
+ SimpleTest.waitForExplicitFinish();
+
+</script>
+</pre>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE html>
+<html>
+<!--
+Tests of DOM Worker Blob constructor
+-->
+<head>
+ <title>Test for DOM Worker Blob constructor</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+(function() {
+ onerror = function(e) {
+ ok(false, "Main Thread had an error: " + event.data);
+ SimpleTest.finish();
+ };
+ function f() {
+ onmessage = function(e) {
+ var b = new Blob([e.data, "World"],{type: "text/plain"});
+ var fr = new FileReaderSync();
+ postMessage({text: fr.readAsText(b), type: b.type});
+ };
+ }
+ var b = new Blob([f,"f();"]);
+ var u = URL.createObjectURL(b);
+ var w = new Worker(u);
+ w.onmessage = function(e) {
+ URL.revokeObjectURL(u);
+ is(e.data.text, fr.result);
+ is(e.data.type, "text/plain");
+ SimpleTest.finish();
+ };
+ w.onerror = function(e) {
+ is(e.target, w);
+ ok(false, "Worker had an error: " + e.message);
+ SimpleTest.finish();
+ };
+
+ b = new Blob(["Hello, "]);
+ var fr = new FileReader();
+ fr.readAsText(new Blob([b, "World"],{}));
+ fr.onload = function() {
+ w.postMessage(b);
+ };
+ SimpleTest.waitForExplicitFinish();
+})();
+</script>
+</pre>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js">
+ </script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ </head>
+ <body>
+ <script type="text/javascript">
+ const message = "hi";
+
+ const workerScript =
+ "onmessage = function(event) {" +
+ " postMessage(event.data);" +
+ "};";
+
+ var worker = new Worker(URL.createObjectURL(new Blob([workerScript])));
+ worker.onmessage = function(event) {
+ is(event.data, message, "Got correct message");
+ SimpleTest.finish();
+ };
+ worker.postMessage(message);
+
+ SimpleTest.waitForExplicitFinish();
+ </script>
+ </body>
+</html>
+
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for bug 1002702</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+<script class="testbody" type="text/javascript">
+
+var port = new SharedWorker('data:application/javascript,1').port;
+port.close();
+SpecialPowers.forceGC();
+ok(true, "No crash \\o/");
+
+</script>
+</pre>
+</body>
+</html>
+
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 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1010784
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1010784</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1010784">Mozilla Bug 1010784</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+ var worker = new Worker("file_bug1010784_worker.js");
+
+ worker.onmessage = function(event) {
+ is(event.data, "done", "Got correct result");
+ SimpleTest.finish();
+ }
+
+ worker.postMessage("testXHR.txt");
+
+ SimpleTest.waitForExplicitFinish();
+
+</script>
+</pre>
+</body>
+</html>
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 @@
+<!--
+2 Any copyright is dedicated to the Public Domain.
+3 http://creativecommons.org/publicdomain/zero/1.0/
+4 -->
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1014466
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1014466</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1014466">Mozilla Bug 1014466</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+ var worker = new Worker("bug1014466_worker.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);
+ }
+ };
+
+ worker.postMessage(true);
+
+ SimpleTest.waitForExplicitFinish();
+
+</script>
+</pre>
+</body>
+</html>
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 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1020226
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1020226</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1020226">Mozilla Bug 1020226</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+<iframe id="iframe" src="bug1020226_frame.html" onload="finishTest();">
+</iframe>
+</div>
+<pre id="test">
+<script type="application/javascript">
+function finishTest() {
+ document.getElementById("iframe").onload = null;
+ window.onmessage = function(e) {
+ info("Got message");
+ document.getElementById("iframe").src = "about:blank";
+ // We aren't really interested in the test, it shouldn't crash when the
+ // worker is GCed later.
+ ok(true, "Should not crash");
+ SimpleTest.finish();
+ };
+}
+
+SimpleTest.waitForExplicitFinish();
+</script>
+</pre>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<!--
+Tests of DOM Worker Threads: bug 1036484
+-->
+<head>
+ <title>Test for DOM Worker Threads: bug 1036484</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+function test(script) {
+ var worker = new Worker(script);
+
+ worker.onmessage = function(event) {
+ ok(false, "Shouldn't ever get a message!");
+ }
+
+ worker.onerror = function(event) {
+ is(event.target, worker);
+ ok(event.message.startsWith("NetworkError: Failed to load worker script"))
+ event.preventDefault();
+ runTests();
+ };
+
+ worker.postMessage("dummy");
+}
+
+var tests = [ '404_server.sjs', '404_server.sjs?js' ];
+function runTests() {
+ if (!tests.length) {
+ SimpleTest.finish();
+ return;
+ }
+
+ var script = tests.shift();
+ test(script);
+}
+
+SimpleTest.waitForExplicitFinish();
+runTests();
+
+</script>
+</pre>
+</body>
+</html>
+
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for URLSearchParams object in workers</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+<script class="testbody" type="text/javascript">
+
+ var worker = new Worker("bug1060621_worker.js");
+
+ worker.onmessage = function(event) {
+ ok(true, "The operation is done. We should not leak.");
+ SimpleTest.finish();
+ };
+
+ SimpleTest.waitForExplicitFinish();
+
+</script>
+</pre>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for navigator property override</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+<script class="testbody" type="text/javascript">
+
+ function checkValues() {
+ var worker = new Worker("bug1062920_worker.js");
+
+ worker.onmessage = function(event) {
+ var ifr = document.createElement('IFRAME');
+ ifr.src = "about:blank";
+
+ ifr.addEventListener('load', function() {
+ var nav = ifr.contentWindow.navigator;
+ is(event.data.appCodeName, nav.appCodeName, "appCodeName should match");
+ is(event.data.appName, nav.appName, "appName should match");
+ is(event.data.appVersion, nav.appVersion, "appVersion should match");
+ is(event.data.platform, nav.platform, "platform should match");
+ is(event.data.userAgent, nav.userAgent, "userAgent should match");
+ is(event.data.product, nav.product, "product should match");
+ runTests();
+ }, false);
+
+ document.getElementById('content').appendChild(ifr);
+ };
+ }
+
+ function replaceAndCheckValues() {
+ SpecialPowers.pushPrefEnv({"set": [
+ ["general.appname.override", "appName overridden"],
+ ["general.appversion.override", "appVersion overridden"],
+ ["general.platform.override", "platform overridden"],
+ ["general.useragent.override", "userAgent overridden"]
+ ]}, checkValues);
+ }
+
+ var tests = [
+ checkValues,
+ replaceAndCheckValues
+ ];
+
+ function runTests() {
+ if (tests.length == 0) {
+ SimpleTest.finish();
+ return;
+ }
+
+ var test = tests.shift();
+ test();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ runTests();
+
+</script>
+</pre>
+</body>
+</html>
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 @@
+<?xml version="1.0"?>
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<window title="DOM Worker Threads Test"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
+ <script type="application/javascript" src="dom_worker_helper.js"/>
+
+ <script type="application/javascript">
+
+ function checkValues() {
+ var worker = new Worker("bug1062920_worker.js");
+
+ worker.onmessage = function(event) {
+ is(event.data.appCodeName, navigator.appCodeName, "appCodeName should match");
+ is(event.data.appName, navigator.appName, "appName should match");
+ isnot(event.data.appName, "appName overridden", "appName is not overridden");
+ is(event.data.appVersion, navigator.appVersion, "appVersion should match");
+ isnot(event.data.appVersion, "appVersion overridden", "appVersion is not overridden");
+ is(event.data.platform, navigator.platform, "platform should match");
+ isnot(event.data.platform, "platform overridden", "platform is not overridden");
+ is(event.data.userAgent, navigator.userAgent, "userAgent should match");
+ is(event.data.product, navigator.product, "product should match");
+ runTests();
+ };
+ }
+
+ function replaceAndCheckValues() {
+ SpecialPowers.pushPrefEnv({"set": [
+ ["general.appname.override", "appName overridden"],
+ ["general.appversion.override", "appVersion overridden"],
+ ["general.platform.override", "platform overridden"],
+ ["general.useragent.override", "userAgent overridden"]
+ ]}, checkValues);
+ }
+
+ var tests = [
+ replaceAndCheckValues,
+ checkValues
+ ];
+
+ function runTests() {
+ if (tests.length == 0) {
+ SimpleTest.finish();
+ return;
+ }
+
+ var test = tests.shift();
+ test();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ runTests();
+
+ </script>
+
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <p id="display"></p>
+ <div id="content" style="display:none;"></div>
+ <pre id="test"></pre>
+ </body>
+ <label id="test-result"/>
+</window>
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 @@
+<!--
+2 Any copyright is dedicated to the Public Domain.
+3 http://creativecommons.org/publicdomain/zero/1.0/
+4 -->
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1063538
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1063538</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1063538">Mozilla Bug 1063538</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+function runTest() {
+ var worker = new Worker("bug1063538_worker.js");
+
+ worker.onmessage = function(e) {
+ if (e.data.type == 'finish') {
+ ok(e.data.progressFired, "Progress was fired.");
+ SimpleTest.finish();
+ }
+ };
+
+ worker.postMessage(true);
+}
+
+SimpleTest.waitForExplicitFinish();
+
+addLoadEvent(function() {
+ SpecialPowers.pushPrefEnv({"set": [["network.jar.block-remote-files", false]]}, function() {
+ SpecialPowers.pushPermissions([{'type': 'systemXHR', 'allow': true, 'context': document}], runTest);
+ });
+});
+
+</script>
+</pre>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for bug 1104064</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<script class="testbody" type="text/javascript">
+
+var worker = new Worker("bug1104064_worker.js");
+worker.onmessage = function() {
+ ok(true, "setInterval has been called twice.");
+ SimpleTest.finish();
+}
+worker.postMessage("go");
+
+SimpleTest.waitForExplicitFinish();
+
+</script>
+</pre>
+</body>
+</html>
+
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for 1132395</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+
+<script class="testbody" type="text/javascript">
+
+// This test is full of dummy debug messages. This is because I need to follow
+// an hard-to-reproduce timeout failure.
+
+info("test started");
+var sw = new SharedWorker('bug1132395_sharedWorker.js');
+sw.port.onmessage = function(event) {
+ info("sw.onmessage received");
+ ok(true, "We didn't crash.");
+ SimpleTest.finish();
+}
+
+sw.onerror = function(event) {
+ ok(false, "Failed to create a ServiceWorker");
+ SimpleTest.finish();
+}
+
+info("sw.postmessage called");
+sw.port.postMessage('go');
+
+SimpleTest.waitForExplicitFinish();
+
+</script>
+</pre>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for 1132924</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+
+<script class="testbody" type="text/javascript">
+
+SimpleTest.waitForExplicitFinish();
+var w = new Worker('bug1132924_worker.js');
+w.onmessage = function(event) {
+ ok(true, "We are still alive.");
+ SimpleTest.finish();
+}
+
+w.postMessage('go');
+
+</script>
+</pre>
+</body>
+</html>
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 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1278777
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1278777</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1278777">Mozilla Bug 1278777</a>
+ <script type="application/javascript">
+
+SimpleTest.waitForExplicitFinish();
+
+var worker = new Worker('worker_bug1278777.js');
+worker.onerror = function() {
+ ok(false, "We should not see any error.");
+ SimpleTest.finish();
+}
+
+worker.onmessage = function(e) {
+ ok(e.data, "Everything seems ok.");
+ SimpleTest.finish();
+}
+
+ </script>
+</body>
+</html>
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 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1301094
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1301094</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1301094">Mozilla Bug 1301094</a>
+ <input id="file" type="file"></input>
+ <script type="application/javascript">
+
+SimpleTest.waitForExplicitFinish();
+
+var url = SimpleTest.getTestFileURL("script_createFile.js");
+script = SpecialPowers.loadChromeScript(url);
+
+var mainThreadOk, workerOk;
+
+function maybeFinish() {
+ if (mainThreadOk & workerOk) {
+ SimpleTest.finish();
+ }
+}
+
+function onOpened(message) {
+ var input = document.getElementById('file');
+ SpecialPowers.wrap(input).mozSetDndFilesAndDirectories([message.data]);
+
+ var worker = new Worker('worker_bug1301094.js');
+ worker.onerror = function() {
+ ok(false, "We should not see any error.");
+ SimpleTest.finish();
+ }
+
+ worker.onmessage = function(e) {
+ ok(e.data, "Everything seems OK on the worker-side.");
+
+ workerOk = true;
+ maybeFinish();
+ }
+
+ is(input.files.length, 1, "We have something");
+ ok(input.files[0] instanceof Blob, "We have one Blob");
+ worker.postMessage(input.files[0]);
+
+ var xhr = new XMLHttpRequest();
+ xhr.open("POST", 'worker_bug1301094.js', false);
+ xhr.onload = function() {
+ ok(xhr.responseText, "Everything seems OK on the main-thread-side.");
+ mainThreadOk = true;
+ maybeFinish();
+ };
+
+ var fd = new FormData();
+ fd.append('file', input.files[0]);
+ xhr.send(fd);
+}
+
+script.addMessageListener("file.opened", onOpened);
+script.sendAsyncMessage("file.open");
+
+ </script>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for bug 1317725</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+
+<input type="file" id="file" />
+
+<script type="text/js-worker" id="worker-src">
+onmessage = function(e) {
+ var data = new FormData();
+ data.append('Filedata', e.data.slice(0, 127), encodeURI(e.data.name));
+ var xhr = new XMLHttpRequest();
+ xhr.open('POST', location.href, false);
+ xhr.send(data);
+ postMessage("No crash \\o/");
+}
+</script>
+
+<script class="testbody" type="text/javascript">
+
+SimpleTest.waitForExplicitFinish();
+
+var url = SimpleTest.getTestFileURL("script_createFile.js");
+script = SpecialPowers.loadChromeScript(url);
+
+function onOpened(message) {
+ var input = document.getElementById('file');
+ SpecialPowers.wrap(input).mozSetFileArray([message.data]);
+
+ var blob = new Blob([ document.getElementById("worker-src").textContent ],
+ { type: "text/javascript" });
+ var worker = new Worker(URL.createObjectURL(blob));
+ worker.onerror = function(e) {
+ ok(false, "We should not see any error.");
+ SimpleTest.finish();
+ }
+
+ worker.onmessage = function(e) {
+ ok(e.data, "Everything seems OK on the worker-side.");
+ SimpleTest.finish();
+ }
+
+ is(input.files.length, 1, "We have something");
+ ok(input.files[0] instanceof Blob, "We have one Blob");
+ worker.postMessage(input.files[0]);
+}
+
+script.addMessageListener("file.opened", onOpened);
+script.sendAsyncMessage("file.open");
+
+</script>
+</pre>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for bug 949946</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+<script class="testbody" type="text/javascript">
+
+new SharedWorker('sharedWorker_sharedWorker.js');
+new SharedWorker('sharedWorker_sharedWorker.js', ':');
+new SharedWorker('sharedWorker_sharedWorker.js', '|||');
+ok(true, "3 SharedWorkers created!");
+
+</script>
+</pre>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for DOM Worker Threads</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+ SimpleTest.waitForExplicitFinish();
+
+ var xhr = new XMLHttpRequest();
+ xhr.onload = function () {
+ var worker = new Worker("bug978260_worker.js");
+ worker.onmessage = function(event) {
+ is(event.data, "loaded");
+ SimpleTest.finish();
+ }
+ }
+
+ xhr.open('GET', '/', false);
+ xhr.send();
+
+</script>
+</pre>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for bug 998474</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body onload="boom();">
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+<script class="testbody" type="text/javascript">
+
+function boom()
+{
+ var worker = new SharedWorker("bug998474_worker.js");
+
+ setTimeout(function() {
+ port = worker.port;
+ port.postMessage("");
+
+ setTimeout(function() {
+ port.start();
+ ok(true, "Still alive!");
+ SimpleTest.finish();
+ }, 150);
+ }, 150);
+}
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.requestFlakyTimeout("untriaged");
+
+</script>
+</pre>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html>
+<head>
+ <title>Test for DOM Worker Threads</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+ try {
+ var worker = new ChromeWorker("simpleThread_worker.js");
+ ok(false, "ChromeWorker constructor should be blocked!");
+ }
+ catch (e) {
+ ok(true, "ChromeWorker constructor wasn't blocked!");
+ }
+
+</script>
+</pre>
+</body>
+</html>
+
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 @@
+<?xml version="1.0"?>
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<window title="DOM Worker Threads Test"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="test();">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
+ <script type="application/javascript" src="dom_worker_helper.js"/>
+
+ <script type="application/javascript">
+ <![CDATA[
+
+ function test()
+ {
+ waitForWorkerFinish();
+
+ var worker = new ChromeWorker("chromeWorker_worker.js");
+ worker.onmessage = function(event) {
+ is(event.data, "Done!", "Wrong message!");
+ finish();
+ }
+ worker.onerror = function(event) {
+ ok(false, "Worker had an error: " + event.message);
+ worker.terminate();
+ finish();
+ }
+ worker.postMessage("go");
+ }
+
+ ]]>
+ </script>
+
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <p id="display"></p>
+ <div id="content" style="display:none;"></div>
+ <pre id="test"></pre>
+ </body>
+ <label id="test-result"/>
+</window>
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 @@
+<?xml version="1.0"?>
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<window title="DOM Worker Threads Test"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="test();">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
+ <script type="application/javascript" src="dom_worker_helper.js"/>
+
+ <script type="application/javascript">
+ <![CDATA[
+
+ function test()
+ {
+ waitForWorkerFinish();
+
+ var worker;
+
+ function done()
+ {
+ worker = null;
+ finish();
+ }
+
+ function messageCallback(event) {
+ is(event.data, "Done", "Correct message");
+ done();
+ }
+
+ function errorCallback(event) {
+ ok(false, "Worker had an error: " + event.message);
+ done();
+ }
+
+ Components.utils.import("chrome://mochitests/content/chrome/dom/workers/test/WorkerTest.jsm");
+
+ worker = WorkerTest.go(window.location.href, messageCallback,
+ errorCallback);
+ }
+
+ ]]>
+ </script>
+
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <p id="display"></p>
+ <div id="content" style="display:none;"></div>
+ <pre id="test"></pre>
+ </body>
+ <label id="test-result"/>
+</window>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for DOM Worker Threads</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+ new Worker("clearTimeouts_worker.js").onmessage = function(event) {
+ event.target.terminate();
+
+ is(event.data, "ready", "Correct message");
+ setTimeout(function() { SimpleTest.finish(); }, 1000);
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ SimpleTest.requestFlakyTimeout("untriaged");
+
+</script>
+</pre>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<!--
+Tests of DOM Worker Console
+-->
+<head>
+ <title>Test for DOM Worker Console</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" language="javascript">
+ var worker = new Worker("console_worker.js");
+
+ worker.onmessage = function(event) {
+ is(event.target, worker, "Worker and target match!");
+ ok(event.data.status, event.data.event);
+
+ if (!event.data.status || event.data.last)
+ SimpleTest.finish();
+ };
+
+ worker.onerror = function(event) {
+ ok(false, "Worker had an error: " + event.message);
+ SimpleTest.finish();
+ }
+
+ worker.postMessage(true);
+
+ SimpleTest.waitForExplicitFinish();
+
+</script>
+</pre>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>Test for console API and blobs</title>
+ <script src="/tests/SimpleTest/SimpleTest.js">
+ </script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css">
+ </head>
+ <body>
+ <script type="text/javascript">
+
+ function consoleListener() {
+ SpecialPowers.addObserver(this, "console-api-log-event", false);
+ }
+
+ var order = 0;
+ consoleListener.prototype = {
+ observe: function(aSubject, aTopic, aData) {
+ ok(true, "Something has been received");
+ is(aTopic, "console-api-log-event");
+
+ var obj = aSubject.wrappedJSObject;
+ if (obj.arguments[0] && obj.arguments[0].msg === 'consoleAndBlobs') {
+ SpecialPowers.removeObserver(this, "console-api-log-event");
+ is(obj.arguments[0].blob.size, 3, "The size is correct");
+ is(obj.arguments[0].blob.type, 'foo/bar', "The type is correct");
+ SimpleTest.finish();
+ }
+ }
+ }
+
+ var cl = new consoleListener();
+
+ new Worker('worker_consoleAndBlobs.js');
+ SimpleTest.waitForExplicitFinish();
+
+ </script>
+ </body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<!--
+Tests of DOM Worker Console
+-->
+<head>
+ <title>Test for DOM Worker Console</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" language="javascript">
+ var worker = new Worker("consoleReplaceable_worker.js");
+
+ worker.onmessage = function(event) {
+ is(event.target, worker, "Worker and target match!");
+ ok(event.data.status, event.data.event);
+
+ if (event.data.last)
+ SimpleTest.finish();
+ };
+
+ worker.onerror = function(event) {
+ ok(false, "Worker had an error: " + event.message);
+ SimpleTest.finish();
+ }
+
+ worker.postMessage(true);
+
+ SimpleTest.waitForExplicitFinish();
+
+</script>
+</pre>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>Test for console API in SharedWorker</title>
+ <script src="/tests/SimpleTest/SimpleTest.js">
+ </script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css">
+ </head>
+ <body>
+ <script type="text/javascript">
+
+ function consoleListener() {
+ SpecialPowers.addObserver(this, "console-api-log-event", false);
+ SpecialPowers.addObserver(this, "console-api-profiler", false);
+ }
+
+ var order = 0;
+ consoleListener.prototype = {
+ observe: function(aSubject, aTopic, aData) {
+ ok(true, "Something has been received");
+
+ if (aTopic == "console-api-profiler") {
+ var obj = aSubject.wrappedJSObject;
+ is (obj.arguments[0], "Hello profiling from a SharedWorker!", "A message from a SharedWorker \\o/");
+ is (order++, 0, "First a profiler message.");
+
+ SpecialPowers.removeObserver(this, "console-api-profiler");
+ return;
+ }
+
+ if (aTopic == "console-api-log-event") {
+ var obj = aSubject.wrappedJSObject;
+ is (obj.arguments[0], "Hello world from a SharedWorker!", "A message from a SharedWorker \\o/");
+ is (obj.ID, "http://mochi.test:8888/tests/dom/workers/test/sharedWorker_console.js", "The ID is SharedWorker");
+ is (obj.innerID, "SharedWorker", "The ID is SharedWorker");
+ is (order++, 1, "Then a log message.");
+
+ SpecialPowers.removeObserver(this, "console-api-log-event");
+ SimpleTest.finish();
+ return;
+ }
+ }
+ }
+
+ var cl = new consoleListener();
+ new SharedWorker('sharedWorker_console.js');
+
+ SimpleTest.waitForExplicitFinish();
+
+ </script>
+ </body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for DOM Worker privileged properties</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" language="javascript">
+
+ var workerFilename = "content_worker.js";
+ var worker = new Worker(workerFilename);
+
+ var props = {
+ 'ctypes': 1,
+ 'OS': 1
+ };
+
+ worker.onmessage = function(event) {
+ if (event.data.testfinished) {
+ SimpleTest.finish();
+ return;
+ }
+ var prop = event.data.prop;
+ ok(prop in props, "checking " + prop);
+ is(event.data.value, undefined, prop + " should be undefined");
+ };
+
+ worker.onerror = function(event) {
+ ok(false, "Worker had an error: " + event.message);
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+
+</script>
+</pre>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for DOM Worker + CSP</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+</body>
+<script type="text/javascript" src="test_csp.js"></script>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js">
+ </script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ </head>
+ <body>
+ <script type="text/javascript">
+ const message = "hi";
+ const url = "DATA:text/plain," +
+ "onmessage = function(event) {" +
+ " postMessage(event.data);" +
+ "};";
+
+ var worker = new Worker(url);
+ worker.onmessage = function(event) {
+ is(event.data, message, "Got correct message");
+ SimpleTest.finish();
+ };
+ worker.postMessage(message);
+
+ SimpleTest.waitForExplicitFinish();
+ </script>
+ </body>
+</html>
+
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js">
+ </script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ </head>
+ <body>
+ <iframe id="workerFrame" src="errorPropagation_iframe.html"
+ onload="workerFrameLoaded();"></iframe>
+ <script type="text/javascript">
+ const workerCount = 3;
+
+ const errorMessage = "Error: expectedError";
+ const errorFilename = "http://mochi.test:8888/tests/dom/workers/test/" +
+ "errorPropagation_worker.js";
+ const errorLineno = 48;
+
+ var workerFrame;
+
+ scopeErrorCount = 0;
+ workerErrorCount = 0;
+ windowErrorCount = 0;
+
+ function messageListener(event) {
+ if (event.type == "scope") {
+ scopeErrorCount++;
+ }
+ else if (event.type == "worker") {
+ workerErrorCount++;
+ }
+ else if (event.type == "window") {
+ windowErrorCount++;
+ }
+ else {
+ ok(false, "Bad event type: " + event.type);
+ }
+
+ is(event.data.message, errorMessage, "Correct message event.message");
+ is(event.data.filename, errorFilename,
+ "Correct message event.filename");
+ is(event.data.lineno, errorLineno, "Correct message event.lineno");
+
+ if (windowErrorCount == 1) {
+ is(scopeErrorCount, workerCount, "Good number of scope errors");
+ is(workerErrorCount, workerCount, "Good number of worker errors");
+ workerFrame.stop();
+ SimpleTest.finish();
+ }
+ }
+
+ function workerFrameLoaded() {
+ workerFrame = document.getElementById("workerFrame").contentWindow;
+ workerFrame.start(workerCount, messageListener);
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ </script>
+ </body>
+</html>
+
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<!--
+Test javascript.options.strict in Workers
+-->
+<head>
+ <title>Test javascript.options.strict in Workers</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" language="javascript">
+
+ var errors = 0;
+ function errorHandler(e) {
+ ok(true, "An error has been received!");
+ errors++;
+ }
+
+ function test_noErrors() {
+ errors = 0;
+
+ var worker = new Worker('errorwarning_worker.js');
+ worker.onerror = errorHandler;
+ worker.onmessage = function(e) {
+ if (e.data.type == 'ignore')
+ return;
+
+ if (e.data.type == 'error') {
+ errorHandler();
+ return;
+ }
+
+ if (e.data.type == 'finish') {
+ ok(errors == 0, "Here we are with 0 errors!");
+ runTests();
+ return;
+ }
+ }
+
+ onerror = errorHandler;
+ worker.postMessage({ loop: 5, errors: false });
+ }
+
+ function test_errors() {
+ errors = 0;
+
+ var worker = new Worker('errorwarning_worker.js');
+ worker.onerror = errorHandler;
+ worker.onmessage = function(e) {
+ if (e.data.type == 'ignore')
+ return;
+
+ if (e.data.type == 'error') {
+ errorHandler();
+ return;
+ }
+
+ if (e.data.type == 'finish') {
+ ok(errors != 0, "Here we are with errors!");
+ runTests();
+ return;
+ }
+ }
+
+ onerror = errorHandler;
+ worker.postMessage({ loop: 5, errors: true });
+ }
+
+ var tests = [ test_noErrors, test_errors ];
+ function runTests() {
+ var test = tests.shift();
+ if (test) {
+ test();
+ } else {
+ SimpleTest.finish();
+ }
+ }
+
+ runTests();
+ SimpleTest.waitForExplicitFinish();
+
+</script>
+</pre>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js">
+ </script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ </head>
+ <body>
+ <script type="text/javascript">
+ const message = "Hi";
+
+ var messageCount = 0;
+
+ var worker = new Worker("eventDispatch_worker.js");
+ worker.onmessage = function(event) {
+ is(event.data, message, "Got correct data.");
+ if (!messageCount++) {
+ event.target.postMessage(event.data);
+ return;
+ }
+ SimpleTest.finish();
+ }
+ worker.postMessage(message);
+
+ SimpleTest.waitForExplicitFinish();
+ </script>
+ </body>
+</html>
+
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 @@
+<?xml version="1.0"?>
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<window title="DOM Worker Threads Test"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="test();">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
+ <script type="application/javascript" src="dom_worker_helper.js"/>
+
+ <script type="application/javascript">
+ <![CDATA[
+
+ function test() {
+ const message = "woohoo";
+
+ var workertest =
+ Cc["@mozilla.org/test/workertest;1"].createInstance(Ci.nsIWorkerTest);
+
+ workertest.callback = {
+ onmessage: function(data) {
+ is(data, message, "Correct message");
+ workertest.callback = null;
+ workertest = null;
+ SimpleTest.finish();
+ },
+ onerror: function(data) {
+ ok(false, "Worker had an error: " + data.message);
+ workertest.callback = null;
+ workertest = null;
+ SimpleTest.finish();
+ },
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsIWorkerTestCallback])
+ };
+
+ workertest.postMessage(message);
+
+ SimpleTest.waitForExplicitFinish();
+ }
+
+ ]]>
+ </script>
+
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <p id="display"></p>
+ <div id="content" style="display:none;"></div>
+ <pre id="test"></pre>
+ </body>
+ <label id="test-result"/>
+</window>
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 @@
+<?xml version="1.0"?>
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<window title="DOM Worker Threads Test"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="test();">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
+ <script type="application/javascript" src="dom_worker_helper.js"/>
+
+ <script type="application/javascript">
+ <![CDATA[
+
+ function test() {
+ const message = "woohoo";
+
+ var observer = {
+ observe: function(subject, topic, data) {
+ is(topic, "message", "Correct type of event");
+ is(data, message, "Correct message");
+
+ AddonManager.getAddonByID("workerbootstrap-test@mozilla.org",
+ function(addon) {
+ addon.uninstall();
+
+ const stages = [ "install", "startup", "shutdown", "uninstall" ];
+ const symbols = [ "Worker", "ChromeWorker" ];
+
+ for (var stage of stages) {
+ for (var symbol of symbols) {
+ is(Services.prefs.getBoolPref("workertest.bootstrap." + stage +
+ "." + symbol),
+ true,
+ "Symbol '" + symbol + "' present during '" + stage + "'");
+ }
+ }
+
+ SimpleTest.finish();
+ });
+ },
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver])
+ };
+
+ var workertestbootstrap = Cc["@mozilla.org/test/workertestbootstrap;1"].
+ createInstance(Ci.nsIObserver);
+
+ workertestbootstrap.observe(observer, "postMessage", message);
+
+ SimpleTest.waitForExplicitFinish();
+ }
+
+ ]]>
+ </script>
+
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <p id="display"></p>
+ <div id="content" style="display:none;"></div>
+ <pre id="test"></pre>
+ </body>
+ <label id="test-result"/>
+</window>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<!--
+Tests of DOM Worker Threads with Fibonacci
+-->
+<head>
+ <title>Test for DOM Worker Threads with Fibonacci</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=450452">DOM Worker Threads Fibonacci</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+ const seqNum = 5;
+
+ function recursivefib(n) {
+ return n < 2 ? n : recursivefib(n - 1) + recursivefib(n - 2);
+ }
+
+ var worker = new Worker("fibonacci_worker.js");
+
+ worker.onmessage = function(event) {
+ is(event.target, worker);
+ is(event.data, recursivefib(seqNum));
+ SimpleTest.finish();
+ };
+
+ worker.onerror = function(event) {
+ is(event.target, worker);
+ ok(false, "Worker had an error: " + event.message);
+ SimpleTest.finish();
+ };
+
+ worker.postMessage(seqNum);
+
+ SimpleTest.waitForExplicitFinish();
+
+</script>
+</pre>
+</body>
+</html>
+
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 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="/tests/SimpleTest/test.css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=123456
+-->
+<window title="Mozilla Bug 123456"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+ <script type="application/javascript" src="dom_worker_helper.js"/>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=123456"
+ target="_blank">Mozilla Bug 123456</a>
+
+ <div id="content" style="display: none">
+ <input id="fileList" type="file"></input>
+ </div>
+
+ </body>
+
+ <!-- test code goes here -->
+ <script type="application/javascript">
+ <![CDATA[
+
+ /** Test for Bug 123456 **/
+
+ var fileNum = 0;
+
+ /**
+ * Create a file which contains the given data and optionally adds the specified file extension.
+ */
+ function createFileWithData(fileData, /** optional */ extension) {
+ var testFile = Components.classes["@mozilla.org/file/directory_service;1"]
+ .getService(Components.interfaces.nsIProperties)
+ .get("ProfD", Components.interfaces.nsIFile);
+ var fileExtension = (extension == undefined) ? "" : "." + extension;
+ testFile.append("workerFile" + fileNum++ + fileExtension);
+
+ var outStream = Components.classes["@mozilla.org/network/file-output-stream;1"]
+ .createInstance(Components.interfaces.nsIFileOutputStream);
+ outStream.init(testFile, 0x02 | 0x08 | 0x20, // write, create, truncate
+ 0666, 0);
+ outStream.write(fileData, fileData.length);
+ outStream.close();
+
+ var fileList = document.getElementById('fileList');
+ fileList.value = testFile.path;
+
+ return fileList.files[0];
+ }
+
+ /**
+ * Create a worker to access file properties.
+ */
+ function accessFileProperties(file, expectedSize, expectedType) {
+ waitForWorkerFinish();
+
+ var worker = new Worker("file_worker.js");
+
+ worker.onerror = function(event) {
+ ok(false, "Worker had an error: " + event.message);
+ finish();
+ };
+
+ worker.onmessage = function(event) {
+ is(event.data.size, expectedSize, "size proproperty accessed from worker is not the same as on main thread.");
+ is(event.data.type, expectedType, "type proproperty accessed from worker is incorrect.");
+ is(event.data.name, file.name, "name proproperty accessed from worker is incorrect.");
+ is(event.data.lastModifiedDate.toString(), file.lastModifiedDate.toString(), "lastModifiedDate proproperty accessed from worker is incorrect.");
+ finish();
+ };
+
+ worker.postMessage(file);
+ }
+
+ // Empty file.
+ accessFileProperties(createFileWithData(""), 0, "");
+
+ // Typical use case.
+ accessFileProperties(createFileWithData("Hello"), 5, "");
+
+ // Longish file.
+ var text = "";
+ for (var i = 0; i < 10000; ++i) {
+ text += "long";
+ }
+ accessFileProperties(createFileWithData(text), 40000, "");
+
+ // Type detection based on extension.
+ accessFileProperties(createFileWithData("text", "txt"), 4, "text/plain");
+
+ ]]>
+ </script>
+</window>
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 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="/tests/SimpleTest/test.css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=664783
+-->
+<window title="Mozilla Bug 664783"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+ <script type="application/javascript" src="dom_worker_helper.js"/>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=664783"
+ target="_blank">Mozilla Bug 664783</a>
+
+ <div id="content" style="display: none">
+ <input id="fileList" type="file"></input>
+ </div>
+
+ </body>
+
+ <!-- test code goes here -->
+ <script type="application/javascript">
+ <![CDATA[
+
+ /** Test for Bug 664783 **/
+
+ var fileNum = 0;
+
+ /**
+ * Create a file which contains the given data.
+ */
+ function createFileWithData(fileData) {
+ var testFile = Components.classes["@mozilla.org/file/directory_service;1"]
+ .getService(Components.interfaces.nsIProperties)
+ .get("ProfD", Components.interfaces.nsIFile);
+ testFile.append("workerBlobPosting" + fileNum++);
+
+ var outStream = Components.classes["@mozilla.org/network/file-output-stream;1"]
+ .createInstance(Components.interfaces.nsIFileOutputStream);
+ outStream.init(testFile, 0x02 | 0x08 | 0x20, // write, create, truncate
+ 0666, 0);
+ outStream.write(fileData, fileData.length);
+ outStream.close();
+
+ var fileList = document.getElementById('fileList');
+ fileList.value = testFile.path;
+
+ return fileList.files[0];
+ }
+
+ /**
+ * Create a worker which posts the same blob given. Used to test cloning of blobs.
+ * Checks the size, type, name and path of the file posted from the worker to ensure it
+ * is the same as the original.
+ */
+ function postBlob(file) {
+ var worker = new Worker("filePosting_worker.js");
+
+ worker.onerror = function(event) {
+ ok(false, "Worker had an error: " + event.message);
+ finish();
+ };
+
+ worker.onmessage = function(event) {
+ console.log(event.data);
+ is(event.data.size, file.size, "size of file posted from worker does not match file posted to worker.");
+ finish();
+ };
+
+ var blob = file.slice();
+ worker.postMessage(blob);
+ waitForWorkerFinish();
+ }
+
+ // Empty file.
+ postBlob(createFileWithData(""));
+
+ // Typical use case.
+ postBlob(createFileWithData("Hello"));
+
+ ]]>
+ </script>
+</window>
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 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="/tests/SimpleTest/test.css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=664783
+-->
+<window title="Mozilla Bug 664783"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+ <script type="application/javascript" src="dom_worker_helper.js"/>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=664783"
+ target="_blank">Mozilla Bug 664783</a>
+
+ <div id="content" style="display: none">
+ <input id="fileList" type="file"></input>
+ </div>
+
+ </body>
+
+ <!-- test code goes here -->
+ <script type="application/javascript">
+ <![CDATA[
+
+ /** Test for Bug 664783 **/
+
+ var fileNum = 0;
+
+ /**
+ * Create a file which contains the given data and optionally adds the specified file extension.
+ */
+ function createFileWithData(fileData, /** optional */ extension) {
+ var testFile = Components.classes["@mozilla.org/file/directory_service;1"]
+ .getService(Components.interfaces.nsIProperties)
+ .get("ProfD", Components.interfaces.nsIFile);
+ var fileExtension = (extension == undefined) ? "" : "." + extension;
+ testFile.append("workerBlobSubWorker" + fileNum++ + fileExtension);
+
+ var outStream = Components.classes["@mozilla.org/network/file-output-stream;1"]
+ .createInstance(Components.interfaces.nsIFileOutputStream);
+ outStream.init(testFile, 0x02 | 0x08 | 0x20, // write, create, truncate
+ 0666, 0);
+ outStream.write(fileData, fileData.length);
+ outStream.close();
+
+ var fileList = document.getElementById('fileList');
+ fileList.value = testFile.path;
+
+ return fileList.files[0];
+ }
+
+ /**
+ * Create a worker to access blob properties.
+ */
+ function accessFileProperties(file, expectedSize) {
+ var worker = new Worker("fileBlobSubWorker_worker.js");
+
+ worker.onerror = function(event) {
+ ok(false, "Worker had an error: " + event.message);
+ finish();
+ };
+
+ worker.onmessage = function(event) {
+ if (event.data == undefined) {
+ ok(false, "Worker had an error.");
+ } else {
+ is(event.data.size, expectedSize, "size proproperty accessed from worker is not the same as on main thread.");
+ }
+ finish();
+ };
+
+ var blob = file.slice();
+ worker.postMessage(blob);
+ waitForWorkerFinish();
+ }
+
+ // Empty file.
+ accessFileProperties(createFileWithData(""), 0);
+
+ // Typical use case.
+ accessFileProperties(createFileWithData("Hello"), 5);
+
+ // Longish file.
+ var text = "";
+ for (var i = 0; i < 10000; ++i) {
+ text += "long";
+ }
+ accessFileProperties(createFileWithData(text), 40000);
+
+ // Type detection based on extension.
+ accessFileProperties(createFileWithData("text", "txt"), 4);
+
+ ]]>
+ </script>
+</window>
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 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="/tests/SimpleTest/test.css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=664783
+-->
+<window title="Mozilla Bug 664783"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+ <script type="application/javascript" src="dom_worker_helper.js"/>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=664783"
+ target="_blank">Mozilla Bug 664783</a>
+
+ <div id="content" style="display: none">
+ <input id="fileList" type="file"></input>
+ </div>
+
+ </body>
+
+ <!-- test code goes here -->
+ <script type="application/javascript">
+ <![CDATA[
+
+ /** Test for Bug 664783 **/
+
+ var fileNum = 0;
+
+ /**
+ * Create a file which contains the given data.
+ */
+ function createFileWithData(fileData) {
+ var testFile = Components.classes["@mozilla.org/file/directory_service;1"]
+ .getService(Components.interfaces.nsIProperties)
+ .get("ProfD", Components.interfaces.nsIFile);
+ testFile.append("workerFilePosting" + fileNum++);
+
+ var outStream = Components.classes["@mozilla.org/network/file-output-stream;1"]
+ .createInstance(Components.interfaces.nsIFileOutputStream);
+ outStream.init(testFile, 0x02 | 0x08 | 0x20, // write, create, truncate
+ 0666, 0);
+ outStream.write(fileData, fileData.length);
+ outStream.close();
+
+ var fileList = document.getElementById('fileList');
+ fileList.value = testFile.path;
+
+ return fileList.files[0];
+ }
+
+ /**
+ * Create a worker which posts the same file given. Used to test cloning of files.
+ * Checks the size, type, name and path of the file posted from the worker to ensure it
+ * is the same as the original.
+ */
+ function postFile(file) {
+ var worker = new Worker("file_worker.js");
+
+ worker.onerror = function(event) {
+ ok(false, "Worker had an error: " + event.message);
+ finish();
+ };
+
+ worker.onmessage = function(event) {
+ is(event.data.size, file.size, "size of file posted from worker does not match file posted to worker.");
+ is(event.data.type, file.type, "type of file posted from worker does not match file posted to worker.");
+ is(event.data.name, file.name, "name of file posted from worker does not match file posted to worker.");
+ finish();
+ };
+
+ worker.postMessage(file);
+ waitForWorkerFinish();
+ }
+
+ // Empty file.
+ postFile(createFileWithData(""));
+
+ // Typical use case.
+ postFile(createFileWithData("Hello"));
+
+ ]]>
+ </script>
+</window>
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 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="/tests/SimpleTest/test.css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=664783
+-->
+<window title="Mozilla Bug 664783"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+ <script type="application/javascript" src="dom_worker_helper.js"/>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=664783"
+ target="_blank">Mozilla Bug 664783</a>
+
+ <div id="content" style="display: none">
+ <input id="fileList" type="file"></input>
+ </div>
+
+ </body>
+
+ <!-- test code goes here -->
+ <script type="application/javascript">
+ <![CDATA[
+
+ if (navigator.platform.startsWith("Win")) {
+ SimpleTest.expectAssertions(0, 1);
+ }
+
+ /** Test for Bug 664783 **/
+
+ var fileNum = 0;
+
+ /**
+ * Create a file which contains the given data.
+ */
+ function createFileWithData(fileData) {
+ var testFile = Components.classes["@mozilla.org/file/directory_service;1"]
+ .getService(Components.interfaces.nsIProperties)
+ .get("ProfD", Components.interfaces.nsIFile);
+ testFile.append("workerReadSlice" + fileNum++);
+
+ var outStream = Components.classes["@mozilla.org/network/file-output-stream;1"]
+ .createInstance(Components.interfaces.nsIFileOutputStream);
+ outStream.init(testFile, 0x02 | 0x08 | 0x20, // write, create, truncate
+ 0666, 0);
+ outStream.write(fileData, fileData.length);
+ outStream.close();
+
+ var fileList = document.getElementById('fileList');
+ fileList.value = testFile.path;
+
+ return fileList.files[0];
+ }
+
+ /**
+ * Creates a worker which slices a blob to the given start and end offset and
+ * reads the content as text.
+ */
+ function readSlice(blob, start, end, expectedText) {
+ var worker = new Worker("fileReadSlice_worker.js");
+
+ worker.onerror = function(event) {
+ ok(false, "Worker had an error: " + event.message);
+ finish();
+ };
+
+ worker.onmessage = function(event) {
+ is(event.data, expectedText, "Text from sliced blob in worker is incorrect.");
+ finish();
+ };
+
+ var params = {blob: blob, start: start, end: end};
+ worker.postMessage(params);
+ waitForWorkerFinish();
+ }
+
+ // Empty file.
+ readSlice(createFileWithData(""), 0, 0, "");
+
+ // Typical use case.
+ readSlice(createFileWithData("HelloBye"), 5, 8, "Bye");
+
+ // End offset too large.
+ readSlice(createFileWithData("HelloBye"), 5, 9, "Bye");
+
+ // Start of file.
+ readSlice(createFileWithData("HelloBye"), 0, 5, "Hello");
+
+ ]]>
+ </script>
+</window>
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 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for FileReader in workers</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+
+<body>
+<script type="text/javascript;version=1.7">
+
+const minFileSize = 20000;
+SimpleTest.waitForExplicitFinish();
+
+// Create strings containing data we'll test with. We'll want long
+// strings to ensure they span multiple buffers while loading
+var testTextData = "asd b\tlah\u1234w\u00a0r";
+while (testTextData.length < minFileSize) {
+ testTextData = testTextData + testTextData;
+}
+
+var testASCIIData = "abcdef 123456\n";
+while (testASCIIData.length < minFileSize) {
+ testASCIIData = testASCIIData + testASCIIData;
+}
+
+var testBinaryData = "";
+for (var i = 0; i < 256; i++) {
+ testBinaryData += String.fromCharCode(i);
+}
+while (testBinaryData.length < minFileSize) {
+ testBinaryData = testBinaryData + testBinaryData;
+}
+
+var dataurldata0 = testBinaryData.substr(0, testBinaryData.length -
+ testBinaryData.length % 3);
+var dataurldata1 = testBinaryData.substr(0, testBinaryData.length - 2 -
+ testBinaryData.length % 3);
+var dataurldata2 = testBinaryData.substr(0, testBinaryData.length - 1 -
+ testBinaryData.length % 3);
+
+
+//Set up files for testing
+var openerURL = SimpleTest.getTestFileURL("fileapi_chromeScript.js");
+var opener = SpecialPowers.loadChromeScript(openerURL);
+opener.addMessageListener("files.opened", onFilesOpened);
+opener.sendAsyncMessage("files.open", [
+ testASCIIData,
+ testBinaryData,
+ null,
+ convertToUTF8(testTextData),
+ convertToUTF16(testTextData),
+ "",
+ dataurldata0,
+ dataurldata1,
+ dataurldata2,
+]);
+
+function onFilesOpened(message) {
+ var worker = new Worker('worker_fileReader.js');
+ worker.postMessage({ blobs: message,
+ testTextData: testTextData,
+ testASCIIData: testASCIIData,
+ testBinaryData: testBinaryData,
+ dataurldata0: dataurldata0,
+ dataurldata1: dataurldata1,
+ dataurldata2: dataurldata2 });
+
+ worker.onmessage = function(e) {
+ var msg = e.data;
+ if (msg.type == 'finish') {
+ SimpleTest.finish();
+ return;
+ }
+
+ if (msg.type == 'check') {
+ ok(msg.status, msg.msg);
+ return;
+ }
+
+ ok(false, "Unknown message.");
+ }
+}
+
+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));
+}
+
+</script>
+</body>
+</html>
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 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="/tests/SimpleTest/test.css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=664783
+-->
+<window title="Mozilla Bug 664783"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+ <script type="application/javascript" src="dom_worker_helper.js"/>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=664783"
+ target="_blank">Mozilla Bug 664783</a>
+
+ <div id="content" style="display: none">
+ <input id="fileList" type="file"></input>
+ </div>
+
+ </body>
+
+ <!-- test code goes here -->
+ <script type="application/javascript">
+ <![CDATA[
+
+ /** Test for Bug 664783 **/
+
+ var fileNum = 0;
+
+ /**
+ * Create a file which contains the given data and optionally adds the specified file extension.
+ */
+ function createFileWithData(fileData, /** optional */ extension) {
+ var testFile = Components.classes["@mozilla.org/file/directory_service;1"]
+ .getService(Components.interfaces.nsIProperties)
+ .get("ProfD", Components.interfaces.nsIFile);
+ var fileExtension = (extension == undefined) ? "" : "." + extension;
+ testFile.append("workerFileReaderSync" + fileNum++ + fileExtension);
+
+ var outStream = Components.classes["@mozilla.org/network/file-output-stream;1"]
+ .createInstance(Components.interfaces.nsIFileOutputStream);
+ outStream.init(testFile, 0x02 | 0x08 | 0x20, // write, create, truncate
+ 0666, 0);
+ outStream.write(fileData, fileData.length);
+ outStream.close();
+
+ var fileList = document.getElementById('fileList');
+ fileList.value = testFile.path;
+
+ return fileList.files[0];
+ }
+
+ 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;
+ }
+
+ /**
+ * Converts the given string to a data URL of the specified mime type.
+ */
+ function convertToDataURL(mime, s) {
+ return "data:" + mime + ";base64," + btoa(s);
+ }
+
+ /**
+ * Create a worker to read a file containing fileData using FileReaderSync and
+ * checks the return type against the expected type. Optionally set an encoding
+ * for reading the file as text.
+ */
+ function readFileData(fileData, expectedText, /** optional */ encoding) {
+ var worker = new Worker("fileReaderSync_worker.js");
+
+ worker.onmessage = function(event) {
+ is(event.data.text, expectedText, "readAsText in worker returned incorrect result.");
+ is(event.data.bin, fileData, "readAsBinaryString in worker returned incorrect result.");
+ is(event.data.url, convertToDataURL("application/octet-stream", fileData), "readAsDataURL in worker returned incorrect result.");
+ is(event.data.arrayBuffer.byteLength, fileData.length, "readAsArrayBuffer returned buffer of incorrect length.");
+ finish();
+ };
+
+ worker.onerror = function(event) {
+ ok(false, "Worker had an error: " + event.message);
+ finish();
+ };
+
+ var params = {file: createFileWithData(fileData), encoding: encoding};
+
+ worker.postMessage(params);
+
+ waitForWorkerFinish();
+ }
+
+ /**
+ * Create a worker which reuses a FileReaderSync to read multiple files as DataURLs.
+ */
+ function reuseReaderForURL(files, expected) {
+ var worker = new Worker("fileReaderSync_worker.js");
+
+ worker.onerror = function(event) {
+ ok(false, "Worker had an error: " + event.message);
+ finish();
+ };
+
+ var k = 0;
+ worker.onmessage = function(event) {
+ is(event.data.url, expected[k], "readAsDataURL in worker returned incorrect result when reusing FileReaderSync.");
+ k++;
+ finish();
+ };
+
+ for (var i = 0; i < files.length; ++i) {
+ var params = {file: files[i], encoding: undefined};
+ worker.postMessage(params);
+ waitForWorkerFinish();
+ }
+ }
+
+ /**
+ * Create a worker which reuses a FileReaderSync to read multiple files as text.
+ */
+ function reuseReaderForText(fileData, expected) {
+ var worker = new Worker("fileReaderSync_worker.js");
+
+ worker.onerror = function(event) {
+ ok(false, "Worker had an error: " + event.message);
+ finish();
+ };
+
+ var k = 0;
+ worker.onmessage = function(event) {
+ is(event.data.text, expected[k++], "readAsText in worker returned incorrect result when reusing FileReaderSync.");
+ finish();
+ };
+
+ for (var i = 0; i < fileData.length; ++i) {
+ var params = {file: createFileWithData(fileData[i]), encoding: undefined};
+ worker.postMessage(params);
+ waitForWorkerFinish();
+ }
+ }
+
+
+ /**
+ * Creates a a worker which reads a file containing fileData as an ArrayBuffer.
+ * Verifies that the ArrayBuffer when interpreted as a string matches the original data.
+ */
+ function readArrayBuffer(fileData) {
+ var worker = new Worker("fileReaderSync_worker.js");
+
+ worker.onmessage = function(event) {
+ var view = new Uint8Array(event.data.arrayBuffer);
+ is(event.data.arrayBuffer.byteLength, fileData.length, "readAsArrayBuffer returned buffer of incorrect length.");
+ is(String.fromCharCode.apply(String, view), fileData, "readAsArrayBuffer returned buffer containing incorrect data.");
+ finish();
+ };
+
+ worker.onerror = function(event) {
+ ok(false, "Worker had an error: " + event.message);
+ finish();
+ };
+
+ var params = {file: createFileWithData(fileData), encoding: undefined};
+
+ worker.postMessage(params);
+
+ waitForWorkerFinish();
+ }
+
+ // Empty file.
+ readFileData("", "");
+
+ // Typical use case.
+ readFileData("text", "text");
+
+ // Test reading UTF-16 characters.
+ readFileData(convertToUTF16("text"), "text", "UTF-16");
+
+ // First read a file of type "text/plain", then read a file of type "application/octet-stream".
+ reuseReaderForURL([createFileWithData("text", "txt"), createFileWithData("text")],
+ [convertToDataURL("text/plain", "text"),
+ convertToDataURL("application/octet-stream", "text")]);
+
+ // First read UTF-16 characters marked using BOM, then read UTF-8 characters.
+ reuseReaderForText([convertToUTF16("\ufefftext"), "text"],
+ ["text", "text"]);
+
+ // Reading data as ArrayBuffer.
+ readArrayBuffer("");
+ readArrayBuffer("text");
+
+ ]]>
+ </script>
+</window>
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 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="/tests/SimpleTest/test.css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=664783
+-->
+<window title="Mozilla Bug 664783"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+ <script type="application/javascript" src="dom_worker_helper.js"/>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=664783"
+ target="_blank">Mozilla Bug 664783</a>
+
+ <div id="content" style="display: none">
+ <input id="fileList" type="file"></input>
+ </div>
+
+ </body>
+
+ <!-- test code goes here -->
+ <script type="application/javascript">
+ <![CDATA[
+
+ /** Test for Bug 664783 **/
+
+ var fileNum = 0;
+
+ /**
+ * Create a file which contains the given data.
+ */
+ function createFileWithData(fileData) {
+ var testFile = Components.classes["@mozilla.org/file/directory_service;1"]
+ .getService(Components.interfaces.nsIProperties)
+ .get("ProfD", Components.interfaces.nsIFile);
+ testFile.append("workerFileReaderSyncErrors" + fileNum++);
+
+ var outStream = Components.classes["@mozilla.org/network/file-output-stream;1"]
+ .createInstance(Components.interfaces.nsIFileOutputStream);
+ outStream.init(testFile, 0x02 | 0x08 | 0x20, // write, create, truncate
+ 0666, 0);
+ outStream.write(fileData, fileData.length);
+ outStream.close();
+
+ var fileList = document.getElementById('fileList');
+ fileList.value = testFile.path;
+
+ return fileList.files[0];
+ }
+
+ /**
+ * Creates a worker which runs errors cases.
+ */
+ function runWorkerErrors(file) {
+ var worker = new Worker("fileReaderSyncErrors_worker.js");
+
+ worker.onerror = function(event) {
+ ok(false, "Worker had an error: " + event.message);
+ finish();
+ };
+
+ worker.onmessage = function(event) {
+ if(event.data == undefined) {
+ // Worker returns undefined when tests have finished running.
+ finish();
+ } else {
+ // Otherwise worker will return results of tests to be evaluated.
+ is(event.data.actual, event.data.expected, event.data.message);
+ }
+ };
+
+ worker.postMessage(file);
+ waitForWorkerFinish();
+ }
+
+ // Run worker which creates exceptions.
+ runWorkerErrors(createFileWithData("text"));
+
+ ]]>
+ </script>
+</window>
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 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="/tests/SimpleTest/test.css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=664783
+-->
+<window title="Mozilla Bug 664783"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+ <script type="application/javascript" src="dom_worker_helper.js"/>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=664783"
+ target="_blank">Mozilla Bug 664783</a>
+
+ <div id="content" style="display: none">
+ <input id="fileList" type="file"></input>
+ </div>
+
+ </body>
+
+ <!-- test code goes here -->
+ <script type="application/javascript">
+ <![CDATA[
+
+ /** Test for Bug 664783 **/
+
+ var fileNum = 0;
+
+ /**
+ * Create a file which contains the given data and optionally adds the specified file extension.
+ */
+ function createFileWithData(fileData, /** optional */ extension) {
+ var testFile = Components.classes["@mozilla.org/file/directory_service;1"]
+ .getService(Components.interfaces.nsIProperties)
+ .get("ProfD", Components.interfaces.nsIFile);
+ var fileExtension = (extension == undefined) ? "" : "." + extension;
+ testFile.append("workerSlice" + fileNum++ + fileExtension);
+
+ var outStream = Components.classes["@mozilla.org/network/file-output-stream;1"]
+ .createInstance(Components.interfaces.nsIFileOutputStream);
+ outStream.init(testFile, 0x02 | 0x08 | 0x20, // write, create, truncate
+ 0666, 0);
+ outStream.write(fileData, fileData.length);
+ outStream.close();
+
+ var fileList = document.getElementById('fileList');
+ fileList.value = testFile.path;
+
+ return fileList.files[0];
+ }
+
+ /**
+ * Starts a worker which slices the blob to the given start offset and optional end offset and
+ * content type. It then verifies that the size and type of the sliced blob is correct.
+ */
+ function createSlice(blob, start, expectedLength, /** optional */ end, /** optional */ contentType) {
+ var worker = new Worker("fileSlice_worker.js");
+
+ worker.onerror = function(event) {
+ ok(false, "Worker had an error: " + event.message);
+ finish();
+ };
+
+ worker.onmessage = function(event) {
+ is(event.data.size, expectedLength, "size property of slice is incorrect.");
+ is(event.data.type, contentType ? contentType : blob.type, "type property of slice is incorrect.");
+ finish();
+ };
+
+ var params = {blob: blob, start: start, end: end, contentType: contentType};
+ worker.postMessage(params);
+ waitForWorkerFinish();
+ }
+
+ // Empty file.
+ createSlice(createFileWithData(""), 0, 0, 0);
+
+ // Typical use case.
+ createSlice(createFileWithData("Hello"), 1, 1, 2);
+
+ // Longish file.
+ var text = "";
+ for (var i = 0; i < 10000; ++i) {
+ text += "long";
+ }
+ createSlice(createFileWithData(text), 2000, 2000, 4000);
+
+ // Slice to different type.
+ createSlice(createFileWithData("text", "txt"), 0, 2, 2, "image/png");
+
+ // Length longer than blob.
+ createSlice(createFileWithData("text"), 0, 4, 20);
+
+ // Start longer than blob.
+ createSlice(createFileWithData("text"), 20, 0, 4);
+
+ // No optional arguments
+ createSlice(createFileWithData("text"), 0, 4);
+ createSlice(createFileWithData("text"), 2, 2);
+
+ ]]>
+ </script>
+</window>
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 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="/tests/SimpleTest/test.css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=664783
+-->
+<window title="Mozilla Bug 664783"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+ <script type="application/javascript" src="dom_worker_helper.js"/>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=664783"
+ target="_blank">Mozilla Bug 664783</a>
+
+ <div id="content" style="display: none">
+ <input id="fileList" type="file"></input>
+ </div>
+
+ </body>
+
+ <!-- test code goes here -->
+ <script type="application/javascript">
+ <![CDATA[
+
+ /** Test for Bug 664783 **/
+
+ var fileNum = 0;
+
+ /**
+ * Create a file which contains the given data and optionally adds the specified file extension.
+ */
+ function createFileWithData(fileData, /** optional */ extension) {
+ var testFile = Components.classes["@mozilla.org/file/directory_service;1"]
+ .getService(Components.interfaces.nsIProperties)
+ .get("ProfD", Components.interfaces.nsIFile);
+ var fileExtension = (extension == undefined) ? "" : "." + extension;
+ testFile.append("workerSubWorker" + fileNum++ + fileExtension);
+
+ var outStream = Components.classes["@mozilla.org/network/file-output-stream;1"]
+ .createInstance(Components.interfaces.nsIFileOutputStream);
+ outStream.init(testFile, 0x02 | 0x08 | 0x20, // write, create, truncate
+ 0666, 0);
+ outStream.write(fileData, fileData.length);
+ outStream.close();
+
+ var fileList = document.getElementById('fileList');
+ fileList.value = testFile.path;
+
+ return fileList.files[0];
+ }
+
+ /**
+ * Create a worker to access file properties.
+ */
+ function accessFileProperties(file, expectedSize, expectedType) {
+ var worker = new Worker("fileSubWorker_worker.js");
+
+ worker.onerror = function(event) {
+ ok(false, "Worker had an error: " + event.message);
+ finish();
+ };
+
+ worker.onmessage = function(event) {
+ if (event.data == undefined) {
+ ok(false, "Worker had an error.");
+ } else {
+ is(event.data.size, expectedSize, "size proproperty accessed from worker is not the same as on main thread.");
+ is(event.data.type, expectedType, "type proproperty accessed from worker is incorrect.");
+ is(event.data.name, file.name, "name proproperty accessed from worker is incorrect.");
+ }
+ finish();
+ };
+
+ worker.postMessage(file);
+ waitForWorkerFinish();
+ }
+
+ // Empty file.
+ accessFileProperties(createFileWithData(""), 0, "");
+
+ // Typical use case.
+ accessFileProperties(createFileWithData("Hello"), 5, "");
+
+ // Longish file.
+ var text = "";
+ for (var i = 0; i < 10000; ++i) {
+ text += "long";
+ }
+ accessFileProperties(createFileWithData(text), 40000, "");
+
+ // Type detection based on extension.
+ accessFileProperties(createFileWithData("text", "txt"), 4, "text/plain");
+
+ ]]>
+ </script>
+</window>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<!--
+Tests of DOM Worker Threads (Bug 437152)
+-->
+<head>
+ <title>Test for DOM Worker Threads (Bug 437152)</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=437152">DOM Worker Threads Bug 437152</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+ var worker = new Worker("importScripts_worker.js");
+
+ worker.onmessage = function(event) {
+ switch (event.data) {
+ case "started":
+ worker.postMessage("stop");
+ break;
+ case "stopped":
+ ok(true, "worker correctly stopped");
+ SimpleTest.finish();
+ break;
+ default:
+ ok(false, "Unexpected message:" + event.data);
+ SimpleTest.finish();
+ }
+ };
+
+ worker.onerror = function(event) {
+ ok(false, "Worker had an error:" + event.message);
+ SimpleTest.finish();
+ }
+
+ worker.postMessage("start");
+
+ SimpleTest.waitForExplicitFinish();
+
+</script>
+</pre>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for 3rd party imported script and muted errors</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<script type="text/javascript">
+
+const workerURL = 'http://mochi.test:8888/tests/dom/workers/test/importScripts_3rdParty_worker.js';
+
+var tests = [
+ function() {
+ var worker = new Worker("importScripts_3rdParty_worker.js");
+ worker.onmessage = function(event) {
+ ok("result" in event.data && event.data.result, "It seems we don't share data!");
+ next();
+ };
+
+ worker.postMessage({ url: location.href, test: 'try', nested: false });
+ },
+
+ function() {
+ var worker = new Worker("importScripts_3rdParty_worker.js");
+ worker.onmessage = function(event) {
+ ok("result" in event.data && event.data.result, "It seems we don't share data in nested workers!");
+ next();
+ };
+
+ worker.postMessage({ url: location.href, test: 'try', nested: true });
+ },
+
+ function() {
+ var worker = new Worker("importScripts_3rdParty_worker.js");
+ worker.onmessage = function(event) {
+ ok("result" in event.data && event.data.result, "It seems we don't share data via eventListener!");
+ next();
+ };
+
+ worker.postMessage({ url: location.href, test: 'eventListener', nested: false });
+ },
+
+ function() {
+ var worker = new Worker("importScripts_3rdParty_worker.js");
+ worker.onmessage = function(event) {
+ ok("result" in event.data && event.data.result, "It seems we don't share data in nested workers via eventListener!");
+ next();
+ };
+
+ worker.postMessage({ url: location.href, test: 'eventListener', nested: true });
+ },
+
+ function() {
+ var worker = new Worker("importScripts_3rdParty_worker.js");
+ worker.onmessage = function(event) {
+ ok("result" in event.data && event.data.result, "It seems we don't share data via onerror!");
+ next();
+ };
+ worker.onerror = function(event) {
+ event.preventDefault();
+ }
+
+ worker.postMessage({ url: location.href, test: 'onerror', nested: false });
+ },
+
+ function() {
+ var worker = new Worker("importScripts_3rdParty_worker.js");
+ worker.onerror = function(event) {
+ event.preventDefault();
+ ok(event instanceof ErrorEvent, "ErrorEvent received.");
+ is(event.filename, workerURL, "ErrorEvent.filename is correct");
+ next();
+ };
+
+ worker.postMessage({ url: location.href, test: 'none', nested: false });
+ },
+
+ function() {
+ var worker = new Worker("importScripts_3rdParty_worker.js");
+ worker.addEventListener("error", function(event) {
+ event.preventDefault();
+ ok(event instanceof ErrorEvent, "ErrorEvent received.");
+ is(event.filename, workerURL, "ErrorEvent.filename is correct");
+ next();
+ });
+
+ worker.postMessage({ url: location.href, test: 'none', nested: false });
+ },
+
+ function() {
+ var worker = new Worker("importScripts_3rdParty_worker.js");
+ worker.onerror = function(event) {
+ ok(false, "No error should be received!");
+ };
+
+ worker.onmessage = function(event) {
+ ok("error" in event.data && event.data.error, "The error has been fully received from a nested worker");
+ next();
+ };
+ worker.postMessage({ url: location.href, test: 'none', nested: true });
+ },
+
+ function() {
+ var url = URL.createObjectURL(new Blob(["%&%^&%^"]));
+ var worker = new Worker(url);
+ worker.onerror = function(event) {
+ event.preventDefault();
+ ok(event instanceof ErrorEvent, "ErrorEvent received.");
+ next();
+ };
+ }
+];
+
+function next() {
+ if (!tests.length) {
+ SimpleTest.finish();
+ return;
+ }
+
+ var test = tests.shift();
+ test();
+}
+
+SimpleTest.waitForExplicitFinish();
+next();
+
+</script>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 1198078 - test that we respect mixed content blocking in importScript() inside workers</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1198078">DOM Worker Threads Bug 1198078</a>
+<iframe></iframe>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+ onmessage = function(event) {
+ switch (event.data.status) {
+ case "done":
+ SimpleTest.finish();
+ break;
+ case "ok":
+ ok(event.data.data, event.data.msg);
+ break;
+ default:
+ ok(false, "Unexpected message:" + event.data);
+ SimpleTest.finish();
+ }
+ };
+
+ SimpleTest.waitForExplicitFinish();
+ onload = function() {
+ SpecialPowers.pushPrefEnv({"set": [
+ ["dom.workers.sharedWorkers.enabled", true],
+ ["security.mixed_content.block_active_content", false],
+ ]}, function() {
+ var iframe = document.querySelector("iframe");
+ iframe.src = "https://example.com/tests/dom/workers/test/importScripts_mixedcontent.html";
+ });
+ };
+
+</script>
+</pre>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<!--
+Tests of DOM Worker JSON messages
+-->
+<head>
+ <title>Test for DOM Worker Navigator</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script src="json_worker.js" language="javascript"></script>
+<script class="testbody" language="javascript">
+
+ var worker = new Worker("instanceof_worker.js");
+
+ worker.onmessage = function(event) {
+ ok(event.data.status, event.data.event);
+
+ if (event.data.last)
+ SimpleTest.finish();
+ };
+
+ worker.postMessage(42);
+
+ SimpleTest.waitForExplicitFinish();
+
+</script>
+</pre>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<!--
+Tests of DOM Worker JSON messages
+-->
+<head>
+ <title>Test for DOM Worker Navigator</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script src="json_worker.js" language="javascript"></script>
+<script class="testbody" language="javascript">
+
+ ok(messages.length, "No messages to test!");
+
+ var worker = new Worker("json_worker.js");
+
+ var index = 0;
+ worker.onmessage = function(event) {
+ var key = messages[index++];
+
+ // Loop for the ones we shouldn't receive.
+ while (key.exception) {
+ key = messages[index++];
+ }
+
+ is(typeof event.data, key.type, "Bad type! " + messages.indexOf(key));
+
+ if (key.array) {
+ is(event.data instanceof Array, key.array,
+ "Array mismatch! " + messages.indexOf(key));
+ }
+
+ if (key.isNaN) {
+ ok(isNaN(event.data), "Should be NaN!" + messages.indexOf(key));
+ }
+
+ if (key.isInfinity) {
+ is(event.data, Infinity, "Should be Infinity!" + messages.indexOf(key));
+ }
+
+ if (key.isNegativeInfinity) {
+ is(event.data, -Infinity, "Should be -Infinity!" + messages.indexOf(key));
+ }
+
+ if (key.shouldCompare || key.shouldEqual) {
+ ok(event.data == key.compareValue,
+ "Values don't compare! " + messages.indexOf(key));
+ }
+
+ if (key.shouldEqual) {
+ ok(event.data === key.compareValue,
+ "Values don't equal! " + messages.indexOf(key));
+ }
+
+ if (key.jsonValue) {
+ is(JSON.stringify(event.data), key.jsonValue,
+ "Object stringification inconsistent!" + messages.indexOf(key));
+ }
+
+ if (event.data == "testFinished") {
+ is(index, messages.length, "Didn't see the right number of messages!");
+ SimpleTest.finish();
+ }
+ };
+
+ worker.onerror = function(event) {
+ ok(false, "Worker had an error: " + event.message);
+ SimpleTest.finish();
+ }
+
+ worker.postMessage("start");
+
+ SimpleTest.waitForExplicitFinish();
+
+</script>
+</pre>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for JSVersion in workers - Bug 487070</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" language="javascript">
+
+ var gExpectedError = false;
+
+ onerror = function(evt) {
+ ok(gExpectedError, "Error expected!");
+ runTest();
+ }
+
+ function doMagic() {
+ var worker = new Worker('jsversion_worker.js');
+ worker.onmessage = function(evt) {
+ ok(evt.data, 'All the tests passed');
+ runTest();
+ }
+ worker.postMessage(1);
+ }
+
+ var tests = [
+ // No custom version
+ function() {
+ gExpectedError = true;
+ SpecialPowers.pushPrefEnv({"set":[['dom.workers.latestJSVersion', false]]},
+ function() { doMagic(true); });
+ },
+
+ // Enable latest JS Version
+ function() {
+ gExpectedError = false;
+ SpecialPowers.pushPrefEnv({"set":[['dom.workers.latestJSVersion', true]]},
+ function() { doMagic(false); });
+ }
+ ];
+
+ function runTest() {
+ if (!tests.length) {
+ SimpleTest.finish();
+ return;
+ }
+
+ var test = tests.shift();
+ test();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ runTest();
+
+</script>
+</pre>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 484305 - Load workers as UTF-8</title>
+ <meta http-equiv="content-type" content="text/html; charset=KOI8-R">
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=484305">Bug 484305 - Load workers as UTF-8</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+SimpleTest.waitForExplicitFinish();
+
+var canonical = String.fromCharCode(0x41F, 0x440, 0x438, 0x432, 0x435, 0x442);
+ok(document.inputEncoding === "KOI8-R", "Document encoding is KOI8-R");
+
+// Worker sends two strings, one with `canonical` encoded in KOI8-R and one as UTF-8.
+// Since Worker scripts should always be decoded using UTF-8, even if the owning document's charset is different, the UTF-8 decode should match, while KOI8-R should fail.
+var counter = 0;
+var worker = new Worker("loadEncoding_worker.js");
+worker.onmessage = function(e) {
+ if (e.data.encoding === "KOI8-R") {
+ ok(e.data.text !== canonical, "KOI8-R decoded text should not match");
+ } else if (e.data.encoding === "UTF-8") {
+ ok(e.data.text === canonical, "UTF-8 decoded text should match");
+ }
+ counter++;
+ if (counter === 2)
+ SimpleTest.finish();
+}
+
+worker.onerror = function(e) {
+ ok(false, "Worker error");
+ SimpleTest.finish();
+}
+</script>
+
+</pre>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html>
+<head>
+ <title>Test for DOM Worker Threads</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+"use strict";
+
+var loadErrorMessage = 'SecurityError: Failed to load worker script at "about:blank"';
+
+function nextTest() {
+ (function(){
+ function workerfunc() {
+ var subworker = new Worker("about:blank");
+ subworker.onerror = function(e) {
+ e.preventDefault();
+ postMessage(e.message);
+ }
+ }
+ var b = new Blob([workerfunc+'workerfunc();']);
+ var u = URL.createObjectURL(b);
+ function callworker(i) {
+ try {
+ var w = new Worker(u);
+ URL.revokeObjectURL(u);
+ is(i, 0, 'worker creation succeeded');
+ } catch (e) {
+ is(i, 1, 'worker creation failed');
+ SimpleTest.finish();
+ return;
+ }
+ w.onmessage = function(e) {
+ is(e.data, loadErrorMessage,
+ "Should catch the error when loading inner script");
+ if (++i < 2) callworker(i);
+ else SimpleTest.finish();
+ };
+ w.onerrror = function(e) {
+ ok(false, "Should not get any errors from this worker");
+ }
+ }
+ callworker(0);
+ })();
+}
+
+try {
+ var worker = new Worker("about:blank");
+ worker.onerror = function(e) {
+ e.preventDefault();
+ is(e.message, loadErrorMessage,
+ "Should get the right error from the toplevel script");
+ nextTest();
+ }
+
+ worker.onmessage = function(event) {
+ ok(false, "Shouldn't get a message!");
+ SimpleTest.finish();
+ }
+} catch (e) {
+ ok(false, "This should not happen.");
+}
+
+
+SimpleTest.waitForExplicitFinish();
+
+</script>
+</pre>
+</body>
+</html>
+
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<!--
+Tests of DOM Worker Location
+-->
+<head>
+ <title>Test for DOM Worker Location</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" language="javascript">
+
+ var thisFilename = "test_location.html";
+ var workerFilename = "location_worker.js";
+
+ var href = window.location.href
+ var queryPos = href.lastIndexOf(window.location.search);
+ var baseHref = href.substr(0, href.substr(0, queryPos).lastIndexOf("/") + 1);
+
+ var path = window.location.pathname;
+ var basePath = path.substr(0, path.lastIndexOf("/") + 1);
+
+ var strings = {
+ "toString": baseHref + workerFilename,
+ "href": baseHref + workerFilename,
+ "protocol": window.location.protocol,
+ "host": window.location.host,
+ "hostname": window.location.hostname,
+ "port": window.location.port,
+ "pathname": basePath + workerFilename,
+ "search": "",
+ "hash": "",
+ "origin": "http://mochi.test:8888"
+ };
+
+ var lastSlash = href.substr(0, queryPos).lastIndexOf("/") + 1;
+ is(thisFilename,
+ href.substr(lastSlash, queryPos - lastSlash),
+ "Correct filename ");
+
+ var worker = new Worker(workerFilename);
+
+ worker.onmessage = function(event) {
+ if (event.data.string == "testfinished") {
+ SimpleTest.finish();
+ return;
+ }
+ ok(event.data.string in strings, event.data.string);
+ is(event.data.value, strings[event.data.string], event.data.string);
+ };
+
+ worker.onerror = function(event) {
+ ok(false, "Worker had an error: " + event.message);
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+
+</script>
+</pre>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<!--
+Tests of DOM Worker Threads (Bug 437152)
+-->
+<head>
+ <title>Test for DOM Worker Threads (Bug 437152)</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=437152">DOM Worker Threads Bug 437152</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+ const numThreads = 5;
+ var doneThreads = 0;
+
+ function onmessage(event) {
+ switch (event.data) {
+ case "done":
+ if (++doneThreads == numThreads) {
+ ok(true, "All messages received from workers");
+ SimpleTest.finish();
+ }
+ break;
+ default:
+ ok(false, "Unexpected message");
+ SimpleTest.finish();
+ }
+ }
+
+ function onerror(event) {
+ ok(false, "Worker had an error");
+ SimpleTest.finish();
+ }
+
+ for (var i = 0; i < numThreads; i++) {
+ var worker = new Worker("longThread_worker.js");
+ worker.onmessage = onmessage;
+ worker.onerror = onerror;
+ worker.postMessage("start");
+ }
+
+ SimpleTest.waitForExplicitFinish();
+
+</script>
+</pre>
+</body>
+</html>
+
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>Test for SharedWorker</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css">
+ <script class="testbody" type="text/javascript;version=1.7">
+ "use strict";
+
+ const basePath =
+ location.pathname.substring(0,
+ location.pathname.lastIndexOf("/") + 1);
+ const baseURL = location.origin + basePath;
+
+ const frameRelativeURL = "multi_sharedWorker_frame.html";
+ const frameAbsoluteURL = baseURL + frameRelativeURL;
+ const workerAbsoluteURL =
+ baseURL + "multi_sharedWorker_sharedWorker.js";
+
+ const storedData = "0123456789abcdefghijklmnopqrstuvwxyz";
+ const errorMessage = "Error: Expected";
+ const errorLineno = 34;
+
+ let testGenerator = (function() {
+ SimpleTest.waitForExplicitFinish();
+
+ window.addEventListener("message", function(event) {
+ if (typeof(event.data) == "string") {
+ info(event.data);
+ } else {
+ sendToGenerator(event);
+ }
+ });
+
+ let frame1 = document.getElementById("frame1");
+ frame1.src = frameRelativeURL;
+ frame1.onload = sendToGenerator;
+
+ yield undefined;
+
+ frame1 = frame1.contentWindow;
+
+ let frame2 = document.getElementById("frame2");
+ frame2.src = frameAbsoluteURL;
+ frame2.onload = sendToGenerator;
+
+ yield undefined;
+
+ frame2 = frame2.contentWindow;
+
+ let data = {
+ command: "start"
+ };
+
+ frame1.postMessage(data, "*");
+ frame2.postMessage(data, "*");
+
+ let event = yield undefined;
+ ok(event instanceof MessageEvent, "Got a MessageEvent");
+ is(event.source, frame1, "First window got the event");
+ is(event.data.type, "connect", "Got a connect message");
+
+ data = {
+ command: "retrieve"
+ };
+ frame1.postMessage(data, "*");
+
+ event = yield undefined;
+ ok(event instanceof MessageEvent, "Got a MessageEvent");
+ is(event.source, frame1, "First window got the event");
+ is(event.data.type, "result", "Got a result message");
+ is(event.data.data, undefined, "No data stored yet");
+
+ frame2.postMessage(data, "*");
+
+ event = yield undefined;
+ ok(event instanceof MessageEvent, "Got a MessageEvent");
+ is(event.source, frame2, "Second window got the event");
+ is(event.data.type, "result", "Got a result message");
+ is(event.data.data, undefined, "No data stored yet");
+
+ data = {
+ command: "store",
+ data: storedData
+ };
+ frame2.postMessage(data, "*");
+
+ data = {
+ command: "retrieve"
+ };
+ frame1.postMessage(data, "*");
+
+ event = yield undefined;
+ ok(event instanceof MessageEvent, "Got a MessageEvent");
+ is(event.source, frame1, "First window got the event");
+ is(event.data.type, "result", "Got a result message");
+ is(event.data.data, storedData, "Got stored data");
+
+ // This will generate two MessageEvents, one for each window.
+ let sawFrame1Error = false;
+ let sawFrame2Error = false;
+
+ data = {
+ command: "error"
+ };
+ frame1.postMessage(data, "*");
+
+ // First event.
+ event = yield undefined;
+
+ ok(event instanceof MessageEvent, "Got a MessageEvent");
+ is(event.data.type, "worker-error", "Got an error message");
+ is(event.data.message, errorMessage, "Got correct error message");
+ is(event.data.filename, workerAbsoluteURL, "Got correct filename");
+ is(event.data.lineno, errorLineno, "Got correct lineno");
+ if (event.source == frame1) {
+ is(sawFrame1Error, false, "Haven't seen error for frame1 yet");
+ sawFrame1Error = true;
+ } else if (event.source == frame2) {
+ is(sawFrame2Error, false, "Haven't seen error for frame1 yet");
+ sawFrame2Error = true;
+ } else {
+ ok(false, "Saw error from unknown window");
+ }
+
+ // Second event
+ event = yield undefined;
+
+ ok(event instanceof MessageEvent, "Got a MessageEvent");
+ is(event.data.type, "worker-error", "Got an error message");
+ is(event.data.message, errorMessage, "Got correct error message");
+ is(event.data.filename, workerAbsoluteURL, "Got correct filename");
+ is(event.data.lineno, errorLineno, "Got correct lineno");
+ if (event.source == frame1) {
+ is(sawFrame1Error, false, "Haven't seen error for frame1 yet");
+ sawFrame1Error = true;
+ } else if (event.source == frame2) {
+ is(sawFrame2Error, false, "Haven't seen error for frame1 yet");
+ sawFrame2Error = true;
+ } else {
+ ok(false, "Saw error from unknown window");
+ }
+
+ is(sawFrame1Error, true, "Saw error for frame1");
+ is(sawFrame2Error, true, "Saw error for frame2");
+
+ // This will generate two MessageEvents, one for each window.
+ sawFrame1Error = false;
+ sawFrame2Error = false;
+
+ data = {
+ command: "error"
+ };
+ frame1.postMessage(data, "*");
+
+ // First event.
+ event = yield undefined;
+
+ ok(event instanceof MessageEvent, "Got a MessageEvent");
+ is(event.data.type, "error", "Got an error message");
+ is(event.data.message, errorMessage, "Got correct error message");
+ is(event.data.filename, workerAbsoluteURL, "Got correct filename");
+ is(event.data.lineno, errorLineno, "Got correct lineno");
+ is(event.data.isErrorEvent, true, "Frame got an ErrorEvent");
+ if (event.source == frame1) {
+ is(sawFrame1Error, false, "Haven't seen error for frame1 yet");
+ sawFrame1Error = true;
+ } else if (event.source == frame2) {
+ is(sawFrame2Error, false, "Haven't seen error for frame1 yet");
+ sawFrame2Error = true;
+ } else {
+ ok(false, "Saw error from unknown window");
+ }
+
+ // Second event
+ event = yield undefined;
+
+ ok(event instanceof MessageEvent, "Got a MessageEvent");
+ is(event.data.type, "error", "Got an error message");
+ is(event.data.message, errorMessage, "Got correct error message");
+ is(event.data.filename, workerAbsoluteURL, "Got correct filename");
+ is(event.data.lineno, errorLineno, "Got correct lineno");
+ is(event.data.isErrorEvent, true, "Frame got an ErrorEvent");
+ if (event.source == frame1) {
+ is(sawFrame1Error, false, "Haven't seen error for frame1 yet");
+ sawFrame1Error = true;
+ } else if (event.source == frame2) {
+ is(sawFrame2Error, false, "Haven't seen error for frame1 yet");
+ sawFrame2Error = true;
+ } else {
+ ok(false, "Saw error from unknown window");
+ }
+
+ is(sawFrame1Error, true, "Saw error for frame1");
+ is(sawFrame2Error, true, "Saw error for frame2");
+
+ // Try a shared worker in a different origin.
+ frame1 = document.getElementById("frame1");
+ frame1.src = "http://example.org" + basePath + frameRelativeURL;
+ frame1.onload = sendToGenerator;
+ yield undefined;
+
+ frame1 = frame1.contentWindow;
+
+ data = {
+ command: "retrieve"
+ };
+ frame1.postMessage(data, "*");
+
+ event = yield undefined;
+ ok(event instanceof MessageEvent, "Got a MessageEvent");
+ is(event.source, frame1, "First window got the event");
+ is(event.data.type, "result", "Got a result message");
+ is(event.data.data, undefined, "No data stored yet");
+
+ frame2.postMessage(data, "*");
+
+ event = yield undefined;
+ ok(event instanceof MessageEvent, "Got a MessageEvent");
+ is(event.source, frame2, "First window got the event");
+ is(event.data.type, "result", "Got a result message");
+ is(event.data.data, storedData, "Got stored data");
+
+ window.removeEventListener("message", sendToGenerator);
+
+ SimpleTest.finish();
+ yield undefined;
+ })();
+
+ let sendToGenerator = testGenerator.send.bind(testGenerator);
+
+ </script>
+ </head>
+ <body onload="testGenerator.next();">
+ <iframe id="frame1"></iframe>
+ <iframe id="frame2"></iframe>
+ </body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>Test for SharedWorker</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css">
+ <script class="testbody" type="text/javascript;version=1.7">
+ "use strict";
+
+ const scrollbarPref = "layout.testing.overlay-scrollbars.always-visible";
+ const bfCacheEnabledPref = "browser.sessionhistory.cache_subframes";
+ const bfCacheDepthPref = "browser.sessionhistory.max_total_viewers";
+ const bfCacheDepth = 10;
+
+ const frameRelativeURL = "multi_sharedWorker_frame.html";
+ const storedData = "0123456789abcdefghijklmnopqrstuvwxyz";
+
+ let testGenerator = (function() {
+ SimpleTest.waitForExplicitFinish();
+
+ // Force scrollbar to always be shown. The scrollbar setting is
+ // necessary to avoid the fade-in/fade-out from evicting our document
+ // from the BF cache below. If bug 1049277 is fixed, then we can
+ // stop setting the scrollbar pref here.
+ SpecialPowers.pushPrefEnv({ set: [[scrollbarPref, true]] },
+ sendToGenerator);
+ yield undefined;
+
+ window.addEventListener("message", function(event) {
+ if (typeof(event.data) == "string") {
+ info(event.data);
+ } else {
+ sendToGenerator(event);
+ }
+ });
+
+ let frame = document.getElementById("frame");
+ frame.src = frameRelativeURL;
+ frame.onload = sendToGenerator;
+
+ yield undefined;
+
+ frame = frame.contentWindow;
+ frame.postMessage({ command: "retrieve" }, "*");
+
+ let event = yield undefined;
+ ok(event instanceof MessageEvent, "Got a MessageEvent");
+ is(event.source, frame, "Correct window got the event");
+ is(event.data.type, "result", "Got a result message");
+ is(event.data.data, undefined, "No data stored yet");
+
+ frame.postMessage({ command: "store", data: storedData }, "*");
+ frame.postMessage({ command: "retrieve" }, "*");
+
+ event = yield undefined;
+ ok(event instanceof MessageEvent, "Got a MessageEvent");
+ is(event.source, frame, "Correct window got the event");
+ is(event.data.type, "result", "Got a result message");
+ is(event.data.data, storedData, "Got stored data");
+
+ // Navigate when the bfcache is disabled.
+ info("Navigating to about:blank");
+ frame = document.getElementById("frame");
+ frame.onload = sendToGenerator;
+ frame.src = "about:blank";
+ frame.contentWindow.document.body.offsetTop;
+
+ yield undefined;
+
+ info("Navigating to " + frameRelativeURL);
+ frame.src = frameRelativeURL;
+ frame.contentWindow.document.body.offsetTop;
+
+ yield undefined;
+
+ frame = frame.contentWindow;
+ frame.postMessage({ command: "retrieve" }, "*");
+
+ event = yield undefined;
+ ok(event instanceof MessageEvent, "Got a MessageEvent");
+ is(event.source, frame, "Correct window got the event");
+ is(event.data.type, "result", "Got a result message");
+ is(event.data.data, undefined, "No data stored");
+
+ frame.postMessage({ command: "store", data: storedData }, "*");
+ frame.postMessage({ command: "retrieve" }, "*");
+
+ event = yield undefined;
+ ok(event instanceof MessageEvent, "Got a MessageEvent");
+ is(event.source, frame, "Correct window got the event");
+ is(event.data.type, "result", "Got a result message");
+ is(event.data.data, storedData, "Got stored data");
+
+ info("Enabling '" + bfCacheEnabledPref + "' pref");
+ SpecialPowers.pushPrefEnv({ set: [[bfCacheEnabledPref, true],
+ [bfCacheDepthPref, bfCacheDepth]] },
+ sendToGenerator);
+ yield undefined;
+
+ // Navigate when the bfcache is enabled.
+ frame = document.getElementById("frame");
+ frame.onload = sendToGenerator;
+
+ info("Navigating to about:blank");
+ frame.src = "about:blank";
+ frame.contentWindow.document.body.offsetTop;
+
+ yield undefined;
+
+ for (let i = 0; i < 3; i++) {
+ info("Running GC");
+ SpecialPowers.exactGC(sendToGenerator);
+ yield undefined;
+
+ info("Waiting the event queue to clear");
+ SpecialPowers.executeSoon(sendToGenerator);
+ yield undefined;
+ }
+
+ info("Navigating to " + frameRelativeURL);
+ frame.src = frameRelativeURL;
+ frame.contentWindow.document.body.offsetTop;
+
+ yield undefined;
+
+ frame = frame.contentWindow;
+ frame.postMessage({ command: "retrieve" }, "*");
+
+ event = yield undefined;
+ ok(event instanceof MessageEvent, "Got a MessageEvent");
+ is(event.source, frame, "Correct window got the event");
+ is(event.data.type, "result", "Got a result message");
+ is(event.data.data, storedData, "Still have data stored");
+
+ info("Resetting '" + bfCacheEnabledPref + "' pref");
+ SpecialPowers.popPrefEnv(sendToGenerator);
+ yield undefined;
+
+ window.removeEventListener("message", sendToGenerator);
+
+ SimpleTest.finish();
+ yield undefined;
+ })();
+
+ let sendToGenerator = testGenerator.send.bind(testGenerator);
+
+ </script>
+ </head>
+ <body onload="testGenerator.next();">
+ <iframe id="frame"></iframe>
+ </body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<!--
+Tests of DOM Worker Navigator
+-->
+<head>
+ <title>Test for DOM Worker Navigator</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" language="javascript">
+
+ var worker = new Worker("navigator_worker.js");
+
+ worker.onmessage = function(event) {
+ var args = JSON.parse(event.data);
+
+ if (args.name == "testFinished") {
+ SimpleTest.finish();
+ return;
+ }
+
+ if (typeof navigator[args.name] == "undefined") {
+ ok(false, "Navigator has no '" + args.name + "' property!");
+ return;
+ }
+
+ if (args.name === "languages") {
+ is(navigator.languages.toString(), args.value.toString(), "languages matches");
+ return;
+ }
+
+ if (args.name === "storage") {
+ is(typeof navigator.storage, typeof args.value, "storage type matches");
+ return;
+ }
+
+ is(navigator[args.name], args.value,
+ "Mismatched navigator string for " + args.name + "!");
+ };
+
+ worker.onerror = function(event) {
+ ok(false, "Worker had an error: " + event.message);
+ SimpleTest.finish();
+ }
+
+ var version = SpecialPowers.Cc["@mozilla.org/xre/app-info;1"].getService(SpecialPowers.Ci.nsIXULAppInfo).version;
+ var isNightly = version.endsWith("a1");
+ var isRelease = !version.includes("a");
+
+ worker.postMessage({ isNightly, isRelease });
+
+ SimpleTest.waitForExplicitFinish();
+
+</script>
+</pre>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<!--
+Tests of DOM Worker Navigator
+-->
+<head>
+ <title>Test for DOM Worker Navigator.languages</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" language="javascript">
+
+ var tests = [ 'en,it', 'it,en,fr', '', 'en' ];
+ var expectedLanguages;
+ function runTests() {
+ if (!tests.length) {
+ worker.postMessage('finish');
+ SimpleTest.finish();
+ return;
+ }
+
+ expectedLanguages = tests.shift();
+ SpecialPowers.pushPrefEnv({"set": [["intl.accept_languages", expectedLanguages]]}, function() {
+ worker.postMessage(true);
+ });
+ }
+
+ SimpleTest.waitForExplicitFinish();
+
+ var worker = new Worker("navigator_languages_worker.js");
+
+ worker.onmessage = function(event) {
+ is(event.data.toString(), navigator.languages.toString(), "The languages mach.");
+ is(event.data.toString(), expectedLanguages, "This is the correct result.");
+ runTests();
+ }
+
+ runTests();
+
+</script>
+</pre>
+</body>
+</html>
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 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test for Navigator.hardwareConcurrency</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+ SimpleTest.waitForExplicitFinish();
+ var script = "postMessage(navigator.hardwareConcurrency)";
+ var url = URL.createObjectURL(new Blob([script]));
+ var w = new Worker(url);
+ w.onmessage = function(e) {
+ var x = e.data;
+ is(typeof x, "number", "hardwareConcurrency should be a number.");
+ ok(x > 0, "hardwareConcurrency should be greater than 0.");
+ SimpleTest.finish();
+ }
+ </script>
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html>
+<head>
+ <title>Test for DOM Worker Threads</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+ var worker = new Worker("newError_worker.js");
+
+ worker.onmessage = function(event) {
+ ok(false, "Shouldn't get a message!");
+ SimpleTest.finish();
+ }
+
+ worker.onerror = function(event) {
+ is(event.message, "Error: foo!", "Got wrong error message!");
+ event.preventDefault();
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+
+</script>
+</pre>
+</body>
+</html>
+
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 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=916893
+-->
+<head>
+ <title>Bug 916893</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="/tests/dom/tests/mochitest/notification/MockServices.js"></script>
+ <script type="text/javascript" src="/tests/dom/tests/mochitest/notification/NotificationTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=916893">Bug 916893</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+</pre>
+<script type="text/javascript">
+ SimpleTest.requestFlakyTimeout("Mock alert service dispatches show and click events.");
+
+ function runTest() {
+ MockServices.register();
+ var w = new Worker("notification_worker.js");
+ w.onmessage = function(e) {
+ if (e.data.type === 'finish') {
+ MockServices.unregister();
+ SimpleTest.finish();
+ } else if (e.data.type === 'ok') {
+ ok(e.data.test, e.data.message);
+ } else if (e.data.type === 'is') {
+ is(e.data.test1, e.data.test2, e.data.message);
+ }
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ // turn on testing pref (used by notification.cpp, and mock the alerts
+ SpecialPowers.setBoolPref("notification.prompt.testing", true);
+ w.postMessage('start')
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ SpecialPowers.pushPrefEnv(
+ {"set": [["dom.webnotifications.workers.enabled", true]]},
+ runTest
+ );
+</script>
+</body>
+</html>
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 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=916893
+-->
+<head>
+ <title>Bug 916893 - Test Notifications in child workers.</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="/tests/dom/tests/mochitest/notification/MockServices.js"></script>
+ <script type="text/javascript" src="/tests/dom/tests/mochitest/notification/NotificationTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=916893">Bug 916893</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+</pre>
+<script type="text/javascript">
+ SimpleTest.requestFlakyTimeout("Mock alert service dispatches show event.");
+ function runTest() {
+ MockServices.register();
+ var w = new Worker("notification_worker_child-parent.js");
+ w.onmessage = function(e) {
+ if (e.data.type === 'finish') {
+ MockServices.unregister();
+ SimpleTest.finish();
+ } else if (e.data.type === 'ok') {
+ ok(e.data.test, e.data.message);
+ } else if (e.data.type === 'is') {
+ is(e.data.test1, e.data.test2, e.data.message);
+ }
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ // turn on testing pref (used by notification.cpp, and mock the alerts
+ SpecialPowers.setBoolPref("notification.prompt.testing", true);
+ w.postMessage('start')
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ SpecialPowers.pushPrefEnv(
+ {"set": [["dom.webnotifications.workers.enabled", true]]},
+ runTest
+ );
+</script>
+</body>
+</html>
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 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=916893
+-->
+<head>
+ <title>Bug 916893 - Make sure error is fired on Notification if permission is denied.</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="/tests/dom/tests/mochitest/notification/MockServices.js"></script>
+ <script type="text/javascript" src="/tests/dom/tests/mochitest/notification/NotificationTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=916893">Bug 916893</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+</pre>
+<script type="text/javascript">
+ SimpleTest.requestFlakyTimeout("Mock alert service dispatches show event.");
+ function runTest() {
+ MockServices.register();
+ var w = new Worker("notification_permission_worker.js");
+ w.onmessage = function(e) {
+ if (e.data.type === 'finish') {
+ SpecialPowers.setBoolPref("notification.prompt.testing.allow", true);
+ MockServices.unregister();
+ SimpleTest.finish();
+ } else if (e.data.type === 'ok') {
+ ok(e.data.test, e.data.message);
+ } else if (e.data.type === 'is') {
+ is(e.data.test1, e.data.test2, e.data.message);
+ }
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ // turn on testing pref (used by notification.cpp, and mock the alerts
+ SpecialPowers.setBoolPref("notification.prompt.testing", true);
+ SpecialPowers.setBoolPref("notification.prompt.testing.allow", false);
+ w.postMessage('start')
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ SpecialPowers.pushPrefEnv(
+ {"set": [["dom.webnotifications.workers.enabled", true]]},
+ runTest
+ );
+</script>
+</body>
+</html>
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 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+Bug 925437: online/offline events tests.
+
+Any copyright is dedicated to the Public Domain.
+http://creativecommons.org/licenses/publicdomain/
+-->
+<head>
+ <title>Test for Bug 925437 (worker online/offline events)</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=925437">Mozilla Bug 925437</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+
+<script class="testbody" type="text/javascript">
+
+addLoadEvent(function() {
+ var w = new Worker("onLine_worker.js");
+
+ w.onmessage = function(e) {
+ if (e.data.type === 'ready') {
+ doTest();
+ } else if (e.data.type === 'ok') {
+ ok(e.data.test, e.data.message);
+ } else if (e.data.type === 'finished') {
+ SimpleTest.finish();
+ }
+ }
+
+ function doTest() {
+ var iosvc = SpecialPowers.Cc["@mozilla.org/network/io-service;1"]
+ .getService(SpecialPowers.Ci.nsIIOService2);
+ iosvc.manageOfflineStatus = false;
+
+ info("setting iosvc.offline = true");
+ iosvc.offline = true;
+
+ info("setting iosvc.offline = false");
+ iosvc.offline = false;
+
+ info("setting iosvc.offline = true");
+ iosvc.offline = true;
+
+ for (var i = 0; i < 10; ++i) {
+ iosvc.offline = !iosvc.offline;
+ }
+
+ info("setting iosvc.offline = false");
+ w.postMessage('lastTest');
+ iosvc.offline = false;
+ }
+});
+
+SimpleTest.waitForExplicitFinish();
+</script>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for Promise object in workers</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+<script class="testbody" type="text/javascript">
+
+ function runTest() {
+ var worker = new Worker("promise_worker.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);
+ }
+ }
+
+ worker.onerror = function(event) {
+ ok(false, "Worker had an error: " + event.message);
+ SimpleTest.finish();
+ };
+
+ worker.postMessage(true);
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ runTest();
+</script>
+</pre>
+</body>
+</html>
+
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 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1027221
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1027221</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+ /** Test for Bug 1027221 **/
+ // Set up a permanent atom
+ SimpleTest.waitForExplicitFinish();
+ var x = "x";
+ // Trigger some incremental gc
+ SpecialPowers.Cu.getJSTestingFunctions().gcslice(1);
+
+ // Kick off a worker that uses this same atom
+ var w = new Worker("data:text/plain,Promise.resolve('x').then(function() { postMessage(1); });");
+ // Maybe trigger some more incremental gc
+ SpecialPowers.Cu.getJSTestingFunctions().gcslice(1);
+
+ w.onmessage = function() {
+ ok(true, "Got here");
+ SimpleTest.finish();
+ };
+
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1027221">Mozilla Bug 1027221</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<!--
+Tests of DOM Worker Threads
+-->
+<head>
+ <title>Test for DOM Worker Threads Recursion</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+ // Intermittently triggers one assertion on Mac (bug 848098).
+ if (navigator.platform.indexOf("Mac") == 0) {
+ SimpleTest.expectAssertions(0, 1);
+ }
+
+ const testCount = 2;
+ var errorCount = 0;
+
+ var worker = new Worker("recursion_worker.js");
+
+ function done() {
+ worker.terminate();
+ SimpleTest.finish();
+ }
+
+ worker.onmessage = function(event) {
+ if (event.data == "Done") {
+ ok(true, "correct message");
+ }
+ else {
+ ok(false, "Bad message: " + event.data);
+ }
+ done();
+ }
+
+ worker.onerror = function(event) {
+ event.preventDefault();
+ if (event.message == "too much recursion") {
+ ok(true, "got correct error message");
+ ++errorCount;
+ }
+ else {
+ ok(false, "got bad error message: " + event.message);
+ done();
+ }
+ }
+
+ for (var i = 0; i < testCount; i++) {
+ worker.postMessage("");
+ }
+
+ SimpleTest.waitForExplicitFinish();
+
+</script>
+</pre>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js">
+ </script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ </head>
+ <body>
+ <script type="text/javascript">
+ const filename = "http://mochi.test:8888/tests/dom/workers/test/" +
+ "recursiveOnerror_worker.js";
+ const errors = [
+ { message: "Error: 2", lineno: 6 },
+ { message: "Error: 1", lineno: 10 }
+ ]
+
+ var errorCount = 0;
+
+ var worker = new Worker("recursiveOnerror_worker.js");
+ worker.postMessage("go");
+
+ worker.onerror = function(event) {
+ event.preventDefault();
+
+ ok(errorCount < errors.length, "Correct number of error events");
+ const error = errors[errorCount++];
+
+ is(event.message, error.message, "Correct message");
+ is(event.filename, filename, "Correct filename");
+ is(event.lineno, error.lineno, "Correct lineno");
+
+ if (errorCount == errors.length) {
+ SimpleTest.finish();
+ }
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ </script>
+ </body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test the referrer of workers</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+<script class="testbody" type="text/javascript">
+
+ function test_mainScript() {
+ var worker = new Worker("referrer.sjs?worker");
+ worker.onmessage = function() {
+ var xhr = new XMLHttpRequest();
+ xhr.open('GET', 'referrer.sjs?result', true);
+ xhr.onload = function() {
+ is(xhr.responseText, location.href, "The referrer has been sent.");
+ next();
+ }
+ xhr.send();
+ }
+ worker.postMessage(42);
+ }
+
+ function test_importScript() {
+ var worker = new Worker("worker_referrer.js");
+ worker.onmessage = function(e) {
+ is(e.data, location.href.replace("test_referrer.html", "worker_referrer.js"), "The referrer has been sent.");
+ next();
+ }
+ worker.postMessage(42);
+ }
+
+ var tests = [ test_mainScript, test_importScript ];
+ function next() {
+ if (!tests.length) {
+ SimpleTest.finish();
+ return;
+ }
+
+ var test = tests.shift();
+ test();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ next();
+
+</script>
+</pre>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test the referrer of workers</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+ <script class="testbody" type="text/javascript">
+ SimpleTest.waitForExplicitFinish();
+
+ SpecialPowers.pushPrefEnv(
+ {"set": [
+ ['security.mixed_content.block_display_content', false],
+ ['security.mixed_content.block_active_content', false]
+ ]},
+ function() {
+ SpecialPowers.pushPermissions([{'type': 'systemXHR', 'allow': true, 'context': document}], test);
+ });
+
+ function test() {
+ function messageListener(event) {
+ eval(event.data);
+ }
+ window.addEventListener("message", messageListener, false);
+
+ var ifr = document.createElement('iframe');
+ ifr.setAttribute('src', 'https://example.com/tests/dom/workers/test/referrer_worker.html');
+ document.body.appendChild(ifr);
+ }
+ </script>
+</body>
+</html>
+
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE html>
+<html>
+ <head>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ </head>
+ <body>
+ <script type="application/javascript">
+ window.Worker = 17; // resolve through assignment
+
+ var desc = Object.getOwnPropertyDescriptor(window, "Worker");
+ ok(typeof desc === "object" && desc !== null, "Worker property must exist");
+
+ is(desc.value, 17, "Overwrite didn't work correctly");
+ is(desc.enumerable, false,
+ "Initial descriptor was non-enumerable, and [[Put]] changes the " +
+ "property value but not its enumerability");
+ is(desc.configurable, true,
+ "Initial descriptor was configurable, and [[Put]] changes the " +
+ "property value but not its configurability");
+ is(desc.writable, true,
+ "Initial descriptor was writable, and [[Put]] changes the " +
+ "property value but not its writability");
+ </script>
+ </body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE html>
+<html>
+ <head>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ </head>
+ <body>
+ <script type="application/javascript">
+ window.Worker; // resolve not through assignment
+ Worker = 17;
+
+ var desc = Object.getOwnPropertyDescriptor(window, "Worker");
+ ok(typeof desc === "object" && desc !== null, "Worker property must exist");
+
+ is(desc.value, 17, "Overwrite didn't work correctly");
+ is(desc.enumerable, false,
+ "Initial descriptor was non-enumerable, and [[Put]] changes the " +
+ "property value but not its enumerability");
+ is(desc.configurable, true,
+ "Initial descriptor was configurable, and [[Put]] changes the " +
+ "property value but not its configurability");
+ is(desc.writable, true,
+ "Initial descriptor was writable, and [[Put]] changes the " +
+ "property value but not its writability");
+ </script>
+ </body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for bug 911085</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<script class="testbody" type="text/javascript">
+
+ var worker = new Worker("rvals_worker.js");
+
+ worker.onmessage = function(event) {
+ if (event.data == 'ignore') return;
+
+ if (event.data == 'finished') {
+ is(worker.terminate(), undefined, "Terminate() returns 'undefined'");
+ SimpleTest.finish();
+ return;
+ }
+
+ ok(event.data, "something good returns 'undefined' in workers");
+ };
+
+ is(worker.postMessage(42), undefined, "PostMessage() returns 'undefined' on main thread");
+ SimpleTest.waitForExplicitFinish();
+
+</script>
+</pre>
+</body>
+</html>
+
+
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 @@
+<html>
+<head>
+ <title>Test for DOM Worker setTimeout and strings containing 0</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<script>
+
+var a = new Worker('worker_setTimeoutWith0.js');
+a.onmessage = function(e) {
+ is(e.data, 2, "We want to see 2 here");
+ SimpleTest.finish();
+}
+
+SimpleTest.waitForExplicitFinish();
+</script>
+</body>
+</html>
+
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>Test for SharedWorker</title>
+ <script src="/tests/SimpleTest/SimpleTest.js">
+ </script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css">
+ </head>
+ <body>
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test">
+ <script class="testbody">
+ "use strict";
+
+ const href = window.location.href;
+ const filename = "sharedWorker_sharedWorker.js";
+ const sentMessage = "ping";
+ const errorFilename = href.substring(0, href.lastIndexOf("/") + 1) +
+ filename;
+ const errorLine = 91;
+ const errorColumn = 0;
+
+ var worker = new SharedWorker(filename);
+
+ ok(worker instanceof SharedWorker, "Got SharedWorker instance");
+ ok(!("postMessage" in worker), "SharedWorker has no 'postMessage'");
+ ok(worker.port instanceof MessagePort,
+ "Shared worker has MessagePort");
+
+ var receivedMessage;
+ var receivedError;
+
+ worker.port.onmessage = function(event) {
+ ok(event instanceof MessageEvent, "Got a MessageEvent");
+ ok(event.target === worker.port,
+ "MessageEvent has correct 'target' property");
+ is(event.data, sentMessage, "Got correct message");
+ ok(receivedMessage === undefined, "Haven't gotten message yet");
+ receivedMessage = event.data;
+ if (receivedError) {
+ SimpleTest.finish();
+ }
+ };
+
+ worker.onerror = function(event) {
+ ok(event instanceof ErrorEvent, "Got an ErrorEvent");
+ is(event.message, "Error: " + sentMessage, "Got correct error");
+ is(event.filename, errorFilename, "Got correct filename");
+ is(event.lineno, errorLine, "Got correct lineno");
+ is(event.colno, errorColumn, "Got correct column");
+ ok(receivedError === undefined, "Haven't gotten error yet");
+ receivedError = event.message;
+ event.preventDefault();
+ if (receivedMessage) {
+ SimpleTest.finish();
+ }
+ };
+
+ worker.port.postMessage(sentMessage);
+
+ SimpleTest.waitForExplicitFinish();
+
+ </script>
+ </pre>
+ </body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>Test for MessagePort and SharedWorkers</title>
+ <meta http-equiv="content-type" content="text/html; charset=UTF-8">
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ </head>
+ <body>
+ <script class="testbody" type="text/javascript">
+
+var gced = false;
+
+var sw = new SharedWorker('sharedWorker_lifetime.js');
+sw.port.onmessage = function(event) {
+ ok(gced, "The SW is still alive also after GC");
+ SimpleTest.finish();
+}
+
+sw = null;
+SpecialPowers.forceGC();
+gced = true;
+
+SimpleTest.waitForExplicitFinish();
+ </script>
+ </body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>Test for MessagePort and SharedWorkers</title>
+ <meta http-equiv="content-type" content="text/html; charset=UTF-8">
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ </head>
+ <body>
+ <script class="testbody" type="text/javascript">
+
+var sw1 = new SharedWorker('sharedWorker_ports.js');
+sw1.port.onmessage = function(event) {
+ if (event.data.type == "connected") {
+ ok(true, "The SharedWorker is alive.");
+
+ var sw2 = new SharedWorker('sharedWorker_ports.js');
+ sw1.port.postMessage("Port from the main-thread!", [sw2.port]);
+ return;
+ }
+
+ if (event.data.type == "status") {
+ ok(event.data.test, event.data.msg);
+ return;
+ }
+
+ if (event.data.type == "finish") {
+ info("Finished!");
+ ok(sw1.port, "The port still exists");
+ sw1.port.foo = sw1; // Just a test to see if we leak.
+ SimpleTest.finish();
+ }
+}
+
+SimpleTest.waitForExplicitFinish();
+ </script>
+ </body>
+</html>
+
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 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+ <title>Test for SharedWorker - Private Browsing</title>
+ <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
+</head>
+<body>
+
+<script type="application/javascript">
+
+const Ci = Components.interfaces;
+var mainWindow;
+
+var contentPage = "http://mochi.test:8888/chrome/dom/workers/test/empty.html";
+
+function testOnWindow(aIsPrivate, aCallback) {
+ var win = mainWindow.OpenBrowserWindow({private: aIsPrivate});
+ win.addEventListener("load", function onLoad() {
+ win.removeEventListener("load", onLoad, false);
+ win.addEventListener("DOMContentLoaded", function onInnerLoad() {
+ if (win.content.location.href != contentPage) {
+ win.gBrowser.loadURI(contentPage);
+ return;
+ }
+
+ win.removeEventListener("DOMContentLoaded", onInnerLoad, true);
+ SimpleTest.executeSoon(function() { aCallback(win); });
+ }, true);
+
+ if (!aIsPrivate) {
+ win.gBrowser.loadURI(contentPage);
+ }
+ }, true);
+}
+
+function setupWindow() {
+ mainWindow = window.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIWebNavigation)
+ .QueryInterface(Ci.nsIDocShellTreeItem)
+ .rootTreeItem
+ .QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIDOMWindow);
+ runTest();
+}
+
+var wN;
+var wP;
+
+function doTests() {
+ testOnWindow(false, function(aWin) {
+ wN = aWin;
+
+ testOnWindow(true, function(aWin) {
+ wP = aWin;
+
+ var sharedWorker1 = new wP.content.SharedWorker('sharedWorker_privateBrowsing.js');
+ sharedWorker1.port.onmessage = function(event) {
+ is(event.data, 1, "Only 1 sharedworker expected in the private window");
+
+ var sharedWorker2 = new wN.content.SharedWorker('sharedWorker_privateBrowsing.js');
+ sharedWorker2.port.onmessage = function(event) {
+ is(event.data, 1, "Only 1 sharedworker expected in the normal window");
+
+ var sharedWorker3 = new wP.content.SharedWorker('sharedWorker_privateBrowsing.js');
+ sharedWorker3.port.onmessage = function(event) {
+ is(event.data, 2, "Only 2 sharedworker expected in the private window");
+ runTest();
+ }
+ }
+ }
+ });
+ });
+}
+
+var steps = [
+ setupWindow,
+ doTests
+];
+
+function runTest() {
+ if (!steps.length) {
+ wN.close();
+ wP.close();
+
+ SimpleTest.finish();
+ return;
+ }
+
+ var step = steps.shift();
+ step();
+}
+
+SimpleTest.waitForExplicitFinish();
+SpecialPowers.pushPrefEnv({"set": [
+ ["browser.startup.page", 0],
+ ["browser.startup.homepage_override.mstone", "ignore"],
+]}, runTest);
+
+</script>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<!--
+Tests of DOM Worker Threads (Bug 437152)
+-->
+<head>
+ <title>Test for DOM Worker Threads (Bug 437152)</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=437152">DOM Worker Threads Bug 437152</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+ var worker = new Worker("simpleThread_worker.js");
+
+ worker.addEventListener("message",function(event) {
+ is(event.target, worker);
+ switch (event.data) {
+ case "no-op":
+ break;
+ case "started":
+ is(gotErrors, true);
+ worker.postMessage("no-op");
+ worker.postMessage("stop");
+ break;
+ case "stopped":
+ worker.postMessage("no-op");
+ SimpleTest.finish();
+ break;
+ default:
+ ok(false, "Unexpected message:" + event.data);
+ SimpleTest.finish();
+ }
+ }, false);
+
+ var gotErrors = false;
+ worker.onerror = function(event) {
+ event.preventDefault();
+ is(event.target, worker);
+ is(event.message, "uncaught exception: Bad message: asdf");
+
+ worker.onerror = function(otherEvent) {
+ otherEvent.preventDefault();
+ is(otherEvent.target, worker);
+ is(otherEvent.message, "ReferenceError: Components is not defined");
+ gotErrors = true;
+
+ worker.onerror = function(oneMoreEvent) {
+ ok(false, "Worker had an error:" + oneMoreEvent.message);
+ SimpleTest.finish();
+ };
+ };
+ };
+
+ worker.postMessage("asdf");
+ worker.postMessage("components");
+ worker.postMessage("start");
+
+ SimpleTest.waitForExplicitFinish();
+
+</script>
+</pre>
+</body>
+</html>
+
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 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test for sub workers+bfcache behavior</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+ <script type="application/javascript">
+
+ const WORKER_URL = "worker_suspended.js";
+ const SUB_WORKERS = 3
+
+ var testUrl1 = "window_suspended.html?page1Shown";
+ var testUrl2 = "window_suspended.html?page2Shown";
+
+ var testWin;
+ var counter = 0;
+
+ function cacheData() {
+ return caches.open("test")
+ .then(function(cache) {
+ return cache.match("http://mochi.test:888/foo");
+ })
+ .then(function(response) {
+ return response.text();
+ });
+ }
+
+ function page1Shown(e) {
+ info("Page1Shown: " + testWin.location.href);
+
+ // First time this page is shown.
+ if (counter == 0) {
+ ok(!e.persisted, "test page should have been persisted initially");
+
+ info("Create a worker and subworkers...");
+ let worker = new e.target.defaultView.Worker(WORKER_URL);
+
+ var promise = new Promise((resolve, reject) => {
+ info("Waiting until workers are ready...");
+ worker.addEventListener("message", function onmessage(e) {
+ is(e.data, "ready", "We want to receive: -ready-");
+ worker.removeEventListener("message", onmessage);
+ resolve();
+ });
+ worker.postMessage({ type: "page1", count: SUB_WORKERS });
+ });
+
+ promise.then(function() {
+ info("Retrieving data from cache...");
+ return cacheData();
+ })
+
+ .then(function(content) {
+ is(content.indexOf("page1-"), 0, "We have data from the worker");
+ })
+
+ .then(function() {
+ info("New location: " + testUrl2);
+ testWin.location.href = testUrl2;
+ });
+ } else {
+ is(e.persisted, true, "test page should have been persisted in pageshow");
+
+ var promise = new Promise((resolve, reject) => {
+ info("Waiting a few seconds...");
+ setTimeout(resolve, 5000);
+ });
+
+ promise.then(function() {
+ info("Retrieving data from cache...");
+ return cacheData();
+ })
+
+ .then(function(content) {
+ is(content.indexOf("page1-"), 0, "We have data from the worker");
+ })
+
+ .then(function() {
+ testWin.close();
+ SimpleTest.finish();
+ });
+ }
+
+ counter++;
+ }
+
+ function page2Shown(e) {
+ info("Page2Shown: " + testWin.location.href);
+
+ info("Create a worker...");
+ let worker = new e.target.defaultView.Worker(WORKER_URL);
+
+ var promise = new Promise((resolve, reject) => {
+ info("Waiting until workers are ready...");
+ worker.addEventListener("message", function onmessage(e) {
+ is(e.data, "ready", "We want to receive: -ready-");
+ worker.removeEventListener("message", onmessage);
+ resolve();
+ });
+ worker.postMessage({ type: "page2" });
+ });
+
+ promise.then(function() {
+ info("Retrieving data from cache...");
+ return cacheData();
+ })
+
+ .then(function(content) {
+ is(content, "page2-0", "We have data from the second worker");
+ })
+
+ .then(function() {
+ info("Going back");
+ testWin.history.back();
+ });
+ }
+
+ SpecialPowers.pushPrefEnv({ set: [
+ ["dom.caches.enabled", true],
+ ["dom.caches.testing.enabled", true],
+ ] },
+ function() {
+ testWin = window.open(testUrl1);
+ });
+
+ SimpleTest.waitForExplicitFinish();
+ SimpleTest.requestFlakyTimeout("untriaged");
+
+ </script>
+</body>
+</html>
+
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test for DOM Worker Threads</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test">
+<iframe id="workerFrame" src="suspend_iframe.html" onload="subframeLoaded();">
+</iframe>
+<script class="testbody" type="text/javascript">
+
+ SimpleTest.waitForExplicitFinish();
+
+ var iframe;
+ var lastCount;
+
+ var suspended = false;
+ var resumed = false;
+ var finished = false;
+
+ var interval;
+ var oldMessageCount;
+ var waitCount = 0;
+
+ function finishTest() {
+ if (finished) {
+ return;
+ }
+ finished = true;
+ SpecialPowers.flushPrefEnv(function () {
+ iframe.terminateWorker();
+ SimpleTest.finish();
+ });
+ }
+
+ function waitInterval() {
+ if (finished) {
+ return;
+ }
+ is(String(iframe.location), "about:blank", "Wrong url!");
+ is(suspended, true, "Not suspended?");
+ is(resumed, false, "Already resumed?!");
+ is(lastCount, oldMessageCount, "Received a message while suspended!");
+ if (++waitCount == 5) {
+ clearInterval(interval);
+ resumed = true;
+ iframe.history.back();
+ }
+ }
+
+ function badOnloadCallback() {
+ if (finished) {
+ return;
+ }
+ ok(false, "We don't want suspend_iframe.html to fire a new load event, we want it to come out of the bfcache!");
+ finishTest();
+ }
+
+ function suspendCallback() {
+ if (finished) {
+ return;
+ }
+ is(String(iframe.location), "about:blank", "Wrong url!");
+ is(suspended, false, "Already suspended?");
+ is(resumed, false, "Already resumed?");
+ SpecialPowers.popPrefEnv(function () {
+ suspended = true;
+ var iframeElement = document.getElementById("workerFrame");
+ iframeElement.onload = badOnloadCallback;
+ oldMessageCount = lastCount;
+ interval = setInterval(waitInterval, 1000);
+ });
+ }
+
+ function messageCallback(data) {
+ if (finished) {
+ return;
+ }
+
+ if (!suspended) {
+ ok(lastCount === undefined || lastCount == data - 1,
+ "Got good data, lastCount = " + lastCount + ", data = " + data);
+ lastCount = data;
+ if (lastCount == 25) {
+ SpecialPowers.pushPrefEnv({"set": [["browser.sessionhistory.cache_subframes", true]]}, function () {
+ iframe.location = "about:blank";
+ // We want suspend_iframe.html to go into bfcache, so we need to flush
+ // out all pending notifications. Otherwise, if they're flushed too
+ // late, they could kick us out of the bfcache again.
+ iframe.document.body.offsetTop;
+ });
+ }
+ return;
+ }
+
+ var newLocation =
+ window.location.toString().replace("test_suspend.html",
+ "suspend_iframe.html");
+ is(newLocation.indexOf(iframe.location.toString()), 0, "Wrong url!");
+ is(resumed, true, "Got message before resumed!");
+ is(lastCount, data - 1, "Missed a message, suspend failed!");
+ finishTest();
+ }
+
+ function errorCallback(data) {
+ if (finished) {
+ return;
+ }
+ ok(false, "Iframe had an error: '" + data + "'");
+ finishTest();
+ }
+
+ function subframeLoaded() {
+ if (finished) {
+ return;
+ }
+ var iframeElement = document.getElementById("workerFrame");
+ iframeElement.onload = suspendCallback;
+
+ iframe = iframeElement.contentWindow;
+ ok(iframe, "No iframe?!");
+
+ iframe.startWorker(messageCallback, errorCallback);
+ }
+
+</script>
+</pre>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<!--
+Tests of DOM Worker terminate feature
+-->
+<head>
+ <title>Test for DOM Worker Navigator</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" language="javascript">
+
+
+ var messageCount = 0;
+ var intervalCount = 0;
+
+ var interval;
+
+ var worker;
+
+ function messageListener(event) {
+ is(event.data, "Still alive!", "Correct message!");
+ if (++messageCount == 20) {
+ ok(worker.onmessage === messageListener,
+ "Correct listener before terminate");
+
+ worker.terminate();
+
+ var exception = false;
+ try {
+ worker.addEventListener("message", messageListener, false);
+ }
+ catch (e) {
+ exception = true;
+ }
+ is(exception, false, "addEventListener didn't throw after terminate");
+
+ exception = false;
+ try {
+ worker.removeEventListener("message", messageListener, false);
+ }
+ catch (e) {
+ exception = true;
+ }
+ is(exception, false, "removeEventListener didn't throw after terminate");
+
+ exception = false;
+ try {
+ worker.postMessage("foo");
+ }
+ catch (e) {
+ exception = true;
+ }
+ is(exception, false, "postMessage didn't throw after terminate");
+
+ exception = false;
+ try {
+ worker.terminate();
+ }
+ catch (e) {
+ exception = true;
+ }
+ is(exception, false, "terminate didn't throw after terminate");
+
+ ok(worker.onmessage === messageListener,
+ "Correct listener after terminate");
+
+ worker.onmessage = function(event) { }
+
+ interval = setInterval(testCount, 1000);
+ }
+ }
+
+ function testCount() {
+ is(messageCount, 20, "Received another message after terminated!");
+ if (intervalCount++ == 5) {
+ clearInterval(interval);
+ SimpleTest.finish();
+ }
+ }
+
+ worker = new Worker("terminate_worker.js");
+ worker.onmessage = messageListener;
+
+ SimpleTest.waitForExplicitFinish();
+
+</script>
+</pre>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<!--
+Tests of DOM Worker Threads (Bug 437152)
+-->
+<head>
+ <title>Test for DOM Worker Threads (Bug 437152)</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=437152">DOM Worker Threads Bug 437152</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+ const expectedErrorCount = 4;
+
+ function messageListener(event) {
+ ok(false, "Unexpected message: " + event.data);
+ SimpleTest.finish();
+ };
+
+ var actualErrorCount = 0;
+ var failedWorkers = [];
+
+ function errorListener(event) {
+ event.preventDefault();
+
+ if (failedWorkers.indexOf(event.target) != -1) {
+ ok(false, "Seen an extra error from this worker");
+ SimpleTest.finish();
+ return;
+ }
+
+ failedWorkers.push(event.target);
+ actualErrorCount++;
+
+ if (actualErrorCount == expectedErrorCount) {
+ ok(true, "all errors correctly detected");
+ SimpleTest.finish();
+ }
+ };
+
+ for (var i = 1; i <= expectedErrorCount; i++) {
+ var worker = new Worker("threadErrors_worker" + i + ".js");
+ worker.onmessage = messageListener;
+ worker.onerror = errorListener;
+ worker.postMessage("Hi");
+ }
+
+ SimpleTest.waitForExplicitFinish();
+
+</script>
+</pre>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<!--
+Tests of DOM Worker Threads (Bug 437152)
+-->
+<head>
+ <title>Test for DOM Worker Threads (Bug 437152)</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=437152">DOM Worker Threads Bug 437152</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+ var worker = new Worker("threadTimeouts_worker.js");
+
+ worker.onmessage = function(event) {
+ is(event.target, worker);
+ switch (event.data) {
+ case "timeoutFinished":
+ event.target.postMessage("startInterval");
+ break;
+ case "intervalFinished":
+ event.target.postMessage("cancelInterval");
+ break;
+ case "intervalCanceled":
+ worker.postMessage("startExpression");
+ break;
+ case "expressionFinished":
+ SimpleTest.finish();
+ break;
+ default:
+ ok(false, "Unexpected message");
+ SimpleTest.finish();
+ }
+ };
+
+ worker.onerror = function(event) {
+ is(event.target, worker);
+ ok(false, "Worker had an error: " + event.message);
+ SimpleTest.finish();
+ };
+
+ worker.postMessage("startTimeout");
+
+ SimpleTest.waitForExplicitFinish();
+
+</script>
+</pre>
+</body>
+</html>
+
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<!--
+Tests of DOM Worker Threads
+-->
+<head>
+ <title>Test for DOM Worker Threads Recursion</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+ var worker = new Worker("throwingOnerror_worker.js");
+
+ var errors = ["foo", "bar"];
+
+ worker.onerror = function(event) {
+ event.preventDefault();
+ var found = false;
+ for (var index in errors) {
+ if (event.message == "uncaught exception: " + errors[index]) {
+ errors.splice(index, 1);
+ found = true;
+ break;
+ }
+ }
+ is(found, true, "Unexpected error!");
+ };
+
+ worker.onmessage = function(event) {
+ is(errors.length, 0, "Didn't see expected errors!");
+ SimpleTest.finish();
+ };
+
+ for (var i = 0; i < 2; i++) {
+ worker.postMessage("");
+ }
+
+ SimpleTest.waitForExplicitFinish();
+
+</script>
+</pre>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<!--
+Tests of DOM Worker Threads
+-->
+<head>
+ <title>Test for DOM Worker Threads</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+ var worker = new Worker("timeoutTracing_worker.js");
+
+ worker.onmessage = function(event) {
+ // begin
+ worker.onmessage = null;
+
+ // 1 second should be enough to crash.
+ window.setTimeout(function(event) {
+ ok(true, "Didn't crash!");
+ SimpleTest.finish();
+ }, 1000);
+
+ var os = SpecialPowers.Cc["@mozilla.org/observer-service;1"]
+ .getService(SpecialPowers.Ci.nsIObserverService);
+ os.notifyObservers(null, "memory-pressure", "heap-minimize");
+ }
+
+ worker.onerror = function(event) {
+ ok(false, "I was expecting a crash, not an error");
+ SimpleTest.finish();
+ };
+
+ SimpleTest.waitForExplicitFinish();
+ SimpleTest.requestFlakyTimeout("untriaged");
+
+</script>
+</pre>
+</body>
+</html>
+
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<!--
+Tests of DOM Worker transferable objects
+-->
+<head>
+ <title>Test for DOM Worker transferable objects</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" language="javascript">
+
+ function test1(sizes) {
+ if (!sizes.length) {
+ runTests();
+ return;
+ }
+
+ var size = sizes.pop();
+
+ var worker = new Worker("transferable_worker.js");
+ worker.onmessage = function(event) {
+ ok(event.data.status, event.data.event);
+ if (!event.data.status) {
+ runTests();
+ return;
+ }
+
+ if ("notEmpty" in event.data && "byteLength" in event.data.notEmpty) {
+ ok(event.data.notEmpty.byteLength != 0,
+ "P: NotEmpty object received: " + event.data.notEmpty.byteLength);
+ }
+
+ if (!event.data.last)
+ return;
+
+ test1(sizes);
+ }
+ worker.onerror = function(event) {
+ ok(false, "No errors!");
+ }
+
+ try {
+ worker.postMessage(42, true);
+ ok(false, "P: PostMessage - Exception for wrong type");
+ } catch(e) {
+ ok(true, "P: PostMessage - Exception for wrong type");
+ }
+
+ try {
+ ab = new ArrayBuffer(size);
+ worker.postMessage(42,[ab, ab]);
+ ok(false, "P: PostMessage - Exception for duplicate");
+ } catch(e) {
+ ok(true, "P: PostMessage - Exception for duplicate");
+ }
+
+ var ab = new ArrayBuffer(size);
+ ok(ab.byteLength == size, "P: The size is: " + size + " == " + ab.byteLength);
+ worker.postMessage({ data: 0, timeout: 0, ab: ab, cb: ab, size: size }, [ab]);
+ ok(ab.byteLength == 0, "P: PostMessage - The size is: 0 == " + ab.byteLength)
+ }
+
+ function test2() {
+ var worker = new Worker("transferable_worker.js");
+ worker.onmessage = function(event) {
+ ok(event.data.status, event.data.event);
+ if (!event.data.status) {
+ runTests();
+ return;
+ }
+
+ if ("notEmpty" in event.data && "byteLength" in event.data.notEmpty) {
+ ok(event.data.notEmpty.byteLength != 0,
+ "P: NotEmpty object received: " + event.data.notEmpty.byteLength);
+ }
+
+ if (event.data.last) {
+ runTests();
+ }
+ }
+ worker.onerror = function(event) {
+ ok(false, "No errors!");
+ }
+
+ var f = new Float32Array([0,1,2,3]);
+ ok(f.byteLength != 0, "P: The size is: " + f.byteLength + " is not 0");
+ worker.postMessage({ event: "P: postMessage with Float32Array", status: true,
+ size: 4, notEmpty: f, bc: [ f, f, { dd: f } ] }, [f.buffer]);
+ ok(f.byteLength == 0, "P: The size is: " + f.byteLength + " is 0");
+ }
+
+ var tests = [
+ function() { test1([1024 * 1024 * 32, 128, 4]); },
+ test2
+ ];
+ function runTests() {
+ if (!tests.length) {
+ SimpleTest.finish();
+ return;
+ }
+
+ var test = tests.shift();
+ test();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ runTests();
+
+</script>
+</pre>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for bug 1090183</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+
+<script class="testbody" type="text/javascript">
+
+var sw = new SharedWorker('webSocket_sharedWorker.js');
+sw.port.onmessage = function(event) {
+ if (event.data.type == 'finish') {
+ SimpleTest.finish();
+ } else if (event.data.type == 'status') {
+ ok(event.data.status, event.data.msg);
+ }
+}
+
+SimpleTest.waitForExplicitFinish();
+
+</script>
+</pre>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for WebSocket object in workers</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<pre id="feedback"></pre>
+<script class="testbody" type="text/javascript">
+
+ var worker = new Worker("websocket_worker1.js");
+
+ worker.onmessage = function(event) {
+ is(event.target, worker, "event.target should be a worker!");
+
+ if (event.data.type == 'finish') {
+ info("All done!");
+ SimpleTest.finish();
+ } else if (event.data.type == 'status') {
+ ok(event.data.status, event.data.msg);
+ } else if (event.data.type == 'feedback') {
+ info(event.data.msg);
+ document.getElementById('feedback').innerHTML += event.data.msg + "\n";
+ }
+ };
+
+ worker.onerror = function(event) {
+ is(event.target, worker);
+ info("error!");
+ ok(false, "Worker had an error: " + event.data);
+ SimpleTest.finish();
+ };
+
+ worker.postMessage('foobar');
+ SimpleTest.waitForExplicitFinish();
+
+</script>
+</pre>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for WebSocket object in workers</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<pre id="feedback"></pre>
+<script class="testbody" type="text/javascript">
+
+ var worker = new Worker("websocket_worker2.js");
+
+ worker.onmessage = function(event) {
+ is(event.target, worker, "event.target should be a worker!");
+
+ if (event.data.type == 'finish') {
+ info("All done!");
+ SimpleTest.finish();
+ } else if (event.data.type == 'status') {
+ ok(event.data.status, event.data.msg);
+ } else if (event.data.type == 'feedback') {
+ info(event.data.msg);
+ document.getElementById('feedback').innerHTML += event.data.msg + "\n";
+ }
+ };
+
+ worker.onerror = function(event) {
+ is(event.target, worker);
+ info("error!");
+ ok(false, "Worker had an error: " + event.data);
+ SimpleTest.finish();
+ };
+
+ worker.postMessage('foobar');
+ SimpleTest.waitForExplicitFinish();
+
+</script>
+</pre>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for WebSocket object in workers</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<pre id="feedback"></pre>
+<script class="testbody" type="text/javascript">
+
+ var worker = new Worker("websocket_worker3.js");
+
+ worker.onmessage = function(event) {
+ is(event.target, worker, "event.target should be a worker!");
+
+ if (event.data.type == 'finish') {
+ info("All done!");
+ SimpleTest.finish();
+ } else if (event.data.type == 'status') {
+ ok(event.data.status, event.data.msg);
+ } else if (event.data.type == 'feedback') {
+ info(event.data.msg);
+ document.getElementById('feedback').innerHTML += event.data.msg + "\n";
+ }
+ };
+
+ worker.onerror = function(event) {
+ is(event.target, worker);
+ info("error!");
+ ok(false, "Worker had an error: " + event.data);
+ SimpleTest.finish();
+ };
+
+ worker.postMessage('foobar');
+ SimpleTest.waitForExplicitFinish();
+
+</script>
+</pre>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for WebSocket object in workers</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<pre id="feedback"></pre>
+<script class="testbody" type="text/javascript">
+
+ var worker = new Worker("websocket_worker4.js");
+
+ worker.onmessage = function(event) {
+ is(event.target, worker, "event.target should be a worker!");
+
+ if (event.data.type == 'finish') {
+ info("All done!");
+ SimpleTest.finish();
+ } else if (event.data.type == 'status') {
+ ok(event.data.status, event.data.msg);
+ } else if (event.data.type == 'feedback') {
+ info(event.data.msg);
+ document.getElementById('feedback').innerHTML += event.data.msg + "\n";
+ }
+ };
+
+ worker.onerror = function(event) {
+ is(event.target, worker);
+ info("error!");
+ ok(false, "Worker had an error: " + event.data);
+ SimpleTest.finish();
+ };
+
+ worker.postMessage('foobar');
+ SimpleTest.waitForExplicitFinish();
+
+</script>
+</pre>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for WebSocket object in workers</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<pre id="feedback"></pre>
+<script class="testbody" type="text/javascript">
+
+ var worker = new Worker("websocket_worker5.js");
+
+ worker.onmessage = function(event) {
+ is(event.target, worker, "event.target should be a worker!");
+
+ if (event.data.type == 'finish') {
+ info("All done!");
+ SimpleTest.finish();
+ } else if (event.data.type == 'status') {
+ ok(event.data.status, event.data.msg);
+ } else if (event.data.type == 'feedback') {
+ info(event.data.msg);
+ document.getElementById('feedback').innerHTML += event.data.msg + "\n";
+ }
+ };
+
+ worker.onerror = function(event) {
+ is(event.target, worker);
+ info("error!");
+ ok(false, "Worker had an error: " + event.data);
+ SimpleTest.finish();
+ };
+
+ worker.postMessage('foobar');
+ SimpleTest.waitForExplicitFinish();
+
+</script>
+</pre>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for WebSocket object in workers</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+<script class="testbody" type="text/javascript">
+
+ var worker = new Worker("websocket_basic_worker.js");
+
+ worker.onmessage = function(event) {
+ is(event.target, worker);
+
+ if (event.data.type == 'finish') {
+ runTest();
+ } else if (event.data.type == 'status') {
+ ok(event.data.status, event.data.msg);
+ }
+ };
+
+ worker.onerror = function(event) {
+ is(event.target, worker);
+ ok(false, "Worker had an error: " + event.data);
+ SimpleTest.finish();
+ };
+
+ var tests = [
+ function() { worker.postMessage(0); },
+ function() { worker.postMessage(1); }
+ ];
+
+ function runTest() {
+ if (!tests.length) {
+ SimpleTest.finish();
+ return;
+ }
+
+ var test = tests.shift();
+ test();
+ }
+
+ runTest();
+ SimpleTest.waitForExplicitFinish();
+
+</script>
+</pre>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test that creating insecure websockets from https workers is not possible</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" language="javascript">
+
+ onmessage = function(event) {
+ is(event.data, "not created", "WebSocket object must not be created");
+ SimpleTest.finish();
+ };
+ SimpleTest.waitForExplicitFinish();
+
+</script>
+</pre>
+<iframe src="https://example.com/tests/dom/workers/test/websocket_https.html"></iframe>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for WebSocket object in workers</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+<script class="testbody" type="text/javascript">
+
+ var worker = new Worker("websocket_loadgroup_worker.js");
+
+ var stopped = false;
+ worker.onmessage = function(e) {
+ if (e.data == 'opened') {
+ stopped = true;
+ window.stop();
+ } else if (e.data == 'closed') {
+ ok(stopped, "Good!");
+ stopped = false;
+ runTest();
+ } else {
+ ok(false, "An error has been received");
+ }
+ };
+
+ worker.onerror = function(event) {
+ is(event.target, worker);
+ ok(false, "Worker had an error: " + event.data);
+ SimpleTest.finish();
+ };
+
+ var tests = [
+ function() { worker.postMessage(0); },
+ function() { worker.postMessage(1); }
+ ];
+
+ function runTest() {
+ if (!tests.length) {
+ SimpleTest.finish();
+ return;
+ }
+
+ var test = tests.shift();
+ test();
+ }
+
+ runTest();
+ SimpleTest.waitForExplicitFinish();
+
+</script>
+</pre>
+</body>
+</html>
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 @@
+<!-- Any copyright is dedicated to the Public Domain.
+ - http://creativecommons.org/publicdomain/zero/1.0/ -->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Validate Interfaces Exposed to Workers</title>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ <script type="text/javascript" src="worker_driver.js"></script>
+</head>
+<body>
+<script class="testbody" type="text/javascript">
+workerTestExec("test_worker_interfaces.js");
+</script>
+</body>
+</html>
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 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js">
+ </script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ </head>
+ <body>
+ <script type="text/javascript">
+ SimpleTest.waitForExplicitFinish();
+
+ const enabledPref = "dom.workers.enabled";
+
+ is(SpecialPowers.getBoolPref(enabledPref), true,
+ "Workers should be enabled.");
+
+ SpecialPowers.pushPrefEnv({"set": [[enabledPref, false]]}, test1);
+
+ function test1() {
+ ok(!("Worker" in window), "Worker constructor should not be available.");
+
+ var exception;
+ try {
+ var worker = new Worker("workersDisabled_worker.js");
+ }
+ catch(e) {
+ exception = e;
+ }
+
+ ok(exception, "Shouldn't be able to make a worker.");
+
+ SpecialPowers.pushPrefEnv({"set": [[enabledPref, true]]}, test2);
+ }
+
+ function test2() {
+ ok(("Worker" in window), "Worker constructor should be available.");
+
+ const message = "Hi";
+
+ var worker = new Worker("workersDisabled_worker.js");
+ worker.onmessage = function(event) {
+ is(event.data, message, "Good message.");
+ SimpleTest.finish();
+ }
+ worker.postMessage(message);
+ }
+ </script>
+ </body>
+</html>
+
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 @@
+<?xml version="1.0"?>
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<window title="DOM Worker Threads Test"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="test();">
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+ <script type="application/javascript" src="dom_worker_helper.js"/>
+
+ <script type="application/javascript">
+ <![CDATA[
+ function test()
+ {
+ const enabledPref = "dom.workers.enabled";
+ const message = "Hi";
+
+ var prefs = Components.classes["@mozilla.org/preferences-service;1"]
+ .getService(Components.interfaces.nsIPrefBranch);
+ is(prefs.getBoolPref(enabledPref), true, "Workers should be enabled.");
+
+ prefs.setBoolPref(enabledPref, false);
+
+ ok("Worker" in window, "Worker constructor should be available.");
+ ok("ChromeWorker" in window,
+ "ChromeWorker constructor should be available.");
+
+ var worker = new ChromeWorker("workersDisabled_worker.js");
+ worker.onmessage = function(event) {
+ is(event.data, message, "Good message.");
+ prefs.clearUserPref(enabledPref);
+ finish();
+ }
+ worker.postMessage(message);
+
+ waitForWorkerFinish();
+ }
+ ]]>
+ </script>
+
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <p id="display"></p>
+ <div id="content" style="display:none;"></div>
+ <pre id="test"></pre>
+ </body>
+ <label id="test-result"/>
+</window>
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 @@
+<!DOCTYPE html>
+<script>
+ var worker = new Worker("https://example.com/tests/dom/workers/test/websocket_https_worker.js");
+
+ worker.onmessage = function(event) {
+ parent.postMessage(event.data, "*");
+ };
+
+ worker.onerror = function(event) {
+ parent.postMessage("error", "*");
+ };
+
+ worker.postMessage("start");
+</script>
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 @@
+<script>
+onpageshow = function(e) {
+ opener[location.search.split('?')[1]](e);
+}
+</script>
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:
+//
+// <script type="text/javascript" src="worker_driver.js"></script>
+// <script type="text/javascript">
+// workerTestExec('myWorkerTestCase.js')
+// </script>
+//
+// 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]