Bug 1354577 - require ServiceWorkerGlobalScope.importScripts() to only accept JavaScript MIME types r=edenchuang
authorPerry Jiang <perry@mozilla.com>
Mon, 29 Oct 2018 15:08:24 +0000
changeset 443441 82c8a3aae99cb0207639b1850b2ef46625a60ace
parent 443440 73b6c58850206414f5921ab974106e88802725fc
child 443442 dda21fd10aeace89040f4cf30cc427b6720f7e6e
push id34957
push userdvarga@mozilla.com
push dateTue, 30 Oct 2018 09:39:58 +0000
treeherdermozilla-central@a7332ea46f0f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersedenchuang
bugs1354577
milestone65.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1354577 - require ServiceWorkerGlobalScope.importScripts() to only accept JavaScript MIME types r=edenchuang - Make ServiceWorkerGlobalScope.importScripts() throw a NetworkError when receiving a bad (i.e. non-JavaScript) MIME type - Correct registration-tests-mime-types.js to expect TypeError when registering a service worker that calls importScripts() with a bad MIME type, per spec - Add WPT import-scripts-mime-types.https.html to test importScripts success/failure, depending on MIME type Depends on D6416 Differential Revision: https://phabricator.services.mozilla.com/D9886
dom/workers/ScriptLoader.cpp
testing/web-platform/meta/service-workers/service-worker/registration-mime-types.https.html.ini
testing/web-platform/meta/workers/importscripts_mime.tentative.any.js.ini
testing/web-platform/tests/service-workers/service-worker/import-scripts-mime-types.https.html
testing/web-platform/tests/service-workers/service-worker/resources/import-scripts-mime-types-worker.js
testing/web-platform/tests/service-workers/service-worker/resources/registration-tests-mime-types.js
--- a/dom/workers/ScriptLoader.cpp
+++ b/dom/workers/ScriptLoader.cpp
@@ -62,16 +62,17 @@
 #include "mozilla/dom/PerformanceStorage.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/PromiseNativeHandler.h"
 #include "mozilla/dom/Response.h"
 #include "mozilla/dom/ScriptLoader.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/dom/SRILogHelper.h"
 #include "mozilla/dom/ServiceWorkerBinding.h"
+#include "mozilla/dom/ServiceWorkerManager.h"
 #include "mozilla/UniquePtr.h"
 #include "Principal.h"
 #include "WorkerHolder.h"
 #include "WorkerPrivate.h"
 #include "WorkerRunnable.h"
 #include "WorkerScope.h"
 
 #define MAX_CONCURRENT_SCRIPTS 1000
@@ -751,16 +752,43 @@ private:
       aRequest->Cancel(NS_ERROR_FAILURE);
       return NS_ERROR_FAILURE;
     }
 
     ScriptLoadInfo& loadInfo = mLoadInfos[aIndex];
 
     nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
 
+    // Checking the MIME type is only required for ServiceWorkers'
+    // importScripts, per step 10 of https://w3c.github.io/ServiceWorker/#importscripts
+    //
+    // "Extract a MIME type from the response’s header list. If this MIME type
+    // (ignoring parameters) is not a JavaScript MIME type, return a network error."
+    if (mWorkerPrivate->IsServiceWorker()) {
+      nsAutoCString mimeType;
+      channel->GetContentType(mimeType);
+
+      if (!nsContentUtils::IsJavascriptMIMEType(NS_ConvertUTF8toUTF16(mimeType))) {
+        const nsCString& scope =
+          mWorkerPrivate->GetServiceWorkerRegistrationDescriptor().Scope();
+
+        ServiceWorkerManager::LocalizeAndReportToAllClients(
+          scope, "ServiceWorkerRegisterMimeTypeError2",
+          nsTArray<nsString> {
+            NS_ConvertUTF8toUTF16(scope),
+            NS_ConvertUTF8toUTF16(mimeType),
+            loadInfo.mURL
+          }
+        );
+
+        channel->Cancel(NS_ERROR_DOM_NETWORK_ERR);
+        return NS_ERROR_DOM_NETWORK_ERR;
+      }
+    }
+
     // Note that importScripts() can redirect.  In theory the main
     // script could also encounter an internal redirect, but currently
     // the assert does not allow that.
     MOZ_ASSERT_IF(mIsMainScript, channel == loadInfo.mChannel);
     loadInfo.mChannel = channel;
 
     // We synthesize the result code, but its never exposed to content.
     RefPtr<mozilla::dom::InternalResponse> ir =
deleted file mode 100644
--- a/testing/web-platform/meta/service-workers/service-worker/registration-mime-types.https.html.ini
+++ /dev/null
@@ -1,6 +0,0 @@
-[registration-mime-types.https.html]
-  [Registering script that imports script with no MIME type]
-    expected: FAIL
-
-  [Registering script that imports script with bad MIME type]
-    expected: FAIL
--- a/testing/web-platform/meta/workers/importscripts_mime.tentative.any.js.ini
+++ b/testing/web-platform/meta/workers/importscripts_mime.tentative.any.js.ini
@@ -67,47 +67,8 @@
   [importScripts() requires scripty MIME types: text/html is blocked.]
     expected: FAIL
 
   [importScripts() requires scripty MIME types: application/xml is blocked.]
     expected: FAIL
 
   [importScripts() requires scripty MIME types: text/plain is blocked.]
     expected: FAIL
