Bug 1180861 - Various ServiceWorker registration fixes to get test passing. r=bkelly,jgraham.
authorNikhil Marathe <nsm.nikhil@gmail.com>
Wed, 19 Aug 2015 16:21:25 -0700
changeset 259324 e1d8c2010f8346935cfcfc5b6c3586b4a3efe4c8
parent 259323 7c5cc8cfca19afaeb4af08c669a78219edea72c4
child 259325 d2629e3e4b0c67390c95c5f56ccb101ff1802772
push id64201
push usernsm.nikhil@gmail.com
push dateTue, 25 Aug 2015 20:59:19 +0000
treeherdermozilla-inbound@e1d8c2010f83 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbkelly, jgraham
bugs1180861
milestone43.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 1180861 - Various ServiceWorker registration fixes to get test passing. r=bkelly,jgraham. This commit implements the following changes to get registration.https.html working. 1) Fail with NS_ERROR_DOM_SECURITY_ERR where the spec requires it. 2) Propagate JSExnType to ServiceWorkerManager::HandleError() so that a JS exception object with the correct .name can be created. 3) Fail with security error on redirect failure. 4) Check fetched script's mimetype. 5) Add missing python server files for web-platform-tests. 6) Update web-platform-tests expected data. 7) Several tests have been changed to use TypeError or more appropriate JS errors based on my reading of the spec.
dom/workers/ServiceWorkerContainer.cpp
dom/workers/ServiceWorkerManager.cpp
dom/workers/ServiceWorkerManager.h
dom/workers/ServiceWorkerScriptCache.cpp
dom/workers/WorkerPrivate.cpp
dom/workers/test/serviceworkers/test_installation_simple.html
testing/web-platform/mozilla/meta/service-workers/service-worker/registration.https.html.ini
testing/web-platform/mozilla/tests/service-workers/service-worker/registration.https.html
testing/web-platform/mozilla/tests/service-workers/service-worker/resources/invalid-chunked-encoding-with-flush.py
testing/web-platform/mozilla/tests/service-workers/service-worker/resources/invalid-chunked-encoding.py
testing/web-platform/mozilla/tests/service-workers/service-worker/resources/malformed-worker.py
testing/web-platform/mozilla/tests/service-workers/service-worker/resources/mime-type-worker.py
testing/web-platform/mozilla/tests/service-workers/service-worker/resources/redirect.py
--- a/dom/workers/ServiceWorkerContainer.cpp
+++ b/dom/workers/ServiceWorkerContainer.cpp
@@ -101,19 +101,21 @@ ServiceWorkerContainer::WrapObject(JSCon
   return ServiceWorkerContainerBinding::Wrap(aCx, this, aGivenProto);
 }
 
 static nsresult
 CheckForSlashEscapedCharsInPath(nsIURI* aURI)
 {
   MOZ_ASSERT(aURI);
 
+  // A URL that can't be downcast to a standard URL is an invalid URL and should
+  // be treated as such and fail with SecurityError.
   nsCOMPtr<nsIURL> url(do_QueryInterface(aURI));
   if (NS_WARN_IF(!url)) {
-    return NS_ERROR_FAILURE;
+    return NS_ERROR_DOM_SECURITY_ERR;
   }
 
   nsAutoCString path;
   nsresult rv = url->GetFilePath(path);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
--- a/dom/workers/ServiceWorkerManager.cpp
+++ b/dom/workers/ServiceWorkerManager.cpp
@@ -569,17 +569,17 @@ public:
 
   void
   UpdateFailed(nsresult aStatus) override
   {
     mPromise->MaybeReject(aStatus);
   }
 
   void
-  UpdateFailed(const ErrorEventInit& aErrorDesc) override
+  UpdateFailed(JSExnType aExnType, const ErrorEventInit& aErrorDesc) override
   {
     AutoJSAPI jsapi;
     jsapi.Init(mWindow);
 
     JSContext* cx = jsapi.cx();
 
     JS::Rooted<JS::Value> fnval(cx);
     if (!ToJSValue(cx, aErrorDesc.mFilename, &fnval)) {
@@ -593,17 +593,18 @@ public:
     if (!ToJSValue(cx, aErrorDesc.mMessage, &msgval)) {
       JS_ClearPendingException(cx);
       mPromise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
       return;
     }
     JS::Rooted<JSString*> msg(cx, msgval.toString());
 
     JS::Rooted<JS::Value> error(cx);
-    if (!JS::CreateError(cx, JSEXN_ERR, nullptr, fn, aErrorDesc.mLineno,
+    if ((aExnType < JSEXN_ERR) ||
+        !JS::CreateError(cx, aExnType, nullptr, fn, aErrorDesc.mLineno,
                          aErrorDesc.mColno, nullptr, msg, &error)) {
       JS_ClearPendingException(cx);
       mPromise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
       return;
     }
 
     mPromise->MaybeReject(cx, error);
   }
@@ -970,23 +971,27 @@ public:
   }
 
   void
   ComparisonResult(nsresult aStatus, bool aInCacheAndEqual,
                    const nsAString& aNewCacheName,
                    const nsACString& aMaxScope) override
   {
     nsRefPtr<ServiceWorkerRegisterJob> kungFuDeathGrip = this;
-    if (mCanceled) {
+    if (NS_WARN_IF(mCanceled)) {
       Fail(NS_ERROR_DOM_TYPE_ERR);
       return;
     }
 
     if (NS_WARN_IF(NS_FAILED(aStatus))) {
-      Fail(NS_ERROR_DOM_TYPE_ERR);
+      if (aStatus == NS_ERROR_DOM_SECURITY_ERR) {
+        Fail(aStatus);
+      } else {
+        Fail(NS_ERROR_DOM_TYPE_ERR);
+      }
       return;
     }
 
     if (aInCacheAndEqual) {
       Succeed();
       Done(NS_OK);
       return;
     }
@@ -1096,27 +1101,27 @@ public:
       data->mSetOfScopesBeingUpdated.Remove(aScopeKey);
     }
     Fail(NS_ERROR_DOM_ABORT_ERR);
   }
 
   // Public so our error handling code can use it.
   // Callers MUST hold a strong ref before calling this!
   void
-  Fail(const ErrorEventInit& aError)
+  Fail(JSExnType aExnType, const ErrorEventInit& aError)
   {
     MOZ_ASSERT(mCallback);
     nsRefPtr<ServiceWorkerUpdateFinishCallback> callback = mCallback.forget();
     // With cancellation support, we may only be running with one reference
     // from another object like a stream loader or something.
     // UpdateFailed may do something with that, so hold a ref to ourself since
     // FailCommon relies on it.
     // FailCommon does check for cancellation, but let's be safe here.
     nsRefPtr<ServiceWorkerRegisterJob> kungFuDeathGrip = this;
-    callback->UpdateFailed(aError);
+    callback->UpdateFailed(aExnType, aError);
     FailCommon(NS_ERROR_DOM_JS_EXCEPTION);
   }
 
   // Public so our error handling code can continue with a successful worker.
   void
   ContinueInstall()
   {
     // mRegistration will be null if we have already Fail()ed.
@@ -2922,17 +2927,18 @@ ServiceWorkerManager::HandleError(JSCont
                                   nsIPrincipal* aPrincipal,
                                   const nsCString& aScope,
                                   const nsString& aWorkerURL,
                                   nsString aMessage,
                                   nsString aFilename,
                                   nsString aLine,
                                   uint32_t aLineNumber,
                                   uint32_t aColumnNumber,
-                                  uint32_t aFlags)
+                                  uint32_t aFlags,
+                                  JSExnType aExnType)
 {
   AssertIsOnMainThread();
   MOZ_ASSERT(aPrincipal);
   MOZ_ASSERT(!JSREPORT_IS_WARNING(aFlags));
 
   nsAutoCString scopeKey;
   nsresult rv = PrincipalToScopeKey(aPrincipal, scopeKey);
   if (NS_WARN_IF(NS_FAILED(rv))) {
@@ -2962,17 +2968,17 @@ ServiceWorkerManager::HandleError(JSCont
     init.mFilename = aFilename;
     init.mLineno = aLineNumber;
     init.mColno = aColumnNumber;
 
   NS_WARNING(nsPrintfCString(
               "Script error caused ServiceWorker registration to fail: %s:%u '%s'",
               NS_ConvertUTF16toUTF8(aFilename).get(), aLineNumber,
               NS_ConvertUTF16toUTF8(aMessage).get()).get());
-    regJob->Fail(init);
+    regJob->Fail(aExnType, init);
   }
 
   return true;
 }
 
 void
 ServiceWorkerRegistrationInfo::FinishActivate(bool aSuccess)
 {
--- a/dom/workers/ServiceWorkerManager.h
+++ b/dom/workers/ServiceWorkerManager.h
@@ -143,17 +143,17 @@ public:
   void UpdateSucceeded(ServiceWorkerRegistrationInfo* aInfo)
   { }
 
   virtual
   void UpdateFailed(nsresult aStatus)
   { }
 
   virtual
-  void UpdateFailed(const ErrorEventInit& aDesc)
+  void UpdateFailed(JSExnType aExnType, const ErrorEventInit& aDesc)
   { }
 };
 
 /*
  * Wherever the spec treats a worker instance and a description of said worker
  * as the same thing; i.e. "Resolve foo with
  * _GetNewestWorker(serviceWorkerRegistration)", we represent the description
  * by this class and spawn a ServiceWorker in the right global when required.
@@ -370,17 +370,18 @@ public:
               nsIPrincipal* aPrincipal,
               const nsCString& aScope,
               const nsString& aWorkerURL,
               nsString aMessage,
               nsString aFilename,
               nsString aLine,
               uint32_t aLineNumber,
               uint32_t aColumnNumber,
-              uint32_t aFlags);
+              uint32_t aFlags,
+              JSExnType aExnType);
 
   void
   GetAllClients(nsIPrincipal* aPrincipal,
                 const nsCString& aScope,
                 nsTArray<ServiceWorkerClientInfo>& aControlledDocuments);
 
   void
   MaybeClaimClient(nsIDocument* aDocument,
--- a/dom/workers/ServiceWorkerScriptCache.cpp
+++ b/dom/workers/ServiceWorkerScriptCache.cpp
@@ -361,17 +361,17 @@ public:
 
   void
   NetworkFinished(nsresult aStatus)
   {
     AssertIsOnMainThread();
 
     mNetworkFinished = true;
 
-    if (NS_FAILED(aStatus)) {
+    if (NS_WARN_IF(NS_FAILED(aStatus))) {
       if (mCC) {
         mCC->Abort();
       }
 
       ComparisonFinished(aStatus, false);
       return;
     }
 
@@ -381,17 +381,17 @@ public:
   void
   CacheFinished(nsresult aStatus, bool aInCache)
   {
     AssertIsOnMainThread();
 
     mCacheFinished = true;
     mInCache = aInCache;
 
-    if (NS_FAILED(aStatus)) {
+    if (NS_WARN_IF(NS_FAILED(aStatus))) {
       if (mCN) {
         mCN->Abort();
       }
 
       ComparisonFinished(aStatus, false);
       return;
     }
 
@@ -402,17 +402,17 @@ public:
   MaybeCompare()
   {
     AssertIsOnMainThread();
 
     if (!mNetworkFinished || (mCC && !mCacheFinished)) {
       return;
     }
 
-    if (!mCC || !mInCache) {
+    if (NS_WARN_IF(!mCC || !mInCache)) {
       ComparisonFinished(NS_OK, false);
       return;
     }
 
     ComparisonFinished(NS_OK, mCC->Buffer().Equals(mCN->Buffer()));
   }
 
   // This class manages 2 promises: 1 is to retrieve Cache object, and 2 is to
@@ -531,17 +531,17 @@ private:
   }
 
   void
   ComparisonFinished(nsresult aStatus, bool aIsEqual)
   {
     AssertIsOnMainThread();
     MOZ_ASSERT(mCallback);
 
-    if (NS_FAILED(aStatus)) {
+    if (NS_WARN_IF(NS_FAILED(aStatus))) {
       Fail(aStatus);
       return;
     }
 
     if (aIsEqual) {
       mCallback->ComparisonResult(aStatus, aIsEqual, EmptyString(), mMaxScope);
       Cleanup();
       return;
@@ -690,17 +690,21 @@ CompareNetwork::OnStreamComplete(nsIStre
   AssertIsOnMainThread();
 
   // If no channel, Abort() has been called.
   if (!mChannel) {
     return NS_OK;
   }
 
   if (NS_WARN_IF(NS_FAILED(aStatus))) {
-    mManager->NetworkFinished(aStatus);
+    if (aStatus == NS_ERROR_REDIRECT_LOOP) {
+      mManager->NetworkFinished(NS_ERROR_DOM_SECURITY_ERR);
+    } else {
+      mManager->NetworkFinished(aStatus);
+    }
     return NS_OK;
   }
 
   nsCOMPtr<nsIRequest> request;
   nsresult rv = aLoader->GetRequest(getter_AddRefs(request));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     mManager->NetworkFinished(rv);
     return NS_OK;
@@ -710,28 +714,42 @@ CompareNetwork::OnStreamComplete(nsIStre
   if (httpChannel) {
     bool requestSucceeded;
     rv = httpChannel->GetRequestSucceeded(&requestSucceeded);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       mManager->NetworkFinished(rv);
       return NS_OK;
     }
 
-    if (!requestSucceeded) {
+    if (NS_WARN_IF(!requestSucceeded)) {
       mManager->NetworkFinished(NS_ERROR_FAILURE);
       return NS_OK;
     }
 
     nsAutoCString maxScope;
     // Note: we explicitly don't check for the return value here, because the
-    // absense of the header is not an error condition.
+    // absence of the header is not an error condition.
     unused << httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("Service-Worker-Allowed"),
                                              maxScope);
 
     mManager->SetMaxScope(maxScope);
+
+    nsAutoCString mimeType;
+    rv = httpChannel->GetContentType(mimeType);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      mManager->NetworkFinished(NS_ERROR_DOM_SECURITY_ERR);
+      return rv;
+    }
+
+    if (!mimeType.LowerCaseEqualsLiteral("text/javascript") &&
+        !mimeType.LowerCaseEqualsLiteral("application/x-javascript") &&
+        !mimeType.LowerCaseEqualsLiteral("application/javascript")) {
+      mManager->NetworkFinished(NS_ERROR_DOM_SECURITY_ERR);
+      return rv;
+    }
   }
   else {
     // The only supported request schemes are http, https, and app.
     // Above, we check to ensure that the request is http or https
     // based on the channel qi.  Here we test the scheme to ensure
     // that it is app.  Otherwise, bail.
     nsCOMPtr<nsIChannel> channel = do_QueryInterface(request);
     if (NS_WARN_IF(!channel)) {
@@ -747,24 +765,22 @@ CompareNetwork::OnStreamComplete(nsIStre
 
     nsAutoCString scheme;
     rv = uri->GetScheme(scheme);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       mManager->NetworkFinished(rv);
       return NS_OK;
     }
 
-    if (!scheme.LowerCaseEqualsLiteral("app")) {
+    if (NS_WARN_IF(!scheme.LowerCaseEqualsLiteral("app"))) {
       mManager->NetworkFinished(NS_ERROR_FAILURE);
       return NS_OK;      
     }
   }
 
-  // FIXME(nsm): "Extract mime type..."
-
   char16_t* buffer = nullptr;
   size_t len = 0;
 
   rv = nsScriptLoader::ConvertToUTF16(httpChannel, aString, aLen,
                                       NS_LITERAL_STRING("UTF-8"), nullptr,
                                       buffer, len);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     mManager->NetworkFinished(rv);
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -1361,28 +1361,29 @@ class ReportErrorRunnable final : public
 {
   nsString mMessage;
   nsString mFilename;
   nsString mLine;
   uint32_t mLineNumber;
   uint32_t mColumnNumber;
   uint32_t mFlags;
   uint32_t mErrorNumber;
+  JSExnType mExnType;
 
 public:
   // aWorkerPrivate is the worker thread we're on (or the main thread, if null)
   // aTarget is the worker object that we are going to fire an error at
   // (if any).
   static bool
   ReportError(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
               bool aFireAtScope, WorkerPrivate* aTarget,
               const nsString& aMessage, const nsString& aFilename,
               const nsString& aLine, uint32_t aLineNumber,
               uint32_t aColumnNumber, uint32_t aFlags,
-              uint32_t aErrorNumber, uint64_t aInnerWindowId)
+              uint32_t aErrorNumber, JSExnType aExnType, uint64_t aInnerWindowId)
   {
     if (aWorkerPrivate) {
       aWorkerPrivate->AssertIsOnWorkerThread();
     }
     else {
       AssertIsOnMainThread();
     }
 
@@ -1477,35 +1478,36 @@ public:
       }
     }
 
     // Now fire a runnable to do the same on the parent's thread if we can.
     if (aWorkerPrivate) {
       nsRefPtr<ReportErrorRunnable> runnable =
         new ReportErrorRunnable(aWorkerPrivate, aMessage, aFilename, aLine,
                                 aLineNumber, aColumnNumber, aFlags,
-                                aErrorNumber);
+                                aErrorNumber, aExnType);
       return runnable->Dispatch(aCx);
     }
 
     // Otherwise log an error to the error console.
     LogErrorToConsole(aMessage, aFilename, aLine, aLineNumber, aColumnNumber,
                       aFlags, aInnerWindowId);
     return true;
   }
 
 private:
   ReportErrorRunnable(WorkerPrivate* aWorkerPrivate, const nsString& aMessage,
                       const nsString& aFilename, const nsString& aLine,
                       uint32_t aLineNumber, uint32_t aColumnNumber,
-                      uint32_t aFlags, uint32_t aErrorNumber)
+                      uint32_t aFlags, uint32_t aErrorNumber,
+                      JSExnType aExnType)
   : WorkerRunnable(aWorkerPrivate, ParentThreadUnchangedBusyCount),
     mMessage(aMessage), mFilename(aFilename), mLine(aLine),
     mLineNumber(aLineNumber), mColumnNumber(aColumnNumber), mFlags(aFlags),
-    mErrorNumber(aErrorNumber)
+    mErrorNumber(aErrorNumber), mExnType(aExnType)
   { }
 
   virtual void
   PostDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
                bool aDispatchResult) override
   {
     aWorkerPrivate->AssertIsOnWorkerThread();
 
@@ -1539,17 +1541,17 @@ private:
         if (aWorkerPrivate->IsServiceWorker() && !JSREPORT_IS_WARNING(mFlags)) {
           nsRefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
           MOZ_ASSERT(swm);
           bool handled = swm->HandleError(aCx, aWorkerPrivate->GetPrincipal(),
                                           aWorkerPrivate->SharedWorkerName(),
                                           aWorkerPrivate->ScriptURL(),
                                           mMessage,
                                           mFilename, mLine, mLineNumber,
-                                          mColumnNumber, mFlags);
+                                          mColumnNumber, mFlags, mExnType);
           if (handled) {
             return true;
           }
         }
 
         aWorkerPrivate->BroadcastErrorToSharedWorkers(aCx, mMessage, mFilename,
                                                       mLine, mLineNumber,
                                                       mColumnNumber, mFlags);
@@ -1568,17 +1570,17 @@ private:
     // Don't fire this event if the JS object has been disconnected from the
     // private object.
     if (!workerIsAcceptingEvents) {
       return true;
     }
 
     return ReportError(aCx, parent, fireAtScope, aWorkerPrivate, mMessage,
                        mFilename, mLine, mLineNumber, mColumnNumber, mFlags,
-                       mErrorNumber, innerWindowId);
+                       mErrorNumber, mExnType, innerWindowId);
   }
 };
 
 class TimerRunnable final : public WorkerRunnable
 {
 public:
   explicit TimerRunnable(WorkerPrivate* aWorkerPrivate)
   : WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount)
@@ -6378,16 +6380,17 @@ WorkerPrivate::ReportError(JSContext* aC
   NS_ASSERTION(mErrorHandlerRecursionCount == 0 ||
                mErrorHandlerRecursionCount == 1,
                "Bad recursion logic!");
 
   JS_ClearPendingException(aCx);
 
   nsString message, filename, line;
   uint32_t lineNumber, columnNumber, flags, errorNumber;
+  JSExnType exnType = JSEXN_ERR;
 
   if (aReport) {
     // ErrorEvent objects don't have a |name| field the way ES |Error| objects
     // do. Traditionally (and mostly by accident), the |message| field of
     // ErrorEvent has corresponded to |Name: Message| of the original Error
     // object. Things have been cleaned up in the JS engine, so now we need to
     // format this string explicitly.
     JS::Rooted<JSString*> messageStr(aCx,
@@ -6399,16 +6402,18 @@ WorkerPrivate::ReportError(JSContext* aC
       }
     }
     filename = NS_ConvertUTF8toUTF16(aReport->filename);
     line = aReport->uclinebuf;
     lineNumber = aReport->lineno;
     columnNumber = aReport->uctokenptr - aReport->uclinebuf;
     flags = aReport->flags;
     errorNumber = aReport->errorNumber;
+    MOZ_ASSERT(aReport->exnType >= JSEXN_NONE && aReport->exnType < JSEXN_LIMIT);
+    exnType = JSExnType(aReport->exnType);
   }
   else {
     lineNumber = columnNumber = errorNumber = 0;
     flags = nsIScriptError::errorFlag | nsIScriptError::exceptionFlag;
   }
 
   if (message.IsEmpty()) {
     message = NS_ConvertUTF8toUTF16(aMessage);
@@ -6420,17 +6425,17 @@ WorkerPrivate::ReportError(JSContext* aC
   // if there was an error in the close handler or if we ran out of memory.
   bool fireAtScope = mErrorHandlerRecursionCount == 1 &&
                      !mCloseHandlerStarted &&
                      errorNumber != JSMSG_OUT_OF_MEMORY &&
                      JS::CurrentGlobalOrNull(aCx);
 
   if (!ReportErrorRunnable::ReportError(aCx, this, fireAtScope, nullptr, message,
                                         filename, line, lineNumber,
-                                        columnNumber, flags, errorNumber, 0)) {
+                                        columnNumber, flags, errorNumber, exnType, 0)) {
     JS_ReportPendingException(aCx);
   }
 
   mErrorHandlerRecursionCount--;
 }
 
 int32_t
 WorkerPrivate::SetTimeout(JSContext* aCx,
--- a/dom/workers/test/serviceworkers/test_installation_simple.html
+++ b/dom/workers/test/serviceworkers/test_installation_simple.html
@@ -85,19 +85,19 @@
         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 with TypeError");
+        ok(false, "redirection should fail");
       }, function (e) {
-        ok(e.name === "TypeError", "redirection should fail with TypeError");
+        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) {
deleted file mode 100644
--- a/testing/web-platform/mozilla/meta/service-workers/service-worker/registration.https.html.ini
+++ /dev/null
@@ -1,50 +0,0 @@
-[registration.https.html]
-  type: testharness
-  [Registering non-existent script]
-    expected: FAIL
-
-  [Registering invalid chunked encoding script]
-    expected: FAIL
-
-  [Registering invalid chunked encoding script with flush]
-    expected: FAIL
-
-  [Registering script with no MIME type]
-    expected: FAIL
-
-  [Registering script with bad MIME type]
-    expected: FAIL
-
-  [Registering redirected script]
-    expected: FAIL
-
-  [Registering script including parse error]
-    expected: FAIL
-
-  [Registering script including undefined error]
-    expected: FAIL
-
-  [Registering script including uncaught exception]
-    expected: FAIL
-
-  [Registering script including caught exception]
-    expected: FAIL
-
-  [Registering script importing malformed script]
-    expected: FAIL
-
-  [Registering script importing non-existent script]
-    expected: FAIL
-
-  [Script URL including URL-encoded slash]
-    expected: FAIL
-
-  [Scope including URL-encoded slash]
-    expected: FAIL
-
-  [Script URL including URL-encoded backslash]
-    expected: FAIL
-
-  [Scope including URL-encoded backslash]
-    expected: FAIL
-
--- a/testing/web-platform/mozilla/tests/service-workers/service-worker/registration.https.html
+++ b/testing/web-platform/mozilla/tests/service-workers/service-worker/registration.https.html
@@ -82,35 +82,35 @@ promise_test(function(t) {
         'Registration script outside domain should fail with SecurityError.');
   }, 'Registering script outside domain');
 
 promise_test(function(t) {
     var script = 'resources/no-such-worker.js';
     var scope = 'resources/scope/no-such-worker';
     return assert_promise_rejects(
         navigator.serviceWorker.register(script, {scope: scope}),
-        'NetworkError',
+        new TypeError(),
         'Registration of non-existent script should fail.');
   }, 'Registering non-existent script');
 
 promise_test(function(t) {
     var script = 'resources/invalid-chunked-encoding.py';
     var scope = 'resources/scope/invalid-chunked-encoding/';
     return assert_promise_rejects(
         navigator.serviceWorker.register(script, {scope: scope}),
-        'NetworkError',
+        new TypeError(),
         'Registration of invalid chunked encoding script should fail.');
   }, 'Registering invalid chunked encoding script');
 
 promise_test(function(t) {
     var script = 'resources/invalid-chunked-encoding-with-flush.py';
     var scope = 'resources/scope/invalid-chunked-encoding-with-flush/';
     return assert_promise_rejects(
         navigator.serviceWorker.register(script, {scope: scope}),
-        'NetworkError',
+        new TypeError(),
         'Registration of invalid chunked encoding script should fail.');
   }, 'Registering invalid chunked encoding script with flush');
 
 promise_test(function(t) {
     var script = 'resources/mime-type-worker.py';
     var scope = 'resources/scope/no-mime-type-worker/';
     return assert_promise_rejects(
         navigator.serviceWorker.register(script, {scope: scope}),
@@ -137,26 +137,26 @@ promise_test(function(t) {
         'Registration of redirected script should fail.');
   }, 'Registering redirected script');
 
 promise_test(function(t) {
     var script = 'resources/malformed-worker.py?parse-error';
     var scope = 'resources/scope/parse-error';
     return assert_promise_rejects(
         navigator.serviceWorker.register(script, {scope: scope}),
-        'AbortError',
+        new SyntaxError(),
         'Registration of script including parse error should fail.');
   }, 'Registering script including parse error');
 
 promise_test(function(t) {
     var script = 'resources/malformed-worker.py?undefined-error';
     var scope = 'resources/scope/undefined-error';
     return assert_promise_rejects(
         navigator.serviceWorker.register(script, {scope: scope}),
-        'AbortError',
+        new ReferenceError(),
         'Registration of script including undefined error should fail.');
   }, 'Registering script including undefined error');
 
 promise_test(function(t) {
     var script = 'resources/malformed-worker.py?uncaught-exception';
     var scope = 'resources/scope/uncaught-exception';
     return assert_promise_rejects(
         navigator.serviceWorker.register(script, {scope: scope}),
@@ -175,17 +175,17 @@ promise_test(function(t) {
           })
   }, 'Registering script including caught exception');
 
 promise_test(function(t) {
     var script = 'resources/malformed-worker.py?import-malformed-script';
     var scope = 'resources/scope/import-malformed-script';
     return assert_promise_rejects(
         navigator.serviceWorker.register(script, {scope: scope}),
-        'AbortError',
+        new SyntaxError(),
         'Registration of script importing malformed script should fail.');
   }, 'Registering script importing malformed script');
 
 promise_test(function(t) {
     var script = 'resources/malformed-worker.py?import-no-such-script';
     var scope = 'resources/scope/import-no-such-script';
     return assert_promise_rejects(
         navigator.serviceWorker.register(script, {scope: scope}),
@@ -223,44 +223,44 @@ promise_test(function(t) {
         });
   }, 'Scope including non-escaped multibyte characters');
 
 promise_test(function(t) {
     var script = 'resources%2fempty-worker.js';
     var scope = 'resources/scope/encoded-slash-in-script-url';
     return assert_promise_rejects(
         navigator.serviceWorker.register(script, {scope: scope}),
-        'SecurityError',
+        new TypeError(),
         'URL-encoded slash in the script URL should be rejected.');
   }, 'Script URL including URL-encoded slash');
 
 promise_test(function(t) {
     var script = 'resources/empty-worker.js';
     var scope = 'resources/scope%2fencoded-slash-in-scope';
     return assert_promise_rejects(
         navigator.serviceWorker.register(script, {scope: scope}),
-        'SecurityError',
+        new TypeError(),
         'URL-encoded slash in the scope should be rejected.');
   }, 'Scope including URL-encoded slash');
 
 promise_test(function(t) {
     var script = 'resources%5cempty-worker.js';
     var scope = 'resources/scope/encoded-slash-in-script-url';
     return assert_promise_rejects(
         navigator.serviceWorker.register(script, {scope: scope}),
-        'SecurityError',
+        new TypeError(),
         'URL-encoded backslash in the script URL should be rejected.');
   }, 'Script URL including URL-encoded backslash');
 
 promise_test(function(t) {
     var script = 'resources/empty-worker.js';
     var scope = 'resources/scope%5cencoded-slash-in-scope';
     return assert_promise_rejects(
         navigator.serviceWorker.register(script, {scope: scope}),
-        'SecurityError',
+        new TypeError(),
         'URL-encoded backslash in the scope should be rejected.');
   }, 'Scope including URL-encoded backslash');
 
 promise_test(function(t) {
     var script = 'resources/././empty-worker.js';
     var scope = 'resources/scope/parent-reference-in-script-url';
     return navigator.serviceWorker.register(script, {scope: scope})
       .then(function(registration) {
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/invalid-chunked-encoding-with-flush.py
@@ -0,0 +1,12 @@
+import time
+def main(request, response):
+    print dir(response)
+    response.headers.set("Content-Type", "application/javascript")
+    response.headers.set("Transfer-encoding", "chunked")
+    response.write_status_headers()
+
+    time.sleep(1)
+    response.explicit_flush = True
+
+    response.writer.write("XX\r\n\r\n")
+    response.writer.flush()
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/invalid-chunked-encoding.py
@@ -0,0 +1,2 @@
+def main(request, response):
+    return [("Content-Type", "application/javascript"), ("Transfer-encoding", "chunked")], "XX\r\n\r\n"
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/malformed-worker.py
@@ -0,0 +1,10 @@
+def main(request, response):
+    headers = [("Content-Type", "application/javascript")]
+
+    body = {'parse-error': 'var foo = function() {;',
+            'undefined-error': 'foo.bar = 42;',
+            'uncaught-exception': 'throw new DOMException("AbortError");',
+            'caught-exception': 'try { throw new Error; } catch(e) {}',
+            'import-malformed-script': 'importScripts("malformed-worker.py?parse-error");',
+            'import-no-such-script': 'importScripts("no-such-script.js");'}[request.url_parts.query]
+    return headers, body
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/mime-type-worker.py
@@ -0,0 +1,4 @@
+def main(request, response):
+    if 'mime' in request.GET:
+        return [('Content-Type', request.GET['mime'])], ""
+    return [], ""
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/redirect.py
@@ -0,0 +1,25 @@
+def main(request, response):
+    if 'Status' in request.GET:
+        status = int(request.GET["Status"])
+    else:
+        status = 302
+
+    headers = []
+
+    url = request.GET['Redirect']
+    headers.append(("Location", url))
+
+    if "ACAOrigin" in request.GET:
+        for item in request.GET["ACAOrigin"].split(","):
+            headers.append(("Access-Control-Allow-Origin", item))
+
+    for suffix in ["Headers", "Methods", "Credentials"]:
+        query = "ACA%s" % suffix
+        header = "Access-Control-Allow-%s" % suffix
+        if query in request.GET:
+            headers.append((header, request.GET[query]))
+
+    if "ACEHeaders" in request.GET:
+        headers.append(("Access-Control-Expose-Headers", request.GET["ACEHeaders"]))
+
+    return status, headers, ""