Bug 643325 - Implement SharedWorker. r=khuey.
authorBen Turner <bent.mozilla@gmail.com>
Wed, 05 Jun 2013 07:04:23 -0700
changeset 149396 eedf61cab3fa8be11c865eddb39fb0d952234027
parent 149395 111be6d857e12fc2263f8c84f5df2f7c26a2fb3e
child 149397 4665d96e7f1a75a7830c8ccd2cc22a4dfa21b0e2
push id34550
push userbturner@mozilla.com
push dateTue, 01 Oct 2013 06:09:24 +0000
treeherdermozilla-inbound@eedf61cab3fa [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskhuey
bugs643325
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 643325 - Implement SharedWorker. r=khuey.
content/events/test/test_all_synthetic_events.html
dom/base/nsGlobalWindow.cpp
dom/bindings/BindingDeclarations.h
dom/bindings/Bindings.conf
dom/tests/mochitest/general/test_interfaces.html
dom/webidl/AbstractWorker.webidl
dom/webidl/ErrorEvent.webidl
dom/webidl/SharedWorker.webidl
dom/webidl/WorkerMessagePort.webidl
dom/webidl/moz.build
dom/workers/DOMBindingInlines.h
dom/workers/Events.cpp
dom/workers/Events.h
dom/workers/FileReaderSync.cpp
dom/workers/FileReaderSync.h
dom/workers/Location.cpp
dom/workers/MessagePort.cpp
dom/workers/MessagePort.h
dom/workers/Navigator.cpp
dom/workers/RuntimeService.cpp
dom/workers/RuntimeService.h
dom/workers/ScriptLoader.cpp
dom/workers/ScriptLoader.h
dom/workers/SharedWorker.cpp
dom/workers/SharedWorker.h
dom/workers/Worker.cpp
dom/workers/Worker.h
dom/workers/WorkerMessagePort.cpp
dom/workers/WorkerMessagePort.h
dom/workers/WorkerPrivate.cpp
dom/workers/WorkerPrivate.h
dom/workers/WorkerScope.cpp
dom/workers/WorkerScope.h
dom/workers/Workers.h
dom/workers/moz.build
dom/workers/test/Makefile.in
dom/workers/test/mochitest.ini
dom/workers/test/multi_sharedWorker_frame.html
dom/workers/test/multi_sharedWorker_sharedWorker.js
dom/workers/test/sharedWorker_sharedWorker.js
dom/workers/test/test_multi_sharedWorker.html
dom/workers/test/test_multi_sharedWorker_lifetimes.html
dom/workers/test/test_sharedWorker.html
modules/libpref/src/init/all.js
--- a/content/events/test/test_all_synthetic_events.html
+++ b/content/events/test/test_all_synthetic_events.html
@@ -130,16 +130,20 @@ const kEventConstructors = {
                                                                          aProps.view, aProps.detail,
                                                                          aProps.screenX, aProps.screenY,
                                                                          aProps.clientX, aProps.clientY,
                                                                          aProps.ctrlKey, aProps.altKey, aProps.shiftKey, aProps.metaKey,
                                                                          aProps.button, aProps.relatedTarget, aProps.dataTransfer);
                                                          return e;
                                                        },
                                              },
+  ErrorEvent:                                { create: function (aName, aProps) {
+                                                         return new ErrorEvent(aName, aProps);
+                                                       },
+  },
   ElementReplaceEvent:                       { create: function (aName, aProps) {
                                                          return new ElementReplaceEvent(aName, aProps);
                                                        },
                                              },
   FocusEvent:                                { create: function (aName, aProps) {
                                                          return new FocusEvent(aName, aProps);
                                                        },
                                              },
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -1428,22 +1428,17 @@ nsGlobalWindow::FreeInnerObjects()
   NS_ASSERTION(IsInnerWindow(), "Don't free inner objects on an outer window");
 
   // Make sure that this is called before we null out the document and
   // other members that the window destroyed observers could
   // re-create.
   NotifyDOMWindowDestroyed(this);
 
   // Kill all of the workers for this window.
-  // We push a cx so that exceptions get reported in the right DOM Window.
-  {
-    nsIScriptContext *scx = GetContextInternal();
-    AutoPushJSContext cx(scx ? scx->GetNativeContext() : nsContentUtils::GetSafeJSContext());
-    mozilla::dom::workers::CancelWorkersForWindow(cx, this);
-  }
+  mozilla::dom::workers::CancelWorkersForWindow(this);
 
   // Close all offline storages for this window.
   quota::QuotaManager* quotaManager = quota::QuotaManager::Get();
   if (quotaManager) {
     quotaManager->AbortCloseStoragesForWindow(this);
   }
 
   ClearAllTimeouts();
