Bug 919885: Move Worker to WebIDL and thread-agnostic event dispatch. r=bent,bz
☠☠ backed out by 30afbcdcec4d ☠ ☠
authorKyle Huey <khuey@kylehuey.com>
Mon, 14 Oct 2013 19:58:05 +0800
changeset 164485 db9190b3ce60b9fdab6cc6a17894645a123c8826
parent 164484 70466645fa43e45f38751cf44e9f295fbba29cf0
child 164486 04610078280c26218abe22c773d9b8dfe462febd
push id3066
push userakeybl@mozilla.com
push dateMon, 09 Dec 2013 19:58:46 +0000
treeherdermozilla-beta@a31a0dce83aa [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbent, bz
bugs919885
milestone27.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 919885: Move Worker to WebIDL and thread-agnostic event dispatch. r=bent,bz
dom/base/nsDOMClassInfo.cpp
dom/bindings/Bindings.conf
dom/bindings/Codegen.py
dom/bindings/Configuration.py
dom/bindings/test/TestBindingHeader.h
dom/tests/mochitest/general/test_interfaces.html
dom/webidl/Worker.webidl
dom/webidl/moz.build
dom/workers/RuntimeService.cpp
dom/workers/RuntimeService.h
dom/workers/SharedWorker.cpp
dom/workers/SharedWorker.h
dom/workers/Worker.cpp
dom/workers/Worker.h
dom/workers/WorkerPrivate.cpp
dom/workers/WorkerPrivate.h
dom/workers/WorkerScope.cpp
dom/workers/moz.build
toolkit/components/osfile/modules/osfile_async_front.jsm
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -136,19 +136,16 @@
 
 // Storage includes
 #include "nsIDOMStorage.h"
 #include "nsPIDOMStorage.h"
 
 // Drag and drop
 #include "nsIDOMDataTransfer.h"
 
-// Workers
-#include "mozilla/dom/workers/Workers.h"
-
 #include "nsIDOMFile.h"
 #include "nsDOMBlobBuilder.h" // nsDOMMultipartFile
 
 #include "nsIEventListenerService.h"
 #include "nsIMessageManager.h"
 #include "nsIDOMMediaQueryList.h"
 
 #include "nsDOMTouchEvent.h"
@@ -189,17 +186,16 @@
 #include "nsIInterfaceInfoManager.h"
 
 #ifdef MOZ_TIME_MANAGER
 #include "TimeManager.h"
 #endif
 
 using namespace mozilla;
 using namespace mozilla::dom;
-using mozilla::dom::workers::ResolveWorkerClasses;
 
 static NS_DEFINE_CID(kDOMSOF_CID, NS_DOM_SCRIPT_OBJECT_FACTORY_CID);
 
 // NOTE: DEFAULT_SCRIPTABLE_FLAGS and DOM_DEFAULT_SCRIPTABLE_FLAGS
 //       are defined in nsIDOMClassInfo.h.
 
 #define WINDOW_SCRIPTABLE_FLAGS                                               \
  (nsIXPCScriptable::WANT_PRECREATE |                                          \
@@ -3600,26 +3596,16 @@ nsWindowSH::NewResolve(nsIXPConnectWrapp
                                JSPROP_ENUMERATE)) {
       return NS_ERROR_FAILURE;
     }
     *objp = obj;
 
     return NS_OK;
   }
 
-  // Handle resolving if id refers to a name resolved by DOM worker code.
-  JS::RootedObject tmp(cx, NULL);
-  if (!ResolveWorkerClasses(cx, obj, id, flags, &tmp)) {
-    return NS_ERROR_FAILURE;
-  }
-  if (tmp) {
-    *objp = tmp;
-    return NS_OK;
-  }
-
   // Check for names managed by the script namespace manager.  Call
   // GlobalResolve() after we call FindChildWithName() so that named child
   // frames will override external properties which have been registered with
   // the script namespace manager -- pages must be able to depend on frame
   // names working no matter how Gecko's been configured.
   bool did_resolve = false;
   nsresult rv = GlobalResolve(win, cx, obj, id, &did_resolve);
   NS_ENSURE_SUCCESS(rv, rv);
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -26,16 +26,19 @@
 #                interfaces.  Defaults to True otherwise.
 #   * workers - Indicates whether the descriptor is intended to be used solely
 #               for worker threads (defaults to false). If true the interface
 #               will not be made available on the main thread.
 #   * customTrace - The native class will use a custom trace hook (defaults to
 #                   true for workers, false otherwise).
 #   * customFinalize - The native class will use a custom finalize hook
 #                      (defaults to true for workers, false otherwise).
+#   * customWrapperManagement - The native class will be responsible for
+#                               preserving its own wrapper (no AddProperty
+#                               hook will be generated, defaults to false).
 #   * notflattened - The native type does not have nsIClassInfo, so when
 #                    wrapping it the right IID needs to be passed in.
 #   * register - True if this binding should be registered.  Defaults to true.
 #   * binaryNames - Dict for mapping method and attribute names to different
 #                   names when calling the native methods (defaults to an empty
 #                   dict). The keys are the property names as they appear in the
 #                   .webidl file and the values are the names as they should be
 #                   in the WebIDL.
@@ -198,16 +201,23 @@ DOMInterfaces = {
     'nativeType': 'nsDOMCaretPosition',
 },
 
 'CharacterData': {
     'nativeType': 'nsGenericDOMDataNode',
     'concrete': False
 },
 
+'ChromeWorker': {
+    'headerFile': 'mozilla/dom/WorkerPrivate.h',
+    'nativeType': 'mozilla::dom::workers::ChromeWorkerPrivate',
+    'customFinalize': True,
+    'customWrapperManagement': True,
+},
+
 'DOMRectList': {
     'headerFile': 'mozilla/dom/DOMRect.h',
     'resultNotAddRefed': [ 'item' ]
 },
 
 'ClipboardEvent': {
     'nativeType': 'nsDOMClipboardEvent',
 },
@@ -1420,16 +1430,26 @@ DOMInterfaces = {
     'concrete': False
 },
 {
     # We need a worker descriptor for WindowProxy because EventTarget exists in
     # workers.  But it's an external interface, so it'll just map to JSObject*.
     'workers': True
 }],
 
