зеркало из https://github.com/mozilla/gecko-dev.git
Bug 966439 - BroadcastChannel API - patch 1 - BroadcastChannel for main-thread, r=smaug, r=bent
This commit is contained in:
Родитель
e9973161c9
Коммит
65afdf13d8
|
@ -0,0 +1,516 @@
|
|||
/* -*- 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 : public workers::WorkerFeature
|
||||
{
|
||||
BroadcastChannel* mChannel;
|
||||
|
||||
public:
|
||||
BroadcastChannelFeature(BroadcastChannel* aChannel)
|
||||
: mChannel(aChannel)
|
||||
{
|
||||
MOZ_ASSERT(aChannel);
|
||||
}
|
||||
|
||||
virtual bool Notify(JSContext* aCx, workers::Status aStatus) MOZ_OVERRIDE
|
||||
{
|
||||
if (aStatus >= Canceling) {
|
||||
WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(aCx);
|
||||
MOZ_ASSERT(workerPrivate);
|
||||
|
||||
mChannel->Shutdown();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
} // 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
|
|
@ -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;
|
||||
|
||||
workers::WorkerFeature* mWorkerFeature;
|
||||
|
||||
PrincipalInfo mPrincipalInfo;
|
||||
nsString mOrigin;
|
||||
nsString mChannel;
|
||||
|
||||
bool mIsKeptAlive;
|
||||
|
||||
uint64_t mInnerID;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_BroadcastChannel_h
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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");
|
||||
}
|
|
@ -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>
|
||||
|
||||
|
|
@ -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]
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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>
|
|
@ -101,6 +101,11 @@ public:
|
|||
return static_cast<DOMEventTargetHelper*>(target);
|
||||
}
|
||||
|
||||
bool HasListenersFor(const nsAString& aType)
|
||||
{
|
||||
return mListenerManager && mListenerManager->HasListenersFor(aType);
|
||||
}
|
||||
|
||||
bool HasListenersFor(nsIAtom* aTypeWithOn)
|
||||
{
|
||||
return mListenerManager && mListenerManager->HasListenersFor(aTypeWithOn);
|
||||
|
|
|
@ -1263,8 +1263,19 @@ EventListenerManager::MutationListenerBits()
|
|||
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
|
||||
|
|
|
@ -119,14 +119,23 @@ MessageEvent::Constructor(const GlobalObject& aGlobal,
|
|||
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;
|
||||
|
|
|
@ -74,6 +74,12 @@ public:
|
|||
const MessageEventInit& aEventInit,
|
||||
ErrorResult& aRv);
|
||||
|
||||
static already_AddRefed<MessageEvent>
|
||||
Constructor(EventTarget* aEventTarget,
|
||||
const nsAString& aType,
|
||||
const MessageEventInit& aEventInit,
|
||||
ErrorResult& aRv);
|
||||
|
||||
protected:
|
||||
~MessageEvent();
|
||||
|
||||
|
|
|
@ -93,6 +93,7 @@ DIRS += [
|
|||
'workers',
|
||||
'camera',
|
||||
'audiochannel',
|
||||
'broadcastchannel',
|
||||
'promise',
|
||||
'smil',
|
||||
'telephony',
|
||||
|
|
|
@ -199,6 +199,8 @@ var interfaceNamesInGlobalScope =
|
|||
{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!
|
||||
|
|
|
@ -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;
|
||||
};
|
|
@ -55,6 +55,7 @@ WEBIDL_FILES = [
|
|||
'BiquadFilterNode.webidl',
|
||||
'Blob.webidl',
|
||||
'BoxObject.webidl',
|
||||
'BroadcastChannel.webidl',
|
||||
'BrowserElement.webidl',
|
||||
'BrowserElementDictionaries.webidl',
|
||||
'CallsList.webidl',
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include "BackgroundChildImpl.h"
|
||||
|
||||
#include "ActorsChild.h" // IndexedDB
|
||||
#include "BroadcastChannelChild.h"
|
||||
#include "FileDescriptorSetChild.h"
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/dom/PBlobChild.h"
|
||||
|
@ -185,6 +186,30 @@ BackgroundChildImpl::DeallocPFileDescriptorSetChild(
|
|||
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
|
||||
|
||||
|
|
|
@ -70,6 +70,14 @@ protected:
|
|||
|
||||
virtual bool
|
||||
DeallocPFileDescriptorSetChild(PFileDescriptorSetChild* 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
|
||||
|
|
|
@ -4,13 +4,18 @@
|
|||
|
||||
#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 "nsNetUtil.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "nsTraceRefcnt.h"
|
||||
#include "nsXULAppAPI.h"
|
||||
|
@ -62,6 +67,9 @@ public:
|
|||
namespace mozilla {
|
||||
namespace ipc {
|
||||
|
||||
using mozilla::dom::ContentParent;
|
||||
using mozilla::dom::BroadcastChannelParent;
|
||||
|
||||
BackgroundParentImpl::BackgroundParentImpl()
|
||||
{
|
||||
AssertIsInMainProcess();
|
||||
|
@ -207,6 +215,116 @@ BackgroundParentImpl::DeallocPFileDescriptorSetParent(
|
|||
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
|
||||
|
||||
|
|
|
@ -58,6 +58,20 @@ protected:
|
|||
virtual bool
|
||||
DeallocPFileDescriptorSetParent(PFileDescriptorSetParent* 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
|
||||
|
|
|
@ -5,9 +5,11 @@
|
|||
include protocol PBackgroundIDBFactory;
|
||||
include protocol PBackgroundTest;
|
||||
include protocol PBlob;
|
||||
include protocol PBroadcastChannel;
|
||||
include protocol PFileDescriptorSet;
|
||||
|
||||
include DOMTypes;
|
||||
include PBackgroundSharedTypes;
|
||||
include PBackgroundIDBSharedTypes;
|
||||
|
||||
namespace mozilla {
|
||||
|
@ -18,6 +20,7 @@ sync protocol PBackground
|
|||
manages PBackgroundIDBFactory;
|
||||
manages PBackgroundTest;
|
||||
manages PBlob;
|
||||
manages PBroadcastChannel;
|
||||
manages PFileDescriptorSet;
|
||||
|
||||
parent:
|
||||
|
@ -26,6 +29,8 @@ parent:
|
|||
|
||||
PBackgroundIDBFactory(LoggingInfo loggingInfo);
|
||||
|
||||
PBroadcastChannel(PrincipalInfo pInfo, nsString origin, nsString channel);
|
||||
|
||||
both:
|
||||
PBlob(BlobConstructorParams params);
|
||||
|
||||
|
|
|
@ -130,6 +130,7 @@ SOURCES += [
|
|||
]
|
||||
|
||||
LOCAL_INCLUDES += [
|
||||
'/dom/broadcastchannel',
|
||||
'/dom/indexedDB',
|
||||
'/xpcom/build',
|
||||
]
|
||||
|
|
Загрузка…
Ссылка в новой задаче