@@ -11235,22 +11230,17 @@ nsGlobalWindow::SuspendTimeouts(uint32_t
     nsCOMPtr<nsIDeviceSensors> ac = do_GetService(NS_DEVICE_SENSORS_CONTRACTID);
     if (ac) {
       for (uint32_t i = 0; i < mEnabledSensors.Length(); i++)
         ac->RemoveWindowListener(mEnabledSensors[i], this);
     }
     DisableGamepadUpdates();
 
     // Suspend all of the workers for this window.
-    // We push a cx so that exceptions get reported in the right DOM Window.
-    {
-      nsIScriptContext *scx = GetContextInternal();
-      AutoPushJSContext cx(scx ? scx->GetNativeContext() : nsContentUtils::GetSafeJSContext());
-      mozilla::dom::workers::SuspendWorkersForWindow(cx, this);
-    }
+    mozilla::dom::workers::SuspendWorkersForWindow(this);
 
     TimeStamp now = TimeStamp::Now();
     for (nsTimeout *t = mTimeouts.getFirst(); t; t = t->getNext()) {
       // Set mTimeRemaining to be the time remaining for this timer.
       if (t->mWhen > now)
         t->mTimeRemaining = t->mWhen - now;
       else
         t->mTimeRemaining = TimeDuration(0);
@@ -11329,20 +11319,17 @@ nsGlobalWindow::ResumeTimeouts(bool aTha
     EnableGamepadUpdates();
 
     // Resume all of the AudioContexts for this window
     for (uint32_t i = 0; i < mAudioContexts.Length(); ++i) {
       mAudioContexts[i]->Resume();
     }
 
     // Resume all of the workers for this window.
-    // We push a cx so that exceptions get reported in the right DOM Window.
-    nsIScriptContext *scx = GetContextInternal();
-    AutoPushJSContext cx(scx ? scx->GetNativeContext() : nsContentUtils::GetSafeJSContext());
-    mozilla::dom::workers::ResumeWorkersForWindow(scx, this);
+    mozilla::dom::workers::ResumeWorkersForWindow(this);
 
     // Restore all of the timeouts, using the stored time remaining
     // (stored in timeout->mTimeRemaining).
 
     TimeStamp now = TimeStamp::Now();
 
 #ifdef DEBUG
     bool _seenDummyTimeout = false;
@@ -12124,10 +12111,9 @@ nsGlobalWindow::DisableNetworkEvent(uint
   }
 #define WINDOW_ONLY_EVENT EVENT
 #define TOUCH_EVENT EVENT
 #include "nsEventNameList.h"
 #undef TOUCH_EVENT
 #undef WINDOW_ONLY_EVENT
 #undef BEFOREUNLOAD_EVENT
 #undef ERROR_EVENT
-#undef EVENT
-
+#undef EVENT
\ No newline at end of file
--- a/dom/bindings/BindingDeclarations.h
+++ b/dom/bindings/BindingDeclarations.h
@@ -616,9 +616,9 @@ struct ParentObject {
 
   nsISupports* const mObject;
   nsWrapperCache* const mWrapperCache;
 };
 
 } // namespace dom
 } // namespace mozilla
 
-#endif // mozilla_dom_BindingDeclarations_h__
+#endif // mozilla_dom_BindingDeclarations_h__
\ No newline at end of file
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -78,16 +78,20 @@
 # worker descriptors for objects that will never actually appear in workers.
 
 DOMInterfaces = {
 
 'MozActivity': {
     'nativeType': 'mozilla::dom::Activity',
 },
 
+'AbstractWorker': {
+    'concrete': False
+},
+
 'AnimationEvent': {
     'nativeType': 'nsDOMAnimationEvent',
 },
 
 'ArchiveReader': {
     'nativeType': 'mozilla::dom::file::ArchiveReader',
 },
 
@@ -938,16 +942,22 @@ DOMInterfaces = {
 'Screen': {
     'nativeType': 'nsScreen',
 },
 
 'ScrollAreaEvent': {
     'nativeType': 'nsDOMScrollAreaEvent',
 },
 
+'SharedWorker': {
+    'nativeType': 'mozilla::dom::workers::SharedWorker',
+    'headerFile': 'mozilla/dom/workers/bindings/SharedWorker.h',
+    'implicitJSContext': [ 'constructor' ],
+},
+
 'SimpleGestureEvent': {
     'nativeType': 'nsDOMSimpleGestureEvent',
 },
 
 'SourceBufferList': {
     'resultNotAddRefed': [ '__indexedGetter' ],
 },
 
@@ -1433,16 +1443,28 @@ DOMInterfaces = {
     'workers': True
 }],
 
 'WorkerLocation': {
     'headerFile': 'mozilla/dom/workers/bindings/Location.h',
     'workers': True,
 },
 
+'WorkerMessagePort': [{
+    'nativeType': 'mozilla::dom::workers::MessagePort',
+    'headerFile': 'mozilla/dom/workers/bindings/MessagePort.h',
+    'implicitJSContext': [ 'postMessage' ],
+},
+{
+    'nativeType': 'mozilla::dom::workers::WorkerMessagePort',
+    'headerFile': 'mozilla/dom/workers/bindings/WorkerMessagePort.h',
+    'workers': True,
+    'nativeOwnership': 'worker',
+}],
+
 'WorkerNavigator': {
     'headerFile': 'mozilla/dom/workers/bindings/Navigator.h',
     'workers': True,
 },
 
 'XMLHttpRequest': [
 {
     'nativeType': 'nsXMLHttpRequest',
@@ -1858,9 +1880,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')
+addExternalIface('CameraRecorderStateChange', nativeType='nsICameraRecorderStateChange', headerFile='nsIDOMCameraManager.h')
\ No newline at end of file
--- a/dom/tests/mochitest/general/test_interfaces.html
+++ b/dom/tests/mochitest/general/test_interfaces.html
@@ -179,16 +179,17 @@ var interfaceNamesInGlobalScope =
     "DOMStringList",
     "DOMStringMap",
     "DOMTokenList",
     "DOMTransactionEvent",
     "DragEvent",
     "DynamicsCompressorNode",
     "Element",
     "ElementReplaceEvent",
+    "ErrorEvent",
     "Event",
     "EventListenerInfo",
     "EventSource",
     "EventTarget",
     "File",
     "FileHandle",
     "FileList",
     "FileReader",
new file mode 100644
--- /dev/null
+++ b/dom/webidl/AbstractWorker.webidl
@@ -0,0 +1,10 @@
+/* -*- 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/.
+ */
+
+[NoInterfaceObject]
+interface AbstractWorker {
+    attribute EventHandler onerror;
+};
new file mode 100644
--- /dev/null
+++ b/dom/webidl/ErrorEvent.webidl
@@ -0,0 +1,21 @@
+/* -*- 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/. */
+
+[Constructor(DOMString type, optional ErrorEventInit eventInitDict)]
+interface ErrorEvent : Event
+{
+  readonly attribute DOMString message;
+  readonly attribute DOMString filename;
+  readonly attribute unsigned long lineno;
+  readonly attribute unsigned long column;
+};
+
+dictionary ErrorEventInit : EventInit
+{
+  DOMString message = "";
+  DOMString filename = "";
+  unsigned long lineno = 0;
+  unsigned long column = 0;
+};
new file mode 100644
--- /dev/null
+++ b/dom/webidl/SharedWorker.webidl
@@ -0,0 +1,13 @@
+/* -*- 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/.
+ */
+
+[PrefControlled,
+ Constructor(DOMString scriptURL, optional DOMString name)]
+interface SharedWorker : EventTarget {
+    readonly attribute WorkerMessagePort port;
+};
+
+SharedWorker implements AbstractWorker;
new file mode 100644
--- /dev/null
+++ b/dom/webidl/WorkerMessagePort.webidl
@@ -0,0 +1,18 @@
+/* -*- 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/.
+ */
+// XXX Remove me soon!
+[PrefControlled]
+interface WorkerMessagePort : EventTarget {
+    [Throws]
+    void postMessage(any message, optional sequence<any> transferable);
+
+    void start();
+
+    void close();
+
+    [SetterThrows=Workers, GetterThrows=Workers]
+    attribute EventHandler onmessage;
+};
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -11,16 +11,17 @@ GENERATED_WEBIDL_FILES = [
 ]
 
 PREPROCESSED_WEBIDL_FILES = [
     'Crypto.webidl',
     'Navigator.webidl',
 ]
 
 WEBIDL_FILES = [
+    'AbstractWorker.webidl',
     'AnalyserNode.webidl',
     'AnimationEvent.webidl',
     'ArchiveReader.webidl',
     'ArchiveRequest.webidl',
     'Attr.webidl',
     'AudioBuffer.webidl',
     'AudioBufferSourceNode.webidl',
     'AudioContext.webidl',
@@ -361,16 +362,17 @@ WEBIDL_FILES = [
     'SVGUseElement.webidl',
     'SVGViewElement.webidl',
     'SVGZoomAndPan.webidl',
     'SVGZoomEvent.webidl',
     'Screen.webidl',
     'ScriptProcessorNode.webidl',
     'ScrollAreaEvent.webidl',
     'SettingsManager.webidl',
+    'SharedWorker.webidl',
     'SimpleGestureEvent.webidl',
     'SourceBuffer.webidl',
     'SourceBufferList.webidl',
     'StorageType.webidl',
     'StyleSheet.webidl',
     'Telephony.webidl',
     'TelephonyCall.webidl',
     'TelephonyCallGroup.webidl',
@@ -401,16 +403,17 @@ WEBIDL_FILES = [
     'VideoStreamTrack.webidl',
     'WaveShaperNode.webidl',
     'WebComponents.webidl',
     'WebSocket.webidl',
     'WheelEvent.webidl',
     'WifiOptions.webidl',
     'Window.webidl',
     'WorkerLocation.webidl',
+    'WorkerMessagePort.webidl',
     'WorkerNavigator.webidl',
     'XMLDocument.webidl',
     'XMLHttpRequest.webidl',
     'XMLHttpRequestEventTarget.webidl',
     'XMLHttpRequestUpload.webidl',
     'XMLSerializer.webidl',
     'XMLStylesheetProcessingInstruction.webidl',
     'XPathEvaluator.webidl',
@@ -532,23 +535,23 @@ if CONFIG['ENABLE_TESTS']:
         'TestExampleGen.webidl',
         'TestJSImplGen.webidl',
     ]
 
 GENERATED_EVENTS_WEBIDL_FILES = [
     'BlobEvent.webidl',
     'DeviceLightEvent.webidl',
     'DeviceProximityEvent.webidl',
+    'ErrorEvent.webidl',
     'MediaStreamEvent.webidl',
     'MozInterAppMessageEvent.webidl',
     'RTCDataChannelEvent.webidl',
     'RTCPeerConnectionIceEvent.webidl',
     'UserProximityEvent.webidl',
 ]
 
 if CONFIG['MOZ_GAMEPAD']:
     GENERATED_EVENTS_WEBIDL_FILES += [
         'GamepadAxisMoveEvent.webidl',
         'GamepadButtonEvent.webidl',
         'GamepadEvent.webidl',
     ]
 
-
--- a/dom/workers/DOMBindingInlines.h
+++ b/dom/workers/DOMBindingInlines.h
@@ -1,27 +1,29 @@
 /* -*- 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_dombindinginlines_h__
 #define mozilla_dom_workers_dombindinginlines_h__
 
+#include "jsfriendapi.h"
 #include "mozilla/dom/JSSlots.h"
+#include "mozilla/dom/URLBinding.h"
+#include "mozilla/dom/WorkerMessagePortBinding.h"
 #include "mozilla/dom/XMLHttpRequestBinding.h"
 #include "mozilla/dom/XMLHttpRequestUploadBinding.h"
-#include "mozilla/dom/URLBinding.h"
-#include "jsfriendapi.h"
 
 BEGIN_WORKERS_NAMESPACE
 
+class URL;
+class WorkerMessagePort;
 class XMLHttpRequest;
 class XMLHttpRequestUpload;
-class URL;
 
 namespace {
 
 template <class T>
 struct WrapPrototypeTraits
 { };
 
 // XXX I kinda hate this, but we decided it wasn't worth generating this in the
@@ -40,19 +42,20 @@ struct WrapPrototypeTraits
     static inline JSObject*                                                    \
     GetProtoObject(JSContext* aCx, JS::Handle<JSObject*> aGlobal)              \
     {                                                                          \
       using namespace mozilla::dom;                                            \
       return _class##Binding_workers::GetProtoObject(aCx, aGlobal);            \
     }                                                                          \
   };
 
+SPECIALIZE_PROTO_TRAITS(URL)
+SPECIALIZE_PROTO_TRAITS(WorkerMessagePort)
 SPECIALIZE_PROTO_TRAITS(XMLHttpRequest)
 SPECIALIZE_PROTO_TRAITS(XMLHttpRequestUpload)
-SPECIALIZE_PROTO_TRAITS(URL)
 
 #undef SPECIALIZE_PROTO_TRAITS
 
 } // anonymous namespace
 
 template <class T>
 inline JSObject*
 Wrap(JSContext* aCx, JSObject* aGlobal, nsRefPtr<T>& aConcreteObject)
--- a/dom/workers/Events.cpp
+++ b/dom/workers/Events.cpp
@@ -409,57 +409,101 @@ public:
   {
     const JSClass* clasp = aMainRuntime ? &sMainRuntimeClass : &sClass;
 
     return JS_InitClass(aCx, aObj, aParentProto, clasp, Construct, 0,
                         sProperties, sFunctions, NULL, NULL);
   }
 
   static JSObject*
-  Create(JSContext* aCx, JS::Handle<JSObject*> aParent, JSAutoStructuredCloneBuffer& aData,
-         nsTArray<nsCOMPtr<nsISupports> >& aClonedObjects, bool aMainRuntime)
+  Create(JSContext* aCx, JS::Handle<JSObject*> aParent,
+         JSAutoStructuredCloneBuffer& aData,
+         nsTArray<nsCOMPtr<nsISupports> >& aClonedObjects,
+         bool aMainRuntime)
   {
     JS::Rooted<JSString*> type(aCx, JS_InternString(aCx, "message"));
     if (!type) {
       return NULL;
     }
 
     const JSClass* clasp = aMainRuntime ? &sMainRuntimeClass : &sClass;
 
     JS::Rooted<JSObject*> obj(aCx, JS_NewObject(aCx, clasp, NULL, aParent));
     if (!obj) {
       return NULL;
     }
 
+    JS::Rooted<JSObject*> ports(aCx, JS_NewArrayObject(aCx, 0, nullptr));
+    if (!ports) {
+      return NULL;
+    }
+
     MessageEvent* priv = new MessageEvent(aMainRuntime);
     SetJSPrivateSafeish(obj, priv);
+
     InitMessageEventCommon(aCx, obj, priv, type, false, false, NULL, NULL, NULL,
-                           true);
+                           ports, true);
+
     priv->mBuffer.swap(aData);
     priv->mClonedObjects.SwapElements(aClonedObjects);
 
     return obj;
   }
 
+  static JSObject*
+  Create(JSContext* aCx, JS::Handle<JSObject*> aParent,
+         JS::Handle<JSString*> aType, bool aBubbles, bool aCancelable,
+         JS::Handle<JSString*> aData, JS::Handle<JSString*> aOrigin,
+         JS::Handle<JSObject*> aSource, JS::Handle<JSObject*> aMessagePort,
+         bool aIsTrusted)
+  {
+    JS::Rooted<JSObject*> obj(aCx,
+                              JS_NewObject(aCx, &sClass, nullptr, aParent));
+    if (!obj) {
+      return nullptr;
+    }
+
+    JS::Rooted<JSObject*> ports(aCx);
+    if (aMessagePort) {
+      JS::Value port = OBJECT_TO_JSVAL(aMessagePort);
+      ports = JS_NewArrayObject(aCx, 1, &port);
+    } else {
+      ports = JS_NewArrayObject(aCx, 0, nullptr);
+    }
+
+    if (!ports) {
+      return NULL;
+    }
+
+    MessageEvent* priv = new MessageEvent(false);
+    SetJSPrivateSafeish(obj, priv);
+
+    InitMessageEventCommon(aCx, obj, priv, aType, aBubbles, aCancelable, aData,
+                           aOrigin, aSource, ports, aIsTrusted);
+
+    return obj;
+  }
+
 protected:
   MessageEvent(bool aMainRuntime)
   : mMainRuntime(aMainRuntime)
   {
     MOZ_COUNT_CTOR(mozilla::dom::workers::MessageEvent);
   }
 
   virtual ~MessageEvent()
   {
     MOZ_COUNT_DTOR(mozilla::dom::workers::MessageEvent);
   }
 
   enum SLOT {
     SLOT_data = Event::SLOT_COUNT,
     SLOT_origin,
     SLOT_source,
+    SLOT_ports,
 
     SLOT_COUNT,
     SLOT_FIRST = SLOT_data
   };
 
 private:
   static MessageEvent*
   GetInstancePrivate(JSContext* aCx, JSObject* aObj, const char* aFunctionName)
@@ -474,27 +518,28 @@ private:
                          classPtr->name);
     return NULL;
   }
 
   static void
   InitMessageEventCommon(JSContext* aCx, JSObject* aObj, Event* aEvent,
                          JSString* aType, bool aBubbles, bool aCancelable,
                          JSString* aData, JSString* aOrigin, JSObject* aSource,
-                         bool aIsTrusted)
+                         JS::Handle<JSObject*> aMessagePorts, bool aIsTrusted)
   {
     jsval emptyString = JS_GetEmptyStringValue(aCx);
 
     Event::InitEventCommon(aObj, aEvent, aType, aBubbles, aCancelable,
                            aIsTrusted);
     JS_SetReservedSlot(aObj, SLOT_data,
                        aData ? STRING_TO_JSVAL(aData) : emptyString);
     JS_SetReservedSlot(aObj, SLOT_origin,
                        aOrigin ? STRING_TO_JSVAL(aOrigin) : emptyString);
     JS_SetReservedSlot(aObj, SLOT_source, OBJECT_TO_JSVAL(aSource));
+    JS_SetReservedSlot(aObj, SLOT_ports, OBJECT_TO_JSVAL(aMessagePorts));
   }
 
   static bool
   Construct(JSContext* aCx, unsigned aArgc, jsval* aVp)
   {
     JS_ReportErrorNumber(aCx, js_GetErrorMessage, NULL, JSMSG_WRONG_CONSTRUCTOR,
                          sClass.name);
     return false;
@@ -586,17 +631,17 @@ private:
     JS::Rooted<JSObject*> source(aCx);
     if (!JS_ConvertArguments(aCx, aArgc, JS_ARGV(aCx, aVp), "SbbSSo", type.address(),
                              &bubbles, &cancelable, data.address(),
                              origin.address(), source.address())) {
       return false;
     }
 
     InitMessageEventCommon(aCx, obj, event, type, bubbles, cancelable,
-                           data, origin, source, false);
+                           data, origin, source, JS::NullPtr(), false);
     return true;
   }
 };
 
 #define DECL_MESSAGEEVENT_CLASS(_varname, _name) \
   const JSClass _varname = { \
     _name, \
     JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(SLOT_COUNT), \
@@ -611,16 +656,18 @@ DECL_MESSAGEEVENT_CLASS(MessageEvent::sM
 
 const JSPropertySpec MessageEvent::sProperties[] = {
   JS_PSGS("data", Property<SLOT_data>::Get, GetterOnlyJSNative,
           JSPROP_ENUMERATE),
   JS_PSGS("origin", Property<SLOT_origin>::Get, GetterOnlyJSNative,
           JSPROP_ENUMERATE),
   JS_PSGS("source", Property<SLOT_source>::Get, GetterOnlyJSNative,
           JSPROP_ENUMERATE),
+  JS_PSGS("ports", Property<SLOT_ports>::Get, GetterOnlyJSNative,
+          JSPROP_ENUMERATE),
   JS_PS_END
 };
 
 const JSFunctionSpec MessageEvent::sFunctions[] = {
   JS_FN("initMessageEvent", InitMessageEvent, 6, FUNCTION_FLAGS),
   JS_FS_END
 };
 
@@ -1044,16 +1091,35 @@ JSObject*
 CreateProgressEvent(JSContext* aCx, JS::Handle<JSString*> aType, bool aLengthComputable,
                     double aLoaded, double aTotal)
 {
   JS::Rooted<JSObject*> global(aCx, JS::CurrentGlobalOrNull(aCx));
   return ProgressEvent::Create(aCx, global, aType, aLengthComputable, aLoaded,
                                aTotal);
 }
 
+JSObject*
+CreateConnectEvent(JSContext* aCx, JS::Handle<JSObject*> aMessagePort)
+{
+  JS::Rooted<JSObject*> global(aCx, JS::CurrentGlobalOrNull(aCx));
+
+  JS::Rooted<JSString*> type(aCx, JS_InternString(aCx, "connect"));
+  if (!type) {
+    return nullptr;
+  }
+
+  JS::Rooted<JSString*> emptyStr(aCx, JS_GetEmptyString(JS_GetRuntime(aCx)));
+  if (!emptyStr) {
+    return nullptr;
+  }
+
+  return MessageEvent::Create(aCx, global, type, false, false, emptyStr,
+                              emptyStr, JS::NullPtr(), aMessagePort, true);
+}
+
 bool
 IsSupportedEventClass(JSObject* aEvent)
 {
   return Event::IsSupportedClass(aEvent);
 }
 
 void
 SetEventTarget(JSObject* aEvent, JSObject* aTarget)
@@ -1095,9 +1161,9 @@ DispatchEventToTarget(JSContext* aCx, JS
   }
 
   *aPreventDefaultCalled = !!preventDefaultCalled;
   return true;
 }
 
 } // namespace events
 
-END_WORKERS_NAMESPACE
+END_WORKERS_NAMESPACE
\ No newline at end of file
--- a/dom/workers/Events.h
+++ b/dom/workers/Events.h
@@ -30,16 +30,19 @@ JSObject*
 CreateErrorEvent(JSContext* aCx, JS::Handle<JSString*> aMessage,
                  JS::Handle<JSString*> aFilename,
                  uint32_t aLineNumber, bool aMainRuntime);
 
 JSObject*
 CreateProgressEvent(JSContext* aCx, JS::Handle<JSString*> aType,
                     bool aLengthComputable, double aLoaded, double aTotal);
 
+JSObject*
+CreateConnectEvent(JSContext* aCx, JS::Handle<JSObject*> aMessagePort);
+
 bool
 IsSupportedEventClass(JSObject* aEvent);
 
 void
 SetEventTarget(JSObject* aEvent, JSObject* aTarget);
 
 bool
 EventWasCanceled(JSObject* aEvent);
--- a/dom/workers/FileReaderSync.cpp
+++ b/dom/workers/FileReaderSync.cpp
@@ -1,59 +1,67 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "FileReaderSync.h"
 
+#include "jsfriendapi.h"
+#include "mozilla/Base64.h"
+#include "mozilla/dom/EncodingUtils.h"
+#include "mozilla/dom/FileReaderSyncBinding.h"
 #include "nsCExternalHandlerService.h"
 #include "nsComponentManagerUtils.h"
 #include "nsCOMPtr.h"
 #include "nsDOMClassInfoID.h"
 #include "nsError.h"
 #include "nsIDOMFile.h"
 #include "nsICharsetDetector.h"
 #include "nsIConverterInputStream.h"
 #include "nsIInputStream.h"
 #include "nsIPlatformCharset.h"
 #include "nsISeekableStream.h"
 #include "nsISupportsImpl.h"
 #include "nsISupportsImpl.h"
 #include "nsNetUtil.h"
 #include "nsServiceManagerUtils.h"
+
 #include "File.h"
 #include "RuntimeService.h"
-#include "DOMBindingInlines.h"
-
-#include "mozilla/Base64.h"
-#include "mozilla/dom/EncodingUtils.h"
 
 USING_WORKERS_NAMESPACE
 using namespace mozilla;
 using mozilla::dom::Optional;
 using mozilla::dom::GlobalObject;
 
 NS_IMPL_ADDREF(FileReaderSync)
 NS_IMPL_RELEASE(FileReaderSync)
+
 NS_INTERFACE_MAP_BEGIN(FileReaderSync)
   NS_INTERFACE_MAP_ENTRY(nsICharsetDetectionObserver)
 NS_INTERFACE_MAP_END
 
 // static
 already_AddRefed<FileReaderSync>
 FileReaderSync::Constructor(const GlobalObject& aGlobal, ErrorResult& aRv)
 {
   nsRefPtr<FileReaderSync> frs = new FileReaderSync();
 
   return frs.forget();
 }
 
 JSObject*
+FileReaderSync::WrapObject(JSContext* aCx, JS::HandleObject aScope)
+{
+  return FileReaderSyncBinding_workers::Wrap(aCx, aScope, this);
+}
+
+JSObject*
 FileReaderSync::ReadAsArrayBuffer(JSContext* aCx,
                                   JS::Handle<JSObject*> aScopeObj,
                                   JS::Handle<JSObject*> aBlob,
                                   ErrorResult& aRv)
 {
   nsIDOMBlob* blob = file::GetDOMBlobFromJSObject(aBlob);
   if (!blob) {
     aRv.Throw(NS_ERROR_INVALID_ARG);
--- a/dom/workers/FileReaderSync.h
+++ b/dom/workers/FileReaderSync.h
@@ -5,19 +5,16 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_workers_filereadersync_h__
 #define mozilla_dom_workers_filereadersync_h__
 
 #include "Workers.h"
 
 #include "nsICharsetDetectionObserver.h"
-#include "nsString.h"
-#include "mozilla/Attributes.h"
-#include "mozilla/dom/FileReaderSyncBinding.h"
 
 class nsIInputStream;
 class nsIDOMBlob;
 
 namespace mozilla {
 class ErrorResult;
 
 namespace dom {
@@ -34,20 +31,17 @@ class FileReaderSync MOZ_FINAL : public 
   nsresult ConvertStream(nsIInputStream *aStream, const char *aCharset,
                          nsAString &aResult);
   nsresult GuessCharset(nsIInputStream *aStream, nsACString &aCharset);
 
 public:
   static already_AddRefed<FileReaderSync>
   Constructor(const GlobalObject& aGlobal, ErrorResult& aRv);
 
-  JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope)
-  {
-    return FileReaderSyncBinding_workers::Wrap(aCx, aScope, this);
-  }
+  JSObject* WrapObject(JSContext* aCx, JS::HandleObject aScope);
 
   NS_DECL_ISUPPORTS
 
   JSObject* ReadAsArrayBuffer(JSContext* aCx, JS::Handle<JSObject*> aScopeObj,
                               JS::Handle<JSObject*> aBlob,
                               ErrorResult& aRv);
   void ReadAsBinaryString(JS::Handle<JSObject*> aBlob, nsAString& aResult,
                           ErrorResult& aRv);
--- a/dom/workers/Location.cpp
+++ b/dom/workers/Location.cpp
@@ -1,18 +1,15 @@
 /* -*- 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 "Location.h"
 
-#include "DOMBindingInlines.h"
-
-#include "nsTraceRefcnt.h"
 #include "mozilla/dom/WorkerLocationBinding.h"
 
 BEGIN_WORKERS_NAMESPACE
 
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WorkerLocation)
 
 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(WorkerLocation, AddRef)
 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(WorkerLocation, Release)
new file mode 100644
--- /dev/null
+++ b/dom/workers/MessagePort.cpp
@@ -0,0 +1,209 @@
+/* -*- 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 "MessagePort.h"
+
+#include "mozilla/dom/WorkerMessagePortBinding.h"
+#include "nsDOMEvent.h"
+#include "nsEventDispatcher.h"
+
+#include "SharedWorker.h"
+
+using mozilla::dom::Optional;
+using mozilla::dom::Sequence;
+
+USING_WORKERS_NAMESPACE
+
+namespace {
+
+class DelayedEventRunnable MOZ_FINAL : public nsIRunnable
+{
+  nsRefPtr<MessagePort> mMessagePort;
+  nsCOMPtr<nsIDOMEvent> mEvent;
+
+public:
+  DelayedEventRunnable(MessagePort* aMessagePort,
+                       already_AddRefed<nsIDOMEvent> aEvent)
+  : mMessagePort(aMessagePort), mEvent(aEvent)
+  {
+    AssertIsOnMainThread();
+    MOZ_ASSERT(aMessagePort);
+    MOZ_ASSERT(aEvent.get());
+  }
+
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIRUNNABLE
+};
+
+} // anonymous namespace
+
+MessagePort::MessagePort(nsPIDOMWindow* aWindow, SharedWorker* aSharedWorker,
+                         uint64_t aSerial)
+: nsDOMEventTargetHelper(aWindow), mSharedWorker(aSharedWorker),
+  mSerial(aSerial), mStarted(false)
+{
+  AssertIsOnMainThread();
+  MOZ_ASSERT(aSharedWorker);
+}
+
+MessagePort::~MessagePort()
+{
+  AssertIsOnMainThread();
+
+  Close();
+}
+
+// static
+bool
+MessagePort::PrefEnabled()
+{
+  AssertIsOnMainThread();
+
+  // Currently tied to the SharedWorker preference.
+  return SharedWorker::PrefEnabled();
+}
+
+void
+MessagePort::PostMessage(JSContext* aCx, JS::HandleValue aMessage,
+                         const Optional<Sequence<JS::Value>>& aTransferable,
+                         ErrorResult& aRv)
+{
+  AssertIsOnMainThread();
+
+  if (IsClosed()) {
+    aRv = NS_ERROR_DOM_INVALID_STATE_ERR;
+    return;
+  }
+
+  mSharedWorker->PostMessage(aCx, aMessage, aTransferable, aRv);
+}
+
+void
+MessagePort::Start()
+{
+  AssertIsOnMainThread();
+
+  if (IsClosed()) {
+    NS_WARNING("Called start() after calling close()!");
+    return;
+  }
+
+  if (mStarted) {
+    return;
+  }
+
+  mStarted = true;
+
+  if (!mQueuedEvents.IsEmpty()) {
+    for (uint32_t index = 0; index < mQueuedEvents.Length(); index++) {
+      nsCOMPtr<nsIRunnable> runnable =
+        new DelayedEventRunnable(this, mQueuedEvents[index].forget());
+      if (NS_FAILED(NS_DispatchToCurrentThread(runnable))) {
+        NS_WARNING("Failed to dispatch queued event!");
+      }
+    }
+    mQueuedEvents.Clear();
+  }
+}
+
+void
+MessagePort::QueueEvent(nsIDOMEvent* aEvent)
+{
+  AssertIsOnMainThread();
+  MOZ_ASSERT(aEvent);
+  MOZ_ASSERT(!IsClosed());
+  MOZ_ASSERT(!mStarted);
+
+  mQueuedEvents.AppendElement(aEvent);
+}
+
+void
+MessagePort::CloseInternal()
+{
+  AssertIsOnMainThread();
+  MOZ_ASSERT(!IsClosed());
+  MOZ_ASSERT_IF(mStarted, mQueuedEvents.IsEmpty());
+
+  NS_WARN_IF_FALSE(mStarted, "Called close() before start()!");
+
+  if (!mStarted) {
+    mQueuedEvents.Clear();
+  }
+
+  mSharedWorker = nullptr;
+}
+
+NS_IMPL_ADDREF_INHERITED(MessagePort, nsDOMEventTargetHelper)
+NS_IMPL_RELEASE_INHERITED(MessagePort, nsDOMEventTargetHelper)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(MessagePort)
+NS_INTERFACE_MAP_END_INHERITING(nsDOMEventTargetHelper)
+
+NS_IMPL_CYCLE_COLLECTION_CLASS(MessagePort)
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(MessagePort,
+                                                  nsDOMEventTargetHelper)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSharedWorker)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mQueuedEvents)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(MessagePort,
+                                                nsDOMEventTargetHelper)
+  tmp->Close();
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+JSObject*
+MessagePort::WrapObject(JSContext* aCx, JS::HandleObject aScope)
+{
+  AssertIsOnMainThread();
+
+  return WorkerMessagePortBinding::Wrap(aCx, aScope, this);
+}
+
+nsresult
+MessagePort::PreHandleEvent(nsEventChainPreVisitor& aVisitor)
+{
+  AssertIsOnMainThread();
+
+  nsIDOMEvent*& event = aVisitor.mDOMEvent;
+
+  if (event) {
+    bool preventDispatch = false;
+
+    if (IsClosed()) {
+      preventDispatch = true;
+    } else if (mSharedWorker->IsSuspended()) {
+      mSharedWorker->QueueEvent(event);
+      preventDispatch = true;
+    } else if (!mStarted) {
+      QueueEvent(event);
+      preventDispatch = true;
+    }
+
+    if (preventDispatch) {
+      aVisitor.mCanHandle = false;
+      aVisitor.mParentTarget = nullptr;
+      return NS_OK;
+    }
+  }
+
+  return nsDOMEventTargetHelper::PreHandleEvent(aVisitor);
+}
+
+NS_IMPL_ISUPPORTS1(DelayedEventRunnable, nsIRunnable)
+
+NS_IMETHODIMP
+DelayedEventRunnable::Run()
+{
+  AssertIsOnMainThread();
+  MOZ_ASSERT(mMessagePort);
+  MOZ_ASSERT(mEvent);
+
+  bool ignored;
+  nsresult rv = mMessagePort->DispatchEvent(mEvent, &ignored);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return NS_OK;
+}
new file mode 100644
--- /dev/null
+++ b/dom/workers/MessagePort.h
@@ -0,0 +1,112 @@
+/* -*- 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_messageport_h_
+#define mozilla_dom_workers_messageport_h_
+
+#include "Workers.h"
+
+#include "mozilla/dom/BindingDeclarations.h"
+#include "nsDOMEventTargetHelper.h"
+
+class nsIDOMEvent;
+class nsPIDOMWindow;
+
+BEGIN_WORKERS_NAMESPACE
+
+class SharedWorker;
+
+class MessagePort MOZ_FINAL : public nsDOMEventTargetHelper
+{
+  friend class SharedWorker;
+
+  typedef mozilla::ErrorResult ErrorResult;
+
+  nsRefPtr<SharedWorker> mSharedWorker;
+  nsTArray<nsCOMPtr<nsIDOMEvent>> mQueuedEvents;
+  uint64_t mSerial;
+  bool mStarted;
+
+public:
+  static bool
+  PrefEnabled();
+
+  void
+  PostMessage(JSContext* aCx, JS::HandleValue aMessage,
+              const Optional<Sequence<JS::Value>>& aTransferable,
+              ErrorResult& aRv);
+
+  void
+  Start();
+
+  void
+  Close()
+  {
+    AssertIsOnMainThread();
+
+    if (!IsClosed()) {
+      CloseInternal();
+    }
+  }
+
+  uint64_t
+  Serial() const
+  {
+    return mSerial;
+  }
+
+  void
+  QueueEvent(nsIDOMEvent* aEvent);
+
+  NS_DECL_ISUPPORTS_INHERITED
+  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(MessagePort, nsDOMEventTargetHelper)
+
+  EventHandlerNonNull*
+  GetOnmessage()
+  {
+    AssertIsOnMainThread();
+
+    return GetEventHandler(nsGkAtoms::onmessage, EmptyString());
+  }
+
+  void
+  SetOnmessage(EventHandlerNonNull* aCallback)
+  {
+    AssertIsOnMainThread();
+
+    SetEventHandler(nsGkAtoms::onmessage, EmptyString(), aCallback);
+
+    Start();
+  }
+
+  bool
+  IsClosed() const
+  {
+    AssertIsOnMainThread();
+
+    return !mSharedWorker;
+  }
+
+  virtual JSObject*
+  WrapObject(JSContext* aCx, JS::HandleObject aScope) MOZ_OVERRIDE;
+
+  virtual nsresult
+  PreHandleEvent(nsEventChainPreVisitor& aVisitor) MOZ_OVERRIDE;
+
+private:
+  // This class can only be created by SharedWorker.
+  MessagePort(nsPIDOMWindow* aWindow, SharedWorker* aSharedWorker,
+              uint64_t aSerial);
+
+  // This class is reference-counted and will be destroyed from Release().
+  ~MessagePort();
+
+  void
+  CloseInternal();
+};
+
+END_WORKERS_NAMESPACE
+
+#endif // mozilla_dom_workers_messageport_h_
--- a/dom/workers/Navigator.cpp
+++ b/dom/workers/Navigator.cpp
@@ -1,18 +1,18 @@
 /* -*- 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 "Navigator.h"
 
-#include "DOMBindingInlines.h"
+#include "mozilla/dom/WorkerNavigatorBinding.h"
+
 #include "RuntimeService.h"
-#include "mozilla/dom/WorkerNavigatorBinding.h"
 
 BEGIN_WORKERS_NAMESPACE
 
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WorkerNavigator)
 
 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(WorkerNavigator, AddRef)
 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(WorkerNavigator, Release)
 
--- a/dom/workers/RuntimeService.cpp
+++ b/dom/workers/RuntimeService.cpp
@@ -1,25 +1,29 @@
 /* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "RuntimeService.h"
 
+#include "nsIChannel.h"
 #include "nsIContentSecurityPolicy.h"
+#include "nsIDocument.h"
 #include "nsIDOMChromeWindow.h"
 #include "nsIEffectiveTLDService.h"
 #include "nsIObserverService.h"
 #include "nsIPlatformCharset.h"
 #include "nsIPrincipal.h"
+#include "nsIScriptContext.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsISupportsPriority.h"
 #include "nsITimer.h"
+#include "nsIURI.h"
 #include "nsPIDOMWindow.h"
 
 #include <algorithm>
 #include "GeckoProfiler.h"
 #include "js/OldDebugAPI.h"
 #include "jsfriendapi.h"
 #include "mozilla/CycleCollectedJSRuntime.h"
 #include "mozilla/dom/AtomList.h"
@@ -31,22 +35,24 @@
 #include <Navigator.h>
 #include "nsContentUtils.h"
 #include "nsCycleCollector.h"
 #include "nsDOMJSUtils.h"
 #include "nsLayoutStatics.h"
 #include "nsNetUtil.h"
 #include "nsServiceManagerUtils.h"
 #include "nsThreadUtils.h"
+#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;
@@ -1025,42 +1031,42 @@ ResolveWorkerClasses(JSContext* aCx, JS:
   }
 
   // Not resolved.
   aObjp.set(nullptr);
   return true;
 }
 
 void
-CancelWorkersForWindow(JSContext* aCx, nsPIDOMWindow* aWindow)
+CancelWorkersForWindow(nsPIDOMWindow* aWindow)
 {
   AssertIsOnMainThread();
   RuntimeService* runtime = RuntimeService::GetService();
   if (runtime) {
-    runtime->CancelWorkersForWindow(aCx, aWindow);
+    runtime->CancelWorkersForWindow(aWindow);
   }
 }
 
 void
-SuspendWorkersForWindow(JSContext* aCx, nsPIDOMWindow* aWindow)
+SuspendWorkersForWindow(nsPIDOMWindow* aWindow)
 {
   AssertIsOnMainThread();
   RuntimeService* runtime = RuntimeService::GetService();
   if (runtime) {
-    runtime->SuspendWorkersForWindow(aCx, aWindow);
+    runtime->SuspendWorkersForWindow(aWindow);
   }
 }
 
 void
-ResumeWorkersForWindow(nsIScriptContext* aCx, nsPIDOMWindow* aWindow)
+ResumeWorkersForWindow(nsPIDOMWindow* aWindow)
 {
   AssertIsOnMainThread();
   RuntimeService* runtime = RuntimeService::GetService();
   if (runtime) {
-    runtime->ResumeWorkersForWindow(aCx, aWindow);
+    runtime->ResumeWorkersForWindow(aWindow);
   }
 }
 
 namespace {
 
 class WorkerTaskRunnable : public WorkerRunnable
 {
 public:
@@ -1200,51 +1206,75 @@ RuntimeService::RegisterWorker(JSContext
     AssertIsOnMainThread();
 
     if (mShuttingDown) {
       JS_ReportError(aCx, "Cannot create worker during shutdown!");
       return false;
     }
   }
 
+  bool isSharedWorker = aWorkerPrivate->IsSharedWorker();
+
+  const nsString& sharedWorkerName = aWorkerPrivate->SharedWorkerName();
+  nsCString sharedWorkerScriptSpec;
+
+  if (isSharedWorker) {
+    AssertIsOnMainThread();
+
+    nsCOMPtr<nsIURI> scriptURI = aWorkerPrivate->GetResolvedScriptURI();
+    NS_ASSERTION(scriptURI, "Null script URI!");
+
+    nsresult rv = scriptURI->GetSpec(sharedWorkerScriptSpec);
+    if (NS_FAILED(rv)) {
+      NS_WARNING("GetSpec failed?!");
+      xpc::Throw(aCx, rv);
+      return false;
+    }
+
+    NS_ASSERTION(!sharedWorkerScriptSpec.IsEmpty(), "Empty spec!");
+  }
+
+  const nsCString& domain = aWorkerPrivate->Domain();
+
   WorkerDomainInfo* domainInfo;
   bool queued = false;
   {
-    const nsCString& domain = aWorkerPrivate->Domain();
-
     MutexAutoLock lock(mMutex);
 
     if (!mDomainMap.Get(domain, &domainInfo)) {
       NS_ASSERTION(!parent, "Shouldn't have a parent here!");
 
       domainInfo = new WorkerDomainInfo();
       domainInfo->mDomain = domain;
       mDomainMap.Put(domain, domainInfo);
     }
 
-    if (domainInfo) {
-      queued = gMaxWorkersPerDomain &&
-               domainInfo->ActiveWorkerCount() >= gMaxWorkersPerDomain &&
-               !domain.IsEmpty();
+    queued = gMaxWorkersPerDomain &&
+             domainInfo->ActiveWorkerCount() >= gMaxWorkersPerDomain &&
+             !domain.IsEmpty();
 
-      if (queued) {
-        domainInfo->mQueuedWorkers.AppendElement(aWorkerPrivate);
-      }
-      else if (parent) {
-        domainInfo->mChildWorkerCount++;
-      }
-      else {
-        domainInfo->mActiveWorkers.AppendElement(aWorkerPrivate);
-      }
+    if (queued) {
+      domainInfo->mQueuedWorkers.AppendElement(aWorkerPrivate);
+    }
+    else if (parent) {
+      domainInfo->mChildWorkerCount++;
+    }
+    else {
+      domainInfo->mActiveWorkers.AppendElement(aWorkerPrivate);
     }
-  }
+
+    if (isSharedWorker) {
+      MOZ_ASSERT(!domainInfo->mSharedWorkerInfos.Get(sharedWorkerScriptSpec));
 
-  if (!domainInfo) {
-    JS_ReportOutOfMemory(aCx);
-    return false;
+      SharedWorkerInfo* sharedWorkerInfo =
+        new SharedWorkerInfo(aWorkerPrivate, sharedWorkerScriptSpec,
+                             sharedWorkerName);
+      domainInfo->mSharedWorkerInfos.Put(sharedWorkerScriptSpec,
+                                         sharedWorkerInfo);
+    }
   }
 
   // From here on out we must call UnregisterWorker if something fails!
   if (parent) {
     if (!parent->AddChildWorker(aCx, aWorkerPrivate)) {
       UnregisterWorker(aCx, aWorkerPrivate);
       return false;
     }
@@ -1262,25 +1292,25 @@ RuntimeService::RegisterWorker(JSContext
 
       mNavigatorStringsLoaded = true;
     }
 
     nsPIDOMWindow* window = aWorkerPrivate->GetWindow();
 
     nsTArray<WorkerPrivate*>* windowArray;
     if (!mWindowMap.Get(window, &windowArray)) {
-      NS_ASSERTION(!parent, "Shouldn't have a parent here!");
-
       windowArray = new nsTArray<WorkerPrivate*>(1);
       mWindowMap.Put(window, windowArray);
     }
 
-    NS_ASSERTION(!windowArray->Contains(aWorkerPrivate),
-                 "Already know about this worker!");
-    windowArray->AppendElement(aWorkerPrivate);
+    if (!windowArray->Contains(aWorkerPrivate)) {
+      windowArray->AppendElement(aWorkerPrivate);
+    } else {
+      MOZ_ASSERT(aWorkerPrivate->IsSharedWorker());
+    }
   }
 
   if (!queued && !ScheduleWorker(aCx, aWorkerPrivate)) {
     return false;
   }
 
   return true;
 }
@@ -1290,20 +1320,20 @@ RuntimeService::UnregisterWorker(JSConte
 {
   aWorkerPrivate->AssertIsOnParentThread();
 
   WorkerPrivate* parent = aWorkerPrivate->GetParent();
   if (!parent) {
     AssertIsOnMainThread();
   }
 
+  const nsCString& domain = aWorkerPrivate->Domain();
+
   WorkerPrivate* queuedWorker = nullptr;
   {
-    const nsCString& domain = aWorkerPrivate->Domain();
-
     MutexAutoLock lock(mMutex);
 
     WorkerDomainInfo* domainInfo;
     if (!mDomainMap.Get(domain, &domainInfo)) {
       NS_ERROR("Don't have an entry for this domain!");
     }
 
     // Remove old worker from everywhere.
@@ -1317,16 +1347,27 @@ RuntimeService::UnregisterWorker(JSConte
       domainInfo->mChildWorkerCount--;
     }
     else {
       NS_ASSERTION(domainInfo->mActiveWorkers.Contains(aWorkerPrivate),
                    "Don't know about this worker!");
       domainInfo->mActiveWorkers.RemoveElement(aWorkerPrivate);
     }
 
+    if (aWorkerPrivate->IsSharedWorker()) {
+      MatchSharedWorkerInfo match(aWorkerPrivate);
+      domainInfo->mSharedWorkerInfos.EnumerateRead(FindSharedWorkerInfo,
+                                                   &match);
+
+      if (match.mSharedWorkerInfo) {
+        domainInfo->mSharedWorkerInfos.Remove(
+          match.mSharedWorkerInfo->mScriptSpec);
+      }
+    }
+
     // See if there's a queued worker we can schedule.
     if (domainInfo->ActiveWorkerCount() < gMaxWorkersPerDomain &&
         !domainInfo->mQueuedWorkers.IsEmpty()) {
       queuedWorker = domainInfo->mQueuedWorkers[0];
       domainInfo->mQueuedWorkers.RemoveElementAt(0);
 
       if (queuedWorker->GetParent()) {
         domainInfo->mChildWorkerCount++;
@@ -1337,33 +1378,49 @@ RuntimeService::UnregisterWorker(JSConte
     }
 
     if (!domainInfo->ActiveWorkerCount()) {
       NS_ASSERTION(domainInfo->mQueuedWorkers.IsEmpty(), "Huh?!");
       mDomainMap.Remove(domain);
     }
   }
 
+  if (aWorkerPrivate->IsSharedWorker()) {
+    AssertIsOnMainThread();
+
+    nsAutoTArray<nsRefPtr<SharedWorker>, 5> sharedWorkersToNotify;
+    aWorkerPrivate->GetAllSharedWorkers(sharedWorkersToNotify);
+
+    for (uint32_t index = 0; index < sharedWorkersToNotify.Length(); index++) {
+      MOZ_ASSERT(sharedWorkersToNotify[index]);
+      sharedWorkersToNotify[index]->NoteDeadWorker(aCx);
+    }
+  }
+
   if (parent) {
     parent->RemoveChildWorker(aCx, aWorkerPrivate);
   }
+  else if (aWorkerPrivate->IsSharedWorker()) {
+    mWindowMap.Enumerate(RemoveSharedWorkerFromWindowMap, aWorkerPrivate);
+  }
   else {
+    // May be null.
     nsPIDOMWindow* window = aWorkerPrivate->GetWindow();
 
     nsTArray<WorkerPrivate*>* windowArray;
     if (!mWindowMap.Get(window, &windowArray)) {
-      NS_ERROR("Don't have an entry for this window!");
+      MOZ_ASSERT(false, "Don't have an entry for this window!");
     }
 
-    NS_ASSERTION(windowArray->Contains(aWorkerPrivate),
-                 "Don't know about this worker!");
-    windowArray->RemoveElement(aWorkerPrivate);
+    if (!windowArray->RemoveElement(aWorkerPrivate)) {
+      MOZ_ASSERT(false, "Worker wasn't in the correct window array!");
+    }
 
     if (windowArray->IsEmpty()) {
-      NS_ASSERTION(!queuedWorker, "How can this be?!");
+      MOZ_ASSERT(!queuedWorker, "queuedWorker should be in this array!");
       mWindowMap.Remove(window);
     }
   }
 
   if (queuedWorker && !ScheduleWorker(aCx, queuedWorker)) {
     UnregisterWorker(aCx, queuedWorker);
   }
 }
@@ -1789,16 +1846,58 @@ RuntimeService::AddAllTopLevelWorkersToA
     if (!worker->GetParent()) {
       array->AppendElement(worker);
     }
   }
 
   return PL_DHASH_NEXT;
 }
 
+// static
+PLDHashOperator
+RuntimeService::RemoveSharedWorkerFromWindowMap(
+                                  nsPIDOMWindow* aKey,
+                                  nsAutoPtr<nsTArray<WorkerPrivate*> >& aData,
+                                  void* aUserArg)
+{
+  AssertIsOnMainThread();
+  MOZ_ASSERT(aData.get());
+  MOZ_ASSERT(aUserArg);
+
+  auto workerPrivate = static_cast<WorkerPrivate*>(aUserArg);
+
+  MOZ_ASSERT(workerPrivate->IsSharedWorker());
+
+  if (aData->RemoveElement(workerPrivate)) {
+    MOZ_ASSERT(!aData->Contains(workerPrivate), "Added worker more than once!");
+
+    if (aData->IsEmpty()) {
+      return PL_DHASH_REMOVE;
+    }
+  }
+
+  return PL_DHASH_NEXT;
+}
+
+// static
+PLDHashOperator
+RuntimeService::FindSharedWorkerInfo(const nsACString& aKey,
+                                     SharedWorkerInfo* aData,
+                                     void* aUserArg)
+{
+  auto match = static_cast<MatchSharedWorkerInfo*>(aUserArg);
+
+  if (aData->mWorkerPrivate == match->mWorkerPrivate) {
+    match->mSharedWorkerInfo = aData;
+    return PL_DHASH_STOP;
+  }
+
+  return PL_DHASH_NEXT;
+}
+
 void
 RuntimeService::GetWorkersForWindow(nsPIDOMWindow* aWindow,
                                     nsTArray<WorkerPrivate*>& aWorkers)
 {
   AssertIsOnMainThread();
 
   nsTArray<WorkerPrivate*>* workers;
   if (mWindowMap.Get(aWindow, &workers)) {
@@ -1806,67 +1905,177 @@ RuntimeService::GetWorkersForWindow(nsPI
     aWorkers.AppendElements(*workers);
   }
   else {
     NS_ASSERTION(aWorkers.IsEmpty(), "Should be empty!");
   }
 }
 
 void
-RuntimeService::CancelWorkersForWindow(JSContext* aCx,
-                                       nsPIDOMWindow* aWindow)
+RuntimeService::CancelWorkersForWindow(nsPIDOMWindow* aWindow)
 {
   AssertIsOnMainThread();
 
-  nsAutoTArray<WorkerPrivate*, 100> workers;
+  nsAutoTArray<WorkerPrivate*, MAX_WORKERS_PER_DOMAIN> workers;
   GetWorkersForWindow(aWindow, workers);
 
   if (!workers.IsEmpty()) {
+    nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(aWindow);
+    MOZ_ASSERT(sgo);
+
+    nsIScriptContext* scx = sgo->GetContext();
+
+    AutoPushJSContext cx(scx ?
+                         scx->GetNativeContext() :
+                         nsContentUtils::GetSafeJSContext());
+
     for (uint32_t index = 0; index < workers.Length(); index++) {
-      if (!workers[index]->Cancel(aCx)) {
-        NS_WARNING("Failed to cancel worker!");
+      WorkerPrivate*& worker = workers[index];
+
+      if (worker->IsSharedWorker()) {
+        worker->CloseSharedWorkersForWindow(aWindow);
+      } else if (!worker->Cancel(cx)) {
+        JS_ReportPendingException(cx);
+      }
+    }
+  }
+}
+
+void
+RuntimeService::SuspendWorkersForWindow(nsPIDOMWindow* aWindow)
+{
+  AssertIsOnMainThread();
+  MOZ_ASSERT(aWindow);
+
+  nsAutoTArray<WorkerPrivate*, MAX_WORKERS_PER_DOMAIN> workers;
+  GetWorkersForWindow(aWindow, workers);
+
+  if (!workers.IsEmpty()) {
+    nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(aWindow);
+    MOZ_ASSERT(sgo);
+
+    nsIScriptContext* scx = sgo->GetContext();
+
+    AutoPushJSContext cx(scx ?
+                         scx->GetNativeContext() :
+                         nsContentUtils::GetSafeJSContext());
+
+    for (uint32_t index = 0; index < workers.Length(); index++) {
+      if (!workers[index]->Suspend(cx, aWindow)) {
+        JS_ReportPendingException(cx);
       }
     }
   }
 }
 
 void
-RuntimeService::SuspendWorkersForWindow(JSContext* aCx,
-                                        nsPIDOMWindow* aWindow)
+RuntimeService::ResumeWorkersForWindow(nsPIDOMWindow* aWindow)
 {
   AssertIsOnMainThread();
+  MOZ_ASSERT(aWindow);
 
-  nsAutoTArray<WorkerPrivate*, 100> workers;
+  nsAutoTArray<WorkerPrivate*, MAX_WORKERS_PER_DOMAIN> workers;
   GetWorkersForWindow(aWindow, workers);
 
   if (!workers.IsEmpty()) {
+    nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(aWindow);
+    MOZ_ASSERT(sgo);
+
+    nsIScriptContext* scx = sgo->GetContext();
+
+    AutoPushJSContext cx(scx ?
+                         scx->GetNativeContext() :
+                         nsContentUtils::GetSafeJSContext());
+
     for (uint32_t index = 0; index < workers.Length(); index++) {
-      if (!workers[index]->Suspend(aCx)) {
-        NS_WARNING("Failed to cancel worker!");
+      if (!workers[index]->SynchronizeAndResume(cx, aWindow, scx)) {
+        JS_ReportPendingException(cx);
       }
     }
   }
 }
 
-void
-RuntimeService::ResumeWorkersForWindow(nsIScriptContext* aCx,
-                                       nsPIDOMWindow* aWindow)
+nsresult
+RuntimeService::CreateSharedWorker(JSContext* aCx, nsPIDOMWindow* aWindow,
+                                   const nsAString& aScriptURL,
+                                   const nsAString& aName,
+                                   SharedWorker** aSharedWorker)
 {
   AssertIsOnMainThread();
+  MOZ_ASSERT(aCx);
+  MOZ_ASSERT(aWindow);
 
-  nsAutoTArray<WorkerPrivate*, 100> workers;
-  GetWorkersForWindow(aWindow, workers);
+  WorkerPrivate::LoadInfo loadInfo;
+  nsresult rv = WorkerPrivate::GetLoadInfo(aCx, aWindow, nullptr, aScriptURL,
+                                           false, &loadInfo);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  MOZ_ASSERT(loadInfo.mResolvedScriptURI);
 
-  if (!workers.IsEmpty()) {
-    for (uint32_t index = 0; index < workers.Length(); index++) {
-      if (!workers[index]->SynchronizeAndResume(aCx)) {
-        NS_WARNING("Failed to cancel worker!");
-      }
+  nsCString scriptSpec;
+  rv = loadInfo.mResolvedScriptURI->GetSpec(scriptSpec);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  WorkerPrivate* workerPrivate = nullptr;
+  {
+    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;
+    }
+
+    created = true;
+    newWorkerPrivate.forget(&workerPrivate);
+  }
+
+  MOZ_ASSERT(workerPrivate->IsSharedWorker());
+
+  nsRefPtr<SharedWorker> sharedWorker =
+    new SharedWorker(aWindow, workerPrivate);
+
+  if (!workerPrivate->RegisterSharedWorker(aCx, sharedWorker)) {
+    NS_WARNING("Failed to dispatch to worker!");
+    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)) {
+      windowArray = new nsTArray<WorkerPrivate*>(1);
+      mWindowMap.Put(aWindow, windowArray);
+    }
+
+    if (!windowArray->Contains(workerPrivate)) {
+      windowArray->AppendElement(workerPrivate);
+    }
+  }
+
+  sharedWorker.forget(aSharedWorker);
+  return NS_OK;
 }
 
 void
 RuntimeService::NoteIdleThread(nsIThread* aThread)
 {
   AssertIsOnMainThread();
   NS_ASSERTION(aThread, "Null pointer!");
 
--- a/dom/workers/RuntimeService.h
+++ b/dom/workers/RuntimeService.h
@@ -9,65 +9,95 @@
 
 #include "Workers.h"
 
 #include "nsIObserver.h"
 
 #include "mozilla/Attributes.h"
 #include "mozilla/Mutex.h"
 #include "mozilla/TimeStamp.h"
+#include "mozilla/dom/BindingDeclarations.h"
 #include "nsAutoPtr.h"
 #include "nsClassHashtable.h"
 #include "nsCOMPtr.h"
+#include "nsCycleCollectionParticipant.h"
 #include "nsHashKeys.h"
 #include "nsString.h"
 #include "nsTArray.h"
 
 class nsIThread;
 class nsITimer;
 class nsPIDOMWindow;
 
 BEGIN_WORKERS_NAMESPACE
 
+class SharedWorker;
 class WorkerPrivate;
 
 class RuntimeService MOZ_FINAL : public nsIObserver
 {
+  struct SharedWorkerInfo
+  {
+    WorkerPrivate* mWorkerPrivate;
+    nsCString mScriptSpec;
+    nsString mName;
+
+    SharedWorkerInfo(WorkerPrivate* aWorkerPrivate,
+                     const nsACString& aScriptSpec,
+                     const nsAString& aName)
+    : mWorkerPrivate(aWorkerPrivate), mScriptSpec(aScriptSpec), mName(aName)
+    { }
+  };
+
   struct WorkerDomainInfo
   {
     nsCString mDomain;
     nsTArray<WorkerPrivate*> mActiveWorkers;
     nsTArray<WorkerPrivate*> mQueuedWorkers;
+    nsClassHashtable<nsCStringHashKey, SharedWorkerInfo> mSharedWorkerInfos;
     uint32_t mChildWorkerCount;
 
-    WorkerDomainInfo() : mActiveWorkers(1), mChildWorkerCount(0) { }
+    WorkerDomainInfo()
+    : mActiveWorkers(1), mChildWorkerCount(0)
+    { }
 
     uint32_t
     ActiveWorkerCount() const
     {
       return mActiveWorkers.Length() + mChildWorkerCount;
     }
   };
 
   struct IdleThreadInfo
   {
     nsCOMPtr<nsIThread> mThread;
     mozilla::TimeStamp mExpirationTime;
   };
 
+  struct MatchSharedWorkerInfo
+  {
+    WorkerPrivate* mWorkerPrivate;
+    SharedWorkerInfo* mSharedWorkerInfo;
+
+    MatchSharedWorkerInfo(WorkerPrivate* aWorkerPrivate)
+    : mWorkerPrivate(aWorkerPrivate), mSharedWorkerInfo(nullptr)
+    { }
+  };
+
   mozilla::Mutex mMutex;
 
   // Protected by mMutex.
   nsClassHashtable<nsCStringHashKey, WorkerDomainInfo> mDomainMap;
 
   // Protected by mMutex.
   nsTArray<IdleThreadInfo> mIdleThreadArray;
 
   // *Not* protected by mMutex.
-  nsClassHashtable<nsPtrHashKey<nsPIDOMWindow>, nsTArray<WorkerPrivate*> > mWindowMap;
+  nsClassHashtable<nsPtrHashKey<nsPIDOMWindow>,
+                   nsTArray<WorkerPrivate*> > mWindowMap;
 
   // Only used on the main thread.
   nsCOMPtr<nsITimer> mIdleThreadTimer;
 
   nsCString mDetectorName;
   nsCString mSystemCharset;
 
   static JSSettings sDefaultJSSettings;
@@ -101,23 +131,28 @@ public:
 
   bool
   RegisterWorker(JSContext* aCx, WorkerPrivate* aWorkerPrivate);
 
   void
   UnregisterWorker(JSContext* aCx, WorkerPrivate* aWorkerPrivate);
 
   void
-  CancelWorkersForWindow(JSContext* aCx, nsPIDOMWindow* aWindow);
+  CancelWorkersForWindow(nsPIDOMWindow* aWindow);
+
+  void
+  SuspendWorkersForWindow(nsPIDOMWindow* aWindow);
 
   void
-  SuspendWorkersForWindow(JSContext* aCx, nsPIDOMWindow* aWindow);
+  ResumeWorkersForWindow(nsPIDOMWindow* aWindow);
 
-  void
-  ResumeWorkersForWindow(nsIScriptContext* aCx, nsPIDOMWindow* aWindow);
+  nsresult
+  CreateSharedWorker(JSContext* aCx, nsPIDOMWindow* aWindow,
+                     const nsAString& aScriptURL, const nsAString& aName,
+                     SharedWorker** aSharedWorker);
 
   const nsACString&
   GetDetectorName() const
   {
     return mDetectorName;
   }
 
   const nsACString&
@@ -214,16 +249,26 @@ private:
   void
   Cleanup();
 
   static PLDHashOperator
   AddAllTopLevelWorkersToArray(const nsACString& aKey,
                                WorkerDomainInfo* aData,
                                void* aUserArg);
 
+  static PLDHashOperator
+  RemoveSharedWorkerFromWindowMap(nsPIDOMWindow* aKey,
+                                  nsAutoPtr<nsTArray<WorkerPrivate*> >& aData,
+                                  void* aUserArg);
+
+  static PLDHashOperator
+  FindSharedWorkerInfo(const nsACString& aKey,
+                       SharedWorkerInfo* aData,
+                       void* aUserArg);
+
   void
   GetWorkersForWindow(nsPIDOMWindow* aWindow,
                       nsTArray<WorkerPrivate*>& aWorkers);
 
   bool
   ScheduleWorker(JSContext* aCx, WorkerPrivate* aWorkerPrivate);
 
   static void
--- a/dom/workers/ScriptLoader.cpp
+++ b/dom/workers/ScriptLoader.cpp
@@ -33,40 +33,108 @@
 
 #include "mozilla/dom/Exceptions.h"
 #include "Principal.h"
 #include "WorkerFeature.h"
 #include "WorkerPrivate.h"
 
 #define MAX_CONCURRENT_SCRIPTS 1000
 
-BEGIN_WORKERS_NAMESPACE
+USING_WORKERS_NAMESPACE
 
-namespace scriptloader {
-static
+using mozilla::dom::workers::exceptions::ThrowDOMExceptionForNSResult;
+
+namespace {
+
 nsresult
 ChannelFromScriptURL(nsIPrincipal* principal,
                      nsIURI* baseURI,
                      nsIDocument* parentDoc,
                      nsILoadGroup* loadGroup,
                      nsIIOService* ios,
                      nsIScriptSecurityManager* secMan,
-                     const nsString& aScriptURL,
+                     const nsAString& aScriptURL,
                      bool aIsWorkerScript,
-                     nsIChannel** aChannel);
+                     nsIChannel** aChannel)
+{
+  AssertIsOnMainThread();
+
+  nsresult rv;
+  nsCOMPtr<nsIURI> uri;
+  rv = nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(uri),
+                                                 aScriptURL, parentDoc,
+                                                 baseURI);
+  if (NS_FAILED(rv)) {
+    return NS_ERROR_DOM_SYNTAX_ERR;
+  }
 
-} // namespace scriptloader
+  // If we're part of a document then check the content load policy.
+  if (parentDoc) {
+    int16_t shouldLoad = nsIContentPolicy::ACCEPT;
+    rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_SCRIPT, uri,
+                                   principal, parentDoc,
+                                   NS_LITERAL_CSTRING("text/javascript"),
+                                   nullptr, &shouldLoad,
+                                   nsContentUtils::GetContentPolicy(),
+                                   secMan);
+    if (NS_FAILED(rv) || NS_CP_REJECTED(shouldLoad)) {
+      if (NS_FAILED(rv) || shouldLoad != nsIContentPolicy::REJECT_TYPE) {
+        return rv = NS_ERROR_CONTENT_BLOCKED;
+      }
+      return rv = NS_ERROR_CONTENT_BLOCKED_SHOW_ALT;
+    }
+  }
+
+  // If this script loader is being used to make a new worker then we need
+  // to do a same-origin check. Otherwise we need to clear the load with the
+  // security manager.
+  if (aIsWorkerScript) {
+    nsCString scheme;
+    rv = uri->GetScheme(scheme);
+    NS_ENSURE_SUCCESS(rv, rv);
 
-END_WORKERS_NAMESPACE
+    // We pass true as the 3rd argument to checkMayLoad here.
+    // This allows workers in sandboxed documents to load data URLs
+    // (and other URLs that inherit their principal from their
+    // creator.)
+    rv = principal->CheckMayLoad(uri, false, true);
+    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_SECURITY_ERR);
+  }
+  else {
+    rv = secMan->CheckLoadURIWithPrincipal(principal, uri, 0);
+    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_SECURITY_ERR);
+  }
 
-USING_WORKERS_NAMESPACE
+  // Get Content Security Policy from parent document to pass into channel.
+  nsCOMPtr<nsIContentSecurityPolicy> csp;
+  rv = principal->GetCsp(getter_AddRefs(csp));
+  NS_ENSURE_SUCCESS(rv, rv);
 
-using mozilla::dom::Throw;
+  nsCOMPtr<nsIChannelPolicy> channelPolicy;
+  if (csp) {
+    channelPolicy = do_CreateInstance(NSCHANNELPOLICY_CONTRACTID, &rv);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = channelPolicy->SetContentSecurityPolicy(csp);
+    NS_ENSURE_SUCCESS(rv, rv);
 
-namespace {
+    rv = channelPolicy->SetLoadType(nsIContentPolicy::TYPE_SCRIPT);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  uint32_t flags = nsIRequest::LOAD_NORMAL | nsIChannel::LOAD_CLASSIFY_URI;
+
+  nsCOMPtr<nsIChannel> channel;
+  rv = NS_NewChannel(getter_AddRefs(channel), uri, ios, loadGroup, nullptr,
+                     flags, channelPolicy);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  channel.forget(aChannel);
+  return rv;
+}
 
 class ScriptLoaderRunnable;
 
 struct ScriptLoadInfo
 {
   ScriptLoadInfo()
   : mLoadResult(NS_ERROR_NOT_INITIALIZED), mExecutionScheduled(false),
     mExecutionResult(false)
@@ -270,17 +338,17 @@ public:
     }
 
     // May be null.
     nsCOMPtr<nsIDocument> parentDoc = mWorkerPrivate->GetDocument();
 
     nsCOMPtr<nsIChannel> channel;
     if (mIsWorkerScript) {
       // May be null.
-      channel = mWorkerPrivate->GetChannel();
+      channel = mWorkerPrivate->ForgetWorkerChannel();
     }
 
     // All of these can potentially be null, but that should be ok. We'll either
     // succeed without them or fail below.
     nsCOMPtr<nsILoadGroup> loadGroup;
     if (parentDoc) {
       loadGroup = parentDoc->GetDocumentLoadGroup();
     }
@@ -290,19 +358,18 @@ public:
     nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
     NS_ASSERTION(secMan, "This should never be null!");
 
     for (uint32_t index = 0; index < mLoadInfos.Length(); index++) {
       ScriptLoadInfo& loadInfo = mLoadInfos[index];
       nsresult& rv = loadInfo.mLoadResult;
 
       if (!channel) {
-        rv = scriptloader::ChannelFromScriptURL(principal, baseURI, parentDoc,
-                                                loadGroup, ios, secMan,
-                                                loadInfo.mURL, mIsWorkerScript,
+        rv = ChannelFromScriptURL(principal, baseURI, parentDoc, loadGroup, ios,
+                                  secMan, loadInfo.mURL, mIsWorkerScript,
                                                 getter_AddRefs(channel));
         if (NS_FAILED(rv)) {
           return rv;
         }
       }
 
       // We need to know which index we're on in OnStreamComplete so we know
       // where to put the result.
@@ -476,16 +543,22 @@ public:
     }
 
     return NS_OK;
   }
 
   void
   ExecuteFinishedScripts()
   {
+    AssertIsOnMainThread();
+
+    if (mIsWorkerScript) {
+      mWorkerPrivate->WorkerScriptLoaded();
+    }
+
     uint32_t firstIndex = UINT32_MAX;
     uint32_t lastIndex = UINT32_MAX;
 
     // Find firstIndex based on whether mExecutionScheduled is unset.
     for (uint32_t index = 0; index < mLoadInfos.Length(); index++) {
       if (!mLoadInfos[index].mExecutionScheduled) {
         firstIndex = index;
         break;
@@ -539,24 +612,24 @@ public:
     return true;
   }
 };
 
 class ChannelGetterRunnable MOZ_FINAL : public nsRunnable
 {
   WorkerPrivate* mParentWorker;
   uint32_t mSyncQueueKey;
-  const nsString& mScriptURL;
+  const nsAString& mScriptURL;
   nsIChannel** mChannel;
   nsresult mResult;
 
 public:
   ChannelGetterRunnable(WorkerPrivate* aParentWorker,
                         uint32_t aSyncQueueKey,
-                        const nsString& aScriptURL,
+                        const nsAString& aScriptURL,
                         nsIChannel** aChannel)
   : mParentWorker(aParentWorker), mSyncQueueKey(aSyncQueueKey),
     mScriptURL(aScriptURL), mChannel(aChannel), mResult(NS_ERROR_FAILURE)
   {
     aParentWorker->AssertIsOnWorkerThread();
   }
 
   virtual ~ChannelGetterRunnable() { }
@@ -718,108 +791,21 @@ LoadAllScripts(JSContext* aCx, WorkerPri
 }
 
 } /* anonymous namespace */
 
 BEGIN_WORKERS_NAMESPACE
 
 namespace scriptloader {
 
-// static
-nsresult
-ChannelFromScriptURL(nsIPrincipal* principal,
-                     nsIURI* baseURI,
-                     nsIDocument* parentDoc,
-                     nsILoadGroup* loadGroup,
-                     nsIIOService* ios,
-                     nsIScriptSecurityManager* secMan,
-                     const nsString& aScriptURL,
-                     bool aIsWorkerScript,
-                     nsIChannel** aChannel)
-{
-  nsresult rv;
-  nsCOMPtr<nsIURI> uri;
-  rv = nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(uri),
-                                                 aScriptURL, parentDoc,
-                                                 baseURI);
-  if (NS_FAILED(rv)) {
-    return NS_ERROR_DOM_SYNTAX_ERR;
-  }
-
-  // If we're part of a document then check the content load policy.
-  if (parentDoc) {
-    int16_t shouldLoad = nsIContentPolicy::ACCEPT;
-    rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_SCRIPT, uri,
-                                   principal, parentDoc,
-                                   NS_LITERAL_CSTRING("text/javascript"),
-                                   nullptr, &shouldLoad,
-                                   nsContentUtils::GetContentPolicy(),
-                                   secMan);
-    if (NS_FAILED(rv) || NS_CP_REJECTED(shouldLoad)) {
-      if (NS_FAILED(rv) || shouldLoad != nsIContentPolicy::REJECT_TYPE) {
-        return rv = NS_ERROR_CONTENT_BLOCKED;
-      }
-      return rv = NS_ERROR_CONTENT_BLOCKED_SHOW_ALT;
-    }
-  }
-
-  // If this script loader is being used to make a new worker then we need
-  // to do a same-origin check. Otherwise we need to clear the load with the
-  // security manager.
-  if (aIsWorkerScript) {
-    nsCString scheme;
-    rv = uri->GetScheme(scheme);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    // We pass true as the 3rd argument to checkMayLoad here.
-    // This allows workers in sandboxed documents to load data URLs
-    // (and other URLs that inherit their principal from their
-    // creator.)
-    rv = principal->CheckMayLoad(uri, false, true);
-    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_SECURITY_ERR);
-  }
-  else {
-    rv = secMan->CheckLoadURIWithPrincipal(principal, uri, 0);
-    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_SECURITY_ERR);
-  }
-
-  // Get Content Security Policy from parent document to pass into channel.
-  nsCOMPtr<nsIContentSecurityPolicy> csp;
-  rv = principal->GetCsp(getter_AddRefs(csp));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  nsCOMPtr<nsIChannelPolicy> channelPolicy;
-  if (csp) {
-    channelPolicy = do_CreateInstance(NSCHANNELPOLICY_CONTRACTID, &rv);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    rv = channelPolicy->SetContentSecurityPolicy(csp);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    rv = channelPolicy->SetLoadType(nsIContentPolicy::TYPE_SCRIPT);
-    NS_ENSURE_SUCCESS(rv, rv);
-  }
-
-  uint32_t flags = nsIRequest::LOAD_NORMAL | nsIChannel::LOAD_CLASSIFY_URI;
-
-  nsCOMPtr<nsIChannel> channel;
-  rv = NS_NewChannel(getter_AddRefs(channel), uri, ios, loadGroup, nullptr,
-                     flags, channelPolicy);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  channel.forget(aChannel);
-  return rv;
-}
-
-
 nsresult
 ChannelFromScriptURLMainThread(nsIPrincipal* aPrincipal,
                                nsIURI* aBaseURI,
                                nsIDocument* aParentDoc,
-                               const nsString& aScriptURL,
+                               const nsAString& aScriptURL,
                                nsIChannel** aChannel)
 {
   AssertIsOnMainThread();
 
   nsCOMPtr<nsILoadGroup> loadGroup;
   if (aParentDoc) {
     loadGroup = aParentDoc->GetDocumentLoadGroup();
   }
@@ -831,17 +817,17 @@ ChannelFromScriptURLMainThread(nsIPrinci
 
   return ChannelFromScriptURL(aPrincipal, aBaseURI, aParentDoc, loadGroup,
                               ios, secMan, aScriptURL, true, aChannel);
 }
 
 nsresult
 ChannelFromScriptURLWorkerThread(JSContext* aCx,
                                  WorkerPrivate* aParent,
-                                 const nsString& aScriptURL,
+                                 const nsAString& aScriptURL,
                                  nsIChannel** aChannel)
 {
   aParent->AssertIsOnWorkerThread();
 
   AutoSyncLoopHolder syncLoop(aParent);
 
   nsRefPtr<ChannelGetterRunnable> getter =
     new ChannelGetterRunnable(aParent, syncLoop.SyncQueueKey(),
@@ -854,17 +840,17 @@ ChannelFromScriptURLWorkerThread(JSConte
 
   if (!syncLoop.RunAndForget(aCx)) {
     return NS_ERROR_FAILURE;
   }
 
   return getter->GetResult();
 }
 
-void ReportLoadError(JSContext* aCx, const nsString& aURL,
+void ReportLoadError(JSContext* aCx, const nsAString& aURL,
                      nsresult aLoadResult, bool aIsMainThread)
 {
   NS_LossyConvertUTF16toASCII url(aURL);
 
   switch (aLoadResult) {
     case NS_BINDING_ABORTED:
       // Canceled, don't set an exception.
       break;
--- a/dom/workers/ScriptLoader.h
+++ b/dom/workers/ScriptLoader.h
@@ -17,26 +17,26 @@ class nsIChannel;
 BEGIN_WORKERS_NAMESPACE
 
 namespace scriptloader {
 
 nsresult
 ChannelFromScriptURLMainThread(nsIPrincipal* aPrincipal,
                                nsIURI* aBaseURI,
                                nsIDocument* aParentDoc,
-                               const nsString& aScriptURL,
+                               const nsAString& aScriptURL,
                                nsIChannel** aChannel);
 
 nsresult
 ChannelFromScriptURLWorkerThread(JSContext* aCx,
                                  WorkerPrivate* aParent,
-                                 const nsString& aScriptURL,
+                                 const nsAString& aScriptURL,
                                  nsIChannel** aChannel);
 
-void ReportLoadError(JSContext* aCx, const nsString& aURL,
+void ReportLoadError(JSContext* aCx, const nsAString& aURL,
                      nsresult aLoadResult, bool aIsMainThread);
 
 bool LoadWorkerScript(JSContext* aCx);
 
 bool Load(JSContext* aCx, unsigned aURLCount, jsval* aURLs);
 
 } // namespace scriptloader
 
new file mode 100644
--- /dev/null
+++ b/dom/workers/SharedWorker.cpp
@@ -0,0 +1,235 @@
+/* -*- 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 "SharedWorker.h"
+
+#include "nsPIDOMWindow.h"
+
+#include "mozilla/Preferences.h"
+#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 {
+
+const char kSharedWorkersEnabledPref[] = "dom.workers.sharedWorkers.enabled";
+
+} // anonymous namespace
+
+SharedWorker::SharedWorker(nsPIDOMWindow* aWindow,
+                           WorkerPrivate* aWorkerPrivate)
+: nsDOMEventTargetHelper(aWindow), mWorkerPrivate(aWorkerPrivate),
+  mSuspended(false)
+{
+  AssertIsOnMainThread();
+  MOZ_ASSERT(aWorkerPrivate);
+
+  mSerial = aWorkerPrivate->NextMessagePortSerial();
+
+  mMessagePort = new MessagePort(aWindow, this, mSerial);
+}
+
+SharedWorker::~SharedWorker()
+{
+  AssertIsOnMainThread();
+  MOZ_ASSERT(!mWorkerPrivate);
+}
+
+//static
+bool
+SharedWorker::PrefEnabled()
+{
+  AssertIsOnMainThread();
+
+  return mozilla::Preferences::GetBool(kSharedWorkersEnabledPref, false);
+}
+
+// static
+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,
+                                        getter_AddRefs(sharedWorker));
+  if (NS_FAILED(rv)) {
+    aRv = rv;
+    return nullptr;
+  }
+
+  return sharedWorker.forget();
+}
+
+already_AddRefed<MessagePort>
+SharedWorker::Port()
+{
+  AssertIsOnMainThread();
+
+  nsRefPtr<MessagePort> messagePort = mMessagePort;
+  return messagePort.forget();
+}
+
+void
+SharedWorker::Suspend()
+{
+  AssertIsOnMainThread();
+  MOZ_ASSERT(!IsSuspended());
+
+  mSuspended = true;
+}
+
+void
+SharedWorker::Resume()
+{
+  AssertIsOnMainThread();
+  MOZ_ASSERT(IsSuspended());
+
+  mSuspended = false;
+
+  if (!mSuspendedEvents.IsEmpty()) {
+    nsTArray<nsCOMPtr<nsIDOMEvent>> events;
+    mSuspendedEvents.SwapElements(events);
+
+    for (uint32_t index = 0; index < events.Length(); index++) {
+      nsCOMPtr<nsIDOMEvent>& event = events[index];
+      MOZ_ASSERT(event);
+
+      nsCOMPtr<nsIDOMEventTarget> target;
+      if (NS_SUCCEEDED(event->GetTarget(getter_AddRefs(target)))) {
+        bool ignored;
+        if (NS_FAILED(target->DispatchEvent(event, &ignored))) {
+          NS_WARNING("Failed to dispatch event!");
+        }
+      } else {
+        NS_WARNING("Failed to get target!");
+      }
+    }
+  }
+}
+
+void
+SharedWorker::QueueEvent(nsIDOMEvent* aEvent)
+{
+  AssertIsOnMainThread();
+  MOZ_ASSERT(aEvent);
+  MOZ_ASSERT(IsSuspended());
+
+  mSuspendedEvents.AppendElement(aEvent);
+}
+
+void
+SharedWorker::Close()
+{
+  AssertIsOnMainThread();
+
+  if (mMessagePort) {
+    mMessagePort->Close();
+  }
+
+  if (mWorkerPrivate) {
+    AutoSafeJSContext cx;
+    NoteDeadWorker(cx);
+  }
+}
+
+void
+SharedWorker::PostMessage(JSContext* aCx, JS::HandleValue aMessage,
+                          const Optional<Sequence<JS::Value>>& aTransferable,
+                          ErrorResult& aRv)
+{
+  AssertIsOnMainThread();
+  MOZ_ASSERT(mWorkerPrivate);
+  MOZ_ASSERT(mMessagePort);
+
+  mWorkerPrivate->PostMessageToMessagePort(aCx, mMessagePort->Serial(),
+                                           aMessage, aTransferable, aRv);
+}
+
+void
+SharedWorker::NoteDeadWorker(JSContext* aCx)
+{
+  AssertIsOnMainThread();
+  MOZ_ASSERT(mWorkerPrivate);
+
+  mWorkerPrivate->UnregisterSharedWorker(aCx, this);
+  mWorkerPrivate = nullptr;
+}
+
+NS_IMPL_ADDREF_INHERITED(SharedWorker, nsDOMEventTargetHelper)
+NS_IMPL_RELEASE_INHERITED(SharedWorker, nsDOMEventTargetHelper)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(SharedWorker)
+NS_INTERFACE_MAP_END_INHERITING(nsDOMEventTargetHelper)
+
+NS_IMPL_CYCLE_COLLECTION_CLASS(SharedWorker)
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(SharedWorker,
+                                                  nsDOMEventTargetHelper)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMessagePort)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSuspendedEvents)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(SharedWorker,
+                                                nsDOMEventTargetHelper)
+  tmp->Close();
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mMessagePort)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mSuspendedEvents)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+JSObject*
+SharedWorker::WrapObject(JSContext* aCx, JS::HandleObject aScope)
+{
+  AssertIsOnMainThread();
+
+  return SharedWorkerBinding::Wrap(aCx, aScope, this);
+}
+
+nsresult
+SharedWorker::PreHandleEvent(nsEventChainPreVisitor& aVisitor)
+{
+  AssertIsOnMainThread();
+
+  nsIDOMEvent*& event = aVisitor.mDOMEvent;
+
+  if (IsSuspended() && event) {
+    QueueEvent(event);
+
+    aVisitor.mCanHandle = false;
+    aVisitor.mParentTarget = nullptr;
+    return NS_OK;
+  }
+
+  return nsDOMEventTargetHelper::PreHandleEvent(aVisitor);
+}
new file mode 100644
--- /dev/null
+++ b/dom/workers/SharedWorker.h
@@ -0,0 +1,104 @@
+/* -*- 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_sharedworker_h__
+#define mozilla_dom_workers_sharedworker_h__
+
+#include "Workers.h"
+
+#include "mozilla/dom/BindingDeclarations.h"
+#include "nsDOMEventTargetHelper.h"
+
+class nsIDOMEvent;
+class nsPIDOMWindow;
+
+BEGIN_WORKERS_NAMESPACE
+
+class MessagePort;
+class RuntimeService;
+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<MessagePort> mMessagePort;
+  nsTArray<nsCOMPtr<nsIDOMEvent>> mSuspendedEvents;
+  uint64_t mSerial;
+  bool mSuspended;
+
+public:
+  static bool
+  PrefEnabled();
+
+  static already_AddRefed<SharedWorker>
+  Constructor(const GlobalObject& aGlobal, JSContext* aCx,
+              const nsAString& aScriptURL, const Optional<nsAString>& aName,
+              ErrorResult& aRv);
+
+  already_AddRefed<MessagePort>
+  Port();
+
+  uint64_t
+  Serial() const
+  {
+    return mSerial;
+  }
+
+  bool
+  IsSuspended() const
+  {
+    return mSuspended;
+  }
+
+  void
+  Suspend();
+
+  void
+  Resume();
+
+  void
+  QueueEvent(nsIDOMEvent* aEvent);
+
+  void
+  Close();
+
+  NS_DECL_ISUPPORTS_INHERITED
+  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(SharedWorker, nsDOMEventTargetHelper)
+
+  IMPL_EVENT_HANDLER(error)
+
+  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);
+
+  // 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,
+              ErrorResult& aRv);
+
+  // Only called by RuntimeService.
+  void
+  NoteDeadWorker(JSContext* aCx);
+};
+
+END_WORKERS_NAMESPACE
+
+#endif // mozilla_dom_workers_sharedworker_h__
--- a/dom/workers/Worker.cpp
+++ b/dom/workers/Worker.cpp
@@ -3,16 +3,17 @@
  * 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"
 
@@ -86,79 +87,53 @@ public:
     }
 
     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, unsigned aArgc, jsval* aVp,
-                    bool aIsChromeWorker, const JSClass* aClass)
+  ConstructInternal(JSContext* aCx, JS::CallArgs aArgs, bool aIsChromeWorker)
   {
-    if (!aArgc) {
+    if (!aArgs.length()) {
       JS_ReportError(aCx, "Constructor requires at least one argument!");
       return false;
     }
 
-    JS::Rooted<JSString*> scriptURL(aCx, JS_ValueToString(aCx, JS_ARGV(aCx, aVp)[0]));
-    if (!scriptURL) {
+    nsDependentJSString scriptURL;
+    if (!scriptURL.init(aCx, aArgs[0])) {
       return false;
     }
 
-    jsval priv = js::GetFunctionNativeReserved(JSVAL_TO_OBJECT(JS_CALLEE(aCx, aVp)),
-                                               CONSTRUCTOR_SLOT_PARENT);
-
-    RuntimeService* runtimeService;
-    WorkerPrivate* parent;
+    JS::Rooted<JS::Value> priv(aCx,
+      js::GetFunctionNativeReserved(&aArgs.callee(), CONSTRUCTOR_SLOT_PARENT));
 
-    if (JSVAL_IS_VOID(priv)) {
-      runtimeService = RuntimeService::GetOrCreateService();
-      if (!runtimeService) {
-        JS_ReportError(aCx, "Failed to create runtime service!");
-        return false;
-      }
+    WorkerPrivate* parent;
+    if (priv.isUndefined()) {
       parent = NULL;
-    }
-    else {
-      runtimeService = RuntimeService::GetService();
-      parent = static_cast<WorkerPrivate*>(JSVAL_TO_PRIVATE(priv));
+    } else {
+      parent = static_cast<WorkerPrivate*>(priv.get().toPrivate());
       parent->AssertIsOnWorkerThread();
     }
 
-    JS::Rooted<JSObject*> obj(aCx, JS_NewObject(aCx, aClass, nullptr, nullptr));
+    JS::Rooted<JSObject*> obj(aCx,
+      Create(aCx, parent, scriptURL, aIsChromeWorker, false, EmptyString()));
     if (!obj) {
       return false;
     }
 
-    // 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, parent, scriptURL, aIsChromeWorker);
-    if (!worker) {
-      return false;
-    }
-
-    // Worker now owned by the JS object.
-    NS_ADDREF(worker.get());
-    js::SetReservedSlot(obj, DOM_OBJECT_SLOT, PRIVATE_TO_JSVAL(worker));
-
-    if (!runtimeService->RegisterWorker(aCx, worker)) {
-      return false;
-    }
-
-    // Worker now also owned by its thread.
-    NS_ADDREF(worker.get());
-
-    JS_SET_RVAL(aCx, aVp, OBJECT_TO_JSVAL(obj));
+    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();
@@ -276,19 +251,20 @@ private:
   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, jsval* aVp)
+  Construct(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
   {
-    return ConstructInternal(aCx, aArgc, aVp, false, Class());
+    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) {
@@ -484,19 +460,20 @@ private:
         return UnwrapDOMObject<WorkerPrivate>(aObj);
       }
     }
 
     return Worker::GetInstancePrivate(aCx, aObj, aFunctionName);
   }
 
   static bool
-  Construct(JSContext* aCx, unsigned aArgc, jsval* aVp)
+  Construct(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
   {
-    return ConstructInternal(aCx, aArgc, aVp, true, Class());
+    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) {
@@ -569,16 +546,73 @@ Worker::GetInstancePrivate(JSContext* aC
     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,
--- a/dom/workers/Worker.h
+++ b/dom/workers/Worker.h
@@ -3,16 +3,17 @@
  * 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);
 
@@ -26,9 +27,9 @@ InitClass(JSContext* aCx, JSObject* aGlo
 
 } // namespace chromeworker
 
 bool
 ClassIsWorker(const JSClass* aClass);
 
 END_WORKERS_NAMESPACE
 
-#endif /* mozilla_dom_workers_worker_h__ */
+#endif /* mozilla_dom_workers_worker_h__ */
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/dom/workers/WorkerMessagePort.cpp
@@ -0,0 +1,225 @@
+/* -*- 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 "WorkerMessagePort.h"
+
+#include "DOMBindingInlines.h"
+#include "Events.h"
+#include "WorkerPrivate.h"
+
+using mozilla::dom::Optional;
+using mozilla::dom::Sequence;
+
+USING_WORKERS_NAMESPACE
+using namespace mozilla::dom::workers::events;
+
+namespace {
+
+bool
+DispatchMessageEvent(JSContext* aCx, JS::HandleObject aMessagePort,
+                     JSAutoStructuredCloneBuffer& aBuffer,
+                     nsTArray<nsCOMPtr<nsISupports>>& aClonedObjects)
+{
+  MOZ_ASSERT(aMessagePort);
+
+  JSAutoStructuredCloneBuffer buffer;
+  aBuffer.swap(buffer);
+
+  nsTArray<nsCOMPtr<nsISupports>> clonedObjects;
+  aClonedObjects.SwapElements(clonedObjects);
+
+  JS::Rooted<JSObject*> event(aCx,
+    CreateMessageEvent(aCx, buffer, clonedObjects, false));
+  if (!event) {
+    return false;
+  }
+
+  bool dummy;
+  return DispatchEventToTarget(aCx, aMessagePort, event, &dummy);
+}
+
+
+class QueuedMessageEventRunnable : public WorkerRunnable
+{
+  JSAutoStructuredCloneBuffer mBuffer;
+  nsTArray<nsCOMPtr<nsISupports>> mClonedObjects;
+  nsRefPtr<WorkerMessagePort> mMessagePort;
+  JSObject* mMessagePortObject;
+
+public:
+  QueuedMessageEventRunnable(WorkerPrivate* aWorkerPrivate,
+                             JSAutoStructuredCloneBuffer& aBuffer,
+                             nsTArray<nsCOMPtr<nsISupports>>& aClonedObjects)
+  : WorkerRunnable(aWorkerPrivate, WorkerThread, UnchangedBusyCount,
+                   RunWhenClearing),
+    mMessagePortObject(nullptr)
+  {
+    aWorkerPrivate->AssertIsOnWorkerThread();
+    mBuffer.swap(aBuffer);
+    mClonedObjects.SwapElements(aClonedObjects);
+  }
+
+  bool
+  Hold(JSContext* aCx, WorkerMessagePort* aMessagePort)
+  {
+    mWorkerPrivate->AssertIsOnWorkerThread();
+    MOZ_ASSERT(aMessagePort);
+    MOZ_ASSERT(aMessagePort->GetJSObject());
+    MOZ_ASSERT(!mMessagePortObject);
+
+    if (!JS_AddNamedObjectRoot(aCx, &mMessagePortObject,
+                               "WorkerMessagePort::MessageEventRunnable::"
+                               "mMessagePortObject")) {
+      return false;
+    }
+
+    mMessagePortObject = aMessagePort->GetJSObject();
+    mMessagePort = aMessagePort;
+    return true;
+  }
+
+  bool
+  PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
+  {
+    aWorkerPrivate->AssertIsOnWorkerThread();
+    return true;
+  }
+
+  void
+  PostDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
+               bool aDispatchResult)
+  {
+    aWorkerPrivate->AssertIsOnWorkerThread();
+  }
+
+  bool
+  WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
+  {
+    JS::Rooted<JSObject*> messagePortObject(aCx, mMessagePortObject);
+    mMessagePortObject = nullptr;
+
+    JS_RemoveObjectRoot(aCx, &mMessagePortObject);
+
+    nsRefPtr<WorkerMessagePort> messagePort;
+    mMessagePort.swap(messagePort);
+
+    return DispatchMessageEvent(aCx, messagePortObject, mBuffer,
+                                mClonedObjects);
+  }
+};
+
+} // anonymous namespace
+
+void
+WorkerMessagePort::_trace(JSTracer* aTrc)
+{
+  EventTarget::_trace(aTrc);
+}
+
+void
+WorkerMessagePort::_finalize(JSFreeOp* aFop)
+{
+  EventTarget::_finalize(aFop);
+}
+
+void
+WorkerMessagePort::PostMessage(
+                             JSContext* /* aCx */, JS::HandleValue aMessage,
+                             const Optional<Sequence<JS::Value>>& aTransferable,
+                             ErrorResult& aRv)
+{
+  if (mClosed) {
+    aRv = NS_ERROR_DOM_INVALID_STATE_ERR;
+    return;
+  }
+
+  JSContext* cx = GetJSContext();
+
+  WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(cx);
+  MOZ_ASSERT(workerPrivate);
+
+  workerPrivate->PostMessageToParentMessagePort(cx, Serial(), aMessage,
+                                                aTransferable, aRv);
+}
+
+void
+WorkerMessagePort::Start()
+{
+  WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(GetJSContext());
+  MOZ_ASSERT(workerPrivate);
+
+  if (mClosed) {
+    NS_WARNING("Called start() after calling close()!");
+    return;
+  }
+
+  if (mStarted) {
+    return;
+  }
+
+  mStarted = true;
+
+  if (!mQueuedMessages.IsEmpty()) {
+    for (uint32_t index = 0; index < mQueuedMessages.Length(); index++) {
+      MessageInfo& info = mQueuedMessages[index];
+
+      nsRefPtr<QueuedMessageEventRunnable> runnable =
+        new QueuedMessageEventRunnable(workerPrivate, info.mBuffer,
+                                       info.mClonedObjects);
+
+      JSContext* cx = GetJSContext();
+
+      if (!runnable->Hold(cx, this) ||
+          !runnable->Dispatch(cx)) {
+        NS_WARNING("Failed to dispatch queued event!");
+        break;
+      }
+    }
+    mQueuedMessages.Clear();
+  }
+}
+
+bool
+WorkerMessagePort::MaybeDispatchEvent(
+                                JSContext* aCx,
+                                JSAutoStructuredCloneBuffer& aBuffer,
+                                nsTArray<nsCOMPtr<nsISupports>>& aClonedObjects)
+{
+  if (mClosed) {
+    NS_WARNING("Not going to ever run this event!");
+    aBuffer.clear();
+    aClonedObjects.Clear();
+    return true;
+  }
+
+  if (!mStarted) {
+    // Queue the message for later.
+    MessageInfo* info = mQueuedMessages.AppendElement();
+    info->mBuffer.swap(aBuffer);
+    info->mClonedObjects.SwapElements(aClonedObjects);
+    return true;
+  }
+
+  // Go ahead and dispatch the event.
+  JS::Rooted<JSObject*> target(aCx, GetJSObject());
+  return DispatchMessageEvent(aCx, target, aBuffer, aClonedObjects);
+}
+
+void
+WorkerMessagePort::CloseInternal()
+{
+  WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(GetJSContext());
+  MOZ_ASSERT(workerPrivate);
+  MOZ_ASSERT(!IsClosed());
+  MOZ_ASSERT_IF(mStarted, mQueuedMessages.IsEmpty());
+
+  mClosed = true;
+
+  workerPrivate->DisconnectMessagePort(Serial());
+
+  if (!mStarted) {
+    mQueuedMessages.Clear();
+  }
+}
new file mode 100644
--- /dev/null
+++ b/dom/workers/WorkerMessagePort.h
@@ -0,0 +1,103 @@
+/* -*- 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_workermessageport_h__
+#define mozilla_dom_workers_workermessageport_h__
+
+#include "js/StructuredClone.h"
+#include "mozilla/dom/BindingDeclarations.h"
+#include "mozilla/dom/workers/bindings/EventTarget.h"
+
+BEGIN_WORKERS_NAMESPACE
+
+class WorkerPrivate;
+
+class WorkerMessagePort : public EventTarget
+{
+  friend class WorkerPrivate;
+
+  typedef mozilla::ErrorResult ErrorResult;
+
+  struct MessageInfo
+  {
+    JSAutoStructuredCloneBuffer mBuffer;
+    nsTArray<nsCOMPtr<nsISupports>> mClonedObjects;
+  };
+
+  nsTArray<MessageInfo> mQueuedMessages;
+  uint64_t mSerial;
+  bool mStarted;
+  bool mClosed;
+
+public:
+  virtual void
+  _trace(JSTracer* aTrc) MOZ_OVERRIDE;
+
+  virtual void
+  _finalize(JSFreeOp* aFop) MOZ_OVERRIDE;
+
+  void
+  PostMessage(JSContext* aCx, JS::HandleValue aMessage,
+              const Optional<Sequence<JS::Value>>& aTransferable,
+              ErrorResult& aRv);
+
+  void
+  Start();
+
+  void
+  Close()
+  {
+    if (!IsClosed()) {
+      CloseInternal();
+    }
+  }
+
+  already_AddRefed<EventHandlerNonNull>
+  GetOnmessage(ErrorResult& aRv)
+  {
+    return GetEventListener(NS_LITERAL_STRING("message"), aRv);
+  }
+
+  void
+  SetOnmessage(EventHandlerNonNull* aListener, ErrorResult& aRv)
+  {
+    SetEventListener(NS_LITERAL_STRING("message"), aListener, aRv);
+    if (!aRv.Failed()) {
+      Start();
+    }
+  }
+
+  uint64_t
+  Serial() const
+  {
+    return mSerial;
+  }
+
+  bool
+  MaybeDispatchEvent(JSContext* aCx, JSAutoStructuredCloneBuffer& aBuffer,
+                     nsTArray<nsCOMPtr<nsISupports>>& aClonedObjects);
+
+  bool
+  IsClosed() const
+  {
+    return mClosed;
+  }
+
+private:
+  // Only created by WorkerPrivate.
+  WorkerMessagePort(JSContext* aCx, uint64_t aSerial)
+  : EventTarget(aCx), mSerial(aSerial), mStarted(false), mClosed(false)
+  { }
+
+  virtual ~WorkerMessagePort()
+  { }
+
+  void
+  CloseInternal();
+};
+
+END_WORKERS_NAMESPACE
+
+#endif // mozilla_dom_workers_workermessageport_h__
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -6,68 +6,77 @@
 
 #include "WorkerPrivate.h"
 
 #include "amIAddonManager.h"
 #include "nsIClassInfo.h"
 #include "nsIContentSecurityPolicy.h"
 #include "nsIConsoleService.h"
 #include "nsIDOMDOMException.h"
+#include "nsIDOMEvent.h"
 #include "nsIDOMFile.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"
 #include "nsITextToSubURI.h"
 #include "nsITimer.h"
 #include "nsIURI.h"
 #include "nsIURL.h"
 #include "nsIXPConnect.h"
-#include "nsPrintfCString.h"
-#include "nsHostObjectProtocolHandler.h"
 
 #include <algorithm>
+#include "GeneratedEvents.h"
 #include "jsfriendapi.h"
 #include "js/OldDebugAPI.h"
 #include "js/MemoryMetrics.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/ContentEvents.h"
 #include "mozilla/Likely.h"
 #include "mozilla/dom/BindingUtils.h"
+#include "mozilla/dom/ErrorEvent.h"
 #include "mozilla/dom/ImageData.h"
 #include "mozilla/dom/ImageDataBinding.h"
+#include "mozilla/Util.h"
 #include "nsAlgorithm.h"
 #include "nsContentUtils.h"
 #include "nsCxPusher.h"
 #include "nsError.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"
 #include "nsSandboxFlags.h"
 #include "nsThreadUtils.h"
 #include "xpcpublic.h"
 
 #ifdef ANDROID
 #include <android/log.h>
 #endif
 
+#include "DOMBindingInlines.h"
 #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
 
@@ -126,16 +135,95 @@ SwapToISupportsArray(SmartPtr<T>& aSrc,
   T* raw = nullptr;
   aSrc.swap(raw);
 
   nsISupports* rawSupports =
     static_cast<typename ISupportsBaseInfo<T>::ISupportsBase*>(raw);
   dest->swap(rawSupports);
 }
 
+struct WindowAction
+{
+  nsPIDOMWindow* mWindow;
+  JSContext* mJSContext;
+  bool mDefaultAction;
+
+  WindowAction(nsPIDOMWindow* aWindow, JSContext* aJSContext)
+  : mWindow(aWindow), mJSContext(aJSContext), mDefaultAction(true)
+  {
+    MOZ_ASSERT(aJSContext);
+  }
+
+  WindowAction(nsPIDOMWindow* aWindow)
+  : mWindow(aWindow), mJSContext(nullptr), mDefaultAction(true)
+  { }
+
+  bool
+  operator==(const WindowAction& aOther) const
+  {
+    return mWindow == aOther.mWindow;
+  }
+};
+
+void
+LogErrorToConsole(const nsAString& aMessage,
+                  const nsAString& aFilename,
+                  const nsAString& aLine,
+                  uint32_t aLineNumber,
+                  uint32_t aColumnNumber,
+                  uint32_t aFlags,
+                  uint64_t aInnerWindowId)
+{
+  AssertIsOnMainThread();
+
+  nsCOMPtr<nsIScriptError> scriptError =
+    do_CreateInstance(NS_SCRIPTERROR_CONTRACTID);
+  NS_WARN_IF_FALSE(scriptError, "Failed to create script error!");
+
+  if (scriptError) {
+    if (NS_FAILED(scriptError->InitWithWindowID(aMessage, aFilename, aLine,
+                                                aLineNumber, aColumnNumber,
+                                                aFlags, "Web Worker",
+                                                aInnerWindowId))) {
+      NS_WARNING("Failed to init script error!");
+      scriptError = nullptr;
+    }
+  }
+
+  nsCOMPtr<nsIConsoleService> consoleService =
+    do_GetService(NS_CONSOLESERVICE_CONTRACTID);
+  NS_WARN_IF_FALSE(consoleService, "Failed to get console service!");
+
+  if (consoleService) {
+    if (scriptError) {
+      if (NS_SUCCEEDED(consoleService->LogMessage(scriptError))) {
+        return;
+      }
+      NS_WARNING("LogMessage failed!");
+    } else if (NS_SUCCEEDED(consoleService->LogStringMessage(
+                                                    aMessage.BeginReading()))) {
+      return;
+    }
+    NS_WARNING("LogStringMessage failed!");
+  }
+
+  NS_ConvertUTF16toUTF8 msg(aMessage);
+  NS_ConvertUTF16toUTF8 filename(aFilename);
+
+  static const char kErrorString[] = "JS error in Web Worker: %s [%s:%u]";
+
+#ifdef ANDROID
+  __android_log_print(ANDROID_LOG_INFO, "Gecko", kErrorString, msg.get(),
+                      filename.get(), aLineNumber);
+#endif
+
+  fprintf(stderr, kErrorString, msg.get(), filename.get(), aLineNumber);
+  fflush(stderr);
+}
+
 struct WorkerStructuredCloneCallbacks
 {
   static JSObject*
   Read(JSContext* aCx, JSStructuredCloneReader* aReader, uint32_t aTag,
        uint32_t aData, void* aClosure)
   {
     // See if object is a nsIDOMFile pointer.
     if (aTag == DOMWORKER_SCTAG_FILE) {
@@ -714,17 +802,17 @@ public:
   CompileScriptRunnable(WorkerPrivate* aWorkerPrivate)
   : WorkerRunnable(aWorkerPrivate, WorkerThread, ModifyBusyCount,
                    SkipWhenClearing)
   { }
 
   bool
   WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
   {
-    JS::Rooted<JSObject*> global(aCx, CreateDedicatedWorkerGlobalScope(aCx));
+    JS::Rooted<JSObject*> global(aCx, CreateGlobalScope(aCx));
     if (!global) {
       NS_WARNING("Failed to make global!");
       return false;
     }
 
     JSAutoCompartment ac(aCx, global);
     return scriptloader::LoadWorkerScript(aCx);
   }
@@ -774,57 +862,81 @@ public:
     aWorkerPrivate->CloseHandlerFinished();
   }
 };
 
 class MessageEventRunnable : public WorkerRunnable
 {
   JSAutoStructuredCloneBuffer mBuffer;
   nsTArray<nsCOMPtr<nsISupports> > mClonedObjects;
+  uint64_t mMessagePortSerial;
+  bool mToMessagePort;
 
 public:
   MessageEventRunnable(WorkerPrivate* aWorkerPrivate, Target aTarget,
                        JSAutoStructuredCloneBuffer& aData,
-                       nsTArray<nsCOMPtr<nsISupports> >& aClonedObjects)
+                       nsTArray<nsCOMPtr<nsISupports> >& aClonedObjects,
+                       bool aToMessagePort, uint64_t aMessagePortSerial)
   : WorkerRunnable(aWorkerPrivate, aTarget, aTarget == WorkerThread ?
                                                        ModifyBusyCount :
                                                        UnchangedBusyCount,
-                   SkipWhenClearing)
+                   SkipWhenClearing),
+    mMessagePortSerial(aMessagePortSerial), mToMessagePort(aToMessagePort)
   {
     mBuffer.swap(aData);
     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();
     }
     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);
+      }
+
       mainRuntime = false;
       target = JS::CurrentGlobalOrNull(aCx);
     }
 
     NS_ASSERTION(target, "This should never be null!");
 
     JS::Rooted<JSObject*> event(aCx,
       CreateMessageEvent(aCx, mBuffer, mClonedObjects, mainRuntime));
@@ -953,53 +1065,61 @@ public:
     // private object.
     if (!aWorkerPrivate->IsAcceptingEvents()) {
       return true;
     }
 
     JS::Rooted<JSObject*> target(aCx, aWorkerPrivate->GetJSObject());
 
     uint64_t innerWindowId;
+    bool fireAtScope = true;
 
     WorkerPrivate* parent = aWorkerPrivate->GetParent();
     if (parent) {
       innerWindowId = 0;
     }
     else {
       AssertIsOnMainThread();
 
       if (aWorkerPrivate->IsSuspended()) {
         aWorkerPrivate->QueueRunnable(this);
         return true;
       }
 
+      if (aWorkerPrivate->IsSharedWorker()) {
+        aWorkerPrivate->BroadcastErrorToSharedWorkers(aCx, mMessage, mFilename,
+                                                      mLine, mLineNumber,
+                                                      mColumnNumber, mFlags);
+        return true;
+      }
+
       aWorkerPrivate->AssertInnerWindowIsCorrect();
 
       innerWindowId = aWorkerPrivate->GetInnerWindowId();
     }
 
-    return ReportErrorRunnable::ReportError(aCx, parent, true, target, mMessage,
-                                            mFilename, mLine, mLineNumber,
-                                            mColumnNumber, mFlags,
+    return ReportErrorRunnable::ReportError(aCx, parent, fireAtScope, target,
+                                            mMessage, mFilename, mLine,
+                                            mLineNumber, mColumnNumber, mFlags,
                                             mErrorNumber, innerWindowId);
   }
 
   void PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate, bool aRunResult)
   {
     WorkerRunnable::PostRun(aCx, aWorkerPrivate, aRunResult);
   }
 
   static bool
   ReportError(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
-              bool aFireAtScope, JSObject* target, const nsString& aMessage,
+              bool aFireAtScope, JSObject* 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*> aTarget(aCx, target);
+    JS::Rooted<JSObject*> target(aCx, aTarget);
     if (aWorkerPrivate) {
       aWorkerPrivate->AssertIsOnWorkerThread();
     }
     else {
       AssertIsOnMainThread();
     }
 
     JS::Rooted<JSString*> message(aCx, JS_NewUCStringCopyN(aCx, aMessage.get(),
@@ -1013,52 +1133,52 @@ 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 (aTarget) {
+      if (target) {
         JS::Rooted<JSObject*> event(aCx,
           CreateErrorEvent(aCx, message, filename, aLineNumber, !aWorkerPrivate));
         if (!event) {
           return false;
         }
 
         bool preventDefaultCalled;
-        if (!DispatchEventToTarget(aCx, aTarget, event, &preventDefaultCalled)) {
+        if (!DispatchEventToTarget(aCx, target, event, &preventDefaultCalled)) {
           return false;
         }
 
         if (preventDefaultCalled) {
           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 && (aTarget || aErrorNumber != JSMSG_OVER_RECURSED)) {
-        aTarget = JS::CurrentGlobalOrNull(aCx);
-        NS_ASSERTION(aTarget, "This should never be null!");
+      if (aFireAtScope && (target || aErrorNumber != JSMSG_OVER_RECURSED)) {
+        target = JS::CurrentGlobalOrNull(aCx);
+        NS_ASSERTION(target, "This should never be null!");
 
         bool preventDefaultCalled;
         nsIScriptGlobalObject* sgo;
 
         if (aWorkerPrivate ||
-            !(sgo = nsJSUtils::GetStaticScriptGlobal(aTarget))) {
+            !(sgo = nsJSUtils::GetStaticScriptGlobal(target))) {
           // Fire a normal ErrorEvent if we're running on a worker thread.
           JS::Rooted<JSObject*> event(aCx,
             CreateErrorEvent(aCx, message, filename, aLineNumber, false));
           if (!event) {
             return false;
           }
 
-          if (!DispatchEventToTarget(aCx, aTarget, event,
+          if (!DispatchEventToTarget(aCx, target, event,
                                      &preventDefaultCalled)) {
             return false;
           }
         }
         else {
           // Icky, we have to fire an InternalScriptErrorEvent...
           InternalScriptErrorEvent event(true, NS_LOAD_ERROR);
           event.lineNr = aLineNumber;
@@ -1085,66 +1205,18 @@ public:
       nsRefPtr<ReportErrorRunnable> runnable =
         new ReportErrorRunnable(aWorkerPrivate, aMessage, aFilename, aLine,
                                 aLineNumber, aColumnNumber, aFlags,
                                 aErrorNumber);
       return runnable->Dispatch(aCx);
     }
 
     // Otherwise log an error to the error console.
-    nsCOMPtr<nsIScriptError> scriptError =
-      do_CreateInstance(NS_SCRIPTERROR_CONTRACTID);
-    NS_WARN_IF_FALSE(scriptError, "Failed to create script error!");
-
-    if (scriptError) {
-      if (NS_FAILED(scriptError->InitWithWindowID(aMessage,
-                                                  aFilename,
-                                                  aLine, aLineNumber,
-                                                  aColumnNumber, aFlags,
-                                                  "Web Worker",
-                                                  aInnerWindowId))) {
-        NS_WARNING("Failed to init script error!");
-        scriptError = nullptr;
-      }
-    }
-
-    nsCOMPtr<nsIConsoleService> consoleService =
-      do_GetService(NS_CONSOLESERVICE_CONTRACTID);
-    NS_WARN_IF_FALSE(consoleService, "Failed to get console service!");
-
-    bool logged = false;
-
-    if (consoleService) {
-      if (scriptError) {
-        if (NS_SUCCEEDED(consoleService->LogMessage(scriptError))) {
-          logged = true;
-        }
-        else {
-          NS_WARNING("Failed to log script error!");
-        }
-      }
-      else if (NS_SUCCEEDED(consoleService->LogStringMessage(aMessage.get()))) {
-        logged = true;
-      }
-      else {
-        NS_WARNING("Failed to log script error!");
-      }
-    }
-
-    if (!logged || nsContentUtils::DOMWindowDumpEnabled()) {
-      NS_ConvertUTF16toUTF8 msg(aMessage);
-      NS_ConvertUTF16toUTF8 fname(aFilename);
-#ifdef ANDROID
-      __android_log_print(ANDROID_LOG_INFO, "Gecko", "JS error in worker: %s, %s:%u",
-                          msg.get(), fname.get(), aLineNumber);
-#endif
-      fprintf(stderr, "JS error in worker: %s, %s:%u\n", msg.get(), fname.get(), aLineNumber);
-      fflush(stderr);
-    }
-
+    LogErrorToConsole(aMessage, aFilename, aLine, aLineNumber, aColumnNumber,
+                      aFlags, aInnerWindowId);
     return true;
   }
 };
 
 class TimerRunnable : public WorkerRunnable
 {
 public:
   TimerRunnable(WorkerPrivate* aWorkerPrivate)
@@ -1435,34 +1507,42 @@ public:
     return true;
   }
 };
 
 class SynchronizeAndResumeRunnable : public nsRunnable
 {
 protected:
   WorkerPrivate* mWorkerPrivate;
-  nsCOMPtr<nsIScriptContext> mCx;
+  nsCOMPtr<nsPIDOMWindow> mWindow;
+  nsCOMPtr<nsIScriptContext> mScriptContext;
 
 public:
   SynchronizeAndResumeRunnable(WorkerPrivate* aWorkerPrivate,
-                               nsIScriptContext* aCx)
-  : mWorkerPrivate(aWorkerPrivate), mCx(aCx)
+                               nsPIDOMWindow* aWindow,
+                               nsIScriptContext* aScriptContext)
+  : mWorkerPrivate(aWorkerPrivate), mWindow(aWindow),
+    mScriptContext(aScriptContext)
   {
-    NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+    AssertIsOnMainThread();
+    MOZ_ASSERT(aWorkerPrivate);
+    MOZ_ASSERT(aWindow);
   }
 
   NS_IMETHOD Run()
   {
-    NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-
-    AutoPushJSContext cx(mCx ? mCx->GetNativeContext() :
+    AssertIsOnMainThread();
+
+    AutoPushJSContext cx(mScriptContext ?
+                         mScriptContext->GetNativeContext() :
                          nsContentUtils::GetSafeJSContext());
-    JSAutoRequest ar(cx);
-    mWorkerPrivate->Resume(cx);
+
+    if (!mWorkerPrivate->Resume(cx, mWindow)) {
+      JS_ReportPendingException(cx);
+    }
 
     return NS_OK;
   }
 };
 
 class WorkerJSRuntimeStats : public JS::RuntimeStats
 {
   const nsACString& mRtPath;
@@ -1520,23 +1600,50 @@ public:
 
     // This should never be used when reporting with workers (hence the "?!").
     extras->domPathPrefix.AssignLiteral("explicit/workers/?!/");
 
     aCompartmentStats->extra = extras;
   }
 };
 
+class MessagePortRunnable : public WorkerRunnable
+{
+  uint64_t mMessagePortSerial;
+  bool mConnect;
+
+public:
+  MessagePortRunnable(WorkerPrivate* aWorkerPrivate,
+                      uint64_t aMessagePortSerial,
+                      bool aConnect)
+  : WorkerRunnable(aWorkerPrivate, WorkerThread,
+                   aConnect ? ModifyBusyCount : UnchangedBusyCount,
+                   SkipWhenClearing),
+    mMessagePortSerial(aMessagePortSerial), mConnect(aConnect)
+  { }
+
+  bool
+  WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
+  {
+    if (mConnect) {
+      return aWorkerPrivate->ConnectMessagePort(aCx, mMessagePortSerial);
+    }
+
+    aWorkerPrivate->DisconnectMessagePort(mMessagePortSerial);
+    return true;
+  }
+};
+
 } /* anonymous namespace */
 
 #ifdef DEBUG
 void
 mozilla::dom::workers::AssertIsOnMainThread()
 {
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+  MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
 }
 
 WorkerRunnable::WorkerRunnable(WorkerPrivate* aWorkerPrivate, Target aTarget,
                                BusyBehavior aBusyBehavior,
                                ClearingBehavior aClearingBehavior)
 : mWorkerPrivate(aWorkerPrivate), mTarget(aTarget),
   mBusyBehavior(aBusyBehavior), mClearingBehavior(aClearingBehavior)
 {
@@ -1883,69 +1990,64 @@ private:
     mRtPath.Insert(addonId, explicitLength);
   }
 };
 
 NS_IMPL_ISUPPORTS1(WorkerPrivate::MemoryReporter, nsIMemoryReporter)
 
 template <class Derived>
 WorkerPrivateParent<Derived>::WorkerPrivateParent(
-                                     JSContext* aCx,
-                                     JS::Handle<JSObject*> aObject,
-                                     WorkerPrivate* aParent,
-                                     JSContext* aParentJSContext,
-                                     const nsAString& aScriptURL,
-                                     bool aIsChromeWorker,
-                                     const nsACString& aDomain,
-                                     nsCOMPtr<nsPIDOMWindow>& aWindow,
-                                     nsCOMPtr<nsIScriptContext>& aScriptContext,
-                                     nsCOMPtr<nsIURI>& aBaseURI,
-                                     nsCOMPtr<nsIPrincipal>& aPrincipal,
-                                     nsCOMPtr<nsIChannel>& aChannel,
-                                     nsCOMPtr<nsIContentSecurityPolicy>& aCSP,
-                                     bool aEvalAllowed,
-                                     bool aReportCSPViolations)
-: EventTarget(aParent ? aCx : NULL), mMutex("WorkerPrivateParent Mutex"),
+                                             JSContext* aCx,
+                                             JS::HandleObject aObject,
+                                             WorkerPrivate* aParent,
+                                             const nsAString& aScriptURL,
+                                             bool aIsChromeWorker,
+                                             bool aIsSharedWorker,
+                                             const nsAString& aSharedWorkerName,
+                                             LoadInfo& aLoadInfo)
+: EventTarget(aParent ? aCx : nullptr), mMutex("WorkerPrivateParent Mutex"),
   mCondVar(mMutex, "WorkerPrivateParent CondVar"),
   mMemoryReportCondVar(mMutex, "WorkerPrivateParent Memory Report CondVar"),
-  mJSObject(aObject), mParent(aParent), mParentJSContext(aParentJSContext),
-  mScriptURL(aScriptURL), mDomain(aDomain), mBusyCount(0),
+  mJSObject(aObject), mParent(aParent), mScriptURL(aScriptURL),
+  mSharedWorkerName(aSharedWorkerName), mBusyCount(0), mMessagePortSerial(0),
   mParentStatus(Pending), mJSObjectRooted(false), mParentSuspended(false),
-  mIsChromeWorker(aIsChromeWorker), mPrincipalIsSystem(false),
-  mMainThreadObjectsForgotten(false), mEvalAllowed(aEvalAllowed),
-  mReportCSPViolations(aReportCSPViolations)
+  mIsChromeWorker(aIsChromeWorker), mMainThreadObjectsForgotten(false),
+  mIsSharedWorker(aIsSharedWorker)
 {
   MOZ_COUNT_CTOR(mozilla::dom::workers::WorkerPrivateParent);
-
-  if (aWindow) {
-    NS_ASSERTION(aWindow->IsInnerWindow(), "Should have inner window here!");
-  }
-
-  mWindow.swap(aWindow);
-  mScriptContext.swap(aScriptContext);
-  mBaseURI.swap(aBaseURI);
-  mPrincipal.swap(aPrincipal);
-  mChannel.swap(aChannel);
-  mCSP.swap(aCSP);
+  MOZ_ASSERT_IF(aIsSharedWorker, !aObject && !aSharedWorkerName.IsVoid());
+  MOZ_ASSERT_IF(!aIsSharedWorker, aObject && aSharedWorkerName.IsEmpty());
+
+  if (aLoadInfo.mWindow) {
+    NS_ASSERTION(aLoadInfo.mWindow->IsInnerWindow(),
+                 "Should have inner window here!");
+  }
+
+  mLoadInfo.StealFrom(aLoadInfo);
 
   if (aParent) {
     aParent->AssertIsOnWorkerThread();
 
     aParent->CopyJSSettings(mJSSettings);
 
     NS_ASSERTION(JS_GetOptions(aCx) == (aParent->IsChromeWorker() ?
                                         mJSSettings.chrome.options :
                                         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);
 }
 
@@ -2011,88 +2113,223 @@ WorkerPrivateParent<Derived>::NotifyPriv
 
   nsRefPtr<NotifyRunnable> runnable =
     new NotifyRunnable(ParentAsWorkerPrivate(), aStatus);
   return runnable->Dispatch(aCx);
 }
 
 template <class Derived>
 bool
-WorkerPrivateParent<Derived>::Suspend(JSContext* aCx)
+WorkerPrivateParent<Derived>::Suspend(JSContext* aCx, nsPIDOMWindow* aWindow)
 {
   AssertIsOnParentThread();
-  NS_ASSERTION(!mParentSuspended, "Suspended more than once!");
+  MOZ_ASSERT(aCx);
+
+  // Shared workers are only suspended if all of their owning documents are
+  // suspended.
+  if (IsSharedWorker()) {
+    AssertIsOnMainThread();
+    MOZ_ASSERT(mSharedWorkers.Count());
+
+    struct Closure
+    {
+      nsPIDOMWindow* mWindow;
+      bool mAllSuspended;
+
+      Closure(nsPIDOMWindow* aWindow)
+      : mWindow(aWindow), mAllSuspended(true)
+      {
+        AssertIsOnMainThread();
+        // aWindow may be null here.
+      }
+
+      static PLDHashOperator
+      Suspend(const uint64_t& aKey,
+              SharedWorker* aSharedWorker,
+              void* aClosure)
+      {
+        AssertIsOnMainThread();
+        MOZ_ASSERT(aSharedWorker);
+        MOZ_ASSERT(aClosure);
+
+        auto closure = static_cast<Closure*>(aClosure);
+
+        if (closure->mWindow && aSharedWorker->GetOwner() == closure->mWindow) {
+          // Calling Suspend() may change the refcount, ensure that the worker
+          // outlives this call.
+          nsRefPtr<SharedWorker> kungFuDeathGrip = aSharedWorker;
+
+          aSharedWorker->Suspend();
+        } else {
+          MOZ_ASSERT_IF(aSharedWorker->GetOwner() && closure->mWindow,
+                        !SameCOMIdentity(aSharedWorker->GetOwner(),
+                                         closure->mWindow));
+          if (!aSharedWorker->IsSuspended()) {
+            closure->mAllSuspended = false;
+          }
+        }
+        return PL_DHASH_NEXT;
+      }
+    };
+
+    Closure closure(aWindow);
+
+    mSharedWorkers.EnumerateRead(Closure::Suspend, &closure);
+
+    if (!closure.mAllSuspended || mParentSuspended) {
+      return true;
+    }
+  }
+
+  MOZ_ASSERT(!mParentSuspended, "Suspended more than once!");
 
   mParentSuspended = true;
 
   {
     MutexAutoLock lock(mMutex);
 
     if (mParentStatus >= Terminating) {
       return true;
     }
   }
 
   nsRefPtr<SuspendRunnable> runnable =
     new SuspendRunnable(ParentAsWorkerPrivate());
-  return runnable->Dispatch(aCx);
+  if (!runnable->Dispatch(aCx)) {
+    return false;
+  }
+
+  return true;
 }
 
 template <class Derived>
-void
-WorkerPrivateParent<Derived>::Resume(JSContext* aCx)
+bool
+WorkerPrivateParent<Derived>::Resume(JSContext* aCx, nsPIDOMWindow* aWindow)
 {
   AssertIsOnParentThread();
-  NS_ASSERTION(mParentSuspended, "Not yet suspended!");
+  MOZ_ASSERT(aCx);
+  MOZ_ASSERT_IF(!IsSharedWorker(), mParentSuspended);
+
+  // Shared workers are resumed if any of their owning documents are resumed.
+  if (IsSharedWorker()) {
+    AssertIsOnMainThread();
+    MOZ_ASSERT(mSharedWorkers.Count());
+
+    struct Closure
+    {
+      nsPIDOMWindow* mWindow;
+      bool mAnyRunning;
+
+      Closure(nsPIDOMWindow* aWindow)
+      : mWindow(aWindow), mAnyRunning(false)
+      {
+        AssertIsOnMainThread();
+        // aWindow may be null here.
+      }
+
+      static PLDHashOperator
+      Resume(const uint64_t& aKey,
+              SharedWorker* aSharedWorker,
+              void* aClosure)
+      {
+        AssertIsOnMainThread();
+        MOZ_ASSERT(aSharedWorker);
+        MOZ_ASSERT(aClosure);
+
+        auto closure = static_cast<Closure*>(aClosure);
+
+        if (closure->mWindow && aSharedWorker->GetOwner() == closure->mWindow) {
+          // Calling Resume() may change the refcount, ensure that the worker
+          // outlives this call.
+          nsRefPtr<SharedWorker> kungFuDeathGrip = aSharedWorker;
+
+          aSharedWorker->Resume();
+          closure->mAnyRunning = true;
+        } else {
+          MOZ_ASSERT_IF(aSharedWorker->GetOwner() && closure->mWindow,
+                        !SameCOMIdentity(aSharedWorker->GetOwner(),
+                                         closure->mWindow));
+          if (!aSharedWorker->IsSuspended()) {
+            closure->mAnyRunning = true;
+          }
+        }
+        return PL_DHASH_NEXT;
+      }
+    };
+
+    Closure closure(aWindow);
+
+    mSharedWorkers.EnumerateRead(Closure::Resume, &closure);
+
+    if (!closure.mAnyRunning || !mParentSuspended) {
+      return true;
+    }
+  }
+
+  MOZ_ASSERT(mParentSuspended);
 
   mParentSuspended = false;
 
   {
     MutexAutoLock lock(mMutex);
 
     if (mParentStatus >= Terminating) {
-      return;
+      return true;
     }
   }
 
   // Execute queued runnables before waking up the worker, otherwise the worker
   // could post new messages before we run those that have been queued.
   if (!mQueuedRunnables.IsEmpty()) {
     AssertIsOnMainThread();
+    MOZ_ASSERT(!IsSharedWorker());
 
     nsTArray<nsRefPtr<WorkerRunnable> > runnables;
     mQueuedRunnables.SwapElements(runnables);
 
     for (uint32_t index = 0; index < runnables.Length(); index++) {
       nsRefPtr<WorkerRunnable>& runnable = runnables[index];
       runnable->Run();
     }
   }
 
   nsRefPtr<ResumeRunnable> runnable =
     new ResumeRunnable(ParentAsWorkerPrivate());
-  runnable->Dispatch(aCx);
+  if (!runnable->Dispatch(aCx)) {
+    return false;
+  }
+
+  return true;
 }
 
 template <class Derived>
 bool
-WorkerPrivateParent<Derived>::SynchronizeAndResume(nsIScriptContext* aCx)
+WorkerPrivateParent<Derived>::SynchronizeAndResume(
+                                               JSContext* aCx,
+                                               nsPIDOMWindow* aWindow,
+                                               nsIScriptContext* aScriptContext)
 {
-  AssertIsOnParentThread();
-  NS_ASSERTION(mParentSuspended, "Not yet suspended!");
+  AssertIsOnMainThread();
+  MOZ_ASSERT_IF(!IsSharedWorker(), mParentSuspended);
 
   // NB: There may be pending unqueued messages.  If we resume here we will
   // execute those messages out of order.  Instead we post an event to the
   // end of the event queue, allowing all of the outstanding messages to be
   // queued up in order on the worker.  Then and only then we execute all of
   // the messages.
 
   nsRefPtr<SynchronizeAndResumeRunnable> runnable =
-    new SynchronizeAndResumeRunnable(ParentAsWorkerPrivate(), aCx);
-  return NS_SUCCEEDED(NS_DispatchToCurrentThread(runnable));
+    new SynchronizeAndResumeRunnable(ParentAsWorkerPrivate(), aWindow,
+                                     aScriptContext);
+  if (NS_FAILED(NS_DispatchToCurrentThread(runnable))) {
+    JS_ReportError(aCx, "Failed to dispatch to current thread!");
+    return false;
+  }
+
+  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
@@ -2220,34 +2457,42 @@ WorkerPrivateParent<Derived>::RootJSObje
 template <class Derived>
 void
 WorkerPrivateParent<Derived>::ForgetMainThreadObjects(
                                       nsTArray<nsCOMPtr<nsISupports> >& aDoomed)
 {
   AssertIsOnParentThread();
   MOZ_ASSERT(!mMainThreadObjectsForgotten);
 
-  aDoomed.SetCapacity(7);
-
-  SwapToISupportsArray(mWindow, aDoomed);
-  SwapToISupportsArray(mScriptContext, aDoomed);
-  SwapToISupportsArray(mBaseURI, aDoomed);
-  SwapToISupportsArray(mScriptURI, aDoomed);
-  SwapToISupportsArray(mPrincipal, aDoomed);
-  SwapToISupportsArray(mChannel, aDoomed);
-  SwapToISupportsArray(mCSP, aDoomed);
+  static const uint32_t kDoomedCount = 7;
+
+  aDoomed.SetCapacity(kDoomedCount);
+
+  SwapToISupportsArray(mLoadInfo.mWindow, aDoomed);
+  SwapToISupportsArray(mLoadInfo.mScriptContext, aDoomed);
+  SwapToISupportsArray(mLoadInfo.mBaseURI, aDoomed);
+  SwapToISupportsArray(mLoadInfo.mResolvedScriptURI, aDoomed);
+  SwapToISupportsArray(mLoadInfo.mPrincipal, aDoomed);
+  SwapToISupportsArray(mLoadInfo.mChannel, aDoomed);
+  SwapToISupportsArray(mLoadInfo.mCSP, aDoomed);
+  // Before adding anything here update kDoomedCount above!
+
+  MOZ_ASSERT(aDoomed.Length() == kDoomedCount);
 
   mMainThreadObjectsForgotten = true;
 }
 
 template <class Derived>
 bool
-WorkerPrivateParent<Derived>::PostMessage(JSContext* aCx,
-                                          JS::Handle<JS::Value> aMessage,
-                                          JS::Handle<JS::Value> aTransferable)
+WorkerPrivateParent<Derived>::PostMessageInternal(
+                                            JSContext* aCx,
+                                            JS::Handle<JS::Value> aMessage,
+                                            JS::Handle<JS::Value> aTransferable,
+                                            bool aToMessagePort,
+                                            uint64_t aMessagePortSerial)
 {
   AssertIsOnParentThread();
 
   {
     MutexAutoLock lock(mMutex);
     if (mParentStatus > Running) {
       return true;
     }
@@ -2278,27 +2523,131 @@ WorkerPrivateParent<Derived>::PostMessag
   JSAutoStructuredCloneBuffer buffer;
   if (!buffer.write(aCx, aMessage, aTransferable, callbacks, &clonedObjects)) {
     return false;
   }
 
   nsRefPtr<MessageEventRunnable> runnable =
     new MessageEventRunnable(ParentAsWorkerPrivate(),
                              WorkerRunnable::WorkerThread, buffer,
-                             clonedObjects);
+                             clonedObjects, aToMessagePort, aMessagePortSerial);
   return runnable->Dispatch(aCx);
 }
 
 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;
+  }
+}
+
+template <class Derived>
+bool
+WorkerPrivateParent<Derived>::DispatchMessageEventToMessagePort(
+                               JSContext* aCx, uint64_t aMessagePortSerial,
+                               JSAutoStructuredCloneBuffer& aBuffer,
+                               nsTArray<nsCOMPtr<nsISupports> >& aClonedObjects)
+{
+  AssertIsOnMainThread();
+
+  JSAutoStructuredCloneBuffer buffer;
+  buffer.swap(aBuffer);
+
+  nsTArray<nsCOMPtr<nsISupports> > clonedObjects;
+  clonedObjects.SwapElements(aClonedObjects);
+
+  SharedWorker* sharedWorker;
+  if (!mSharedWorkers.Get(aMessagePortSerial, &sharedWorker)) {
+    // SharedWorker has already been unregistered?
+    return true;
+  }
+
+  nsRefPtr<MessagePort> port = sharedWorker->Port();
+  NS_ASSERTION(port, "SharedWorkers always have a port!");
+
+  if (port->IsClosed()) {
+    return true;
+  }
+
+  nsCOMPtr<nsIScriptGlobalObject> sgo;
+  port->GetParentObject(getter_AddRefs(sgo));
+  MOZ_ASSERT(sgo, "Should never happen if IsClosed() returned false!");
+  MOZ_ASSERT(sgo->GetGlobalJSObject());
+
+  nsCOMPtr<nsIScriptContext> scx = sgo->GetContext();
+  MOZ_ASSERT_IF(scx, scx->GetNativeContext());
+
+  AutoPushJSContext cx(scx ? scx->GetNativeContext() : aCx);
+  JSAutoCompartment(cx, sgo->GetGlobalJSObject());
+
+  JS::Rooted<JS::Value> data(cx);
+  if (!buffer.read(cx, data.address(), WorkerStructuredCloneCallbacks(true))) {
+    return false;
+  }
+
+  buffer.clear();
+
+  nsRefPtr<nsDOMMessageEvent> event =
+    new nsDOMMessageEvent(port, nullptr, nullptr);
+
+  nsresult rv =
+    event->InitMessageEvent(NS_LITERAL_STRING("message"), false, false, data,
+                            EmptyString(), EmptyString(), nullptr);
+  if (NS_FAILED(rv)) {
+    xpc::Throw(cx, rv);
+    return false;
+  }
+
+  event->SetTrusted(true);
+
+  nsCOMPtr<nsIDOMEvent> domEvent;
+  CallQueryInterface(event.get(), getter_AddRefs(domEvent));
+  NS_ASSERTION(domEvent, "This should never fail!");
+
+  bool ignored;
+  rv = port->DispatchEvent(domEvent, &ignored);
+  if (NS_FAILED(rv)) {
+    xpc::Throw(cx, rv);
+    return false;
+  }
+
+  return true;
+}
+
+template <class Derived>
 uint64_t
 WorkerPrivateParent<Derived>::GetInnerWindowId()
 {
   AssertIsOnMainThread();
-  NS_ASSERTION(!mWindow || mWindow->IsInnerWindow(), "Outer window?");
-  return mWindow ? mWindow->WindowID() : 0;
+  NS_ASSERTION(!mLoadInfo.mWindow || mLoadInfo.mWindow->IsInnerWindow(),
+               "Outer window?");
+  return mLoadInfo.mWindow ? mLoadInfo.mWindow->WindowID() : 0;
 }
 
 template <class Derived>
 void
 WorkerPrivateParent<Derived>::UpdateJSContextOptions(JSContext* aCx,
                                                      uint32_t aContentOptions,
                                                      uint32_t aChromeOptions)
 {
@@ -2387,31 +2736,345 @@ WorkerPrivateParent<Derived>::UpdateJITH
     JS_ClearPendingException(aCx);
   }
 }
 
 template <class Derived>
 void
 WorkerPrivateParent<Derived>::GarbageCollect(JSContext* aCx, bool aShrinking)
 {
+  AssertIsOnParentThread();
+
   nsRefPtr<GarbageCollectRunnable> runnable =
     new GarbageCollectRunnable(ParentAsWorkerPrivate(), aShrinking, true);
   if (!runnable->Dispatch(aCx)) {
     NS_WARNING("Failed to update worker heap size!");
     JS_ClearPendingException(aCx);
   }
 }
 
 template <class Derived>
+bool
+WorkerPrivateParent<Derived>::RegisterSharedWorker(JSContext* aCx,
+                                                   SharedWorker* aSharedWorker)
+{
+  AssertIsOnMainThread();
+  MOZ_ASSERT(aSharedWorker);
+  MOZ_ASSERT(IsSharedWorker());
+  MOZ_ASSERT(!mSharedWorkers.Get(aSharedWorker->Serial()));
+
+  nsRefPtr<MessagePortRunnable> runnable =
+    new MessagePortRunnable(ParentAsWorkerPrivate(), aSharedWorker->Serial(),
+                            true);
+  if (!runnable->Dispatch(aCx)) {
+    return false;
+  }
+
+  mSharedWorkers.Put(aSharedWorker->Serial(), aSharedWorker);
+
+  // If there were other SharedWorker objects attached to this worker then they
+  // may all have been suspended and this worker would need to be resumed.
+  if (mSharedWorkers.Count() > 1 && !Resume(aCx, nullptr)) {
+    return false;
+  }
+
+  return true;
+}
+
+template <class Derived>
+void
+WorkerPrivateParent<Derived>::UnregisterSharedWorker(
+                                                    JSContext* aCx,
+                                                    SharedWorker* aSharedWorker)
+{
+  AssertIsOnMainThread();
+  MOZ_ASSERT(aSharedWorker);
+  MOZ_ASSERT(IsSharedWorker());
+  MOZ_ASSERT(mSharedWorkers.Get(aSharedWorker->Serial()));
+
+  nsRefPtr<MessagePortRunnable> runnable =
+    new MessagePortRunnable(ParentAsWorkerPrivate(), aSharedWorker->Serial(),
+                            false);
+  if (!runnable->Dispatch(aCx)) {
+    JS_ReportPendingException(aCx);
+  }
+
+  mSharedWorkers.Remove(aSharedWorker->Serial());
+
+  if (mSharedWorkers.Count()) {
+    // If there are still SharedWorker objects attached to this worker then they
+    // may all be suspended and this worker would need to be suspended.
+    if (!Suspend(aCx, nullptr)) {
+      JS_ReportPendingException(aCx);
+    }
+  } else {
+    // If that was the last SharedWorker then it's time to cancel this worker.
+    if (!Cancel(aCx)) {
+      JS_ReportPendingException(aCx);
+    }
+  }
+}
+
+template <class Derived>
+void
+WorkerPrivateParent<Derived>::BroadcastErrorToSharedWorkers(
+                                                    JSContext* aCx,
+                                                    const nsAString& aMessage,
+                                                    const nsAString& aFilename,
+                                                    const nsAString& aLine,
+                                                    uint32_t aLineNumber,
+                                                    uint32_t aColumnNumber,
+                                                    uint32_t aFlags)
+{
+  AssertIsOnMainThread();
+
+  nsAutoTArray<nsRefPtr<SharedWorker>, 10> sharedWorkers;
+  GetAllSharedWorkers(sharedWorkers);
+
+  if (sharedWorkers.IsEmpty()) {
+    return;
+  }
+
+  nsAutoTArray<WindowAction, 10> windowActions;
+  nsresult rv;
+
+  // First fire the error event at all SharedWorker objects. This may include
+  // multiple objects in a single window as well as objects in different
+  // windows.
+  for (uint32_t index = 0; index < sharedWorkers.Length(); index++) {
+    nsRefPtr<SharedWorker>& sharedWorker = sharedWorkers[index];
+
+    // May be null.
+    nsPIDOMWindow* window = sharedWorker->GetOwner();
+
+    uint32_t actionsIndex = windowActions.LastIndexOf(WindowAction(window));
+
+    // Get the context for this window so that we can report errors correctly.
+    JSContext* cx;
+    rv = NS_OK;
+
+    if (actionsIndex == windowActions.NoIndex) {
+      nsIScriptContext* scx = sharedWorker->GetContextForEventHandlers(&rv);
+      cx = (NS_SUCCEEDED(rv) && scx) ?
+           scx->GetNativeContext() :
+           nsContentUtils::GetSafeJSContext();
+    } else {
+      cx = windowActions[actionsIndex].mJSContext;
+    }
+
+    MOZ_ASSERT(cx);
+
+    AutoPushJSContext autoPush(cx);
+
+    if (NS_FAILED(rv)) {
+      Throw(cx, rv);
+      JS_ReportPendingException(cx);
+      continue;
+    }
+
+    ErrorEventInit errorInit;
+    errorInit.mBubbles = false;
+    errorInit.mCancelable = true;
+    errorInit.mMessage = aMessage;
+    errorInit.mFilename = aFilename;
+    errorInit.mLineno = aLineNumber;
+    errorInit.mColumn = aColumnNumber;
+
+    nsRefPtr<ErrorEvent> errorEvent =
+      ErrorEvent::Constructor(sharedWorker, NS_LITERAL_STRING("error"),
+                              errorInit);
+    if (!errorEvent) {
+      Throw(cx, NS_ERROR_UNEXPECTED);
+      JS_ReportPendingException(cx);
+      continue;
+    }
+
+    errorEvent->SetTrusted(true);
+
+    bool defaultActionEnabled;
+    rv = sharedWorker->DispatchEvent(errorEvent, &defaultActionEnabled);
+    if (NS_FAILED(rv)) {
+      Throw(cx, rv);
+      JS_ReportPendingException(cx);
+      continue;
+    }
+
+    if (defaultActionEnabled) {
+      // Add the owning window to our list so that we will fire an error event
+      // at it later.
+      if (!windowActions.Contains(window)) {
+        windowActions.AppendElement(WindowAction(window, cx));
+      }
+    } else if (actionsIndex != windowActions.NoIndex) {
+      // Any listener that calls preventDefault() will prevent the window from
+      // receiving the error event.
+      windowActions[actionsIndex].mDefaultAction = false;
+    }
+  }
+
+  // If there are no windows to consider further then we're done.
+  if (windowActions.IsEmpty()) {
+    return;
+  }
+
+  bool shouldLogErrorToConsole = true;
+
+  // Now fire error events at all the windows remaining.
+  for (uint32_t index = 0; index < windowActions.Length(); index++) {
+    WindowAction& windowAction = windowActions[index];
+
+    // If there is no window or the script already called preventDefault then
+    // skip this window.
+    if (!windowAction.mWindow || !windowAction.mDefaultAction) {
+      continue;
+    }
+
+    AutoPushJSContext cx(windowAction.mJSContext);
+    MOZ_ASSERT(cx);
+
+    nsCOMPtr<nsIScriptGlobalObject> sgo =
+      do_QueryInterface(windowAction.mWindow);
+    MOZ_ASSERT(sgo);
+
+    InternalScriptErrorEvent event(true, NS_LOAD_ERROR);
+    event.lineNr = aLineNumber;
+    event.errorMsg = aMessage.BeginReading();
+    event.fileName = aFilename.BeginReading();
+
+    nsEventStatus status = nsEventStatus_eIgnore;
+    rv = sgo->HandleScriptError(&event, &status);
+    if (NS_FAILED(rv)) {
+      Throw(cx, rv);
+      JS_ReportPendingException(cx);
+      continue;
+    }
+
+    if (status == nsEventStatus_eConsumeNoDefault) {
+      shouldLogErrorToConsole = false;
+    }
+  }
+
+  // Finally log a warning in the console if no window tried to prevent it.
+  if (shouldLogErrorToConsole) {
+    LogErrorToConsole(aMessage, aFilename, aLine, aLineNumber, aColumnNumber,
+                      aFlags, 0);
+  }
+}
+
+template <class Derived>
+void
+WorkerPrivateParent<Derived>::GetAllSharedWorkers(
+                              nsTArray<nsRefPtr<SharedWorker> >& aSharedWorkers)
+{
+  AssertIsOnMainThread();
+  MOZ_ASSERT(IsSharedWorker());
+
+  struct Helper
+  {
+    static PLDHashOperator
+    Collect(const uint64_t& aKey,
+            SharedWorker* aSharedWorker,
+            void* aClosure)
+    {
+      AssertIsOnMainThread();
+      MOZ_ASSERT(aSharedWorker);
+      MOZ_ASSERT(aClosure);
+
+      auto array = static_cast<nsTArray<nsRefPtr<SharedWorker> >*>(aClosure);
+      array->AppendElement(aSharedWorker);
+
+      return PL_DHASH_NEXT;
+    }
+  };
+
+  if (!aSharedWorkers.IsEmpty()) {
+    aSharedWorkers.Clear();
+  }
+
+  mSharedWorkers.EnumerateRead(Helper::Collect, &aSharedWorkers);
+}
+
+template <class Derived>
+void
+WorkerPrivateParent<Derived>::CloseSharedWorkersForWindow(
+                                                         nsPIDOMWindow* aWindow)
+{
+  AssertIsOnMainThread();
+  MOZ_ASSERT(IsSharedWorker());
+  MOZ_ASSERT(aWindow);
+
+  struct Closure
+  {
+    nsPIDOMWindow* mWindow;
+    nsAutoTArray<nsRefPtr<SharedWorker>, 10> mSharedWorkers;
+
+    Closure(nsPIDOMWindow* aWindow)
+    : mWindow(aWindow)
+    {
+      AssertIsOnMainThread();
+      MOZ_ASSERT(aWindow);
+    }
+
+    static PLDHashOperator
+    Collect(const uint64_t& aKey,
+            SharedWorker* aSharedWorker,
+            void* aClosure)
+    {
+      AssertIsOnMainThread();
+      MOZ_ASSERT(aSharedWorker);
+      MOZ_ASSERT(aClosure);
+
+      auto closure = static_cast<Closure*>(aClosure);
+      MOZ_ASSERT(closure->mWindow);
+
+      if (aSharedWorker->GetOwner() == closure->mWindow) {
+        closure->mSharedWorkers.AppendElement(aSharedWorker);
+      } else {
+        MOZ_ASSERT(!SameCOMIdentity(aSharedWorker->GetOwner(),
+                                    closure->mWindow));
+      }
+
+      return PL_DHASH_NEXT;
+    }
+  };
+
+  Closure closure(aWindow);
+
+  mSharedWorkers.EnumerateRead(Closure::Collect, &closure);
+
+  for (uint32_t index = 0; index < closure.mSharedWorkers.Length(); index++) {
+    closure.mSharedWorkers[index]->Close();
+  }
+}
+
+template <class Derived>
+void
+WorkerPrivateParent<Derived>::WorkerScriptLoaded()
+{
+  AssertIsOnMainThread();
+
+  if (IsSharedWorker()) {
+    // No longer need to hold references to the window or document we came from.
+    mLoadInfo.mWindow = nullptr;
+    mLoadInfo.mScriptContext = nullptr;
+  }
+}
+
+template <class Derived>
 void
 WorkerPrivateParent<Derived>::SetBaseURI(nsIURI* aBaseURI)
 {
   AssertIsOnMainThread();
 
-  mBaseURI = aBaseURI;
+  if (!mLoadInfo.mBaseURI) {
+    NS_ASSERTION(GetParent(), "Shouldn't happen without a parent!");
+    mLoadInfo.mResolvedScriptURI = aBaseURI;
+  }
+
+  mLoadInfo.mBaseURI = aBaseURI;
 
   if (NS_FAILED(aBaseURI->GetSpec(mLocationInfo.mHref))) {
     mLocationInfo.mHref.Truncate();
   }
 
   if (NS_FAILED(aBaseURI->GetHost(mLocationInfo.mHostname))) {
     mLocationInfo.mHostname.Truncate();
   }
@@ -2471,324 +3134,313 @@ WorkerPrivateParent<Derived>::SetBaseURI
 }
 
 template <class Derived>
 void
 WorkerPrivateParent<Derived>::SetPrincipal(nsIPrincipal* aPrincipal)
 {
   AssertIsOnMainThread();
 
-  mPrincipal = aPrincipal;
-  mPrincipalIsSystem = nsContentUtils::IsSystemPrincipal(aPrincipal);
+  mLoadInfo.mPrincipal = aPrincipal;
+  mLoadInfo.mPrincipalIsSystem = nsContentUtils::IsSystemPrincipal(aPrincipal);
 }
 
 template <class Derived>
 JSContext*
 WorkerPrivateParent<Derived>::ParentJSContext() const
 {
   AssertIsOnParentThread();
 
-  if (!mParent) {
-    AssertIsOnMainThread();
-
-    if (!mScriptContext) {
-      NS_ASSERTION(!mParentJSContext, "Shouldn't have a parent context!");
-      return nsContentUtils::GetSafeJSContext();
-    }
-
-    NS_ASSERTION(mParentJSContext == mScriptContext->GetNativeContext(),
-                 "Native context has changed!");
-  }
-
-  return mParentJSContext;
+  if (mParent) {
+    return mParent->GetJSContext();
+  }
+
+  AssertIsOnMainThread();
+
+  return mLoadInfo.mScriptContext ?
+         mLoadInfo.mScriptContext->GetNativeContext() :
+         nsContentUtils::GetSafeJSContext();
 }
 
-WorkerPrivate::WorkerPrivate(JSContext* aCx, JS::Handle<JSObject*> aObject,
+WorkerPrivate::WorkerPrivate(JSContext* aCx, JS::HandleObject aObject,
                              WorkerPrivate* aParent,
-                             JSContext* aParentJSContext,
-                             const nsAString& aScriptURL, bool aIsChromeWorker,
-                             const nsACString& aDomain,
-                             nsCOMPtr<nsPIDOMWindow>& aWindow,
-                             nsCOMPtr<nsIScriptContext>& aParentScriptContext,
-                             nsCOMPtr<nsIURI>& aBaseURI,
-                             nsCOMPtr<nsIPrincipal>& aPrincipal,
-                             nsCOMPtr<nsIChannel>& aChannel,
-                             nsCOMPtr<nsIContentSecurityPolicy>& aCSP,
-                             bool aEvalAllowed,
-                             bool aReportCSPViolations,
-                             bool aXHRParamsAllowed)
-: WorkerPrivateParent<WorkerPrivate>(aCx, aObject, aParent, aParentJSContext,
-                                     aScriptURL, aIsChromeWorker, aDomain,
-                                     aWindow, aParentScriptContext, aBaseURI,
-                                     aPrincipal, aChannel, aCSP, aEvalAllowed,
-                                     aReportCSPViolations),
+                             const nsAString& aScriptURL,
+                             bool aIsChromeWorker, bool aIsSharedWorker,
+                             const nsAString& aSharedWorkerName,
+                             LoadInfo& aLoadInfo)
+: WorkerPrivateParent<WorkerPrivate>(aCx, aObject, 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), mXHRParamsAllowed(aXHRParamsAllowed)
+  mBlockedForMemoryReporter(false)
 {
   MOZ_COUNT_CTOR(mozilla::dom::workers::WorkerPrivate);
+  MOZ_ASSERT_IF(aIsSharedWorker, !aObject && !aSharedWorkerName.IsVoid());
+  MOZ_ASSERT_IF(!aIsSharedWorker, aObject && aSharedWorkerName.IsEmpty());
 }
 
 WorkerPrivate::~WorkerPrivate()
 {
   MOZ_COUNT_DTOR(mozilla::dom::workers::WorkerPrivate);
 }
 
 // static
 already_AddRefed<WorkerPrivate>
-WorkerPrivate::Create(JSContext* aCx, JS::Handle<JSObject*> aObj, WorkerPrivate* aParent,
-                      JS::Handle<JSString*> aScriptURL, bool aIsChromeWorker)
+WorkerPrivate::Create(JSContext* aCx, JS::HandleObject aObject,
+                      WorkerPrivate* aParent, const nsAString& aScriptURL,
+                      bool aIsChromeWorker, bool aIsSharedWorker,
+                      const nsAString& aSharedWorkerName, LoadInfo* aLoadInfo)
 {
-  nsCString domain;
-  nsCOMPtr<nsIURI> baseURI;
-  nsCOMPtr<nsIPrincipal> principal;
-  nsCOMPtr<nsIScriptContext> scriptContext;
-  nsCOMPtr<nsIDocument> document;
-  nsCOMPtr<nsPIDOMWindow> window;
-  nsCOMPtr<nsIContentSecurityPolicy> csp;
-
-  bool evalAllowed = true;
-  bool reportEvalViolations = false;
-
-  JSContext* parentContext;
-
-  bool xhrParamsAllowed = false;
+  if (aParent) {
+    aParent->AssertIsOnWorkerThread();
+  } else {
+    AssertIsOnMainThread();
+  }
+
+  MOZ_ASSERT_IF(aIsSharedWorker, !aObject && !aSharedWorkerName.IsVoid());
+  MOZ_ASSERT_IF(!aIsSharedWorker, aObject && aSharedWorkerName.IsEmpty());
+
+  mozilla::Maybe<LoadInfo> stackLoadInfo;
+  if (!aLoadInfo) {
+    stackLoadInfo.construct();
+
+    nsresult rv = GetLoadInfo(aCx, nullptr, aParent, aScriptURL,
+                              aIsChromeWorker, stackLoadInfo.addr());
+    if (NS_FAILED(rv)) {
+      scriptloader::ReportLoadError(aCx, aScriptURL, rv, !aParent);
+      return nullptr;
+    }
+
+    aLoadInfo = stackLoadInfo.addr();
+  }
+
+  nsRefPtr<WorkerPrivate> worker =
+    new WorkerPrivate(aCx, aObject, aParent, aScriptURL, aIsChromeWorker,
+                      aIsSharedWorker, aSharedWorkerName, *aLoadInfo);
+
+  nsRefPtr<CompileScriptRunnable> compiler = new CompileScriptRunnable(worker);
+  if (!compiler->Dispatch(aCx)) {
+    return nullptr;
+  }
+
+  return worker.forget();
+}
+
+// static
+nsresult
+WorkerPrivate::GetLoadInfo(JSContext* aCx, nsPIDOMWindow* aWindow,
+                           WorkerPrivate* aParent, const nsAString& aScriptURL,
+                           bool aIsChromeWorker, LoadInfo* aLoadInfo)
+{
+  using namespace mozilla::dom::workers::scriptloader;
+
+  MOZ_ASSERT(aCx);
+
+  if (aWindow) {
+    AssertIsOnMainThread();
+  }
+
+  LoadInfo loadInfo;
+  nsresult rv;
 
   if (aParent) {
     aParent->AssertIsOnWorkerThread();
 
     // If the parent is going away give up now.
-    Status currentStatus;
+    Status parentStatus;
     {
       MutexAutoLock lock(aParent->mMutex);
-      currentStatus = aParent->mStatus;
+      parentStatus = aParent->mStatus;
     }
 
-    if (currentStatus > Running) {
-      JS_ReportError(aCx, "Cannot create child workers from the close handler!");
-      return nullptr;
+    if (parentStatus > Running) {
+      NS_WARNING("Cannot create child workers from the close handler!");
+      return NS_ERROR_FAILURE;
     }
 
-    parentContext = aCx;
-
-    // Domain is the only thing we can touch here. The rest will be handled by
-    // the ScriptLoader.
-    domain = aParent->Domain();
-  }
-  else {
+    rv = ChannelFromScriptURLWorkerThread(aCx, aParent, aScriptURL,
+                                          getter_AddRefs(loadInfo.mChannel));
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    // Now that we've spun the loop there's no guarantee that our parent is
+    // still alive.  We may have received control messages initiating shutdown.
+    {
+      MutexAutoLock lock(aParent->mMutex);
+      parentStatus = aParent->mStatus;
+    }
+
+    if (parentStatus > Running) {
+      nsCOMPtr<nsIThread> mainThread;
+      if (NS_FAILED(NS_GetMainThread(getter_AddRefs(mainThread))) ||
+          NS_FAILED(NS_ProxyRelease(mainThread, loadInfo.mChannel))) {
+        NS_WARNING("Failed to proxy release of channel, leaking instead!");
+      }
+      return NS_ERROR_FAILURE;
+    }
+
+    loadInfo.mDomain = aParent->Domain();
+  } else {
     AssertIsOnMainThread();
 
     nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
-    NS_ASSERTION(ssm, "This should never be null!");
+    MOZ_ASSERT(ssm);
 
     bool isChrome = nsContentUtils::IsCallerChrome();
 
-    // First check to make sure the caller has permission to make a
-    // ChromeWorker if they called the ChromeWorker constructor.
+    // First check to make sure the caller has permission to make a privileged
+    // worker if they called the ChromeWorker/ChromeSharedWorker constructor.
     if (aIsChromeWorker && !isChrome) {
-      xpc::Throw(aCx, NS_ERROR_DOM_SECURITY_ERR);
-      return nullptr;
+      return NS_ERROR_DOM_SECURITY_ERR;
     }
 
     // Chrome callers (whether ChromeWorker of Worker) always get the system
     // principal here as they're allowed to load anything. The script loader may
     // change the principal later depending on the script uri.
-    if (isChrome &&
-        NS_FAILED(ssm->GetSystemPrincipal(getter_AddRefs(principal)))) {
-      JS_ReportError(aCx, "Could not get system principal!");
-      return nullptr;
+    if (isChrome) {
+      rv = ssm->GetSystemPrincipal(getter_AddRefs(loadInfo.mPrincipal));
+      NS_ENSURE_SUCCESS(rv, rv);
     }
 
-    // See if we're being called from a window or from somewhere else.
-    nsCOMPtr<nsIScriptGlobalObject> scriptGlobal =
-      nsJSUtils::GetStaticScriptGlobal(JS::CurrentGlobalOrNull(aCx));
-    if (scriptGlobal) {
-      // Window!
-      nsCOMPtr<nsPIDOMWindow> globalWindow = do_QueryInterface(scriptGlobal);
-
+    // See if we're being called from a window.
+    nsCOMPtr<nsPIDOMWindow> globalWindow = aWindow;
+    if (!globalWindow) {
+      nsCOMPtr<nsIScriptGlobalObject> scriptGlobal =
+        nsJSUtils::GetStaticScriptGlobal(JS::CurrentGlobalOrNull(aCx));
+      if (scriptGlobal) {
+        globalWindow = do_QueryInterface(scriptGlobal);
+        MOZ_ASSERT(globalWindow);
+      }
+    }
+
+    nsCOMPtr<nsIDocument> document;
+
+    if (globalWindow) {
       // Only use the current inner window, and only use it if the caller can
       // access it.
-      nsPIDOMWindow* outerWindow = globalWindow ?
-                                   globalWindow->GetOuterWindow() :
-                                   nullptr;
-      window = outerWindow ? outerWindow->GetCurrentInnerWindow() : nullptr;
-      if (!window ||
-          (globalWindow != window &&
-           !nsContentUtils::CanCallerAccess(window))) {
-        xpc::Throw(aCx, NS_ERROR_DOM_SECURITY_ERR);
-        return nullptr;
+      nsPIDOMWindow* outerWindow = globalWindow->GetOuterWindow();
+      if (outerWindow) {
+        loadInfo.mWindow = outerWindow->GetCurrentInnerWindow();
       }
 
-      scriptContext = scriptGlobal->GetContext();
-      if (!scriptContext) {
-        JS_ReportError(aCx, "Couldn't get script context for this worker!");
-        return nullptr;
+      if (!loadInfo.mWindow ||
+          (globalWindow != loadInfo.mWindow &&
+            !nsContentUtils::CanCallerAccess(loadInfo.mWindow))) {
+        return NS_ERROR_DOM_SECURITY_ERR;
       }
 
-      parentContext = scriptContext->GetNativeContext();
+      nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(loadInfo.mWindow);
+      MOZ_ASSERT(sgo);
+
+      loadInfo.mScriptContext = sgo->GetContext();
+      NS_ENSURE_TRUE(loadInfo.mScriptContext, NS_ERROR_FAILURE);
 
       // If we're called from a window then we can dig out the principal and URI
       // from the document.
-      document = window->GetExtantDoc();
-      if (!document) {
-        JS_ReportError(aCx, "No document in this window!");
-        return nullptr;
-      }
-
-      baseURI = document->GetDocBaseURI();
+      document = loadInfo.mWindow->GetExtantDoc();
+      NS_ENSURE_TRUE(document, NS_ERROR_FAILURE);
+
+      loadInfo.mBaseURI = document->GetDocBaseURI();
 
       // Use the document's NodePrincipal as our principal if we're not being
       // called from chrome.
-      if (!principal) {
-        if (!(principal = document->NodePrincipal())) {
-          JS_ReportError(aCx, "Could not get document principal!");
-          return nullptr;
-        }
+      if (!loadInfo.mPrincipal) {
+        loadInfo.mPrincipal = document->NodePrincipal();
+        NS_ENSURE_TRUE(loadInfo.mPrincipal, NS_ERROR_FAILURE);
 
         // We use the document's base domain to limit the number of workers
         // each domain can create. For sandboxed documents, we use the domain
         // of their first non-sandboxed document, walking up until we find
         // one. If we can't find one, we fall back to using the GUID of the
         // null principal as the base domain.
         if (document->GetSandboxFlags() & SANDBOXED_ORIGIN) {
           nsCOMPtr<nsIDocument> tmpDoc = document;
           do {
             tmpDoc = tmpDoc->GetParentDocument();
           } while (tmpDoc && tmpDoc->GetSandboxFlags() & SANDBOXED_ORIGIN);
 
           if (tmpDoc) {
             // There was an unsandboxed ancestor, yay!
             nsCOMPtr<nsIPrincipal> tmpPrincipal = tmpDoc->NodePrincipal();
-
-            if (NS_FAILED(tmpPrincipal->GetBaseDomain(domain))) {
-              JS_ReportError(aCx, "Could not determine base domain!");
-              return nullptr;
-            }
+            rv = tmpPrincipal->GetBaseDomain(loadInfo.mDomain);
+            NS_ENSURE_SUCCESS(rv, rv);
           } else {
             // No unsandboxed ancestor, use our GUID.
-            if (NS_FAILED(principal->GetBaseDomain(domain))) {
-              JS_ReportError(aCx, "Could not determine base domain!");
-             return nullptr;
-            }
+            rv = loadInfo.mPrincipal->GetBaseDomain(loadInfo.mDomain);
+            NS_ENSURE_SUCCESS(rv, rv);
           }
         } else {
           // Document creating the worker is not sandboxed.
-          if (NS_FAILED(principal->GetBaseDomain(domain))) {
-            JS_ReportError(aCx, "Could not determine base domain!");
-            return nullptr;
-          }
+          rv = loadInfo.mPrincipal->GetBaseDomain(loadInfo.mDomain);
+          NS_ENSURE_SUCCESS(rv, rv);
         }
       }
 
-      xhrParamsAllowed = CheckXHRParamsAllowed(window);
-    }
-    else {
+      nsCOMPtr<nsIPermissionManager> permMgr =
+        do_GetService(NS_PERMISSIONMANAGER_CONTRACTID, &rv);
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      uint32_t perm;
+      rv = permMgr->TestPermissionFromPrincipal(loadInfo.mPrincipal, "systemXHR",
+                                                &perm);
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      loadInfo.mXHRParamsAllowed = perm == nsIPermissionManager::ALLOW_ACTION;
+    } else {
       // Not a window
-      NS_ASSERTION(isChrome, "Should be chrome only!");
-
-      parentContext = nullptr;
+      MOZ_ASSERT(isChrome);
 
       // We're being created outside of a window. Need to figure out the script
       // that is creating us in order for us to use relative URIs later on.
       JS::RootedScript script(aCx);
       if (JS_DescribeScriptedCaller(aCx, &script, nullptr)) {
-        if (NS_FAILED(NS_NewURI(getter_AddRefs(baseURI),
-                                JS_GetScriptFilename(aCx, script)))) {
-          JS_ReportError(aCx, "Failed to construct base URI!");
-          return nullptr;
+        rv = NS_NewURI(getter_AddRefs(loadInfo.mBaseURI),
+                       JS_GetScriptFilename(aCx, script));
+        NS_ENSURE_SUCCESS(rv, rv);
         }
-      }
-
-      xhrParamsAllowed = true;
+
+      loadInfo.mXHRParamsAllowed = true;
     }
 
-    NS_ASSERTION(principal, "Must have a principal now!");
-
-    if (!isChrome && domain.IsEmpty()) {
-      NS_ERROR("Must be chrome or have an domain!");
-      return nullptr;
-    }
-
-    if (!nsContentUtils::GetContentSecurityPolicy(aCx, getter_AddRefs(csp))) {
-      return nullptr;
-    }
-
-    if (csp && NS_FAILED(csp->GetAllowsEval(&reportEvalViolations, &evalAllowed))) {
-      NS_ERROR("CSP: failed to get allowsEval");
-      return nullptr;
+    MOZ_ASSERT(loadInfo.mPrincipal);
+    MOZ_ASSERT(isChrome || !loadInfo.mDomain.IsEmpty());
+
+    // XXXbent Use subject principal here instead of the one we already have?
+    nsCOMPtr<nsIPrincipal> subjectPrincipal = ssm->GetCxSubjectPrincipal(aCx);
+    MOZ_ASSERT(subjectPrincipal);
+
+    if (!nsContentUtils::GetContentSecurityPolicy(aCx,
+                                               getter_AddRefs(loadInfo.mCSP))) {
+      NS_WARNING("Failed to get CSP!");
+      return NS_ERROR_FAILURE;
     }
-  }
-
-  size_t urlLength;
-  const jschar* urlChars = JS_GetStringCharsZAndLength(aCx, aScriptURL,
-                                                       &urlLength);
-  if (!urlChars) {
-    return nullptr;
-  }
-
-  nsDependentString scriptURL(urlChars, urlLength);
-  nsCOMPtr<nsIChannel> channel;
-  nsresult rv;
-  if (aParent) {
-    rv =
-      scriptloader::ChannelFromScriptURLWorkerThread(aCx, aParent, scriptURL,
-                                                     getter_AddRefs(channel));
-
-    // Now that we've spun the loop there's no guarantee that our parent is
-    // still alive.  We may have received control messages initiating shutdown.
-
-    Status currentStatus;
-    {
-      MutexAutoLock lock(aParent->mMutex);
-      currentStatus = aParent->mStatus;
+
+    if (loadInfo.mCSP) {
+      rv = loadInfo.mCSP->GetAllowsEval(&loadInfo.mReportCSPViolations,
+                                        &loadInfo.mEvalAllowed);
+      NS_ENSURE_SUCCESS(rv, rv);
+    } else {
+      loadInfo.mEvalAllowed = true;
+      loadInfo.mReportCSPViolations = false;
     }
 
-    if (currentStatus > Running) {
-      nsCOMPtr<nsIThread> mainThread;
-      NS_GetMainThread(getter_AddRefs(mainThread));
-      if (!mainThread) {
-        MOZ_CRASH();
-      }
-
-      nsIChannel* rawChannel;
-      channel.forget(&rawChannel);
-      // If this fails we accept the leak.
-      NS_ProxyRelease(mainThread, rawChannel);
-
-      return nullptr;
-    }
-  }
-  else {
-    rv =
-      scriptloader::ChannelFromScriptURLMainThread(principal, baseURI,
-                                                   document, scriptURL,
-                                                   getter_AddRefs(channel));
-  }
-  if (NS_FAILED(rv)) {
-    scriptloader::ReportLoadError(aCx, scriptURL, rv, !aParent);
-    return nullptr;
-  }
-
-  nsRefPtr<WorkerPrivate> worker =
-    new WorkerPrivate(aCx, aObj, aParent, parentContext, scriptURL,
-                      aIsChromeWorker, domain, window, scriptContext, baseURI,
-                      principal, channel, csp, evalAllowed, reportEvalViolations,
-                      xhrParamsAllowed);
-
-  worker->SetIsDOMBinding();
-  worker->SetWrapper(aObj);
-
-  nsRefPtr<CompileScriptRunnable> compiler = new CompileScriptRunnable(worker);
-  if (!compiler->Dispatch(aCx)) {
-    return nullptr;
-  }
-
-  return worker.forget();
+    rv = ChannelFromScriptURLMainThread(loadInfo.mPrincipal, loadInfo.mBaseURI,
+                                        document, aScriptURL,
+                                        getter_AddRefs(loadInfo.mChannel));
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = NS_GetFinalChannelURI(loadInfo.mChannel,
+                               getter_AddRefs(loadInfo.mResolvedScriptURI));
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  aLoadInfo->StealFrom(loadInfo);
+  return NS_OK;
 }
 
 void
 WorkerPrivate::DoRunLoop(JSContext* aCx)
 {
   AssertIsOnWorkerThread();
 
   {
@@ -2968,17 +3620,23 @@ WorkerPrivate::OperationCallback(JSConte
   AssertIsOnWorkerThread();
 
   bool mayContinue = true;
 
   for (;;) {
     // Run all control events now.
     mayContinue = ProcessAllControlRunnables();
 
-    if (!mayContinue || !mSuspended) {
+    bool maySuspend = mSuspended;
+    if (maySuspend) {
+      MutexAutoLock lock(mMutex);
+      maySuspend = mStatus <= Running;
+    }
+
+    if (!mayContinue || !maySuspend) {
       break;
     }
 
     // Clean up before suspending.
     JS_GC(JS_GetRuntime(aCx));
 
     while ((mayContinue = MayContinueRunning())) {
       MutexAutoLock lock(mMutex);
@@ -3233,46 +3891,16 @@ WorkerPrivate::ProcessAllControlRunnable
 
     NS_RELEASE(event);
   }
 
   return result;
 }
 
 bool
-WorkerPrivate::CheckXHRParamsAllowed(nsPIDOMWindow* aWindow)
-{
-  AssertIsOnMainThread();
-  NS_ASSERTION(aWindow, "Wrong cannot be null");
-
-  if (!aWindow->GetDocShell()) {
-    return false;
-  }
-
-  nsCOMPtr<nsIDocument> doc = aWindow->GetExtantDoc();
-  if (!doc) {
-    return false;
-  }
-
-  nsCOMPtr<nsIPermissionManager> permMgr = do_GetService(NS_PERMISSIONMANAGER_CONTRACTID);
-  if (!permMgr) {
-    return false;
-  }
-
-  uint32_t permission;
-  nsresult rv = permMgr->TestPermissionFromPrincipal(doc->NodePrincipal(),
-                                                     "systemXHR", &permission);
-  if (NS_FAILED(rv) || permission != nsIPermissionManager::ALLOW_ACTION) {
-    return false;
-  }
-
-  return true;
-}
-
-bool
 WorkerPrivate::Dispatch(WorkerRunnable* aEvent, EventQueue* aQueue)
 {
   nsRefPtr<WorkerRunnable> event(aEvent);
 
   {
     MutexAutoLock lock(mMutex);
 
     if (mStatus == Dead) {
@@ -3390,16 +4018,30 @@ WorkerPrivate::TraceInternal(JSTracer* a
     TimeoutInfo* info = mTimeouts[index];
     JS_CallHeapValueTracer(aTrc, &info->mTimeoutVal,
                            "WorkerPrivate timeout value");
     for (uint32_t index2 = 0; index2 < info->mExtraArgVals.Length(); index2++) {
       JS_CallHeapValueTracer(aTrc, &info->mExtraArgVals[index2],
                              "WorkerPrivate timeout extra argument value");
     }
   }
+
+  // MessagePort objects are basically held alive as long as the global.
+  mWorkerPorts.EnumerateRead(TraceMessagePorts, aTrc);
+}
+
+// static
+PLDHashOperator
+WorkerPrivate::TraceMessagePorts(const uint64_t& aKey,
+                                 WorkerMessagePort* aData,
+                                 void* aUserArg)
+{
+  JSTracer* trc = static_cast<JSTracer*>(aUserArg);
+  aData->TraceJSObject(trc, "mWorkerPorts");
+  return PL_DHASH_NEXT;
 }
 
 bool
 WorkerPrivate::ModifyBusyCountFromWorker(JSContext* aCx, bool aIncrease)
 {
   AssertIsOnWorkerThread();
 
   {
@@ -3636,19 +4278,21 @@ void
 WorkerPrivate::DestroySyncLoop(uint32_t aSyncLoopKey)
 {
   AssertIsOnWorkerThread();
 
   mSyncQueues.RemoveElementAt(aSyncLoopKey);
 }
 
 bool
-WorkerPrivate::PostMessageToParent(JSContext* aCx,
+WorkerPrivate::PostMessageToParentInternal(JSContext* aCx,
                                    JS::Handle<JS::Value> aMessage,
-                                   JS::Handle<JS::Value> aTransferable)
+                                           JS::Handle<JS::Value> aTransferable,
+                                           bool aToMessagePort,
+                                           uint64_t aMessagePortSerial)
 {
   AssertIsOnWorkerThread();
 
   JSStructuredCloneCallbacks* callbacks =
     IsChromeWorker() ?
     &gChromeWorkerStructuredCloneCallbacks :
     &gWorkerStructuredCloneCallbacks;
 
@@ -3656,20 +4300,55 @@ WorkerPrivate::PostMessageToParent(JSCon
 
   JSAutoStructuredCloneBuffer buffer;
   if (!buffer.write(aCx, aMessage, aTransferable, callbacks, &clonedObjects)) {
     return false;
   }
 
   nsRefPtr<MessageEventRunnable> runnable =
     new MessageEventRunnable(this, WorkerRunnable::ParentThread, buffer,
-                             clonedObjects);
+                             clonedObjects, aToMessagePort, aMessagePortSerial);
   return runnable->Dispatch(aCx);
 }
 
+void
+WorkerPrivate::PostMessageToParentMessagePort(
+                           JSContext* aCx,
+                           uint64_t aMessagePortSerial,
+                           JS::Handle<JS::Value> aMessage,
+                           const Optional<Sequence<JS::Value > >& aTransferable,
+                           ErrorResult& aRv)
+{
+  AssertIsOnWorkerThread();
+
+  if (!mWorkerPorts.Get(aMessagePortSerial)) {
+    // This port has been closed from the main thread. There's no point in
+    // sending this message so just bail.
+    return;
+  }
+
+  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 (!PostMessageToParentInternal(aCx, aMessage, transferable, true,
+                                   aMessagePortSerial)) {
+    aRv = NS_ERROR_FAILURE;
+  }
+}
+
 bool
 WorkerPrivate::NotifyInternal(JSContext* aCx, Status aStatus)
 {
   AssertIsOnWorkerThread();
 
   NS_ASSERTION(aStatus > Running && aStatus < Dead, "Bad status!");
 
   // Save the old status and set the new status.
@@ -4270,62 +4949,16 @@ WorkerPrivate::GarbageCollectInternal(JS
 
   if (aCollectChildren) {
     for (uint32_t index = 0; index < mChildWorkers.Length(); index++) {
       mChildWorkers[index]->GarbageCollect(aCx, aShrinking);
     }
   }
 }
 
-#ifdef DEBUG
-template <class Derived>
-void
-WorkerPrivateParent<Derived>::AssertIsOnParentThread() const
-{
-  if (GetParent()) {
-    GetParent()->AssertIsOnWorkerThread();
-  }
-  else {
-    AssertIsOnMainThread();
-  }
-}
-
-template <class Derived>
-void
-WorkerPrivateParent<Derived>::AssertInnerWindowIsCorrect() const
-{
-  AssertIsOnParentThread();
-
-  // Only care about top level workers from windows.
-  if (mParent || !mWindow) {
-    return;
-  }
-
-  AssertIsOnMainThread();
-
-  nsPIDOMWindow* outer = mWindow->GetOuterWindow();
-  NS_ASSERTION(outer && outer->GetCurrentInnerWindow() == mWindow,
-               "Inner window no longer correct!");
-}
-
-void
-WorkerPrivate::AssertIsOnWorkerThread() const
-{
-  if (mThread) {
-    bool current;
-    if (NS_FAILED(mThread->IsOnCurrentThread(&current)) || !current) {
-      NS_ERROR("Wrong thread!");
-    }
-  }
-  else {
-    NS_ERROR("Trying to assert thread identity after thread has been "
-             "shutdown!");
-  }
-}
-#endif
 
 template <class Derived>
 void
 WorkerPrivateParent<Derived>::RegisterHostObjectURI(const nsACString& aURI)
 {
   AssertIsOnMainThread();
   mHostObjectURIs.AppendElement(aURI);
 }
@@ -4340,23 +4973,34 @@ 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()
 {
-  mozilla::MutexAutoLock lock(mMutex);
+  MutexAutoLock lock(mMutex);
+
   if (!mCrossThreadDispatcher && mStatus <= Running) {
     mCrossThreadDispatcher = new WorkerCrossThreadDispatcher(this);
   }
+
   return mCrossThreadDispatcher;
 }
 
 void
 WorkerPrivate::BeginCTypesCall()
 {
   AssertIsOnWorkerThread();
 
@@ -4390,16 +5034,117 @@ WorkerPrivate::EndCTypesCall()
     mMemoryReportCondVar.Wait();
   }
 
   // No need to notify the main thread here as it shouldn't be waiting to see
   // this state.
   mBlockedForMemoryReporter = false;
 }
 
+bool
+WorkerPrivate::ConnectMessagePort(JSContext* aCx, uint64_t aMessagePortSerial)
+{
+  AssertIsOnWorkerThread();
+
+  NS_ASSERTION(!mWorkerPorts.Get(aMessagePortSerial),
+               "Already have this port registered!");
+
+  nsRefPtr<WorkerMessagePort> port =
+    new WorkerMessagePort(aCx, aMessagePortSerial);
+
+  JS::Rooted<JSObject*> global(aCx, JS::CurrentGlobalOrNull(aCx));
+
+  JS::Rooted<JSObject*> portObj(aCx, Wrap(aCx, global, port));
+  if (!portObj) {
+    return false;
+  }
+
+  JS::Rooted<JSObject*> event(aCx, CreateConnectEvent(aCx, portObj));
+  if (!event) {
+    return false;
+  }
+
+  mWorkerPorts.Put(aMessagePortSerial, port);
+
+  bool dummy;
+  if (!DispatchEventToTarget(aCx, global, event, &dummy)) {
+    mWorkerPorts.Remove(aMessagePortSerial);
+    return false;
+  }
+
+  return true;
+}
+
+void
+WorkerPrivate::DisconnectMessagePort(uint64_t aMessagePortSerial)
+{
+  AssertIsOnWorkerThread();
+
+  // The port may have already been removed from this list since either the main
+  // thread or the worker thread can remove it.
+  mWorkerPorts.Remove(aMessagePortSerial);
+}
+
+WorkerMessagePort*
+WorkerPrivate::GetMessagePort(uint64_t aMessagePortSerial)
+{
+  AssertIsOnWorkerThread();
+
+  WorkerMessagePort* port;
+  if (mWorkerPorts.Get(aMessagePortSerial, &port)) {
+    return port;
+  }
+
+  return nullptr;
+}
+
+#ifdef DEBUG
+template <class Derived>
+void
+WorkerPrivateParent<Derived>::AssertIsOnParentThread() const
+{
+  if (GetParent()) {
+    GetParent()->AssertIsOnWorkerThread();
+  }
+  else {
+    AssertIsOnMainThread();
+  }
+}
+
+template <class Derived>
+void
+WorkerPrivateParent<Derived>::AssertInnerWindowIsCorrect() const
+{
+  AssertIsOnParentThread();
+
+  // Only care about top level workers from windows.
+  if (mParent || !mLoadInfo.mWindow) {
+    return;
+  }
+
+  AssertIsOnMainThread();
+
+  nsPIDOMWindow* outer = mLoadInfo.mWindow->GetOuterWindow();
+  NS_ASSERTION(outer && outer->GetCurrentInnerWindow() == mLoadInfo.mWindow,
+               "Inner window no longer correct!");
+}
+
+void
+WorkerPrivate::AssertIsOnWorkerThread() const
+{
+  MOZ_ASSERT(mThread,
+             "Trying to assert thread identity after thread has been "
+             "shutdown!");
+
+  bool current;
+  MOZ_ASSERT(NS_SUCCEEDED(mThread->IsOnCurrentThread(&current)));
+  MOZ_ASSERT(current, "Wrong thread!");
+}
+#endif // DEBUG
+
 BEGIN_WORKERS_NAMESPACE
 
 // Force instantiation.
 template class WorkerPrivateParent<WorkerPrivate>;
 
 JSStructuredCloneCallbacks*
 WorkerStructuredCloneCallbacks(bool aMainRuntime)
 {
--- a/dom/workers/WorkerPrivate.h
+++ b/dom/workers/WorkerPrivate.h
@@ -14,43 +14,49 @@
 #include "nsIThread.h"
 #include "nsIThreadInternal.h"
 #include "nsPIDOMWindow.h"
 
 #include "mozilla/Assertions.h"
 #include "mozilla/CondVar.h"
 #include "mozilla/Mutex.h"
 #include "mozilla/TimeStamp.h"
-#include "nsAutoPtr.h"
-#include "nsCOMPtr.h"
+#include "mozilla/dom/BindingDeclarations.h"
+#include "nsCycleCollectionParticipant.h"
+#include "nsDataHashtable.h"
 #include "nsEventQueue.h"
+#include "nsHashKeys.h"
 #include "nsString.h"
 #include "nsTArray.h"
 #include "nsTPriorityQueue.h"
 #include "StructuredCloneTags.h"
 
 #include "EventTarget.h"
 #include "Queue.h"
 #include "WorkerFeature.h"
 
 class JSAutoStructuredCloneBuffer;
 class nsIChannel;
+class nsIContentSecurityPolicy;
 class nsIDocument;
 class nsIPrincipal;
 class nsIScriptContext;
 class nsIURI;
 class nsPIDOMWindow;
 class nsITimer;
 
 namespace JS {
 class RuntimeStats;
 }
 
 BEGIN_WORKERS_NAMESPACE
 
+class MessagePort;
+class SharedWorker;
+class WorkerMessagePort;
 class WorkerPrivate;
 
 class WorkerRunnable : public nsIRunnable
 {
 public:
   enum Target { ParentThread, WorkerThread };
   enum BusyBehavior { ModifyBusyCount, UnchangedBusyCount };
   enum ClearingBehavior { SkipWhenClearing, RunWhenClearing };
@@ -241,69 +247,99 @@ public:
     nsCString mHost;
     nsCString mHostname;
     nsCString mPort;
     nsCString mPathname;
     nsCString mSearch;
     nsCString mHash;
   };
 
+  struct LoadInfo
+  {
+    // All of these should be released in ForgetMainThreadObjects.
+    nsCOMPtr<nsIURI> mBaseURI;
+    nsCOMPtr<nsIURI> mResolvedScriptURI;
+    nsCOMPtr<nsIPrincipal> mPrincipal;
+    nsCOMPtr<nsIScriptContext> mScriptContext;
+    nsCOMPtr<nsPIDOMWindow> mWindow;
+    nsCOMPtr<nsIContentSecurityPolicy> mCSP;
+    nsCOMPtr<nsIChannel> mChannel;
+
+    nsCString mDomain;
+
+    bool mEvalAllowed;
+    bool mReportCSPViolations;
+    bool mXHRParamsAllowed;
+    bool mPrincipalIsSystem;
+
+    LoadInfo()
+    : mEvalAllowed(false), mReportCSPViolations(false),
+      mXHRParamsAllowed(false), mPrincipalIsSystem(false)
+    { }
+
+    void
+    StealFrom(LoadInfo& aOther)
+    {
+      mBaseURI = aOther.mBaseURI.forget();
+      mResolvedScriptURI = aOther.mResolvedScriptURI.forget();
+      mPrincipal = aOther.mPrincipal.forget();
+      mScriptContext = aOther.mScriptContext.forget();
+      mWindow = aOther.mWindow.forget();
+      mCSP = aOther.mCSP.forget();
+      mChannel = aOther.mChannel.forget();
+      mDomain = aOther.mDomain;
+      mEvalAllowed = aOther.mEvalAllowed;
+      mReportCSPViolations = aOther.mReportCSPViolations;
+      mXHRParamsAllowed = aOther.mXHRParamsAllowed;
+      mPrincipalIsSystem = aOther.mPrincipalIsSystem;
+    }
+  };
+
 protected:
+  typedef mozilla::ErrorResult ErrorResult;
+
   SharedMutex mMutex;
   mozilla::CondVar mCondVar;
   mozilla::CondVar mMemoryReportCondVar;
 
 private:
   JSObject* mJSObject;
   WorkerPrivate* mParent;
-  JSContext* mParentJSContext;
   nsString mScriptURL;
-  nsCString mDomain;
+  nsString mSharedWorkerName;
   LocationInfo mLocationInfo;
-
-  // Main-thread things.
-  nsCOMPtr<nsPIDOMWindow> mWindow;
-  nsCOMPtr<nsIScriptContext> mScriptContext;
-  nsCOMPtr<nsIURI> mBaseURI;
-  nsCOMPtr<nsIURI> mScriptURI;
-  nsCOMPtr<nsIPrincipal> mPrincipal;
-  nsCOMPtr<nsIChannel> mChannel;
-  nsCOMPtr<nsIContentSecurityPolicy> mCSP;
+  LoadInfo mLoadInfo;
 
   // Only used for top level workers.
   nsTArray<nsRefPtr<WorkerRunnable> > mQueuedRunnables;
 
   // Only for ChromeWorkers without window and only touched on the main thread.
   nsTArray<nsCString> mHostObjectURIs;
 
   // Protected by mMutex.
   JSSettings mJSSettings;
 
+  // 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 mParentSuspended;
   bool mIsChromeWorker;
-  bool mPrincipalIsSystem;
   bool mMainThreadObjectsForgotten;
-  bool mEvalAllowed;
-  bool mReportCSPViolations;
+  bool mIsSharedWorker;
 
 protected:
-  WorkerPrivateParent(JSContext* aCx, JS::Handle<JSObject*> aObject, WorkerPrivate* aParent,
-                      JSContext* aParentJSContext, const nsAString& aScriptURL,
-                      bool aIsChromeWorker, const nsACString& aDomain,
-                      nsCOMPtr<nsPIDOMWindow>& aWindow,
-                      nsCOMPtr<nsIScriptContext>& aScriptContext,
-                      nsCOMPtr<nsIURI>& aBaseURI,
-                      nsCOMPtr<nsIPrincipal>& aPrincipal,
-                      nsCOMPtr<nsIChannel>& aChannel,
-                      nsCOMPtr<nsIContentSecurityPolicy>& aCSP,
-                      bool aEvalAllowed,
-                      bool aReportCSPViolations);
+  WorkerPrivateParent(JSContext* aCx, JS::HandleObject aObject,
+                      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));
@@ -315,16 +351,21 @@ private:
 
   // aCx is null when called from the finalizer
   bool
   TerminatePrivate(JSContext* aCx)
   {
     return NotifyPrivate(aCx, Terminating);
   }
 
+  bool
+  PostMessageInternal(JSContext* aCx, JS::Handle<JS::Value> aMessage,
+                      JS::Handle<JS::Value> aTransferable,
+                      bool aToMessagePort, uint64_t aMessagePortSerial);
+
 public:
   // May be called on any thread...
   bool
   Start();
 
   // Called on the parent thread.
   bool
   Notify(JSContext* aCx, Status aStatus)
@@ -340,23 +381,24 @@ public:
 
   bool
   Kill(JSContext* aCx)
   {
     return Notify(aCx, Killing);
   }
 
   bool
-  Suspend(JSContext* aCx);
-
-  void
-  Resume(JSContext* aCx);
+  Suspend(JSContext* aCx, nsPIDOMWindow* aWindow);
 
   bool
-  SynchronizeAndResume(nsIScriptContext* aCx);
+  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
@@ -382,17 +424,34 @@ public:
   bool
   RootJSObject(JSContext* aCx, bool aRoot);
 
   void
   ForgetMainThreadObjects(nsTArray<nsCOMPtr<nsISupports> >& aDoomed);
 
   bool
   PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
-              JS::Handle<JS::Value> aTransferable);
+              JS::Handle<JS::Value> aTransferable)
+  {
+    return PostMessageInternal(aCx, aMessage, aTransferable, false, 0);
+  }
+
+  void
+  PostMessageToMessagePort(JSContext* aCx,
+                           uint64_t aMessagePortSerial,
+                           JS::Handle<JS::Value> aMessage,
+                           const Optional<Sequence<JS::Value > >& aTransferable,
+                           ErrorResult& aRv);
+
+  bool
+  DispatchMessageEventToMessagePort(
+                              JSContext* aCx,
+                              uint64_t aMessagePortSerial,
+                              JSAutoStructuredCloneBuffer& aBuffer,
+                              nsTArray<nsCOMPtr<nsISupports> >& aClonedObjects);
 
   uint64_t
   GetInnerWindowId();
 
   void
   UpdateJSContextOptions(JSContext* aCx, uint32_t aChromeOptions,
                          uint32_t aContentOptions);
 
@@ -406,16 +465,34 @@ public:
 #endif
 
   void
   UpdateJITHardening(JSContext* aCx, bool aJITHardening);
 
   void
   GarbageCollect(JSContext* aCx, bool aShrinking);
 
+  bool
+  RegisterSharedWorker(JSContext* aCx, SharedWorker* aSharedWorker);
+
+  void
+  UnregisterSharedWorker(JSContext* aCx, SharedWorker* aSharedWorker);
+
+  void
+  BroadcastErrorToSharedWorkers(JSContext* aCx,
+                                const nsAString& aMessage,
+                                const nsAString& aFilename,
+                                const nsAString& aLine,
+                                uint32_t aLineNumber,
+                                uint32_t aColumnNumber,
+                                uint32_t aFlags);
+
+  void
+  WorkerScriptLoaded();
+
   void
   QueueRunnable(WorkerRunnable* aRunnable)
   {
     AssertIsOnMainThread();
     mQueuedRunnables.AppendElement(aRunnable);
   }
 
   WorkerPrivate*
@@ -452,17 +529,17 @@ public:
 
   JSContext*
   ParentJSContext() const;
 
   nsIScriptContext*
   GetScriptContext() const
   {
     AssertIsOnMainThread();
-    return mScriptContext;
+    return mLoadInfo.mScriptContext;
   }
 
   JSObject*
   GetJSObject() const
   {
     return mJSObject;
   }
 
@@ -470,110 +547,115 @@ public:
   ScriptURL() const
   {
     return mScriptURL;
   }
 
   const nsCString&
   Domain() const
   {
-    return mDomain;
+    return mLoadInfo.mDomain;
   }
 
   nsIURI*
   GetBaseURI() const
   {
     AssertIsOnMainThread();
-    return mBaseURI;
+    return mLoadInfo.mBaseURI;
   }
 
   void
   SetBaseURI(nsIURI* aBaseURI);
 
   nsIURI*
-  GetScriptURI() const
+  GetResolvedScriptURI() const
   {
     AssertIsOnMainThread();
-    return mScriptURI;
-  }
-
-  void
-  SetScriptURI(nsIURI* aScriptURI)
-  {
-    AssertIsOnMainThread();
-    mScriptURI = aScriptURI;
+    return mLoadInfo.mResolvedScriptURI;
   }
 
   nsIPrincipal*
   GetPrincipal() const
   {
     AssertIsOnMainThread();
-    return mPrincipal;
+    return mLoadInfo.mPrincipal;
   }
 
   void
   SetPrincipal(nsIPrincipal* aPrincipal);
 
   bool
   UsesSystemPrincipal() const
   {
-    return mPrincipalIsSystem;
+    return mLoadInfo.mPrincipalIsSystem;
   }
 
-  nsIChannel*
-  GetChannel() const
+  already_AddRefed<nsIChannel>
+  ForgetWorkerChannel()
   {
     AssertIsOnMainThread();
-    return mChannel;
+    return mLoadInfo.mChannel.forget();
   }
 
   nsIDocument*
   GetDocument() const
   {
     AssertIsOnMainThread();
-    return mWindow ? mWindow->GetExtantDoc() : nullptr;
+    return mLoadInfo.mWindow ? mLoadInfo.mWindow->GetExtantDoc() : nullptr;
   }
 
   nsPIDOMWindow*
   GetWindow()
   {
     AssertIsOnMainThread();
-    return mWindow;
+    return mLoadInfo.mWindow;
   }
 
   nsIContentSecurityPolicy*
   GetCSP() const
   {
     AssertIsOnMainThread();
-    return mCSP;
+    return mLoadInfo.mCSP;
   }
 
   void
   SetCSP(nsIContentSecurityPolicy* aCSP)
   {
     AssertIsOnMainThread();
-    mCSP = aCSP;
+    mLoadInfo.mCSP = aCSP;
   }
 
   bool
   IsEvalAllowed() const
   {
-    return mEvalAllowed;
+    return mLoadInfo.mEvalAllowed;
   }
 
   void
   SetEvalAllowed(bool aEvalAllowed)
   {
-    mEvalAllowed = aEvalAllowed;
+    mLoadInfo.mEvalAllowed = aEvalAllowed;
   }
 
   bool
   GetReportCSPViolations() const
   {
-    return mReportCSPViolations;
+    return mLoadInfo.mReportCSPViolations;
+  }
+
+  bool
+  XHRParamsAllowed() const
+  {
+    return mLoadInfo.mXHRParamsAllowed;
+  }
+
+  void
+  SetXHRParamsAllowed(bool aAllowed)
+  {
+    mLoadInfo.mXHRParamsAllowed = aAllowed;
   }
 
   LocationInfo&
   GetLocationInfo()
   {
     return mLocationInfo;
   }
 
@@ -585,35 +667,68 @@ public:
   }
 
   bool
   IsChromeWorker() const
   {
     return mIsChromeWorker;
   }
 
+  bool
+  IsSharedWorker() const
+  {
+    return mIsSharedWorker;
+  }
+
+  const nsString&
+  SharedWorkerName() const
+  {
+    return mSharedWorkerName;
+  }
+
+  uint64_t
+  NextMessagePortSerial()
+  {
+    AssertIsOnMainThread();
+    return mMessagePortSerial++;
+  }
+
+  void
+  GetAllSharedWorkers(nsTArray<nsRefPtr<SharedWorker> >& aSharedWorkers);
+
+  void
+  CloseSharedWorkersForWindow(nsPIDOMWindow* aWindow);
+
+  void
+  RegisterHostObjectURI(const nsACString& aURI);
+
+  void
+  UnregisterHostObjectURI(const nsACString& aURI);
+
+  void
+  StealHostObjectURIs(nsTArray<nsCString>& aArray);
+
+  virtual JSObject*
+  WrapObject(JSContext* aCx, JS::HandleObject aScope) MOZ_OVERRIDE;
+
 #ifdef DEBUG
   void
   AssertIsOnParentThread() const;
 
   void
   AssertInnerWindowIsCorrect() const;
 #else
   void
   AssertIsOnParentThread() const
   { }
 
   void
   AssertInnerWindowIsCorrect() const
   { }
 #endif
-
-  void RegisterHostObjectURI(const nsACString& aURI);
-  void UnregisterHostObjectURI(const nsACString& aURI);
-  void StealHostObjectURIs(nsTArray<nsCString>& aArray);
 };
 
 class WorkerPrivate : public WorkerPrivateParent<WorkerPrivate>
 {
   friend class WorkerPrivateParent<WorkerPrivate>;
   typedef WorkerPrivateParent<WorkerPrivate> ParentType;
 
   struct TimeoutInfo;
@@ -653,39 +768,47 @@ class WorkerPrivate : public WorkerPriva
   // Things touched on worker thread only.
   nsTArray<ParentType*> mChildWorkers;
   nsTArray<WorkerFeature*> mFeatures;
   nsTArray<nsAutoPtr<TimeoutInfo> > mTimeouts;
 
   nsCOMPtr<nsITimer> mTimer;
   nsRefPtr<MemoryReporter> mMemoryReporter;
 
+  nsDataHashtable<nsUint64HashKey, WorkerMessagePort*> mWorkerPorts;
+
   mozilla::TimeStamp mKillTime;
   uint32_t mErrorHandlerRecursionCount;
   uint32_t mNextTimeoutId;
   Status mStatus;
   bool mSuspended;
   bool mTimerRunning;
   bool mRunningExpiredTimeouts;
   bool mCloseHandlerStarted;
   bool mCloseHandlerFinished;
   bool mMemoryReporterRunning;
   bool mBlockedForMemoryReporter;
-  bool mXHRParamsAllowed;
 
 #ifdef DEBUG
   nsCOMPtr<nsIThread> mThread;
 #endif
 
 public:
   ~WorkerPrivate();
 
   static already_AddRefed<WorkerPrivate>
-  Create(JSContext* aCx, JS::Handle<JSObject*> aObj, WorkerPrivate* aParent,
-         JS::Handle<JSString*> aScriptURL, bool aIsChromeWorker);
+  Create(JSContext* aCx, JS::HandleObject aObject, WorkerPrivate* aParent,
+         const nsAString& aScriptURL, bool aIsChromeWorker,
+         bool aIsSharedWorker, const nsAString& aSharedWorkerName,
+         LoadInfo* aLoadInfo = nullptr);
+
+  static nsresult
+  GetLoadInfo(JSContext* aCx, nsPIDOMWindow* aWindow, WorkerPrivate* aParent,
+              const nsAString& aScriptURL, bool aIsChromeWorker,
+              LoadInfo* aLoadInfo);
 
   void
   DoRunLoop(JSContext* aCx);
 
   bool
   OperationCallback(JSContext* aCx);
 
   bool
@@ -759,18 +882,30 @@ public:
 
   void
   StopSyncLoop(uint32_t aSyncLoopKey, bool aSyncResult);
 
   void
   DestroySyncLoop(uint32_t aSyncLoopKey);
 
   bool
-  PostMessageToParent(JSContext* aCx, JS::Handle<JS::Value> aMessage,
-                      JS::Handle<JS::Value> transferable);
+  PostMessageToParent(JSContext* aCx,
+                      JS::Handle<JS::Value> aMessage,
+                      JS::Handle<JS::Value> aTransferable)
+  {
+    return PostMessageToParentInternal(aCx, aMessage, aTransferable, false, 0);
+  }
+
+  void
+  PostMessageToParentMessagePort(
+                           JSContext* aCx,
+                           uint64_t aMessagePortSerial,
+                           JS::Handle<JS::Value> aMessage,
+                           const Optional<Sequence<JS::Value > >& aTransferable,
+                           ErrorResult& aRv);
 
   bool
   NotifyInternal(JSContext* aCx, Status aStatus);
 
   void
   ReportError(JSContext* aCx, const char* aMessage, JSErrorReport* aReport);
 
   bool
@@ -807,28 +942,16 @@ public:
   UpdateJSWorkerMemoryParameterInternal(JSContext* aCx, JSGCParamKey key, uint32_t aValue);
 
   void
   ScheduleDeletion(bool aWasPending);
 
   bool
   BlockAndCollectRuntimeStats(JS::RuntimeStats* aRtStats);
 
-  bool
-  XHRParamsAllowed() const
-  {
-    return mXHRParamsAllowed;
-  }
-
-  void
-  SetXHRParamsAllowed(bool aAllowed)
-  {
-    mXHRParamsAllowed = aAllowed;
-  }
-
 #ifdef JS_GC_ZEAL
   void
   UpdateGCZealInternal(JSContext* aCx, uint8_t aGCZeal, uint32_t aFrequency);
 #endif
 
   void
   UpdateJITHardeningInternal(JSContext* aCx, bool aJITHardening);
 
@@ -880,26 +1003,30 @@ public:
   void
   EndCTypesCallback()
   {
     // If a callback is ending then we need to do the exact same thing as
     // when a ctypes call begins.
     BeginCTypesCall();
   }
 
+  bool
+  ConnectMessagePort(JSContext* aCx, uint64_t aMessagePortSerial);
+
+  void
+  DisconnectMessagePort(uint64_t aMessagePortSerial);
+
+  WorkerMessagePort*
+  GetMessagePort(uint64_t aMessagePortSerial);
+
 private:
-  WorkerPrivate(JSContext* aCx, JS::Handle<JSObject*> aObject, WorkerPrivate* aParent,
-                JSContext* aParentJSContext, const nsAString& aScriptURL,
-                bool aIsChromeWorker, const nsACString& aDomain,
-                nsCOMPtr<nsPIDOMWindow>& aWindow,
-                nsCOMPtr<nsIScriptContext>& aScriptContext,
-                nsCOMPtr<nsIURI>& aBaseURI, nsCOMPtr<nsIPrincipal>& aPrincipal,
-                nsCOMPtr<nsIChannel>& aChannel,
-                nsCOMPtr<nsIContentSecurityPolicy>& aCSP, bool aEvalAllowed,
-                bool aReportCSPViolations, bool aXHRParamsAllowed);
+  WorkerPrivate(JSContext* aCx, JS::HandleObject aObject,
+                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
@@ -955,18 +1082,27 @@ private:
   EnableMemoryReporter();
 
   void
   DisableMemoryReporter();
 
   void
   WaitForWorkerEvents(PRIntervalTime interval = PR_INTERVAL_NO_TIMEOUT);
 
-  static bool
-  CheckXHRParamsAllowed(nsPIDOMWindow* aWindow);
+  static PLDHashOperator
+  TraceMessagePorts(const uint64_t& aKey,
+                    WorkerMessagePort* aData,
+                    void* aUserArg);
+
+  bool
+  PostMessageToParentInternal(JSContext* aCx,
+                              JS::Handle<JS::Value> aMessage,
+                              JS::Handle<JS::Value> aTransferable,
+                              bool aToMessagePort,
+                              uint64_t aMessagePortSerial);
 };
 
 WorkerPrivate*
 GetWorkerPrivateFromContext(JSContext* aCx);
 
 bool
 IsCurrentThreadRunningChromeWorker();
 
--- a/dom/workers/WorkerScope.cpp
+++ b/dom/workers/WorkerScope.cpp
@@ -136,17 +136,18 @@ protected:
     mWorker->AssertIsOnWorkerThread();
     return GetJSObject();
   }
 
   virtual void
   _trace(JSTracer* aTrc) MOZ_OVERRIDE
   {
     for (int32_t i = 0; i < SLOT_COUNT; i++) {
-      JS_CallHeapValueTracer(aTrc, &mSlots[i], "WorkerGlobalScope instance slot");
+      JS_CallHeapValueTracer(aTrc, &mSlots[i],
+                             "WorkerGlobalScope instance slot");
     }
     mWorker->TraceInternal(aTrc);
     EventTarget::_trace(aTrc);
   }
 
   virtual void
   _finalize(JSFreeOp* aFop) MOZ_OVERRIDE
   {
@@ -291,18 +292,20 @@ private:
   UnwrapErrorEvent(JSContext* aCx, unsigned aArgc, jsval* aVp)
   {
     JS_ASSERT(aArgc == 1);
     JS_ASSERT((JS_ARGV(aCx, aVp)[0]).isObject());
 
     JSObject* wrapper = &JS_CALLEE(aCx, aVp).toObject();
     JS_ASSERT(JS_ObjectIsFunction(aCx, wrapper));
 
-    JS::Rooted<JS::Value> scope(aCx, js::GetFunctionNativeReserved(wrapper, SLOT_wrappedScope));
-    JS::Rooted<JS::Value> listener(aCx, js::GetFunctionNativeReserved(wrapper, SLOT_wrappedFunction));
+    JS::Rooted<JS::Value> scope(aCx,
+      js::GetFunctionNativeReserved(wrapper, SLOT_wrappedScope));
+    JS::Rooted<JS::Value> listener(aCx,
+      js::GetFunctionNativeReserved(wrapper, SLOT_wrappedFunction));
 
     JS_ASSERT(scope.isObject());
 
     JS::Rooted<JSObject*> event(aCx, &JS_ARGV(aCx, aVp)[0].toObject());
 
     jsval argv[3] = { JSVAL_VOID, JSVAL_VOID, JSVAL_VOID };
     JS::AutoArrayRooter rootedArgv(aCx, ArrayLength(argv), argv);
     if (!JS_GetProperty(aCx, event, "message", rootedArgv.handleAt(0)) ||
@@ -314,34 +317,35 @@ private:
     JS::Rooted<JS::Value> rval(aCx, JS::UndefinedValue());
     if (!JS_CallFunctionValue(aCx, JSVAL_TO_OBJECT(scope), listener,
                               ArrayLength(argv), argv, rval.address())) {
       JS_ReportPendingException(aCx);
       return false;
     }
 
     if (JSVAL_IS_BOOLEAN(rval) && JSVAL_TO_BOOLEAN(rval) &&
-        !JS_CallFunctionName(aCx, event, "preventDefault", 0, NULL, rval.address())) {
+        !JS_CallFunctionName(aCx, event, "preventDefault", 0, NULL,
+                             rval.address())) {
       return false;
     }
 
     return true;
   }
 
   static bool
   GetOnErrorListenerImpl(JSContext* aCx, JS::CallArgs aArgs)
   {
     const char* name = sEventStrings[STRING_onerror];
     WorkerGlobalScope* scope = GetInstancePrivate(aCx, &aArgs.thisv().toObject(), name);
     MOZ_ASSERT(scope);
 
     ErrorResult rv;
+
     nsRefPtr<EventHandlerNonNull> adaptor =
       scope->GetEventListener(NS_ConvertASCIItoUTF16(name + 2), rv);
-
     if (rv.Failed()) {
       JS_ReportError(aCx, "Failed to get event listener!");
       return false;
     }
 
     if (!adaptor) {
       aArgs.rval().setNull();
       return true;
@@ -440,17 +444,17 @@ private:
   {
     JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp);
     return JS::CallNonGenericMethod<IsWorkerGlobalScope, GetNavigatorImpl>(aCx, args);
   }
 
   static bool
   Close(JSContext* aCx, unsigned aArgc, jsval* aVp)
   {
-    JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
+    JS::Rooted<JSObject*> obj(aCx, JS_THIS_OBJECT(aCx, aVp));
     if (!obj) {
       return false;
     }
 
     WorkerGlobalScope* scope = GetInstancePrivate(aCx, obj, sFunctions[0].name);
     if (!scope) {
       return false;
     }
@@ -461,17 +465,17 @@ private:
 
     JS_RVAL(aCx, aVp).setUndefined();
     return true;
   }
 
   static bool
   ImportScripts(JSContext* aCx, unsigned aArgc, jsval* aVp)
   {
-    JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
+    JS::Rooted<JSObject*> obj(aCx, JS_THIS_OBJECT(aCx, aVp));
     if (!obj) {
       return false;
     }
 
     WorkerGlobalScope* scope = GetInstancePrivate(aCx, obj, sFunctions[1].name);
     if (!scope) {
       return false;
     }
@@ -482,38 +486,39 @@ private:
 
     JS_RVAL(aCx, aVp).setUndefined();
     return true;
   }
 
   static bool
   SetTimeout(JSContext* aCx, unsigned aArgc, jsval* aVp)
   {
-    JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
+    JS::Rooted<JSObject*> obj(aCx, JS_THIS_OBJECT(aCx, aVp));
     if (!obj) {
       return false;
     }
 
     WorkerGlobalScope* scope = GetInstancePrivate(aCx, obj, sFunctions[2].name);
     if (!scope) {
       return false;
     }
 
     JS::Rooted<JS::Value> dummy(aCx);
-    if (!JS_ConvertArguments(aCx, aArgc, JS_ARGV(aCx, aVp), "v", dummy.address())) {
+    if (!JS_ConvertArguments(aCx, aArgc, JS_ARGV(aCx, aVp), "v",
+                             dummy.address())) {
       return false;
     }
 
     return scope->mWorker->SetTimeout(aCx, aArgc, aVp, false);
   }
 
   static bool
   ClearTimeout(JSContext* aCx, unsigned aArgc, jsval* aVp)
   {
-    JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
+    JS::Rooted<JSObject*> obj(aCx, JS_THIS_OBJECT(aCx, aVp));
     if (!obj) {
       return false;
     }
 
     WorkerGlobalScope* scope = GetInstancePrivate(aCx, obj, sFunctions[3].name);
     if (!scope) {
       return false;
     }
@@ -529,38 +534,39 @@ private:
 
     JS_RVAL(aCx, aVp).setUndefined();
     return true;
   }
 
   static bool
   SetInterval(JSContext* aCx, unsigned aArgc, jsval* aVp)
   {
-    JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
+    JS::Rooted<JSObject*> obj(aCx, JS_THIS_OBJECT(aCx, aVp));
     if (!obj) {
       return false;
     }
 
     WorkerGlobalScope* scope = GetInstancePrivate(aCx, obj, sFunctions[4].name);
     if (!scope) {
       return false;
     }
 
     JS::Rooted<JS::Value> dummy(aCx);
-    if (!JS_ConvertArguments(aCx, aArgc, JS_ARGV(aCx, aVp), "v", dummy.address())) {
+    if (!JS_ConvertArguments(aCx, aArgc, JS_ARGV(aCx, aVp), "v",
+                             dummy.address())) {
       return false;
     }
 
     return scope->mWorker->SetTimeout(aCx, aArgc, aVp, true);
   }
 
   static bool
   ClearInterval(JSContext* aCx, unsigned aArgc, jsval* aVp)
   {
-    JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
+    JS::Rooted<JSObject*> obj(aCx, JS_THIS_OBJECT(aCx, aVp));
     if (!obj) {
       return false;
     }
 
     WorkerGlobalScope* scope = GetInstancePrivate(aCx, obj, sFunctions[5].name);
     if (!scope) {
       return false;
     }
@@ -576,17 +582,17 @@ private:
 
     JS_RVAL(aCx, aVp).setUndefined();
     return true;
   }
 
   static bool
   Dump(JSContext* aCx, unsigned aArgc, jsval* aVp)
   {
-    JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
+    JS::Rooted<JSObject*> obj(aCx, JS_THIS_OBJECT(aCx, aVp));
     if (!obj) {
       return false;
     }
 
     if (!GetInstancePrivate(aCx, obj, sFunctions[6].name)) {
       return false;
     }
 
@@ -610,53 +616,55 @@ private:
 
     JS_RVAL(aCx, aVp).setUndefined();
     return true;
   }
 
   static bool
   AtoB(JSContext* aCx, unsigned aArgc, jsval* aVp)
   {
-    JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
+    JS::Rooted<JSObject*> obj(aCx, JS_THIS_OBJECT(aCx, aVp));
     if (!obj) {
       return false;
     }
 
     if (!GetInstancePrivate(aCx, obj, sFunctions[7].name)) {
       return false;
     }
 
     JS::Rooted<JS::Value> string(aCx);
-    if (!JS_ConvertArguments(aCx, aArgc, JS_ARGV(aCx, aVp), "v", string.address())) {
+    if (!JS_ConvertArguments(aCx, aArgc, JS_ARGV(aCx, aVp), "v",
+                             string.address())) {
       return false;
     }
 
     JS::Rooted<JS::Value> result(aCx);
     if (!xpc::Base64Decode(aCx, string, result.address())) {
       return false;
     }
 
     JS_SET_RVAL(aCx, aVp, result);
     return true;
   }
 
   static bool
   BtoA(JSContext* aCx, unsigned aArgc, jsval* aVp)
   {
-    JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
+    JS::Rooted<JSObject*> obj(aCx, JS_THIS_OBJECT(aCx, aVp));
     if (!obj) {
       return false;
     }
 
     if (!GetInstancePrivate(aCx, obj, sFunctions[8].name)) {
       return false;
     }
 
     JS::Rooted<JS::Value> binary(aCx);
-    if (!JS_ConvertArguments(aCx, aArgc, JS_ARGV(aCx, aVp), "v", binary.address())) {
+    if (!JS_ConvertArguments(aCx, aArgc, JS_ARGV(aCx, aVp), "v",
+                             binary.address())) {
       return false;
     }
 
     JS::Rooted<JS::Value> result(aCx);
     if (!xpc::Base64Encode(aCx, binary, result.address())) {
       return false;
     }
 
@@ -671,18 +679,18 @@ NS_IMPL_RELEASE_INHERITED(WorkerGlobalSc
 NS_INTERFACE_MAP_BEGIN(WorkerGlobalScope)
   NS_INTERFACE_MAP_ENTRY(nsIGlobalObject)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, DOMBindingBase)
 NS_INTERFACE_MAP_END
 
 const JSClass WorkerGlobalScope::sClass = {
   "WorkerGlobalScope",
   0,
-  JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
-  JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub
+  JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub,
+  JS_StrictPropertyStub, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub
 };
 
 const JSPropertySpec WorkerGlobalScope::sProperties[] = {
   JS_PSGS("location", GetLocation, GetterOnlyJSNative, JSPROP_ENUMERATE),
   JS_PSGS(sEventStrings[STRING_onerror], GetOnErrorListener, SetOnErrorListener,
           JSPROP_ENUMERATE),
   JS_PSGS(sEventStrings[STRING_onclose], GetOnClose, SetOnClose,
           JSPROP_ENUMERATE),
@@ -742,22 +750,23 @@ public:
   DOMClassStruct()
   {
     return &sClass.mClass;
   }
 
   static JSObject*
   InitClass(JSContext* aCx, JSObject* aObj, JSObject* aParentProto)
   {
-    JSObject* proto =
+    JS::Rooted<JSObject*> proto(aCx,
       JS_InitClass(aCx, aObj, aParentProto, ProtoClass(), Construct, 0,
-                   sProperties, sFunctions, NULL, NULL);
+                   sProperties, sFunctions, NULL, NULL));
     if (proto) {
+      void* domClass = const_cast<DOMClass *>(DOMClassStruct());
       js::SetReservedSlot(proto, DOM_PROTO_INSTANCE_CLASS_SLOT,
-                          JS::PrivateValue(const_cast<DOMClass *>(DOMClassStruct())));
+                          JS::PrivateValue(domClass));
     }
     return proto;
   }
 
   static bool
   InitPrivate(JSContext* aCx, JSObject* aObj, WorkerPrivate* aWorkerPrivate)
   {
     JS_ASSERT(JS_GetClass(aObj) == Class());
@@ -802,19 +811,19 @@ private:
   GetOnMessageImpl(JSContext* aCx, JS::CallArgs aArgs)
   {
     const char* name = sEventStrings[STRING_onmessage];
     DedicatedWorkerGlobalScope* scope =
       GetInstancePrivate(aCx, &aArgs.thisv().toObject(), name);
     MOZ_ASSERT(scope);
 
     ErrorResult rv;
+
     nsRefPtr<EventHandlerNonNull> handler =
       scope->GetEventListener(NS_ConvertASCIItoUTF16(name + 2), rv);
-
     if (rv.Failed()) {
       JS_ReportError(aCx, "Failed to get event listener!");
       return false;
     }
 
     if (!handler) {
       aArgs.rval().setNull();
     } else {
@@ -847,18 +856,17 @@ private:
 
     JS::Rooted<JSObject*> listenerObj(aCx, aArgs[0].toObjectOrNull());
     nsRefPtr<EventHandlerNonNull> handler;
     if (listenerObj && JS_ObjectIsCallable(aCx, listenerObj)) {
       handler = new EventHandlerNonNull(listenerObj);
     } else {
       handler = nullptr;
     }
-    scope->SetEventListener(NS_ConvertASCIItoUTF16(name + 2),
-                            handler, rv);
+    scope->SetEventListener(NS_ConvertASCIItoUTF16(name + 2), handler, rv);
 
     if (rv.Failed()) {
       JS_ReportError(aCx, "Failed to set event listener!");
       return false;
     }
 
     aArgs.rval().setUndefined();
     return true;
@@ -889,18 +897,18 @@ private:
   Construct(JSContext* aCx, unsigned aArgc, jsval* aVp)
   {
     JS_ReportErrorNumber(aCx, js_GetErrorMessage, NULL, JSMSG_WRONG_CONSTRUCTOR,
                          Class()->name);
     return false;
   }
 
   static bool
-  Resolve(JSContext* aCx, JS::Handle<JSObject*> aObj, JS::Handle<jsid> aId, unsigned aFlags,
-          JS::MutableHandle<JSObject*> aObjp)
+  Resolve(JSContext* aCx, JS::Handle<JSObject*> aObj, JS::Handle<jsid> aId,
+          unsigned aFlags, JS::MutableHandle<JSObject*> aObjp)
   {
     bool resolved;
     if (!JS_ResolveStandardClass(aCx, aObj, aId, &resolved)) {
       return false;
     }
 
     aObjp.set(resolved ? aObj.get() : NULL);
     return true;
@@ -928,17 +936,17 @@ private:
       TraceProtoAndIfaceCache(aTrc, aObj);
       scope->_trace(aTrc);
     }
   }
 
   static bool
   PostMessage(JSContext* aCx, unsigned aArgc, jsval* aVp)
   {
-    JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
+    JS::Rooted<JSObject*> obj(aCx, JS_THIS_OBJECT(aCx, aVp));
     if (!obj) {
       return false;
     }
 
     const char* name = sFunctions[0].name;
     DedicatedWorkerGlobalScope* scope = GetInstancePrivate(aCx, obj, name);
     if (!scope) {
       return false;
@@ -962,19 +970,20 @@ private:
 
 const DOMJSClass DedicatedWorkerGlobalScope::sClass = {
   {
     // We don't have to worry about Xray expando slots here because we'll never
     // have an Xray wrapper to a worker global scope.
     "DedicatedWorkerGlobalScope",
     JSCLASS_DOM_GLOBAL | JSCLASS_IS_DOMJSCLASS | JSCLASS_IMPLEMENTS_BARRIERS |
     JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(DOM_GLOBAL_SLOTS) | JSCLASS_NEW_RESOLVE,
-    JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
-    JS_EnumerateStub, reinterpret_cast<JSResolveOp>(Resolve), JS_ConvertStub,
-    Finalize, NULL, NULL, NULL, NULL, Trace
+    JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub,
+    JS_StrictPropertyStub, JS_EnumerateStub,
+    reinterpret_cast<JSResolveOp>(Resolve), JS_ConvertStub, Finalize, nullptr,
+    nullptr, nullptr, nullptr, Trace
   },
   {
     INTERFACE_CHAIN_1(prototypes::id::EventTarget_workers),
     false,
     &sWorkerNativePropertyHooks
   }
 };
 
@@ -1018,142 +1027,472 @@ const JSFunctionSpec DedicatedWorkerGlob
   JS_FN("postMessage", PostMessage, 1, FUNCTION_FLAGS),
   JS_FS_END
 };
 
 const char* const DedicatedWorkerGlobalScope::sEventStrings[STRING_COUNT] = {
   "onmessage",
 };
 
+class SharedWorkerGlobalScope : public WorkerGlobalScope
+{
+  static DOMJSClass sClass;
+  static DOMIfaceAndProtoJSClass sProtoClass;
+  static const JSPropertySpec sProperties[];
+
+  enum
+  {
+    STRING_onconnect = 0,
+
+    STRING_COUNT
+  };
+
+  static const char* const sEventStrings[STRING_COUNT];
+
+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)
+  {
+    JS::Rooted<JSObject*> proto(aCx,
+      JS_InitClass(aCx, aObj, aParentProto, ProtoClass(), Construct, 0,
+                   sProperties, nullptr, nullptr, nullptr));
+    if (proto) {
+      void* domClass = const_cast<DOMClass *>(DOMClassStruct());
+      js::SetReservedSlot(proto, DOM_PROTO_INSTANCE_CLASS_SLOT,
+                          JS::PrivateValue(domClass));
+    }
+    return proto;
+  }
+
+  static bool
+  InitPrivate(JSContext* aCx, JSObject* aObj, WorkerPrivate* aWorkerPrivate)
+  {
+    MOZ_ASSERT(JS_GetClass(aObj) == Class());
+
+    dom::AllocateProtoAndIfaceCache(aObj);
+
+    nsRefPtr<SharedWorkerGlobalScope> scope =
+      new SharedWorkerGlobalScope(aCx, aWorkerPrivate);
+
+    js::SetReservedSlot(aObj, DOM_OBJECT_SLOT, PRIVATE_TO_JSVAL(scope));
+
+    scope->SetIsDOMBinding();
+    scope->SetWrapper(aObj);
+
+    scope.forget();
+    return true;
+  }
+
+protected:
+  SharedWorkerGlobalScope(JSContext* aCx, WorkerPrivate* aWorker)
+  : WorkerGlobalScope(aCx, aWorker)
+  {
+    MOZ_COUNT_CTOR(mozilla::dom::workers::SharedWorkerGlobalScope);
+  }
+
+  ~SharedWorkerGlobalScope()
+  {
+    MOZ_COUNT_DTOR(mozilla::dom::workers::SharedWorkerGlobalScope);
+  }
+
+private:
+  using EventTarget::GetEventListener;
+  using EventTarget::SetEventListener;
+
+  static bool
+  IsSharedWorkerGlobalScope(JS::Handle<JS::Value> aVal)
+  {
+    return aVal.isObject() && JS_GetClass(&aVal.toObject()) == Class();
+  }
+
+  static bool
+  GetOnconnectImpl(JSContext* aCx, JS::CallArgs aArgs)
+  {
+    auto name = sEventStrings[STRING_onconnect];
+
+    auto scope = GetInstancePrivate(aCx, &aArgs.thisv().toObject(), name);
+    MOZ_ASSERT(scope);
+
+    ErrorResult rv;
+
+    nsRefPtr<EventHandlerNonNull> handler =
+      scope->GetEventListener(NS_ConvertASCIItoUTF16(name + 2), rv);
+    if (rv.Failed()) {
+      JS_ReportError(aCx, "Failed to get event listener!");
+      return false;
+    }
+
+    if (!handler) {
+      aArgs.rval().setNull();
+    } else {
+      aArgs.rval().setObject(*handler->Callable());
+    }
+
+    return true;
+  }
+
+  static bool
+  GetOnconnect(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
+  {
+    auto args = JS::CallArgsFromVp(aArgc, aVp);
+    return JS::CallNonGenericMethod<IsSharedWorkerGlobalScope,
+                                    GetOnconnectImpl>(aCx, args);
+  }
+
+  static bool
+  SetOnconnectImpl(JSContext* aCx, JS::CallArgs aArgs)
+  {
+    auto name = sEventStrings[STRING_onconnect];
+
+    auto scope = GetInstancePrivate(aCx, &aArgs.thisv().toObject(), name);
+    MOZ_ASSERT(scope);
+
+    if (aArgs.length() == 0 || !aArgs[0].isObject()) {
+      JS_ReportError(aCx, "Not an event listener!");
+      return false;
+    }
+
+
+    ErrorResult rv;
+
+    JS::Rooted<JSObject*> listenerObj(aCx, aArgs[0].toObjectOrNull());
+    nsRefPtr<EventHandlerNonNull> handler;
+    if (listenerObj && JS_ObjectIsCallable(aCx, listenerObj)) {
+      handler = new EventHandlerNonNull(listenerObj);
+    } else {
+      handler = nullptr;
+    }
+    scope->SetEventListener(NS_ConvertASCIItoUTF16(name + 2), handler, rv);
+
+    if (rv.Failed()) {
+      JS_ReportError(aCx, "Failed to set event listener!");
+      return false;
+    }
+
+    aArgs.rval().setUndefined();
+    return true;
+  }
+
+  static bool
+  SetOnconnect(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
+  {
+    auto args = JS::CallArgsFromVp(aArgc, aVp);
+    return JS::CallNonGenericMethod<IsSharedWorkerGlobalScope,
+                                    SetOnconnectImpl>(aCx, args);
+  }
+
+  static bool
+  GetNameImpl(JSContext* aCx, JS::CallArgs aArgs)
+  {
+    auto scope = GetInstancePrivate(aCx, &aArgs.thisv().toObject(), "name");
+    MOZ_ASSERT(scope);
+
+    auto name = scope->mWorker->SharedWorkerName();
+    MOZ_ASSERT(!name.IsVoid());
+
+    JS::Rooted<JSString*> nameStr(aCx,
+      JS_InternUCStringN(aCx, name.get(), name.Length()));
+    if (!nameStr) {
+      return false;
+    }
+
+    aArgs.rval().setString(nameStr);
+    return true;
+  }
+
+  static bool
+  GetName(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
+  {
+    auto args = JS::CallArgsFromVp(aArgc, aVp);
+    return JS::CallNonGenericMethod<IsSharedWorkerGlobalScope,
+                                    GetNameImpl>(aCx, args);
+  }
+
+
+  static SharedWorkerGlobalScope*
+  GetInstancePrivate(JSContext* aCx, JSObject* aObj, const char* aFunctionName)
+  {
+    const JSClass* classPtr = JS_GetClass(aObj);
+    if (classPtr == Class()) {
+      return UnwrapDOMObject<SharedWorkerGlobalScope>(aObj);
+    }
+
+    JS_ReportErrorNumber(aCx, js_GetErrorMessage, nullptr,
+                         JSMSG_INCOMPATIBLE_PROTO, Class()->name, aFunctionName,
+                         classPtr->name);
+    return nullptr;
+  }
+
+  static bool
+  Construct(JSContext* aCx, unsigned aArgc, jsval* aVp)
+  {
+    JS_ReportErrorNumber(aCx, js_GetErrorMessage, nullptr,
+                         JSMSG_WRONG_CONSTRUCTOR, Class()->name);
+    return false;
+  }
+
+  static bool
+  Resolve(JSContext* aCx, JS::Handle<JSObject*> aObj, JS::Handle<jsid> aId,
+          unsigned aFlags, JS::MutableHandle<JSObject*> aObjp)
+  {
+    bool resolved;
+    if (!JS_ResolveStandardClass(aCx, aObj, aId, &resolved)) {
+      return false;
+    }
+
+    aObjp.set(resolved ? aObj.get() : nullptr);
+    return true;
+  }
+
+  static void
+  Finalize(JSFreeOp* aFop, JSObject* aObj)
+  {
+    MOZ_ASSERT(JS_GetClass(aObj) == Class());
+    SharedWorkerGlobalScope* scope =
+      UnwrapDOMObject<SharedWorkerGlobalScope>(aObj);
+    if (scope) {
+      DestroyProtoAndIfaceCache(aObj);
+      scope->_finalize(aFop);
+    }
+  }
+
+  static void
+  Trace(JSTracer* aTrc, JSObject* aObj)
+  {
+    MOZ_ASSERT(JS_GetClass(aObj) == Class());
+    SharedWorkerGlobalScope* scope =
+      UnwrapDOMObject<SharedWorkerGlobalScope>(aObj);
+    if (scope) {
+      TraceProtoAndIfaceCache(aTrc, aObj);
+      scope->_trace(aTrc);
+    }
+  }
+};
+
+DOMJSClass SharedWorkerGlobalScope::sClass = {
+  {
+    // We don't have to worry about Xray expando slots here because we'll never
+    // have an Xray wrapper to a worker global scope.
+    "SharedWorkerGlobalScope",
+    JSCLASS_DOM_GLOBAL | JSCLASS_IS_DOMJSCLASS | JSCLASS_IMPLEMENTS_BARRIERS |
+    JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(DOM_GLOBAL_SLOTS) | JSCLASS_NEW_RESOLVE,
+    JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub,
+    JS_StrictPropertyStub, JS_EnumerateStub,
+    reinterpret_cast<JSResolveOp>(Resolve), JS_ConvertStub, Finalize, nullptr,
+    nullptr, nullptr, nullptr, Trace
+  },
+  {
+    INTERFACE_CHAIN_1(prototypes::id::EventTarget_workers),
+    false,
+    &sWorkerNativePropertyHooks
+  }
+};
+
+DOMIfaceAndProtoJSClass SharedWorkerGlobalScope::sProtoClass = {
+  {
+    // XXXbz we use "SharedWorkerGlobalScope" here to match sClass
+    // so that we can JS_InitClass this JSClass and then
+    // call JS_NewObject with our sClass and have it find the right
+    // prototype.
+    "SharedWorkerGlobalScope",
+    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 SharedWorkerGlobalScope]",
+  prototypes::id::_ID_Count,
+  0
+};
+
+const JSPropertySpec SharedWorkerGlobalScope::sProperties[] = {
+  JS_PSGS(sEventStrings[STRING_onconnect], GetOnconnect, SetOnconnect,
+          JSPROP_ENUMERATE),
+  JS_PSGS("name", GetName, GetterOnlyJSNative, JSPROP_ENUMERATE),
+  JS_PS_END
+};
+
+const char* const SharedWorkerGlobalScope::sEventStrings[STRING_COUNT] = {
+  "onconnect",
+};
+
 WorkerGlobalScope*
 WorkerGlobalScope::GetInstancePrivate(JSContext* aCx, JSObject* aObj,
                                       const char* aFunctionName)
 {
   const JSClass* classPtr = JS_GetClass(aObj);
 
-  // We can only make DedicatedWorkerGlobalScope, not WorkerGlobalScope, so this
-  // should never happen.
-  JS_ASSERT(classPtr != Class());
+  // We can only make [Dedicated|Shared]WorkerGlobalScope, not
+  // WorkerGlobalScope, so this should never happen.
+  MOZ_ASSERT(classPtr != Class());
 
   if (classPtr == DedicatedWorkerGlobalScope::Class()) {
     return UnwrapDOMObject<DedicatedWorkerGlobalScope>(aObj);
   }
 
-  JS_ReportErrorNumber(aCx, js_GetErrorMessage, NULL, JSMSG_INCOMPATIBLE_PROTO,
-                       sClass.name, aFunctionName, classPtr->name);
-  return NULL;
+  if (classPtr == SharedWorkerGlobalScope::Class()) {
+    return UnwrapDOMObject<SharedWorkerGlobalScope>(aObj);
+  }
+
+  JS_ReportErrorNumber(aCx, js_GetErrorMessage, nullptr,
+                       JSMSG_INCOMPATIBLE_PROTO, sClass.name, aFunctionName,
+                       classPtr->name);
+  return nullptr;
 }
 
 bool
-WorkerGlobalScope::IsWorkerGlobalScope(JS::Handle<JS::Value> v)
+WorkerGlobalScope::IsWorkerGlobalScope(JS::Handle<JS::Value> aVal)
 {
-  return v.isObject() && JS_GetClass(&v.toObject()) == DedicatedWorkerGlobalScope::Class();
+  if (!aVal.isObject()) {
+    return false;
+  }
+
+  auto classPtr = JS_GetClass(&aVal.toObject());
+
+  return classPtr == DedicatedWorkerGlobalScope::Class() ||
+         classPtr == SharedWorkerGlobalScope::Class();
 }
 
 } /* anonymous namespace */
 
 BEGIN_WORKERS_NAMESPACE
 
 JSObject*
-CreateDedicatedWorkerGlobalScope(JSContext* aCx)
+CreateGlobalScope(JSContext* aCx)
 {
   using namespace mozilla::dom;
 
   WorkerPrivate* worker = GetWorkerPrivateFromContext(aCx);
-  JS_ASSERT(worker);
+  MOZ_ASSERT(worker);
+
+  const JSClass* classPtr = worker->IsSharedWorker() ?
+                            SharedWorkerGlobalScope::Class() :
+                            DedicatedWorkerGlobalScope::Class();
 
   JS::CompartmentOptions options;
-  if (worker->IsChromeWorker())
+  if (worker->IsChromeWorker()) {
     options.setVersion(JSVERSION_LATEST);
+  }
+
   JS::Rooted<JSObject*> global(aCx,
-    JS_NewGlobalObject(aCx, DedicatedWorkerGlobalScope::Class(),
-                       GetWorkerPrincipal(), JS::DontFireOnNewGlobalHook, options));
+    JS_NewGlobalObject(aCx, classPtr, GetWorkerPrincipal(),
+                       JS::DontFireOnNewGlobalHook, options));
   if (!global) {
-    return NULL;
+    return nullptr;
   }
 
   JSAutoCompartment ac(aCx, global);
 
   // Make the private slots now so that all our instance checks succeed.
-  if (!DedicatedWorkerGlobalScope::InitPrivate(aCx, global, worker)) {
-    return NULL;
+  if (worker->IsSharedWorker()) {
+    if (!SharedWorkerGlobalScope::InitPrivate(aCx, global, worker)) {
+      return nullptr;
+  }
+  } else if (!DedicatedWorkerGlobalScope::InitPrivate(aCx, global, worker)) {
+    return nullptr;
   }
 
   // Proto chain should be:
-  //   global -> DedicatedWorkerGlobalScope
+  //   global -> [Dedicated|Shared]WorkerGlobalScope
   //          -> WorkerGlobalScope
   //          -> EventTarget
   //          -> Object
 
   JS::Rooted<JSObject*> eventTargetProto(aCx,
     EventTargetBinding_workers::GetProtoObject(aCx, global));
   if (!eventTargetProto) {
-    return NULL;
+    return nullptr;
   }
 
   JS::Rooted<JSObject*> scopeProto(aCx,
     WorkerGlobalScope::InitClass(aCx, global, eventTargetProto));
   if (!scopeProto) {
-    return NULL;
-  }
-
-  JS::Rooted<JSObject*> dedicatedScopeProto(aCx,
-    DedicatedWorkerGlobalScope::InitClass(aCx, global, scopeProto));
-  if (!dedicatedScopeProto) {
-    return NULL;
+    return nullptr;
   }
 
-  if (!JS_SetPrototype(aCx, global, dedicatedScopeProto)) {
-    return NULL;
+  JS::Rooted<JSObject*> finalScopeProto(aCx,
+    worker->IsSharedWorker() ?
+    SharedWorkerGlobalScope::InitClass(aCx, global, scopeProto) :
+    DedicatedWorkerGlobalScope::InitClass(aCx, global, scopeProto));
+  if (!finalScopeProto) {
+    return nullptr;
   }
 
-  JSObject* workerProto = worker::InitClass(aCx, global, eventTargetProto,
-                                            false);
+  if (!JS_SetPrototype(aCx, global, finalScopeProto)) {
+    return nullptr;
+  }
+
+  JS::Rooted<JSObject*> workerProto(aCx,
+    worker::InitClass(aCx, global, eventTargetProto, false));
   if (!workerProto) {
-    return NULL;
+    return nullptr;
   }
 
   if (worker->IsChromeWorker()) {
     if (!chromeworker::InitClass(aCx, global, workerProto, false) ||
         !DefineChromeWorkerFunctions(aCx, global) ||
         !DefineOSFileConstants(aCx, global)) {
-      return NULL;
+      return nullptr;
     }
   }
 
   // Init other classes we care about.
   if (!events::InitClasses(aCx, global, false) ||
       !file::InitClasses(aCx, global)) {
-    return NULL;
+    return nullptr;
   }
 
   // Init other paris-bindings.
   if (!DOMExceptionBinding::GetConstructorObject(aCx, global) ||
       !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) ||
       !WorkerLocationBinding_workers::GetConstructorObject(aCx, global) ||
       !WorkerNavigatorBinding_workers::GetConstructorObject(aCx, global)) {
-    return NULL;
+    return nullptr;
   }
 
   if (!JS_DefineProfilingFunctions(aCx, global)) {
-    return NULL;
+    return nullptr;
   }
 
   JS_FireOnNewGlobalObject(aCx, global);
 
   return global;
 }
 
-bool
-ClassIsWorkerGlobalScope(const JSClass* aClass)
-{
-  return WorkerGlobalScope::Class() == aClass ||
-         DedicatedWorkerGlobalScope::Class() == aClass;
-}
-
 END_WORKERS_NAMESPACE
--- a/dom/workers/WorkerScope.h
+++ b/dom/workers/WorkerScope.h
@@ -6,16 +6,13 @@
 #ifndef mozilla_dom_workers_workerscope_h__
 #define mozilla_dom_workers_workerscope_h__
 
 #include "Workers.h"
 
 BEGIN_WORKERS_NAMESPACE
 
 JSObject*
-CreateDedicatedWorkerGlobalScope(JSContext* aCx);
-
-bool
-ClassIsWorkerGlobalScope(const JSClass* aClass);
+CreateGlobalScope(JSContext* aCx);
 
 END_WORKERS_NAMESPACE
 
-#endif /* mozilla_dom_workers_workerscope_h__ */
+#endif /* mozilla_dom_workers_workerscope_h__ */
\ No newline at end of file
--- a/dom/workers/Workers.h
+++ b/dom/workers/Workers.h
@@ -163,23 +163,23 @@ struct JSSettings
 };
 
 // All of these are implemented in RuntimeService.cpp
 bool
 ResolveWorkerClasses(JSContext* aCx, JS::Handle<JSObject*> aObj, JS::Handle<jsid> aId,
                      unsigned aFlags, JS::MutableHandle<JSObject*> aObjp);
 
 void
-CancelWorkersForWindow(JSContext* aCx, nsPIDOMWindow* aWindow);
+CancelWorkersForWindow(nsPIDOMWindow* aWindow);
 
 void
-SuspendWorkersForWindow(JSContext* aCx, nsPIDOMWindow* aWindow);
+SuspendWorkersForWindow(nsPIDOMWindow* aWindow);
 
 void
-ResumeWorkersForWindow(nsIScriptContext* aCx, nsPIDOMWindow* aWindow);
+ResumeWorkersForWindow(nsPIDOMWindow* aWindow);
 
 class WorkerTask {
 public:
     NS_INLINE_DECL_THREADSAFE_REFCOUNTING(WorkerTask)
 
     virtual ~WorkerTask() { }
 
     virtual bool RunTask(JSContext* aCx) = 0;
--- a/dom/workers/moz.build
+++ b/dom/workers/moz.build
@@ -15,39 +15,45 @@ EXPORTS.mozilla.dom.workers += [
 
 # Stuff needed for the bindings, not really public though.
 EXPORTS.mozilla.dom.workers.bindings += [
     'DOMBindingBase.h',
     'EventListenerManager.h',
     'EventTarget.h',
     'FileReaderSync.h',
     'Location.h',
+    'MessagePort.h',
     'Navigator.h',
+    'SharedWorker.h',
     'URL.h',
     'WorkerFeature.h',
+    'WorkerMessagePort.h',
     'XMLHttpRequest.h',
     'XMLHttpRequestEventTarget.h',
     'XMLHttpRequestUpload.h',
 ]
 
 CPP_SOURCES += [
     'ChromeWorkerScope.cpp',
     'DOMBindingBase.cpp',
     'EventListenerManager.cpp',
     'EventTarget.cpp',
     'Events.cpp',
     'File.cpp',
     'FileReaderSync.cpp',
     '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',
 ]
 
 LIBRARY_NAME = 'domworkers_s'
@@ -59,9 +65,9 @@ LIBXUL_LIBRARY = True
 MSVC_ENABLE_PGO = True
 
 LOCAL_INCLUDES += [
     '../base',
     '../system',
     '/content/base/src',
     '/content/events/src',
     '/xpcom/build',
-]
+]
\ No newline at end of file
--- a/dom/workers/test/Makefile.in
+++ b/dom/workers/test/Makefile.in
@@ -3,9 +3,8 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 # Bug 842344, 842386 - Disabled on Windows & OSX for intermittent failures.
 ifeq (,$(filter Darwin WINNT,$(OS_ARCH)))
 MOCHITEST_FILES += \
   test_xhr_timeout.html \
   $(NULL)
 endif
-
--- a/dom/workers/test/mochitest.ini
+++ b/dom/workers/test/mochitest.ini
@@ -17,24 +17,27 @@ support-files =
   importScripts_worker_imported1.js
   importScripts_worker_imported2.js
   importScripts_worker_imported3.js
   importScripts_worker_imported4.js
   instanceof_worker.js
   json_worker.js
   location_worker.js
   longThread_worker.js
+  multi_sharedWorker_frame.html
+  multi_sharedWorker_sharedWorker.js
   navigator_worker.js
   newError_worker.js
   recursion_worker.js
   recursiveOnerror_worker.js
   relativeLoad_import.js
   relativeLoad_worker.js
   relativeLoad_worker2.js
   rvals_worker.js
+  sharedWorker_sharedWorker.js
   simpleThread_worker.js
   suspend_iframe.html
   suspend_worker.js
   terminate_worker.js
   testXHR.txt
   threadErrors_worker1.js
   threadErrors_worker2.js
   threadErrors_worker3.js
@@ -68,24 +71,27 @@ support-files =
 [test_eventDispatch.html]
 [test_fibonacci.html]
 [test_importScripts.html]
 [test_instanceof.html]
 [test_json.html]
 [test_loadError.html]
 [test_location.html]
 [test_longThread.html]
+[test_multi_sharedWorker.html]
+[test_multi_sharedWorker_lifetimes.html]
 [test_navigator.html]
 [test_newError.html]
 [test_recursion.html]
 [test_recursiveOnerror.html]
 [test_relativeLoad.html]
 [test_resolveWorker-assignment.html]
 [test_resolveWorker.html]
 [test_rvals.html]
+[test_sharedWorker.html]
 [test_simpleThread.html]
 [test_suspend.html]
 [test_terminate.html]
 [test_threadErrors.html]
 [test_threadTimeouts.html]
 [test_throwingOnerror.html]
 [test_transferable.html]
 [test_url.html]
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/multi_sharedWorker_frame.html
@@ -0,0 +1,52 @@
+<!--
+  Any copyright is dedicated to the Public Domain.
+  http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+  <head>
+    <title>Test for SharedWorker</title>
+  </head>
+  <body>
+    <script type="text/javascript;version=1.7">
+"use strict";
+
+function debug(message) {
+  if (typeof(message) != "string") {
+    throw new Error("debug() only accepts strings!");
+  }
+  parent.postMessage(message, "*");
+}
+
+let worker;
+
+window.addEventListener("message", function(event) {
+  if (!worker) {
+    worker = new SharedWorker("multi_sharedWorker_sharedWorker.js",
+                              "FrameWorker");
+    worker.onerror = function(event) {
+      debug("Worker error: " + event.message);
+      event.preventDefault();
+
+      let data = {
+        type: "error",
+        message: event.message,
+        filename: event.filename,
+        lineno: event.lineno,
+        isErrorEvent: event instanceof ErrorEvent
+      };
+      parent.postMessage(data, "*");
+    };
+
+    worker.port.onmessage = function(event) {
+      debug("Worker message: " + JSON.stringify(event.data));
+      parent.postMessage(event.data, "*");
+    };
+  }
+
+  debug("Posting message: " + JSON.stringify(event.data));
+  worker.port.postMessage(event.data);
+});
+    </script>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/multi_sharedWorker_sharedWorker.js
@@ -0,0 +1,72 @@
+/**
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+"use strict";
+
+if (self.name != "FrameWorker") {
+  throw new Error("Bad worker name: " + self.name);
+}
+
+var registeredPorts = [];
+var errorCount = 0;
+var storedData;
+
+self.onconnect = function(event) {
+  var port = event.ports[0];
+
+  if (registeredPorts.length) {
+    var data = {
+      type: "connect"
+    };
+
+    registeredPorts.forEach(function(registeredPort) {
+      registeredPort.postMessage(data);
+    });
+  }
+
+  port.onmessage = function(event) {
+    switch (event.data.command) {
+      case "start":
+        break;
+
+      case "error":
+        throw new Error("Expected");
+
+      case "store":
+        storedData = event.data.data;
+        break;
+
+      case "retrieve":
+        var data = {
+          type: "result",
+          data: storedData
+        };
+        port.postMessage(data);
+        break;
+
+      default:
+        throw new Error("Unknown command '" + error.data.command + "'");
+    }
+  };
+
+  registeredPorts.push(port);
+};
+
+self.onerror = function(message, filename, lineno) {
+  if (!errorCount++) {
+    var data = {
+      type: "worker-error",
+      message: message,
+      filename: filename,
+      lineno: lineno
+    };
+
+    registeredPorts.forEach(function (registeredPort) {
+      registeredPort.postMessage(data);
+    });
+
+    // Prevent the error from propagating the first time only.
+    return true;
+  }
+};
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/sharedWorker_sharedWorker.js
@@ -0,0 +1,82 @@
+/**
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+"use strict";
+
+if (!("self" in this)) {
+  throw new Error("No 'self' exists on SharedWorkerGlobalScope!");
+}
+if (this !== self) {
+  throw new Error("'self' not equal to global object!");
+}
+if (!(self instanceof SharedWorkerGlobalScope)) {
+  throw new Error("self not a SharedWorkerGlobalScope instance!");
+}
+
+var propsToCheck = [
+  "location",
+  "navigator",
+  "close",
+  "importScripts",
+  "setTimeout",
+  "clearTimeout",
+  "setInterval",
+  "clearInterval",
+  "dump",
+  "atob",
+  "btoa"
+];
+
+for (var index = 0; index < propsToCheck.length; index++) {
+  var prop = propsToCheck[index];
+  if (!(prop in self)) {
+    throw new Error("SharedWorkerGlobalScope has no '" + prop + "' property!");
+  }
+}
+
+onconnect = function(event) {
+  if (!(event instanceof WorkerMessageEvent)) {
+    throw new Error("'connect' event is not a WorkerMessageEvent!");
+  }
+  if (!("ports" in event)) {
+    throw new Error("'connect' event doesn't have a 'ports' property!");
+  }
+  if (!Array.isArray(event.ports)) {
+    throw new Error("'connect' event has 'ports' property that isn't an " +
+                    "Array!");
+  }
+  if (event.ports.length != 1) {
+    throw new Error("'connect' event has a 'ports' property with length '" +
+                    event.ports.length + "'!");
+  }
+  if (!event.ports[0]) {
+    throw new Error("'connect' event has a null 'ports[0]' property!");
+  }
+  if (!(event.ports[0] instanceof WorkerMessagePort)) {
+    throw new Error("'connect' event has a 'ports[0]' property that isn't a " +
+                    "MessagePort!");
+  }
+  if (event.data) {
+    throw new Error("'connect' event has data: " + event.data);
+  }
+
+  event.ports[0].onmessage = function(event) {
+    if (!(event instanceof WorkerMessageEvent)) {
+      throw new Error("'message' event is not a WorkerMessageEvent!");
+    }
+    if (!("ports" in event)) {
+      throw new Error("'message' event doesn't have a 'ports' property!");
+    }
+    if (!Array.isArray(event.ports)) {
+      throw new Error("'message' event has 'ports' property that isn't an " +
+                      "Array!");
+    }
+    if (event.ports.length) {
+      throw new Error("'message' event has a 'ports' property with length '" +
+                      event.ports.length + "'!");
+    }
+    event.target.postMessage(event.data);
+    throw new Error(event.data);
+  };
+};
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/test_multi_sharedWorker.html
@@ -0,0 +1,251 @@
+<!--
+  Any copyright is dedicated to the Public Domain.
+  http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+  <head>
+    <title>Test for SharedWorker</title>
+    <script src="/tests/SimpleTest/SimpleTest.js"></script>
+    <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css">
+      <script class="testbody" type="text/javascript;version=1.7">
+        "use strict";
+
+        const swPref = "dom.workers.sharedWorkers.enabled";
+
+        const basePath =
+          location.pathname.substring(0,
+                                      location.pathname.lastIndexOf("/") + 1);
+        const baseURL = location.origin + basePath;
+
+        const frameRelativeURL = "multi_sharedWorker_frame.html";
+        const frameAbsoluteURL = baseURL + frameRelativeURL;
+        const workerAbsoluteURL =
+          baseURL + "multi_sharedWorker_sharedWorker.js";
+
+        const storedData = "0123456789abcdefghijklmnopqrstuvwxyz";
+        const errorMessage = "Error: Expected";
+        const errorLineno = 34;
+
+        let testGenerator = (function() {
+          SimpleTest.waitForExplicitFinish();
+
+          ok(!("SharedWorker" in window), "No SharedWorker without pref set");
+          ok(!("WorkerMessagePort" in window),
+             "No WorkerMessagePort without pref set");
+
+          SpecialPowers.pushPrefEnv({ set: [[swPref, true]] }, sendToGenerator);
+          yield undefined;
+
+          window.addEventListener("message", function(event) {
+            if (typeof(event.data) == "string") {
+              info(event.data);
+            } else {
+              sendToGenerator(event);
+            }
+          });
+
+          let frame1 = document.getElementById("frame1");
+          frame1.src = frameRelativeURL;
+          frame1.onload = sendToGenerator;
+
+          yield undefined;
+
+          frame1 = frame1.contentWindow;
+
+          let frame2 = document.getElementById("frame2");
+          frame2.src = frameAbsoluteURL;
+          frame2.onload = sendToGenerator;
+
+          yield undefined;
+
+          frame2 = frame2.contentWindow;
+
+          let data = {
+            command: "start"
+          };
+
+          frame1.postMessage(data, "*");
+          frame2.postMessage(data, "*");
+
+          let event = yield undefined;
+          ok(event instanceof MessageEvent, "Got a MessageEvent");
+          is(event.source, frame1, "First window got the event");
+          is(event.data.type, "connect", "Got a connect message");
+
+          data = {
+            command: "retrieve"
+          };
+          frame1.postMessage(data, "*");
+
+          event = yield undefined;
+          ok(event instanceof MessageEvent, "Got a MessageEvent");
+          is(event.source, frame1, "First window got the event");
+          is(event.data.type, "result", "Got a result message");
+          is(event.data.data, undefined, "No data stored yet");
+
+          frame2.postMessage(data, "*");
+
+          event = yield undefined;
+          ok(event instanceof MessageEvent, "Got a MessageEvent");
+          is(event.source, frame2, "Second window got the event");
+          is(event.data.type, "result", "Got a result message");
+          is(event.data.data, undefined, "No data stored yet");
+
+          data = {
+            command: "store",
+            data: storedData
+          };
+          frame2.postMessage(data, "*");
+
+          data = {
+            command: "retrieve"
+          };
+          frame1.postMessage(data, "*");
+
+          event = yield undefined;
+          ok(event instanceof MessageEvent, "Got a MessageEvent");
+          is(event.source, frame1, "First window got the event");
+          is(event.data.type, "result", "Got a result message");
+          is(event.data.data, storedData, "Got stored data");
+
+          // This will generate two MessageEvents, one for each window.
+          let sawFrame1Error = false;
+          let sawFrame2Error = false;
+
+          data = {
+            command: "error"
+          };
+          frame1.postMessage(data, "*");
+
+          // First event.
+          event = yield undefined;
+
+          ok(event instanceof MessageEvent, "Got a MessageEvent");
+          is(event.data.type, "worker-error", "Got an error message");
+          is(event.data.message, errorMessage, "Got correct error message");
+          is(event.data.filename, workerAbsoluteURL, "Got correct filename");
+          is(event.data.lineno, errorLineno, "Got correct lineno");
+          if (event.source == frame1) {
+            is(sawFrame1Error, false, "Haven't seen error for frame1 yet");
+            sawFrame1Error = true;
+          } else if (event.source == frame2) {
+            is(sawFrame2Error, false, "Haven't seen error for frame1 yet");
+            sawFrame2Error = true;
+          } else {
+            ok(false, "Saw error from unknown window");
+          }
+
+          // Second event
+          event = yield undefined;
+
+          ok(event instanceof MessageEvent, "Got a MessageEvent");
+          is(event.data.type, "worker-error", "Got an error message");
+          is(event.data.message, errorMessage, "Got correct error message");
+          is(event.data.filename, workerAbsoluteURL, "Got correct filename");
+          is(event.data.lineno, errorLineno, "Got correct lineno");
+          if (event.source == frame1) {
+            is(sawFrame1Error, false, "Haven't seen error for frame1 yet");
+            sawFrame1Error = true;
+          } else if (event.source == frame2) {
+            is(sawFrame2Error, false, "Haven't seen error for frame1 yet");
+            sawFrame2Error = true;
+          } else {
+            ok(false, "Saw error from unknown window");
+          }
+
+          is(sawFrame1Error, true, "Saw error for frame1");
+          is(sawFrame2Error, true, "Saw error for frame2");
+
+          // This will generate two MessageEvents, one for each window.
+          sawFrame1Error = false;
+          sawFrame2Error = false;
+
+          data = {
+            command: "error"
+          };
+          frame1.postMessage(data, "*");
+
+          // First event.
+          event = yield undefined;
+
+          ok(event instanceof MessageEvent, "Got a MessageEvent");
+          is(event.data.type, "error", "Got an error message");
+          is(event.data.message, errorMessage, "Got correct error message");
+          is(event.data.filename, workerAbsoluteURL, "Got correct filename");
+          is(event.data.lineno, errorLineno, "Got correct lineno");
+          is(event.data.isErrorEvent, true, "Frame got an ErrorEvent");
+          if (event.source == frame1) {
+            is(sawFrame1Error, false, "Haven't seen error for frame1 yet");
+            sawFrame1Error = true;
+          } else if (event.source == frame2) {
+            is(sawFrame2Error, false, "Haven't seen error for frame1 yet");
+            sawFrame2Error = true;
+          } else {
+            ok(false, "Saw error from unknown window");
+          }
+
+          // Second event
+          event = yield undefined;
+
+          ok(event instanceof MessageEvent, "Got a MessageEvent");
+          is(event.data.type, "error", "Got an error message");
+          is(event.data.message, errorMessage, "Got correct error message");
+          is(event.data.filename, workerAbsoluteURL, "Got correct filename");
+          is(event.data.lineno, errorLineno, "Got correct lineno");
+          is(event.data.isErrorEvent, true, "Frame got an ErrorEvent");
+          if (event.source == frame1) {
+            is(sawFrame1Error, false, "Haven't seen error for frame1 yet");
+            sawFrame1Error = true;
+          } else if (event.source == frame2) {
+            is(sawFrame2Error, false, "Haven't seen error for frame1 yet");
+            sawFrame2Error = true;
+          } else {
+            ok(false, "Saw error from unknown window");
+          }
+
+          is(sawFrame1Error, true, "Saw error for frame1");
+          is(sawFrame2Error, true, "Saw error for frame2");
+
+          // Try a shared worker in a different origin.
+          frame1 = document.getElementById("frame1");
+          frame1.src = "http://example.org" + basePath + frameRelativeURL;
+          frame1.onload = sendToGenerator;
+          yield undefined;
+
+          frame1 = frame1.contentWindow;
+
+          data = {
+            command: "retrieve"
+          };
+          frame1.postMessage(data, "*");
+
+          event = yield undefined;
+          ok(event instanceof MessageEvent, "Got a MessageEvent");
+          is(event.source, frame1, "First window got the event");
+          is(event.data.type, "result", "Got a result message");
+          is(event.data.data, undefined, "No data stored yet");
+
+          frame2.postMessage(data, "*");
+
+          event = yield undefined;
+          ok(event instanceof MessageEvent, "Got a MessageEvent");
+          is(event.source, frame2, "First window got the event");
+          is(event.data.type, "result", "Got a result message");
+          is(event.data.data, storedData, "Got stored data");
+
+          window.removeEventListener("message", sendToGenerator);
+
+          SimpleTest.finish();
+          yield undefined;
+        })();
+
+        let sendToGenerator = testGenerator.send.bind(testGenerator);
+
+      </script>
+  </head>
+  <body onload="testGenerator.next();">
+    <iframe id="frame1"></iframe>
+    <iframe id="frame2"></iframe>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/test_multi_sharedWorker_lifetimes.html
@@ -0,0 +1,151 @@
+<!--
+  Any copyright is dedicated to the Public Domain.
+  http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+  <head>
+    <title>Test for SharedWorker</title>
+    <script src="/tests/SimpleTest/SimpleTest.js"></script>
+    <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css">
+      <script class="testbody" type="text/javascript;version=1.7">
+        "use strict";
+
+        const swPref = "dom.workers.sharedWorkers.enabled";
+        const bfCachePref = "browser.sessionhistory.cache_subframes";
+
+        const frameRelativeURL = "multi_sharedWorker_frame.html";
+        const storedData = "0123456789abcdefghijklmnopqrstuvwxyz";
+
+        let testGenerator = (function() {
+          SimpleTest.waitForExplicitFinish();
+
+          ok(!("SharedWorker" in window), "No SharedWorker without pref set");
+          ok(!("WorkerMessagePort" in window),
+             "No WorkerMessagePort without pref set");
+
+          SpecialPowers.pushPrefEnv({ set: [[swPref, true]] }, sendToGenerator);
+          yield undefined;
+
+          window.addEventListener("message", function(event) {
+            if (typeof(event.data) == "string") {
+              info(event.data);
+            } else {
+              sendToGenerator(event);
+            }
+          });
+
+          let frame = document.getElementById("frame");
+          frame.src = frameRelativeURL;
+          frame.onload = sendToGenerator;
+
+          yield undefined;
+
+          frame = frame.contentWindow;
+          frame.postMessage({ command: "retrieve" }, "*");
+
+          let event = yield undefined;
+          ok(event instanceof MessageEvent, "Got a MessageEvent");
+          is(event.source, frame, "Correct window got the event");
+          is(event.data.type, "result", "Got a result message");
+          is(event.data.data, undefined, "No data stored yet");
+
+          frame.postMessage({ command: "store", data: storedData }, "*");
+          frame.postMessage({ command: "retrieve" }, "*");
+
+          event = yield undefined;
+          ok(event instanceof MessageEvent, "Got a MessageEvent");
+          is(event.source, frame, "Correct window got the event");
+          is(event.data.type, "result", "Got a result message");
+          is(event.data.data, storedData, "Got stored data");
+
+          // Navigate when the bfcache is disabled.
+          info("Navigating to about:blank");
+          let frame = document.getElementById("frame");
+          frame.onload = sendToGenerator;
+          frame.src = "about:blank";
+          frame.contentWindow.document.body.offsetTop;
+
+          yield undefined;
+
+          info("Navigating to " + frameRelativeURL);
+          frame.src = frameRelativeURL;
+          frame.contentWindow.document.body.offsetTop;
+
+          yield undefined;
+
+          frame = frame.contentWindow;
+          frame.postMessage({ command: "retrieve" }, "*");
+
+          let event = yield undefined;
+          ok(event instanceof MessageEvent, "Got a MessageEvent");
+          is(event.source, frame, "Correct window got the event");
+          is(event.data.type, "result", "Got a result message");
+          is(event.data.data, undefined, "No data stored");
+
+          frame.postMessage({ command: "store", data: storedData }, "*");
+          frame.postMessage({ command: "retrieve" }, "*");
+
+          event = yield undefined;
+          ok(event instanceof MessageEvent, "Got a MessageEvent");
+          is(event.source, frame, "Correct window got the event");
+          is(event.data.type, "result", "Got a result message");
+          is(event.data.data, storedData, "Got stored data");
+
+          info("Enabling '" + bfCachePref + "' pref");
+          SpecialPowers.pushPrefEnv({ set: [[bfCachePref, true]] },
+                                    sendToGenerator);
+          yield undefined;
+
+          // Navigate when the bfcache is enabled.
+          let frame = document.getElementById("frame");
+          frame.onload = sendToGenerator;
+
+          info("Navigating to about:blank");
+          frame.src = "about:blank";
+          frame.contentWindow.document.body.offsetTop;
+
+          yield undefined;
+
+          for (let i = 0; i < 3; i++) {
+            info("Running GC");
+            SpecialPowers.gc();
+
+            info("Waiting the event queue to clear");
+            SpecialPowers.executeSoon(sendToGenerator);
+            yield undefined;
+          }
+
+          info("Navigating to " + frameRelativeURL);
+          frame.src = frameRelativeURL;
+          frame.contentWindow.document.body.offsetTop;
+
+          yield undefined;
+
+          frame = frame.contentWindow;
+          frame.postMessage({ command: "retrieve" }, "*");
+
+          let event = yield undefined;
+          ok(event instanceof MessageEvent, "Got a MessageEvent");
+          is(event.source, frame, "Correct window got the event");
+          is(event.data.type, "result", "Got a result message");
+          is(event.data.data, storedData, "Still have data stored");
+
+          info("Resetting '" + bfCachePref + "' pref");
+          SpecialPowers.popPrefEnv(sendToGenerator);
+          yield undefined;
+
+          window.removeEventListener("message", sendToGenerator);
+
+          SimpleTest.finish();
+          yield undefined;
+        })();
+
+        let sendToGenerator = testGenerator.send.bind(testGenerator);
+
+      </script>
+  </head>
+  <body onload="testGenerator.next();">
+    <iframe id="frame"></iframe>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/test_sharedWorker.html
@@ -0,0 +1,76 @@
+<!--
+  Any copyright is dedicated to the Public Domain.
+  http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+  <head>
+    <title>Test for SharedWorker</title>
+    <script src="/tests/SimpleTest/SimpleTest.js">
+    </script>
+    <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css">
+  </head>
+  <body>
+    <p id="display"></p>
+    <div id="content" style="display: none"></div>
+    <pre id="test">
+      <script class="testbody">
+        "use strict";
+
+        const swPref = "dom.workers.sharedWorkers.enabled";
+
+        const href = window.location.href;
+        const filename = "sharedWorker_sharedWorker.js";
+        const sentMessage = "ping";
+        const errorFilename = href.substring(0, href.lastIndexOf("/") + 1) +
+                              filename;
+        const errorLine = 80;
+        const errorColumn = 0;
+
+        ok(!("SharedWorker" in window), "No SharedWorker without pref set");
+        ok(!("WorkerMessagePort" in window),
+           "No WorkerMessagePort without pref set");
+
+        SpecialPowers.pushPrefEnv({ set: [[swPref, true]] }, function() {
+          var worker = new SharedWorker(filename);
+
+          ok(worker instanceof SharedWorker, "Got SharedWorker instance");
+          ok(!("postMessage" in worker), "SharedWorker has no 'postMessage'");
+          ok(worker.port instanceof WorkerMessagePort,
+            "Shared worker has MessagePort");
+
+          var receivedMessage;
+          var receivedError;
+
+          worker.port.onmessage = function(event) {
+            ok(event instanceof MessageEvent, "Got a MessageEvent");
+            ok(event.target === worker.port,
+               "MessageEvent has correct 'target' property");
+            is(event.data, sentMessage, "Got correct message");
+            ok(receivedMessage === undefined, "Haven't gotten message yet");
+            ok(receivedError === undefined, "Haven't gotten error yet");
+            receivedMessage = event.data;
+          };
+
+          worker.onerror = function(event) {
+            ok(event instanceof ErrorEvent, "Got an ErrorEvent");
+            is(event.message, "Error: " + sentMessage, "Got correct error");
+            is(event.filename, errorFilename, "Got correct filename");
+            is(event.lineno, errorLine, "Got correct lineno");
+            is(event.column, errorColumn, "Got correct column");
+            ok(receivedMessage !== undefined, "Got message already");
+            ok(receivedError === undefined, "Haven't gotten error yet");
+            receivedError = event.message;
+            event.preventDefault();
+            SimpleTest.finish();
+          };
+
+          worker.port.postMessage(sentMessage);
+        });
+
+        SimpleTest.waitForExplicitFinish();
+
+      </script>
+    </pre>
+  </body>
+</html>
--- a/modules/libpref/src/init/all.js
+++ b/modules/libpref/src/init/all.js
@@ -98,16 +98,19 @@ pref("dom.indexedDB.warningQuota", 50);
 // Whether or not indexedDB experimental features are enabled.
 pref("dom.indexedDB.experimental", false);
 
 // Whether or not Web Workers are enabled.
 pref("dom.workers.enabled", true);
 // The number of workers per domain allowed to run concurrently.
 pref("dom.workers.maxPerDomain", 20);
 
+// Whether or not Shared Web Workers are enabled.
+pref("dom.workers.sharedWorkers.enabled", false);
+
 // Whether nonzero values can be returned from performance.timing.*
 pref("dom.enable_performance", true);
 
 // Whether the Gamepad API is enabled
 #ifdef RELEASE_BUILD
 pref("dom.gamepad.enabled", false);
 pref("dom.gamepad.non_standard_events.enabled", false);
 #else