fune/dom/base/nsFrameMessageManager.cpp
Ryan VanderMeulen 3a61e232f7 Backed out 14 changesets (bug 1165486) for causing frequent crashes/asserts in automation.
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
2015-06-18 13:05:10 -04:00

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*)&params);
#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);
}
}