summaryrefslogtreecommitdiff
path: root/dom/workers/test/serviceworkers
diff options
context:
space:
mode:
authorMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
committerMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
commit5f8de423f190bbb79a62f804151bc24824fa32d8 (patch)
tree10027f336435511475e392454359edea8e25895d /dom/workers/test/serviceworkers
parent49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff)
downloaduxp-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz
Add m-esr52 at 52.6.0
Diffstat (limited to 'dom/workers/test/serviceworkers')
-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
339 files changed, 13569 insertions, 0 deletions
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 0000000000..a5f159d350
--- /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 0000000000..0bc6f7b7f2
--- /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 0000000000..c0aae30d68
--- /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 0000000000..1b0d2defe2
--- /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 0000000000..33bd8cdaa6
--- /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 0000000000..bd4da10c9c
--- /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 0000000000..a2c9c871c3
--- /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 0000000000..e540ad2396
--- /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 0000000000..06585e8e78
--- /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 0000000000..5a588aedff
--- /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 0000000000..e064e7fd0a
--- /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 0000000000..a438333e27
--- /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 0000000000..eecfb294e3
--- /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 0000000000..ea62c37b88
--- /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 0000000000..269afa7213
--- /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 0000000000..e5f6392d3f
--- /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 0000000000..be8281d341
--- /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 0000000000..6138d6421e
--- /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 0000000000..740e24f15c
--- /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 0000000000..f49194fa50
--- /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 0000000000..7d7893e0e6
--- /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 0000000000..fe46d1a3bd
--- /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 0000000000..e69de29bb2
--- /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 0000000000..fbc4ca6fc7
--- /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 0000000000..c60f2f6370
--- /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 0000000000..eb62cbd4c5
--- /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 0000000000..5b88be7c32
--- /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 0000000000..7c6f7302fa
--- /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 0000000000..579e9f5680
--- /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 0000000000..f6ae0d96f1
--- /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 0000000000..187d0bc6f9
--- /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 0000000000..f92811e63e
--- /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 0000000000..45a80e324d
--- /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 0000000000..59e8e92ab6
--- /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 0000000000..d9f380e2f4
--- /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 0000000000..8692f91863
--- /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 0000000000..6d5dbb0246
--- /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 0000000000..38d20a6389
--- /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 0000000000..8401bc29bb
--- /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 0000000000..b98d2ab3ca
--- /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 0000000000..4c3e76d152
--- /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 0000000000..c6dfef99c8
--- /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 0000000000..eac8d5e717
--- /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 0000000000..839fb6640b
--- /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 0000000000..b1bebe41ec
--- /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 0000000000..1a41623f81
--- /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 0000000000..5b920f7c06
--- /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 0000000000..6528d0eae9
--- /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 0000000000..94dca58399
--- /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 0000000000..1f13508fa7
--- /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 0000000000..e26e5bc691
--- /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 0000000000..69c64adf1d
--- /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 0000000000..abacdd2adb
--- /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 0000000000..54da1b66eb
--- /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 0000000000..61efb647c8
--- /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 0000000000..c985554234
--- /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 0000000000..ab54164ed2
--- /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 0000000000..ae6a8a6b88
--- /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 0000000000..fe391dc8a2
--- /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 0000000000..cadbdef5af
--- /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 0000000000..b3d1d527e3
--- /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 0000000000..bcdc146aec
--- /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 0000000000..a46bf65bd9
--- /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 0000000000..1f13508fa7
--- /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 0000000000..48f7b93073
--- /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 0000000000..a435548443
--- /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 0000000000..41774f70d1
--- /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 0000000000..1f13508fa7
--- /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 0000000000..6f87bb5ee1
--- /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 0000000000..a435548443
--- /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 0000000000..fa666fe957
--- /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 0000000000..1f13508fa7
--- /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 0000000000..ae6a8a6b88
--- /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 0000000000..fe391dc8a2
--- /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 0000000000..426c27a732
--- /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 0000000000..1922111dfb
--- /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 0000000000..af4dde2e29
--- /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 0000000000..1f13508fa7
--- /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 0000000000..ae6a8a6b88
--- /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 0000000000..fe391dc8a2
--- /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 0000000000..598d8213f3
--- /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 0000000000..93b30f1842
--- /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 0000000000..72a650d26c
--- /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 0000000000..f6d1eb382f
--- /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 0000000000..1f13508fa7
--- /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 0000000000..0f08ba74e3
--- /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 0000000000..41774f70d1
--- /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 0000000000..1f13508fa7
--- /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 0000000000..4db0fb1399
--- /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 0000000000..f6fe870efa
--- /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 0000000000..7266925ea7
--- /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 0000000000..9839fc5f07
--- /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 0000000000..87f3489455
--- /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 0000000000..5ed82fd065
--- /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 0000000000..2e99adba53
--- /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 0000000000..1f13508fa7
--- /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 0000000000..1cc916ff39
--- /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 0000000000..a79588e76e
--- /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 0000000000..d2be9573b7
--- /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 0000000000..87f3489455
--- /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 0000000000..3a6a85d894
--- /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 0000000000..2e99adba53
--- /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 0000000000..1f13508fa7
--- /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 0000000000..78e31b3c29
--- /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 0000000000..e97d062050
--- /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 0000000000..3ca2088ec0
--- /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 0000000000..dab558f4a8
--- /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 0000000000..bc3e400a94
--- /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 0000000000..7b92fec20d
--- /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 0000000000..73bf4af49c
--- /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 0000000000..19a2e022c2
--- /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 0000000000..c8be3daf43
--- /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 0000000000..694c336355
--- /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 0000000000..1f13508fa7
--- /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 0000000000..1094a3995d
--- /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 0000000000..87261a495f
--- /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 0000000000..427b1a8da9
--- /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 0000000000..1ed351794e
--- /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 0000000000..1f13508fa7
--- /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 0000000000..6098a45dd4
--- /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 0000000000..602d9dc38d
--- /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 0000000000..ae6a8a6b88
--- /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 0000000000..fe391dc8a2
--- /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 0000000000..34e24e35a4
--- /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 0000000000..aaa255aad3
--- /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 0000000000..6309b9b218
--- /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 0000000000..1f13508fa7
--- /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 0000000000..ab54164ed2
--- /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 0000000000..1caef71e89
--- /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 0000000000..4b4379d0b7
--- /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 0000000000..96d9d0f176
--- /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 0000000000..f0752d0cb0
--- /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 0000000000..72aeba2223
--- /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 0000000000..7061041039
--- /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 0000000000..97eb03c902
--- /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 0000000000..6d177a7341
--- /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 0000000000..3cddec1947
--- /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 0000000000..c06d648b87
--- /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 0000000000..f965d28aa8
--- /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 0000000000..5502a44daa
--- /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 0000000000..3721aedfef
--- /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 0000000000..7ac6fc9d05
--- /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 0000000000..a7d9ff594e
--- /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 0000000000..25317b9fc8
--- /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 0000000000..f007a5ce8e
--- /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 0000000000..9d1c8c3639
--- /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 0000000000..26db997759
--- /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 0000000000..82cb587c72
--- /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 0000000000..29ac9e0367
--- /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 0000000000..1c6e282ece
--- /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 0000000000..b7df73bede
--- /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 0000000000..d5a141f830
--- /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 0000000000..b7df73bede
--- /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 0000000000..d5a141f830
--- /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 0000000000..644dba480e
--- /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 0000000000..540c9d93cc
--- /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 0000000000..f64e82aabd
--- /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 0000000000..448764a1cb
--- /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 0000000000..39e7adb815
--- /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 0000000000..0152d397f3
--- /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 0000000000..5fb73651ec
--- /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 0000000000..10c8da4539
--- /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 0000000000..d482180752
--- /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 0000000000..d07573b2cb
--- /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 0000000000..d593be7832
--- /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 0000000000..46c0f998ef
--- /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 0000000000..82b93ff7e4
--- /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 0000000000..b6a8ef0a1a
--- /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 0000000000..b6249cadff
--- /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 0000000000..8b805be634
--- /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 0000000000..9d3a0a2cd6
--- /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 0000000000..d14d8c3808
--- /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 0000000000..4de8f317b2
--- /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 0000000000..b4bf7a1ff1
--- /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 0000000000..f1edd27be8
--- /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 0000000000..66495e1867
--- /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 0000000000..4164e7a244
--- /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 0000000000..11edd001a2
--- /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 0000000000..077da2366d
--- /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 0000000000..6a7ec0c0f6
--- /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 0000000000..a0a2de7603
--- /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 0000000000..0df93da96d
--- /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 0000000000..f093d38db4
--- /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 0000000000..4970a1fd2f
--- /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 0000000000..2c0eb5345e
--- /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 0000000000..3afc4bfdb2
--- /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 0000000000..68573f1003
--- /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 0000000000..b8a64d512e
--- /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 0000000000..36ce951fec
--- /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 0000000000..38418de3d8
--- /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 0000000000..f371807db9
--- /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 0000000000..a1f9e38d90
--- /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 0000000000..e594c514d5
--- /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 0000000000..f6019bf28b
--- /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 0000000000..054f6bfc85
--- /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 0000000000..3ec0cc4270
--- /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 0000000000..55e97ac247
--- /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 0000000000..4204d8601d
--- /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 0000000000..7a45bcafab
--- /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 0000000000..4204d8601d
--- /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 0000000000..e0d7bce573
--- /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 0000000000..3e4d7deca5
--- /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 0000000000..e69de29bb2
--- /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 0000000000..e85a7f09de
--- /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 0000000000..e69de29bb2
--- /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 0000000000..e37307d666
--- /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 0000000000..e69de29bb2
--- /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 0000000000..30b0530557
--- /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 0000000000..e69de29bb2
--- /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 0000000000..b2056fc4a0
--- /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 0000000000..e69de29bb2
--- /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 0000000000..22add13bf7
--- /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 0000000000..92811775bf
--- /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 0000000000..e93535840d
--- /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 0000000000..d7015850fc
--- /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 0000000000..8db6d304e4
--- /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 0000000000..4605cfb76f
--- /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 0000000000..b0bf43bb34
--- /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 0000000000..d2f72b9ef9
--- /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 0000000000..789d7746da
--- /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 0000000000..e56bb84ca0
--- /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 0000000000..fe4cb991cf
--- /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 0000000000..e429518961
--- /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 0000000000..619d602e82
--- /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 0000000000..001c660242
--- /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 0000000000..bfe8ac280d
--- /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 0000000000..51ffaa71dd
--- /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 0000000000..55df62bb7b
--- /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 0000000000..764be87b13
--- /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 0000000000..50eb05581a
--- /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 0000000000..6db0656c66
--- /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 0000000000..30a31eb7e8
--- /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 0000000000..05caf0e6ac
--- /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 0000000000..7ac92122ce
--- /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 0000000000..dfce406b80
--- /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 0000000000..e990200f85
--- /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 0000000000..1cf1dbef13
--- /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 0000000000..3878a1df6d
--- /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 0000000000..81a1d1da0e
--- /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 0000000000..7bf3b352ae
--- /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 0000000000..8627b54af7
--- /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 0000000000..eb3c1f1668
--- /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 0000000000..5d2d5b3522
--- /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 0000000000..a659af92ba
--- /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 0000000000..0e59510fe7
--- /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 0000000000..ccccd2b43d
--- /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 0000000000..1b0d6c9471
--- /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 0000000000..f4e65a730e
--- /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 0000000000..a458ed70ba
--- /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 0000000000..4c8ed9673d
--- /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 0000000000..14e3445a4b
--- /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 0000000000..164f41bcd2
--- /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 0000000000..a90e068d34
--- /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 0000000000..6a8ecf8c05
--- /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 0000000000..dbb312e7b5
--- /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 0000000000..4a785be9a8
--- /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 0000000000..d5c3ecf8b0
--- /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 0000000000..81d6e269c3
--- /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 0000000000..3b81132c4b
--- /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 0000000000..5cb12e518c
--- /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 0000000000..2417648b91
--- /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 0000000000..b68537d9d2
--- /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 0000000000..69644abfaf
--- /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 0000000000..dcac11aea1
--- /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 0000000000..3922fdb90f
--- /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 0000000000..e366423cb9
--- /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 0000000000..8ea0d2300d
--- /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 0000000000..543f64b4a6
--- /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 0000000000..9763377118
--- /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 0000000000..58b08d27a9
--- /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 0000000000..2c8e998f72
--- /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 0000000000..aebba79a76
--- /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 0000000000..929a244281
--- /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 0000000000..ce44214d60
--- /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 0000000000..3d62baabc1
--- /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 0000000000..a27e793037
--- /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 0000000000..f8d3742462
--- /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 0000000000..94de8be311
--- /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 0000000000..d81a0686b7
--- /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 0000000000..d5dc1f7452
--- /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 0000000000..d3b0675e0e
--- /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 0000000000..810063da4d
--- /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 0000000000..95b2b7214f
--- /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 0000000000..45f454495b
--- /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 0000000000..226de691bb
--- /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 0000000000..48934a57c2
--- /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 0000000000..189c5adbb9
--- /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 0000000000..1b49e7eb93
--- /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 0000000000..460e9efb43
--- /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 0000000000..862e9d4a55
--- /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 0000000000..ec560ef724
--- /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 0000000000..93ccdf3ae6
--- /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 0000000000..b557d5c047
--- /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 0000000000..1b161c4d06
--- /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 0000000000..1886e31d73
--- /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 0000000000..9de1273045
--- /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 0000000000..ed0d60bf87
--- /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 0000000000..a6d837b693
--- /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 0000000000..273df53ff5
--- /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 0000000000..842cb38c33
--- /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 0000000000..054b60f374
--- /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 0000000000..2d8116f837
--- /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 0000000000..eca94ebb43
--- /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 0000000000..ac5a6e49f9
--- /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 0000000000..0130ca2d99
--- /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 0000000000..9dbfcc0990
--- /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 0000000000..96dd9f1599
--- /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 0000000000..96e4bb1c37
--- /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 0000000000..ead935a3c4
--- /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 0000000000..c879dc01be
--- /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 0000000000..7707d60350
--- /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 0000000000..5b66673b91
--- /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 0000000000..33e815379a
--- /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 0000000000..8366f50c1c
--- /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 0000000000..7296a3ddf2
--- /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 0000000000..947861c176
--- /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 0000000000..5621d6cb89
--- /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 0000000000..3361eba08a
--- /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 0000000000..44270753b5
--- /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 0000000000..43fe8c5729
--- /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 0000000000..fac6a9395d
--- /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 0000000000..59b8c5c41a
--- /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 0000000000..ca45698c83
--- /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 0000000000..2cb6ee0ce9
--- /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 0000000000..db23d2cb78
--- /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 0000000000..6fda820268
--- /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 0000000000..71fbad7813
--- /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 0000000000..78088c7cd0
--- /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 0000000000..2aba167d18
--- /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 0000000000..3072d0817f
--- /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 0000000000..449fc2f976
--- /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 0000000000..8f984ccc44
--- /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 0000000000..7a3e764f4e
--- /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 0000000000..9f3e55b18a
--- /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 0000000000..a297bf4556
--- /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 0000000000..da4c592aad
--- /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 0000000000..83c7776339
--- /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 0000000000..db681ab500
--- /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 0000000000..bf9bdbc566
--- /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;
+};