-
-
-[importscripts_mime.tentative.any.serviceworker.html]
-  [importScripts() requires scripty MIME types: aaa/aaa is blocked.]
-    expected: FAIL
-
-  [importScripts() requires scripty MIME types: Text/html is blocked.]
-    expected: FAIL
-
-  [importScripts() requires scripty MIME types: zzz/zzz is blocked.]
-    expected: FAIL
-
-  [importScripts() requires scripty MIME types: application/octet-stream is blocked.]
-    expected: FAIL
-
-  [importScripts() requires scripty MIME types: text/potato is blocked.]
-    expected: FAIL
-
-  [importScripts() requires scripty MIME types: TEXT/HTML is blocked.]
-    expected: FAIL
-
-  [importScripts() requires scripty MIME types: potato/text is blocked.]
-    expected: FAIL
-
-  [importScripts() requires scripty MIME types: text/Html is blocked.]
-    expected: FAIL
-
-  [importScripts() requires scripty MIME types: TeXt/HtMl is blocked.]
-    expected: FAIL
-
-  [importScripts() requires scripty MIME types: text/html is blocked.]
-    expected: FAIL
-
-  [importScripts() requires scripty MIME types: application/xml is blocked.]
-    expected: FAIL
-
-  [importScripts() requires scripty MIME types: text/plain is blocked.]
-    expected: FAIL
-
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/import-scripts-mime-types.https.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Tests for importScripts: MIME types</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/test-helpers.sub.js"></script>
+<body>
+<script>
+/**
+ * Test that a Service Worker's importScript() only accepts valid MIME types.
+ */
+let serviceWorker = null;
+
+promise_test(async t => {
+  const scope = 'resources/import-scripts-mime-types';
+  const registration = await service_worker_unregister_and_register(t,
+    'resources/import-scripts-mime-types-worker.js', scope);
+
+  add_completion_callback(() => { registration.unregister(); });
+
+  await wait_for_state(t, registration.installing, 'activated');
+
+  serviceWorker = registration.active;
+}, 'Global setup');
+
+promise_test(async t => {
+  await fetch_tests_from_worker(serviceWorker);
+}, 'Fetch importScripts tests from service worker')
+</script>
+</body>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/import-scripts-mime-types-worker.js
@@ -0,0 +1,49 @@
+const badMimeTypes = [
+  null,  // no MIME type
+  'text/plain',
+];
+
+const validMimeTypes = [
+  'application/ecmascript',
+  'application/javascript',
+  'application/x-ecmascript',
+  'application/x-javascript',
+  'text/ecmascript',
+  'text/javascript',
+  'text/javascript1.0',
+  'text/javascript1.1',
+  'text/javascript1.2',
+  'text/javascript1.3',
+  'text/javascript1.4',
+  'text/javascript1.5',
+  'text/jscript',
+  'text/livescript',
+  'text/x-ecmascript',
+  'text/x-javascript',
+];
+
+function importScriptsWithMimeType(mimeType) {
+  importScripts(`./mime-type-worker.py${mimeType ? '?mime=' + mimeType : ''}`);
+}
+
+importScripts('/resources/testharness.js');
+
+for (const mimeType of badMimeTypes) {
+  test(() => {
+    assert_throws(
+      'NetworkError',
+      () => { importScriptsWithMimeType(mimeType); },
+      `importScripts with ${mimeType ? 'bad' : 'no'} MIME type ${mimeType || ''} throws NetworkError`,
+    );
+  }, `Importing script with ${mimeType ? 'bad' : 'no'} MIME type ${mimeType || ''}`);
+}
+
+for (const mimeType of validMimeTypes) {
+  test(() => {
+    try {
+      importScriptsWithMimeType(mimeType);
+    } catch {
+      assert_unreached(`importScripts with MIME type ${mimeType} should not throw`);
+    }
+  }, `Importing script with valid JavaScript MIME type ${mimeType}`);
+}
--- a/testing/web-platform/tests/service-workers/service-worker/resources/registration-tests-mime-types.js
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/registration-tests-mime-types.js
@@ -16,30 +16,40 @@ function registration_tests_mime_types(r
       var script = 'resources/mime-type-worker.py?mime=text/plain';
       var scope = 'resources/scope/bad-mime-type-worker/';
       return promise_rejects(t,
           check_error_types ? 'SecurityError' : null,
           register_method(script, {scope: scope}),
           'Registration of plain text script should fail.');
     }, 'Registering script with bad MIME type');
 
+  /**
+   * ServiceWorkerContainer.register() should throw a TypeError, according to
+   * step 17.1 of https://w3c.github.io/ServiceWorker/#importscripts
+   *
+   * "[17] If an uncaught runtime script error occurs during the above step, then:
+   *  [17.1] Invoke Reject Job Promise with job and TypeError"
+   *
+   * (Where the "uncaught runtime script error" is thrown by an unsuccessful
+   * importScripts())
+   */
   promise_test(function(t) {
       var script = 'resources/import-mime-type-worker.py';
       var scope = 'resources/scope/no-mime-type-worker/';
       return promise_rejects(t,
-          check_error_types ? 'SecurityError' : null,
+          check_error_types ? new TypeError() : null,
           register_method(script, {scope: scope}),
           'Registration of no MIME type imported script should fail.');
     }, 'Registering script that imports script with no MIME type');
 
   promise_test(function(t) {
       var script = 'resources/import-mime-type-worker.py?mime=text/plain';
       var scope = 'resources/scope/bad-mime-type-worker/';
       return promise_rejects(t,
-          check_error_types ? 'SecurityError' : null,
+          check_error_types ? new TypeError() : null,
           register_method(script, {scope: scope}),
           'Registration of plain text imported script should fail.');
     }, 'Registering script that imports script with bad MIME type');
 
   const validMimeTypes = [
     'application/ecmascript',
     'application/javascript',
     'application/x-ecmascript',