/* -*- 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/. */
/* 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)
#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 (constauto& entry : mListeners) {
nsAutoTObserverArray<nsMessageListenerInfo, 1>* listeners = entry.GetWeak();
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* const listeners = mListeners.GetOrInsertNew(aMessageName);
uint32_t len = listeners->Length(); for (uint32_t i = 0; i < len; ++i) { if (listeners->ElementAt(i).mWeakListener == weak) { 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;
}
nsCOMPtr<nsIConsoleService> console(
do_GetService(NS_CONSOLESERVICE_CONTRACTID)); if (console) { auto location = JSCallingLocation::Get(aCx);
nsCOMPtr<nsIScriptError> error(
do_CreateInstance(NS_SCRIPTERROR_CONTRACTID));
error->Init(
u"Sending message that cannot be cloned. Are " "you trying to send an XPCOM object?"_ns,
location.FileName(), location.mLine, location.mColumn,
nsIScriptError::warningFlag, "chrome javascript"_ns, false/* from private window */, true /* from chrome context */);
console->LogMessage(error);
}
// Not clonable, try JSON // Bug 1749037 - 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; if (!nsContentUtils::StringifyJSON(aCx, v, json,
UndefinedIsNullStringLiteral)) {
NS_WARNING("nsContentUtils::StringifyJSON() failed");
JS_ClearPendingException(aCx); returnfalse;
}
NS_ENSURE_TRUE(!json.IsEmpty(), false);
aData.Write(aCx, val, rv); if (NS_WARN_IF(rv.Failed())) {
rv.SuppressException(); returnfalse;
}
returntrue;
}
staticbool sSendingSyncMessage = false;
void nsFrameMessageManager::SendSyncMessage(JSContext* aCx, const nsAString& aMessageName,
JS::Handle<JS::Value> aObj,
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!");
// 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);
// 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));
}
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 +
u": message reply cannot be cloned. Are " "you trying to send an XPCOM object?"_ns;
nsCOMPtr<nsIConsoleService> console(
do_GetService(NS_CONSOLESERVICE_CONTRACTID)); if (console) {
nsCOMPtr<nsIScriptError> error(
do_CreateInstance(NS_SCRIPTERROR_CONTRACTID));
error->Init(msg, ""_ns, 0, 0, nsIScriptError::warningFlag, "chrome javascript"_ns, false/* from private window */, true/* from chrome context */);
console->LogMessage(error);
}
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());
}
}
JS::Rooted<JS::Value> 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::Rooted<JSObject*> global(aCx, xpc::PrivilegedJunkScope());
JSAutoRealm ar(aCx, global);
JS::Rooted<JSObject*> obj(aCx, JS_NewPlainObject(aCx)); if (!obj) {
aError.NoteJSContextException(aCx); return;
}
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;
}
// 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);
}
}
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++) { const uint32_t totalReferentCount =
aReferentCount.mMessageCounter.Get(aReferentCount.mSuspectMessages[i]);
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));
}
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 nullptr;
}
// 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. // // NOTE: This does not affect the JS::CompileOptions. We generate the same // bytecode as though it were run multiple times. This is required for the // batch decoding from ScriptPreloader to work. bool isRunOnce = 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 nullptr;
}
JSContext* cx = jsapi.cx();
// If we are not encoding to the ScriptPreloader cache, we can now relax the // compile options and use the JS syntax-parser for lower latency. if (!useScriptPreloader || !ScriptPreloader::GetChildSingleton().Active()) {
options.setSourceIsLazy(false);
}
JS::SourceText<Utf8Unit> srcBuf; if (!srcBuf.init(cx, std::move(dataStringBuf), dataStringLength)) { return nullptr;
}
if (isCacheable && !isRunOnce) { // Store into our cache only when we compile it here. auto* holder = new nsMessageManagerScriptHolder(stencil);
sCachedScripts->InsertOrUpdate(aURL, holder);
}
#ifdef DEBUG // The above shouldn't touch any options for instantiation.
JS::InstantiateOptions instantiateOptions(options);
instantiateOptions.assertDefault(); #endif
}
class nsAsyncMessageToSameProcessChild : public nsSameProcessAsyncMessageBase, public Runnable { public:
nsAsyncMessageToSameProcessChild()
: 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); return global && global->LoadScript(aURL);
}
nsresult DoSendAsyncMessage(const nsAString& aMessage,
StructuredCloneData& aData) override {
RefPtr<nsAsyncMessageToSameProcessChild> ev = new nsAsyncMessageToSameProcessChild();
nsresult DoSendAsyncMessage(const nsAString& aMessage,
StructuredCloneData& aData) override {
mozilla::dom::ContentChild* cc = mozilla::dom::ContentChild::GetSingleton(); if (!cc) { return NS_OK;
}
ClonedMessageData data; if (!BuildClonedMessageData(aData, data)) { return NS_ERROR_DOM_DATA_CLONE_ERR;
} if (!cc->SendAsyncMessage(PromiseFlatString(aMessage), data)) { return NS_ERROR_UNEXPECTED;
}
return NS_OK;
}
};
class nsAsyncMessageToSameProcessParent
: public nsSameProcessAsyncMessageBase, public SameProcessMessageQueue::Runnable { public:
nsAsyncMessageToSameProcessParent() = default;
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);
}
// 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;
}
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;
}
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 (constauto& entry : mListeners) {
nsAutoTObserverArray<nsMessageListenerInfo, 1>* listeners = entry.GetWeak();
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();
}
}
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) {
RefPtr<nsFrameMessageManager> mm = aManager;
mm->ReceiveMessage(aTarget, aTargetFrameLoader, mMessage, false, &mData,
nullptr, IgnoreErrors());
}
}
¤ Dauer der Verarbeitung: 0.13 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.