The inclusions were removed with the following very crude script and the
resulting breakage was fixed up by hand. The manual fixups did either
revert the changes done by the script, replace a generic header with a more
specific one or replace a header with a forward declaration.
find . -name "*.idl" | grep -v web-platform | grep -v third_party | while read path; do
interfaces=$(grep "^\(class\|interface\).*:.*" "$path" | cut -d' ' -f2)
if [ -n "$interfaces" ]; then
if [[ "$interfaces" == *$'\n'* ]]; then
regexp="\("
for i in $interfaces; do regexp="$regexp$i\|"; done
regexp="${regexp%%\\\|}\)"
else
regexp="$interfaces"
fi
interface=$(basename "$path")
rg -l "#include.*${interface%%.idl}.h" . | while read path2; do
hits=$(grep -v "#include.*${interface%%.idl}.h" "$path2" | grep -c "$regexp" )
if [ $hits -eq 0 ]; then
echo "Removing ${interface} from ${path2}"
grep -v "#include.*${interface%%.idl}.h" "$path2" > "$path2".tmp
mv -f "$path2".tmp "$path2"
fi
done
fi
done
Differential Revision: https://phabricator.services.mozilla.com/D55442
1675 lines
56 KiB
C++
1675 lines
56 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 "ContentChild.h"
|
|
#include "GeckoProfiler.h"
|
|
#include "nsASCIIMask.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 "mozilla/dom/ScriptLoader.h"
|
|
#include "nsFrameLoader.h"
|
|
#include "nsIInputStream.h"
|
|
#include "nsIScriptError.h"
|
|
#include "nsIConsoleService.h"
|
|
#include "nsIMemoryReporter.h"
|
|
#include "nsIProtocolHandler.h"
|
|
#include "xpcpublic.h"
|
|
#include "js/CompilationAndEvaluation.h"
|
|
#include "js/JSON.h"
|
|
#include "js/SourceText.h"
|
|
#include "mozilla/ClearOnShutdown.h"
|
|
#include "mozilla/CycleCollectedJSContext.h"
|
|
#include "mozilla/Preferences.h"
|
|
#include "mozilla/ScriptPreloader.h"
|
|
#include "mozilla/Telemetry.h"
|
|
#include "mozilla/dom/ChildProcessMessageManager.h"
|
|
#include "mozilla/dom/ChromeMessageBroadcaster.h"
|
|
#include "mozilla/dom/File.h"
|
|
#include "mozilla/dom/MessageManagerBinding.h"
|
|
#include "mozilla/dom/MessagePort.h"
|
|
#include "mozilla/dom/ContentParent.h"
|
|
#include "mozilla/dom/ContentProcessMessageManager.h"
|
|
#include "mozilla/dom/ParentProcessMessageManager.h"
|
|
#include "mozilla/dom/PermissionMessageUtils.h"
|
|
#include "mozilla/dom/ProcessMessageManager.h"
|
|
#include "mozilla/dom/SameProcessMessageQueue.h"
|
|
#include "mozilla/dom/ScriptSettings.h"
|
|
#include "mozilla/dom/ToJSValue.h"
|
|
#include "mozilla/dom/ipc/SharedMap.h"
|
|
#include "mozilla/dom/ipc/StructuredCloneData.h"
|
|
#include "mozilla/dom/DOMStringList.h"
|
|
#include "mozilla/jsipc/CrossProcessObjectWrappers.h"
|
|
#include "mozilla/recordreplay/ParentIPC.h"
|
|
#include "nsPrintfCString.h"
|
|
#include "nsXULAppAPI.h"
|
|
#include "nsQueryObject.h"
|
|
#include "xpcprivate.h"
|
|
#include <algorithm>
|
|
#include "chrome/common/ipc_channel.h" // for IPC::Channel::kMaximumMessageSize
|
|
|
|
#ifdef XP_WIN
|
|
# if defined(SendMessage)
|
|
# undef SendMessage
|
|
# endif
|
|
#endif
|
|
|
|
#ifdef FUZZING
|
|
# include "MessageManagerFuzzer.h"
|
|
#endif
|
|
|
|
using namespace mozilla;
|
|
using namespace mozilla::dom;
|
|
using namespace mozilla::dom::ipc;
|
|
|
|
nsFrameMessageManager::nsFrameMessageManager(MessageManagerCallback* aCallback,
|
|
MessageManagerFlags aFlags)
|
|
: mChrome(aFlags & MessageManagerFlags::MM_CHROME),
|
|
mGlobal(aFlags & MessageManagerFlags::MM_GLOBAL),
|
|
mIsProcessManager(aFlags & MessageManagerFlags::MM_PROCESSMANAGER),
|
|
mIsBroadcaster(aFlags & MessageManagerFlags::MM_BROADCASTER),
|
|
mOwnsCallback(aFlags & MessageManagerFlags::MM_OWNSCALLBACK),
|
|
mHandlingMessage(false),
|
|
mClosed(false),
|
|
mDisconnected(false),
|
|
mCallback(aCallback) {
|
|
NS_ASSERTION(!mIsBroadcaster || !mCallback,
|
|
"Broadcasters cannot have callbacks!");
|
|
if (mOwnsCallback) {
|
|
mOwnedCallback = aCallback;
|
|
}
|
|
}
|
|
|
|
nsFrameMessageManager::~nsFrameMessageManager() {
|
|
for (int32_t i = mChildManagers.Length(); i > 0; --i) {
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
|
|
inline void ImplCycleCollectionTraverse(
|
|
nsCycleCollectionTraversalCallback& aCallback,
|
|
nsMessageListenerInfo& aField, const char* aName, uint32_t aFlags = 0) {
|
|
ImplCycleCollectionTraverse(aCallback, aField.mStrongListener, aName, aFlags);
|
|
ImplCycleCollectionTraverse(aCallback, aField.mWeakListener, aName, aFlags);
|
|
}
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_CLASS(nsFrameMessageManager)
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsFrameMessageManager)
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mListeners)
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChildManagers)
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSharedData)
|
|
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)
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mListeners)
|
|
for (int32_t i = tmp->mChildManagers.Length(); i > 0; --i) {
|
|
tmp->mChildManagers[i - 1]->Disconnect(false);
|
|
}
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mChildManagers)
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mSharedData)
|
|
tmp->mInitialProcessData.setNull();
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
|
|
|
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsFrameMessageManager)
|
|
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
|
|
|
/* Message managers in child process implement nsIMessageSender.
|
|
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(nsIMessageSender,
|
|
!mChrome || !mIsBroadcaster)
|
|
|
|
NS_INTERFACE_MAP_END
|
|
|
|
NS_IMPL_CYCLE_COLLECTING_ADDREF(nsFrameMessageManager)
|
|
NS_IMPL_CYCLE_COLLECTING_RELEASE(nsFrameMessageManager)
|
|
|
|
void MessageManagerCallback::DoGetRemoteType(nsAString& aRemoteType,
|
|
ErrorResult& aError) const {
|
|
aRemoteType.Truncate();
|
|
mozilla::dom::ProcessMessageManager* parent = GetProcessMessageManager();
|
|
if (!parent) {
|
|
return;
|
|
}
|
|
|
|
parent->GetRemoteType(aRemoteType, aError);
|
|
}
|
|
|
|
bool MessageManagerCallback::BuildClonedMessageDataForParent(
|
|
ContentParent* aParent, StructuredCloneData& aData,
|
|
ClonedMessageData& aClonedData) {
|
|
return aData.BuildClonedMessageDataForParent(aParent, aClonedData);
|
|
}
|
|
|
|
bool MessageManagerCallback::BuildClonedMessageDataForChild(
|
|
ContentChild* aChild, StructuredCloneData& aData,
|
|
ClonedMessageData& aClonedData) {
|
|
return aData.BuildClonedMessageDataForChild(aChild, aClonedData);
|
|
}
|
|
|
|
void mozilla::dom::ipc::UnpackClonedMessageDataForParent(
|
|
const ClonedMessageData& aClonedData, StructuredCloneData& aData) {
|
|
aData.BorrowFromClonedMessageDataForParent(aClonedData);
|
|
}
|
|
|
|
void mozilla::dom::ipc::UnpackClonedMessageDataForChild(
|
|
const ClonedMessageData& aClonedData, StructuredCloneData& aData) {
|
|
aData.BorrowFromClonedMessageDataForChild(aClonedData);
|
|
}
|
|
|
|
bool SameProcessCpowHolder::ToObject(JSContext* aCx,
|
|
JS::MutableHandle<JSObject*> aObjp) {
|
|
if (!mObj) {
|
|
return true;
|
|
}
|
|
|
|
aObjp.set(mObj);
|
|
return JS_WrapObject(aCx, aObjp);
|
|
}
|
|
|
|
void nsFrameMessageManager::AddMessageListener(const nsAString& aMessageName,
|
|
MessageListener& aListener,
|
|
bool aListenWhenClosed,
|
|
ErrorResult& aError) {
|
|
auto& listeners = mListeners.LookupForAdd(aMessageName).OrInsert([]() {
|
|
return new nsAutoTObserverArray<nsMessageListenerInfo, 1>();
|
|
});
|
|
uint32_t len = listeners->Length();
|
|
for (uint32_t i = 0; i < len; ++i) {
|
|
MessageListener* strongListener = listeners->ElementAt(i).mStrongListener;
|
|
if (strongListener && *strongListener == aListener) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
nsMessageListenerInfo* entry = listeners->AppendElement();
|
|
entry->mStrongListener = &aListener;
|
|
entry->mListenWhenClosed = aListenWhenClosed;
|
|
}
|
|
|
|
void nsFrameMessageManager::RemoveMessageListener(const nsAString& aMessageName,
|
|
MessageListener& aListener,
|
|
ErrorResult& aError) {
|
|
nsAutoTObserverArray<nsMessageListenerInfo, 1>* listeners =
|
|
mListeners.Get(aMessageName);
|
|
if (listeners) {
|
|
uint32_t len = listeners->Length();
|
|
for (uint32_t i = 0; i < len; ++i) {
|
|
MessageListener* strongListener = listeners->ElementAt(i).mStrongListener;
|
|
if (strongListener && *strongListener == aListener) {
|
|
listeners->RemoveElementAt(i);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static already_AddRefed<nsISupports> ToXPCOMMessageListener(
|
|
MessageListener& aListener) {
|
|
return CallbackObjectHolder<mozilla::dom::MessageListener, nsISupports>(
|
|
&aListener)
|
|
.ToXPCOMCallback();
|
|
}
|
|
|
|
void nsFrameMessageManager::AddWeakMessageListener(
|
|
const nsAString& aMessageName, MessageListener& aListener,
|
|
ErrorResult& aError) {
|
|
nsCOMPtr<nsISupports> listener(ToXPCOMMessageListener(aListener));
|
|
nsWeakPtr weak = do_GetWeakReference(listener);
|
|
if (!weak) {
|
|
aError.Throw(NS_ERROR_NO_INTERFACE);
|
|
return;
|
|
}
|
|
|
|
#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(listener);
|
|
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
|
|
|
|
auto& listeners = mListeners.LookupForAdd(aMessageName).OrInsert([]() {
|
|
return new nsAutoTObserverArray<nsMessageListenerInfo, 1>();
|
|
});
|
|
uint32_t len = listeners->Length();
|
|
for (uint32_t i = 0; i < len; ++i) {
|
|
if (listeners->ElementAt(i).mWeakListener == weak) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
nsMessageListenerInfo* entry = listeners->AppendElement();
|
|
entry->mWeakListener = weak;
|
|
entry->mListenWhenClosed = false;
|
|
}
|
|
|
|
void nsFrameMessageManager::RemoveWeakMessageListener(
|
|
const nsAString& aMessageName, MessageListener& aListener,
|
|
ErrorResult& aError) {
|
|
nsCOMPtr<nsISupports> listener(ToXPCOMMessageListener(aListener));
|
|
nsWeakPtr weak = do_GetWeakReference(listener);
|
|
if (!weak) {
|
|
aError.Throw(NS_ERROR_NO_INTERFACE);
|
|
return;
|
|
}
|
|
|
|
nsAutoTObserverArray<nsMessageListenerInfo, 1>* listeners =
|
|
mListeners.Get(aMessageName);
|
|
if (!listeners) {
|
|
return;
|
|
}
|
|
|
|
uint32_t len = listeners->Length();
|
|
for (uint32_t i = 0; i < len; ++i) {
|
|
if (listeners->ElementAt(i).mWeakListener == weak) {
|
|
listeners->RemoveElementAt(i);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
void nsFrameMessageManager::LoadScript(const nsAString& aURL,
|
|
bool aAllowDelayedLoad,
|
|
bool aRunInGlobalScope,
|
|
ErrorResult& aError) {
|
|
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
|
|
if (!mCallback->DoLoadMessageManagerScript(aURL, aRunInGlobalScope)) {
|
|
aError.Throw(NS_ERROR_FAILURE);
|
|
return;
|
|
}
|
|
}
|
|
|
|
for (uint32_t i = 0; i < mChildManagers.Length(); ++i) {
|
|
RefPtr<nsFrameMessageManager> mm = 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, IgnoreErrors());
|
|
}
|
|
}
|
|
}
|
|
|
|
void 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;
|
|
}
|
|
}
|
|
}
|
|
|
|
void nsFrameMessageManager::GetDelayedScripts(
|
|
JSContext* aCx, nsTArray<nsTArray<JS::Value>>& aList, ErrorResult& aError) {
|
|
// 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");
|
|
aError.Throw(NS_ERROR_NOT_IMPLEMENTED);
|
|
return;
|
|
}
|
|
|
|
aError.MightThrowJSException();
|
|
|
|
aList.SetCapacity(mPendingScripts.Length());
|
|
for (uint32_t i = 0; i < mPendingScripts.Length(); ++i) {
|
|
JS::Rooted<JS::Value> url(aCx);
|
|
if (!ToJSValue(aCx, mPendingScripts[i], &url)) {
|
|
aError.NoteJSContextException(aCx);
|
|
return;
|
|
}
|
|
|
|
nsTArray<JS::Value>* array = aList.AppendElement(2);
|
|
array->AppendElement(url);
|
|
array->AppendElement(JS::BooleanValue(mPendingScriptsGlobalStates[i]));
|
|
}
|
|
}
|
|
|
|
/* static */
|
|
bool nsFrameMessageManager::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",
|
|
false /* from private window */,
|
|
true /* from chrome context */);
|
|
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(nsContentUtils::StringifyJSON(aCx, &v, 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;
|
|
}
|
|
|
|
static bool sSendingSyncMessage = false;
|
|
|
|
static bool AllowMessage(size_t aDataLength, const nsAString& aMessageName) {
|
|
// 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;
|
|
}
|
|
|
|
NS_ConvertUTF16toUTF8 messageName(aMessageName);
|
|
messageName.StripTaggedASCII(ASCIIMask::Mask0to9());
|
|
|
|
Telemetry::Accumulate(Telemetry::REJECTED_MESSAGE_MANAGER_MESSAGE,
|
|
messageName);
|
|
|
|
return false;
|
|
}
|
|
|
|
void nsFrameMessageManager::SendMessage(
|
|
JSContext* aCx, const nsAString& aMessageName, JS::Handle<JS::Value> aObj,
|
|
JS::Handle<JSObject*> aObjects, nsIPrincipal* aPrincipal, bool aIsSync,
|
|
nsTArray<JS::Value>& aResult, ErrorResult& aError) {
|
|
NS_ASSERTION(!IsGlobal(), "Should not call SendSyncMessage in chrome");
|
|
NS_ASSERTION(!IsBroadcaster(), "Should not call SendSyncMessage in chrome");
|
|
NS_ASSERTION(!GetParentManager(),
|
|
"Should not have parent manager in content!");
|
|
|
|
AUTO_PROFILER_LABEL_DYNAMIC_LOSSY_NSSTRING(
|
|
"nsFrameMessageManager::SendMessage", OTHER, aMessageName);
|
|
|
|
if (sSendingSyncMessage && aIsSync) {
|
|
// No kind of blocking send should be issued on top of a sync message.
|
|
aError.Throw(NS_ERROR_UNEXPECTED);
|
|
return;
|
|
}
|
|
|
|
StructuredCloneData data;
|
|
if (!aObj.isUndefined() &&
|
|
!GetParamsForMessage(aCx, aObj, JS::UndefinedHandleValue, data)) {
|
|
aError.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
|
|
return;
|
|
}
|
|
|
|
#ifdef FUZZING
|
|
if (data.DataLength() > 0) {
|
|
MessageManagerFuzzer::TryMutate(aCx, aMessageName, &data,
|
|
JS::UndefinedHandleValue);
|
|
}
|
|
#endif
|
|
|
|
if (!AllowMessage(data.DataLength(), aMessageName)) {
|
|
aError.Throw(NS_ERROR_FAILURE);
|
|
return;
|
|
}
|
|
|
|
if (!mCallback) {
|
|
aError.Throw(NS_ERROR_NOT_INITIALIZED);
|
|
return;
|
|
}
|
|
|
|
nsTArray<StructuredCloneData> retval;
|
|
|
|
TimeStamp start = TimeStamp::Now();
|
|
sSendingSyncMessage |= aIsSync;
|
|
bool ok = mCallback->DoSendBlockingMessage(aCx, aMessageName, data, aObjects,
|
|
aPrincipal, &retval, aIsSync);
|
|
if (aIsSync) {
|
|
sSendingSyncMessage = false;
|
|
}
|
|
|
|
uint32_t latencyMs = round((TimeStamp::Now() - start).ToMilliseconds());
|
|
if (latencyMs >= kMinTelemetrySyncMessageManagerLatencyMs) {
|
|
NS_ConvertUTF16toUTF8 messageName(aMessageName);
|
|
// NOTE: We need to strip digit characters from the message name in order to
|
|
// avoid a large number of buckets due to generated names from addons (such
|
|
// as "ublock:sb:{N}"). See bug 1348113 comment 10.
|
|
messageName.StripTaggedASCII(ASCIIMask::Mask0to9());
|
|
Telemetry::Accumulate(Telemetry::IPC_SYNC_MESSAGE_MANAGER_LATENCY_MS,
|
|
messageName, latencyMs);
|
|
}
|
|
|
|
if (!ok) {
|
|
return;
|
|
}
|
|
|
|
uint32_t len = retval.Length();
|
|
aResult.SetCapacity(len);
|
|
for (uint32_t i = 0; i < len; ++i) {
|
|
JS::Rooted<JS::Value> ret(aCx);
|
|
retval[i].Read(aCx, &ret, aError);
|
|
if (aError.Failed()) {
|
|
MOZ_ASSERT(false, "Unable to read structured clone in SendMessage");
|
|
return;
|
|
}
|
|
aResult.AppendElement(ret);
|
|
}
|
|
}
|
|
|
|
nsresult nsFrameMessageManager::DispatchAsyncMessageInternal(
|
|
JSContext* aCx, const nsAString& aMessage, StructuredCloneData& aData,
|
|
JS::Handle<JSObject*> aCpows, nsIPrincipal* aPrincipal) {
|
|
if (mIsBroadcaster) {
|
|
uint32_t len = mChildManagers.Length();
|
|
for (uint32_t i = 0; i < len; ++i) {
|
|
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;
|
|
}
|
|
|
|
void nsFrameMessageManager::DispatchAsyncMessage(
|
|
JSContext* aCx, const nsAString& aMessageName, JS::Handle<JS::Value> aObj,
|
|
JS::Handle<JSObject*> aObjects, nsIPrincipal* aPrincipal,
|
|
JS::Handle<JS::Value> aTransfers, ErrorResult& aError) {
|
|
StructuredCloneData data;
|
|
if (!aObj.isUndefined() &&
|
|
!GetParamsForMessage(aCx, aObj, aTransfers, data)) {
|
|
aError.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
|
|
return;
|
|
}
|
|
|
|
#ifdef FUZZING
|
|
if (data.DataLength()) {
|
|
MessageManagerFuzzer::TryMutate(aCx, aMessageName, &data, aTransfers);
|
|
}
|
|
#endif
|
|
|
|
if (!AllowMessage(data.DataLength(), aMessageName)) {
|
|
aError.Throw(NS_ERROR_FAILURE);
|
|
return;
|
|
}
|
|
|
|
aError = DispatchAsyncMessageInternal(aCx, aMessageName, data, aObjects,
|
|
aPrincipal);
|
|
}
|
|
|
|
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;
|
|
};
|
|
|
|
// When recording or replaying, return whether a message should be received in
|
|
// the middleman process instead of the recording/replaying process.
|
|
static bool DirectMessageToMiddleman(const nsAString& aMessage) {
|
|
// Middleman processes run developer tools server code and need to receive
|
|
// debugger related messages. The session store flush message needs to be
|
|
// received in order to cleanly shutdown the process.
|
|
return (StringBeginsWith(aMessage, NS_LITERAL_STRING("debug:")) &&
|
|
recordreplay::parent::DebuggerRunsInMiddleman()) ||
|
|
aMessage.EqualsLiteral("SessionStore:flush");
|
|
}
|
|
|
|
void nsFrameMessageManager::ReceiveMessage(
|
|
nsISupports* aTarget, nsFrameLoader* aTargetFrameLoader, bool aTargetClosed,
|
|
const nsAString& aMessage, bool aIsSync, StructuredCloneData* aCloneData,
|
|
mozilla::jsipc::CpowHolder* aCpows, nsIPrincipal* aPrincipal,
|
|
nsTArray<StructuredCloneData>* aRetVal, ErrorResult& aError) {
|
|
// If we are recording or replaying, we will end up here in both the
|
|
// middleman process and the recording/replaying process. Ignore the message
|
|
// in one of the processes, so that it is only received in one place.
|
|
if (recordreplay::IsRecordingOrReplaying()) {
|
|
if (DirectMessageToMiddleman(aMessage)) {
|
|
return;
|
|
}
|
|
} else if (recordreplay::IsMiddleman()) {
|
|
if (!DirectMessageToMiddleman(aMessage)) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
MOZ_ASSERT(aTarget);
|
|
|
|
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;
|
|
}
|
|
|
|
JS::RootingContext* rcx = RootingCx();
|
|
JS::Rooted<JSObject*> object(rcx);
|
|
JS::Rooted<JSObject*> objectGlobal(rcx);
|
|
|
|
RefPtr<MessageListener> webIDLListener;
|
|
if (!weakListener) {
|
|
webIDLListener = listener.mStrongListener;
|
|
object = webIDLListener->CallbackOrNull();
|
|
objectGlobal = webIDLListener->CallbackGlobalOrNull();
|
|
} else {
|
|
nsCOMPtr<nsIXPConnectWrappedJS> wrappedJS =
|
|
do_QueryInterface(weakListener);
|
|
if (!wrappedJS) {
|
|
continue;
|
|
}
|
|
|
|
object = wrappedJS->GetJSObject();
|
|
objectGlobal = wrappedJS->GetJSObjectGlobal();
|
|
}
|
|
|
|
if (!object) {
|
|
continue;
|
|
}
|
|
|
|
AutoEntryScript aes(js::UncheckedUnwrap(object),
|
|
"message manager handler");
|
|
JSContext* cx = aes.cx();
|
|
|
|
// We passed the unwrapped object to AutoEntryScript so we now need to
|
|
// enter the realm of the global object that represents the realm of our
|
|
// callback.
|
|
JSAutoRealm ar(cx, objectGlobal);
|
|
|
|
RootedDictionary<ReceiveMessageArgument> argument(cx);
|
|
|
|
JS::Rooted<JSObject*> cpows(cx);
|
|
if (aCpows && !aCpows->ToObject(cx, &cpows)) {
|
|
aError.Throw(NS_ERROR_UNEXPECTED);
|
|
return;
|
|
}
|
|
|
|
if (!cpows) {
|
|
cpows = JS_NewPlainObject(cx);
|
|
if (!cpows) {
|
|
aError.Throw(NS_ERROR_UNEXPECTED);
|
|
return;
|
|
}
|
|
}
|
|
argument.mObjects = cpows;
|
|
|
|
JS::Rooted<JS::Value> json(cx, JS::NullValue());
|
|
if (aCloneData && aCloneData->DataLength()) {
|
|
aCloneData->Read(cx, &json, aError);
|
|
if (NS_WARN_IF(aError.Failed())) {
|
|
aError.SuppressException();
|
|
JS_ClearPendingException(cx);
|
|
return;
|
|
}
|
|
}
|
|
argument.mData = json;
|
|
argument.mJson = json;
|
|
|
|
// Get cloned MessagePort from StructuredCloneData.
|
|
if (aCloneData) {
|
|
Sequence<OwningNonNull<MessagePort>> ports;
|
|
if (!aCloneData->TakeTransferredPortsAsSequence(ports)) {
|
|
aError.Throw(NS_ERROR_FAILURE);
|
|
return;
|
|
}
|
|
argument.mPorts.Construct(std::move(ports));
|
|
}
|
|
|
|
argument.mName = aMessage;
|
|
argument.mPrincipal = aPrincipal;
|
|
argument.mSync = aIsSync;
|
|
argument.mTarget = aTarget;
|
|
if (aTargetFrameLoader) {
|
|
argument.mTargetFrameLoader.Construct(*aTargetFrameLoader);
|
|
}
|
|
|
|
JS::Rooted<JS::Value> thisValue(cx, JS::UndefinedValue());
|
|
|
|
if (JS::IsCallable(object)) {
|
|
// A small hack to get 'this' value right on content side where
|
|
// messageManager is wrapped in BrowserChildMessageManager's global.
|
|
nsCOMPtr<nsISupports> defaultThisValue;
|
|
if (mChrome) {
|
|
defaultThisValue = do_QueryObject(this);
|
|
} else {
|
|
defaultThisValue = aTarget;
|
|
}
|
|
js::AssertSameCompartment(cx, object);
|
|
aError = nsContentUtils::WrapNative(cx, defaultThisValue, &thisValue);
|
|
if (aError.Failed()) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
JS::Rooted<JS::Value> rval(cx, JS::UndefinedValue());
|
|
if (webIDLListener) {
|
|
webIDLListener->ReceiveMessage(thisValue, argument, &rval, aError);
|
|
if (aError.Failed()) {
|
|
// At this point the call to ReceiveMessage will have reported any
|
|
// exceptions (we kept the default of eReportExceptions). We suppress
|
|
// the failure in the ErrorResult and continue.
|
|
aError.SuppressException();
|
|
continue;
|
|
}
|
|
} else {
|
|
JS::Rooted<JS::Value> funval(cx);
|
|
if (JS::IsCallable(object)) {
|
|
// If the listener is a JS function:
|
|
funval.setObject(*object);
|
|
} else {
|
|
// If the listener is a JS object which has receiveMessage function:
|
|
if (!JS_GetProperty(cx, object, "receiveMessage", &funval) ||
|
|
!funval.isObject()) {
|
|
aError.Throw(NS_ERROR_UNEXPECTED);
|
|
return;
|
|
}
|
|
|
|
// Check if the object is even callable.
|
|
if (!JS::IsCallable(&funval.toObject())) {
|
|
aError.Throw(NS_ERROR_UNEXPECTED);
|
|
return;
|
|
}
|
|
thisValue.setObject(*object);
|
|
}
|
|
|
|
JS::Rooted<JS::Value> argv(cx);
|
|
if (!ToJSValue(cx, argument, &argv)) {
|
|
aError.Throw(NS_ERROR_UNEXPECTED);
|
|
return;
|
|
}
|
|
|
|
{
|
|
JS::Rooted<JSObject*> thisObject(cx, thisValue.toObjectOrNull());
|
|
js::AssertSameCompartment(cx, thisObject);
|
|
if (!JS_CallFunctionValue(cx, thisObject, funval,
|
|
JS::HandleValueArray(argv), &rval)) {
|
|
// Because the AutoEntryScript is inside the loop this continue will
|
|
// make us report any exceptions (after which we'll move on to the
|
|
// next listener).
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (aRetVal) {
|
|
StructuredCloneData* data = aRetVal->AppendElement();
|
|
data->InitScope(JS::StructuredCloneScope::DifferentProcess);
|
|
data->Write(cx, rval, aError);
|
|
if (NS_WARN_IF(aError.Failed())) {
|
|
aRetVal->RemoveLastElement();
|
|
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",
|
|
false /* from private window */,
|
|
true /* from chrome context */);
|
|
console->LogMessage(error);
|
|
}
|
|
|
|
JS_ClearPendingException(cx);
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
RefPtr<nsFrameMessageManager> kungFuDeathGrip = GetParentManager();
|
|
if (kungFuDeathGrip) {
|
|
kungFuDeathGrip->ReceiveMessage(aTarget, aTargetFrameLoader, aTargetClosed,
|
|
aMessage, aIsSync, aCloneData, aCpows,
|
|
aPrincipal, aRetVal, aError);
|
|
}
|
|
}
|
|
|
|
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.
|
|
nsFrameMessageManager* parentManager = aManager->GetParentManager();
|
|
if (parentManager) {
|
|
LoadPendingScripts(parentManager, aChildMM);
|
|
}
|
|
|
|
for (uint32_t i = 0; i < aManager->mPendingScripts.Length(); ++i) {
|
|
aChildMM->LoadScript(aManager->mPendingScripts[i], false,
|
|
aManager->mPendingScriptsGlobalStates[i],
|
|
IgnoreErrors());
|
|
}
|
|
}
|
|
|
|
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::Close() {
|
|
if (!mClosed) {
|
|
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
|
|
if (obs) {
|
|
obs->NotifyObservers(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(this, "message-manager-disconnect", nullptr);
|
|
}
|
|
}
|
|
|
|
ClearParentManager(aRemoveFromParent);
|
|
|
|
mDisconnected = true;
|
|
if (!mHandlingMessage) {
|
|
mListeners.Clear();
|
|
}
|
|
}
|
|
|
|
void nsFrameMessageManager::SetInitialProcessData(
|
|
JS::HandleValue aInitialData) {
|
|
MOZ_ASSERT(!mChrome);
|
|
MOZ_ASSERT(mIsProcessManager);
|
|
MOZ_ASSERT(aInitialData.isObject());
|
|
mInitialProcessData = aInitialData;
|
|
}
|
|
|
|
void nsFrameMessageManager::GetInitialProcessData(
|
|
JSContext* aCx, JS::MutableHandle<JS::Value> aInitialProcessData,
|
|
ErrorResult& aError) {
|
|
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 realm, that realm would leak until shutdown.
|
|
JS::RootedObject global(aCx, xpc::PrivilegedJunkScope());
|
|
JSAutoRealm ar(aCx, global);
|
|
|
|
JS::RootedObject obj(aCx, JS_NewPlainObject(aCx));
|
|
if (!obj) {
|
|
aError.NoteJSContextException(aCx);
|
|
return;
|
|
}
|
|
|
|
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. Create it first through do_GetService and use the cached
|
|
// pointer in sParentProcessManager.
|
|
nsCOMPtr<nsISupports> ppmm =
|
|
do_GetService("@mozilla.org/parentprocessmessagemanager;1");
|
|
sParentProcessManager->GetInitialProcessData(aCx, &init, aError);
|
|
if (aError.Failed()) {
|
|
return;
|
|
}
|
|
mInitialProcessData = init;
|
|
}
|
|
|
|
if (!JS_WrapValue(aCx, &init)) {
|
|
aError.NoteJSContextException(aCx);
|
|
return;
|
|
}
|
|
aInitialProcessData.set(init);
|
|
}
|
|
|
|
WritableSharedMap* nsFrameMessageManager::SharedData() {
|
|
if (!mChrome || !mIsProcessManager) {
|
|
MOZ_ASSERT(false, "Should only call this binding method on ppmm");
|
|
return nullptr;
|
|
}
|
|
if (!mSharedData) {
|
|
mSharedData = new WritableSharedMap();
|
|
}
|
|
return mSharedData;
|
|
}
|
|
|
|
already_AddRefed<ProcessMessageManager>
|
|
nsFrameMessageManager::GetProcessMessageManager(ErrorResult& aError) {
|
|
RefPtr<ProcessMessageManager> pmm;
|
|
if (mCallback) {
|
|
pmm = mCallback->GetProcessMessageManager();
|
|
}
|
|
return pmm.forget();
|
|
}
|
|
|
|
void nsFrameMessageManager::GetRemoteType(nsAString& aRemoteType,
|
|
ErrorResult& aError) const {
|
|
aRemoteType.Truncate();
|
|
if (mCallback) {
|
|
mCallback->DoGetRemoteType(aRemoteType, aError);
|
|
}
|
|
}
|
|
|
|
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() = default;
|
|
|
|
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 = aMessageManager->mChildManagers[i];
|
|
CountReferents(mm, aReferentCount);
|
|
}
|
|
}
|
|
|
|
static void ReportReferentCount(
|
|
const char* aManagerType, const MessageManagerReferentCount& aReferentCount,
|
|
nsIHandleReportCallback* aHandleReport, nsISupports* aData) {
|
|
#define REPORT(_path, _amount, _desc) \
|
|
do { \
|
|
aHandleReport->Callback( \
|
|
EmptyCString(), _path, nsIMemoryReporter::KIND_OTHER, \
|
|
nsIMemoryReporter::UNITS_COUNT, _amount, _desc, aData); \
|
|
} 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
|
|
}
|
|
|
|
static StaticRefPtr<ChromeMessageBroadcaster> sGlobalMessageManager;
|
|
|
|
NS_IMETHODIMP
|
|
MessageManagerReporter::CollectReports(nsIHandleReportCallback* aHandleReport,
|
|
nsISupports* aData, bool aAnonymize) {
|
|
if (XRE_IsParentProcess() && sGlobalMessageManager) {
|
|
MessageManagerReferentCount count;
|
|
CountReferents(sGlobalMessageManager, &count);
|
|
ReportReferentCount("global-manager", count, aHandleReport, aData);
|
|
}
|
|
|
|
if (nsFrameMessageManager::sParentProcessManager) {
|
|
MessageManagerReferentCount count;
|
|
CountReferents(nsFrameMessageManager::sParentProcessManager, &count);
|
|
ReportReferentCount("parent-process-manager", count, aHandleReport, aData);
|
|
}
|
|
|
|
if (nsFrameMessageManager::sChildProcessManager) {
|
|
MessageManagerReferentCount count;
|
|
CountReferents(nsFrameMessageManager::sChildProcessManager, &count);
|
|
ReportReferentCount("child-process-manager", count, aHandleReport, aData);
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
} // namespace dom
|
|
} // namespace mozilla
|
|
|
|
already_AddRefed<ChromeMessageBroadcaster>
|
|
nsFrameMessageManager::GetGlobalMessageManager() {
|
|
RefPtr<ChromeMessageBroadcaster> mm;
|
|
if (sGlobalMessageManager) {
|
|
mm = sGlobalMessageManager;
|
|
} else {
|
|
sGlobalMessageManager = mm =
|
|
new ChromeMessageBroadcaster(MessageManagerFlags::MM_GLOBAL);
|
|
ClearOnShutdown(&sGlobalMessageManager);
|
|
RegisterStrongMemoryReporter(new MessageManagerReporter());
|
|
}
|
|
return mm.forget();
|
|
}
|
|
|
|
nsresult NS_NewGlobalMessageManager(nsISupports** aResult) {
|
|
*aResult = nsFrameMessageManager::GetGlobalMessageManager().take();
|
|
return NS_OK;
|
|
}
|
|
|
|
nsDataHashtable<nsStringHashKey, nsMessageManagerScriptHolder*>*
|
|
nsMessageManagerScriptExecutor::sCachedScripts = nullptr;
|
|
StaticRefPtr<nsScriptCacheCleaner>
|
|
nsMessageManagerScriptExecutor::sScriptCacheCleaner;
|
|
|
|
void nsMessageManagerScriptExecutor::DidCreateScriptLoader() {
|
|
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(
|
|
JS::Handle<JSObject*> aMessageManager, const nsAString& aURL,
|
|
bool aRunInUniqueScope) {
|
|
AUTO_PROFILER_LABEL_DYNAMIC_LOSSY_NSSTRING(
|
|
"nsMessageManagerScriptExecutor::LoadScriptInternal", OTHER, aURL);
|
|
|
|
if (!sCachedScripts) {
|
|
return;
|
|
}
|
|
|
|
JS::RootingContext* rcx = RootingCx();
|
|
JS::Rooted<JSScript*> script(rcx);
|
|
|
|
nsMessageManagerScriptHolder* holder = sCachedScripts->Get(aURL);
|
|
if (holder) {
|
|
script = holder->mScript;
|
|
} else {
|
|
TryCacheLoadAndCompileScript(aURL, aRunInUniqueScope, true, aMessageManager,
|
|
&script);
|
|
}
|
|
|
|
AutoEntryScript aes(aMessageManager, "message manager script load");
|
|
JSContext* cx = aes.cx();
|
|
if (script) {
|
|
if (aRunInUniqueScope) {
|
|
JS::Rooted<JSObject*> scope(cx);
|
|
bool ok = js::ExecuteInFrameScriptEnvironment(cx, aMessageManager, script,
|
|
&scope);
|
|
if (ok) {
|
|
// Force the scope to stay alive.
|
|
mAnonymousGlobalScopes.AppendElement(scope);
|
|
}
|
|
} else {
|
|
JS::RootedValue rval(cx);
|
|
JS::RootedVector<JSObject*> envChain(cx);
|
|
if (!envChain.append(aMessageManager)) {
|
|
return;
|
|
}
|
|
JS::CloneAndExecuteScript(cx, envChain, script, &rval);
|
|
}
|
|
}
|
|
}
|
|
|
|
void nsMessageManagerScriptExecutor::TryCacheLoadAndCompileScript(
|
|
const nsAString& aURL, bool aRunInUniqueScope, bool aShouldCache,
|
|
JS::Handle<JSObject*> aMessageManager,
|
|
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;
|
|
}
|
|
|
|
// If this script won't be cached, or there is only one of this type of
|
|
// message manager per process, treat this script as run-once. Run-once
|
|
// scripts can be compiled directly for the target global, and will be dropped
|
|
// from the preloader cache after they're executed and serialized.
|
|
bool isRunOnce = !aShouldCache || IsProcessScoped();
|
|
|
|
// If the script will be reused in this session, compile it in the compilation
|
|
// scope instead of the current global to avoid keeping the current
|
|
// compartment alive.
|
|
AutoJSAPI jsapi;
|
|
if (!jsapi.Init(isRunOnce ? aMessageManager : xpc::CompilationScope())) {
|
|
return;
|
|
}
|
|
JSContext* cx = jsapi.cx();
|
|
JS::Rooted<JSScript*> script(cx);
|
|
|
|
script = ScriptPreloader::GetChildSingleton().GetCachedScript(cx, url);
|
|
|
|
if (!script) {
|
|
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->Open(getter_AddRefs(input));
|
|
NS_ENSURE_SUCCESS_VOID(rv);
|
|
nsString dataString;
|
|
char16_t* dataStringBuf = nullptr;
|
|
size_t dataStringLength = 0;
|
|
if (input) {
|
|
nsCString buffer;
|
|
uint64_t written;
|
|
if (NS_FAILED(NS_ReadInputStreamToString(input, buffer, -1, &written))) {
|
|
return;
|
|
}
|
|
|
|
uint32_t size = (uint32_t)std::min(written, (uint64_t)UINT32_MAX);
|
|
ScriptLoader::ConvertToUTF16(channel, (uint8_t*)buffer.get(), size,
|
|
EmptyString(), nullptr, dataStringBuf,
|
|
dataStringLength);
|
|
}
|
|
|
|
if (!dataStringBuf || dataStringLength == 0) {
|
|
return;
|
|
}
|
|
|
|
JS::UniqueTwoByteChars srcChars(dataStringBuf);
|
|
|
|
JS::SourceText<char16_t> srcBuf;
|
|
if (!srcBuf.init(cx, std::move(srcChars), dataStringLength)) {
|
|
return;
|
|
}
|
|
|
|
JS::CompileOptions options(cx);
|
|
options.setFileAndLine(url.get(), 1);
|
|
options.setNoScriptRval(true);
|
|
|
|
script = JS::CompileForNonSyntacticScope(cx, options, srcBuf);
|
|
if (!script) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
MOZ_ASSERT(script);
|
|
aScriptp.set(script);
|
|
|
|
nsAutoCString scheme;
|
|
uri->GetScheme(scheme);
|
|
// We don't cache data: scripts!
|
|
if (aShouldCache && !scheme.EqualsLiteral("data")) {
|
|
ScriptPreloader::GetChildSingleton().NoteScript(url, url, script,
|
|
isRunOnce);
|
|
|
|
// If this script will only run once per process, only cache it in the
|
|
// preloader cache, not the session cache.
|
|
if (!isRunOnce) {
|
|
// Root the object also for caching.
|
|
auto* holder = new nsMessageManagerScriptHolder(cx, script);
|
|
sCachedScripts->Put(aURL, holder);
|
|
}
|
|
}
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
void nsMessageManagerScriptExecutor::Unlink() {
|
|
ImplCycleCollectionUnlink(mAnonymousGlobalScopes);
|
|
}
|
|
|
|
bool nsMessageManagerScriptExecutor::Init() {
|
|
DidCreateScriptLoader();
|
|
return true;
|
|
}
|
|
|
|
void nsMessageManagerScriptExecutor::MarkScopesForCC() {
|
|
for (uint32_t i = 0; i < mAnonymousGlobalScopes.Length(); ++i) {
|
|
mAnonymousGlobalScopes[i].exposeToActiveJS();
|
|
}
|
|
}
|
|
|
|
NS_IMPL_ISUPPORTS(nsScriptCacheCleaner, nsIObserver)
|
|
|
|
ChildProcessMessageManager* nsFrameMessageManager::sChildProcessManager =
|
|
nullptr;
|
|
ParentProcessMessageManager* 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),
|
|
mozilla::Runnable("nsAsyncMessageToSameProcessChild") {}
|
|
NS_IMETHOD Run() override {
|
|
nsFrameMessageManager* ppm =
|
|
nsFrameMessageManager::GetChildProcessManager();
|
|
ReceiveMessage(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);
|
|
}
|
|
~SameParentProcessMessageManagerCallback() override {
|
|
MOZ_COUNT_DTOR(SameParentProcessMessageManagerCallback);
|
|
}
|
|
|
|
bool DoLoadMessageManagerScript(const nsAString& aURL,
|
|
bool aRunInGlobalScope) override {
|
|
auto* global = ContentProcessMessageManager::Get();
|
|
MOZ_ASSERT(!aRunInGlobalScope);
|
|
global->LoadScript(aURL);
|
|
return true;
|
|
}
|
|
|
|
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;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Send messages to the parent process.
|
|
*/
|
|
class ChildProcessMessageManagerCallback : public MessageManagerCallback {
|
|
public:
|
|
ChildProcessMessageManagerCallback() {
|
|
MOZ_COUNT_CTOR(ChildProcessMessageManagerCallback);
|
|
}
|
|
~ChildProcessMessageManagerCallback() override {
|
|
MOZ_COUNT_DTOR(ChildProcessMessageManagerCallback);
|
|
}
|
|
|
|
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;
|
|
}
|
|
nsTArray<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);
|
|
}
|
|
|
|
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;
|
|
}
|
|
nsTArray<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) {}
|
|
nsresult HandleMessage() override {
|
|
nsFrameMessageManager* ppm =
|
|
nsFrameMessageManager::sSameProcessParentManager;
|
|
ReceiveMessage(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);
|
|
}
|
|
~SameChildProcessMessageManagerCallback() override {
|
|
MOZ_COUNT_DTOR(SameChildProcessMessageManagerCallback);
|
|
}
|
|
|
|
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(ppm, nullptr, aMessage, true, &aData, &cpows,
|
|
aPrincipal, aRetVal, IgnoreErrors());
|
|
}
|
|
return true;
|
|
}
|
|
|
|
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(nsISupports** aResult) {
|
|
NS_ASSERTION(!nsFrameMessageManager::sParentProcessManager,
|
|
"Re-creating sParentProcessManager");
|
|
RefPtr<ParentProcessMessageManager> mm = new ParentProcessMessageManager();
|
|
nsFrameMessageManager::sParentProcessManager = mm;
|
|
nsFrameMessageManager::NewProcessMessageManager(
|
|
false); // Create same process message manager.
|
|
mm.forget(aResult);
|
|
return NS_OK;
|
|
}
|
|
|
|
ProcessMessageManager* nsFrameMessageManager::NewProcessMessageManager(
|
|
bool aIsRemote) {
|
|
if (!nsFrameMessageManager::sParentProcessManager) {
|
|
nsCOMPtr<nsISupports> dummy =
|
|
do_GetService("@mozilla.org/parentprocessmessagemanager;1");
|
|
}
|
|
|
|
MOZ_ASSERT(nsFrameMessageManager::sParentProcessManager,
|
|
"parent process manager not created");
|
|
ProcessMessageManager* mm;
|
|
if (aIsRemote) {
|
|
// Callback is set in ContentParent::InitInternal so that the process has
|
|
// already started when we send pending scripts.
|
|
mm = new ProcessMessageManager(
|
|
nullptr, nsFrameMessageManager::sParentProcessManager);
|
|
} else {
|
|
mm =
|
|
new ProcessMessageManager(new SameParentProcessMessageManagerCallback(),
|
|
nsFrameMessageManager::sParentProcessManager,
|
|
MessageManagerFlags::MM_OWNSCALLBACK);
|
|
mm->SetOsPid(base::GetCurrentProcId());
|
|
sSameProcessParentManager = mm;
|
|
}
|
|
return mm;
|
|
}
|
|
|
|
nsresult NS_NewChildProcessMessageManager(nsISupports** aResult) {
|
|
NS_ASSERTION(!nsFrameMessageManager::GetChildProcessManager(),
|
|
"Re-creating sChildProcessManager");
|
|
|
|
MessageManagerCallback* cb;
|
|
if (XRE_IsParentProcess()) {
|
|
cb = new SameChildProcessMessageManagerCallback();
|
|
} else {
|
|
cb = new ChildProcessMessageManagerCallback();
|
|
RegisterStrongMemoryReporter(new MessageManagerReporter());
|
|
}
|
|
auto* mm = new ChildProcessMessageManager(cb);
|
|
nsFrameMessageManager::SetChildProcessManager(mm);
|
|
auto global = MakeRefPtr<ContentProcessMessageManager>(mm);
|
|
NS_ENSURE_TRUE(global->Init(), NS_ERROR_UNEXPECTED);
|
|
return CallQueryInterface(global, aResult);
|
|
}
|
|
|
|
void 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++) {
|
|
MessageListener* strongListener = listeners->ElementAt(i).mStrongListener;
|
|
if (strongListener) {
|
|
strongListener->MarkForCC();
|
|
}
|
|
}
|
|
}
|
|
|
|
if (mRefCnt.IsPurple()) {
|
|
mRefCnt.RemovePurple();
|
|
}
|
|
}
|
|
|
|
nsSameProcessAsyncMessageBase::nsSameProcessAsyncMessageBase(
|
|
JS::RootingContext* aRootingCx, JS::Handle<JSObject*> aCpows)
|
|
: 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, nsFrameLoader* aTargetFrameLoader,
|
|
nsFrameMessageManager* aManager) {
|
|
// Make sure that we have called Init() and it has succeeded.
|
|
MOZ_ASSERT(mCalledInit);
|
|
if (aManager) {
|
|
SameProcessCpowHolder cpows(RootingCx(), mCpows);
|
|
|
|
RefPtr<nsFrameMessageManager> mm = aManager;
|
|
mm->ReceiveMessage(aTarget, aTargetFrameLoader, mMessage, false, &mData,
|
|
&cpows, mPrincipal, nullptr, IgnoreErrors());
|
|
}
|
|
}
|