+'Worker': {
+    'headerFile': 'mozilla/dom/WorkerPrivate.h',
+    'nativeType': 'mozilla::dom::workers::WorkerPrivate',
+    'implicitJSContext': [
+        'terminate',
+    ],
+    'customFinalize': True,
+    'customWrapperManagement': True,
+},
+
 'WorkerLocation': {
     'headerFile': 'mozilla/dom/workers/bindings/Location.h',
     'workers': True,
 },
 
 'WorkerMessagePort': [{
     'nativeType': 'mozilla::dom::workers::MessagePort',
     'headerFile': 'mozilla/dom/workers/bindings/MessagePort.h',
@@ -1861,9 +1881,9 @@ addExternalIface('CameraCapabilities', n
 addExternalIface('CameraAutoFocusCallback', nativeType='nsICameraAutoFocusCallback', headerFile='nsIDOMCameraManager.h')
 addExternalIface('CameraShutterCallback', nativeType='nsICameraShutterCallback', headerFile='nsIDOMCameraManager.h')
 addExternalIface('CameraClosedCallback', nativeType='nsICameraClosedCallback', headerFile='nsIDOMCameraManager.h')
 addExternalIface('CameraTakePictureCallback', nativeType='nsICameraTakePictureCallback', headerFile='nsIDOMCameraManager.h')
 addExternalIface('CameraReleaseCallback', nativeType='nsICameraReleaseCallback', headerFile='nsIDOMCameraManager.h')
 addExternalIface('CameraStartRecordingCallback', nativeType='nsICameraStartRecordingCallback', headerFile='nsIDOMCameraManager.h')
 addExternalIface('CameraPreviewStateChange', nativeType='nsICameraPreviewStateChange', headerFile='nsIDOMCameraManager.h')
 addExternalIface('CameraPreviewStreamCallback', nativeType='nsICameraPreviewStreamCallback', headerFile='nsIDOMCameraManager.h')
-addExternalIface('CameraRecorderStateChange', nativeType='nsICameraRecorderStateChange', headerFile='nsIDOMCameraManager.h')
\ No newline at end of file
+addExternalIface('CameraRecorderStateChange', nativeType='nsICameraRecorderStateChange', headerFile='nsIDOMCameraManager.h')
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -48,16 +48,20 @@ def replaceFileIfChanged(filename, newCo
     return True
 
 def toStringBool(arg):
     return str(not not arg).lower()
 
 def toBindingNamespace(arg):
     return re.sub("((_workers)?$)", "Binding\\1", arg);
 
+def wantsAddProperty(desc):
+    return desc.concrete and not desc.nativeOwnership == 'worker' and \
+           desc.wrapperCache and not desc.customWrapperManagement
+
 class CGThing():
     """
     Abstract base class for things that spit out code.
     """
     def __init__(self):
         pass # Nothing for now
     def declare(self):
         """Produce code for a header file."""
@@ -204,17 +208,17 @@ static const DOMJSClass Class = {
     nullptr,               /* construct */
     %s, /* trace */
     JSCLASS_NO_INTERNAL_MEMBERS
   },
 %s
 };
 """ % (self.descriptor.interface.identifier.name,
        classFlags,
-       ADDPROPERTY_HOOK_NAME if self.descriptor.concrete and not self.descriptor.nativeOwnership == 'worker' and self.descriptor.wrapperCache else 'JS_PropertyStub',
+       ADDPROPERTY_HOOK_NAME if wantsAddProperty(self.descriptor) else 'JS_PropertyStub',
        enumerateHook, newResolveHook, FINALIZE_HOOK_NAME, callHook, traceHook,
        CGIndenter(CGGeneric(DOMClass(self.descriptor))).define())
 
 def PrototypeIDAndDepth(descriptor):
     prototypeID = "prototypes::id::"
     if descriptor.interface.hasInterfacePrototypeObject():
         prototypeID += descriptor.interface.identifier.name
         if descriptor.workers:
@@ -692,19 +696,26 @@ class CGHeaders(CGWrapper):
                         callbacks))
 
         # Now for non-callback descriptors make sure we include any
         # headers needed by Func declarations.
         for desc in descriptors:
             if desc.interface.isExternal():
                 continue
             def addHeaderForFunc(func):
+                if func is None:
+                    return
                 # Include the right class header, which we can only do
                 # if this is a class member function.
-                if func is not None and "::" in func:
+                if not desc.headerIsDefault:
+                    # An explicit header file was provided, assume that we know
+                    # what we're doing.
+                    return
+
+                if "::" in func:
                     # Strip out the function name and convert "::" to "/"
                     bindingHeaders.add("/".join(func.split("::")[:-1]) + ".h")
             for m in desc.interface.members:
                 addHeaderForFunc(PropertyDefiner.getStringAttr(m, "Func"))
             # getExtendedAttribute() returns a list, extract the entry.
             funcList = desc.interface.getExtendedAttribute("Func")
             if funcList is not None:
                 addHeaderForFunc(funcList[0])
@@ -8028,17 +8039,17 @@ class CGDescriptor(CGThing):
         if hasLenientSetter: cgThings.append(CGGenericSetter(descriptor,
                                                              lenientThis=True))
 
         if descriptor.interface.getNavigatorProperty():
             cgThings.append(CGConstructNavigatorObjectHelper(descriptor))
             cgThings.append(CGConstructNavigatorObject(descriptor))
 
         if descriptor.concrete and not descriptor.proxy:
-            if not descriptor.nativeOwnership == 'worker' and descriptor.wrapperCache:
+            if wantsAddProperty(descriptor):
                 cgThings.append(CGAddPropertyHook(descriptor))
 
             # Always have a finalize hook, regardless of whether the class
             # wants a custom hook.
             cgThings.append(CGClassFinalizeHook(descriptor))
 
             # Only generate a trace hook if the class wants a custom hook.
             if (descriptor.customTrace):
--- a/dom/bindings/Configuration.py
+++ b/dom/bindings/Configuration.py
@@ -233,16 +233,17 @@ class Descriptor(DescriptorProvider):
             if self.workers:
                 headerDefault = "mozilla/dom/workers/bindings/%s.h" % ifaceName
             elif not self.interface.isExternal() and self.interface.getExtendedAttribute("HeaderFile"):
                 headerDefault = self.interface.getExtendedAttribute("HeaderFile")[0]
             else:
                 headerDefault = self.nativeType
                 headerDefault = headerDefault.replace("::", "/") + ".h"
         self.headerFile = desc.get('headerFile', headerDefault)
+        self.headerIsDefault = self.headerFile == headerDefault
         if self.jsImplParent == self.nativeType:
             self.jsImplParentHeader = self.headerFile
         else:
             self.jsImplParentHeader = self.jsImplParent.replace("::", "/") + ".h"
 
         self.skipGen = desc.get('skipGen', False)
 
         self.notflattened = desc.get('notflattened', False)
@@ -351,22 +352,27 @@ class Descriptor(DescriptorProvider):
         else:
             self.nativeOwnership = desc.get('nativeOwnership', 'refcounted')
             if not self.nativeOwnership in ['owned', 'refcounted']:
                 raise TypeError("Descriptor for %s has unrecognized value (%s) "
                                 "for nativeOwnership" %
                                 (self.interface.identifier.name, self.nativeOwnership))
         self.customTrace = desc.get('customTrace', self.nativeOwnership == 'worker')
         self.customFinalize = desc.get('customFinalize', self.nativeOwnership == 'worker')
+        self.customWrapperManagement = desc.get('customWrapperManagement', False)
         if desc.get('wantsQI', None) != None:
             self._wantsQI = desc.get('wantsQI', None)
         self.wrapperCache = (not self.interface.isCallback() and
                              (self.nativeOwnership == 'worker' or
                               (self.nativeOwnership != 'owned' and
                                desc.get('wrapperCache', True))))
+        if self.customWrapperManagement and not self.wrapperCache:
+            raise TypeError("Descriptor for %s has customWrapperManagement "
+                            "but is not wrapperCached." %
+                            (self.interface.identifier.name))
 
         def make_name(name):
             return name + "_workers" if self.workers else name
         self.name = make_name(interface.identifier.name)
 
         # self.extendedAttributes is a dict of dicts, keyed on
         # all/getterOnly/setterOnly and then on member name. Values are an
         # array of extended attributes.
--- a/dom/bindings/test/TestBindingHeader.h
+++ b/dom/bindings/test/TestBindingHeader.h
@@ -8,16 +8,17 @@
 #define TestBindingHeader_h
 
 #include "mozilla/dom/BindingUtils.h"
 #include "mozilla/dom/Date.h"
 #include "mozilla/dom/TypedArray.h"
 #include "mozilla/dom/UnionTypes.h"
 #include "mozilla/ErrorResult.h"
 #include "nsCOMPtr.h"
+#include "nsGenericHTMLElement.h"
 #include "nsWrapperCache.h"
 
 // Forward declare this before we include TestCodeGenBinding.h, because that header relies on including
 // this one for it, for ParentDict. Hopefully it won't begin to rely on it in more fundamental ways.
 namespace mozilla {
 namespace dom {
 class TestExternalInterface;
 } // namespace dom
--- a/dom/tests/mochitest/general/test_interfaces.html
+++ b/dom/tests/mochitest/general/test_interfaces.html
@@ -574,16 +574,17 @@ var interfaceNamesInGlobalScope =
     "WebGLShaderPrecisionFormat",
     "WebGLTexture",
     "WebGLUniformLocation",
     "WebGLVertexArray",
     "WebSocket",
     "WheelEvent",
     "Window",
     "WindowUtils",
+    "Worker",
     "XMLDocument",
     "XMLHttpRequest",
     "XMLHttpRequestUpload",
     "XMLSerializer",
     "XMLStylesheetProcessingInstruction",
     "XPathEvaluator",
     "XPathExpression",
     "XPathNamespace",
new file mode 100644
--- /dev/null
+++ b/dom/webidl/Worker.webidl
@@ -0,0 +1,31 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * The origin of this IDL file is
+ * http://www.whatwg.org/specs/web-apps/current-work/multipage/workers.html
+ *
+ * © Copyright 2004-2011 Apple Computer, Inc., Mozilla Foundation, and Opera
+ * Software ASA.
+ * You are granted a license to use, reproduce and create derivative works of
+ * this document.
+ */
+
+[Constructor(DOMString scriptURL),
+ Func="mozilla::dom::workers::WorkerPrivate::WorkerAvailable"]
+interface Worker : EventTarget {
+  void terminate();
+
+  [Throws]
+  void postMessage(any message, optional sequence<any> transfer);
+
+  attribute EventHandler onmessage;
+};
+
+Worker implements AbstractWorker;
+
+[Constructor(DOMString scriptURL),
+ Func="mozilla::dom::workers::ChromeWorkerPrivate::WorkerAvailable"]
+interface ChromeWorker : Worker {
+};
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -405,16 +405,17 @@ WEBIDL_FILES = [
     'VideoPlaybackQuality.webidl',
     'VideoStreamTrack.webidl',
     'WaveShaperNode.webidl',
     'WebComponents.webidl',
     'WebSocket.webidl',
     'WheelEvent.webidl',
     'WifiOptions.webidl',
     'Window.webidl',
+    'Worker.webidl',
     'WorkerLocation.webidl',
     'WorkerMessagePort.webidl',
     'WorkerNavigator.webidl',
     'XMLDocument.webidl',
     'XMLHttpRequest.webidl',
     'XMLHttpRequestEventTarget.webidl',
     'XMLHttpRequestUpload.webidl',
     'XMLSerializer.webidl',
--- a/dom/workers/RuntimeService.cpp
+++ b/dom/workers/RuntimeService.cpp
@@ -23,17 +23,20 @@
 
 #include <algorithm>
 #include "GeckoProfiler.h"
 #include "js/OldDebugAPI.h"
 #include "jsfriendapi.h"
 #include "mozilla/CycleCollectedJSRuntime.h"
 #include "mozilla/dom/AtomList.h"
 #include "mozilla/dom/BindingUtils.h"
+#include "mozilla/dom/ErrorEventBinding.h"
 #include "mozilla/dom/EventTargetBinding.h"
+#include "mozilla/dom/MessageEventBinding.h"
+#include "mozilla/dom/WorkerBinding.h"
 #include "mozilla/DebugOnly.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Util.h"
 #include <Navigator.h>
 #include "nsContentUtils.h"
 #include "nsCycleCollector.h"
 #include "nsDOMJSUtils.h"
 #include "nsLayoutStatics.h"
@@ -43,17 +46,16 @@
 #include "nsTraceRefcnt.h"
 #include "nsXPCOM.h"
 #include "nsXPCOMPrivate.h"
 #include "OSFileConstants.h"
 #include "xpcpublic.h"
 
 #include "Events.h"
 #include "SharedWorker.h"
-#include "Worker.h"
 #include "WorkerPrivate.h"
 
 #ifdef MOZ_NUWA_PROCESS
 #include "ipc/Nuwa.h"
 #endif
 
 using namespace mozilla;
 using namespace mozilla::dom;
@@ -89,17 +91,16 @@ static_assert(MAX_WORKERS_PER_DOMAIN >= 
 
 // The number of seconds that idle threads can hang around before being killed.
 #define IDLE_THREAD_TIMEOUT_SEC 30
 
 // The maximum number of threads that can be idle at one time.
 #define MAX_IDLE_THREADS 20
 
 #define PREF_WORKERS_PREFIX "dom.workers."
-#define PREF_WORKERS_ENABLED PREF_WORKERS_PREFIX "enabled"
 #define PREF_WORKERS_MAX_PER_DOMAIN PREF_WORKERS_PREFIX "maxPerDomain"
 
 #define PREF_MAX_SCRIPT_RUN_TIME_CONTENT "dom.max_script_run_time"
 #define PREF_MAX_SCRIPT_RUN_TIME_CHROME "dom.max_chrome_script_run_time"
 
 #define GC_REQUEST_OBSERVER_TOPIC "child-gc-request"
 #define MEMORY_PRESSURE_OBSERVER_TOPIC "memory-pressure"
 
@@ -156,19 +157,19 @@ enum {
 };
 
 // These are jsids for the main runtime. Only touched on the main thread.
 jsid gStringIDs[ID_COUNT] = { JSID_VOID };
 
 const char* gStringChars[] = {
   "Worker",
   "ChromeWorker",
-  "WorkerEvent",
-  "WorkerMessageEvent",
-  "WorkerErrorEvent"
+  "Event",
+  "MessageEvent",
+  "ErrorEvent"
 
   // XXX Don't care about ProgressEvent since it should never leak to the main
   // thread.
 };
 
 static_assert(NS_ARRAY_LENGTH(gStringChars) == ID_COUNT,
               "gStringChars should have the right length.");
 
@@ -961,82 +962,60 @@ public:
     return NS_OK;
   }
 };
 
 } /* anonymous namespace */
 
 BEGIN_WORKERS_NAMESPACE
 
-// Entry point for the DOM.
+// Entry point for main thread non-window globals.
 bool
 ResolveWorkerClasses(JSContext* aCx, JS::Handle<JSObject*> aObj, JS::Handle<jsid> aId,
                      unsigned aFlags, JS::MutableHandle<JSObject*> aObjp)
 {
   AssertIsOnMainThread();
+  MOZ_ASSERT(nsContentUtils::IsCallerChrome());
 
   // Make sure our strings are interned.
   if (JSID_IS_VOID(gStringIDs[0])) {
     for (uint32_t i = 0; i < ID_COUNT; i++) {
       JSString* str = JS_InternString(aCx, gStringChars[i]);
       if (!str) {
         while (i) {
           gStringIDs[--i] = JSID_VOID;
         }
         return false;
       }
       gStringIDs[i] = INTERNED_STRING_TO_JSID(aCx, str);
     }
   }
 
-  bool isChrome = false;
   bool shouldResolve = false;
 
   for (uint32_t i = 0; i < ID_COUNT; i++) {
     if (gStringIDs[i] == aId) {
-      isChrome = nsContentUtils::IsCallerChrome();
-
-      // Don't resolve if this is ChromeWorker and we're not chrome. Otherwise
-      // always resolve.
-      shouldResolve = gStringIDs[ID_ChromeWorker] == aId ? isChrome : true;
+      shouldResolve = true;
       break;
     }
   }
 
-  if (shouldResolve) {
-    // Don't do anything if workers are disabled.
-    if (!isChrome && !Preferences::GetBool(PREF_WORKERS_ENABLED)) {
-      aObjp.set(nullptr);
-      return true;
-    }
-
-    JSObject* eventTarget = EventTargetBinding_workers::GetProtoObject(aCx, aObj);
-    if (!eventTarget) {
-      return false;
-    }
-
-    JSObject* worker = worker::InitClass(aCx, aObj, eventTarget, true);
-    if (!worker) {
-      return false;
-    }
-
-    if (isChrome && !chromeworker::InitClass(aCx, aObj, worker, true)) {
-      return false;
-    }
-
-    if (!events::InitClasses(aCx, aObj, true)) {
-      return false;
-    }
-
-    aObjp.set(aObj);
+  if (!shouldResolve) {
+    aObjp.set(nullptr);
     return true;
   }
 
-  // Not resolved.
-  aObjp.set(nullptr);
+  if (!WorkerBinding::GetConstructorObject(aCx, aObj) ||
+      !ChromeWorkerBinding::GetConstructorObject(aCx, aObj) ||
+      !ErrorEventBinding::GetConstructorObject(aCx, aObj) ||
+      !MessageEventBinding::GetConstructorObject(aCx, aObj)) {
+    return false;
+  }
+
+  aObjp.set(aObj);
   return true;
 }
 
 void
 CancelWorkersForWindow(nsPIDOMWindow* aWindow)
 {
   AssertIsOnMainThread();
   RuntimeService* runtime = RuntimeService::GetService();
@@ -1989,85 +1968,83 @@ RuntimeService::ResumeWorkersForWindow(n
       if (!workers[index]->SynchronizeAndResume(cx, aWindow, scx)) {
         JS_ReportPendingException(cx);
       }
     }
   }
 }
 
 nsresult
-RuntimeService::CreateSharedWorker(JSContext* aCx, nsPIDOMWindow* aWindow,
+RuntimeService::CreateSharedWorker(const GlobalObject& aGlobal,
                                    const nsAString& aScriptURL,
                                    const nsAString& aName,
                                    SharedWorker** aSharedWorker)
 {
   AssertIsOnMainThread();
-  MOZ_ASSERT(aCx);
-  MOZ_ASSERT(aWindow);
+
+  nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aGlobal.GetAsSupports());
+  MOZ_ASSERT(window);
+
+  JSContext* cx = aGlobal.GetContext();
 
   WorkerPrivate::LoadInfo loadInfo;
-  nsresult rv = WorkerPrivate::GetLoadInfo(aCx, aWindow, nullptr, aScriptURL,
+  nsresult rv = WorkerPrivate::GetLoadInfo(cx, window, nullptr, aScriptURL,
                                            false, &loadInfo);
   NS_ENSURE_SUCCESS(rv, rv);
 
   MOZ_ASSERT(loadInfo.mResolvedScriptURI);
 
   nsCString scriptSpec;
   rv = loadInfo.mResolvedScriptURI->GetSpec(scriptSpec);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  WorkerPrivate* workerPrivate = nullptr;
+  nsRefPtr<WorkerPrivate> workerPrivate;
   {
     MutexAutoLock lock(mMutex);
 
     WorkerDomainInfo* domainInfo;
     SharedWorkerInfo* sharedWorkerInfo;
 
     if (mDomainMap.Get(loadInfo.mDomain, &domainInfo) &&
         domainInfo->mSharedWorkerInfos.Get(scriptSpec, &sharedWorkerInfo) &&
         sharedWorkerInfo->mName == aName) {
       workerPrivate = sharedWorkerInfo->mWorkerPrivate;
     }
   }
 
   bool created = false;
 
   if (!workerPrivate) {
-    nsRefPtr<WorkerPrivate> newWorkerPrivate =
-      WorkerPrivate::Create(aCx, JS::NullPtr(), nullptr, aScriptURL, false,
-                            true, aName, &loadInfo);
-    NS_ENSURE_TRUE(newWorkerPrivate, NS_ERROR_FAILURE);
-
-    if (!RegisterWorker(aCx, newWorkerPrivate)) {
-      NS_WARNING("Failed to register worker!");
-      return NS_ERROR_FAILURE;
-    }
+    ErrorResult rv;
+    workerPrivate =
+      WorkerPrivate::Constructor(aGlobal, aScriptURL, false, true, aName,
+                                 &loadInfo, rv);
+    NS_ENSURE_TRUE(workerPrivate, rv.ErrorCode());
 
     created = true;
-    newWorkerPrivate.forget(&workerPrivate);
   }
 
   MOZ_ASSERT(workerPrivate->IsSharedWorker());
 
   nsRefPtr<SharedWorker> sharedWorker =
-    new SharedWorker(aWindow, workerPrivate);
+    new SharedWorker(window, workerPrivate);
 
-  if (!workerPrivate->RegisterSharedWorker(aCx, sharedWorker)) {
+  if (!workerPrivate->RegisterSharedWorker(cx, sharedWorker)) {
     NS_WARNING("Worker is unreachable, this shouldn't happen!");
     sharedWorker->Close();
     return NS_ERROR_FAILURE;
   }
 
   // This is normally handled in RegisterWorker, but that wasn't called if the
   // worker already existed.
   if (!created) {
     nsTArray<WorkerPrivate*>* windowArray;
-    if (!mWindowMap.Get(aWindow, &windowArray)) {
+    if (!mWindowMap.Get(window, &windowArray)) {
       windowArray = new nsTArray<WorkerPrivate*>(1);
-      mWindowMap.Put(aWindow, windowArray);
+      mWindowMap.Put(window, windowArray);
     }
 
     if (!windowArray->Contains(workerPrivate)) {
       windowArray->AppendElement(workerPrivate);
     }
   }
 
   sharedWorker.forget(aSharedWorker);
--- a/dom/workers/RuntimeService.h
+++ b/dom/workers/RuntimeService.h
@@ -140,18 +140,19 @@ public:
 
   void
   SuspendWorkersForWindow(nsPIDOMWindow* aWindow);
 
   void
   ResumeWorkersForWindow(nsPIDOMWindow* aWindow);
 
   nsresult
-  CreateSharedWorker(JSContext* aCx, nsPIDOMWindow* aWindow,
-                     const nsAString& aScriptURL, const nsAString& aName,
+  CreateSharedWorker(const GlobalObject& aGlobal,
+                     const nsAString& aScriptURL,
+                     const nsAString& aName,
                      SharedWorker** aSharedWorker);
 
   void
   ForgetSharedWorker(WorkerPrivate* aWorkerPrivate);
 
   const nsACString&
   GetDetectorName() const
   {
--- a/dom/workers/SharedWorker.cpp
+++ b/dom/workers/SharedWorker.cpp
@@ -11,17 +11,16 @@
 #include "mozilla/dom/SharedWorkerBinding.h"
 #include "nsContentUtils.h"
 #include "nsDOMEvent.h"
 #include "nsEventDispatcher.h"
 #include "nsIClassInfoImpl.h"
 
 #include "MessagePort.h"
 #include "RuntimeService.h"
-#include "Worker.h"
 #include "WorkerPrivate.h"
 
 using mozilla::dom::Optional;
 using mozilla::dom::Sequence;
 
 USING_WORKERS_NAMESPACE
 
 namespace {
@@ -62,32 +61,29 @@ SharedWorker::PrefEnabled()
 already_AddRefed<SharedWorker>
 SharedWorker::Constructor(const GlobalObject& aGlobal, JSContext* aCx,
                           const nsAString& aScriptURL,
                           const mozilla::dom::Optional<nsAString>& aName,
                           ErrorResult& aRv)
 {
   AssertIsOnMainThread();
 
-  nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aGlobal.GetAsSupports());
-  MOZ_ASSERT(window);
-
   RuntimeService* rts = RuntimeService::GetOrCreateService();
   if (!rts) {
     aRv = NS_ERROR_NOT_AVAILABLE;
     return nullptr;
   }
 
   nsString name;
   if (aName.WasPassed()) {
     name = aName.Value();
   }
 
   nsRefPtr<SharedWorker> sharedWorker;
-  nsresult rv = rts->CreateSharedWorker(aCx, window, aScriptURL, name,
+  nsresult rv = rts->CreateSharedWorker(aGlobal, aScriptURL, name,
                                         getter_AddRefs(sharedWorker));
   if (NS_FAILED(rv)) {
     aRv = rv;
     return nullptr;
   }
 
   return sharedWorker.forget();
 }
--- a/dom/workers/SharedWorker.h
+++ b/dom/workers/SharedWorker.h
@@ -23,17 +23,17 @@ class WorkerPrivate;
 class SharedWorker MOZ_FINAL : public nsDOMEventTargetHelper
 {
   friend class MessagePort;
   friend class RuntimeService;
 
   typedef mozilla::ErrorResult ErrorResult;
   typedef mozilla::dom::GlobalObject GlobalObject;
 
-  WorkerPrivate* mWorkerPrivate;
+  nsRefPtr<WorkerPrivate> mWorkerPrivate;
   nsRefPtr<MessagePort> mMessagePort;
   nsTArray<nsCOMPtr<nsIDOMEvent>> mSuspendedEvents;
   uint64_t mSerial;
   bool mSuspended;
 
 public:
   static bool
   PrefEnabled();
@@ -78,17 +78,18 @@ public:
   virtual JSObject*
   WrapObject(JSContext* aCx, JS::HandleObject aScope) MOZ_OVERRIDE;
 
   virtual nsresult
   PreHandleEvent(nsEventChainPreVisitor& aVisitor) MOZ_OVERRIDE;
 
 private:
   // This class can only be created from the RuntimeService.
-  SharedWorker(nsPIDOMWindow* aWindow, WorkerPrivate* aWorkerPrivate);
+  SharedWorker(nsPIDOMWindow* aWindow,
+               WorkerPrivate* aWorkerPrivate);
 
   // This class is reference-counted and will be destroyed from Release().
   ~SharedWorker();
 
   // Only called by MessagePort.
   void
   PostMessage(JSContext* aCx, JS::HandleValue aMessage,
               const Optional<Sequence<JS::Value>>& aTransferable,
deleted file mode 100644
--- a/dom/workers/Worker.cpp
+++ /dev/null
@@ -1,672 +0,0 @@
-/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "Worker.h"
-
-#include "mozilla/dom/DOMJSClass.h"
-#include "mozilla/dom/BindingUtils.h"
-#include "mozilla/dom/EventHandlerBinding.h"
-#include "nsJSUtils.h"
-
-#include "jsapi.h"
-#include "EventTarget.h"
-#include "RuntimeService.h"
-#include "WorkerPrivate.h"
-
-#include "WorkerInlines.h"
-
-#define FUNCTION_FLAGS \
-  JSPROP_ENUMERATE
-
-USING_WORKERS_NAMESPACE
-
-using namespace mozilla::dom;
-using mozilla::ErrorResult;
-
-namespace {
-
-class Worker
-{
-  static const DOMJSClass sClass;
-  static const DOMIfaceAndProtoJSClass sProtoClass;
-  static const JSPropertySpec sProperties[];
-  static const JSFunctionSpec sFunctions[];
-
-protected:
-  enum {
-    // The constructor function holds a WorkerPrivate* in its first reserved
-    // slot.
-    CONSTRUCTOR_SLOT_PARENT = 0
-  };
-
-public:
-  static const JSClass*
-  Class()
-  {
-    return sClass.ToJSClass();
-  }
-
-  static const JSClass*
-  ProtoClass()
-  {
-    return sProtoClass.ToJSClass();
-  }
-
-  static const DOMClass*
-  DOMClassStruct()
-  {
-    return &sClass.mClass;
-  }
-
-  static JSObject*
-  InitClass(JSContext* aCx, JSObject* aObj, JSObject* aParentProto,
-            bool aMainRuntime)
-  {
-    JS::Rooted<JSObject*> proto(aCx,
-      js::InitClassWithReserved(aCx, aObj, aParentProto, ProtoClass(),
-                                Construct, 0, sProperties, sFunctions,
-                                NULL, NULL));
-    if (!proto) {
-      return NULL;
-    }
-
-    js::SetReservedSlot(proto, DOM_PROTO_INSTANCE_CLASS_SLOT,
-                        JS::PrivateValue(const_cast<DOMClass *>(DOMClassStruct())));
-
-    if (!aMainRuntime) {
-      WorkerPrivate* parent = GetWorkerPrivateFromContext(aCx);
-      parent->AssertIsOnWorkerThread();
-
-      JSObject* constructor = JS_GetConstructor(aCx, proto);
-      if (!constructor)
-        return NULL;
-      js::SetFunctionNativeReserved(constructor, CONSTRUCTOR_SLOT_PARENT,
-                                    PRIVATE_TO_JSVAL(parent));
-    }
-
-    return proto;
-  }
-
-  static WorkerPrivate*
-  GetInstancePrivate(JSContext* aCx, JSObject* aObj, const char* aFunctionName);
-
-  static JSObject*
-  Create(JSContext* aCx, WorkerPrivate* aParentObj, const nsAString& aScriptURL,
-         bool aIsChromeWorker, bool aIsSharedWorker,
-         const nsAString& aSharedWorkerName);
-
-protected:
-  static bool
-  ConstructInternal(JSContext* aCx, JS::CallArgs aArgs, bool aIsChromeWorker)
-  {
-    if (!aArgs.length()) {
-      JS_ReportError(aCx, "Constructor requires at least one argument!");
-      return false;
-    }
-
-    JS::RootedString scriptURLStr(aCx, JS_ValueToString(aCx, aArgs[0]));
-    if (!scriptURLStr) {
-      return false;
-    }
-
-    nsDependentJSString scriptURL;
-    if (!scriptURL.init(aCx, scriptURLStr)) {
-      return false;
-    }
-
-    JS::Rooted<JS::Value> priv(aCx,
-      js::GetFunctionNativeReserved(&aArgs.callee(), CONSTRUCTOR_SLOT_PARENT));
-
-    WorkerPrivate* parent;
-    if (priv.isUndefined()) {
-      parent = NULL;
-    } else {
-      parent = static_cast<WorkerPrivate*>(priv.get().toPrivate());
-      parent->AssertIsOnWorkerThread();
-    }
-
-    JS::Rooted<JSObject*> obj(aCx,
-      Create(aCx, parent, scriptURL, aIsChromeWorker, false, EmptyString()));
-    if (!obj) {
-      return false;
-    }
-
-    aArgs.rval().setObject(*obj);
-    return true;
-  }
-
-private:
-  // No instance of this class should ever be created so these are explicitly
-  // left without an implementation to prevent linking in case someone tries to
-  // make one.
-  Worker();
-  ~Worker();
-
-  static bool
-  IsWorker(JS::Handle<JS::Value> v)
-  {
-    return v.isObject() && ClassIsWorker(JS_GetClass(&v.toObject()));
-  }
-
-  static bool
-  GetEventListener(JSContext* aCx, const JS::CallArgs aArgs,
-                   const nsAString &aNameStr)
-  {
-    WorkerPrivate* worker =
-      GetInstancePrivate(aCx, &aArgs.thisv().toObject(),
-                         NS_ConvertUTF16toUTF8(aNameStr).get());
-    MOZ_ASSERT(worker);
-
-    ErrorResult rv;
-    nsRefPtr<EventHandlerNonNull> handler =
-      worker->GetEventListener(Substring(aNameStr, 2), rv);
-
-    if (rv.Failed()) {
-      JS_ReportError(aCx, "Failed to get listener!");
-      return false;
-    }
-
-    if (!handler) {
-      aArgs.rval().setNull();
-    } else {
-      aArgs.rval().setObject(*handler->Callable());
-    }
-    return true;
-  }
-
-  static bool
-  GetOnerrorImpl(JSContext* aCx, JS::CallArgs aArgs)
-  {
-    return GetEventListener(aCx, aArgs, NS_LITERAL_STRING("onerror"));
-  }
-
-  static bool
-  GetOnerror(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
-  {
-    JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp);
-    return JS::CallNonGenericMethod<IsWorker, GetOnerrorImpl>(aCx, args);
-  }
-
-  static bool
-  GetOnmessageImpl(JSContext* aCx, JS::CallArgs aArgs)
-  {
-    return GetEventListener(aCx, aArgs, NS_LITERAL_STRING("onmessage"));
-  }
-
-  static bool
-  GetOnmessage(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
-  {
-    JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp);
-    return JS::CallNonGenericMethod<IsWorker, GetOnmessageImpl>(aCx, args);
-  }
-
-  static bool
-  SetEventListener(JSContext* aCx, JS::CallArgs aArgs,
-                   const nsAString& aNameStr)
-  {
-    WorkerPrivate* worker =
-      GetInstancePrivate(aCx, &aArgs.thisv().toObject(),
-                         NS_ConvertUTF16toUTF8(aNameStr).get());
-    MOZ_ASSERT(worker);
-
-    JS::Rooted<JSObject*> listener(aCx);
-    if (!JS_ValueToObject(aCx, aArgs.get(0), &listener)) {
-      return false;
-    }
-
-    nsRefPtr<EventHandlerNonNull> handler;
-    if (listener && JS_ObjectIsCallable(aCx, listener)) {
-      handler = new EventHandlerNonNull(listener);
-    } else {
-      handler = nullptr;
-    }
-    ErrorResult rv;
-    worker->SetEventListener(Substring(aNameStr, 2), handler, rv);
-
-    if (rv.Failed()) {
-      JS_ReportError(aCx, "Failed to set listener!");
-      return false;
-    }
-
-    aArgs.rval().setUndefined();
-    return true;
-  }
-
-  static bool
-  SetOnerrorImpl(JSContext* aCx, JS::CallArgs aArgs)
-  {
-    return SetEventListener(aCx, aArgs, NS_LITERAL_STRING("onerror"));
-  }
-
-  static bool
-  SetOnerror(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
-  {
-    JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp);
-    return JS::CallNonGenericMethod<IsWorker, SetOnerrorImpl>(aCx, args);
-  }
-
-  static bool
-  SetOnmessageImpl(JSContext* aCx, JS::CallArgs aArgs)
-  {
-    return SetEventListener(aCx, aArgs, NS_LITERAL_STRING("onmessage"));
-  }
-
-  static bool
-  SetOnmessage(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
-  {
-    JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp);
-    return JS::CallNonGenericMethod<IsWorker, SetOnmessageImpl>(aCx, args);
-  }
-
-  static bool
-  Construct(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
-  {
-    JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp);
-    return ConstructInternal(aCx, args, false);
-  }
-
-  static void
-  Finalize(JSFreeOp* aFop, JSObject* aObj)
-  {
-    JS_ASSERT(JS_GetClass(aObj) == Class());
-    WorkerPrivate* worker = UnwrapDOMObject<WorkerPrivate>(aObj);
-    if (worker) {
-      worker->_finalize(aFop);
-    }
-  }
-
-  static void
-  Trace(JSTracer* aTrc, JSObject* aObj)
-  {
-    JS_ASSERT(JS_GetClass(aObj) == Class());
-    WorkerPrivate* worker = UnwrapDOMObject<WorkerPrivate>(aObj);
-    if (worker) {
-      worker->_trace(aTrc);
-    }
-  }
-
-  static bool
-  Terminate(JSContext* aCx, unsigned aArgc, jsval* aVp)
-  {
-    JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
-    if (!obj) {
-      return false;
-    }
-
-    const char* name = sFunctions[0].name;
-    WorkerPrivate* worker = GetInstancePrivate(aCx, obj, name);
-    if (!worker) {
-      return !JS_IsExceptionPending(aCx);
-    }
-
-    if (!worker->Terminate(aCx)) {
-      return false;
-    }
-
-    JS_RVAL(aCx, aVp).setUndefined();
-    return true;
-  }
-
-  static bool
-  PostMessage(JSContext* aCx, unsigned aArgc, jsval* aVp)
-  {
-    JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
-    if (!obj) {
-      return false;
-    }
-
-    const char* name = sFunctions[1].name;
-    WorkerPrivate* worker = GetInstancePrivate(aCx, obj, name);
-    if (!worker) {
-      return !JS_IsExceptionPending(aCx);
-    }
-
-    JS::Rooted<JS::Value> message(aCx);
-    JS::Rooted<JS::Value> transferable(aCx, JS::UndefinedValue());
-    if (!JS_ConvertArguments(aCx, aArgc, JS_ARGV(aCx, aVp), "v/v",
-                             message.address(), transferable.address())) {
-      return false;
-    }
-
-    if (!worker->PostMessage(aCx, message, transferable)) {
-      return false;
-    }
-
-    JS_RVAL(aCx, aVp).setUndefined();
-    return true;
-  }
-};
-
-const DOMJSClass Worker::sClass = {
-  {
-    "Worker",
-    JSCLASS_IS_DOMJSCLASS | JSCLASS_HAS_RESERVED_SLOTS(3) |
-    JSCLASS_IMPLEMENTS_BARRIERS,
-    JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
-    JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, Finalize,
-    NULL, NULL, NULL, NULL, Trace
-  },
-  {
-    INTERFACE_CHAIN_1(prototypes::id::EventTarget_workers),
-    false,
-    &sWorkerNativePropertyHooks
-  }
-};
-
-const DOMIfaceAndProtoJSClass Worker::sProtoClass = {
-  {
-    // XXXbz we use "Worker" here to match sClass so that we can
-    // js::InitClassWithReserved this JSClass and then call
-    // JS_NewObject with our sClass and have it find the right
-    // prototype.
-    "Worker",
-    JSCLASS_IS_DOMIFACEANDPROTOJSCLASS | JSCLASS_HAS_RESERVED_SLOTS(2),
-    JS_PropertyStub,       /* addProperty */
-    JS_DeletePropertyStub, /* delProperty */
-    JS_PropertyStub,       /* getProperty */
-    JS_StrictPropertyStub, /* setProperty */
-    JS_EnumerateStub,
-    JS_ResolveStub,
-    JS_ConvertStub,
-    nullptr,               /* finalize */
-    nullptr,               /* checkAccess */
-    nullptr,               /* call */
-    nullptr,               /* hasInstance */
-    nullptr,               /* construct */
-    nullptr,               /* trace */
-    JSCLASS_NO_INTERNAL_MEMBERS
-  },
-  eInterfacePrototype,
-  &sWorkerNativePropertyHooks,
-  "[object Worker]",
-  prototypes::id::_ID_Count,
-  0
-};
-
-const JSPropertySpec Worker::sProperties[] = {
-  JS_PSGS("onerror", GetOnerror, SetOnerror, JSPROP_ENUMERATE),
-  JS_PSGS("onmessage", GetOnmessage, SetOnmessage, JSPROP_ENUMERATE),
-  JS_PS_END
-};
-
-const JSFunctionSpec Worker::sFunctions[] = {
-  JS_FN("terminate", Terminate, 0, FUNCTION_FLAGS),
-  JS_FN("postMessage", PostMessage, 1, FUNCTION_FLAGS),
-  JS_FS_END
-};
-
-class ChromeWorker : public Worker
-{
-  static const DOMJSClass sClass;
-  static const DOMIfaceAndProtoJSClass sProtoClass;
-
-public:
-  static const JSClass*
-  Class()
-  {
-    return sClass.ToJSClass();
-  }
-
-  static const JSClass*
-  ProtoClass()
-  {
-    return sProtoClass.ToJSClass();
-  }
-
-  static const DOMClass*
-  DOMClassStruct()
-  {
-    return &sClass.mClass;
-  }
-
-  static JSObject*
-  InitClass(JSContext* aCx, JSObject* aObj, JSObject* aParentProto,
-            bool aMainRuntime)
-  {
-    JS::Rooted<JSObject*> proto(aCx,
-      js::InitClassWithReserved(aCx, aObj, aParentProto, ProtoClass(),
-                                Construct, 0, NULL, NULL, NULL, NULL));
-    if (!proto) {
-      return NULL;
-    }
-
-    js::SetReservedSlot(proto, DOM_PROTO_INSTANCE_CLASS_SLOT,
-                        JS::PrivateValue(const_cast<DOMClass *>(DOMClassStruct())));
-
-    if (!aMainRuntime) {
-      WorkerPrivate* parent = GetWorkerPrivateFromContext(aCx);
-      parent->AssertIsOnWorkerThread();
-
-      JSObject* constructor = JS_GetConstructor(aCx, proto);
-      if (!constructor)
-        return NULL;
-      js::SetFunctionNativeReserved(constructor, CONSTRUCTOR_SLOT_PARENT,
-                                    PRIVATE_TO_JSVAL(parent));
-    }
-
-    return proto;
-  }
-
-private:
-  // No instance of this class should ever be created so these are explicitly
-  // left without an implementation to prevent linking in case someone tries to
-  // make one.
-  ChromeWorker();
-  ~ChromeWorker();
-
-  static WorkerPrivate*
-  GetInstancePrivate(JSContext* aCx, JSObject* aObj, const char* aFunctionName)
-  {
-    if (aObj) {
-      const JSClass* classPtr = JS_GetClass(aObj);
-      if (classPtr == Class()) {
-        return UnwrapDOMObject<WorkerPrivate>(aObj);
-      }
-    }
-
-    return Worker::GetInstancePrivate(aCx, aObj, aFunctionName);
-  }
-
-  static bool
-  Construct(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
-  {
-    JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp);
-    return ConstructInternal(aCx, args, true);
-  }
-
-  static void
-  Finalize(JSFreeOp* aFop, JSObject* aObj)
-  {
-    JS_ASSERT(JS_GetClass(aObj) == Class());
-    WorkerPrivate* worker = UnwrapDOMObject<WorkerPrivate>(aObj);
-    if (worker) {
-      worker->_finalize(aFop);
-    }
-  }
-
-  static void
-  Trace(JSTracer* aTrc, JSObject* aObj)
-  {
-    JS_ASSERT(JS_GetClass(aObj) == Class());
-    WorkerPrivate* worker = UnwrapDOMObject<WorkerPrivate>(aObj);
-    if (worker) {
-      worker->_trace(aTrc);
-    }
-  }
-};
-
-const DOMJSClass ChromeWorker::sClass = {
-  { "ChromeWorker",
-    JSCLASS_IS_DOMJSCLASS | JSCLASS_HAS_RESERVED_SLOTS(3) |
-    JSCLASS_IMPLEMENTS_BARRIERS,
-    JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
-    JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, Finalize,
-    NULL, NULL, NULL, NULL, Trace,
-  },
-  {
-    INTERFACE_CHAIN_1(prototypes::id::EventTarget_workers),
-    false,
-    &sWorkerNativePropertyHooks
-  }
-};
-
-const DOMIfaceAndProtoJSClass ChromeWorker::sProtoClass = {
-  {
-    // XXXbz we use "ChromeWorker" here to match sClass so that we can
-    // js::InitClassWithReserved this JSClass and then call
-    // JS_NewObject with our sClass and have it find the right
-    // prototype.
-    "ChromeWorker",
-    JSCLASS_IS_DOMIFACEANDPROTOJSCLASS | JSCLASS_HAS_RESERVED_SLOTS(2),
-    JS_PropertyStub,       /* addProperty */
-    JS_DeletePropertyStub, /* delProperty */
-    JS_PropertyStub,       /* getProperty */
-    JS_StrictPropertyStub, /* setProperty */
-    JS_EnumerateStub,
-    JS_ResolveStub,
-    JS_ConvertStub,
-    nullptr,               /* finalize */
-    nullptr,               /* checkAccess */
-    nullptr,               /* call */
-    nullptr,               /* hasInstance */
-    nullptr,               /* construct */
-    nullptr,               /* trace */
-    JSCLASS_NO_INTERNAL_MEMBERS
-  },
-  eInterfacePrototype,
-  &sWorkerNativePropertyHooks,
-  "[object ChromeWorker]",
-  prototypes::id::_ID_Count,
-  0
-};
-
-WorkerPrivate*
-Worker::GetInstancePrivate(JSContext* aCx, JSObject* aObj,
-                           const char* aFunctionName)
-{
-  const JSClass* classPtr = JS_GetClass(aObj);
-  if (ClassIsWorker(classPtr)) {
-    return UnwrapDOMObject<WorkerPrivate>(aObj);
-  }
-
-  JS_ReportErrorNumber(aCx, js_GetErrorMessage, NULL, JSMSG_INCOMPATIBLE_PROTO,
-                       Class()->name, aFunctionName, classPtr->name);
-  return NULL;
-}
-
-JSObject*
-Worker::Create(JSContext* aCx, WorkerPrivate* aParent,
-               const nsAString& aScriptURL, bool aIsChromeWorker,
-               bool aIsSharedWorker, const nsAString& aSharedWorkerName)
-{
-  MOZ_ASSERT_IF(aIsSharedWorker, !aSharedWorkerName.IsVoid());
-  MOZ_ASSERT_IF(!aIsSharedWorker, aSharedWorkerName.IsEmpty());
-
-  RuntimeService* runtimeService;
-  if (aParent) {
-    runtimeService = RuntimeService::GetService();
-    NS_ASSERTION(runtimeService, "Null runtime service!");
-  }
-  else {
-    runtimeService = RuntimeService::GetOrCreateService();
-    if (!runtimeService) {
-      JS_ReportError(aCx, "Failed to create runtime service!");
-      return nullptr;
-    }
-  }
-
-  const JSClass* classPtr = aIsChromeWorker ? ChromeWorker::Class() : Class();
-
-  JS::Rooted<JSObject*> obj(aCx,
-    JS_NewObject(aCx, const_cast<JSClass*>(classPtr), nullptr, nullptr));
-  if (!obj) {
-    return nullptr;
-  }
-
-  // Ensure that the DOM_OBJECT_SLOT always has a PrivateValue set, as this will
-  // be accessed in the Trace() method if WorkerPrivate::Create() triggers a GC.
-  js::SetReservedSlot(obj, DOM_OBJECT_SLOT, JS::PrivateValue(nullptr));
-
-  nsRefPtr<WorkerPrivate> worker =
-    WorkerPrivate::Create(aCx, obj, aParent, aScriptURL, aIsChromeWorker,
-                          aIsSharedWorker, aSharedWorkerName);
-  if (!worker) {
-    // It'd be better if we could avoid allocating the JSObject until after we
-    // make sure we have a WorkerPrivate, but failing that we should at least
-    // make sure that the DOM_OBJECT_SLOT always has a PrivateValue.
-    return nullptr;
-  }
-
-  // Worker now owned by the JS object.
-  NS_ADDREF(worker.get());
-  js::SetReservedSlot(obj, DOM_OBJECT_SLOT, JS::PrivateValue(worker));
-
-  if (!runtimeService->RegisterWorker(aCx, worker)) {
-    return nullptr;
-  }
-
-  // Worker now also owned by its thread.
-  NS_ADDREF(worker.get());
-
-  return obj;
-}
-
-} // anonymous namespace
-
-BEGIN_WORKERS_NAMESPACE
-
-namespace worker {
-
-JSObject*
-InitClass(JSContext* aCx, JSObject* aGlobal, JSObject* aProto,
-          bool aMainRuntime)
-{
-  return Worker::InitClass(aCx, aGlobal, aProto, aMainRuntime);
-}
-
-} // namespace worker
-
-WorkerCrossThreadDispatcher*
-GetWorkerCrossThreadDispatcher(JSContext* aCx, jsval aWorker)
-{
-  if (JSVAL_IS_PRIMITIVE(aWorker)) {
-    return NULL;
-  }
-
-  WorkerPrivate* w =
-      Worker::GetInstancePrivate(aCx, JSVAL_TO_OBJECT(aWorker),
-                                 "GetWorkerCrossThreadDispatcher");
-  if (!w) {
-    return NULL;
-  }
-  return w->GetCrossThreadDispatcher();
-}
-
-
-namespace chromeworker {
-
-bool
-InitClass(JSContext* aCx, JSObject* aGlobal, JSObject* aProto,
-          bool aMainRuntime)
-{
-  return !!ChromeWorker::InitClass(aCx, aGlobal, aProto, aMainRuntime);
-}
-
-} // namespace chromeworker
-
-bool
-ClassIsWorker(const JSClass* aClass)
-{
-  return Worker::Class() == aClass || ChromeWorker::Class() == aClass;
-}
-
-bool
-GetterOnlyJSNative(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
-{
-    JS_ReportErrorNumber(aCx, js_GetErrorMessage, nullptr, JSMSG_GETTER_ONLY);
-    return false;
-}
-
-END_WORKERS_NAMESPACE
deleted file mode 100644
--- a/dom/workers/Worker.h
+++ /dev/null
@@ -1,35 +0,0 @@
-/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#ifndef mozilla_dom_workers_worker_h__
-#define mozilla_dom_workers_worker_h__
-
-#include "Workers.h"
-
-
-BEGIN_WORKERS_NAMESPACE
-
-namespace worker {
-
-JSObject*
-InitClass(JSContext* aCx, JSObject* aGlobal, JSObject* aProto,
-          bool aMainRuntime);
-
-} // namespace worker
-
-namespace chromeworker {
-
-bool
-InitClass(JSContext* aCx, JSObject* aGlobal, JSObject* aProto,
-          bool aMainRuntime);
-
-} // namespace chromeworker
-
-bool
-ClassIsWorker(const JSClass* aClass);
-
-END_WORKERS_NAMESPACE
-
-#endif /* mozilla_dom_workers_worker_h__ */
\ No newline at end of file
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -8,16 +8,17 @@
 
 #include "amIAddonManager.h"
 #include "nsIClassInfo.h"
 #include "nsIContentSecurityPolicy.h"
 #include "nsIConsoleService.h"
 #include "nsIDOMDOMException.h"
 #include "nsIDOMEvent.h"
 #include "nsIDOMFile.h"
+#include "nsIDOMMessageEvent.h"
 #include "nsIDocument.h"
 #include "nsIDocShell.h"
 #include "nsIMemoryReporter.h"
 #include "nsIPermissionManager.h"
 #include "nsIScriptError.h"
 #include "nsIScriptGlobalObject.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsPIDOMWindow.h"
@@ -30,23 +31,27 @@
 #include <algorithm>
 #include "jsfriendapi.h"
 #include "js/OldDebugAPI.h"
 #include "js/MemoryMetrics.h"
 #include "mozilla/ContentEvents.h"
 #include "mozilla/Likely.h"
 #include "mozilla/dom/BindingUtils.h"
 #include "mozilla/dom/ErrorEvent.h"
+#include "mozilla/dom/ErrorEventBinding.h"
 #include "mozilla/dom/ImageData.h"
 #include "mozilla/dom/ImageDataBinding.h"
+#include "mozilla/dom/WorkerBinding.h"
+#include "mozilla/Preferences.h"
 #include "mozilla/Util.h"
 #include "nsAlgorithm.h"
 #include "nsContentUtils.h"
 #include "nsCxPusher.h"
 #include "nsError.h"
+#include "nsEventDispatcher.h"
 #include "nsDOMMessageEvent.h"
 #include "nsDOMJSUtils.h"
 #include "nsHostObjectProtocolHandler.h"
 #include "nsJSEnvironment.h"
 #include "nsJSUtils.h"
 #include "nsNetUtil.h"
 #include "nsPrintfCString.h"
 #include "nsProxyRelease.h"
@@ -61,27 +66,28 @@
 #include "Events.h"
 #include "mozilla/dom/Exceptions.h"
 #include "File.h"
 #include "MessagePort.h"
 #include "Principal.h"
 #include "RuntimeService.h"
 #include "ScriptLoader.h"
 #include "SharedWorker.h"
-#include "Worker.h"
 #include "WorkerFeature.h"
 #include "WorkerMessagePort.h"
 #include "WorkerScope.h"
 
 // GC will run once every thirty seconds during normal execution.
 #define NORMAL_GC_TIMER_DELAY_MS 30000
 
 // GC will run five seconds after the last event is processed.
 #define IDLE_GC_TIMER_DELAY_MS 5000
 
+#define PREF_WORKERS_ENABLED "dom.workers.enabled"
+
 using mozilla::InternalScriptErrorEvent;
 using mozilla::MutexAutoLock;
 using mozilla::TimeDuration;
 using mozilla::TimeStamp;
 using mozilla::dom::Throw;
 using mozilla::AutoCxPusher;
 using mozilla::AutoPushJSContext;
 using mozilla::AutoSafeJSContext;
@@ -883,66 +889,90 @@ public:
     mClonedObjects.SwapElements(aClonedObjects);
   }
 
   bool
   WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
   {
     MOZ_ASSERT_IF(mToMessagePort, aWorkerPrivate->IsSharedWorker());
 
-    bool mainRuntime;
-    JS::Rooted<JSObject*> target(aCx);
     if (mTarget == ParentThread) {
       // Don't fire this event if the JS object has been disconnected from the
       // private object.
       if (!aWorkerPrivate->IsAcceptingEvents()) {
         return true;
       }
 
       if (mToMessagePort) {
         return
           aWorkerPrivate->DispatchMessageEventToMessagePort(aCx,
                                                             mMessagePortSerial,
                                                             mBuffer,
                                                             mClonedObjects);
       }
 
-      mainRuntime = !aWorkerPrivate->GetParent();
-
-      target = aWorkerPrivate->GetJSObject();
-      NS_ASSERTION(target, "Must have a target!");
-
       if (aWorkerPrivate->IsSuspended()) {
         aWorkerPrivate->QueueRunnable(this);
         return true;
       }
 
       aWorkerPrivate->AssertInnerWindowIsCorrect();
+
+      // Release reference to objects that were AddRef'd for
+      // cloning into worker when array goes out of scope.
+      nsTArray<nsCOMPtr<nsISupports> > clonedObjects;
+      clonedObjects.SwapElements(mClonedObjects);
+
+      JS::Rooted<JS::Value> messageData(aCx);
+      if (!mBuffer.read(aCx, messageData.address(),
+                        workers::WorkerStructuredCloneCallbacks(!aWorkerPrivate->GetParent()))) {
+        xpc::Throw(aCx, NS_ERROR_DOM_DATA_CLONE_ERR);
+        return false;
+      }
+
+      nsRefPtr<nsDOMMessageEvent> event =
+        new nsDOMMessageEvent(aWorkerPrivate, nullptr, nullptr);
+      nsresult rv =
+        event->InitMessageEvent(NS_LITERAL_STRING("message"),
+                                false /* non-bubbling */,
+                                true /* cancelable */,
+                                messageData,
+                                EmptyString(),
+                                EmptyString(),
+                                nullptr);
+      if (NS_FAILED(rv)) {
+        xpc::Throw(aCx, rv);
+        return false;
+      }
+
+      event->SetTrusted(true);
+
+      nsCOMPtr<nsIDOMEvent> domEvent = do_QueryObject(event);
+
+      nsEventStatus dummy = nsEventStatus_eIgnore;
+      aWorkerPrivate->DispatchDOMEvent(nullptr, domEvent, nullptr, &dummy);
+      return true;
     }
-    else {
-      NS_ASSERTION(aWorkerPrivate == GetWorkerPrivateFromContext(aCx),
-                   "Badness!");
-      if (mToMessagePort) {
-        WorkerMessagePort* port =
-          aWorkerPrivate->GetMessagePort(mMessagePortSerial);
-        if (!port) {
-          // Must have been closed already.
-          return true;
-        }
-        return port->MaybeDispatchEvent(aCx, mBuffer, mClonedObjects);
+
+    MOZ_ASSERT(aWorkerPrivate == GetWorkerPrivateFromContext(aCx));
+    if (mToMessagePort) {
+      nsRefPtr<WorkerMessagePort> port =
+        aWorkerPrivate->GetMessagePort(mMessagePortSerial);
+      if (!port) {
+        // Must have been closed already.
+        return true;
       }
-
-      mainRuntime = false;
-      target = JS::CurrentGlobalOrNull(aCx);
+      return port->MaybeDispatchEvent(aCx, mBuffer, mClonedObjects);
     }
 
+    JS::Rooted<JSObject*> target(aCx, JS::CurrentGlobalOrNull(aCx));
     NS_ASSERTION(target, "This should never be null!");
 
     JS::Rooted<JSObject*> event(aCx,
-      CreateMessageEvent(aCx, mBuffer, mClonedObjects, mainRuntime));
+      CreateMessageEvent(aCx, mBuffer, mClonedObjects, false));
     if (!event) {
       return false;
     }
 
     bool dummy;
     return DispatchEventToTarget(aCx, target, event, &dummy);
   }
 
@@ -973,16 +1003,27 @@ public:
     return aWorkerPrivate->ModifyBusyCount(aCx, true);
   }
 
   bool
   WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
   {
     return aWorkerPrivate->NotifyInternal(aCx, mStatus);
   }
+
+  void
+  PostDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
+               bool aDispatchResult)
+  {
+    if (!aDispatchResult) {
+      // We couldn't dispatch to the worker, which means it's already dead.
+      // Undo the busy count modification.
+      aWorkerPrivate->ModifyBusyCount(aCx, false);
+    }
+  }
 };
 
 class CloseRunnable MOZ_FINAL : public WorkerControlRunnable
 {
 public:
   CloseRunnable(WorkerPrivate* aWorkerPrivate)
   : WorkerControlRunnable(aWorkerPrivate, ParentThread, UnchangedBusyCount)
   { }
@@ -1060,17 +1101,17 @@ public:
   WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
   {
     // Don't fire this event if the JS object has been disconnected from the
     // private object.
     if (!aWorkerPrivate->IsAcceptingEvents()) {
       return true;
     }
 
-    JS::Rooted<JSObject*> target(aCx, aWorkerPrivate->GetJSObject());
+    JS::Rooted<JSObject*> target(aCx, aWorkerPrivate->GetWrapper());
 
     uint64_t innerWindowId;
     bool fireAtScope = true;
 
     WorkerPrivate* parent = aWorkerPrivate->GetParent();
     if (parent) {
       innerWindowId = 0;
     }
@@ -1089,35 +1130,39 @@ public:
         return true;
       }
 
       aWorkerPrivate->AssertInnerWindowIsCorrect();
 
       innerWindowId = aWorkerPrivate->GetInnerWindowId();
     }
 
-    return ReportErrorRunnable::ReportError(aCx, parent, fireAtScope, target,
-                                            mMessage, mFilename, mLine,
-                                            mLineNumber, mColumnNumber, mFlags,
+    return ReportErrorRunnable::ReportError(aCx, parent, fireAtScope,
+                                            aWorkerPrivate, mMessage,
+                                            mFilename, mLine, mLineNumber,
+                                            mColumnNumber, mFlags,
                                             mErrorNumber, innerWindowId);
   }
 
   void PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate, bool aRunResult)
   {
     WorkerRunnable::PostRun(aCx, aWorkerPrivate, aRunResult);
   }
 
+  // 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, JSObject* aTarget, const nsString& aMessage,
-              const nsString& aFilename, const nsString& aLine,
-              uint32_t aLineNumber, uint32_t aColumnNumber, uint32_t aFlags,
+              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)
   {
-    JS::Rooted<JSObject*> target(aCx, aTarget);
     if (aWorkerPrivate) {
       aWorkerPrivate->AssertIsOnWorkerThread();
     }
     else {
       AssertIsOnMainThread();
     }
 
     JS::Rooted<JSString*> message(aCx, JS_NewUCStringCopyN(aCx, aMessage.get(),
@@ -1131,37 +1176,38 @@ public:
     if (!filename) {
       return false;
     }
 
     // We should not fire error events for warnings but instead make sure that
     // they show up in the error console.
     if (!JSREPORT_IS_WARNING(aFlags)) {
       // First fire an ErrorEvent at the worker.
-      if (target) {
-        JS::Rooted<JSObject*> event(aCx,
-          CreateErrorEvent(aCx, message, filename, aLineNumber, !aWorkerPrivate));
-        if (!event) {
-          return false;
-        }
-
-        bool preventDefaultCalled;
-        if (!DispatchEventToTarget(aCx, target, event, &preventDefaultCalled)) {
-          return false;
-        }
-
-        if (preventDefaultCalled) {
+      if (aTarget) {
+        ErrorEventInit init;
+        init.mMessage = aMessage;
+        init.mFilename = aFilename;
+        init.mLineno = aLineNumber;
+        init.mCancelable = true;
+
+        nsRefPtr<ErrorEvent> event =
+          ErrorEvent::Constructor(aTarget, NS_LITERAL_STRING("error"), init);
+
+        nsEventStatus status = nsEventStatus_eIgnore;
+        aTarget->DispatchDOMEvent(nullptr, event, nullptr, &status);
+
+        if (status == nsEventStatus_eConsumeNoDefault) {
           return true;
         }
       }
 
       // Now fire an event at the global object, but don't do that if the error
       // code is too much recursion and this is the same script threw the error.
-      if (aFireAtScope && (target || aErrorNumber != JSMSG_OVER_RECURSED)) {
-        target = JS::CurrentGlobalOrNull(aCx);
+      if (aFireAtScope && (aTarget || aErrorNumber != JSMSG_OVER_RECURSED)) {
+        JS::Rooted<JSObject*> target(aCx, JS::CurrentGlobalOrNull(aCx));
         NS_ASSERTION(target, "This should never be null!");
 
         bool preventDefaultCalled;
         nsIScriptGlobalObject* sgo;
 
         if (aWorkerPrivate ||
             !(sgo = nsJSUtils::GetStaticScriptGlobal(target))) {
           // Fire a normal ErrorEvent if we're running on a worker thread.
@@ -1749,17 +1795,17 @@ WorkerRunnable::Run()
     }
   }
 
   JS::Rooted<JSObject*> targetCompartmentObject(cx);
 
   if (mTarget == WorkerThread) {
     targetCompartmentObject = JS::CurrentGlobalOrNull(cx);
   } else {
-    targetCompartmentObject = mWorkerPrivate->GetJSObject();
+    targetCompartmentObject = mWorkerPrivate->GetWrapper();
   }
 
   NS_ASSERTION(cx, "Must have a context!");
 
   JSAutoRequest ar(cx);
 
   Maybe<JSAutoCompartment> ac;
   if (targetCompartmentObject) {
@@ -2007,40 +2053,43 @@ private:
     mRtPath.Insert(addonId, explicitLength);
   }
 };
 
 NS_IMPL_ISUPPORTS1(WorkerPrivate::MemoryReporter, nsIMemoryReporter)
 
 template <class Derived>
 WorkerPrivateParent<Derived>::WorkerPrivateParent(
-                                     JSContext* aCx,
-                                             JS::HandleObject aObject,
-                                     WorkerPrivate* aParent,
-                                     const nsAString& aScriptURL,
-                                     bool aIsChromeWorker,
+                                             JSContext* aCx,
+                                             WorkerPrivate* aParent,
+                                             const nsAString& aScriptURL,
+                                             bool aIsChromeWorker,
                                              bool aIsSharedWorker,
                                              const nsAString& aSharedWorkerName,
                                              LoadInfo& aLoadInfo)
-: EventTarget(aParent ? aCx : nullptr), mMutex("WorkerPrivateParent Mutex"),
+: mMutex("WorkerPrivateParent Mutex"),
   mCondVar(mMutex, "WorkerPrivateParent CondVar"),
   mMemoryReportCondVar(mMutex, "WorkerPrivateParent Memory Report CondVar"),
-  mJSObject(aObject), mParent(aParent), mScriptURL(aScriptURL),
+  mParent(aParent), mScriptURL(aScriptURL),
   mSharedWorkerName(aSharedWorkerName), mBusyCount(0), mMessagePortSerial(0),
-  mParentStatus(Pending), mJSObjectRooted(false), mParentSuspended(false),
+  mParentStatus(Pending), mRooted(false), mParentSuspended(false),
   mIsChromeWorker(aIsChromeWorker), mMainThreadObjectsForgotten(false),
   mIsSharedWorker(aIsSharedWorker)
 {
-  MOZ_COUNT_CTOR(mozilla::dom::workers::WorkerPrivateParent);
-  MOZ_ASSERT_IF(aIsSharedWorker, !aObject && !aSharedWorkerName.IsVoid());
-  MOZ_ASSERT_IF(!aIsSharedWorker, aObject && aSharedWorkerName.IsEmpty());
+  SetIsDOMBinding();
+
+  MOZ_ASSERT_IF(aIsSharedWorker, !aSharedWorkerName.IsVoid() &&
+                                 NS_IsMainThread());
+  MOZ_ASSERT_IF(!aIsSharedWorker, aSharedWorkerName.IsEmpty());
 
   if (aLoadInfo.mWindow) {
-    NS_ASSERTION(aLoadInfo.mWindow->IsInnerWindow(),
-                 "Should have inner window here!");
+    AssertIsOnMainThread();
+    MOZ_ASSERT(aLoadInfo.mWindow->IsInnerWindow(),
+               "Should have inner window here!");
+    BindToOwner(aLoadInfo.mWindow);
   }
 
   mLoadInfo.StealFrom(aLoadInfo);
 
   if (aParent) {
     aParent->AssertIsOnWorkerThread();
 
     aParent->CopyJSSettings(mJSSettings);
@@ -2050,27 +2099,73 @@ WorkerPrivateParent<Derived>::WorkerPriv
                                         mJSSettings.content.options),
                  "Options mismatch!");
   }
   else {
     AssertIsOnMainThread();
 
     RuntimeService::GetDefaultJSSettings(mJSSettings);
   }
-
-  if (!aIsSharedWorker) {
-    SetIsDOMBinding();
-    SetWrapper(aObject);
-  }
 }
 
 template <class Derived>
 WorkerPrivateParent<Derived>::~WorkerPrivateParent()
 {
-  MOZ_COUNT_DTOR(mozilla::dom::workers::WorkerPrivateParent);
+  MOZ_ASSERT(!mRooted);
+}
+
+template <class Derived>
+NS_IMPL_ADDREF_INHERITED(WorkerPrivateParent<Derived>, nsDOMEventTargetHelper)
+
+template <class Derived>
+NS_IMPL_RELEASE_INHERITED(WorkerPrivateParent<Derived>, nsDOMEventTargetHelper)
+
+template <class Derived>
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(WorkerPrivateParent<Derived>)
+  // No new interfaces, just cycle collection.
+NS_INTERFACE_MAP_END_INHERITING(nsDOMEventTargetHelper)
+
+template <class Derived>
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(WorkerPrivateParent<Derived>,
+                                                  nsDOMEventTargetHelper)
+  // Nothing else to traverse
+  // The various strong references in LoadInfo are managed manually and cannot
+  // be cycle collected.
+  tmp->AssertIsOnParentThread();
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+template <class Derived>
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(WorkerPrivateParent<Derived>,
+                                                nsDOMEventTargetHelper)
+  tmp->AssertIsOnParentThread();
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+template <class Derived>
+NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(WorkerPrivateParent<Derived>,
+                                               nsDOMEventTargetHelper)
+  tmp->AssertIsOnParentThread();
+NS_IMPL_CYCLE_COLLECTION_TRACE_END
+
+template <class Derived>
+JSObject*
+WorkerPrivateParent<Derived>::WrapObject(JSContext* aCx,
+                                         JS::Handle<JSObject*> aScope)
+{
+  MOZ_ASSERT(!mIsSharedWorker,
+             "We should never wrap a WorkerPrivate for a SharedWorker");
+
+  AssertIsOnParentThread();
+
+  JSObject* obj = WorkerBinding::Wrap(aCx, aScope, ParentAsWorkerPrivate());
+
+  if (mRooted) {
+    PreserveWrapper(this);
+  }
+
+  return obj;
 }
 
 template <class Derived>
 bool
 WorkerPrivateParent<Derived>::Start()
 {
   // May be called on any thread!
   {
@@ -2358,62 +2453,31 @@ WorkerPrivateParent<Derived>::Synchroniz
   }
 
   mSynchronizeRunnable = runnable;
   return true;
 }
 
 template <class Derived>
 void
-WorkerPrivateParent<Derived>::_trace(JSTracer* aTrc)
-{
-  // This should only happen on the parent thread but we can't assert that
-  // because it can also happen on the cycle collector thread when this is a
-  // top-level worker.
-  EventTarget::_trace(aTrc);
-}
-
-template <class Derived>
-void
 WorkerPrivateParent<Derived>::_finalize(JSFreeOp* aFop)
 {
   AssertIsOnParentThread();
 
-  MOZ_ASSERT(mJSObject);
-  MOZ_ASSERT(!mJSObjectRooted);
-
-  // Clear the JS object.
-  mJSObject = nullptr;
+  MOZ_ASSERT(!mRooted);
+
+  ClearWrapper();
+
+  // Ensure that we're held alive across the TerminatePrivate call, and then
+  // release the reference our wrapper held to us.
+  nsRefPtr<WorkerPrivateParent<Derived> > kungFuDeathGrip = dont_AddRef(this);
 
   if (!TerminatePrivate(nullptr)) {
     NS_WARNING("Failed to terminate!");
   }
-
-  // Before calling through to the base class we need to grab another reference
-  // if we're on the main thread. Otherwise the base class' _Finalize method
-  // will call Release, and some of our members cannot be released during
-  // finalization. Of course, if those members are already gone then we can skip
-  // this mess...
-  WorkerPrivateParent<Derived>* extraSelfRef = NULL;
-
-  if (!mParent && !mMainThreadObjectsForgotten) {
-    AssertIsOnMainThread();
-    NS_ADDREF(extraSelfRef = this);
-  }
-
-  EventTarget::_finalize(aFop);
-
-  if (extraSelfRef) {
-    nsCOMPtr<nsIRunnable> runnable =
-      NS_NewNonOwningRunnableMethod(extraSelfRef,
-                                    &WorkerPrivateParent<Derived>::Release);
-    if (NS_FAILED(NS_DispatchToCurrentThread(runnable))) {
-      NS_WARNING("Failed to proxy release, this will leak!");
-    }
-  }
 }
 
 template <class Derived>
 bool
 WorkerPrivateParent<Derived>::Close(JSContext* aCx)
 {
   AssertIsOnParentThread();
 
@@ -2432,65 +2496,63 @@ template <class Derived>
 bool
 WorkerPrivateParent<Derived>::ModifyBusyCount(JSContext* aCx, bool aIncrease)
 {
   AssertIsOnParentThread();
 
   NS_ASSERTION(aIncrease || mBusyCount, "Mismatched busy count mods!");
 
   if (aIncrease) {
-    if (mBusyCount++ == 0 && mJSObject) {
-      if (!RootJSObject(aCx, true)) {
-        return false;
-      }
+    if (mBusyCount++ == 0) {
+      Root(true);
     }
     return true;
   }
 
-  if (--mBusyCount == 0 && mJSObject) {
-    if (!RootJSObject(aCx, false)) {
-      return false;
-    }
+  if (--mBusyCount == 0) {
+    Root(false);
 
     bool shouldCancel;
     {
       MutexAutoLock lock(mMutex);
       shouldCancel = mParentStatus == Terminating;
     }
 
     if (shouldCancel && !Cancel(aCx)) {
       return false;
     }
   }
 
   return true;
 }
 
 template <class Derived>
-bool
-WorkerPrivateParent<Derived>::RootJSObject(JSContext* aCx, bool aRoot)
+void
+WorkerPrivateParent<Derived>::Root(bool aRoot)
 {
   AssertIsOnParentThread();
 
-  if (aRoot != mJSObjectRooted) {
-    if (aRoot) {
-      NS_ASSERTION(mJSObject, "Nothing to root?");
-      if (!JS_AddNamedObjectRoot(aCx, &mJSObject, "Worker root")) {
-        NS_WARNING("JS_AddNamedObjectRoot failed!");
-        return false;
-      }
+  if (aRoot == mRooted) {
+    return;
+  }
+
+  if (aRoot) {
+    NS_ADDREF_THIS();
+    if (GetWrapperPreserveColor()) {
+      PreserveWrapper(this);
     }
-    else {
-      JS_RemoveObjectRoot(aCx, &mJSObject);
+  }
+  else {
+    if (GetWrapperPreserveColor()) {
+      ReleaseWrapper(this);
     }
-
-    mJSObjectRooted = aRoot;
-  }
-
-  return true;
+    NS_RELEASE_THIS();
+  }
+
+  mRooted = aRoot;
 }
 
 template <class Derived>
 void
 WorkerPrivateParent<Derived>::ForgetMainThreadObjects(
                                       nsTArray<nsCOMPtr<nsISupports> >& aDoomed)
 {
   AssertIsOnParentThread();
@@ -2510,30 +2572,31 @@ WorkerPrivateParent<Derived>::ForgetMain
   // Before adding anything here update kDoomedCount above!
 
   MOZ_ASSERT(aDoomed.Length() == kDoomedCount);
 
   mMainThreadObjectsForgotten = true;
 }
 
 template <class Derived>
-bool
+void
 WorkerPrivateParent<Derived>::PostMessageInternal(
                                             JSContext* aCx,
-                                          JS::Handle<JS::Value> aMessage,
-                                            JS::Handle<JS::Value> aTransferable,
+                                            JS::Handle<JS::Value> aMessage,
+                                            const Optional<Sequence<JS::Value> >& aTransferable,
                                             bool aToMessagePort,
-                                            uint64_t aMessagePortSerial)
+                                            uint64_t aMessagePortSerial,
+                                            ErrorResult& aRv)
 {
   AssertIsOnParentThread();
 
   {
     MutexAutoLock lock(mMutex);
     if (mParentStatus > Running) {
-      return true;
+      return;
     }
   }
 
   JSStructuredCloneCallbacks* callbacks;
   if (GetParent()) {
     if (IsChromeWorker()) {
       callbacks = &gChromeWorkerStructuredCloneCallbacks;
     }
@@ -2547,58 +2610,59 @@ WorkerPrivateParent<Derived>::PostMessag
     if (IsChromeWorker()) {
       callbacks = &gMainThreadChromeWorkerStructuredCloneCallbacks;
     }
     else {
       callbacks = &gMainThreadWorkerStructuredCloneCallbacks;
     }
   }
 
+  JS::Rooted<JS::Value> transferable(aCx, JS::UndefinedValue());
+  if (aTransferable.WasPassed()) {
+    const Sequence<JS::Value>& realTransferable = aTransferable.Value();
+    JSObject* array =
+      JS_NewArrayObject(aCx, realTransferable.Length(),
+                        const_cast<JS::Value*>(realTransferable.Elements()));
+    if (!array) {
+      aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
+      return;
+    }
+    transferable.setObject(*array);
+  }
+
   nsTArray<nsCOMPtr<nsISupports> > clonedObjects;
 
   JSAutoStructuredCloneBuffer buffer;
-  if (!buffer.write(aCx, aMessage, aTransferable, callbacks, &clonedObjects)) {
-    return false;
+  if (!buffer.write(aCx, aMessage, transferable, callbacks, &clonedObjects)) {
+    aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
+    return;
   }
 
   nsRefPtr<MessageEventRunnable> runnable =
     new MessageEventRunnable(ParentAsWorkerPrivate(),
                              WorkerRunnable::WorkerThread, buffer,
                              clonedObjects, aToMessagePort, aMessagePortSerial);
-  return runnable->Dispatch(aCx);
+  if (!runnable->Dispatch(aCx)) {
+    aRv.Throw(NS_ERROR_FAILURE);
+  }
 }
 
 template <class Derived>
 void
 WorkerPrivateParent<Derived>::PostMessageToMessagePort(
                              JSContext* aCx,
                              uint64_t aMessagePortSerial,
                              JS::Handle<JS::Value> aMessage,
                              const Optional<Sequence<JS::Value>>& aTransferable,
                              ErrorResult& aRv)
 {
   AssertIsOnMainThread();
 
-  JS::Rooted<JS::Value> transferable(aCx, JS::UndefinedValue());
-  if (aTransferable.WasPassed()) {
-    const Sequence<JS::Value>& realTransferable = aTransferable.Value();
-    JSObject* array =
-      JS_NewArrayObject(aCx, realTransferable.Length(),
-                        const_cast<jsval*>(realTransferable.Elements()));
-    if (!array) {
-      aRv = NS_ERROR_OUT_OF_MEMORY;
-      return;
-    }
-    transferable.setObject(*array);
-  }
-
-  if (!PostMessageInternal(aCx, aMessage, transferable, true,
-                           aMessagePortSerial)) {
-    aRv = NS_ERROR_FAILURE;
-  }
+  PostMessageInternal(aCx, aMessage, aTransferable, true, aMessagePortSerial,
+                      aRv);
 }
 
 template <class Derived>
 bool
 WorkerPrivateParent<Derived>::DispatchMessageEventToMessagePort(
                                 JSContext* aCx, uint64_t aMessagePortSerial,
                                 JSAutoStructuredCloneBuffer& aBuffer,
                                 nsTArray<nsCOMPtr<nsISupports>>& aClonedObjects)
@@ -3186,80 +3250,162 @@ WorkerPrivateParent<Derived>::ParentJSCo
 
   AssertIsOnMainThread();
 
   return mLoadInfo.mScriptContext ?
          mLoadInfo.mScriptContext->GetNativeContext() :
          nsContentUtils::GetSafeJSContext();
 }
 
-WorkerPrivate::WorkerPrivate(JSContext* aCx, JS::HandleObject aObject,
+WorkerPrivate::WorkerPrivate(JSContext* aCx,
                              WorkerPrivate* aParent,
                              const nsAString& aScriptURL,
                              bool aIsChromeWorker, bool aIsSharedWorker,
                              const nsAString& aSharedWorkerName,
                              LoadInfo& aLoadInfo)
-: WorkerPrivateParent<WorkerPrivate>(aCx, aObject, aParent, aScriptURL,
+: WorkerPrivateParent<WorkerPrivate>(aCx, aParent, aScriptURL,
                                      aIsChromeWorker, aIsSharedWorker,
                                      aSharedWorkerName, aLoadInfo),
   mJSContext(nullptr), mErrorHandlerRecursionCount(0), mNextTimeoutId(1),
   mStatus(Pending), mSuspended(false), mTimerRunning(false),
   mRunningExpiredTimeouts(false), mCloseHandlerStarted(false),
   mCloseHandlerFinished(false), mMemoryReporterRunning(false),
   mBlockedForMemoryReporter(false)
 {
-  MOZ_COUNT_CTOR(mozilla::dom::workers::WorkerPrivate);
-  MOZ_ASSERT_IF(aIsSharedWorker, !aObject && !aSharedWorkerName.IsVoid());
-  MOZ_ASSERT_IF(!aIsSharedWorker, aObject && aSharedWorkerName.IsEmpty());
+  MOZ_ASSERT_IF(aIsSharedWorker, !aSharedWorkerName.IsVoid());
+  MOZ_ASSERT_IF(!aIsSharedWorker, aSharedWorkerName.IsEmpty());
 }
 
 WorkerPrivate::~WorkerPrivate()
 {
-  MOZ_COUNT_DTOR(mozilla::dom::workers::WorkerPrivate);
+}
+
+// static
+already_AddRefed<WorkerPrivate>
+WorkerPrivate::Constructor(const GlobalObject& aGlobal,
+                           const nsAString& aScriptURL,
+                           ErrorResult& aRv)
+{
+  return WorkerPrivate::Constructor(aGlobal, aScriptURL, false, false,
+                                    EmptyString(), nullptr, aRv);
+}
+
+// static
+bool
+WorkerPrivate::WorkerAvailable(JSContext* /* unused */, JSObject* /* unused */)
+{
+  // If we're already on a worker workers are clearly enabled.
+  if (!NS_IsMainThread()) {
+    return true;
+  }
+
+  // If our caller is chrome, workers are always available.
+  if (nsContentUtils::IsCallerChrome()) {
+    return true;
+  }
+
+  // Else check the pref.
+  return Preferences::GetBool(PREF_WORKERS_ENABLED);
+}
+
+// static
+already_AddRefed<ChromeWorkerPrivate>
+ChromeWorkerPrivate::Constructor(const GlobalObject& aGlobal,
+                                 const nsAString& aScriptURL,
+                                 ErrorResult& aRv)
+{
+  return WorkerPrivate::Constructor(aGlobal, aScriptURL, true, false,
+                                    EmptyString(), nullptr, aRv).downcast<ChromeWorkerPrivate>();
+}
+
+// static
+bool
+ChromeWorkerPrivate::WorkerAvailable(JSContext* /* unused */, JSObject* /* unused */)
+{
+  // Chrome is always allowed to use workers, and content is never allowed to
+  // use ChromeWorker, so all we have to check is the caller.
+  return nsContentUtils::ThreadsafeIsCallerChrome();
 }
 
 // static
 already_AddRefed<WorkerPrivate>
-WorkerPrivate::Create(JSContext* aCx, JS::HandleObject aObject,
-                      WorkerPrivate* aParent, const nsAString& aScriptURL,
-                      bool aIsChromeWorker, bool aIsSharedWorker,
-                      const nsAString& aSharedWorkerName, LoadInfo* aLoadInfo)
+WorkerPrivate::Constructor(const GlobalObject& aGlobal,
+                           const nsAString& aScriptURL,
+                           bool aIsChromeWorker, bool aIsSharedWorker,
+                           const nsAString& aSharedWorkerName,
+                           LoadInfo* aLoadInfo, ErrorResult& aRv)
 {
-  if (aParent) {
-    aParent->AssertIsOnWorkerThread();
+  WorkerPrivate* parent = NS_IsMainThread() ?
+                          nullptr :
+                          GetCurrentThreadWorkerPrivate();
+  if (parent) {
+    parent->AssertIsOnWorkerThread();
   } else {
     AssertIsOnMainThread();
   }
 
-  MOZ_ASSERT_IF(aIsSharedWorker, !aObject && !aSharedWorkerName.IsVoid());
-  MOZ_ASSERT_IF(!aIsSharedWorker, aObject && aSharedWorkerName.IsEmpty());
+  JSContext* cx = aGlobal.GetContext();
+
+  MOZ_ASSERT_IF(aIsSharedWorker, !aSharedWorkerName.IsVoid());
+  MOZ_ASSERT_IF(!aIsSharedWorker, aSharedWorkerName.IsEmpty());
 
   mozilla::Maybe<LoadInfo> stackLoadInfo;
   if (!aLoadInfo) {
     stackLoadInfo.construct();
 
-    nsresult rv = GetLoadInfo(aCx, nullptr, aParent, aScriptURL,
+    nsresult rv = GetLoadInfo(cx, nullptr, parent, aScriptURL,
                               aIsChromeWorker, stackLoadInfo.addr());
     if (NS_FAILED(rv)) {
-      scriptloader::ReportLoadError(aCx, aScriptURL, rv, !aParent);
+      scriptloader::ReportLoadError(cx, aScriptURL, rv, !parent);
+      aRv.Throw(rv);
       return nullptr;
     }
 
     aLoadInfo = stackLoadInfo.addr();
   }
 
+  // NB: This has to be done before creating the WorkerPrivate, because it will
+  // attempt to use static variables that are initialized in the RuntimeService
+  // constructor.
+  RuntimeService* runtimeService;
+
+  if (!parent) {
+    runtimeService = RuntimeService::GetOrCreateService();
+    if (!runtimeService) {
+      JS_ReportError(cx, "Failed to create runtime service!");
+      aRv.Throw(NS_ERROR_FAILURE);
+      return nullptr;
+    }
+  }
+  else {
+    runtimeService = RuntimeService::GetService();
+  }
+
+  MOZ_ASSERT(runtimeService);
+
   nsRefPtr<WorkerPrivate> worker =
-    new WorkerPrivate(aCx, aObject, aParent, aScriptURL, aIsChromeWorker,
+    new WorkerPrivate(cx, parent, aScriptURL, aIsChromeWorker,
                       aIsSharedWorker, aSharedWorkerName, *aLoadInfo);
 
   nsRefPtr<CompileScriptRunnable> compiler = new CompileScriptRunnable(worker);
-  if (!compiler->Dispatch(aCx)) {
+  if (!compiler->Dispatch(cx)) {
+    aRv.Throw(NS_ERROR_UNEXPECTED);
     return nullptr;
   }
 
+  if (!runtimeService->RegisterWorker(cx, worker)) {
+    aRv.Throw(NS_ERROR_UNEXPECTED);
+    return nullptr;
+  }
+
+  // The worker will be owned by its JSObject (via the reference we return from
+  // this function), but it also needs to be owned by its thread, so AddRef it
+  // again.
+  NS_ADDREF(worker.get());
+
   return worker.forget();
 }
 
 // static
 nsresult
 WorkerPrivate::GetLoadInfo(JSContext* aCx, nsPIDOMWindow* aWindow,
                            WorkerPrivate* aParent, const nsAString& aScriptURL,
                            bool aIsChromeWorker, LoadInfo* aLoadInfo)
@@ -5004,25 +5150,16 @@ WorkerPrivateParent<Derived>::Unregister
 
 template <class Derived>
 void
 WorkerPrivateParent<Derived>::StealHostObjectURIs(nsTArray<nsCString>& aArray)
 {
   aArray.SwapElements(mHostObjectURIs);
 }
 
-template <class Derived>
-JSObject*
-WorkerPrivateParent<Derived>::WrapObject(JSContext* aCx,
-                                         JS::HandleObject aScope)
-{
-  MOZ_CRASH("This should never be called!");
-  return nullptr;
-}
-
 WorkerCrossThreadDispatcher*
 WorkerPrivate::GetCrossThreadDispatcher()
 {
   MutexAutoLock lock(mMutex);
 
   if (!mCrossThreadDispatcher && mStatus <= Running) {
     mCrossThreadDispatcher = new WorkerCrossThreadDispatcher(this);
   }
@@ -5168,16 +5305,34 @@ WorkerPrivate::AssertIsOnWorkerThread() 
   bool current;
   MOZ_ASSERT(NS_SUCCEEDED(mThread->IsOnCurrentThread(&current)));
   MOZ_ASSERT(current, "Wrong thread!");
 }
 #endif // DEBUG
 
 BEGIN_WORKERS_NAMESPACE
 
+WorkerCrossThreadDispatcher*
+GetWorkerCrossThreadDispatcher(JSContext* aCx, JS::Value aWorker)
+{
+  if (!aWorker.isObject()) {
+    return nullptr;
+  }
+
+  WorkerPrivate* w = nullptr;
+  UNWRAP_OBJECT(Worker, aCx, &aWorker.toObject(), w);
+  MOZ_ASSERT(w);
+  return w->GetCrossThreadDispatcher();
+}
+
+// Can't use NS_IMPL_CYCLE_COLLECTION_CLASS(WorkerPrivateParent) because of the
+// templates.
+template <>
+WorkerPrivateParent<WorkerPrivate>::cycleCollection WorkerPrivateParent<WorkerPrivate>::_cycleCollectorGlobal = WorkerPrivateParent<WorkerPrivate>::cycleCollection();
+
 // Force instantiation.
 template class WorkerPrivateParent<WorkerPrivate>;
 
 JSStructuredCloneCallbacks*
 WorkerStructuredCloneCallbacks(bool aMainRuntime)
 {
   return aMainRuntime ?
          &gMainThreadWorkerStructuredCloneCallbacks :
--- a/dom/workers/WorkerPrivate.h
+++ b/dom/workers/WorkerPrivate.h
@@ -15,25 +15,25 @@
 #include "nsPIDOMWindow.h"
 
 #include "mozilla/Assertions.h"
 #include "mozilla/CondVar.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/dom/BindingDeclarations.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsDataHashtable.h"
+#include "nsDOMEventTargetHelper.h"
 #include "nsEventQueue.h"
 #include "nsHashKeys.h"
 #include "nsString.h"
 #include "nsTArray.h"
 #include "nsThreadUtils.h"
 #include "nsTPriorityQueue.h"
 #include "StructuredCloneTags.h"
 
-#include "EventTarget.h"
 #include "Queue.h"
 #include "WorkerFeature.h"
 
 class JSAutoStructuredCloneBuffer;
 class nsIChannel;
 class nsIDocument;
 class nsIPrincipal;
 class nsIScriptContext;
@@ -229,17 +229,17 @@ public:
   AssertCurrentThreadOwns() const
   {
     MOZ_ASSERT(mMutex);
     mMutex->AssertCurrentThreadOwns();
   }
 };
 
 template <class Derived>
-class WorkerPrivateParent : public EventTarget
+class WorkerPrivateParent : public nsDOMEventTargetHelper
 {
   class SynchronizeAndResumeRunnable;
 
 public:
   struct LocationInfo
   {
     nsCString mHref;
     nsCString mProtocol;
@@ -295,21 +295,22 @@ public:
 protected:
   typedef mozilla::ErrorResult ErrorResult;
 
   SharedMutex mMutex;
   mozilla::CondVar mCondVar;
   mozilla::CondVar mMemoryReportCondVar;
 
 private:
-  JSObject* mJSObject;
   WorkerPrivate* mParent;
   nsString mScriptURL;
   nsString mSharedWorkerName;
   LocationInfo mLocationInfo;
+  // The lifetime of these objects within LoadInfo is managed explicitly;
+  // they do not need to be cycle collected.
   LoadInfo mLoadInfo;
 
   // Only used for top level workers.
   nsTArray<nsRefPtr<WorkerRunnable> > mQueuedRunnables;
   nsRevocableEventPtr<SynchronizeAndResumeRunnable> mSynchronizeRunnable;
 
   // Only for ChromeWorkers without window and only touched on the main thread.
   nsTArray<nsCString> mHostObjectURIs;
@@ -319,27 +320,27 @@ private:
 
   // Only touched on the parent thread (currently this is always the main
   // thread as SharedWorkers are always top-level).
   nsDataHashtable<nsUint64HashKey, SharedWorker*> mSharedWorkers;
 
   uint64_t mBusyCount;
   uint64_t mMessagePortSerial;
   Status mParentStatus;
-  bool mJSObjectRooted;
+  bool mRooted;
   bool mParentSuspended;
   bool mIsChromeWorker;
   bool mMainThreadObjectsForgotten;
   bool mIsSharedWorker;
 
 protected:
-  WorkerPrivateParent(JSContext* aCx, JS::HandleObject aObject,
-                      WorkerPrivate* aParent, const nsAString& aScriptURL,
-                      bool aIsChromeWorker, bool aIsSharedWorker,
-                      const nsAString& aSharedWorkerName, LoadInfo& aLoadInfo);
+  WorkerPrivateParent(JSContext* aCx, WorkerPrivate* aParent,
+                      const nsAString& aScriptURL, bool aIsChromeWorker,
+                      bool aIsSharedWorker, const nsAString& aSharedWorkerName,
+                      LoadInfo& aLoadInfo);
 
   ~WorkerPrivateParent();
 
 private:
   Derived*
   ParentAsWorkerPrivate() const
   {
     return static_cast<Derived*>(const_cast<WorkerPrivateParent*>(this));
@@ -351,22 +352,31 @@ private:
 
   // aCx is null when called from the finalizer
   bool
   TerminatePrivate(JSContext* aCx)
   {
     return NotifyPrivate(aCx, Terminating);
   }
 
-  bool
+  void
   PostMessageInternal(JSContext* aCx, JS::Handle<JS::Value> aMessage,
-                      JS::Handle<JS::Value> aTransferable,
-                      bool aToMessagePort, uint64_t aMessagePortSerial);
+                      const Optional<Sequence<JS::Value> >& aTransferable,
+                      bool aToMessagePort, uint64_t aMessagePortSerial,
+                      ErrorResult& aRv);
 
 public:
+
+  virtual JSObject*
+  WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope) MOZ_OVERRIDE;
+
+  NS_DECL_ISUPPORTS_INHERITED
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(WorkerPrivateParent,
+                                                         nsDOMEventTargetHelper)
+
   // May be called on any thread...
   bool
   Start();
 
   // Called on the parent thread.
   bool
   Notify(JSContext* aCx, Status aStatus)
   {
@@ -390,60 +400,58 @@ public:
 
   bool
   Resume(JSContext* aCx, nsPIDOMWindow* aWindow);
 
   bool
   SynchronizeAndResume(JSContext* aCx, nsPIDOMWindow* aWindow,
                        nsIScriptContext* aScriptContext);
 
-  virtual void
-  _trace(JSTracer* aTrc) MOZ_OVERRIDE;
-
-  virtual void
-  _finalize(JSFreeOp* aFop) MOZ_OVERRIDE;
+  void
+  _finalize(JSFreeOp* aFop);
 
   void
   Finish(JSContext* aCx)
   {
-    RootJSObject(aCx, false);
+    Root(false);
   }
 
   bool
   Terminate(JSContext* aCx)
   {
     AssertIsOnParentThread();
-    RootJSObject(aCx, false);
+    Root(false);
     return TerminatePrivate(aCx);
   }
 
   bool
   Close(JSContext* aCx);
 
   bool
   ModifyBusyCount(JSContext* aCx, bool aIncrease);
 
-  bool
-  RootJSObject(JSContext* aCx, bool aRoot);
+  void
+  Root(bool aRoot);
 
   void
   ForgetMainThreadObjects(nsTArray<nsCOMPtr<nsISupports> >& aDoomed);
 
-  bool
+  void
   PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
-              JS::Handle<JS::Value> aTransferable)
+              const Optional<Sequence<JS::Value> >& aTransferable,
+              ErrorResult& aRv)
   {
-    return PostMessageInternal(aCx, aMessage, aTransferable, false, 0);
+    PostMessageInternal(aCx, aMessage, aTransferable, false, 0, aRv);
   }
 
   void
   PostMessageToMessagePort(JSContext* aCx,
                            uint64_t aMessagePortSerial,
                            JS::Handle<JS::Value> aMessage,
-                           const Optional<Sequence<JS::Value > >& aTransferable,
+                           const Optional<Sequence<JS::Value> >& aTransferable,
                            ErrorResult& aRv);
 
   bool
   DispatchMessageEventToMessagePort(
                                JSContext* aCx,
                                uint64_t aMessagePortSerial,
                                JSAutoStructuredCloneBuffer& aBuffer,
                                nsTArray<nsCOMPtr<nsISupports>>& aClonedObjects);
@@ -532,22 +540,16 @@ public:
 
   nsIScriptContext*
   GetScriptContext() const
   {
     AssertIsOnMainThread();
     return mLoadInfo.mScriptContext;
   }
 
-  JSObject*
-  GetJSObject() const
-  {
-    return mJSObject;
-  }
-
   const nsString&
   ScriptURL() const
   {
     return mScriptURL;
   }
 
   const nsCString&
   Domain() const
@@ -701,18 +703,18 @@ public:
   RegisterHostObjectURI(const nsACString& aURI);
 
   void
   UnregisterHostObjectURI(const nsACString& aURI);
 
   void
   StealHostObjectURIs(nsTArray<nsCString>& aArray);
 
-  virtual JSObject*
-  WrapObject(JSContext* aCx, JS::HandleObject aScope) MOZ_OVERRIDE;
+  IMPL_EVENT_HANDLER(message)
+  IMPL_EVENT_HANDLER(error)
 
 #ifdef DEBUG
   void
   AssertIsOnParentThread() const;
 
   void
   AssertInnerWindowIsCorrect() const;
 #else
@@ -786,24 +788,32 @@ class WorkerPrivate : public WorkerPriva
   bool mCloseHandlerFinished;
   bool mMemoryReporterRunning;
   bool mBlockedForMemoryReporter;
 
 #ifdef DEBUG
   nsCOMPtr<nsIThread> mThread;
 #endif
 
+protected:
+  ~WorkerPrivate();
+
 public:
-  ~WorkerPrivate();
+  static already_AddRefed<WorkerPrivate>
+  Constructor(const GlobalObject& aGlobal, const nsAString& aScriptURL,
+              ErrorResult& aRv);
 
   static already_AddRefed<WorkerPrivate>
-  Create(JSContext* aCx, JS::HandleObject aObject, WorkerPrivate* aParent,
-         const nsAString& aScriptURL, bool aIsChromeWorker,
-         bool aIsSharedWorker, const nsAString& aSharedWorkerName,
-         LoadInfo* aLoadInfo = nullptr);
+  Constructor(const GlobalObject& aGlobal, const nsAString& aScriptURL,
+              bool aIsChromeWorker, bool aIsSharedWorker,
+              const nsAString& aSharedWorkerName,
+              LoadInfo* aLoadInfo, ErrorResult& aRv);
+
+  static bool
+  WorkerAvailable(JSContext* /* unused */, JSObject* /* unused */);
 
   static nsresult
   GetLoadInfo(JSContext* aCx, nsPIDOMWindow* aWindow, WorkerPrivate* aParent,
               const nsAString& aScriptURL, bool aIsChromeWorker,
               LoadInfo* aLoadInfo);
 
   void
   DoRunLoop(JSContext* aCx);
@@ -1013,20 +1023,20 @@ public:
 
   void
   DisconnectMessagePort(uint64_t aMessagePortSerial);
 
   WorkerMessagePort*
   GetMessagePort(uint64_t aMessagePortSerial);
 
 private:
-  WorkerPrivate(JSContext* aCx, JS::HandleObject aObject,
-                WorkerPrivate* aParent, const nsAString& aScriptURL,
-                bool aIsChromeWorker, bool aIsSharedWorker,
-                const nsAString& aSharedWorkerName, LoadInfo& aLoadInfo);
+  WorkerPrivate(JSContext* aCx, WorkerPrivate* aParent,
+                const nsAString& aScriptURL, bool aIsChromeWorker,
+                bool aIsSharedWorker, const nsAString& aSharedWorkerName,
+                LoadInfo& aLoadInfo);
 
   bool
   Dispatch(WorkerRunnable* aEvent, EventQueue* aQueue);
 
   bool
   DispatchToSyncQueue(WorkerSyncRunnable* aEvent);
 
   void
@@ -1095,19 +1105,41 @@ private:
   bool
   PostMessageToParentInternal(JSContext* aCx,
                               JS::Handle<JS::Value> aMessage,
                               JS::Handle<JS::Value> aTransferable,
                               bool aToMessagePort,
                               uint64_t aMessagePortSerial);
 };
 
+// This class is only used to trick the DOM bindings.  We never create
+// instances of it, and static_casting to it is fine since it doesn't add
+// anything to WorkerPrivate.
+class ChromeWorkerPrivate : public WorkerPrivate
+{
+public:
+  static already_AddRefed<ChromeWorkerPrivate>
+  Constructor(const GlobalObject& aGlobal, const nsAString& aScriptURL,
+              ErrorResult& rv);
+
+  static bool
+  WorkerAvailable(JSContext* /* unused */, JSObject* /* unused */);
+
+private:
+  ChromeWorkerPrivate() MOZ_DELETE;
+  ChromeWorkerPrivate(const ChromeWorkerPrivate& aRHS) MOZ_DELETE;
+  ChromeWorkerPrivate& operator =(const ChromeWorkerPrivate& aRHS) MOZ_DELETE;
+};
+
 WorkerPrivate*
 GetWorkerPrivateFromContext(JSContext* aCx);
 
+WorkerPrivate*
+GetCurrentThreadWorkerPrivate();
+
 bool
 IsCurrentThreadRunningChromeWorker();
 
 JSContext*
 GetCurrentThreadJSContext();
 
 enum WorkerStructuredDataType
 {
--- a/dom/workers/WorkerScope.cpp
+++ b/dom/workers/WorkerScope.cpp
@@ -18,16 +18,17 @@
 #include "mozilla/dom/FileReaderSyncBinding.h"
 #include "mozilla/dom/ImageData.h"
 #include "mozilla/dom/ImageDataBinding.h"
 #include "mozilla/dom/TextDecoderBinding.h"
 #include "mozilla/dom/TextEncoderBinding.h"
 #include "mozilla/dom/XMLHttpRequestBinding.h"
 #include "mozilla/dom/XMLHttpRequestUploadBinding.h"
 #include "mozilla/dom/URLBinding.h"
+#include "mozilla/dom/WorkerBinding.h"
 #include "mozilla/dom/WorkerLocationBinding.h"
 #include "mozilla/dom/WorkerNavigatorBinding.h"
 #include "mozilla/OSFileConstants.h"
 #include "nsIGlobalObject.h"
 #include "nsTraceRefcnt.h"
 #include "xpcpublic.h"
 
 #ifdef ANDROID
@@ -39,17 +40,16 @@
 #include "EventListenerManager.h"
 #include "EventTarget.h"
 #include "File.h"
 #include "FileReaderSync.h"
 #include "Location.h"
 #include "Navigator.h"
 #include "Principal.h"
 #include "ScriptLoader.h"
-#include "Worker.h"
 #include "WorkerPrivate.h"
 #include "XMLHttpRequest.h"
 
 #include "WorkerInlines.h"
 
 #define FUNCTION_FLAGS \
   JSPROP_ENUMERATE
 
@@ -1446,26 +1446,20 @@ CreateGlobalScope(JSContext* aCx)
   if (!finalScopeProto) {
     return nullptr;
   }
 
   if (!JS_SetPrototype(aCx, global, finalScopeProto)) {
     return nullptr;
   }
 
-  JS::Rooted<JSObject*> workerProto(aCx,
-    worker::InitClass(aCx, global, eventTargetProto, false));
-  if (!workerProto) {
-    return nullptr;
-  }
-
   if (worker->IsChromeWorker()) {
-    if (!chromeworker::InitClass(aCx, global, workerProto, false) ||
-        !DefineChromeWorkerFunctions(aCx, global) ||
-        !DefineOSFileConstants(aCx, global)) {
+    if (!DefineChromeWorkerFunctions(aCx, global) ||
+        !DefineOSFileConstants(aCx, global) ||
+        !ChromeWorkerBinding::GetConstructorObject(aCx, global)) {
       return nullptr;
     }
   }
 
   // Init other classes we care about.
   if (!events::InitClasses(aCx, global, false) ||
       !file::InitClasses(aCx, global)) {
     return nullptr;
@@ -1476,23 +1470,31 @@ CreateGlobalScope(JSContext* aCx)
       !EventBinding::GetConstructorObject(aCx, global) ||
       !FileReaderSyncBinding_workers::GetConstructorObject(aCx, global) ||
       !ImageDataBinding::GetConstructorObject(aCx, global) ||
       !TextDecoderBinding::GetConstructorObject(aCx, global) ||
       !TextEncoderBinding::GetConstructorObject(aCx, global) ||
       !XMLHttpRequestBinding_workers::GetConstructorObject(aCx, global) ||
       !XMLHttpRequestUploadBinding_workers::GetConstructorObject(aCx, global) ||
       !URLBinding_workers::GetConstructorObject(aCx, global) ||
+      !WorkerBinding::GetConstructorObject(aCx, global) ||
       !WorkerLocationBinding_workers::GetConstructorObject(aCx, global) ||
       !WorkerNavigatorBinding_workers::GetConstructorObject(aCx, global)) {
     return nullptr;
   }
 
   if (!JS_DefineProfilingFunctions(aCx, global)) {
     return nullptr;
   }
 
   JS_FireOnNewGlobalObject(aCx, global);
 
   return global;
 }
 
+bool
+GetterOnlyJSNative(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
+{
+  JS_ReportErrorNumber(aCx, js_GetErrorMessage, nullptr, JSMSG_GETTER_ONLY);
+  return false;
+}
+
 END_WORKERS_NAMESPACE
--- a/dom/workers/moz.build
+++ b/dom/workers/moz.build
@@ -4,16 +4,20 @@
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 TEST_DIRS += ['test']
 
 MODULE = 'dom'
 
 # Public stuff.
+EXPORTS.mozilla.dom += [
+    'WorkerPrivate.h',
+]
+
 EXPORTS.mozilla.dom.workers += [
     'Workers.h',
 ]
 
 # Stuff needed for the bindings, not really public though.
 EXPORTS.mozilla.dom.workers.bindings += [
     'DOMBindingBase.h',
     'EventListenerManager.h',
@@ -42,17 +46,16 @@ CPP_SOURCES += [
     'Location.cpp',
     'MessagePort.cpp',
     'Navigator.cpp',
     'Principal.cpp',
     'RuntimeService.cpp',
     'ScriptLoader.cpp',
     'SharedWorker.cpp',
     'URL.cpp',
-    'Worker.cpp',
     'WorkerMessagePort.cpp',
     'WorkerPrivate.cpp',
     'WorkerScope.cpp',
     'XMLHttpRequest.cpp',
     'XMLHttpRequestEventTarget.cpp',
     'XMLHttpRequestUpload.cpp',
 ]
 
--- a/toolkit/components/osfile/modules/osfile_async_front.jsm
+++ b/toolkit/components/osfile/modules/osfile_async_front.jsm
@@ -166,18 +166,18 @@ let Scheduler = {
         }
         return data.ok;
       },
       function onError(error) {
         // Decode any serialized error
         if (error instanceof PromiseWorker.WorkerError) {
           throw OS.File.Error.fromMsg(error.data);
         }
-        // Extract something meaningful from WorkerErrorEvent
-        if (typeof error == "object" && error && error.constructor.name == "WorkerErrorEvent") {
+        // Extract something meaningful from ErrorEvent
+        if (error instanceof ErrorEvent) {
           let message = error.message;
           if (message == "uncaught exception: [object StopIteration]") {
             throw StopIteration;
           }
           throw new Error(message, error.filename, error.lineno);
         }
         throw error;
       }