gecko-dev/dom/base/nsFrameMessageManager.cpp
Kan-Ru Chen 506dfe6ea3 Bug 1264642 - Part 4. Use BufferList to replace raw buffers in StructuredClone. r=baku r=billm r=jorendorff
In JS StructuredClone BufferList<SystemAllocPolicy> is typedef'd to
JSStructuredCloneData and use everywhere in gecko that stores structured
clone data.

This patch changed some raw pointers to UniquePtr<JSStructuredCloneData>
and some to stack allocated JSStructuredCloneData for better life time
management. Some parameters or methods are deleted because of changing
to the new data structure.

MessagePortMessage now has the exactly same structure with
ClonedMessageData. Maybe in the future they can be consolidated.

MozReview-Commit-ID: 1IY9p5eKLgv
2016-08-23 00:40:46 +08:00

2282 lines
73 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 "nsDOMClassInfoID.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 "nsIDOMClassInfo.h"
#include "xpcpublic.h"
#include "mozilla/CycleCollectedJSRuntime.h"
#include "mozilla/IntentionalCrash.h"
#include "mozilla/Preferences.h"
#include "mozilla/Telemetry.h"
#include "mozilla/dom/File.h"
#include "mozilla/dom/MessagePort.h"
#include "mozilla/dom/MessagePortList.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/ipc/BlobChild.h"
#include "mozilla/dom/ipc/BlobParent.h"
#include "mozilla/dom/ipc/StructuredCloneData.h"
#include "mozilla/dom/DOMStringList.h"
#include "mozilla/jsipc/CrossProcessObjectWrappers.h"
#include "nsPrintfCString.h"
#include "nsXULAppAPI.h"
#include "nsQueryObject.h"
#include <algorithm>
#include "chrome/common/ipc_channel.h" // for IPC::Channel::kMaximumMessageSize
#ifdef MOZ_CRASHREPORTER
#include "nsExceptionHandler.h"
#endif
#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;
}
}
}
NS_IMPL_CYCLE_COLLECTION_CLASS(nsFrameMessageManager)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsFrameMessageManager)
for (auto iter = tmp->mListeners.Iter(); !iter.Done(); iter.Next()) {
nsAutoTObserverArray<nsMessageListenerInfo, 1>* listeners = iter.UserData();
uint32_t count = listeners->Length();
for (uint32_t i = 0; i < count; ++i) {
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "listeners[i] mStrongListener");
cb.NoteXPCOMChild(listeners->ElementAt(i).mStrongListener.get());
}
}
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChildManagers)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParentManager)
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_JS_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)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mParentManager)
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,
StructuredCloneData& aData,
ClonedMessageData& aClonedData)
{
SerializedStructuredCloneBuffer& buffer = aClonedData.data();
auto iter = aData.Data().Iter();
size_t size = aData.Data().Size();
bool success;
buffer.data = aData.Data().Borrow<js::SystemAllocPolicy>(iter, size, &success);
if (NS_WARN_IF(!success)) {
return false;
}
aClonedData.identfiers().AppendElements(aData.PortIdentifiers());
const nsTArray<RefPtr<BlobImpl>>& blobImpls = aData.BlobImpls();
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,
StructuredCloneData& aData,
ClonedMessageData& aClonedData)
{
return BuildClonedMessageData<Parent>(aParent, aData, aClonedData);
}
bool
MessageManagerCallback::BuildClonedMessageDataForChild(nsIContentChild* aChild,
StructuredCloneData& aData,
ClonedMessageData& aClonedData)
{
return BuildClonedMessageData<Child>(aChild, aData, aClonedData);
}
template<ActorFlavorEnum Flavor>
static void
UnpackClonedMessageData(const ClonedMessageData& aClonedData,
StructuredCloneData& aData)
{
const SerializedStructuredCloneBuffer& buffer = aClonedData.data();
typedef typename BlobTraits<Flavor>::ProtocolType ProtocolType;
const InfallibleTArray<ProtocolType*>& blobs = DataBlobs<Flavor>::Blobs(aClonedData);
const InfallibleTArray<MessagePortIdentifier>& identifiers = aClonedData.identfiers();
aData.UseExternalData(buffer.data);
aData.PortIdentifiers().AppendElements(identifiers);
if (!blobs.IsEmpty()) {
uint32_t length = blobs.Length();
aData.BlobImpls().SetCapacity(length);
for (uint32_t i = 0; i < length; ++i) {
auto* blob =
static_cast<typename BlobTraits<Flavor>::BlobType*>(blobs[i]);
MOZ_ASSERT(blob);
RefPtr<BlobImpl> blobImpl = blob->GetBlobImpl();
MOZ_ASSERT(blobImpl);
aData.BlobImpls().AppendElement(blobImpl);
}
}
}
void
mozilla::dom::ipc::UnpackClonedMessageDataForParent(const ClonedMessageData& aClonedData,
StructuredCloneData& aData)
{
UnpackClonedMessageData<Parent>(aClonedData, aData);
}
void
mozilla::dom::ipc::UnpackClonedMessageDataForChild(const ClonedMessageData& aClonedData,
StructuredCloneData& aData)
{
UnpackClonedMessageData<Child>(aClonedData, 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;
}
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);
for (auto iter = mListeners.Iter(); !iter.Done(); iter.Next()) {
nsAutoTObserverArray<nsMessageListenerInfo, 1>* listeners = iter.UserData();
uint32_t count = listeners->Length();
for (uint32_t i = 0; i < count; i++) {
nsWeakPtr weakListener = listeners->ElementAt(i).mWeakListener;
if (weakListener) {
nsCOMPtr<nsISupports> otherCanonical = do_QueryReferent(weakListener);
MOZ_ASSERT((canonical == otherCanonical) == (weak == weakListener));
}
}
}
#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) {
// Cache for future windows or frames
mPendingScripts.AppendElement(aURL);
mPendingScriptsGlobalStates.AppendElement(aRunInGlobalScope);
}
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) {
RefPtr<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& aValue,
const JS::Value& aTransfer,
StructuredCloneData& aData)
{
// First try to use structured clone on the whole thing.
JS::RootedValue v(aCx, aValue);
JS::RootedValue t(aCx, aTransfer);
ErrorResult rv;
aData.Write(aCx, v, t, rv);
if (!rv.Failed()) {
return true;
}
rv.SuppressException();
JS_ClearPendingException(aCx);
nsCOMPtr<nsIConsoleService> console(do_GetService(NS_CONSOLESERVICE_CONTRACTID));
if (console) {
nsAutoString filename;
uint32_t lineno = 0, column = 0;
nsJSUtils::GetCallingLocation(aCx, filename, &lineno, &column);
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, column,
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);
aData.Write(aCx, val, rv);
if (NS_WARN_IF(rv.Failed())) {
rv.SuppressException();
return false;
}
return true;
}
// 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);
}
static bool
AllowMessage(size_t aDataLength, const nsAString& aMessageName)
{
static const size_t kMinTelemetryMessageSize = 8192;
if (aDataLength < kMinTelemetryMessageSize) {
return true;
}
NS_ConvertUTF16toUTF8 messageName(aMessageName);
messageName.StripChars("0123456789");
Telemetry::Accumulate(Telemetry::MESSAGE_MANAGER_MESSAGE_SIZE2, messageName,
aDataLength);
// A message includes more than structured clone data, so subtract
// 20KB to make it more likely that a message within this bound won't
// result in an overly large IPC message.
static const size_t kMaxMessageSize = IPC::Channel::kMaximumMessageSize - 20 * 1024;
if (aDataLength < kMaxMessageSize) {
return true;
}
Telemetry::Accumulate(Telemetry::REJECTED_MESSAGE_MANAGER_MESSAGE,
messageName);
return 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;
if (aArgc >= 2 && !GetParamsForMessage(aCx, aJSON, JS::UndefinedHandleValue, data)) {
return NS_ERROR_DOM_DATA_CLONE_ERR;
}
if (!AllowMessage(data.DataLength(), aMessageName)) {
return NS_ERROR_FAILURE;
}
JS::Rooted<JSObject*> objects(aCx);
if (aArgc >= 3 && aObjects.isObject()) {
objects = &aObjects.toObject();
}
nsTArray<StructuredCloneData> retval;
sSendingSyncMessage |= aIsSync;
bool ok = mCallback->DoSendBlockingMessage(aCx, aMessageName, data, objects,
aPrincipal, &retval, aIsSync);
if (aIsSync) {
sSendingSyncMessage = false;
}
if (!ok) {
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);
ErrorResult rv;
retval[i].Read(aCx, &ret, rv);
if (rv.Failed()) {
MOZ_ASSERT(false, "Unable to read structured clone in SendMessage");
rv.SuppressException();
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,
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;
}
if (!mCallback) {
return NS_ERROR_NOT_INITIALIZED;
}
nsresult rv = mCallback->DoSendAsyncMessage(aCx, aMessage, aData, aCpows, aPrincipal);
if (NS_FAILED(rv)) {
return rv;
}
return NS_OK;
}
nsresult
nsFrameMessageManager::DispatchAsyncMessage(const nsAString& aMessageName,
const JS::Value& aJSON,
const JS::Value& aObjects,
nsIPrincipal* aPrincipal,
const JS::Value& aTransfers,
JSContext* aCx,
uint8_t aArgc)
{
StructuredCloneData data;
if (aArgc >= 2 && !GetParamsForMessage(aCx, aJSON, aTransfers, data)) {
return NS_ERROR_DOM_DATA_CLONE_ERR;
}
if (!AllowMessage(data.DataLength(), aMessageName)) {
return NS_ERROR_FAILURE;
}
JS::Rooted<JSObject*> objects(aCx);
if (aArgc >= 3 && aObjects.isObject()) {
objects = &aObjects.toObject();
}
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,
JS::Handle<JS::Value> aTransfers,
JSContext* aCx,
uint8_t aArgc)
{
return DispatchAsyncMessage(aMessageName, aJSON, aObjects, aPrincipal,
aTransfers, 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,
JS::UndefinedHandleValue, 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_IsContentProcess()) {
mozilla::NoteIntentionalCrash("tab");
return NS_OK;
} else {
return NS_ERROR_NOT_IMPLEMENTED;
}
}
NS_IMETHODIMP
nsFrameMessageManager::GetContent(mozIDOMWindowProxy** 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;
RefPtr<nsFrameMessageManager> mMM;
};
// nsIMessageListener
nsresult
nsFrameMessageManager::ReceiveMessage(nsISupports* aTarget,
nsIFrameLoader* aTargetFrameLoader,
const nsAString& aMessage,
bool aIsSync,
StructuredCloneData* aCloneData,
mozilla::jsipc::CpowHolder* aCpows,
nsIPrincipal* aPrincipal,
nsTArray<StructuredCloneData>* 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,
StructuredCloneData* aCloneData,
mozilla::jsipc::CpowHolder* aCpows,
nsIPrincipal* aPrincipal,
nsTArray<StructuredCloneData>* 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;
}
AutoEntryScript aes(wrappedJS->GetJSObject(), "message manager handler");
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->DataLength()) {
ErrorResult rv;
aCloneData->Read(cx, &json, rv);
if (NS_WARN_IF(rv.Failed())) {
rv.SuppressException();
JS_ClearPendingException(cx);
return NS_OK;
}
}
// Get cloned MessagePort from StructuredCloneData.
nsTArray<RefPtr<mozilla::dom::MessagePort>> ports;
if (aCloneData) {
ports = aCloneData->TakeTransferredPorts();
}
JS::Rooted<JSObject*> transferredList(cx);
RefPtr<MessagePortList> portList = new MessagePortList(aTargetFrameLoader, ports);
transferredList = portList->WrapObject(cx, nullptr);
if (NS_WARN_IF(!transferredList)) {
return NS_ERROR_UNEXPECTED;
}
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) &&
JS_DefineProperty(cx, param, "ports", transferredList, 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) {
ErrorResult rv;
StructuredCloneData* data = aRetVal->AppendElement();
data->Write(cx, rval, rv);
if (NS_WARN_IF(rv.Failed())) {
aRetVal->RemoveElementAt(aRetVal->Length() - 1);
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;
}
}
}
}
}
RefPtr<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);
RefPtr<nsFrameMessageManager> kungfuDeathGrip = this;
RefPtr<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::LoadPendingScripts()
{
RefPtr<nsFrameMessageManager> kungfuDeathGrip = this;
LoadPendingScripts(this, this);
}
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 (!mChrome && XRE_IsParentProcess()) {
// This is the cpmm in the parent process. We should use the same object as the ppmm.
nsCOMPtr<nsIGlobalProcessScriptLoader> ppmm =
do_GetService("@mozilla.org/parentprocessmessagemanager;1");
ppmm->GetInitialProcessData(aCx, &init);
mInitialProcessData = init;
}
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;
};
} // 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)
void
MessageManagerReporter::CountReferents(nsFrameMessageManager* aMessageManager,
MessageManagerReferentCount* aReferentCount)
{
for (auto it = aMessageManager->mListeners.Iter(); !it.Done(); it.Next()) {
nsAutoTObserverArray<nsMessageListenerInfo, 1>* listeners =
it.UserData();
uint32_t listenerCount = listeners->Length();
if (listenerCount == 0) {
continue;
}
nsString key(it.Key());
uint32_t oldCount = 0;
aReferentCount->mMessageCounter.Get(key, &oldCount);
uint32_t currentCount = oldCount + listenerCount;
aReferentCount->mMessageCounter.Put(key, currentCount);
// Keep track of messages that have a suspiciously large
// number of referents (symptom of leak).
if (currentCount == MessageManagerReporter::kSuspectReferentCount) {
aReferentCount->mSuspectMessages.AppendElement(key);
}
for (uint32_t i = 0; i < listenerCount; ++i) {
const nsMessageListenerInfo& listenerInfo = listeners->ElementAt(i);
if (listenerInfo.mWeakListener) {
nsCOMPtr<nsISupports> referent =
do_QueryReferent(listenerInfo.mWeakListener);
if (referent) {
aReferentCount->mWeakAlive++;
} else {
aReferentCount->mWeakDead++;
}
} else {
aReferentCount->mStrong++;
}
}
}
// 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) {
RefPtr<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_IsParentProcess()) {
nsCOMPtr<nsIMessageBroadcaster> globalmm =
do_GetService("@mozilla.org/globalmessagemanager;1");
if (globalmm) {
RefPtr<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_IsParentProcess(),
NS_ERROR_NOT_AVAILABLE);
RefPtr<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;
StaticRefPtr<nsScriptCacheCleaner> nsMessageManagerScriptExecutor::sScriptCacheCleaner;
void
nsMessageManagerScriptExecutor::DidCreateGlobal()
{
NS_ASSERTION(mGlobal, "Should have mGlobal!");
if (!sCachedScripts) {
sCachedScripts =
new nsDataHashtable<nsStringHashKey, nsMessageManagerScriptHolder*>;
sScriptCacheCleaner = new nsScriptCacheCleaner();
}
}
// static
void
nsMessageManagerScriptExecutor::PurgeCache()
{
if (sCachedScripts) {
NS_ASSERTION(sCachedScripts != nullptr, "Need cached scripts");
for (auto iter = sCachedScripts->Iter(); !iter.Done(); iter.Next()) {
delete iter.Data();
iter.Remove();
}
}
}
// static
void
nsMessageManagerScriptExecutor::Shutdown()
{
if (sCachedScripts) {
PurgeCache();
delete sCachedScripts;
sCachedScripts = nullptr;
sScriptCacheCleaner = nullptr;
}
}
void
nsMessageManagerScriptExecutor::LoadScriptInternal(const nsAString& aURL,
bool aRunInGlobalScope)
{
if (!mGlobal || !sCachedScripts) {
return;
}
JS::RootingContext* rcx = RootingCx();
JS::Rooted<JSScript*> script(rcx);
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(rcx, mGlobal->GetJSObject());
if (global) {
AutoEntryScript aes(global, "message manager script load");
JSContext* cx = aes.cx();
if (script) {
if (aRunInGlobalScope) {
JS::RootedValue rval(cx);
JS::CloneAndExecuteScript(cx, script, &rval);
} 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_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
nsIContentPolicy::TYPE_OTHER);
if (!channel) {
return;
}
nsCOMPtr<nsIInputStream> input;
rv = channel->Open2(getter_AddRefs(input));
NS_ENSURE_SUCCESS_VOID(rv);
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) {
// Compile the script in the compilation scope instead of the current global
// to avoid keeping the current compartment alive.
AutoJSAPI jsapi;
if (!jsapi.Init(xpc::CompilationScope())) {
return;
}
JSContext* cx = jsapi.cx();
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;
}
// We're going to run these against some non-global scope.
} else if (!JS::CompileForNonSyntacticScope(cx, options, srcBuf, &script)) {
return;
}
MOZ_ASSERT(script);
aScriptp.set(script);
nsAutoCString scheme;
uri->GetScheme(scheme);
// We don't cache data: scripts!
if (aShouldCache && !scheme.EqualsLiteral("data")) {
// Root the object also for caching.
nsMessageManagerScriptHolder* holder =
new nsMessageManagerScriptHolder(cx, script, aRunInGlobalScope);
sCachedScripts->Put(aURL, holder);
}
}
}
void
nsMessageManagerScriptExecutor::TryCacheLoadAndCompileScript(
const nsAString& aURL,
bool aRunInGlobalScope)
{
JS::Rooted<JSScript*> script(RootingCx());
TryCacheLoadAndCompileScript(aURL, aRunInGlobalScope, true, &script);
}
void
nsMessageManagerScriptExecutor::Trace(const TraceCallbacks& aCallbacks, void* aClosure)
{
for (size_t i = 0, length = mAnonymousGlobalScopes.Length(); i < length; ++i) {
aCallbacks.Trace(&mAnonymousGlobalScopes[i], "mAnonymousGlobalScopes[i]", aClosure);
}
}
bool
nsMessageManagerScriptExecutor::InitChildGlobalInternal(
nsISupports* aScope,
const nsACString& aID)
{
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.creationOptions().setZone(JS::SystemZone);
options.behaviors().setVersion(JSVERSION_LATEST);
if (xpc::SharedMemoryEnabled()) {
options.creationOptions().setSharedMemoryAndAtomicsEnabled(true);
}
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 Runnable
{
public:
nsAsyncMessageToSameProcessChild(JS::RootingContext* aRootingCx,
JS::Handle<JSObject*> aCpows)
: nsSameProcessAsyncMessageBase(aRootingCx, aCpows)
{ }
NS_IMETHOD Run() override
{
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 nsresult DoSendAsyncMessage(JSContext* aCx,
const nsAString& aMessage,
StructuredCloneData& aData,
JS::Handle<JSObject *> aCpows,
nsIPrincipal* aPrincipal) override
{
JS::RootingContext* rcx = JS::RootingContext::get(aCx);
RefPtr<nsAsyncMessageToSameProcessChild> ev =
new nsAsyncMessageToSameProcessChild(rcx, aCpows);
nsresult rv = ev->Init(aMessage, aData, aPrincipal);
if (NS_FAILED(rv)) {
return rv;
}
rv = NS_DispatchToCurrentThread(ev);
if (NS_FAILED(rv)) {
return rv;
}
return NS_OK;
}
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,
StructuredCloneData& aData,
JS::Handle<JSObject *> aCpows,
nsIPrincipal* aPrincipal,
nsTArray<StructuredCloneData>* 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 nsresult DoSendAsyncMessage(JSContext* aCx,
const nsAString& aMessage,
StructuredCloneData& aData,
JS::Handle<JSObject *> aCpows,
nsIPrincipal* aPrincipal) override
{
mozilla::dom::ContentChild* cc =
mozilla::dom::ContentChild::GetSingleton();
if (!cc) {
return NS_OK;
}
ClonedMessageData data;
if (!BuildClonedMessageDataForChild(cc, aData, data)) {
return NS_ERROR_DOM_DATA_CLONE_ERR;
}
InfallibleTArray<mozilla::jsipc::CpowEntry> cpows;
if (aCpows && !cc->GetCPOWManager()->Wrap(aCx, aCpows, &cpows)) {
return NS_ERROR_UNEXPECTED;
}
if (!cc->SendAsyncMessage(PromiseFlatString(aMessage), cpows,
IPC::Principal(aPrincipal), data)) {
return NS_ERROR_UNEXPECTED;
}
return NS_OK;
}
};
class nsAsyncMessageToSameProcessParent : public nsSameProcessAsyncMessageBase,
public SameProcessMessageQueue::Runnable
{
public:
nsAsyncMessageToSameProcessParent(JS::RootingContext* aRootingCx,
JS::Handle<JSObject*> aCpows)
: nsSameProcessAsyncMessageBase(aRootingCx, aCpows)
{ }
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,
StructuredCloneData& aData,
JS::Handle<JSObject *> aCpows,
nsIPrincipal* aPrincipal,
nsTArray<StructuredCloneData>* aRetVal,
bool aIsSync) override
{
SameProcessMessageQueue* queue = SameProcessMessageQueue::Get();
queue->Flush();
if (nsFrameMessageManager::sSameProcessParentManager) {
SameProcessCpowHolder cpows(JS::RootingContext::get(aCx), aCpows);
RefPtr<nsFrameMessageManager> ppm = nsFrameMessageManager::sSameProcessParentManager;
ppm->ReceiveMessage(static_cast<nsIContentFrameMessageManager*>(ppm.get()), nullptr, aMessage,
true, &aData, &cpows, aPrincipal, aRetVal);
}
return true;
}
virtual nsresult DoSendAsyncMessage(JSContext* aCx,
const nsAString& aMessage,
StructuredCloneData& aData,
JS::Handle<JSObject *> aCpows,
nsIPrincipal* aPrincipal) override
{
SameProcessMessageQueue* queue = SameProcessMessageQueue::Get();
JS::RootingContext* rcx = JS::RootingContext::get(aCx);
RefPtr<nsAsyncMessageToSameProcessParent> ev =
new nsAsyncMessageToSameProcessParent(rcx, aCpows);
nsresult rv = ev->Init(aMessage, aData, aPrincipal);
if (NS_FAILED(rv)) {
return rv;
}
queue->Push(ev);
return NS_OK;
}
};
// This creates the global parent process message manager.
nsresult
NS_NewParentProcessMessageManager(nsIMessageBroadcaster** aResult)
{
NS_ASSERTION(!nsFrameMessageManager::sParentProcessManager,
"Re-creating sParentProcessManager");
RefPtr<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_IsParentProcess()) {
cb = new SameChildProcessMessageManagerCallback();
} else {
cb = new ChildProcessMessageManagerCallback();
RegisterStrongMemoryReporter(new MessageManagerReporter());
}
nsFrameMessageManager* mm = new nsFrameMessageManager(cb,
nullptr,
MM_PROCESSMANAGER | MM_OWNSCALLBACK);
nsFrameMessageManager::SetChildProcessManager(mm);
RefPtr<ProcessGlobal> global = new ProcessGlobal(mm);
NS_ENSURE_TRUE(global->Init(), NS_ERROR_UNEXPECTED);
global.forget(aResult);
return NS_OK;
}
bool
nsFrameMessageManager::MarkForCC()
{
for (auto iter = mListeners.Iter(); !iter.Done(); iter.Next()) {
nsAutoTObserverArray<nsMessageListenerInfo, 1>* listeners = iter.UserData();
uint32_t count = listeners->Length();
for (uint32_t i = 0; i < count; i++) {
nsCOMPtr<nsIMessageListener> strongListener =
listeners->ElementAt(i).mStrongListener;
if (strongListener) {
xpc_TryUnmarkWrappedGrayObject(strongListener);
}
}
}
if (mRefCnt.IsPurple()) {
mRefCnt.RemovePurple();
}
return true;
}
nsSameProcessAsyncMessageBase::nsSameProcessAsyncMessageBase(JS::RootingContext* aRootingCx,
JS::Handle<JSObject*> aCpows)
: mRootingCx(aRootingCx)
, mCpows(aRootingCx, aCpows)
#ifdef DEBUG
, mCalledInit(false)
#endif
{ }
nsresult
nsSameProcessAsyncMessageBase::Init(const nsAString& aMessage,
StructuredCloneData& aData,
nsIPrincipal* aPrincipal)
{
if (!mData.Copy(aData)) {
Telemetry::Accumulate(Telemetry::IPC_SAME_PROCESS_MESSAGE_COPY_OOM_KB, aData.DataLength());
return NS_ERROR_OUT_OF_MEMORY;
}
mMessage = aMessage;
mPrincipal = aPrincipal;
#ifdef DEBUG
mCalledInit = true;
#endif
return NS_OK;
}
void
nsSameProcessAsyncMessageBase::ReceiveMessage(nsISupports* aTarget,
nsIFrameLoader* aTargetFrameLoader,
nsFrameMessageManager* aManager)
{
// Make sure that we have called Init() and it has succeeded.
MOZ_ASSERT(mCalledInit);
if (aManager) {
SameProcessCpowHolder cpows(mRootingCx, mCpows);
RefPtr<nsFrameMessageManager> mm = aManager;
mm->ReceiveMessage(aTarget, aTargetFrameLoader, mMessage, false, &mData,
&cpows, mPrincipal, nullptr);
}
}