forked from mirrors/gecko-dev
Backed out changeset 1db9723f1617 (bug 1165486) Backed out changeset 662ec8b0561e (bug 1165486) Backed out changeset 97352c48fb98 (bug 1165486) Backed out changeset 4143cca42cc7 (bug 1165486) Backed out changeset 5abb37cb0495 (bug 1165486) Backed out changeset 7b157964c572 (bug 1165486) Backed out changeset 5c01ab1d9790 (bug 1165486) Backed out changeset 0e00d8a3ca1d (bug 1165486) Backed out changeset 5e88d482f5e8 (bug 1165486) Backed out changeset 513bbca6fb6c (bug 1165486) Backed out changeset 64fa28f0255a (bug 1165486) Backed out changeset 7a404929c74c (bug 1165486) Backed out changeset 1dc0d6adf6ed (bug 1165486) Backed out changeset 381003ca79a3 (bug 1165486) CLOSED TREE
2230 lines
72 KiB
C++
2230 lines
72 KiB
C++
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#include "base/basictypes.h"
|
|
|
|
#include "nsFrameMessageManager.h"
|
|
|
|
#include "AppProcessChecker.h"
|
|
#include "ContentChild.h"
|
|
#include "nsContentUtils.h"
|
|
#include "nsError.h"
|
|
#include "nsIXPConnect.h"
|
|
#include "jsapi.h"
|
|
#include "jsfriendapi.h"
|
|
#include "nsJSUtils.h"
|
|
#include "nsJSPrincipals.h"
|
|
#include "nsNetUtil.h"
|
|
#include "nsScriptLoader.h"
|
|
#include "nsFrameLoader.h"
|
|
#include "nsIXULRuntime.h"
|
|
#include "nsIScriptError.h"
|
|
#include "nsIConsoleService.h"
|
|
#include "nsIMemoryReporter.h"
|
|
#include "nsIProtocolHandler.h"
|
|
#include "nsIScriptSecurityManager.h"
|
|
#include "nsIJSRuntimeService.h"
|
|
#include "nsIDOMClassInfo.h"
|
|
#include "xpcpublic.h"
|
|
#include "mozilla/CycleCollectedJSRuntime.h"
|
|
#include "mozilla/IntentionalCrash.h"
|
|
#include "mozilla/Preferences.h"
|
|
#include "mozilla/dom/File.h"
|
|
#include "mozilla/dom/nsIContentParent.h"
|
|
#include "mozilla/dom/PermissionMessageUtils.h"
|
|
#include "mozilla/dom/ProcessGlobal.h"
|
|
#include "mozilla/dom/SameProcessMessageQueue.h"
|
|
#include "mozilla/dom/ScriptSettings.h"
|
|
#include "mozilla/dom/StructuredCloneUtils.h"
|
|
#include "mozilla/dom/ipc/BlobChild.h"
|
|
#include "mozilla/dom/ipc/BlobParent.h"
|
|
#include "mozilla/dom/DOMStringList.h"
|
|
#include "mozilla/jsipc/CrossProcessObjectWrappers.h"
|
|
#include "nsPrintfCString.h"
|
|
#include "nsXULAppAPI.h"
|
|
#include "nsQueryObject.h"
|
|
#include <algorithm>
|
|
|
|
#ifdef ANDROID
|
|
#include <android/log.h>
|
|
#endif
|
|
#ifdef XP_WIN
|
|
#include <windows.h>
|
|
# if defined(SendMessage)
|
|
# undef SendMessage
|
|
# endif
|
|
#endif
|
|
|
|
using namespace mozilla;
|
|
using namespace mozilla::dom;
|
|
using namespace mozilla::dom::ipc;
|
|
|
|
nsFrameMessageManager::nsFrameMessageManager(mozilla::dom::ipc::MessageManagerCallback* aCallback,
|
|
nsFrameMessageManager* aParentManager,
|
|
/* mozilla::dom::ipc::MessageManagerFlags */ uint32_t aFlags)
|
|
: mChrome(!!(aFlags & mozilla::dom::ipc::MM_CHROME)),
|
|
mGlobal(!!(aFlags & mozilla::dom::ipc::MM_GLOBAL)),
|
|
mIsProcessManager(!!(aFlags & mozilla::dom::ipc::MM_PROCESSMANAGER)),
|
|
mIsBroadcaster(!!(aFlags & mozilla::dom::ipc::MM_BROADCASTER)),
|
|
mOwnsCallback(!!(aFlags & mozilla::dom::ipc::MM_OWNSCALLBACK)),
|
|
mHandlingMessage(false),
|
|
mClosed(false),
|
|
mDisconnected(false),
|
|
mCallback(aCallback),
|
|
mParentManager(aParentManager)
|
|
{
|
|
NS_ASSERTION(mChrome || !aParentManager, "Should not set parent manager!");
|
|
NS_ASSERTION(!mIsBroadcaster || !mCallback,
|
|
"Broadcasters cannot have callbacks!");
|
|
if (mIsProcessManager && (!mChrome || IsBroadcaster())) {
|
|
mozilla::HoldJSObjects(this);
|
|
}
|
|
// This is a bit hackish. When parent manager is global, we want
|
|
// to attach the message manager to it immediately.
|
|
// Is it just the frame message manager which waits until the
|
|
// content process is running.
|
|
if (mParentManager && (mCallback || IsBroadcaster())) {
|
|
mParentManager->AddChildManager(this);
|
|
}
|
|
if (mOwnsCallback) {
|
|
mOwnedCallback = aCallback;
|
|
}
|
|
}
|
|
|
|
nsFrameMessageManager::~nsFrameMessageManager()
|
|
{
|
|
if (mIsProcessManager && (!mChrome || IsBroadcaster())) {
|
|
mozilla::DropJSObjects(this);
|
|
}
|
|
for (int32_t i = mChildManagers.Count(); i > 0; --i) {
|
|
static_cast<nsFrameMessageManager*>(mChildManagers[i - 1])->
|
|
Disconnect(false);
|
|
}
|
|
if (mIsProcessManager) {
|
|
if (this == sParentProcessManager) {
|
|
sParentProcessManager = nullptr;
|
|
}
|
|
if (this == sChildProcessManager) {
|
|
sChildProcessManager = nullptr;
|
|
delete mozilla::dom::SameProcessMessageQueue::Get();
|
|
}
|
|
if (this == sSameProcessParentManager) {
|
|
sSameProcessParentManager = nullptr;
|
|
}
|
|
}
|
|
}
|
|
|
|
static PLDHashOperator
|
|
CycleCollectorTraverseListeners(const nsAString& aKey,
|
|
nsAutoTObserverArray<nsMessageListenerInfo, 1>* aListeners,
|
|
void* aCb)
|
|
{
|
|
nsCycleCollectionTraversalCallback* cb =
|
|
static_cast<nsCycleCollectionTraversalCallback*> (aCb);
|
|
uint32_t count = aListeners->Length();
|
|
for (uint32_t i = 0; i < count; ++i) {
|
|
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, "listeners[i] mStrongListener");
|
|
cb->NoteXPCOMChild(aListeners->ElementAt(i).mStrongListener.get());
|
|
}
|
|
return PL_DHASH_NEXT;
|
|
}
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_CLASS(nsFrameMessageManager)
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsFrameMessageManager)
|
|
tmp->mListeners.EnumerateRead(CycleCollectorTraverseListeners,
|
|
static_cast<void*>(&cb));
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChildManagers)
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsFrameMessageManager)
|
|
NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mInitialProcessData)
|
|
NS_IMPL_CYCLE_COLLECTION_TRACE_END
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsFrameMessageManager)
|
|
tmp->mListeners.Clear();
|
|
for (int32_t i = tmp->mChildManagers.Count(); i > 0; --i) {
|
|
static_cast<nsFrameMessageManager*>(tmp->mChildManagers[i - 1])->
|
|
Disconnect(false);
|
|
}
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mChildManagers)
|
|
tmp->mInitialProcessData.setNull();
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
|
|
|
|
|
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsFrameMessageManager)
|
|
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContentFrameMessageManager)
|
|
|
|
/* nsFrameMessageManager implements nsIMessageSender and nsIMessageBroadcaster,
|
|
* both of which descend from nsIMessageListenerManager. QI'ing to
|
|
* nsIMessageListenerManager is therefore ambiguous and needs explicit casts
|
|
* depending on which child interface applies. */
|
|
NS_INTERFACE_MAP_ENTRY_AGGREGATED(nsIMessageListenerManager,
|
|
(mIsBroadcaster ?
|
|
static_cast<nsIMessageListenerManager*>(
|
|
static_cast<nsIMessageBroadcaster*>(this)) :
|
|
static_cast<nsIMessageListenerManager*>(
|
|
static_cast<nsIMessageSender*>(this))))
|
|
|
|
/* Message managers in child process implement nsIMessageSender and
|
|
nsISyncMessageSender. Message managers in the chrome process are
|
|
either broadcasters (if they have subordinate/child message
|
|
managers) or they're simple message senders. */
|
|
NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsISyncMessageSender, !mChrome)
|
|
NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIMessageSender, !mChrome || !mIsBroadcaster)
|
|
NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIMessageBroadcaster, mChrome && mIsBroadcaster)
|
|
|
|
/* nsIContentFrameMessageManager is accessible only in TabChildGlobal. */
|
|
NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIContentFrameMessageManager,
|
|
!mChrome && !mIsProcessManager)
|
|
|
|
/* Frame message managers (non-process message managers) support nsIFrameScriptLoader. */
|
|
NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIFrameScriptLoader,
|
|
mChrome && !mIsProcessManager)
|
|
|
|
/* Process message managers (process message managers) support nsIProcessScriptLoader. */
|
|
NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIProcessScriptLoader,
|
|
mChrome && mIsProcessManager)
|
|
|
|
/* Global process message managers (process message managers) support nsIGlobalProcessScriptLoader. */
|
|
NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIGlobalProcessScriptLoader,
|
|
mChrome && mIsProcessManager && mIsBroadcaster)
|
|
|
|
/* Message senders in the chrome process support nsIProcessChecker. */
|
|
NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIProcessChecker,
|
|
mChrome && !mIsBroadcaster)
|
|
|
|
NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO_CONDITIONAL(ChromeMessageBroadcaster,
|
|
mChrome && mIsBroadcaster)
|
|
NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO_CONDITIONAL(ChromeMessageSender,
|
|
mChrome && !mIsBroadcaster)
|
|
NS_INTERFACE_MAP_END
|
|
|
|
NS_IMPL_CYCLE_COLLECTING_ADDREF(nsFrameMessageManager)
|
|
NS_IMPL_CYCLE_COLLECTING_RELEASE(nsFrameMessageManager)
|
|
|
|
enum ActorFlavorEnum {
|
|
Parent = 0,
|
|
Child
|
|
};
|
|
|
|
template <ActorFlavorEnum>
|
|
struct BlobTraits
|
|
{ };
|
|
|
|
template <>
|
|
struct BlobTraits<Parent>
|
|
{
|
|
typedef mozilla::dom::BlobParent BlobType;
|
|
typedef mozilla::dom::PBlobParent ProtocolType;
|
|
typedef mozilla::dom::nsIContentParent ConcreteContentManagerType;
|
|
};
|
|
|
|
template <>
|
|
struct BlobTraits<Child>
|
|
{
|
|
typedef mozilla::dom::BlobChild BlobType;
|
|
typedef mozilla::dom::PBlobChild ProtocolType;
|
|
typedef mozilla::dom::nsIContentChild ConcreteContentManagerType;
|
|
};
|
|
|
|
template<ActorFlavorEnum>
|
|
struct DataBlobs
|
|
{ };
|
|
|
|
template<>
|
|
struct DataBlobs<Parent>
|
|
{
|
|
typedef BlobTraits<Parent>::ProtocolType ProtocolType;
|
|
|
|
static InfallibleTArray<ProtocolType*>& Blobs(ClonedMessageData& aData)
|
|
{
|
|
return aData.blobsParent();
|
|
}
|
|
|
|
static const InfallibleTArray<ProtocolType*>& Blobs(const ClonedMessageData& aData)
|
|
{
|
|
return aData.blobsParent();
|
|
}
|
|
};
|
|
|
|
template<>
|
|
struct DataBlobs<Child>
|
|
{
|
|
typedef BlobTraits<Child>::ProtocolType ProtocolType;
|
|
|
|
static InfallibleTArray<ProtocolType*>& Blobs(ClonedMessageData& aData)
|
|
{
|
|
return aData.blobsChild();
|
|
}
|
|
|
|
static const InfallibleTArray<ProtocolType*>& Blobs(const ClonedMessageData& aData)
|
|
{
|
|
return aData.blobsChild();
|
|
}
|
|
};
|
|
|
|
template<ActorFlavorEnum Flavor>
|
|
static bool
|
|
BuildClonedMessageData(typename BlobTraits<Flavor>::ConcreteContentManagerType* aManager,
|
|
const StructuredCloneData& aData,
|
|
ClonedMessageData& aClonedData)
|
|
{
|
|
SerializedStructuredCloneBuffer& buffer = aClonedData.data();
|
|
buffer.data = aData.mData;
|
|
buffer.dataLength = aData.mDataLength;
|
|
const nsTArray<nsRefPtr<BlobImpl>>& blobImpls = aData.mClosure.mBlobImpls;
|
|
if (!blobImpls.IsEmpty()) {
|
|
typedef typename BlobTraits<Flavor>::ProtocolType ProtocolType;
|
|
InfallibleTArray<ProtocolType*>& blobList = DataBlobs<Flavor>::Blobs(aClonedData);
|
|
uint32_t length = blobImpls.Length();
|
|
blobList.SetCapacity(length);
|
|
for (uint32_t i = 0; i < length; ++i) {
|
|
typename BlobTraits<Flavor>::BlobType* protocolActor =
|
|
aManager->GetOrCreateActorForBlobImpl(blobImpls[i]);
|
|
if (!protocolActor) {
|
|
return false;
|
|
}
|
|
blobList.AppendElement(protocolActor);
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
MessageManagerCallback::BuildClonedMessageDataForParent(nsIContentParent* aParent,
|
|
const StructuredCloneData& aData,
|
|
ClonedMessageData& aClonedData)
|
|
{
|
|
return BuildClonedMessageData<Parent>(aParent, aData, aClonedData);
|
|
}
|
|
|
|
bool
|
|
MessageManagerCallback::BuildClonedMessageDataForChild(nsIContentChild* aChild,
|
|
const StructuredCloneData& aData,
|
|
ClonedMessageData& aClonedData)
|
|
{
|
|
return BuildClonedMessageData<Child>(aChild, aData, aClonedData);
|
|
}
|
|
|
|
template<ActorFlavorEnum Flavor>
|
|
static StructuredCloneData
|
|
UnpackClonedMessageData(const ClonedMessageData& aData)
|
|
{
|
|
const SerializedStructuredCloneBuffer& buffer = aData.data();
|
|
typedef typename BlobTraits<Flavor>::ProtocolType ProtocolType;
|
|
const InfallibleTArray<ProtocolType*>& blobs = DataBlobs<Flavor>::Blobs(aData);
|
|
StructuredCloneData cloneData;
|
|
cloneData.mData = buffer.data;
|
|
cloneData.mDataLength = buffer.dataLength;
|
|
if (!blobs.IsEmpty()) {
|
|
uint32_t length = blobs.Length();
|
|
cloneData.mClosure.mBlobImpls.SetCapacity(length);
|
|
for (uint32_t i = 0; i < length; ++i) {
|
|
auto* blob =
|
|
static_cast<typename BlobTraits<Flavor>::BlobType*>(blobs[i]);
|
|
MOZ_ASSERT(blob);
|
|
|
|
nsRefPtr<BlobImpl> blobImpl = blob->GetBlobImpl();
|
|
MOZ_ASSERT(blobImpl);
|
|
|
|
cloneData.mClosure.mBlobImpls.AppendElement(blobImpl);
|
|
}
|
|
}
|
|
return cloneData;
|
|
}
|
|
|
|
StructuredCloneData
|
|
mozilla::dom::ipc::UnpackClonedMessageDataForParent(const ClonedMessageData& aData)
|
|
{
|
|
return UnpackClonedMessageData<Parent>(aData);
|
|
}
|
|
|
|
StructuredCloneData
|
|
mozilla::dom::ipc::UnpackClonedMessageDataForChild(const ClonedMessageData& aData)
|
|
{
|
|
return UnpackClonedMessageData<Child>(aData);
|
|
}
|
|
|
|
bool
|
|
SameProcessCpowHolder::ToObject(JSContext* aCx,
|
|
JS::MutableHandle<JSObject*> aObjp)
|
|
{
|
|
if (!mObj) {
|
|
return true;
|
|
}
|
|
|
|
aObjp.set(mObj);
|
|
return JS_WrapObject(aCx, aObjp);
|
|
}
|
|
|
|
// nsIMessageListenerManager
|
|
|
|
NS_IMETHODIMP
|
|
nsFrameMessageManager::AddMessageListener(const nsAString& aMessage,
|
|
nsIMessageListener* aListener,
|
|
bool aListenWhenClosed)
|
|
{
|
|
nsAutoTObserverArray<nsMessageListenerInfo, 1>* listeners =
|
|
mListeners.Get(aMessage);
|
|
if (!listeners) {
|
|
listeners = new nsAutoTObserverArray<nsMessageListenerInfo, 1>();
|
|
mListeners.Put(aMessage, listeners);
|
|
} else {
|
|
uint32_t len = listeners->Length();
|
|
for (uint32_t i = 0; i < len; ++i) {
|
|
if (listeners->ElementAt(i).mStrongListener == aListener) {
|
|
return NS_OK;
|
|
}
|
|
}
|
|
}
|
|
|
|
nsMessageListenerInfo* entry = listeners->AppendElement();
|
|
NS_ENSURE_TRUE(entry, NS_ERROR_OUT_OF_MEMORY);
|
|
entry->mStrongListener = aListener;
|
|
entry->mListenWhenClosed = aListenWhenClosed;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsFrameMessageManager::RemoveMessageListener(const nsAString& aMessage,
|
|
nsIMessageListener* aListener)
|
|
{
|
|
nsAutoTObserverArray<nsMessageListenerInfo, 1>* listeners =
|
|
mListeners.Get(aMessage);
|
|
if (!listeners) {
|
|
return NS_OK;
|
|
}
|
|
|
|
uint32_t len = listeners->Length();
|
|
for (uint32_t i = 0; i < len; ++i) {
|
|
if (listeners->ElementAt(i).mStrongListener == aListener) {
|
|
listeners->RemoveElementAt(i);
|
|
return NS_OK;
|
|
}
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
typedef struct
|
|
{
|
|
nsCOMPtr<nsISupports> mCanonical;
|
|
nsWeakPtr mWeak;
|
|
} CanonicalCheckerParams;
|
|
|
|
static PLDHashOperator
|
|
CanonicalChecker(const nsAString& aKey,
|
|
nsAutoTObserverArray<nsMessageListenerInfo, 1>* aListeners,
|
|
void* aParams)
|
|
{
|
|
CanonicalCheckerParams* params =
|
|
static_cast<CanonicalCheckerParams*> (aParams);
|
|
|
|
uint32_t count = aListeners->Length();
|
|
for (uint32_t i = 0; i < count; i++) {
|
|
if (!aListeners->ElementAt(i).mWeakListener) {
|
|
continue;
|
|
}
|
|
nsCOMPtr<nsISupports> otherCanonical =
|
|
do_QueryReferent(aListeners->ElementAt(i).mWeakListener);
|
|
MOZ_ASSERT((params->mCanonical == otherCanonical) ==
|
|
(params->mWeak == aListeners->ElementAt(i).mWeakListener));
|
|
}
|
|
return PL_DHASH_NEXT;
|
|
}
|
|
#endif
|
|
|
|
NS_IMETHODIMP
|
|
nsFrameMessageManager::AddWeakMessageListener(const nsAString& aMessage,
|
|
nsIMessageListener* aListener)
|
|
{
|
|
nsWeakPtr weak = do_GetWeakReference(aListener);
|
|
NS_ENSURE_TRUE(weak, NS_ERROR_NO_INTERFACE);
|
|
|
|
#ifdef DEBUG
|
|
// It's technically possible that one object X could give two different
|
|
// nsIWeakReference*'s when you do_GetWeakReference(X). We really don't want
|
|
// this to happen; it will break e.g. RemoveWeakMessageListener. So let's
|
|
// check that we're not getting ourselves into that situation.
|
|
nsCOMPtr<nsISupports> canonical = do_QueryInterface(aListener);
|
|
CanonicalCheckerParams params;
|
|
params.mCanonical = canonical;
|
|
params.mWeak = weak;
|
|
mListeners.EnumerateRead(CanonicalChecker, (void*)¶ms);
|
|
#endif
|
|
|
|
nsAutoTObserverArray<nsMessageListenerInfo, 1>* listeners =
|
|
mListeners.Get(aMessage);
|
|
if (!listeners) {
|
|
listeners = new nsAutoTObserverArray<nsMessageListenerInfo, 1>();
|
|
mListeners.Put(aMessage, listeners);
|
|
} else {
|
|
uint32_t len = listeners->Length();
|
|
for (uint32_t i = 0; i < len; ++i) {
|
|
if (listeners->ElementAt(i).mWeakListener == weak) {
|
|
return NS_OK;
|
|
}
|
|
}
|
|
}
|
|
|
|
nsMessageListenerInfo* entry = listeners->AppendElement();
|
|
entry->mWeakListener = weak;
|
|
entry->mListenWhenClosed = false;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsFrameMessageManager::RemoveWeakMessageListener(const nsAString& aMessage,
|
|
nsIMessageListener* aListener)
|
|
{
|
|
nsWeakPtr weak = do_GetWeakReference(aListener);
|
|
NS_ENSURE_TRUE(weak, NS_OK);
|
|
|
|
nsAutoTObserverArray<nsMessageListenerInfo, 1>* listeners =
|
|
mListeners.Get(aMessage);
|
|
if (!listeners) {
|
|
return NS_OK;
|
|
}
|
|
|
|
uint32_t len = listeners->Length();
|
|
for (uint32_t i = 0; i < len; ++i) {
|
|
if (listeners->ElementAt(i).mWeakListener == weak) {
|
|
listeners->RemoveElementAt(i);
|
|
return NS_OK;
|
|
}
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
// nsIFrameScriptLoader
|
|
|
|
NS_IMETHODIMP
|
|
nsFrameMessageManager::LoadScript(const nsAString& aURL,
|
|
bool aAllowDelayedLoad,
|
|
bool aRunInGlobalScope)
|
|
{
|
|
if (aAllowDelayedLoad) {
|
|
if (IsGlobal() || IsBroadcaster()) {
|
|
// Cache for future windows or frames
|
|
mPendingScripts.AppendElement(aURL);
|
|
mPendingScriptsGlobalStates.AppendElement(aRunInGlobalScope);
|
|
} else if (!mCallback) {
|
|
// We're frame message manager, which isn't connected yet.
|
|
mPendingScripts.AppendElement(aURL);
|
|
mPendingScriptsGlobalStates.AppendElement(aRunInGlobalScope);
|
|
return NS_OK;
|
|
}
|
|
}
|
|
|
|
if (mCallback) {
|
|
#ifdef DEBUG_smaug
|
|
printf("Will load %s \n", NS_ConvertUTF16toUTF8(aURL).get());
|
|
#endif
|
|
NS_ENSURE_TRUE(mCallback->DoLoadMessageManagerScript(aURL, aRunInGlobalScope),
|
|
NS_ERROR_FAILURE);
|
|
}
|
|
|
|
for (int32_t i = 0; i < mChildManagers.Count(); ++i) {
|
|
nsRefPtr<nsFrameMessageManager> mm =
|
|
static_cast<nsFrameMessageManager*>(mChildManagers[i]);
|
|
if (mm) {
|
|
// Use false here, so that child managers don't cache the script, which
|
|
// is already cached in the parent.
|
|
mm->LoadScript(aURL, false, aRunInGlobalScope);
|
|
}
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsFrameMessageManager::RemoveDelayedScript(const nsAString& aURL)
|
|
{
|
|
for (uint32_t i = 0; i < mPendingScripts.Length(); ++i) {
|
|
if (mPendingScripts[i] == aURL) {
|
|
mPendingScripts.RemoveElementAt(i);
|
|
mPendingScriptsGlobalStates.RemoveElementAt(i);
|
|
break;
|
|
}
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsFrameMessageManager::GetDelayedScripts(JSContext* aCx, JS::MutableHandle<JS::Value> aList)
|
|
{
|
|
// Frame message managers may return an incomplete list because scripts
|
|
// that were loaded after it was connected are not added to the list.
|
|
if (!IsGlobal() && !IsBroadcaster()) {
|
|
NS_WARNING("Cannot retrieve list of pending frame scripts for frame"
|
|
"message managers as it may be incomplete");
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
JS::Rooted<JSObject*> array(aCx, JS_NewArrayObject(aCx, mPendingScripts.Length()));
|
|
NS_ENSURE_TRUE(array, NS_ERROR_OUT_OF_MEMORY);
|
|
|
|
JS::Rooted<JSString*> url(aCx);
|
|
JS::Rooted<JSObject*> pair(aCx);
|
|
for (uint32_t i = 0; i < mPendingScripts.Length(); ++i) {
|
|
url = JS_NewUCStringCopyN(aCx, mPendingScripts[i].get(), mPendingScripts[i].Length());
|
|
NS_ENSURE_TRUE(url, NS_ERROR_OUT_OF_MEMORY);
|
|
|
|
JS::AutoValueArray<2> pairElts(aCx);
|
|
pairElts[0].setString(url);
|
|
pairElts[1].setBoolean(mPendingScriptsGlobalStates[i]);
|
|
|
|
pair = JS_NewArrayObject(aCx, pairElts);
|
|
NS_ENSURE_TRUE(pair, NS_ERROR_OUT_OF_MEMORY);
|
|
|
|
NS_ENSURE_TRUE(JS_DefineElement(aCx, array, i, pair, JSPROP_ENUMERATE),
|
|
NS_ERROR_OUT_OF_MEMORY);
|
|
}
|
|
|
|
aList.setObject(*array);
|
|
return NS_OK;
|
|
}
|
|
|
|
// nsIFrameScriptLoader
|
|
|
|
NS_IMETHODIMP
|
|
nsFrameMessageManager::LoadFrameScript(const nsAString& aURL,
|
|
bool aAllowDelayedLoad,
|
|
bool aRunInGlobalScope)
|
|
{
|
|
return LoadScript(aURL, aAllowDelayedLoad, aRunInGlobalScope);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsFrameMessageManager::RemoveDelayedFrameScript(const nsAString& aURL)
|
|
{
|
|
return RemoveDelayedScript(aURL);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsFrameMessageManager::GetDelayedFrameScripts(JSContext* aCx, JS::MutableHandle<JS::Value> aList)
|
|
{
|
|
return GetDelayedScripts(aCx, aList);
|
|
}
|
|
|
|
// nsIProcessScriptLoader
|
|
|
|
NS_IMETHODIMP
|
|
nsFrameMessageManager::LoadProcessScript(const nsAString& aURL,
|
|
bool aAllowDelayedLoad)
|
|
{
|
|
return LoadScript(aURL, aAllowDelayedLoad, false);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsFrameMessageManager::RemoveDelayedProcessScript(const nsAString& aURL)
|
|
{
|
|
return RemoveDelayedScript(aURL);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsFrameMessageManager::GetDelayedProcessScripts(JSContext* aCx, JS::MutableHandle<JS::Value> aList)
|
|
{
|
|
return GetDelayedScripts(aCx, aList);
|
|
}
|
|
|
|
static bool
|
|
JSONCreator(const char16_t* aBuf, uint32_t aLen, void* aData)
|
|
{
|
|
nsAString* result = static_cast<nsAString*>(aData);
|
|
result->Append(static_cast<const char16_t*>(aBuf),
|
|
static_cast<uint32_t>(aLen));
|
|
return true;
|
|
}
|
|
|
|
static bool
|
|
GetParamsForMessage(JSContext* aCx,
|
|
const JS::Value& aData,
|
|
JSAutoStructuredCloneBuffer& aBuffer,
|
|
StructuredCloneClosure& aClosure)
|
|
{
|
|
// First try to use structured clone on the whole thing.
|
|
JS::RootedValue v(aCx, aData);
|
|
if (WriteStructuredClone(aCx, v, aBuffer, aClosure)) {
|
|
return true;
|
|
}
|
|
JS_ClearPendingException(aCx);
|
|
|
|
nsCOMPtr<nsIConsoleService> console(do_GetService(NS_CONSOLESERVICE_CONTRACTID));
|
|
if (console) {
|
|
nsAutoString filename;
|
|
uint32_t lineno = 0;
|
|
nsJSUtils::GetCallingLocation(aCx, filename, &lineno);
|
|
nsCOMPtr<nsIScriptError> error(do_CreateInstance(NS_SCRIPTERROR_CONTRACTID));
|
|
error->Init(NS_LITERAL_STRING("Sending message that cannot be cloned. Are you trying to send an XPCOM object?"),
|
|
filename, EmptyString(),
|
|
lineno, 0, nsIScriptError::warningFlag, "chrome javascript");
|
|
console->LogMessage(error);
|
|
}
|
|
|
|
// Not clonable, try JSON
|
|
//XXX This is ugly but currently structured cloning doesn't handle
|
|
// properly cases when interface is implemented in JS and used
|
|
// as a dictionary.
|
|
nsAutoString json;
|
|
NS_ENSURE_TRUE(JS_Stringify(aCx, &v, nullptr, JS::NullHandleValue,
|
|
JSONCreator, &json), false);
|
|
NS_ENSURE_TRUE(!json.IsEmpty(), false);
|
|
|
|
JS::Rooted<JS::Value> val(aCx, JS::NullValue());
|
|
NS_ENSURE_TRUE(JS_ParseJSON(aCx, static_cast<const char16_t*>(json.get()),
|
|
json.Length(), &val), false);
|
|
|
|
return WriteStructuredClone(aCx, val, aBuffer, aClosure);
|
|
}
|
|
|
|
|
|
// nsISyncMessageSender
|
|
|
|
static bool sSendingSyncMessage = false;
|
|
|
|
NS_IMETHODIMP
|
|
nsFrameMessageManager::SendSyncMessage(const nsAString& aMessageName,
|
|
JS::Handle<JS::Value> aJSON,
|
|
JS::Handle<JS::Value> aObjects,
|
|
nsIPrincipal* aPrincipal,
|
|
JSContext* aCx,
|
|
uint8_t aArgc,
|
|
JS::MutableHandle<JS::Value> aRetval)
|
|
{
|
|
return SendMessage(aMessageName, aJSON, aObjects, aPrincipal, aCx, aArgc,
|
|
aRetval, true);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsFrameMessageManager::SendRpcMessage(const nsAString& aMessageName,
|
|
JS::Handle<JS::Value> aJSON,
|
|
JS::Handle<JS::Value> aObjects,
|
|
nsIPrincipal* aPrincipal,
|
|
JSContext* aCx,
|
|
uint8_t aArgc,
|
|
JS::MutableHandle<JS::Value> aRetval)
|
|
{
|
|
return SendMessage(aMessageName, aJSON, aObjects, aPrincipal, aCx, aArgc,
|
|
aRetval, false);
|
|
}
|
|
|
|
nsresult
|
|
nsFrameMessageManager::SendMessage(const nsAString& aMessageName,
|
|
JS::Handle<JS::Value> aJSON,
|
|
JS::Handle<JS::Value> aObjects,
|
|
nsIPrincipal* aPrincipal,
|
|
JSContext* aCx,
|
|
uint8_t aArgc,
|
|
JS::MutableHandle<JS::Value> aRetval,
|
|
bool aIsSync)
|
|
{
|
|
NS_ASSERTION(!IsGlobal(), "Should not call SendSyncMessage in chrome");
|
|
NS_ASSERTION(!IsBroadcaster(), "Should not call SendSyncMessage in chrome");
|
|
NS_ASSERTION(!mParentManager, "Should not have parent manager in content!");
|
|
|
|
aRetval.setUndefined();
|
|
NS_ENSURE_TRUE(mCallback, NS_ERROR_NOT_INITIALIZED);
|
|
|
|
if (sSendingSyncMessage && aIsSync) {
|
|
// No kind of blocking send should be issued on top of a sync message.
|
|
return NS_ERROR_UNEXPECTED;
|
|
}
|
|
|
|
StructuredCloneData data;
|
|
JSAutoStructuredCloneBuffer buffer;
|
|
if (aArgc >= 2 &&
|
|
!GetParamsForMessage(aCx, aJSON, buffer, data.mClosure)) {
|
|
return NS_ERROR_DOM_DATA_CLONE_ERR;
|
|
}
|
|
data.mData = buffer.data();
|
|
data.mDataLength = buffer.nbytes();
|
|
|
|
JS::Rooted<JSObject*> objects(aCx);
|
|
if (aArgc >= 3 && aObjects.isObject()) {
|
|
objects = &aObjects.toObject();
|
|
}
|
|
|
|
nsTArray<OwningSerializedStructuredCloneBuffer> retval;
|
|
|
|
sSendingSyncMessage |= aIsSync;
|
|
bool rv = mCallback->DoSendBlockingMessage(aCx, aMessageName, data, objects,
|
|
aPrincipal, &retval, aIsSync);
|
|
if (aIsSync) {
|
|
sSendingSyncMessage = false;
|
|
}
|
|
|
|
if (!rv) {
|
|
return NS_OK;
|
|
}
|
|
|
|
uint32_t len = retval.Length();
|
|
JS::Rooted<JSObject*> dataArray(aCx, JS_NewArrayObject(aCx, len));
|
|
NS_ENSURE_TRUE(dataArray, NS_ERROR_OUT_OF_MEMORY);
|
|
|
|
for (uint32_t i = 0; i < len; ++i) {
|
|
JS::Rooted<JS::Value> ret(aCx);
|
|
if (!JS_ReadStructuredClone(aCx, retval[i].data, retval[i].dataLength,
|
|
JS_STRUCTURED_CLONE_VERSION, &ret, nullptr, nullptr)) {
|
|
MOZ_ASSERT(false, "Unable to read structured clone in SendMessage");
|
|
return NS_ERROR_UNEXPECTED;
|
|
}
|
|
|
|
NS_ENSURE_TRUE(JS_DefineElement(aCx, dataArray, i, ret, JSPROP_ENUMERATE),
|
|
NS_ERROR_OUT_OF_MEMORY);
|
|
}
|
|
|
|
aRetval.setObject(*dataArray);
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
nsFrameMessageManager::DispatchAsyncMessageInternal(JSContext* aCx,
|
|
const nsAString& aMessage,
|
|
const StructuredCloneData& aData,
|
|
JS::Handle<JSObject *> aCpows,
|
|
nsIPrincipal* aPrincipal)
|
|
{
|
|
if (mIsBroadcaster) {
|
|
int32_t len = mChildManagers.Count();
|
|
for (int32_t i = 0; i < len; ++i) {
|
|
static_cast<nsFrameMessageManager*>(mChildManagers[i])->
|
|
DispatchAsyncMessageInternal(aCx, aMessage, aData, aCpows, aPrincipal);
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_ENSURE_TRUE(mCallback, NS_ERROR_NOT_INITIALIZED);
|
|
if (!mCallback->DoSendAsyncMessage(aCx, aMessage, aData, aCpows, aPrincipal)) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
nsFrameMessageManager::DispatchAsyncMessage(const nsAString& aMessageName,
|
|
const JS::Value& aJSON,
|
|
const JS::Value& aObjects,
|
|
nsIPrincipal* aPrincipal,
|
|
JSContext* aCx,
|
|
uint8_t aArgc)
|
|
{
|
|
StructuredCloneData data;
|
|
JSAutoStructuredCloneBuffer buffer;
|
|
|
|
if (aArgc >= 2 &&
|
|
!GetParamsForMessage(aCx, aJSON, buffer, data.mClosure)) {
|
|
return NS_ERROR_DOM_DATA_CLONE_ERR;
|
|
}
|
|
|
|
JS::Rooted<JSObject*> objects(aCx);
|
|
if (aArgc >= 3 && aObjects.isObject()) {
|
|
objects = &aObjects.toObject();
|
|
}
|
|
|
|
data.mData = buffer.data();
|
|
data.mDataLength = buffer.nbytes();
|
|
|
|
return DispatchAsyncMessageInternal(aCx, aMessageName, data, objects,
|
|
aPrincipal);
|
|
}
|
|
|
|
|
|
// nsIMessageSender
|
|
|
|
NS_IMETHODIMP
|
|
nsFrameMessageManager::SendAsyncMessage(const nsAString& aMessageName,
|
|
JS::Handle<JS::Value> aJSON,
|
|
JS::Handle<JS::Value> aObjects,
|
|
nsIPrincipal* aPrincipal,
|
|
JSContext* aCx,
|
|
uint8_t aArgc)
|
|
{
|
|
return DispatchAsyncMessage(aMessageName, aJSON, aObjects, aPrincipal, aCx,
|
|
aArgc);
|
|
}
|
|
|
|
|
|
// nsIMessageBroadcaster
|
|
|
|
NS_IMETHODIMP
|
|
nsFrameMessageManager::BroadcastAsyncMessage(const nsAString& aMessageName,
|
|
JS::Handle<JS::Value> aJSON,
|
|
JS::Handle<JS::Value> aObjects,
|
|
JSContext* aCx,
|
|
uint8_t aArgc)
|
|
{
|
|
return DispatchAsyncMessage(aMessageName, aJSON, aObjects, nullptr, aCx,
|
|
aArgc);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsFrameMessageManager::GetChildCount(uint32_t* aChildCount)
|
|
{
|
|
*aChildCount = static_cast<uint32_t>(mChildManagers.Count());
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsFrameMessageManager::GetChildAt(uint32_t aIndex,
|
|
nsIMessageListenerManager** aMM)
|
|
{
|
|
*aMM = nullptr;
|
|
nsCOMPtr<nsIMessageListenerManager> mm =
|
|
do_QueryInterface(mChildManagers.SafeObjectAt(static_cast<uint32_t>(aIndex)));
|
|
mm.swap(*aMM);
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
// nsIContentFrameMessageManager
|
|
|
|
NS_IMETHODIMP
|
|
nsFrameMessageManager::Dump(const nsAString& aStr)
|
|
{
|
|
#ifdef ANDROID
|
|
__android_log_print(ANDROID_LOG_INFO, "Gecko", "%s", NS_ConvertUTF16toUTF8(aStr).get());
|
|
#endif
|
|
#ifdef XP_WIN
|
|
if (IsDebuggerPresent()) {
|
|
OutputDebugStringW(PromiseFlatString(aStr).get());
|
|
}
|
|
#endif
|
|
fputs(NS_ConvertUTF16toUTF8(aStr).get(), stdout);
|
|
fflush(stdout);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsFrameMessageManager::PrivateNoteIntentionalCrash()
|
|
{
|
|
if (XRE_GetProcessType() == GeckoProcessType_Content) {
|
|
mozilla::NoteIntentionalCrash("tab");
|
|
return NS_OK;
|
|
} else {
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
}
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsFrameMessageManager::GetContent(nsIDOMWindow** aContent)
|
|
{
|
|
*aContent = nullptr;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsFrameMessageManager::GetDocShell(nsIDocShell** aDocShell)
|
|
{
|
|
*aDocShell = nullptr;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsFrameMessageManager::Btoa(const nsAString& aBinaryData,
|
|
nsAString& aAsciiBase64String)
|
|
{
|
|
return nsContentUtils::Btoa(aBinaryData, aAsciiBase64String);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsFrameMessageManager::Atob(const nsAString& aAsciiString,
|
|
nsAString& aBinaryData)
|
|
{
|
|
return nsContentUtils::Atob(aAsciiString, aBinaryData);
|
|
}
|
|
|
|
// nsIProcessChecker
|
|
|
|
NS_IMETHODIMP
|
|
nsFrameMessageManager::KillChild(bool *aValid)
|
|
{
|
|
if (!mCallback) {
|
|
*aValid = false;
|
|
return NS_ERROR_NOT_AVAILABLE;
|
|
}
|
|
|
|
*aValid = mCallback->KillChild();
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
nsFrameMessageManager::AssertProcessInternal(ProcessCheckerType aType,
|
|
const nsAString& aCapability,
|
|
bool* aValid)
|
|
{
|
|
*aValid = false;
|
|
|
|
// This API is only supported for message senders in the chrome process.
|
|
if (!mChrome || mIsBroadcaster) {
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
}
|
|
if (!mCallback) {
|
|
return NS_ERROR_NOT_AVAILABLE;
|
|
}
|
|
switch (aType) {
|
|
case PROCESS_CHECKER_PERMISSION:
|
|
*aValid = mCallback->CheckPermission(aCapability);
|
|
break;
|
|
case PROCESS_CHECKER_MANIFEST_URL:
|
|
*aValid = mCallback->CheckManifestURL(aCapability);
|
|
break;
|
|
case ASSERT_APP_HAS_PERMISSION:
|
|
*aValid = mCallback->CheckAppHasPermission(aCapability);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsFrameMessageManager::AssertPermission(const nsAString& aPermission,
|
|
bool* aHasPermission)
|
|
{
|
|
return AssertProcessInternal(PROCESS_CHECKER_PERMISSION,
|
|
aPermission,
|
|
aHasPermission);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsFrameMessageManager::AssertContainApp(const nsAString& aManifestURL,
|
|
bool* aHasManifestURL)
|
|
{
|
|
return AssertProcessInternal(PROCESS_CHECKER_MANIFEST_URL,
|
|
aManifestURL,
|
|
aHasManifestURL);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsFrameMessageManager::AssertAppHasPermission(const nsAString& aPermission,
|
|
bool* aHasPermission)
|
|
{
|
|
return AssertProcessInternal(ASSERT_APP_HAS_PERMISSION,
|
|
aPermission,
|
|
aHasPermission);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsFrameMessageManager::AssertAppHasStatus(unsigned short aStatus,
|
|
bool* aHasStatus)
|
|
{
|
|
*aHasStatus = false;
|
|
|
|
// This API is only supported for message senders in the chrome process.
|
|
if (!mChrome || mIsBroadcaster) {
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
}
|
|
if (!mCallback) {
|
|
return NS_ERROR_NOT_AVAILABLE;
|
|
}
|
|
*aHasStatus = mCallback->CheckAppHasStatus(aStatus);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
class MMListenerRemover
|
|
{
|
|
public:
|
|
explicit MMListenerRemover(nsFrameMessageManager* aMM)
|
|
: mWasHandlingMessage(aMM->mHandlingMessage)
|
|
, mMM(aMM)
|
|
{
|
|
mMM->mHandlingMessage = true;
|
|
}
|
|
~MMListenerRemover()
|
|
{
|
|
if (!mWasHandlingMessage) {
|
|
mMM->mHandlingMessage = false;
|
|
if (mMM->mDisconnected) {
|
|
mMM->mListeners.Clear();
|
|
}
|
|
}
|
|
}
|
|
|
|
bool mWasHandlingMessage;
|
|
nsRefPtr<nsFrameMessageManager> mMM;
|
|
};
|
|
|
|
|
|
// nsIMessageListener
|
|
|
|
nsresult
|
|
nsFrameMessageManager::ReceiveMessage(nsISupports* aTarget,
|
|
nsIFrameLoader* aTargetFrameLoader,
|
|
const nsAString& aMessage,
|
|
bool aIsSync,
|
|
const StructuredCloneData* aCloneData,
|
|
mozilla::jsipc::CpowHolder* aCpows,
|
|
nsIPrincipal* aPrincipal,
|
|
nsTArray<OwningSerializedStructuredCloneBuffer>* aRetVal)
|
|
{
|
|
return ReceiveMessage(aTarget, aTargetFrameLoader, mClosed, aMessage, aIsSync,
|
|
aCloneData, aCpows, aPrincipal, aRetVal);
|
|
}
|
|
|
|
nsresult
|
|
nsFrameMessageManager::ReceiveMessage(nsISupports* aTarget,
|
|
nsIFrameLoader* aTargetFrameLoader,
|
|
bool aTargetClosed,
|
|
const nsAString& aMessage,
|
|
bool aIsSync,
|
|
const StructuredCloneData* aCloneData,
|
|
mozilla::jsipc::CpowHolder* aCpows,
|
|
nsIPrincipal* aPrincipal,
|
|
nsTArray<OwningSerializedStructuredCloneBuffer>* aRetVal)
|
|
{
|
|
nsAutoTObserverArray<nsMessageListenerInfo, 1>* listeners =
|
|
mListeners.Get(aMessage);
|
|
if (listeners) {
|
|
|
|
MMListenerRemover lr(this);
|
|
|
|
nsAutoTObserverArray<nsMessageListenerInfo, 1>::EndLimitedIterator
|
|
iter(*listeners);
|
|
while(iter.HasMore()) {
|
|
nsMessageListenerInfo& listener = iter.GetNext();
|
|
// Remove mListeners[i] if it's an expired weak listener.
|
|
nsCOMPtr<nsISupports> weakListener;
|
|
if (listener.mWeakListener) {
|
|
weakListener = do_QueryReferent(listener.mWeakListener);
|
|
if (!weakListener) {
|
|
listeners->RemoveElement(listener);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (!listener.mListenWhenClosed && aTargetClosed) {
|
|
continue;
|
|
}
|
|
|
|
nsCOMPtr<nsIXPConnectWrappedJS> wrappedJS;
|
|
if (weakListener) {
|
|
wrappedJS = do_QueryInterface(weakListener);
|
|
} else {
|
|
wrappedJS = do_QueryInterface(listener.mStrongListener);
|
|
}
|
|
|
|
if (!wrappedJS) {
|
|
continue;
|
|
}
|
|
|
|
if (!wrappedJS->GetJSObject()) {
|
|
continue;
|
|
}
|
|
|
|
// Note - The ergonomics here will get a lot better with bug 971673:
|
|
//
|
|
// AutoEntryScript aes;
|
|
// if (!aes.Init(wrappedJS->GetJSObject())) {
|
|
// continue;
|
|
// }
|
|
// JSContext* cx = aes.cx();
|
|
nsIGlobalObject* nativeGlobal =
|
|
xpc::NativeGlobal(js::GetGlobalForObjectCrossCompartment(wrappedJS->GetJSObject()));
|
|
AutoEntryScript aes(nativeGlobal, "message manager handler");
|
|
aes.TakeOwnershipOfErrorReporting();
|
|
JSContext* cx = aes.cx();
|
|
JS::Rooted<JSObject*> object(cx, wrappedJS->GetJSObject());
|
|
|
|
// The parameter for the listener function.
|
|
JS::Rooted<JSObject*> param(cx, JS_NewPlainObject(cx));
|
|
NS_ENSURE_TRUE(param, NS_ERROR_OUT_OF_MEMORY);
|
|
|
|
JS::Rooted<JS::Value> targetv(cx);
|
|
js::AssertSameCompartment(cx, object);
|
|
nsresult rv = nsContentUtils::WrapNative(cx, aTarget, &targetv);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
JS::Rooted<JSObject*> cpows(cx);
|
|
if (aCpows) {
|
|
if (!aCpows->ToObject(cx, &cpows)) {
|
|
return NS_ERROR_UNEXPECTED;
|
|
}
|
|
}
|
|
|
|
if (!cpows) {
|
|
cpows = JS_NewPlainObject(cx);
|
|
if (!cpows) {
|
|
return NS_ERROR_UNEXPECTED;
|
|
}
|
|
}
|
|
|
|
JS::Rooted<JS::Value> cpowsv(cx, JS::ObjectValue(*cpows));
|
|
|
|
JS::Rooted<JS::Value> json(cx, JS::NullValue());
|
|
if (aCloneData && aCloneData->mDataLength &&
|
|
!ReadStructuredClone(cx, *aCloneData, &json)) {
|
|
JS_ClearPendingException(cx);
|
|
return NS_OK;
|
|
}
|
|
JS::Rooted<JSString*> jsMessage(cx,
|
|
JS_NewUCStringCopyN(cx,
|
|
static_cast<const char16_t*>(aMessage.BeginReading()),
|
|
aMessage.Length()));
|
|
NS_ENSURE_TRUE(jsMessage, NS_ERROR_OUT_OF_MEMORY);
|
|
JS::Rooted<JS::Value> syncv(cx, JS::BooleanValue(aIsSync));
|
|
bool ok = JS_DefineProperty(cx, param, "target", targetv, JSPROP_ENUMERATE) &&
|
|
JS_DefineProperty(cx, param, "name", jsMessage, JSPROP_ENUMERATE) &&
|
|
JS_DefineProperty(cx, param, "sync", syncv, JSPROP_ENUMERATE) &&
|
|
JS_DefineProperty(cx, param, "json", json, JSPROP_ENUMERATE) && // deprecated
|
|
JS_DefineProperty(cx, param, "data", json, JSPROP_ENUMERATE) &&
|
|
JS_DefineProperty(cx, param, "objects", cpowsv, JSPROP_ENUMERATE);
|
|
NS_ENSURE_TRUE(ok, NS_ERROR_UNEXPECTED);
|
|
|
|
if (aTargetFrameLoader) {
|
|
JS::Rooted<JS::Value> targetFrameLoaderv(cx);
|
|
nsresult rv = nsContentUtils::WrapNative(cx, aTargetFrameLoader, &targetFrameLoaderv);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
ok = JS_DefineProperty(cx, param, "targetFrameLoader", targetFrameLoaderv,
|
|
JSPROP_ENUMERATE);
|
|
NS_ENSURE_TRUE(ok, NS_ERROR_UNEXPECTED);
|
|
}
|
|
|
|
// message.principal == null
|
|
if (!aPrincipal) {
|
|
bool ok = JS_DefineProperty(cx, param, "principal",
|
|
JS::UndefinedHandleValue, JSPROP_ENUMERATE);
|
|
NS_ENSURE_TRUE(ok, NS_ERROR_UNEXPECTED);
|
|
}
|
|
|
|
// message.principal = the principal
|
|
else {
|
|
JS::Rooted<JS::Value> principalValue(cx);
|
|
nsresult rv = nsContentUtils::WrapNative(cx, aPrincipal,
|
|
&NS_GET_IID(nsIPrincipal),
|
|
&principalValue);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
bool ok = JS_DefineProperty(cx, param, "principal", principalValue,
|
|
JSPROP_ENUMERATE);
|
|
NS_ENSURE_TRUE(ok, NS_ERROR_UNEXPECTED);
|
|
}
|
|
|
|
JS::Rooted<JS::Value> thisValue(cx, JS::UndefinedValue());
|
|
|
|
JS::Rooted<JS::Value> funval(cx);
|
|
if (JS::IsCallable(object)) {
|
|
// If the listener is a JS function:
|
|
funval.setObject(*object);
|
|
|
|
// A small hack to get 'this' value right on content side where
|
|
// messageManager is wrapped in TabChildGlobal.
|
|
nsCOMPtr<nsISupports> defaultThisValue;
|
|
if (mChrome) {
|
|
defaultThisValue = do_QueryObject(this);
|
|
} else {
|
|
defaultThisValue = aTarget;
|
|
}
|
|
js::AssertSameCompartment(cx, object);
|
|
nsresult rv = nsContentUtils::WrapNative(cx, defaultThisValue, &thisValue);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
} else {
|
|
// If the listener is a JS object which has receiveMessage function:
|
|
if (!JS_GetProperty(cx, object, "receiveMessage", &funval) ||
|
|
!funval.isObject()) {
|
|
return NS_ERROR_UNEXPECTED;
|
|
}
|
|
|
|
// Check if the object is even callable.
|
|
NS_ENSURE_STATE(JS::IsCallable(&funval.toObject()));
|
|
thisValue.setObject(*object);
|
|
}
|
|
|
|
JS::Rooted<JS::Value> rval(cx, JS::UndefinedValue());
|
|
JS::Rooted<JS::Value> argv(cx, JS::ObjectValue(*param));
|
|
|
|
{
|
|
JS::Rooted<JSObject*> thisObject(cx, thisValue.toObjectOrNull());
|
|
|
|
JSAutoCompartment tac(cx, thisObject);
|
|
if (!JS_WrapValue(cx, &argv)) {
|
|
return NS_ERROR_UNEXPECTED;
|
|
}
|
|
|
|
if (!JS_CallFunctionValue(cx, thisObject, funval,
|
|
JS::HandleValueArray(argv), &rval)) {
|
|
continue;
|
|
}
|
|
if (aRetVal) {
|
|
JSAutoStructuredCloneBuffer buffer;
|
|
if (!buffer.write(cx, rval)) {
|
|
nsString msg = aMessage + NS_LITERAL_STRING(": message reply cannot be cloned. Are you trying to send an XPCOM object?");
|
|
|
|
nsCOMPtr<nsIConsoleService> console(do_GetService(NS_CONSOLESERVICE_CONTRACTID));
|
|
if (console) {
|
|
nsCOMPtr<nsIScriptError> error(do_CreateInstance(NS_SCRIPTERROR_CONTRACTID));
|
|
error->Init(msg, EmptyString(), EmptyString(),
|
|
0, 0, nsIScriptError::warningFlag, "chrome javascript");
|
|
console->LogMessage(error);
|
|
}
|
|
|
|
JS_ClearPendingException(cx);
|
|
continue;
|
|
}
|
|
|
|
OwningSerializedStructuredCloneBuffer* data = aRetVal->AppendElement();
|
|
buffer.steal(&data->data, &data->dataLength);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
nsRefPtr<nsFrameMessageManager> kungfuDeathGrip = mParentManager;
|
|
return mParentManager ? mParentManager->ReceiveMessage(aTarget, aTargetFrameLoader,
|
|
aTargetClosed, aMessage,
|
|
aIsSync, aCloneData,
|
|
aCpows, aPrincipal,
|
|
aRetVal) : NS_OK;
|
|
}
|
|
|
|
void
|
|
nsFrameMessageManager::AddChildManager(nsFrameMessageManager* aManager)
|
|
{
|
|
mChildManagers.AppendObject(aManager);
|
|
|
|
nsRefPtr<nsFrameMessageManager> kungfuDeathGrip = this;
|
|
nsRefPtr<nsFrameMessageManager> kungfuDeathGrip2 = aManager;
|
|
|
|
LoadPendingScripts(this, aManager);
|
|
}
|
|
|
|
void
|
|
nsFrameMessageManager::LoadPendingScripts(nsFrameMessageManager* aManager,
|
|
nsFrameMessageManager* aChildMM)
|
|
{
|
|
// We have parent manager if we're a message broadcaster.
|
|
// In that case we want to load the pending scripts from all parent
|
|
// message managers in the hierarchy. Process the parent first so
|
|
// that pending scripts higher up in the hierarchy are loaded before others.
|
|
if (aManager->mParentManager) {
|
|
LoadPendingScripts(aManager->mParentManager, aChildMM);
|
|
}
|
|
|
|
for (uint32_t i = 0; i < aManager->mPendingScripts.Length(); ++i) {
|
|
aChildMM->LoadFrameScript(aManager->mPendingScripts[i],
|
|
false,
|
|
aManager->mPendingScriptsGlobalStates[i]);
|
|
}
|
|
}
|
|
|
|
void
|
|
nsFrameMessageManager::SetCallback(MessageManagerCallback* aCallback)
|
|
{
|
|
MOZ_ASSERT(!mIsBroadcaster || !mCallback,
|
|
"Broadcasters cannot have callbacks!");
|
|
if (aCallback && mCallback != aCallback) {
|
|
mCallback = aCallback;
|
|
if (mOwnsCallback) {
|
|
mOwnedCallback = aCallback;
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
nsFrameMessageManager::InitWithCallback(MessageManagerCallback* aCallback)
|
|
{
|
|
if (mCallback) {
|
|
// Initialization should only happen once.
|
|
return;
|
|
}
|
|
|
|
SetCallback(aCallback);
|
|
|
|
// First load parent scripts by adding this to parent manager.
|
|
if (mParentManager) {
|
|
mParentManager->AddChildManager(this);
|
|
}
|
|
|
|
for (uint32_t i = 0; i < mPendingScripts.Length(); ++i) {
|
|
LoadFrameScript(mPendingScripts[i], false, mPendingScriptsGlobalStates[i]);
|
|
}
|
|
}
|
|
|
|
void
|
|
nsFrameMessageManager::RemoveFromParent()
|
|
{
|
|
if (mParentManager) {
|
|
mParentManager->RemoveChildManager(this);
|
|
}
|
|
mParentManager = nullptr;
|
|
mCallback = nullptr;
|
|
mOwnedCallback = nullptr;
|
|
}
|
|
|
|
void
|
|
nsFrameMessageManager::Close()
|
|
{
|
|
if (!mClosed) {
|
|
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
|
|
if (obs) {
|
|
obs->NotifyObservers(NS_ISUPPORTS_CAST(nsIContentFrameMessageManager*, this),
|
|
"message-manager-close", nullptr);
|
|
}
|
|
}
|
|
mClosed = true;
|
|
mCallback = nullptr;
|
|
mOwnedCallback = nullptr;
|
|
}
|
|
|
|
void
|
|
nsFrameMessageManager::Disconnect(bool aRemoveFromParent)
|
|
{
|
|
// Notify message-manager-close if we haven't already.
|
|
Close();
|
|
|
|
if (!mDisconnected) {
|
|
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
|
|
if (obs) {
|
|
obs->NotifyObservers(NS_ISUPPORTS_CAST(nsIContentFrameMessageManager*, this),
|
|
"message-manager-disconnect", nullptr);
|
|
}
|
|
}
|
|
if (mParentManager && aRemoveFromParent) {
|
|
mParentManager->RemoveChildManager(this);
|
|
}
|
|
mDisconnected = true;
|
|
mParentManager = nullptr;
|
|
if (!mHandlingMessage) {
|
|
mListeners.Clear();
|
|
}
|
|
}
|
|
|
|
void
|
|
nsFrameMessageManager::SetInitialProcessData(JS::HandleValue aInitialData)
|
|
{
|
|
MOZ_ASSERT(!mChrome);
|
|
MOZ_ASSERT(mIsProcessManager);
|
|
mInitialProcessData = aInitialData;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsFrameMessageManager::GetInitialProcessData(JSContext* aCx, JS::MutableHandleValue aResult)
|
|
{
|
|
MOZ_ASSERT(mIsProcessManager);
|
|
MOZ_ASSERT_IF(mChrome, IsBroadcaster());
|
|
|
|
JS::RootedValue init(aCx, mInitialProcessData);
|
|
if (mChrome && init.isUndefined()) {
|
|
// We create the initial object in the junk scope. If we created it in a
|
|
// normal compartment, that compartment would leak until shutdown.
|
|
JS::RootedObject global(aCx, xpc::PrivilegedJunkScope());
|
|
JSAutoCompartment ac(aCx, global);
|
|
|
|
JS::RootedObject obj(aCx, JS_NewPlainObject(aCx));
|
|
if (!obj) {
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
|
|
mInitialProcessData.setObject(*obj);
|
|
init.setObject(*obj);
|
|
}
|
|
|
|
if (!JS_WrapValue(aCx, &init)) {
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
aResult.set(init);
|
|
return NS_OK;
|
|
}
|
|
|
|
namespace {
|
|
|
|
struct MessageManagerReferentCount
|
|
{
|
|
MessageManagerReferentCount() : mStrong(0), mWeakAlive(0), mWeakDead(0) {}
|
|
size_t mStrong;
|
|
size_t mWeakAlive;
|
|
size_t mWeakDead;
|
|
nsTArray<nsString> mSuspectMessages;
|
|
nsDataHashtable<nsStringHashKey, uint32_t> mMessageCounter;
|
|
};
|
|
|
|
} // anonymous namespace
|
|
|
|
namespace mozilla {
|
|
namespace dom {
|
|
|
|
class MessageManagerReporter final : public nsIMemoryReporter
|
|
{
|
|
~MessageManagerReporter() {}
|
|
|
|
public:
|
|
NS_DECL_ISUPPORTS
|
|
NS_DECL_NSIMEMORYREPORTER
|
|
|
|
static const size_t kSuspectReferentCount = 300;
|
|
protected:
|
|
void CountReferents(nsFrameMessageManager* aMessageManager,
|
|
MessageManagerReferentCount* aReferentCount);
|
|
};
|
|
|
|
NS_IMPL_ISUPPORTS(MessageManagerReporter, nsIMemoryReporter)
|
|
|
|
static PLDHashOperator
|
|
CollectMessageListenerData(const nsAString& aKey,
|
|
nsAutoTObserverArray<nsMessageListenerInfo, 1>* aListeners,
|
|
void* aData)
|
|
{
|
|
MessageManagerReferentCount* referentCount =
|
|
static_cast<MessageManagerReferentCount*>(aData);
|
|
|
|
uint32_t listenerCount = aListeners->Length();
|
|
if (!listenerCount) {
|
|
return PL_DHASH_NEXT;
|
|
}
|
|
|
|
nsString key(aKey);
|
|
uint32_t oldCount = 0;
|
|
referentCount->mMessageCounter.Get(key, &oldCount);
|
|
uint32_t currentCount = oldCount + listenerCount;
|
|
referentCount->mMessageCounter.Put(key, currentCount);
|
|
|
|
// Keep track of messages that have a suspiciously large
|
|
// number of referents (symptom of leak).
|
|
if (currentCount == MessageManagerReporter::kSuspectReferentCount) {
|
|
referentCount->mSuspectMessages.AppendElement(key);
|
|
}
|
|
|
|
for (uint32_t i = 0; i < listenerCount; ++i) {
|
|
const nsMessageListenerInfo& listenerInfo =
|
|
aListeners->ElementAt(i);
|
|
if (listenerInfo.mWeakListener) {
|
|
nsCOMPtr<nsISupports> referent =
|
|
do_QueryReferent(listenerInfo.mWeakListener);
|
|
if (referent) {
|
|
referentCount->mWeakAlive++;
|
|
} else {
|
|
referentCount->mWeakDead++;
|
|
}
|
|
} else {
|
|
referentCount->mStrong++;
|
|
}
|
|
}
|
|
return PL_DHASH_NEXT;
|
|
}
|
|
|
|
void
|
|
MessageManagerReporter::CountReferents(nsFrameMessageManager* aMessageManager,
|
|
MessageManagerReferentCount* aReferentCount)
|
|
{
|
|
aMessageManager->mListeners.EnumerateRead(CollectMessageListenerData,
|
|
aReferentCount);
|
|
|
|
// Add referent count in child managers because the listeners
|
|
// participate in messages dispatched from parent message manager.
|
|
for (uint32_t i = 0; i < aMessageManager->mChildManagers.Length(); ++i) {
|
|
nsRefPtr<nsFrameMessageManager> mm =
|
|
static_cast<nsFrameMessageManager*>(aMessageManager->mChildManagers[i]);
|
|
CountReferents(mm, aReferentCount);
|
|
}
|
|
}
|
|
|
|
static nsresult
|
|
ReportReferentCount(const char* aManagerType,
|
|
const MessageManagerReferentCount& aReferentCount,
|
|
nsIMemoryReporterCallback* aCb,
|
|
nsISupports* aClosure)
|
|
{
|
|
#define REPORT(_path, _amount, _desc) \
|
|
do { \
|
|
nsresult rv; \
|
|
rv = aCb->Callback(EmptyCString(), _path, \
|
|
nsIMemoryReporter::KIND_OTHER, \
|
|
nsIMemoryReporter::UNITS_COUNT, _amount, \
|
|
_desc, aClosure); \
|
|
NS_ENSURE_SUCCESS(rv, rv); \
|
|
} while (0)
|
|
|
|
REPORT(nsPrintfCString("message-manager/referent/%s/strong", aManagerType),
|
|
aReferentCount.mStrong,
|
|
nsPrintfCString("The number of strong referents held by the message "
|
|
"manager in the %s manager.", aManagerType));
|
|
REPORT(nsPrintfCString("message-manager/referent/%s/weak/alive", aManagerType),
|
|
aReferentCount.mWeakAlive,
|
|
nsPrintfCString("The number of weak referents that are still alive "
|
|
"held by the message manager in the %s manager.",
|
|
aManagerType));
|
|
REPORT(nsPrintfCString("message-manager/referent/%s/weak/dead", aManagerType),
|
|
aReferentCount.mWeakDead,
|
|
nsPrintfCString("The number of weak referents that are dead "
|
|
"held by the message manager in the %s manager.",
|
|
aManagerType));
|
|
|
|
for (uint32_t i = 0; i < aReferentCount.mSuspectMessages.Length(); i++) {
|
|
uint32_t totalReferentCount = 0;
|
|
aReferentCount.mMessageCounter.Get(aReferentCount.mSuspectMessages[i],
|
|
&totalReferentCount);
|
|
NS_ConvertUTF16toUTF8 suspect(aReferentCount.mSuspectMessages[i]);
|
|
REPORT(nsPrintfCString("message-manager-suspect/%s/referent(message=%s)",
|
|
aManagerType, suspect.get()), totalReferentCount,
|
|
nsPrintfCString("A message in the %s message manager with a "
|
|
"suspiciously large number of referents (symptom "
|
|
"of a leak).", aManagerType));
|
|
}
|
|
|
|
#undef REPORT
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
MessageManagerReporter::CollectReports(nsIMemoryReporterCallback* aCb,
|
|
nsISupports* aClosure, bool aAnonymize)
|
|
{
|
|
nsresult rv;
|
|
|
|
if (XRE_GetProcessType() == GeckoProcessType_Default) {
|
|
nsCOMPtr<nsIMessageBroadcaster> globalmm =
|
|
do_GetService("@mozilla.org/globalmessagemanager;1");
|
|
if (globalmm) {
|
|
nsRefPtr<nsFrameMessageManager> mm =
|
|
static_cast<nsFrameMessageManager*>(globalmm.get());
|
|
MessageManagerReferentCount count;
|
|
CountReferents(mm, &count);
|
|
rv = ReportReferentCount("global-manager", count, aCb, aClosure);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
}
|
|
}
|
|
|
|
if (nsFrameMessageManager::sParentProcessManager) {
|
|
MessageManagerReferentCount count;
|
|
CountReferents(nsFrameMessageManager::sParentProcessManager, &count);
|
|
rv = ReportReferentCount("parent-process-manager", count, aCb, aClosure);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
}
|
|
|
|
if (nsFrameMessageManager::sChildProcessManager) {
|
|
MessageManagerReferentCount count;
|
|
CountReferents(nsFrameMessageManager::sChildProcessManager, &count);
|
|
rv = ReportReferentCount("child-process-manager", count, aCb, aClosure);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
} // namespace dom
|
|
} // namespace mozilla
|
|
|
|
nsresult
|
|
NS_NewGlobalMessageManager(nsIMessageBroadcaster** aResult)
|
|
{
|
|
NS_ENSURE_TRUE(XRE_GetProcessType() == GeckoProcessType_Default,
|
|
NS_ERROR_NOT_AVAILABLE);
|
|
nsRefPtr<nsFrameMessageManager> mm = new nsFrameMessageManager(nullptr,
|
|
nullptr,
|
|
MM_CHROME | MM_GLOBAL | MM_BROADCASTER);
|
|
RegisterStrongMemoryReporter(new MessageManagerReporter());
|
|
mm.forget(aResult);
|
|
return NS_OK;
|
|
}
|
|
|
|
nsDataHashtable<nsStringHashKey, nsMessageManagerScriptHolder*>*
|
|
nsMessageManagerScriptExecutor::sCachedScripts = nullptr;
|
|
nsScriptCacheCleaner* nsMessageManagerScriptExecutor::sScriptCacheCleaner = nullptr;
|
|
|
|
void
|
|
nsMessageManagerScriptExecutor::DidCreateGlobal()
|
|
{
|
|
NS_ASSERTION(mGlobal, "Should have mGlobal!");
|
|
if (!sCachedScripts) {
|
|
sCachedScripts =
|
|
new nsDataHashtable<nsStringHashKey, nsMessageManagerScriptHolder*>;
|
|
|
|
nsRefPtr<nsScriptCacheCleaner> scriptCacheCleaner =
|
|
new nsScriptCacheCleaner();
|
|
scriptCacheCleaner.forget(&sScriptCacheCleaner);
|
|
}
|
|
}
|
|
|
|
static PLDHashOperator
|
|
RemoveCachedScriptEntry(const nsAString& aKey,
|
|
nsMessageManagerScriptHolder*& aData,
|
|
void* aUserArg)
|
|
{
|
|
delete aData;
|
|
return PL_DHASH_REMOVE;
|
|
}
|
|
|
|
// static
|
|
void
|
|
nsMessageManagerScriptExecutor::Shutdown()
|
|
{
|
|
if (sCachedScripts) {
|
|
AutoSafeJSContext cx;
|
|
NS_ASSERTION(sCachedScripts != nullptr, "Need cached scripts");
|
|
sCachedScripts->Enumerate(RemoveCachedScriptEntry, nullptr);
|
|
|
|
delete sCachedScripts;
|
|
sCachedScripts = nullptr;
|
|
|
|
nsRefPtr<nsScriptCacheCleaner> scriptCacheCleaner;
|
|
scriptCacheCleaner.swap(sScriptCacheCleaner);
|
|
}
|
|
}
|
|
|
|
void
|
|
nsMessageManagerScriptExecutor::LoadScriptInternal(const nsAString& aURL,
|
|
bool aRunInGlobalScope)
|
|
{
|
|
if (!mGlobal || !sCachedScripts) {
|
|
return;
|
|
}
|
|
|
|
JSRuntime* rt = CycleCollectedJSRuntime::Get()->Runtime();
|
|
JS::Rooted<JSScript*> script(rt);
|
|
|
|
nsMessageManagerScriptHolder* holder = sCachedScripts->Get(aURL);
|
|
if (holder && holder->WillRunInGlobalScope() == aRunInGlobalScope) {
|
|
script = holder->mScript;
|
|
} else {
|
|
// Don't put anything in the cache if we already have an entry
|
|
// with a different WillRunInGlobalScope() value.
|
|
bool shouldCache = !holder;
|
|
TryCacheLoadAndCompileScript(aURL, aRunInGlobalScope,
|
|
shouldCache, &script);
|
|
}
|
|
|
|
JS::Rooted<JSObject*> global(rt, mGlobal->GetJSObject());
|
|
if (global) {
|
|
AutoEntryScript aes(xpc::NativeGlobal(global),
|
|
"message manager script load");
|
|
aes.TakeOwnershipOfErrorReporting();
|
|
JSContext* cx = aes.cx();
|
|
if (script) {
|
|
if (aRunInGlobalScope) {
|
|
JS::CloneAndExecuteScript(cx, script);
|
|
} else {
|
|
JS::Rooted<JSObject*> scope(cx);
|
|
bool ok = js::ExecuteInGlobalAndReturnScope(cx, global, script, &scope);
|
|
if (ok) {
|
|
// Force the scope to stay alive.
|
|
mAnonymousGlobalScopes.AppendElement(scope);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
nsMessageManagerScriptExecutor::TryCacheLoadAndCompileScript(
|
|
const nsAString& aURL,
|
|
bool aRunInGlobalScope,
|
|
bool aShouldCache,
|
|
JS::MutableHandle<JSScript*> aScriptp)
|
|
{
|
|
nsCString url = NS_ConvertUTF16toUTF8(aURL);
|
|
nsCOMPtr<nsIURI> uri;
|
|
nsresult rv = NS_NewURI(getter_AddRefs(uri), url);
|
|
if (NS_FAILED(rv)) {
|
|
return;
|
|
}
|
|
|
|
bool hasFlags;
|
|
rv = NS_URIChainHasFlags(uri,
|
|
nsIProtocolHandler::URI_IS_LOCAL_RESOURCE,
|
|
&hasFlags);
|
|
if (NS_FAILED(rv) || !hasFlags) {
|
|
NS_WARNING("Will not load a frame script!");
|
|
return;
|
|
}
|
|
|
|
nsCOMPtr<nsIChannel> channel;
|
|
NS_NewChannel(getter_AddRefs(channel),
|
|
uri,
|
|
nsContentUtils::GetSystemPrincipal(),
|
|
nsILoadInfo::SEC_NORMAL,
|
|
nsIContentPolicy::TYPE_OTHER);
|
|
|
|
if (!channel) {
|
|
return;
|
|
}
|
|
|
|
nsCOMPtr<nsIInputStream> input;
|
|
channel->Open(getter_AddRefs(input));
|
|
nsString dataString;
|
|
char16_t* dataStringBuf = nullptr;
|
|
size_t dataStringLength = 0;
|
|
uint64_t avail64 = 0;
|
|
if (input && NS_SUCCEEDED(input->Available(&avail64)) && avail64) {
|
|
if (avail64 > UINT32_MAX) {
|
|
return;
|
|
}
|
|
nsCString buffer;
|
|
uint32_t avail = (uint32_t)std::min(avail64, (uint64_t)UINT32_MAX);
|
|
if (NS_FAILED(NS_ReadInputStreamToString(input, buffer, avail))) {
|
|
return;
|
|
}
|
|
nsScriptLoader::ConvertToUTF16(channel, (uint8_t*)buffer.get(), avail,
|
|
EmptyString(), nullptr,
|
|
dataStringBuf, dataStringLength);
|
|
}
|
|
|
|
JS::SourceBufferHolder srcBuf(dataStringBuf, dataStringLength,
|
|
JS::SourceBufferHolder::GiveOwnership);
|
|
|
|
if (dataStringBuf && dataStringLength > 0) {
|
|
AutoSafeJSContext cx;
|
|
// Compile the script in the compilation scope instead of the current global
|
|
// to avoid keeping the current compartment alive.
|
|
JS::Rooted<JSObject*> global(cx, xpc::CompilationScope());
|
|
|
|
JSAutoCompartment ac(cx, global);
|
|
JS::CompileOptions options(cx, JSVERSION_LATEST);
|
|
options.setFileAndLine(url.get(), 1);
|
|
options.setNoScriptRval(true);
|
|
JS::Rooted<JSScript*> script(cx);
|
|
|
|
if (aRunInGlobalScope) {
|
|
if (!JS::Compile(cx, options, srcBuf, &script)) {
|
|
return;
|
|
}
|
|
} else {
|
|
// We're going to run these against some non-global scope.
|
|
options.setHasPollutedScope(true);
|
|
if (!JS::Compile(cx, options, srcBuf, &script)) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
aScriptp.set(script);
|
|
|
|
nsAutoCString scheme;
|
|
uri->GetScheme(scheme);
|
|
// We don't cache data: scripts!
|
|
if (aShouldCache && !scheme.EqualsLiteral("data")) {
|
|
nsMessageManagerScriptHolder* holder;
|
|
|
|
// Root the object also for caching.
|
|
if (script) {
|
|
holder = new nsMessageManagerScriptHolder(cx, script, aRunInGlobalScope);
|
|
}
|
|
sCachedScripts->Put(aURL, holder);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
nsMessageManagerScriptExecutor::TryCacheLoadAndCompileScript(
|
|
const nsAString& aURL,
|
|
bool aRunInGlobalScope)
|
|
{
|
|
AutoSafeJSContext cx;
|
|
JS::Rooted<JSScript*> script(cx);
|
|
TryCacheLoadAndCompileScript(aURL, aRunInGlobalScope, true, &script);
|
|
}
|
|
|
|
bool
|
|
nsMessageManagerScriptExecutor::InitChildGlobalInternal(
|
|
nsISupports* aScope,
|
|
const nsACString& aID)
|
|
{
|
|
|
|
nsCOMPtr<nsIJSRuntimeService> runtimeSvc =
|
|
do_GetService("@mozilla.org/js/xpc/RuntimeService;1");
|
|
NS_ENSURE_TRUE(runtimeSvc, false);
|
|
|
|
JSRuntime* rt = nullptr;
|
|
runtimeSvc->GetRuntime(&rt);
|
|
NS_ENSURE_TRUE(rt, false);
|
|
|
|
AutoSafeJSContext cx;
|
|
nsContentUtils::GetSecurityManager()->GetSystemPrincipal(getter_AddRefs(mPrincipal));
|
|
|
|
nsIXPConnect* xpc = nsContentUtils::XPConnect();
|
|
const uint32_t flags = nsIXPConnect::INIT_JS_STANDARD_CLASSES;
|
|
|
|
JS::CompartmentOptions options;
|
|
options.setZone(JS::SystemZone)
|
|
.setVersion(JSVERSION_LATEST);
|
|
|
|
nsresult rv =
|
|
xpc->InitClassesWithNewWrappedGlobal(cx, aScope, mPrincipal,
|
|
flags, options, getter_AddRefs(mGlobal));
|
|
NS_ENSURE_SUCCESS(rv, false);
|
|
|
|
|
|
JS::Rooted<JSObject*> global(cx, mGlobal->GetJSObject());
|
|
NS_ENSURE_TRUE(global, false);
|
|
|
|
// Set the location information for the new global, so that tools like
|
|
// about:memory may use that information.
|
|
xpc::SetLocationForGlobal(global, aID);
|
|
|
|
DidCreateGlobal();
|
|
return true;
|
|
}
|
|
|
|
void
|
|
nsMessageManagerScriptExecutor::MarkScopesForCC()
|
|
{
|
|
for (uint32_t i = 0; i < mAnonymousGlobalScopes.Length(); ++i) {
|
|
JSObject* obj = mAnonymousGlobalScopes[i];
|
|
if (obj) {
|
|
JS::ExposeObjectToActiveJS(obj);
|
|
}
|
|
}
|
|
}
|
|
|
|
NS_IMPL_ISUPPORTS(nsScriptCacheCleaner, nsIObserver)
|
|
|
|
nsFrameMessageManager* nsFrameMessageManager::sChildProcessManager = nullptr;
|
|
nsFrameMessageManager* nsFrameMessageManager::sParentProcessManager = nullptr;
|
|
nsFrameMessageManager* nsFrameMessageManager::sSameProcessParentManager = nullptr;
|
|
|
|
class nsAsyncMessageToSameProcessChild : public nsSameProcessAsyncMessageBase,
|
|
public nsRunnable
|
|
{
|
|
public:
|
|
nsAsyncMessageToSameProcessChild(JSContext* aCx,
|
|
const nsAString& aMessage,
|
|
const StructuredCloneData& aData,
|
|
JS::Handle<JSObject *> aCpows,
|
|
nsIPrincipal* aPrincipal)
|
|
: nsSameProcessAsyncMessageBase(aCx, aMessage, aData, aCpows, aPrincipal)
|
|
{
|
|
}
|
|
|
|
NS_IMETHOD Run()
|
|
{
|
|
nsFrameMessageManager* ppm = nsFrameMessageManager::GetChildProcessManager();
|
|
ReceiveMessage(static_cast<nsIContentFrameMessageManager*>(ppm), nullptr, ppm);
|
|
return NS_OK;
|
|
}
|
|
};
|
|
|
|
|
|
/**
|
|
* Send messages to an imaginary child process in a single-process scenario.
|
|
*/
|
|
class SameParentProcessMessageManagerCallback : public MessageManagerCallback
|
|
{
|
|
public:
|
|
SameParentProcessMessageManagerCallback()
|
|
{
|
|
MOZ_COUNT_CTOR(SameParentProcessMessageManagerCallback);
|
|
}
|
|
virtual ~SameParentProcessMessageManagerCallback()
|
|
{
|
|
MOZ_COUNT_DTOR(SameParentProcessMessageManagerCallback);
|
|
}
|
|
|
|
virtual bool DoLoadMessageManagerScript(const nsAString& aURL,
|
|
bool aRunInGlobalScope) override
|
|
{
|
|
ProcessGlobal* global = ProcessGlobal::Get();
|
|
MOZ_ASSERT(!aRunInGlobalScope);
|
|
global->LoadScript(aURL);
|
|
return true;
|
|
}
|
|
|
|
virtual bool DoSendAsyncMessage(JSContext* aCx,
|
|
const nsAString& aMessage,
|
|
const StructuredCloneData& aData,
|
|
JS::Handle<JSObject *> aCpows,
|
|
nsIPrincipal* aPrincipal) override
|
|
{
|
|
nsCOMPtr<nsIRunnable> ev =
|
|
new nsAsyncMessageToSameProcessChild(aCx, aMessage, aData, aCpows,
|
|
aPrincipal);
|
|
NS_DispatchToCurrentThread(ev);
|
|
return true;
|
|
}
|
|
|
|
bool CheckPermission(const nsAString& aPermission) override
|
|
{
|
|
// In a single-process scenario, the child always has all capabilities.
|
|
return true;
|
|
}
|
|
|
|
bool CheckManifestURL(const nsAString& aManifestURL) override
|
|
{
|
|
// In a single-process scenario, the child always has all capabilities.
|
|
return true;
|
|
}
|
|
|
|
bool CheckAppHasPermission(const nsAString& aPermission) override
|
|
{
|
|
// In a single-process scenario, the child always has all capabilities.
|
|
return true;
|
|
}
|
|
|
|
virtual bool CheckAppHasStatus(unsigned short aStatus) override
|
|
{
|
|
// In a single-process scenario, the child always has all capabilities.
|
|
return true;
|
|
}
|
|
};
|
|
|
|
|
|
/**
|
|
* Send messages to the parent process.
|
|
*/
|
|
class ChildProcessMessageManagerCallback : public MessageManagerCallback
|
|
{
|
|
public:
|
|
ChildProcessMessageManagerCallback()
|
|
{
|
|
MOZ_COUNT_CTOR(ChildProcessMessageManagerCallback);
|
|
}
|
|
virtual ~ChildProcessMessageManagerCallback()
|
|
{
|
|
MOZ_COUNT_DTOR(ChildProcessMessageManagerCallback);
|
|
}
|
|
|
|
virtual bool DoSendBlockingMessage(JSContext* aCx,
|
|
const nsAString& aMessage,
|
|
const mozilla::dom::StructuredCloneData& aData,
|
|
JS::Handle<JSObject *> aCpows,
|
|
nsIPrincipal* aPrincipal,
|
|
nsTArray<OwningSerializedStructuredCloneBuffer>* aRetVal,
|
|
bool aIsSync) override
|
|
{
|
|
mozilla::dom::ContentChild* cc =
|
|
mozilla::dom::ContentChild::GetSingleton();
|
|
if (!cc) {
|
|
return true;
|
|
}
|
|
ClonedMessageData data;
|
|
if (!BuildClonedMessageDataForChild(cc, aData, data)) {
|
|
return false;
|
|
}
|
|
InfallibleTArray<mozilla::jsipc::CpowEntry> cpows;
|
|
if (aCpows && !cc->GetCPOWManager()->Wrap(aCx, aCpows, &cpows)) {
|
|
return false;
|
|
}
|
|
if (aIsSync) {
|
|
return cc->SendSyncMessage(PromiseFlatString(aMessage), data, cpows,
|
|
IPC::Principal(aPrincipal), aRetVal);
|
|
}
|
|
return cc->SendRpcMessage(PromiseFlatString(aMessage), data, cpows,
|
|
IPC::Principal(aPrincipal), aRetVal);
|
|
}
|
|
|
|
virtual bool DoSendAsyncMessage(JSContext* aCx,
|
|
const nsAString& aMessage,
|
|
const mozilla::dom::StructuredCloneData& aData,
|
|
JS::Handle<JSObject *> aCpows,
|
|
nsIPrincipal* aPrincipal) override
|
|
{
|
|
mozilla::dom::ContentChild* cc =
|
|
mozilla::dom::ContentChild::GetSingleton();
|
|
if (!cc) {
|
|
return true;
|
|
}
|
|
ClonedMessageData data;
|
|
if (!BuildClonedMessageDataForChild(cc, aData, data)) {
|
|
return false;
|
|
}
|
|
InfallibleTArray<mozilla::jsipc::CpowEntry> cpows;
|
|
if (aCpows && !cc->GetCPOWManager()->Wrap(aCx, aCpows, &cpows)) {
|
|
return false;
|
|
}
|
|
return cc->SendAsyncMessage(PromiseFlatString(aMessage), data, cpows,
|
|
IPC::Principal(aPrincipal));
|
|
}
|
|
|
|
};
|
|
|
|
|
|
class nsAsyncMessageToSameProcessParent : public nsSameProcessAsyncMessageBase,
|
|
public SameProcessMessageQueue::Runnable
|
|
{
|
|
public:
|
|
nsAsyncMessageToSameProcessParent(JSContext* aCx,
|
|
const nsAString& aMessage,
|
|
const StructuredCloneData& aData,
|
|
JS::Handle<JSObject *> aCpows,
|
|
nsIPrincipal* aPrincipal)
|
|
: nsSameProcessAsyncMessageBase(aCx, aMessage, aData, aCpows, aPrincipal)
|
|
{
|
|
}
|
|
|
|
virtual nsresult HandleMessage() override
|
|
{
|
|
nsFrameMessageManager* ppm = nsFrameMessageManager::sSameProcessParentManager;
|
|
ReceiveMessage(static_cast<nsIContentFrameMessageManager*>(ppm), nullptr, ppm);
|
|
return NS_OK;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Send messages to the imaginary parent process in a single-process scenario.
|
|
*/
|
|
class SameChildProcessMessageManagerCallback : public MessageManagerCallback
|
|
{
|
|
public:
|
|
SameChildProcessMessageManagerCallback()
|
|
{
|
|
MOZ_COUNT_CTOR(SameChildProcessMessageManagerCallback);
|
|
}
|
|
virtual ~SameChildProcessMessageManagerCallback()
|
|
{
|
|
MOZ_COUNT_DTOR(SameChildProcessMessageManagerCallback);
|
|
}
|
|
|
|
virtual bool DoSendBlockingMessage(JSContext* aCx,
|
|
const nsAString& aMessage,
|
|
const mozilla::dom::StructuredCloneData& aData,
|
|
JS::Handle<JSObject *> aCpows,
|
|
nsIPrincipal* aPrincipal,
|
|
nsTArray<OwningSerializedStructuredCloneBuffer>* aRetVal,
|
|
bool aIsSync) override
|
|
{
|
|
SameProcessMessageQueue* queue = SameProcessMessageQueue::Get();
|
|
queue->Flush();
|
|
|
|
if (nsFrameMessageManager::sSameProcessParentManager) {
|
|
SameProcessCpowHolder cpows(js::GetRuntime(aCx), aCpows);
|
|
nsRefPtr<nsFrameMessageManager> ppm = nsFrameMessageManager::sSameProcessParentManager;
|
|
ppm->ReceiveMessage(static_cast<nsIContentFrameMessageManager*>(ppm.get()), nullptr, aMessage,
|
|
true, &aData, &cpows, aPrincipal, aRetVal);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
virtual bool DoSendAsyncMessage(JSContext* aCx,
|
|
const nsAString& aMessage,
|
|
const mozilla::dom::StructuredCloneData& aData,
|
|
JS::Handle<JSObject *> aCpows,
|
|
nsIPrincipal* aPrincipal) override
|
|
{
|
|
SameProcessMessageQueue* queue = SameProcessMessageQueue::Get();
|
|
nsRefPtr<nsAsyncMessageToSameProcessParent> ev =
|
|
new nsAsyncMessageToSameProcessParent(aCx, aMessage, aData, aCpows, aPrincipal);
|
|
queue->Push(ev);
|
|
return true;
|
|
}
|
|
|
|
};
|
|
|
|
|
|
// This creates the global parent process message manager.
|
|
nsresult
|
|
NS_NewParentProcessMessageManager(nsIMessageBroadcaster** aResult)
|
|
{
|
|
NS_ASSERTION(!nsFrameMessageManager::sParentProcessManager,
|
|
"Re-creating sParentProcessManager");
|
|
nsRefPtr<nsFrameMessageManager> mm = new nsFrameMessageManager(nullptr,
|
|
nullptr,
|
|
MM_CHROME | MM_PROCESSMANAGER | MM_BROADCASTER);
|
|
nsFrameMessageManager::sParentProcessManager = mm;
|
|
nsFrameMessageManager::NewProcessMessageManager(false); // Create same process message manager.
|
|
mm.forget(aResult);
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
nsFrameMessageManager*
|
|
nsFrameMessageManager::NewProcessMessageManager(bool aIsRemote)
|
|
{
|
|
if (!nsFrameMessageManager::sParentProcessManager) {
|
|
nsCOMPtr<nsIMessageBroadcaster> dummy =
|
|
do_GetService("@mozilla.org/parentprocessmessagemanager;1");
|
|
}
|
|
|
|
MOZ_ASSERT(nsFrameMessageManager::sParentProcessManager,
|
|
"parent process manager not created");
|
|
nsFrameMessageManager* mm;
|
|
if (aIsRemote) {
|
|
// Callback is set in ContentParent::InitInternal so that the process has
|
|
// already started when we send pending scripts.
|
|
mm = new nsFrameMessageManager(nullptr,
|
|
nsFrameMessageManager::sParentProcessManager,
|
|
MM_CHROME | MM_PROCESSMANAGER);
|
|
} else {
|
|
mm = new nsFrameMessageManager(new SameParentProcessMessageManagerCallback(),
|
|
nsFrameMessageManager::sParentProcessManager,
|
|
MM_CHROME | MM_PROCESSMANAGER | MM_OWNSCALLBACK);
|
|
sSameProcessParentManager = mm;
|
|
}
|
|
return mm;
|
|
}
|
|
|
|
nsresult
|
|
NS_NewChildProcessMessageManager(nsISyncMessageSender** aResult)
|
|
{
|
|
NS_ASSERTION(!nsFrameMessageManager::GetChildProcessManager(),
|
|
"Re-creating sChildProcessManager");
|
|
|
|
MessageManagerCallback* cb;
|
|
if (XRE_GetProcessType() == GeckoProcessType_Default) {
|
|
cb = new SameChildProcessMessageManagerCallback();
|
|
} else {
|
|
cb = new ChildProcessMessageManagerCallback();
|
|
RegisterStrongMemoryReporter(new MessageManagerReporter());
|
|
}
|
|
nsFrameMessageManager* mm = new nsFrameMessageManager(cb,
|
|
nullptr,
|
|
MM_PROCESSMANAGER | MM_OWNSCALLBACK);
|
|
nsFrameMessageManager::SetChildProcessManager(mm);
|
|
nsRefPtr<ProcessGlobal> global = new ProcessGlobal(mm);
|
|
NS_ENSURE_TRUE(global->Init(), NS_ERROR_UNEXPECTED);
|
|
global.forget(aResult);
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
static PLDHashOperator
|
|
CycleCollectorMarkListeners(const nsAString& aKey,
|
|
nsAutoTObserverArray<nsMessageListenerInfo, 1>* aListeners,
|
|
void* aData)
|
|
{
|
|
uint32_t count = aListeners->Length();
|
|
for (uint32_t i = 0; i < count; i++) {
|
|
if (aListeners->ElementAt(i).mStrongListener) {
|
|
xpc_TryUnmarkWrappedGrayObject(aListeners->ElementAt(i).mStrongListener);
|
|
}
|
|
}
|
|
return PL_DHASH_NEXT;
|
|
}
|
|
|
|
bool
|
|
nsFrameMessageManager::MarkForCC()
|
|
{
|
|
mListeners.EnumerateRead(CycleCollectorMarkListeners, nullptr);
|
|
|
|
if (mRefCnt.IsPurple()) {
|
|
mRefCnt.RemovePurple();
|
|
}
|
|
return true;
|
|
}
|
|
|
|
nsSameProcessAsyncMessageBase::nsSameProcessAsyncMessageBase(JSContext* aCx,
|
|
const nsAString& aMessage,
|
|
const StructuredCloneData& aData,
|
|
JS::Handle<JSObject*> aCpows,
|
|
nsIPrincipal* aPrincipal)
|
|
: mRuntime(js::GetRuntime(aCx)),
|
|
mMessage(aMessage),
|
|
mCpows(aCx, aCpows),
|
|
mPrincipal(aPrincipal)
|
|
{
|
|
if (aData.mDataLength && !mData.copy(aData.mData, aData.mDataLength)) {
|
|
NS_ABORT_OOM(aData.mDataLength);
|
|
}
|
|
mClosure = aData.mClosure;
|
|
}
|
|
|
|
void
|
|
nsSameProcessAsyncMessageBase::ReceiveMessage(nsISupports* aTarget,
|
|
nsIFrameLoader* aTargetFrameLoader,
|
|
nsFrameMessageManager* aManager)
|
|
{
|
|
if (aManager) {
|
|
StructuredCloneData data;
|
|
data.mData = mData.data();
|
|
data.mDataLength = mData.nbytes();
|
|
data.mClosure = mClosure;
|
|
|
|
SameProcessCpowHolder cpows(mRuntime, mCpows);
|
|
|
|
nsRefPtr<nsFrameMessageManager> mm = aManager;
|
|
mm->ReceiveMessage(aTarget, aTargetFrameLoader, mMessage, false, &data, &cpows,
|
|
mPrincipal, nullptr);
|
|
}
|
|
}
|