Bug 966439 - BroadcastChannel API - patch 1 - BroadcastChannel for main-thread, r=smaug, r=bent
authorAndrea Marchesini <amarchesini@mozilla.com>
Thu, 15 Jan 2015 16:58:40 +0000
changeset 224045 ef5e329a41da2aa429bd5da6f400e831a409a84f
parent 224044 aea8d15e96bf30df42d7d2dcbb2c98962e95cc2d
child 224046 2ebe98dfcc9d07d1af49a6f58fad40b8534bcbca
push id28115
push userkwierso@gmail.com
push dateFri, 16 Jan 2015 00:14:42 +0000
treeherdermozilla-central@cc1cfd6f4ca8 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug, bent
bugs966439
milestone38.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 966439 - BroadcastChannel API - patch 1 - BroadcastChannel for main-thread, r=smaug, r=bent
dom/broadcastchannel/BroadcastChannel.cpp
dom/broadcastchannel/BroadcastChannel.h
dom/broadcastchannel/BroadcastChannelChild.cpp
dom/broadcastchannel/BroadcastChannelChild.h
dom/broadcastchannel/BroadcastChannelParent.cpp
dom/broadcastchannel/BroadcastChannelParent.h
dom/broadcastchannel/BroadcastChannelService.cpp
dom/broadcastchannel/BroadcastChannelService.h
dom/broadcastchannel/PBroadcastChannel.ipdl
dom/broadcastchannel/moz.build
dom/broadcastchannel/tests/broadcastchannel_worker.js
dom/broadcastchannel/tests/iframe_broadcastchannel.html
dom/broadcastchannel/tests/mochitest.ini
dom/broadcastchannel/tests/test_broadcastchannel_basic.html
dom/broadcastchannel/tests/test_broadcastchannel_self.html
dom/broadcastchannel/tests/test_broadcastchannel_worker.html
dom/events/DOMEventTargetHelper.h
dom/events/EventListenerManager.cpp
dom/events/MessageEvent.cpp
dom/events/MessageEvent.h
dom/moz.build
dom/tests/mochitest/general/test_interfaces.html
dom/webidl/BroadcastChannel.webidl
dom/webidl/moz.build
ipc/glue/BackgroundChildImpl.cpp
ipc/glue/BackgroundChildImpl.h
ipc/glue/BackgroundParentImpl.cpp
ipc/glue/BackgroundParentImpl.h
ipc/glue/PBackground.ipdl
ipc/glue/moz.build
new file mode 100644
--- /dev/null
+++ b/dom/broadcastchannel/BroadcastChannel.cpp
@@ -0,0 +1,522 @@
+/* -*- Mode: C++; 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/. */
+
+#include "BroadcastChannel.h"
+#include "BroadcastChannelChild.h"
+#include "mozilla/dom/BroadcastChannelBinding.h"
+#include "mozilla/ipc/BackgroundChild.h"
+#include "mozilla/ipc/BackgroundUtils.h"
+#include "mozilla/ipc/PBackgroundChild.h"
+#include "WorkerPrivate.h"
+#include "WorkerRunnable.h"
+
+#include "nsIAppsService.h"
+#include "nsIDocument.h"
+#include "nsIScriptSecurityManager.h"
+#include "nsServiceManagerUtils.h"
+#include "nsISupportsPrimitives.h"
+
+namespace mozilla {
+
+using namespace ipc;
+
+namespace dom {
+
+using namespace workers;
+
+namespace {
+
+void
+GetOrigin(nsIPrincipal* aPrincipal, nsAString& aOrigin, ErrorResult& aRv)
+{
+  MOZ_ASSERT(aPrincipal);
+
+  uint16_t appStatus = aPrincipal->GetAppStatus();
+
+  if (appStatus == nsIPrincipal::APP_STATUS_NOT_INSTALLED) {
+    nsAutoString tmp;
+    aRv = nsContentUtils::GetUTFOrigin(aPrincipal, tmp);
+    if (NS_WARN_IF(aRv.Failed())) {
+      return;
+    }
+
+    aOrigin = tmp;
+    return;
+  }
+
+  uint32_t appId = aPrincipal->GetAppId();
+
+  // If we are in "app code", use manifest URL as unique origin since
+  // multiple apps can share the same origin but not same broadcast messages.
+  nsresult rv;
+  nsCOMPtr<nsIAppsService> appsService =
+    do_GetService("@mozilla.org/AppsService;1", &rv);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    aRv.Throw(rv);
+    return;
+  }
+
+  appsService->GetManifestURLByLocalId(appId, aOrigin);
+}
+
+class InitializeRunnable MOZ_FINAL : public WorkerMainThreadRunnable
+{
+public:
+  InitializeRunnable(WorkerPrivate* aWorkerPrivate, nsAString& aOrigin,
+                     PrincipalInfo& aPrincipalInfo, ErrorResult& aRv)
+    : WorkerMainThreadRunnable(aWorkerPrivate)
+    , mWorkerPrivate(GetCurrentThreadWorkerPrivate())
+    , mOrigin(aOrigin)
+    , mPrincipalInfo(aPrincipalInfo)
+    , mRv(aRv)
+  {
+    MOZ_ASSERT(mWorkerPrivate);
+  }
+
+  bool MainThreadRun() MOZ_OVERRIDE
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+    nsIPrincipal* principal = mWorkerPrivate->GetPrincipal();
+
+    bool isNullPrincipal;
+    mRv = principal->GetIsNullPrincipal(&isNullPrincipal);
+    if (NS_WARN_IF(mRv.Failed())) {
+      return true;
+    }
+
+    if (NS_WARN_IF(isNullPrincipal)) {
+      mRv.Throw(NS_ERROR_FAILURE);
+      return true;
+    }
+
+    mRv = PrincipalToPrincipalInfo(principal, &mPrincipalInfo);
+    if (NS_WARN_IF(mRv.Failed())) {
+      return true;
+    }
+
+    GetOrigin(principal, mOrigin, mRv);
+    if (NS_WARN_IF(mRv.Failed())) {
+      return true;
+    }
+
+    return true;
+  }
+
+private:
+  WorkerPrivate* mWorkerPrivate;
+  nsAString& mOrigin;
+  PrincipalInfo& mPrincipalInfo;
+  ErrorResult& mRv;
+};
+
+class PostMessageRunnable MOZ_FINAL : public nsICancelableRunnable
+{
+public:
+  NS_DECL_ISUPPORTS
+
+  PostMessageRunnable(BroadcastChannelChild* aActor,
+                      const nsAString& aMessage)
+    : mActor(aActor)
+    , mMessage(aMessage)
+  {
+    MOZ_ASSERT(mActor);
+  }
+
+  NS_IMETHODIMP Run()
+  {
+    MOZ_ASSERT(mActor);
+    if (!mActor->IsActorDestroyed()) {
+      mActor->SendPostMessage(mMessage);
+    }
+    return NS_OK;
+  }
+
+  NS_IMETHODIMP Cancel()
+  {
+    mActor = nullptr;
+    return NS_OK;
+  }
+
+private:
+  ~PostMessageRunnable() {}
+
+  nsRefPtr<BroadcastChannelChild> mActor;
+  nsString mMessage;
+};
+
+NS_IMPL_ISUPPORTS(PostMessageRunnable, nsICancelableRunnable, nsIRunnable)
+
+class TeardownRunnable MOZ_FINAL : public nsICancelableRunnable
+{
+public:
+  NS_DECL_ISUPPORTS
+
+  TeardownRunnable(BroadcastChannelChild* aActor)
+    : mActor(aActor)
+  {
+    MOZ_ASSERT(mActor);
+  }
+
+  NS_IMETHODIMP Run()
+  {
+    MOZ_ASSERT(mActor);
+    if (!mActor->IsActorDestroyed()) {
+      mActor->SendClose();
+    }
+    return NS_OK;
+  }
+
+  NS_IMETHODIMP Cancel()
+  {
+    mActor = nullptr;
+    return NS_OK;
+  }
+
+private:
+  ~TeardownRunnable() {}
+
+  nsRefPtr<BroadcastChannelChild> mActor;
+};
+
+NS_IMPL_ISUPPORTS(TeardownRunnable, nsICancelableRunnable, nsIRunnable)
+
+class BroadcastChannelFeature MOZ_FINAL : public workers::WorkerFeature
+{
+  BroadcastChannel* mChannel;
+
+public:
+  explicit BroadcastChannelFeature(BroadcastChannel* aChannel)
+    : mChannel(aChannel)
+  {
+    MOZ_COUNT_CTOR(BroadcastChannelFeature);
+  }
+
+  virtual bool Notify(JSContext* aCx, workers::Status aStatus) MOZ_OVERRIDE
+  {
+    if (aStatus >= Canceling) {
+      WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(aCx);
+      MOZ_ASSERT(workerPrivate);
+
+      mChannel->Shutdown();
+    }
+
+    return true;
+  }
+
+private:
+  ~BroadcastChannelFeature()
+  {
+    MOZ_COUNT_DTOR(BroadcastChannelFeature);
+  }
+};
+
+} // anonymous namespace
+
+BroadcastChannel::BroadcastChannel(nsPIDOMWindow* aWindow,
+                                   const PrincipalInfo& aPrincipalInfo,
+                                   const nsAString& aOrigin,
+                                   const nsAString& aChannel)
+  : DOMEventTargetHelper(aWindow)
+  , mWorkerFeature(nullptr)
+  , mPrincipalInfo(aPrincipalInfo)
+  , mOrigin(aOrigin)
+  , mChannel(aChannel)
+  , mIsKeptAlive(false)
+  , mInnerID(0)
+{
+  // Window can be null in workers
+}
+
+BroadcastChannel::~BroadcastChannel()
+{
+  Shutdown();
+  MOZ_ASSERT(!mWorkerFeature);
+}
+
+JSObject*
+BroadcastChannel::WrapObject(JSContext* aCx)
+{
+  return BroadcastChannelBinding::Wrap(aCx, this);
+}
+
+/* static */ already_AddRefed<BroadcastChannel>
+BroadcastChannel::Constructor(const GlobalObject& aGlobal,
+                              const nsAString& aChannel,
+                              ErrorResult& aRv)
+{
+  nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aGlobal.GetAsSupports());
+  // Window is null in workers.
+
+  nsAutoString origin;
+  PrincipalInfo principalInfo;
+  WorkerPrivate* workerPrivate = nullptr;
+
+  if (NS_IsMainThread()) {
+    nsCOMPtr<nsIGlobalObject> incumbent = mozilla::dom::GetIncumbentGlobal();
+
+    if (!incumbent) {
+      aRv.Throw(NS_ERROR_FAILURE);
+      return nullptr;
+    }
+
+    nsIPrincipal* principal = incumbent->PrincipalOrNull();
+    if (!principal) {
+      aRv.Throw(NS_ERROR_UNEXPECTED);
+      return nullptr;
+    }
+
+    bool isNullPrincipal;
+    aRv = principal->GetIsNullPrincipal(&isNullPrincipal);
+    if (NS_WARN_IF(aRv.Failed())) {
+      return nullptr;
+    }
+
+    if (NS_WARN_IF(isNullPrincipal)) {
+      aRv.Throw(NS_ERROR_FAILURE);
+      return nullptr;
+    }
+
+    GetOrigin(principal, origin, aRv);
+
+    aRv = PrincipalToPrincipalInfo(principal, &principalInfo);
+    if (NS_WARN_IF(aRv.Failed())) {
+      return nullptr;
+    }
+
+  } else {
+    JSContext* cx = aGlobal.Context();
+    workerPrivate = GetWorkerPrivateFromContext(cx);
+    MOZ_ASSERT(workerPrivate);
+
+    nsRefPtr<InitializeRunnable> runnable =
+      new InitializeRunnable(workerPrivate, origin, principalInfo, aRv);
+    runnable->Dispatch(cx);
+  }
+
+  if (aRv.Failed()) {
+    return nullptr;
+  }
+
+  nsRefPtr<BroadcastChannel> bc =
+    new BroadcastChannel(window, principalInfo, origin, aChannel);
+
+  // Register this component to PBackground.
+  ipc::BackgroundChild::GetOrCreateForCurrentThread(bc);
+
+  if (!workerPrivate) {
+    MOZ_ASSERT(window);
+    MOZ_ASSERT(window->IsInnerWindow());
+    bc->mInnerID = window->WindowID();
+
+    // Register as observer for inner-window-destroyed.
+    nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+    if (obs) {
+      obs->AddObserver(bc, "inner-window-destroyed", false);
+    }
+  } else {
+    bc->mWorkerFeature = new BroadcastChannelFeature(bc);
+    JSContext* cx = workerPrivate->GetJSContext();
+    if (NS_WARN_IF(!workerPrivate->AddFeature(cx, bc->mWorkerFeature))) {
+      NS_WARNING("Failed to register the BroadcastChannel worker feature.");
+      return nullptr;
+    }
+  }
+
+  return bc.forget();
+}
+
+void
+BroadcastChannel::PostMessage(const nsAString& aMessage)
+{
+  if (mActor) {
+    nsRefPtr<PostMessageRunnable> runnable =
+      new PostMessageRunnable(mActor, aMessage);
+
+    if (NS_FAILED(NS_DispatchToCurrentThread(runnable))) {
+      NS_WARNING("Failed to dispatch to the current thread!");
+    }
+
+    return;
+  }
+
+  mPendingMessages.AppendElement(aMessage);
+}
+
+void
+BroadcastChannel::ActorFailed()
+{
+  MOZ_CRASH("Failed to create a PBackgroundChild actor!");
+}
+
+void
+BroadcastChannel::ActorCreated(ipc::PBackgroundChild* aActor)
+{
+  MOZ_ASSERT(aActor);
+
+  PBroadcastChannelChild* actor =
+    aActor->SendPBroadcastChannelConstructor(mPrincipalInfo, mOrigin, mChannel);
+
+  mActor = static_cast<BroadcastChannelChild*>(actor);
+  MOZ_ASSERT(mActor);
+
+  mActor->SetEventTarget(this);
+
+  // Flush pending messages.
+  for (uint32_t i = 0; i < mPendingMessages.Length(); ++i) {
+    PostMessage(mPendingMessages[i]);
+  }
+
+  mPendingMessages.Clear();
+}
+
+void
+BroadcastChannel::Shutdown()
+{
+  // If shutdown() is called we have to release the reference if we still keep
+  // it.
+  if (mIsKeptAlive) {
+    mIsKeptAlive = false;
+    Release();
+  }
+
+  if (mWorkerFeature) {
+    WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
+    workerPrivate->RemoveFeature(workerPrivate->GetJSContext(), mWorkerFeature);
+    mWorkerFeature = nullptr;
+  }
+
+  if (mActor) {
+    mActor->SetEventTarget(nullptr);
+
+    nsRefPtr<TeardownRunnable> runnable = new TeardownRunnable(mActor);
+    NS_DispatchToCurrentThread(runnable);
+
+    mActor = nullptr;
+  }
+}
+
+EventHandlerNonNull*
+BroadcastChannel::GetOnmessage()
+{
+  if (NS_IsMainThread()) {
+    return GetEventHandler(nsGkAtoms::onmessage, EmptyString());
+  }
+  return GetEventHandler(nullptr, NS_LITERAL_STRING("message"));
+}
+
+void
+BroadcastChannel::SetOnmessage(EventHandlerNonNull* aCallback)
+{
+  if (NS_IsMainThread()) {
+    SetEventHandler(nsGkAtoms::onmessage, EmptyString(), aCallback);
+  } else {
+    SetEventHandler(nullptr, NS_LITERAL_STRING("message"), aCallback);
+  }
+
+  UpdateMustKeepAlive();
+}
+
+void
+BroadcastChannel::AddEventListener(const nsAString& aType,
+                                   EventListener* aCallback,
+                                   bool aCapture,
+                                   const dom::Nullable<bool>& aWantsUntrusted,
+                                   ErrorResult& aRv)
+{
+  DOMEventTargetHelper::AddEventListener(aType, aCallback, aCapture,
+                                         aWantsUntrusted, aRv);
+
+  if (aRv.Failed()) {
+    return;
+  }
+
+  UpdateMustKeepAlive();
+}
+
+void
+BroadcastChannel::RemoveEventListener(const nsAString& aType,
+                                      EventListener* aCallback,
+                                      bool aCapture,
+                                      ErrorResult& aRv)
+{
+  DOMEventTargetHelper::RemoveEventListener(aType, aCallback, aCapture, aRv);
+
+  if (aRv.Failed()) {
+    return;
+  }
+
+  UpdateMustKeepAlive();
+}
+
+void
+BroadcastChannel::UpdateMustKeepAlive()
+{
+  bool toKeepAlive = HasListenersFor(NS_LITERAL_STRING("message"));
+  if (toKeepAlive == mIsKeptAlive) {
+    return;
+  }
+
+  mIsKeptAlive = toKeepAlive;
+
+  if (toKeepAlive) {
+    AddRef();
+  } else {
+    Release();
+  }
+}
+
+NS_IMETHODIMP
+BroadcastChannel::Observe(nsISupports* aSubject, const char* aTopic,
+                          const char16_t* aData)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(!strcmp(aTopic, "inner-window-destroyed"));
+
+  // If the window is destroyed we have to release the reference that we are
+  // keeping.
+  if (!mIsKeptAlive) {
+    return NS_OK;
+  }
+
+  nsCOMPtr<nsISupportsPRUint64> wrapper = do_QueryInterface(aSubject);
+  NS_ENSURE_TRUE(wrapper, NS_ERROR_FAILURE);
+
+  uint64_t innerID;
+  nsresult rv = wrapper->GetData(&innerID);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (innerID == mInnerID) {
+    nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+    if (obs) {
+      obs->RemoveObserver(this, "inner-window-destroyed");
+    }
+
+    Shutdown();
+  }
+
+  return NS_OK;
+}
+
+NS_IMPL_CYCLE_COLLECTION_CLASS(BroadcastChannel)
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(BroadcastChannel,
+                                                  DOMEventTargetHelper)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(BroadcastChannel,
+                                                DOMEventTargetHelper)
+  tmp->Shutdown();
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(BroadcastChannel)
+  NS_INTERFACE_MAP_ENTRY(nsIIPCBackgroundChildCreateCallback)
+  NS_INTERFACE_MAP_ENTRY(nsIObserver)
+NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
+
+NS_IMPL_ADDREF_INHERITED(BroadcastChannel, DOMEventTargetHelper)
+NS_IMPL_RELEASE_INHERITED(BroadcastChannel, DOMEventTargetHelper)
+
+} // dom namespace
+} // mozilla namespace
new file mode 100644
--- /dev/null
+++ b/dom/broadcastchannel/BroadcastChannel.h
@@ -0,0 +1,103 @@
+/* -*- Mode: C++; 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/. */
+
+#ifndef mozilla_dom_BroadcastChannel_h
+#define mozilla_dom_BroadcastChannel_h
+
+#include "mozilla/Attributes.h"
+#include "mozilla/DOMEventTargetHelper.h"
+#include "mozilla/ipc/PBackgroundSharedTypes.h"
+#include "nsIIPCBackgroundChildCreateCallback.h"
+#include "nsIObserver.h"
+#include "nsTArray.h"
+#include "nsRefPtr.h"
+
+class nsPIDOMWindow;
+
+namespace mozilla {
+namespace dom {
+
+namespace workers {
+class WorkerFeature;
+}
+
+class BroadcastChannelChild;
+
+class BroadcastChannel MOZ_FINAL
+  : public DOMEventTargetHelper
+  , public nsIIPCBackgroundChildCreateCallback
+  , public nsIObserver
+{
+  NS_DECL_NSIIPCBACKGROUNDCHILDCREATECALLBACK
+  NS_DECL_NSIOBSERVER
+
+  typedef mozilla::ipc::PrincipalInfo PrincipalInfo;
+
+public:
+  NS_DECL_ISUPPORTS_INHERITED
+
+  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(BroadcastChannel,
+                                           DOMEventTargetHelper)
+
+  virtual JSObject*
+  WrapObject(JSContext* aCx) MOZ_OVERRIDE;
+
+  static already_AddRefed<BroadcastChannel>
+  Constructor(const GlobalObject& aGlobal, const nsAString& aChannel,
+              ErrorResult& aRv);
+
+  void GetName(nsAString& aName) const
+  {
+    aName = mChannel;
+  }
+
+  void PostMessage(const nsAString& aMessage);
+
+  EventHandlerNonNull* GetOnmessage();
+  void SetOnmessage(EventHandlerNonNull* aCallback);
+
+  using nsIDOMEventTarget::AddEventListener;
+  using nsIDOMEventTarget::RemoveEventListener;
+
+  virtual void AddEventListener(const nsAString& aType,
+                                EventListener* aCallback,
+                                bool aCapture,
+                                const Nullable<bool>& aWantsUntrusted,
+                                ErrorResult& aRv) MOZ_OVERRIDE;
+  virtual void RemoveEventListener(const nsAString& aType,
+                                   EventListener* aCallback,
+                                   bool aCapture,
+                                   ErrorResult& aRv) MOZ_OVERRIDE;
+
+  void Shutdown();
+
+private:
+  BroadcastChannel(nsPIDOMWindow* aWindow,
+                   const PrincipalInfo& aPrincipalInfo,
+                   const nsAString& aOrigin,
+                   const nsAString& aChannel);
+
+  ~BroadcastChannel();
+
+  void UpdateMustKeepAlive();
+
+  nsRefPtr<BroadcastChannelChild> mActor;
+  nsTArray<nsString> mPendingMessages;
+
+  nsAutoPtr<workers::WorkerFeature> mWorkerFeature;
+
+  PrincipalInfo mPrincipalInfo;
+  nsString mOrigin;
+  nsString mChannel;
+
+  bool mIsKeptAlive;
+
+  uint64_t mInnerID;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_BroadcastChannel_h
new file mode 100644
--- /dev/null
+++ b/dom/broadcastchannel/BroadcastChannelChild.cpp
@@ -0,0 +1,108 @@
+/* -*- Mode: C++; 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/. */
+
+#include "BroadcastChannelChild.h"
+#include "BroadcastChannel.h"
+#include "jsapi.h"
+#include "mozilla/dom/MessageEvent.h"
+#include "mozilla/dom/MessageEventBinding.h"
+#include "mozilla/dom/ScriptSettings.h"
+#include "mozilla/ipc/PBackgroundChild.h"
+#include "WorkerPrivate.h"
+
+namespace mozilla {
+
+using namespace ipc;
+
+namespace dom {
+
+using namespace workers;
+
+BroadcastChannelChild::BroadcastChannelChild(const nsAString& aOrigin,
+                                             const nsAString& aChannel)
+  : mOrigin(aOrigin)
+  , mChannel(aChannel)
+  , mActorDestroyed(false)
+{
+}
+
+BroadcastChannelChild::~BroadcastChannelChild()
+{
+  MOZ_ASSERT(!mEventTarget);
+}
+
+bool
+BroadcastChannelChild::RecvNotify(const nsString& aMessage)
+{
+  // This object is going to be deleted soon. No notify is required.
+  if (!mEventTarget) {
+    return true;
+  }
+
+  if (NS_IsMainThread()) {
+    DOMEventTargetHelper* deth =
+      DOMEventTargetHelper::FromSupports(mEventTarget);
+    MOZ_ASSERT(deth);
+
+    AutoJSAPI autoJS;
+    if (!autoJS.Init(deth->GetParentObject())) {
+      NS_WARNING("Dropping message");
+      return true;
+    }
+
+    Notify(autoJS.cx(), aMessage);
+    return true;
+  }
+
+  WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
+  MOZ_ASSERT(workerPrivate);
+
+  Notify(workerPrivate->GetJSContext(), aMessage);
+  return true;
+}
+
+void
+BroadcastChannelChild::Notify(JSContext* aCx, const nsString& aMessage)
+{
+  JS::Rooted<JSString*> str(aCx,
+                            JS_NewUCStringCopyN(aCx, aMessage.get(),
+                                                aMessage.Length()));
+  if (!str) {
+    // OOM, no exception needed.
+    NS_WARNING("Failed allocating a JS string. Probably OOM.");
+    return;
+  }
+
+  JS::Rooted<JS::Value> value(aCx, JS::StringValue(str));
+
+  RootedDictionary<MessageEventInit> init(aCx);
+  init.mBubbles = false;
+  init.mCancelable = false;
+  init.mOrigin.Construct(mOrigin);
+  init.mData = value;
+
+  ErrorResult rv;
+  nsRefPtr<MessageEvent> event =
+    MessageEvent::Constructor(mEventTarget, NS_LITERAL_STRING("message"),
+                              init, rv);
+  if (rv.Failed()) {
+    NS_WARNING("Failed to create a MessageEvent object.");
+    return;
+  }
+
+  event->SetTrusted(true);
+
+  bool status;
+  mEventTarget->DispatchEvent(static_cast<Event*>(event.get()), &status);
+}
+
+void
+BroadcastChannelChild::ActorDestroy(ActorDestroyReason aWhy)
+{
+  mActorDestroyed = true;
+}
+
+} // dom namespace
+} // mozilla namespace
new file mode 100644
--- /dev/null
+++ b/dom/broadcastchannel/BroadcastChannelChild.h
@@ -0,0 +1,63 @@
+/* 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_BroadcastChannelChild_h
+#define mozilla_dom_BroadcastChannelChild_h
+
+#include "mozilla/dom/EventTarget.h"
+#include "mozilla/dom/PBroadcastChannelChild.h"
+
+namespace mozilla {
+
+namespace ipc {
+class BackgroundChildImpl;
+}
+
+namespace dom {
+
+class EventTarget;
+
+class BroadcastChannelChild MOZ_FINAL : public PBroadcastChannelChild
+{
+  friend class mozilla::ipc::BackgroundChildImpl;
+
+public:
+  NS_INLINE_DECL_REFCOUNTING(BroadcastChannelChild)
+
+  void SetEventTarget(EventTarget* aEventTarget)
+  {
+    mEventTarget = aEventTarget;
+  }
+
+  virtual bool RecvNotify(const nsString& aMessage) MOZ_OVERRIDE;
+
+  bool IsActorDestroyed() const
+  {
+    return mActorDestroyed;
+  }
+
+private:
+  BroadcastChannelChild(const nsAString& aOrigin,
+                        const nsAString& aChannel);
+
+  ~BroadcastChannelChild();
+
+  void Notify(JSContext* aCx, const nsString& aMessage);
+
+  void ActorDestroy(ActorDestroyReason aWhy);
+
+  // This raw pointer is actually the parent object.
+  // It's set to null when the parent object is deleted.
+  EventTarget* mEventTarget;
+
+  nsString mOrigin;
+  nsString mChannel;
+
+  bool mActorDestroyed;
+};
+
+} // dom namespace
+} // mozilla namespace
+
+#endif // mozilla_dom_BroadcastChannelChild_h
new file mode 100644
--- /dev/null
+++ b/dom/broadcastchannel/BroadcastChannelParent.cpp
@@ -0,0 +1,88 @@
+/* -*- Mode: C++; 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/. */
+
+#include "BroadcastChannelParent.h"
+#include "BroadcastChannelService.h"
+#include "mozilla/ipc/BackgroundParent.h"
+#include "mozilla/unused.h"
+
+namespace mozilla {
+
+using namespace ipc;
+
+namespace dom {
+
+BroadcastChannelParent::BroadcastChannelParent(
+                                            const nsAString& aOrigin,
+                                            const nsAString& aChannel)
+  : mService(BroadcastChannelService::GetOrCreate())
+  , mOrigin(aOrigin)
+  , mChannel(aChannel)
+{
+  AssertIsOnBackgroundThread();
+  mService->RegisterActor(this);
+}
+
+BroadcastChannelParent::~BroadcastChannelParent()
+{
+  AssertIsOnBackgroundThread();
+}
+
+bool
+BroadcastChannelParent::RecvPostMessage(const nsString& aMessage)
+{
+  AssertIsOnBackgroundThread();
+
+  if (NS_WARN_IF(!mService)) {
+    return false;
+  }
+
+  mService->PostMessage(this, aMessage, mOrigin, mChannel);
+  return true;
+}
+
+bool
+BroadcastChannelParent::RecvClose()
+{
+  AssertIsOnBackgroundThread();
+
+  if (NS_WARN_IF(!mService)) {
+    return false;
+  }
+
+  mService->UnregisterActor(this);
+  mService = nullptr;
+
+  unused << Send__delete__(this);
+
+  return true;
+}
+
+void
+BroadcastChannelParent::ActorDestroy(ActorDestroyReason aWhy)
+{
+  AssertIsOnBackgroundThread();
+
+  if (mService) {
+    // This object is about to be released and with it, also mService will be
+    // released too.
+    mService->UnregisterActor(this);
+  }
+}
+
+void
+BroadcastChannelParent::CheckAndDeliver(const nsString& aMessage,
+                                        const nsString& aOrigin,
+                                        const nsString& aChannel)
+{
+  AssertIsOnBackgroundThread();
+
+  if (aOrigin == mOrigin && aChannel == mChannel) {
+    unused << SendNotify(aMessage);
+  }
+}
+
+} // dom namespace
+} // mozilla namespace
new file mode 100644
--- /dev/null
+++ b/dom/broadcastchannel/BroadcastChannelParent.h
@@ -0,0 +1,48 @@
+/* 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_BroadcastChannelParent_h
+#define mozilla_dom_BroadcastChannelParent_h
+
+#include "mozilla/dom/PBroadcastChannelParent.h"
+
+namespace mozilla {
+
+namespace ipc {
+class BackgroundParentImpl;
+}
+
+namespace dom {
+
+class BroadcastChannelService;
+
+class BroadcastChannelParent MOZ_FINAL : public PBroadcastChannelParent
+{
+  friend class mozilla::ipc::BackgroundParentImpl;
+
+public:
+  void CheckAndDeliver(const nsString& aMessage,
+                       const nsString& aOrigin,
+                       const nsString& aChannel);
+
+private:
+  BroadcastChannelParent(const nsAString& aOrigin,
+                         const nsAString& aChannel);
+  ~BroadcastChannelParent();
+
+  virtual bool RecvPostMessage(const nsString& aMessage) MOZ_OVERRIDE;
+
+  virtual bool RecvClose() MOZ_OVERRIDE;
+
+  virtual void ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE;
+
+  nsRefPtr<BroadcastChannelService> mService;
+  nsString mOrigin;
+  nsString mChannel;
+};
+
+} // dom namespace
+} // mozilla namespace
+
+#endif // mozilla_dom_BroadcastChannelParent_h
new file mode 100644
--- /dev/null
+++ b/dom/broadcastchannel/BroadcastChannelService.cpp
@@ -0,0 +1,134 @@
+/* -*- Mode: C++; 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/. */
+
+#include "BroadcastChannelService.h"
+#include "BroadcastChannelParent.h"
+#include "mozilla/ipc/BackgroundParent.h"
+
+namespace mozilla {
+
+using namespace ipc;
+
+namespace dom {
+
+namespace {
+
+BroadcastChannelService* sInstance = nullptr;
+
+} // anonymous namespace
+
+BroadcastChannelService::BroadcastChannelService()
+{
+  AssertIsOnBackgroundThread();
+
+  // sInstance is a raw BroadcastChannelService*.
+  MOZ_ASSERT(!sInstance);
+  sInstance = this;
+}
+
+BroadcastChannelService::~BroadcastChannelService()
+{
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(sInstance == this);
+  MOZ_ASSERT(mAgents.Count() == 0);
+
+  sInstance = nullptr;
+}
+
+// static
+already_AddRefed<BroadcastChannelService>
+BroadcastChannelService::GetOrCreate()
+{
+  AssertIsOnBackgroundThread();
+
+  nsRefPtr<BroadcastChannelService> instance = sInstance;
+  if (!instance) {
+    instance = new BroadcastChannelService();
+  }
+  return instance.forget();
+}
+
+void
+BroadcastChannelService::RegisterActor(BroadcastChannelParent* aParent)
+{
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(aParent);
+  MOZ_ASSERT(!mAgents.Contains(aParent));
+
+  mAgents.PutEntry(aParent);
+}
+
+void
+BroadcastChannelService::UnregisterActor(BroadcastChannelParent* aParent)
+{
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(aParent);
+  MOZ_ASSERT(mAgents.Contains(aParent));
+
+  mAgents.RemoveEntry(aParent);
+}
+
+namespace {
+
+struct MOZ_STACK_CLASS PostMessageData MOZ_FINAL
+{
+  PostMessageData(BroadcastChannelParent* aParent,
+                  const nsAString& aMessage,
+                  const nsAString& aOrigin,
+                  const nsAString& aChannel)
+    : mParent(aParent)
+    , mMessage(aMessage)
+    , mOrigin(aOrigin)
+    , mChannel(aChannel)
+  {
+    MOZ_ASSERT(aParent);
+    MOZ_COUNT_CTOR(PostMessageData);
+  }
+
+  ~PostMessageData()
+  {
+    MOZ_COUNT_DTOR(PostMessageData);
+  }
+
+  BroadcastChannelParent* mParent;
+  const nsString mMessage;
+  const nsString mOrigin;
+  const nsString mChannel;
+};
+
+PLDHashOperator
+PostMessageEnumerator(nsPtrHashKey<BroadcastChannelParent>* aKey, void* aPtr)
+{
+  AssertIsOnBackgroundThread();
+
+  auto* data = static_cast<PostMessageData*>(aPtr);
+  BroadcastChannelParent* parent = aKey->GetKey();
+  MOZ_ASSERT(parent);
+
+  if (parent != data->mParent) {
+    parent->CheckAndDeliver(data->mMessage, data->mOrigin, data->mChannel);
+  }
+
+  return PL_DHASH_NEXT;
+}
+
+} // anonymous namespace
+
+void
+BroadcastChannelService::PostMessage(BroadcastChannelParent* aParent,
+                                     const nsAString& aMessage,
+                                     const nsAString& aOrigin,
+                                     const nsAString& aChannel)
+{
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(aParent);
+  MOZ_ASSERT(mAgents.Contains(aParent));
+
+  PostMessageData data(aParent, aMessage, aOrigin, aChannel);
+  mAgents.EnumerateEntries(PostMessageEnumerator, &data);
+}
+
+} // dom namespace
+} // mozilla namespace
new file mode 100644
--- /dev/null
+++ b/dom/broadcastchannel/BroadcastChannelService.h
@@ -0,0 +1,42 @@
+/* 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_BroadcastChannelService_h
+#define mozilla_dom_BroadcastChannelService_h
+
+#include "nsISupportsImpl.h"
+#include "nsHashKeys.h"
+#include "nsTHashtable.h"
+
+namespace mozilla {
+namespace dom {
+
+class BroadcastChannelParent;
+
+class BroadcastChannelService MOZ_FINAL
+{
+public:
+  NS_INLINE_DECL_REFCOUNTING(BroadcastChannelService)
+
+  static already_AddRefed<BroadcastChannelService> GetOrCreate();
+
+  void RegisterActor(BroadcastChannelParent* aParent);
+  void UnregisterActor(BroadcastChannelParent* aParent);
+
+  void PostMessage(BroadcastChannelParent* aParent,
+                   const nsAString& aMessage,
+                   const nsAString& aOrigin,
+                   const nsAString& aChannel);
+
+private:
+  BroadcastChannelService();
+  ~BroadcastChannelService();
+
+  nsTHashtable<nsPtrHashKey<BroadcastChannelParent>> mAgents;
+};
+
+} // dom namespace
+} // mozilla namespace
+
+#endif // mozilla_dom_BroadcastChannelService_h
new file mode 100644
--- /dev/null
+++ b/dom/broadcastchannel/PBroadcastChannel.ipdl
@@ -0,0 +1,25 @@
+/* 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 protocol PBackground;
+
+namespace mozilla {
+namespace dom {
+
+// This protocol is used for the BroadcastChannel API
+protocol PBroadcastChannel
+{
+  manager PBackground;
+
+parent:
+  PostMessage(nsString message);
+  Close();
+
+child:
+  Notify(nsString message);
+  __delete__();
+};
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/broadcastchannel/moz.build
@@ -0,0 +1,31 @@
+# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+EXPORTS.mozilla.dom += [
+    'BroadcastChannel.h',
+]
+
+SOURCES += [
+    'BroadcastChannel.cpp',
+    'BroadcastChannelChild.cpp',
+    'BroadcastChannelParent.cpp',
+    'BroadcastChannelService.cpp',
+]
+
+IPDL_SOURCES += [
+    'PBroadcastChannel.ipdl',
+]
+
+LOCAL_INCLUDES += [
+    '../workers',
+]
+
+MOCHITEST_MANIFESTS += ['tests/mochitest.ini']
+
+include('/ipc/chromium/chromium-config.mozbuild')
+
+FINAL_LIBRARY = 'xul'
+FAIL_ON_WARNINGS = True
new file mode 100644
--- /dev/null
+++ b/dom/broadcastchannel/tests/broadcastchannel_worker.js
@@ -0,0 +1,9 @@
+onmessage = function(evt) {
+  var bc = new BroadcastChannel('foobar');
+  bc.addEventListener('message', function(event) {
+    postMessage(event.data == "hello world from the window" ? "OK" : "KO");
+    bc.postMessage("hello world from the worker");
+  }, false);
+
+  postMessage("READY");
+}
new file mode 100644
--- /dev/null
+++ b/dom/broadcastchannel/tests/iframe_broadcastchannel.html
@@ -0,0 +1,35 @@
+<!DOCTYPE HTML>
+<html>
+<body>
+  <script type="application/javascript">
+
+function is(a, b, msg) {
+  ok(a == b, msg);
+}
+
+function ok(a, msg) {
+  window.parent.postMessage({ status: a ? "OK" : "KO", message: msg }, "*");
+}
+
+ok("BroadcastChannel" in window, "BroadcastChannel exists");
+
+var bc = new BroadcastChannel("foobar");
+ok(bc, "BroadcastChannel can be created");
+is(bc.name, 'foobar', "BroadcastChannel.name is foobar");
+
+ok("postMessage" in bc, "BroadcastChannel has postMessage() method");
+
+bc.onmessage = function(evt) {
+  ok(evt instanceof MessageEvent, 'evt is a MessageEvent');
+  is(evt.target, bc, 'MessageEvent.target is bc');
+  is(evt.target.name, 'foobar', 'MessageEvent.target.name is foobar');
+  is(evt.target.name, bc.name, 'MessageEvent.target.name is bc.name');
+  is(evt.data, "Hello world from the window!", "Message received from the window");
+  bc.postMessage("Hello world from the iframe!");
+}
+
+  </script>
+</body>
+</html>
+
+
new file mode 100644
--- /dev/null
+++ b/dom/broadcastchannel/tests/mochitest.ini
@@ -0,0 +1,8 @@
+[DEFAULT]
+support-files =
+  iframe_broadcastchannel.html
+  broadcastchannel_worker.js
+
+[test_broadcastchannel_basic.html]
+[test_broadcastchannel_self.html]
+[test_broadcastchannel_worker.html]
new file mode 100644
--- /dev/null
+++ b/dom/broadcastchannel/tests/test_broadcastchannel_basic.html
@@ -0,0 +1,60 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test for BroadcastChannel</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+
+<div id="content"></div>
+
+<script type="application/javascript">
+
+SimpleTest.waitForExplicitFinish();
+
+addEventListener('message', receiveMessage, false);
+function receiveMessage(evt) {
+  if (evt.data.status == 'OK') {
+    ok(true, evt.data.message);
+  } else if (evt.data.status == 'KO') {
+    ok(false, evt.data.message);
+  } else {
+    ok(false, "Unknown message");
+  }
+}
+
+ok("BroadcastChannel" in window, "BroadcastChannel exists");
+
+var bc = new BroadcastChannel("foobar");
+ok(bc, "BroadcastChannel can be created");
+is(bc.name, 'foobar', "BroadcastChannel.name is foobar");
+
+ok("postMessage" in bc, "BroadcastChannel has postMessage() method");
+
+bc.onmessage = function(evt) {
+  ok(evt instanceof MessageEvent, "This is a MessageEvent");
+  is(evt.target, bc, "MessageEvent.target is bc");
+  is(evt.target.name, 'foobar', "MessageEvent.target.name is foobar");
+  is(evt.target.name, bc.name, "MessageEvent.target.name == bc.name");
+  is(evt.origin, 'http://mochi.test:8888', "MessageEvent.origin is correct");
+  is(evt.data, "Hello world from the iframe!", "The message from the iframe has been received!");
+  SimpleTest.finish();
+}
+
+var div = document.getElementById("content");
+ok(div, "Parent exists");
+
+var ifr = document.createElement("iframe");
+ifr.addEventListener("load", iframeLoaded, false);
+ifr.setAttribute('src', "iframe_broadcastchannel.html");
+div.appendChild(ifr);
+
+function iframeLoaded() {
+  bc.postMessage("Hello world from the window!");
+}
+
+</script>
+</body>
+</html>
+
new file mode 100644
--- /dev/null
+++ b/dom/broadcastchannel/tests/test_broadcastchannel_self.html
@@ -0,0 +1,35 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test for BroadcastChannel</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+
+<div id="content"></div>
+
+<script type="application/javascript">
+
+SimpleTest.waitForExplicitFinish();
+
+x = new BroadcastChannel('foo');
+y = new BroadcastChannel('foo');
+
+function func(e) {
+  is(e.target, y, "The target is !x");
+
+  SimpleTest.executeSoon(function() {
+    SimpleTest.finish();
+  });
+}
+
+x.onmessage = func;
+y.onmessage = func;
+
+x.postMessage('foo');
+
+</script>
+</body>
+</html>
+
new file mode 100644
--- /dev/null
+++ b/dom/broadcastchannel/tests/test_broadcastchannel_worker.html
@@ -0,0 +1,49 @@
+<!--
+  Any copyright is dedicated to the Public Domain.
+  http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<!--
+Tests of DOM BroadcastChannel in workers
+-->
+<head>
+  <title>Test for BroadcastChannel in workers</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" language="javascript">
+
+var worker = new Worker("broadcastchannel_worker.js");
+
+var bc = new BroadcastChannel('foobar');
+
+worker.onmessage = function(event) {
+  if (event.data == "READY") {
+    ok(true, "Worker is ready!");
+    bc.postMessage('hello world from the window');
+  } else if(event.data == "OK") {
+    ok(true, "Worker has received the message");
+  } else {
+    ok(false, "Something wrong happened");
+  }
+};
+
+bc.onmessage = function(event) {
+  is("hello world from the worker", event.data, "The message matches!");
+  SimpleTest.finish();
+}
+
+worker.postMessage('go');
+SimpleTest.waitForExplicitFinish();
+
+</script>
+</pre>
+</body>
+</html>
--- a/dom/events/DOMEventTargetHelper.h
+++ b/dom/events/DOMEventTargetHelper.h
@@ -96,16 +96,21 @@ public:
       // nsISupports pointer. That must be fixed, or we'll crash...
       NS_ASSERTION(target_qi == target, "Uh, fix QI!");
     }
 #endif
 
     return static_cast<DOMEventTargetHelper*>(target);
   }
 
+  bool HasListenersFor(const nsAString& aType)
+  {
+    return mListenerManager && mListenerManager->HasListenersFor(aType);
+  }
+
   bool HasListenersFor(nsIAtom* aTypeWithOn)
   {
     return mListenerManager && mListenerManager->HasListenersFor(aTypeWithOn);
   }
 
   nsresult SetEventHandler(nsIAtom* aType,
                            JSContext* aCx,
                            const JS::Value& aValue);
--- a/dom/events/EventListenerManager.cpp
+++ b/dom/events/EventListenerManager.cpp
@@ -1242,18 +1242,29 @@ EventListenerManager::MutationListenerBi
     }
   }
   return bits;
 }
 
 bool
 EventListenerManager::HasListenersFor(const nsAString& aEventName)
 {
-  nsCOMPtr<nsIAtom> atom = do_GetAtom(NS_LITERAL_STRING("on") + aEventName);
-  return HasListenersFor(atom);
+  if (mIsMainThreadELM) {
+    nsCOMPtr<nsIAtom> atom = do_GetAtom(NS_LITERAL_STRING("on") + aEventName);
+    return HasListenersFor(atom);
+  }
+
+  uint32_t count = mListeners.Length();
+  for (uint32_t i = 0; i < count; ++i) {
+    Listener* listener = &mListeners.ElementAt(i);
+    if (listener->mTypeString == aEventName) {
+      return true;
+    }
+  }
+  return false;
 }
 
 bool
 EventListenerManager::HasListenersFor(nsIAtom* aEventNameWithOn)
 {
 #ifdef DEBUG
   nsAutoString name;
   aEventNameWithOn->ToString(name);
--- a/dom/events/MessageEvent.cpp
+++ b/dom/events/MessageEvent.cpp
@@ -114,24 +114,33 @@ MessageEvent::GetSource(Nullable<OwningW
 
 /* static */ already_AddRefed<MessageEvent>
 MessageEvent::Constructor(const GlobalObject& aGlobal,
                           const nsAString& aType,
                           const MessageEventInit& aParam,
                           ErrorResult& aRv)
 {
   nsCOMPtr<EventTarget> t = do_QueryInterface(aGlobal.GetAsSupports());
-  nsRefPtr<MessageEvent> event = new MessageEvent(t, nullptr, nullptr);
+  return Constructor(t, aType, aParam, aRv);
+}
+
+/* static */ already_AddRefed<MessageEvent>
+MessageEvent::Constructor(EventTarget* aEventTarget,
+                          const nsAString& aType,
+                          const MessageEventInit& aParam,
+                          ErrorResult& aRv)
+{
+  nsRefPtr<MessageEvent> event = new MessageEvent(aEventTarget, nullptr, nullptr);
 
   aRv = event->InitEvent(aType, aParam.mBubbles, aParam.mCancelable);
   if (aRv.Failed()) {
     return nullptr;
   }
 
-  bool trusted = event->Init(t);
+  bool trusted = event->Init(aEventTarget);
   event->SetTrusted(trusted);
 
   event->mData = aParam.mData;
 
   mozilla::HoldJSObjects(event.get());
 
   if (aParam.mOrigin.WasPassed()) {
     event->mOrigin = aParam.mOrigin.Value();
--- a/dom/events/MessageEvent.h
+++ b/dom/events/MessageEvent.h
@@ -69,16 +69,22 @@ public:
   }
 
   static already_AddRefed<MessageEvent>
   Constructor(const GlobalObject& aGlobal,
               const nsAString& aType,
               const MessageEventInit& aEventInit,
               ErrorResult& aRv);
 
+  static already_AddRefed<MessageEvent>
+  Constructor(EventTarget* aEventTarget,
+              const nsAString& aType,
+              const MessageEventInit& aEventInit,
+              ErrorResult& aRv);
+
 protected:
   ~MessageEvent();
 
 private:
   JS::Heap<JS::Value> mData;
   nsString mOrigin;
   nsString mLastEventId;
   nsCOMPtr<nsIDOMWindow> mWindowSource;
--- a/dom/moz.build
+++ b/dom/moz.build
@@ -88,16 +88,17 @@ DIRS += [
     'plugins/ipc',
     'indexedDB',
     'system',
     'ipc',
     'identity',
     'workers',
     'camera',
     'audiochannel',
+    'broadcastchannel',
     'promise',
     'smil',
     'telephony',
     'tv',
     'voicemail',
     'inputmethod',
     'webidl',
     'xbl',
--- a/dom/tests/mochitest/general/test_interfaces.html
+++ b/dom/tests/mochitest/general/test_interfaces.html
@@ -195,16 +195,18 @@ var interfaceNamesInGlobalScope =
      permission: ["bluetooth"]},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "BluetoothManager", b2g: true, permission: ["bluetooth"]},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "BluetoothStatusChangedEvent", b2g: true, permission: ["bluetooth"]},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "BoxObject", xbl: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
+    "BroadcastChannel",
+// IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "CallEvent", b2g: true, pref: "dom.telephony.enabled"},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "CallGroupErrorEvent", b2g: true, pref: "dom.telephony.enabled"},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "CameraCapabilities", b2g: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "CameraClosedEvent", b2g: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
new file mode 100644
--- /dev/null
+++ b/dom/webidl/BroadcastChannel.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/.
+ *
+ * For more information on this interface, please see
+ * http://www.whatwg.org/specs/web-apps/current-work/multipage/web-messaging.html#broadcasting-to-other-browsing-contexts
+ */
+
+[Constructor(DOMString channel),
+ Exposed=(Window,Worker)]
+interface BroadcastChannel : EventTarget {
+  readonly attribute DOMString name;
+
+  void postMessage(DOMString message);
+
+           attribute EventHandler onmessage;
+};
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -50,16 +50,17 @@ WEBIDL_FILES = [
     'AutocompleteInfo.webidl',
     'BarProp.webidl',
     'BatteryManager.webidl',
     'BeforeAfterKeyboardEvent.webidl',
     'BeforeUnloadEvent.webidl',
     'BiquadFilterNode.webidl',
     'Blob.webidl',
     'BoxObject.webidl',
+    'BroadcastChannel.webidl',
     'BrowserElement.webidl',
     'BrowserElementDictionaries.webidl',
     'CallsList.webidl',
     'CameraCapabilities.webidl',
     'CameraControl.webidl',
     'CameraManager.webidl',
     'CameraUtil.webidl',
     'CanvasRenderingContext2D.webidl',
--- a/ipc/glue/BackgroundChildImpl.cpp
+++ b/ipc/glue/BackgroundChildImpl.cpp
@@ -1,15 +1,16 @@
 /* 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 "BackgroundChildImpl.h"
 
 #include "ActorsChild.h" // IndexedDB
+#include "BroadcastChannelChild.h"
 #include "FileDescriptorSetChild.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/dom/PBlobChild.h"
 #include "mozilla/dom/indexedDB/PBackgroundIDBFactoryChild.h"
 #include "mozilla/dom/ipc/BlobChild.h"
 #include "mozilla/ipc/PBackgroundTestChild.h"
 #include "mozilla/layout/VsyncChild.h"
 #include "nsID.h"
@@ -196,16 +197,40 @@ bool
 BackgroundChildImpl::DeallocPVsyncChild(PVsyncChild* aActor)
 {
   MOZ_ASSERT(aActor);
 
   delete static_cast<mozilla::layout::VsyncChild*>(aActor);
   return true;
 }
 
+// -----------------------------------------------------------------------------
+// BroadcastChannel API
+// -----------------------------------------------------------------------------
+
+dom::PBroadcastChannelChild*
+BackgroundChildImpl::AllocPBroadcastChannelChild(const PrincipalInfo& aPrincipalInfo,
+                                                 const nsString& aOrigin,
+                                                 const nsString& aChannel)
+{
+  nsRefPtr<dom::BroadcastChannelChild> agent =
+    new dom::BroadcastChannelChild(aOrigin, aChannel);
+  return agent.forget().take();
+}
+
+bool
+BackgroundChildImpl::DeallocPBroadcastChannelChild(
+                                                 PBroadcastChannelChild* aActor)
+{
+  nsRefPtr<dom::BroadcastChannelChild> child =
+    dont_AddRef(static_cast<dom::BroadcastChannelChild*>(aActor));
+  MOZ_ASSERT(child);
+  return true;
+}
+
 } // namespace ipc
 } // namespace mozilla
 
 bool
 TestChild::Recv__delete__(const nsCString& aTestArg)
 {
   MOZ_RELEASE_ASSERT(aTestArg == mTestArg,
                      "BackgroundTest message was corrupted!");
--- a/ipc/glue/BackgroundChildImpl.h
+++ b/ipc/glue/BackgroundChildImpl.h
@@ -71,16 +71,24 @@ protected:
   virtual bool
   DeallocPFileDescriptorSetChild(PFileDescriptorSetChild* aActor) MOZ_OVERRIDE;
 
   virtual PVsyncChild*
   AllocPVsyncChild() MOZ_OVERRIDE;
 
   virtual bool
   DeallocPVsyncChild(PVsyncChild* aActor) MOZ_OVERRIDE;
+
+  virtual PBroadcastChannelChild*
+  AllocPBroadcastChannelChild(const PrincipalInfo& aPrincipalInfo,
+                              const nsString& aOrigin,
+                              const nsString& aChannel) MOZ_OVERRIDE;
+
+  virtual bool
+  DeallocPBroadcastChannelChild(PBroadcastChannelChild* aActor) MOZ_OVERRIDE;
 };
 
 class BackgroundChildImpl::ThreadLocal MOZ_FINAL
 {
   friend class nsAutoPtr<ThreadLocal>;
 
 public:
   nsAutoPtr<mozilla::dom::indexedDB::ThreadLocal> mIndexedDBThreadLocal;
--- a/ipc/glue/BackgroundParentImpl.cpp
+++ b/ipc/glue/BackgroundParentImpl.cpp
@@ -1,22 +1,27 @@
 /* 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 "BackgroundParentImpl.h"
 
+#include "BroadcastChannelParent.h"
 #include "FileDescriptorSetParent.h"
+#include "mozilla/AppProcessChecker.h"
 #include "mozilla/Assertions.h"
+#include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/PBlobParent.h"
 #include "mozilla/dom/indexedDB/ActorsParent.h"
 #include "mozilla/dom/ipc/BlobParent.h"
 #include "mozilla/ipc/BackgroundParent.h"
+#include "mozilla/ipc/BackgroundUtils.h"
 #include "mozilla/ipc/PBackgroundTestParent.h"
 #include "mozilla/layout/VsyncParent.h"
+#include "nsNetUtil.h"
 #include "nsRefPtr.h"
 #include "nsThreadUtils.h"
 #include "nsTraceRefcnt.h"
 #include "nsXULAppAPI.h"
 
 #ifdef DISABLE_ASSERTS_FOR_FUZZING
 #define ASSERT_UNLESS_FUZZING(...) do { } while (0)
 #else
@@ -59,16 +64,19 @@ public:
   ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE;
 };
 
 } // anonymous namespace
 
 namespace mozilla {
 namespace ipc {
 
+using mozilla::dom::ContentParent;
+using mozilla::dom::BroadcastChannelParent;
+
 BackgroundParentImpl::BackgroundParentImpl()
 {
   AssertIsInMainProcess();
   AssertIsOnMainThread();
 
   MOZ_COUNT_CTOR(mozilla::ipc::BackgroundParentImpl);
 }
 
@@ -230,16 +238,126 @@ BackgroundParentImpl::DeallocPVsyncParen
   MOZ_ASSERT(aActor);
 
   // This actor already has one ref-count. Please check AllocPVsyncParent().
   nsRefPtr<mozilla::layout::VsyncParent> actor =
       dont_AddRef(static_cast<mozilla::layout::VsyncParent*>(aActor));
   return true;
 }
 
+mozilla::dom::PBroadcastChannelParent*
+BackgroundParentImpl::AllocPBroadcastChannelParent(
+                                            const PrincipalInfo& aPrincipalInfo,
+                                            const nsString& aOrigin,
+                                            const nsString& aChannel)
+{
+  AssertIsInMainProcess();
+  AssertIsOnBackgroundThread();
+
+  return new BroadcastChannelParent(aOrigin, aChannel);
+}
+
+namespace {
+
+class CheckPrincipalRunnable MOZ_FINAL : public nsRunnable
+{
+public:
+  CheckPrincipalRunnable(already_AddRefed<ContentParent> aParent,
+                         const PrincipalInfo& aPrincipalInfo,
+                         const nsString& aOrigin)
+    : mContentParent(aParent)
+    , mPrincipalInfo(aPrincipalInfo)
+    , mOrigin(aOrigin)
+    , mBackgroundThread(NS_GetCurrentThread())
+  {
+    AssertIsInMainProcess();
+    AssertIsOnBackgroundThread();
+
+    MOZ_ASSERT(mContentParent);
+    MOZ_ASSERT(mBackgroundThread);
+  }
+
+  NS_IMETHODIMP Run()
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+
+    nsCOMPtr<nsIPrincipal> principal = PrincipalInfoToPrincipal(mPrincipalInfo);
+    AssertAppPrincipal(mContentParent, principal);
+
+    bool isNullPrincipal;
+    nsresult rv = principal->GetIsNullPrincipal(&isNullPrincipal);
+    if (NS_WARN_IF(NS_FAILED(rv)) || isNullPrincipal) {
+      mContentParent->KillHard();
+      return NS_OK;
+    }
+
+    nsCOMPtr<nsIURI> uri;
+    rv = NS_NewURI(getter_AddRefs(uri), mOrigin);
+    if (NS_FAILED(rv) || !uri) {
+      mContentParent->KillHard();
+      return NS_OK;
+    }
+
+    rv = principal->CheckMayLoad(uri, false, false);
+    if (NS_FAILED(rv)) {
+      mContentParent->KillHard();
+      return NS_OK;
+    }
+
+    mContentParent = nullptr;
+    return NS_OK;
+  }
+
+private:
+  nsRefPtr<ContentParent> mContentParent;
+  PrincipalInfo mPrincipalInfo;
+  nsString mOrigin;
+  nsCOMPtr<nsIThread> mBackgroundThread;
+};
+
+} // anonymous namespace
+
+bool
+BackgroundParentImpl::RecvPBroadcastChannelConstructor(
+                                            PBroadcastChannelParent* actor,
+                                            const PrincipalInfo& aPrincipalInfo,
+                                            const nsString& aOrigin,
+                                            const nsString& aChannel)
+{
+  AssertIsInMainProcess();
+  AssertIsOnBackgroundThread();
+
+  nsRefPtr<ContentParent> parent = BackgroundParent::GetContentParent(this);
+
+  // If the ContentParent is null we are dealing with a same-process actor.
+  if (!parent) {
+    MOZ_ASSERT(aPrincipalInfo.type() != PrincipalInfo::TNullPrincipalInfo);
+    return true;
+  }
+
+  nsRefPtr<CheckPrincipalRunnable> runnable =
+    new CheckPrincipalRunnable(parent.forget(), aPrincipalInfo, aOrigin);
+  nsresult rv = NS_DispatchToMainThread(runnable);
+  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(rv));
+
+  return true;
+}
+
+bool
+BackgroundParentImpl::DeallocPBroadcastChannelParent(
+                                                PBroadcastChannelParent* aActor)
+{
+  AssertIsInMainProcess();
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(aActor);
+
+  delete static_cast<BroadcastChannelParent*>(aActor);
+  return true;
+}
+
 } // namespace ipc
 } // namespace mozilla
 
 void
 TestParent::ActorDestroy(ActorDestroyReason aWhy)
 {
   AssertIsInMainProcess();
   AssertIsOnBackgroundThread();
--- a/ipc/glue/BackgroundParentImpl.h
+++ b/ipc/glue/BackgroundParentImpl.h
@@ -64,14 +64,28 @@ protected:
   DeallocPFileDescriptorSetParent(PFileDescriptorSetParent* aActor)
                                   MOZ_OVERRIDE;
 
   virtual PVsyncParent*
   AllocPVsyncParent() MOZ_OVERRIDE;
 
   virtual bool
   DeallocPVsyncParent(PVsyncParent* aActor) MOZ_OVERRIDE;
+
+  virtual PBroadcastChannelParent*
+  AllocPBroadcastChannelParent(const PrincipalInfo& aPrincipalInfo,
+                               const nsString& aOrigin,
+                               const nsString& aChannel) MOZ_OVERRIDE;
+
+  virtual bool
+  RecvPBroadcastChannelConstructor(PBroadcastChannelParent* actor,
+                                   const PrincipalInfo& aPrincipalInfo,
+                                   const nsString& origin,
+                                   const nsString& channel) MOZ_OVERRIDE;
+
+  virtual bool
+  DeallocPBroadcastChannelParent(PBroadcastChannelParent* aActor) MOZ_OVERRIDE;
 };
 
 } // namespace ipc
 } // namespace mozilla
 
 #endif // mozilla_ipc_backgroundparentimpl_h__
--- a/ipc/glue/PBackground.ipdl
+++ b/ipc/glue/PBackground.ipdl
@@ -1,40 +1,45 @@
 /* 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 protocol PBackgroundIDBFactory;
 include protocol PBackgroundTest;
 include protocol PBlob;
+include protocol PBroadcastChannel;
 include protocol PFileDescriptorSet;
 include protocol PVsync;
 
 include DOMTypes;
+include PBackgroundSharedTypes;
 include PBackgroundIDBSharedTypes;
 
 namespace mozilla {
 namespace ipc {
 
 sync protocol PBackground
 {
   manages PBackgroundIDBFactory;
   manages PBackgroundTest;
   manages PBlob;
+  manages PBroadcastChannel;
   manages PFileDescriptorSet;
   manages PVsync;
 
 parent:
   // Only called at startup during mochitests to check the basic infrastructure.
   PBackgroundTest(nsCString testArg);
 
   PBackgroundIDBFactory(LoggingInfo loggingInfo);
 
   PVsync();
 
+  PBroadcastChannel(PrincipalInfo pInfo, nsString origin, nsString channel);
+
 both:
   PBlob(BlobConstructorParams params);
 
   PFileDescriptorSet(FileDescriptor fd);
 };
 
 } // namespace ipc
 } // namespace mozilla
--- a/ipc/glue/moz.build
+++ b/ipc/glue/moz.build
@@ -125,16 +125,17 @@ SOURCES += [
     'BackgroundParentImpl.cpp',
     'FileDescriptorSetChild.cpp',
     'FileDescriptorSetParent.cpp',
     'GeckoChildProcessHost.cpp',
     'URIUtils.cpp',
 ]
 
 LOCAL_INCLUDES += [
+    '/dom/broadcastchannel',
     '/dom/indexedDB',
     '/xpcom/build',
 ]
 
 IPDL_SOURCES = [
     'InputStreamParams.ipdlh',
     'PBackground.ipdl',
     'PBackgroundSharedTypes.ipdlh',