зеркало из https://github.com/mozilla/gecko-dev.git
785 строки
18 KiB
C++
785 строки
18 KiB
C++
/* -*- 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/dom/Navigator.h"
|
|
#include "mozilla/dom/StructuredCloneUtils.h"
|
|
#include "mozilla/ipc/BackgroundChild.h"
|
|
#include "mozilla/ipc/BackgroundUtils.h"
|
|
#include "mozilla/ipc/PBackgroundChild.h"
|
|
#include "mozilla/Preferences.h"
|
|
#include "WorkerPrivate.h"
|
|
#include "WorkerRunnable.h"
|
|
|
|
#include "nsIAppsService.h"
|
|
#include "nsIDocument.h"
|
|
#include "nsIScriptSecurityManager.h"
|
|
#include "nsServiceManagerUtils.h"
|
|
#include "nsISupportsPrimitives.h"
|
|
|
|
#ifdef XP_WIN
|
|
#undef PostMessage
|
|
#endif
|
|
|
|
namespace mozilla {
|
|
|
|
using namespace ipc;
|
|
|
|
namespace dom {
|
|
|
|
using namespace workers;
|
|
|
|
class BroadcastChannelMessage MOZ_FINAL
|
|
{
|
|
public:
|
|
NS_INLINE_DECL_REFCOUNTING(BroadcastChannelMessage)
|
|
|
|
JSAutoStructuredCloneBuffer mBuffer;
|
|
StructuredCloneClosure mClosure;
|
|
|
|
BroadcastChannelMessage()
|
|
{ }
|
|
|
|
private:
|
|
~BroadcastChannelMessage()
|
|
{ }
|
|
};
|
|
|
|
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;
|
|
if (aOrigin.EqualsASCII("null")) {
|
|
nsCOMPtr<nsIURI> uri;
|
|
aRv = aPrincipal->GetURI(getter_AddRefs(uri));
|
|
if (NS_WARN_IF(aRv.Failed())) {
|
|
return;
|
|
}
|
|
|
|
if (NS_WARN_IF(!uri)) {
|
|
aRv.Throw(NS_ERROR_FAILURE);
|
|
return;
|
|
}
|
|
|
|
nsAutoCString spec;
|
|
aRv = uri->GetSpec(spec);
|
|
if (NS_WARN_IF(aRv.Failed())) {
|
|
return;
|
|
}
|
|
|
|
aOrigin = NS_ConvertUTF8toUTF16(spec);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
nsIPrincipal*
|
|
GetPrincipalFromWorkerPrivate(WorkerPrivate* aWorkerPrivate)
|
|
{
|
|
nsIPrincipal* principal = aWorkerPrivate->GetPrincipal();
|
|
if (principal) {
|
|
return principal;
|
|
}
|
|
|
|
// Walk up to our containing page
|
|
WorkerPrivate* wp = aWorkerPrivate;
|
|
while (wp->GetParent()) {
|
|
wp = wp->GetParent();
|
|
}
|
|
|
|
return wp->GetPrincipal();
|
|
}
|
|
|
|
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 = GetPrincipalFromWorkerPrivate(mWorkerPrivate);
|
|
if (!principal) {
|
|
mRv.Throw(NS_ERROR_FAILURE);
|
|
return true;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
// Walk up to our containing page
|
|
WorkerPrivate* wp = mWorkerPrivate;
|
|
while (wp->GetParent()) {
|
|
wp = wp->GetParent();
|
|
}
|
|
|
|
// Window doesn't exist for some kind of workers (eg: SharedWorkers)
|
|
nsPIDOMWindow* window = wp->GetWindow();
|
|
if (!window) {
|
|
return true;
|
|
}
|
|
|
|
nsIDocument* doc = window->GetExtantDoc();
|
|
// No bfcache when BroadcastChannel is used.
|
|
if (doc) {
|
|
doc->DisallowBFCaching();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private:
|
|
WorkerPrivate* mWorkerPrivate;
|
|
nsAString& mOrigin;
|
|
PrincipalInfo& mPrincipalInfo;
|
|
ErrorResult& mRv;
|
|
};
|
|
|
|
class PostMessageRunnable MOZ_FINAL : public nsICancelableRunnable
|
|
{
|
|
public:
|
|
NS_DECL_ISUPPORTS
|
|
|
|
PostMessageRunnable(BroadcastChannelChild* aActor,
|
|
BroadcastChannelMessage* aData)
|
|
: mActor(aActor)
|
|
, mData(aData)
|
|
{
|
|
MOZ_ASSERT(mActor);
|
|
}
|
|
|
|
NS_IMETHODIMP Run() MOZ_OVERRIDE
|
|
{
|
|
MOZ_ASSERT(mActor);
|
|
if (mActor->IsActorDestroyed()) {
|
|
return NS_OK;
|
|
}
|
|
|
|
ClonedMessageData message;
|
|
|
|
SerializedStructuredCloneBuffer& buffer = message.data();
|
|
buffer.data = mData->mBuffer.data();
|
|
buffer.dataLength = mData->mBuffer.nbytes();
|
|
|
|
PBackgroundChild* backgroundManager = mActor->Manager();
|
|
MOZ_ASSERT(backgroundManager);
|
|
|
|
const nsTArray<nsRefPtr<File>>& blobs = mData->mClosure.mBlobs;
|
|
|
|
if (!blobs.IsEmpty()) {
|
|
message.blobsChild().SetCapacity(blobs.Length());
|
|
|
|
for (uint32_t i = 0, len = blobs.Length(); i < len; ++i) {
|
|
PBlobChild* blobChild =
|
|
BackgroundChild::GetOrCreateActorForBlob(backgroundManager, blobs[i]);
|
|
MOZ_ASSERT(blobChild);
|
|
|
|
message.blobsChild().AppendElement(blobChild);
|
|
}
|
|
}
|
|
|
|
mActor->SendPostMessage(message);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP Cancel() MOZ_OVERRIDE
|
|
{
|
|
mActor = nullptr;
|
|
return NS_OK;
|
|
}
|
|
|
|
private:
|
|
~PostMessageRunnable() {}
|
|
|
|
nsRefPtr<BroadcastChannelChild> mActor;
|
|
nsRefPtr<BroadcastChannelMessage> mData;
|
|
};
|
|
|
|
NS_IMPL_ISUPPORTS(PostMessageRunnable, nsICancelableRunnable, nsIRunnable)
|
|
|
|
class CloseRunnable MOZ_FINAL : public nsICancelableRunnable
|
|
{
|
|
public:
|
|
NS_DECL_ISUPPORTS
|
|
|
|
explicit CloseRunnable(BroadcastChannel* aBC)
|
|
: mBC(aBC)
|
|
{
|
|
MOZ_ASSERT(mBC);
|
|
}
|
|
|
|
NS_IMETHODIMP Run() MOZ_OVERRIDE
|
|
{
|
|
mBC->Shutdown();
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP Cancel() MOZ_OVERRIDE
|
|
{
|
|
mBC = nullptr;
|
|
return NS_OK;
|
|
}
|
|
|
|
private:
|
|
~CloseRunnable() {}
|
|
|
|
nsRefPtr<BroadcastChannel> mBC;
|
|
};
|
|
|
|
NS_IMPL_ISUPPORTS(CloseRunnable, nsICancelableRunnable, nsIRunnable)
|
|
|
|
class TeardownRunnable MOZ_FINAL : public nsICancelableRunnable
|
|
{
|
|
public:
|
|
NS_DECL_ISUPPORTS
|
|
|
|
explicit TeardownRunnable(BroadcastChannelChild* aActor)
|
|
: mActor(aActor)
|
|
{
|
|
MOZ_ASSERT(mActor);
|
|
}
|
|
|
|
NS_IMETHODIMP Run() MOZ_OVERRIDE
|
|
{
|
|
MOZ_ASSERT(mActor);
|
|
if (!mActor->IsActorDestroyed()) {
|
|
mActor->SendClose();
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP Cancel() MOZ_OVERRIDE
|
|
{
|
|
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) {
|
|
mChannel->Shutdown();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private:
|
|
~BroadcastChannelFeature()
|
|
{
|
|
MOZ_COUNT_DTOR(BroadcastChannelFeature);
|
|
}
|
|
};
|
|
|
|
class PrefEnabledRunnable MOZ_FINAL : public WorkerMainThreadRunnable
|
|
{
|
|
public:
|
|
explicit PrefEnabledRunnable(WorkerPrivate* aWorkerPrivate)
|
|
: WorkerMainThreadRunnable(aWorkerPrivate)
|
|
, mEnabled(false)
|
|
{ }
|
|
|
|
bool MainThreadRun() MOZ_OVERRIDE
|
|
{
|
|
AssertIsOnMainThread();
|
|
mEnabled = Preferences::GetBool("dom.broadcastChannel.enabled", false);
|
|
return true;
|
|
}
|
|
|
|
bool IsEnabled() const
|
|
{
|
|
return mEnabled;
|
|
}
|
|
|
|
private:
|
|
bool mEnabled;
|
|
};
|
|
|
|
} // anonymous namespace
|
|
|
|
/* static */ bool
|
|
BroadcastChannel::IsEnabled(JSContext* aCx, JSObject* aGlobal)
|
|
{
|
|
if (NS_IsMainThread()) {
|
|
return Preferences::GetBool("dom.broadcastChannel.enabled", false);
|
|
}
|
|
|
|
WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
|
|
MOZ_ASSERT(workerPrivate);
|
|
workerPrivate->AssertIsOnWorkerThread();
|
|
|
|
nsRefPtr<PrefEnabledRunnable> runnable =
|
|
new PrefEnabledRunnable(workerPrivate);
|
|
runnable->Dispatch(workerPrivate->GetJSContext());
|
|
|
|
return runnable->IsEnabled();
|
|
}
|
|
|
|
BroadcastChannel::BroadcastChannel(nsPIDOMWindow* aWindow,
|
|
const PrincipalInfo& aPrincipalInfo,
|
|
const nsAString& aOrigin,
|
|
const nsAString& aChannel)
|
|
: DOMEventTargetHelper(aWindow)
|
|
, mWorkerFeature(nullptr)
|
|
, mPrincipalInfo(new PrincipalInfo(aPrincipalInfo))
|
|
, mOrigin(aOrigin)
|
|
, mChannel(aChannel)
|
|
, mIsKeptAlive(false)
|
|
, mInnerID(0)
|
|
, mState(StateActive)
|
|
{
|
|
// 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);
|
|
if (NS_WARN_IF(aRv.Failed())) {
|
|
return nullptr;
|
|
}
|
|
|
|
aRv = PrincipalToPrincipalInfo(principal, &principalInfo);
|
|
if (NS_WARN_IF(aRv.Failed())) {
|
|
return nullptr;
|
|
}
|
|
|
|
nsIDocument* doc = window->GetExtantDoc();
|
|
// No bfcache when BroadcastChannel is used.
|
|
if (doc) {
|
|
doc->DisallowBFCaching();
|
|
}
|
|
} 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.
|
|
PBackgroundChild* actor = BackgroundChild::GetForCurrentThread();
|
|
if (actor) {
|
|
bc->ActorCreated(actor);
|
|
} else {
|
|
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(JSContext* aCx, JS::Handle<JS::Value> aMessage,
|
|
ErrorResult& aRv)
|
|
{
|
|
if (mState != StateActive) {
|
|
aRv.Throw(NS_ERROR_FAILURE);
|
|
return;
|
|
}
|
|
|
|
PostMessageInternal(aCx, aMessage, aRv);
|
|
}
|
|
|
|
void
|
|
BroadcastChannel::PostMessageInternal(JSContext* aCx,
|
|
JS::Handle<JS::Value> aMessage,
|
|
ErrorResult& aRv)
|
|
{
|
|
nsRefPtr<BroadcastChannelMessage> data = new BroadcastChannelMessage();
|
|
|
|
if (!WriteStructuredClone(aCx, aMessage, data->mBuffer, data->mClosure)) {
|
|
aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
|
|
return;
|
|
}
|
|
|
|
const nsTArray<nsRefPtr<File>>& blobs = data->mClosure.mBlobs;
|
|
for (uint32_t i = 0, len = blobs.Length(); i < len; ++i) {
|
|
if (!blobs[i]->Impl()->MayBeClonedToOtherThreads()) {
|
|
aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
|
|
return;
|
|
}
|
|
}
|
|
|
|
PostMessageData(data);
|
|
}
|
|
|
|
void
|
|
BroadcastChannel::PostMessageData(BroadcastChannelMessage* aData)
|
|
{
|
|
if (mActor) {
|
|
nsRefPtr<PostMessageRunnable> runnable =
|
|
new PostMessageRunnable(mActor, aData);
|
|
|
|
if (NS_FAILED(NS_DispatchToCurrentThread(runnable))) {
|
|
NS_WARNING("Failed to dispatch to the current thread!");
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
mPendingMessages.AppendElement(aData);
|
|
}
|
|
|
|
void
|
|
BroadcastChannel::Close()
|
|
{
|
|
if (mState != StateActive) {
|
|
return;
|
|
}
|
|
|
|
if (mPendingMessages.IsEmpty()) {
|
|
// We cannot call Shutdown() immediatelly because we could have some
|
|
// postMessage runnable already dispatched. Instead, we change the state to
|
|
// StateClosed and we shutdown the actor asynchrounsly.
|
|
|
|
mState = StateClosed;
|
|
nsRefPtr<CloseRunnable> runnable = new CloseRunnable(this);
|
|
|
|
if (NS_FAILED(NS_DispatchToCurrentThread(runnable))) {
|
|
NS_WARNING("Failed to dispatch to the current thread!");
|
|
}
|
|
} else {
|
|
MOZ_ASSERT(!mActor);
|
|
mState = StateClosing;
|
|
}
|
|
}
|
|
|
|
void
|
|
BroadcastChannel::ActorFailed()
|
|
{
|
|
MOZ_CRASH("Failed to create a PBackgroundChild actor!");
|
|
}
|
|
|
|
void
|
|
BroadcastChannel::ActorCreated(PBackgroundChild* aActor)
|
|
{
|
|
MOZ_ASSERT(aActor);
|
|
|
|
if (mState == StateClosed) {
|
|
return;
|
|
}
|
|
|
|
PBroadcastChannelChild* actor =
|
|
aActor->SendPBroadcastChannelConstructor(*mPrincipalInfo, mOrigin, mChannel);
|
|
|
|
mActor = static_cast<BroadcastChannelChild*>(actor);
|
|
MOZ_ASSERT(mActor);
|
|
|
|
mActor->SetParent(this);
|
|
|
|
// Flush pending messages.
|
|
for (uint32_t i = 0; i < mPendingMessages.Length(); ++i) {
|
|
PostMessageData(mPendingMessages[i]);
|
|
}
|
|
|
|
mPendingMessages.Clear();
|
|
|
|
if (mState == StateClosing) {
|
|
Shutdown();
|
|
}
|
|
}
|
|
|
|
void
|
|
BroadcastChannel::Shutdown()
|
|
{
|
|
mState = StateClosed;
|
|
|
|
// 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->SetParent(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
|