1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "nsFrameMessageManager.h"
15 #include "ContentChild.h"
16 #include "ErrorList.h"
17 #include "mozilla/ProfilerLabels.h"
18 #include "mozilla/Unused.h"
19 #include "base/process_util.h"
20 #include "chrome/common/ipc_channel.h"
21 #include "js/CallAndConstruct.h" // JS::IsCallable, JS_CallFunctionValue
22 #include "js/CompilationAndEvaluation.h"
23 #include "js/CompileOptions.h"
24 #include "js/experimental/JSStencil.h"
25 #include "js/GCVector.h"
27 #include "js/PropertyAndElement.h" // JS_GetProperty
28 #include "js/RootingAPI.h"
29 #include "js/SourceText.h"
30 #include "js/StructuredClone.h"
31 #include "js/TypeDecls.h"
32 #include "js/Utility.h" // JS::FreePolicy
33 #include "js/Wrapper.h"
35 #include "jsfriendapi.h"
36 #include "mozilla/AlreadyAddRefed.h"
37 #include "mozilla/Assertions.h"
38 #include "mozilla/ClearOnShutdown.h"
39 #include "mozilla/ErrorResult.h"
40 #include "mozilla/MacroForEach.h"
41 #include "mozilla/NotNull.h"
42 #include "mozilla/OwningNonNull.h"
43 #include "mozilla/RefPtr.h"
44 #include "mozilla/ScriptPreloader.h"
45 #include "mozilla/Services.h"
46 #include "mozilla/StaticPtr.h"
47 #include "mozilla/Telemetry.h"
48 #include "mozilla/TelemetryHistogramEnums.h"
49 #include "mozilla/TimeStamp.h"
50 #include "mozilla/TypedEnumBits.h"
51 #include "mozilla/UniquePtr.h"
52 #include "mozilla/dom/AutoEntryScript.h"
53 #include "mozilla/dom/BindingDeclarations.h"
54 #include "mozilla/dom/CallbackObject.h"
55 #include "mozilla/dom/ChildProcessMessageManager.h"
56 #include "mozilla/dom/ChromeMessageBroadcaster.h"
57 #include "mozilla/dom/ContentProcessMessageManager.h"
58 #include "mozilla/dom/DOMTypes.h"
59 #include "mozilla/dom/MessageBroadcaster.h"
60 #include "mozilla/dom/MessageListenerManager.h"
61 #include "mozilla/dom/MessageManagerBinding.h"
62 #include "mozilla/dom/MessagePort.h"
63 #include "mozilla/dom/ParentProcessMessageManager.h"
64 #include "mozilla/dom/ProcessMessageManager.h"
65 #include "mozilla/dom/RootedDictionary.h"
66 #include "mozilla/dom/SameProcessMessageQueue.h"
67 #include "mozilla/dom/ScriptLoader.h"
68 #include "mozilla/dom/ScriptSettings.h"
69 #include "mozilla/dom/ToJSValue.h"
70 #include "mozilla/dom/MessageManagerCallback.h"
71 #include "mozilla/dom/ipc/SharedMap.h"
72 #include "mozilla/dom/ipc/StructuredCloneData.h"
73 #include "mozilla/scache/StartupCacheUtils.h"
74 #include "nsASCIIMask.h"
75 #include "nsBaseHashtable.h"
77 #include "nsClassHashtable.h"
78 #include "nsComponentManagerUtils.h"
79 #include "nsContentUtils.h"
80 #include "nsCycleCollectionNoteChild.h"
81 #include "nsCycleCollectionParticipant.h"
82 #include "nsTHashMap.h"
85 #include "nsHashKeys.h"
86 #include "nsIChannel.h"
87 #include "nsIConsoleService.h"
88 #include "nsIContentPolicy.h"
89 #include "nsIInputStream.h"
90 #include "nsILoadInfo.h"
91 #include "nsIMemoryReporter.h"
92 #include "nsIMessageManager.h"
93 #include "nsIObserver.h"
94 #include "nsIObserverService.h"
95 #include "nsIProtocolHandler.h"
96 #include "nsIScriptError.h"
97 #include "nsISupports.h"
98 #include "nsISupportsUtils.h"
100 #include "nsIWeakReferenceUtils.h"
101 #include "nsIXPConnect.h"
102 #include "nsJSUtils.h"
103 #include "nsLiteralString.h"
104 #include "nsNetUtil.h"
105 #include "nsPrintfCString.h"
106 #include "nsQueryObject.h"
107 #include "nsServiceManagerUtils.h"
108 #include "nsString.h"
109 #include "nsStringFlags.h"
110 #include "nsStringFwd.h"
111 #include "nsTArray.h"
112 #include "nsTLiteralString.h"
113 #include "nsTObserverArray.h"
114 #include "nsTPromiseFlatString.h"
115 #include "nsTStringRepr.h"
116 #include "nsThreadUtils.h"
117 #include "nsXULAppAPI.h"
119 #include "xpcpublic.h"
122 # if defined(SendMessage)
128 # include "MessageManagerFuzzer.h"
131 using namespace mozilla
;
132 using namespace mozilla::dom
;
133 using namespace mozilla::dom::ipc
;
135 struct FrameMessageMarker
{
136 static constexpr Span
<const char> MarkerTypeName() {
137 return MakeStringSpan("FrameMessage");
139 static void StreamJSONMarkerData(baseprofiler::SpliceableJSONWriter
& aWriter
,
140 const ProfilerString16View
& aMessageName
,
142 aWriter
.UniqueStringProperty("name", NS_ConvertUTF16toUTF8(aMessageName
));
143 aWriter
.BoolProperty("sync", aIsSync
);
145 static MarkerSchema
MarkerTypeDisplay() {
146 using MS
= MarkerSchema
;
147 MS schema
{MS::Location::MarkerChart
, MS::Location::MarkerTable
};
148 schema
.AddKeyLabelFormatSearchable("name", "Message Name",
149 MS::Format::UniqueString
,
150 MS::Searchable::Searchable
);
151 schema
.AddKeyLabelFormat("sync", "Sync", MS::Format::String
);
152 schema
.SetTooltipLabel("FrameMessage - {marker.name}");
153 schema
.SetTableLabel("{marker.name} - {marker.data.name}");
158 #define CACHE_PREFIX(type) "mm/" type
160 nsFrameMessageManager::nsFrameMessageManager(MessageManagerCallback
* aCallback
,
161 MessageManagerFlags aFlags
)
162 : mChrome(aFlags
& MessageManagerFlags::MM_CHROME
),
163 mGlobal(aFlags
& MessageManagerFlags::MM_GLOBAL
),
164 mIsProcessManager(aFlags
& MessageManagerFlags::MM_PROCESSMANAGER
),
165 mIsBroadcaster(aFlags
& MessageManagerFlags::MM_BROADCASTER
),
166 mOwnsCallback(aFlags
& MessageManagerFlags::MM_OWNSCALLBACK
),
167 mHandlingMessage(false),
169 mDisconnected(false),
170 mCallback(aCallback
) {
171 NS_ASSERTION(!mIsBroadcaster
|| !mCallback
,
172 "Broadcasters cannot have callbacks!");
174 mOwnedCallback
= WrapUnique(aCallback
);
178 nsFrameMessageManager::~nsFrameMessageManager() {
179 for (int32_t i
= mChildManagers
.Length(); i
> 0; --i
) {
180 mChildManagers
[i
- 1]->Disconnect(false);
182 if (mIsProcessManager
) {
183 if (this == sParentProcessManager
) {
184 sParentProcessManager
= nullptr;
186 if (this == sChildProcessManager
) {
187 sChildProcessManager
= nullptr;
188 delete mozilla::dom::SameProcessMessageQueue::Get();
190 if (this == sSameProcessParentManager
) {
191 sSameProcessParentManager
= nullptr;
196 inline void ImplCycleCollectionTraverse(
197 nsCycleCollectionTraversalCallback
& aCallback
,
198 nsMessageListenerInfo
& aField
, const char* aName
, uint32_t aFlags
= 0) {
199 ImplCycleCollectionTraverse(aCallback
, aField
.mStrongListener
, aName
, aFlags
);
200 ImplCycleCollectionTraverse(aCallback
, aField
.mWeakListener
, aName
, aFlags
);
203 NS_IMPL_CYCLE_COLLECTION_CLASS(nsFrameMessageManager
)
205 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsFrameMessageManager
)
206 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mListeners
)
207 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChildManagers
)
208 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSharedData
)
209 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
211 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsFrameMessageManager
)
212 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mInitialProcessData
)
213 NS_IMPL_CYCLE_COLLECTION_TRACE_END
215 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsFrameMessageManager
)
216 NS_IMPL_CYCLE_COLLECTION_UNLINK(mListeners
)
217 for (int32_t i
= tmp
->mChildManagers
.Length(); i
> 0; --i
) {
218 tmp
->mChildManagers
[i
- 1]->Disconnect(false);
220 NS_IMPL_CYCLE_COLLECTION_UNLINK(mChildManagers
)
221 NS_IMPL_CYCLE_COLLECTION_UNLINK(mSharedData
)
222 tmp
->mInitialProcessData
.setNull();
223 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
225 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsFrameMessageManager
)
226 NS_INTERFACE_MAP_ENTRY(nsISupports
)
228 /* Message managers in child process implement nsIMessageSender.
229 Message managers in the chrome process are
230 either broadcasters (if they have subordinate/child message
231 managers) or they're simple message senders. */
232 NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIMessageSender
,
233 !mChrome
|| !mIsBroadcaster
)
237 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsFrameMessageManager
)
238 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsFrameMessageManager
)
240 void MessageManagerCallback::DoGetRemoteType(nsACString
& aRemoteType
,
241 ErrorResult
& aError
) const {
242 aRemoteType
.Truncate();
243 mozilla::dom::ProcessMessageManager
* parent
= GetProcessMessageManager();
248 parent
->GetRemoteType(aRemoteType
, aError
);
251 bool MessageManagerCallback::BuildClonedMessageData(
252 StructuredCloneData
& aData
, ClonedMessageData
& aClonedData
) {
253 return aData
.BuildClonedMessageData(aClonedData
);
256 void mozilla::dom::ipc::UnpackClonedMessageData(
257 const ClonedMessageData
& aClonedData
, StructuredCloneData
& aData
) {
258 aData
.BorrowFromClonedMessageData(aClonedData
);
261 void nsFrameMessageManager::AddMessageListener(const nsAString
& aMessageName
,
262 MessageListener
& aListener
,
263 bool aListenWhenClosed
,
264 ErrorResult
& aError
) {
265 auto* const listeners
= mListeners
.GetOrInsertNew(aMessageName
);
266 uint32_t len
= listeners
->Length();
267 for (uint32_t i
= 0; i
< len
; ++i
) {
268 MessageListener
* strongListener
= listeners
->ElementAt(i
).mStrongListener
;
269 if (strongListener
&& *strongListener
== aListener
) {
274 nsMessageListenerInfo
* entry
= listeners
->AppendElement();
275 entry
->mStrongListener
= &aListener
;
276 entry
->mListenWhenClosed
= aListenWhenClosed
;
279 void nsFrameMessageManager::RemoveMessageListener(const nsAString
& aMessageName
,
280 MessageListener
& aListener
,
281 ErrorResult
& aError
) {
282 nsAutoTObserverArray
<nsMessageListenerInfo
, 1>* listeners
=
283 mListeners
.Get(aMessageName
);
285 uint32_t len
= listeners
->Length();
286 for (uint32_t i
= 0; i
< len
; ++i
) {
287 MessageListener
* strongListener
= listeners
->ElementAt(i
).mStrongListener
;
288 if (strongListener
&& *strongListener
== aListener
) {
289 listeners
->RemoveElementAt(i
);
296 static already_AddRefed
<nsISupports
> ToXPCOMMessageListener(
297 MessageListener
& aListener
) {
298 return CallbackObjectHolder
<mozilla::dom::MessageListener
, nsISupports
>(
303 void nsFrameMessageManager::AddWeakMessageListener(
304 const nsAString
& aMessageName
, MessageListener
& aListener
,
305 ErrorResult
& aError
) {
306 nsCOMPtr
<nsISupports
> listener(ToXPCOMMessageListener(aListener
));
307 nsWeakPtr weak
= do_GetWeakReference(listener
);
309 aError
.Throw(NS_ERROR_NO_INTERFACE
);
314 // It's technically possible that one object X could give two different
315 // nsIWeakReference*'s when you do_GetWeakReference(X). We really don't want
316 // this to happen; it will break e.g. RemoveWeakMessageListener. So let's
317 // check that we're not getting ourselves into that situation.
318 nsCOMPtr
<nsISupports
> canonical
= do_QueryInterface(listener
);
319 for (const auto& entry
: mListeners
) {
320 nsAutoTObserverArray
<nsMessageListenerInfo
, 1>* listeners
= entry
.GetWeak();
321 uint32_t count
= listeners
->Length();
322 for (uint32_t i
= 0; i
< count
; i
++) {
323 nsWeakPtr weakListener
= listeners
->ElementAt(i
).mWeakListener
;
325 nsCOMPtr
<nsISupports
> otherCanonical
= do_QueryReferent(weakListener
);
326 MOZ_ASSERT((canonical
== otherCanonical
) == (weak
== weakListener
));
332 auto* const listeners
= mListeners
.GetOrInsertNew(aMessageName
);
333 uint32_t len
= listeners
->Length();
334 for (uint32_t i
= 0; i
< len
; ++i
) {
335 if (listeners
->ElementAt(i
).mWeakListener
== weak
) {
340 nsMessageListenerInfo
* entry
= listeners
->AppendElement();
341 entry
->mWeakListener
= weak
;
342 entry
->mListenWhenClosed
= false;
345 void nsFrameMessageManager::RemoveWeakMessageListener(
346 const nsAString
& aMessageName
, MessageListener
& aListener
,
347 ErrorResult
& aError
) {
348 nsCOMPtr
<nsISupports
> listener(ToXPCOMMessageListener(aListener
));
349 nsWeakPtr weak
= do_GetWeakReference(listener
);
351 aError
.Throw(NS_ERROR_NO_INTERFACE
);
355 nsAutoTObserverArray
<nsMessageListenerInfo
, 1>* listeners
=
356 mListeners
.Get(aMessageName
);
361 uint32_t len
= listeners
->Length();
362 for (uint32_t i
= 0; i
< len
; ++i
) {
363 if (listeners
->ElementAt(i
).mWeakListener
== weak
) {
364 listeners
->RemoveElementAt(i
);
370 void nsFrameMessageManager::LoadScript(const nsAString
& aURL
,
371 bool aAllowDelayedLoad
,
372 bool aRunInGlobalScope
,
373 ErrorResult
& aError
) {
374 if (aAllowDelayedLoad
) {
375 // Cache for future windows or frames
376 mPendingScripts
.AppendElement(aURL
);
377 mPendingScriptsGlobalStates
.AppendElement(aRunInGlobalScope
);
382 printf("Will load %s \n", NS_ConvertUTF16toUTF8(aURL
).get());
384 if (!mCallback
->DoLoadMessageManagerScript(aURL
, aRunInGlobalScope
)) {
385 aError
.Throw(NS_ERROR_FAILURE
);
390 for (uint32_t i
= 0; i
< mChildManagers
.Length(); ++i
) {
391 RefPtr
<nsFrameMessageManager
> mm
= mChildManagers
[i
];
393 // Use false here, so that child managers don't cache the script, which
394 // is already cached in the parent.
395 mm
->LoadScript(aURL
, false, aRunInGlobalScope
, IgnoreErrors());
400 void nsFrameMessageManager::RemoveDelayedScript(const nsAString
& aURL
) {
401 for (uint32_t i
= 0; i
< mPendingScripts
.Length(); ++i
) {
402 if (mPendingScripts
[i
] == aURL
) {
403 mPendingScripts
.RemoveElementAt(i
);
404 mPendingScriptsGlobalStates
.RemoveElementAt(i
);
410 void nsFrameMessageManager::GetDelayedScripts(
411 JSContext
* aCx
, nsTArray
<nsTArray
<JS::Value
>>& aList
, ErrorResult
& aError
) {
412 // Frame message managers may return an incomplete list because scripts
413 // that were loaded after it was connected are not added to the list.
414 if (!IsGlobal() && !IsBroadcaster()) {
416 "Cannot retrieve list of pending frame scripts for frame"
417 "message managers as it may be incomplete");
418 aError
.Throw(NS_ERROR_NOT_IMPLEMENTED
);
422 aError
.MightThrowJSException();
424 aList
.SetCapacity(mPendingScripts
.Length());
425 for (uint32_t i
= 0; i
< mPendingScripts
.Length(); ++i
) {
426 JS::Rooted
<JS::Value
> url(aCx
);
427 if (!ToJSValue(aCx
, mPendingScripts
[i
], &url
)) {
428 aError
.NoteJSContextException(aCx
);
432 nsTArray
<JS::Value
>* array
= aList
.AppendElement(2);
433 array
->AppendElement(url
);
434 array
->AppendElement(JS::BooleanValue(mPendingScriptsGlobalStates
[i
]));
439 bool nsFrameMessageManager::GetParamsForMessage(JSContext
* aCx
,
440 const JS::Value
& aValue
,
441 const JS::Value
& aTransfer
,
442 StructuredCloneData
& aData
) {
443 // First try to use structured clone on the whole thing.
444 JS::Rooted
<JS::Value
> v(aCx
, aValue
);
445 JS::Rooted
<JS::Value
> t(aCx
, aTransfer
);
447 aData
.Write(aCx
, v
, t
, JS::CloneDataPolicy(), rv
);
452 rv
.SuppressException();
453 JS_ClearPendingException(aCx
);
455 nsCOMPtr
<nsIConsoleService
> console(
456 do_GetService(NS_CONSOLESERVICE_CONTRACTID
));
458 nsAutoString filename
;
459 uint32_t lineno
= 0, column
= 0;
460 nsJSUtils::GetCallingLocation(aCx
, filename
, &lineno
, &column
);
461 nsCOMPtr
<nsIScriptError
> error(
462 do_CreateInstance(NS_SCRIPTERROR_CONTRACTID
));
464 u
"Sending message that cannot be cloned. Are "
465 "you trying to send an XPCOM object?"_ns
,
466 filename
, u
""_ns
, lineno
, column
, nsIScriptError::warningFlag
,
467 "chrome javascript"_ns
, false /* from private window */,
468 true /* from chrome context */);
469 console
->LogMessage(error
);
472 // Not clonable, try JSON
473 // Bug 1749037 - This is ugly but currently structured cloning doesn't handle
474 // properly cases when interface is implemented in JS and used
478 nsContentUtils::StringifyJSON(aCx
, v
, json
, UndefinedIsNullStringLiteral
),
480 NS_ENSURE_TRUE(!json
.IsEmpty(), false);
482 JS::Rooted
<JS::Value
> val(aCx
, JS::NullValue());
483 NS_ENSURE_TRUE(JS_ParseJSON(aCx
, static_cast<const char16_t
*>(json
.get()),
484 json
.Length(), &val
),
487 aData
.Write(aCx
, val
, rv
);
488 if (NS_WARN_IF(rv
.Failed())) {
489 rv
.SuppressException();
496 static bool sSendingSyncMessage
= false;
498 void nsFrameMessageManager::SendSyncMessage(JSContext
* aCx
,
499 const nsAString
& aMessageName
,
500 JS::Handle
<JS::Value
> aObj
,
501 nsTArray
<JS::Value
>& aResult
,
502 ErrorResult
& aError
) {
503 NS_ASSERTION(!IsGlobal(), "Should not call SendSyncMessage in chrome");
504 NS_ASSERTION(!IsBroadcaster(), "Should not call SendSyncMessage in chrome");
505 NS_ASSERTION(!GetParentManager(),
506 "Should not have parent manager in content!");
508 AUTO_PROFILER_LABEL_DYNAMIC_LOSSY_NSSTRING(
509 "nsFrameMessageManager::SendMessage", OTHER
, aMessageName
);
510 profiler_add_marker("SendSyncMessage", geckoprofiler::category::IPC
, {},
511 FrameMessageMarker
{}, aMessageName
, true);
513 if (sSendingSyncMessage
) {
514 // No kind of blocking send should be issued on top of a sync message.
515 aError
.Throw(NS_ERROR_UNEXPECTED
);
519 StructuredCloneData data
;
520 if (!aObj
.isUndefined() &&
521 !GetParamsForMessage(aCx
, aObj
, JS::UndefinedHandleValue
, data
)) {
522 aError
.Throw(NS_ERROR_DOM_DATA_CLONE_ERR
);
527 if (data
.DataLength() > 0) {
528 MessageManagerFuzzer::TryMutate(aCx
, aMessageName
, &data
,
529 JS::UndefinedHandleValue
);
534 aError
.Throw(NS_ERROR_NOT_INITIALIZED
);
538 nsTArray
<StructuredCloneData
> retval
;
540 TimeStamp start
= TimeStamp::Now();
541 sSendingSyncMessage
= true;
542 bool ok
= mCallback
->DoSendBlockingMessage(aMessageName
, data
, &retval
);
543 sSendingSyncMessage
= false;
545 uint32_t latencyMs
= round((TimeStamp::Now() - start
).ToMilliseconds());
546 if (latencyMs
>= kMinTelemetrySyncMessageManagerLatencyMs
) {
547 NS_ConvertUTF16toUTF8
messageName(aMessageName
);
548 // NOTE: We need to strip digit characters from the message name in order to
549 // avoid a large number of buckets due to generated names from addons (such
550 // as "ublock:sb:{N}"). See bug 1348113 comment 10.
551 messageName
.StripTaggedASCII(ASCIIMask::Mask0to9());
552 Telemetry::Accumulate(Telemetry::IPC_SYNC_MESSAGE_MANAGER_LATENCY_MS
,
553 messageName
, latencyMs
);
560 uint32_t len
= retval
.Length();
561 aResult
.SetCapacity(len
);
562 for (uint32_t i
= 0; i
< len
; ++i
) {
563 JS::Rooted
<JS::Value
> ret(aCx
);
564 retval
[i
].Read(aCx
, &ret
, aError
);
565 if (aError
.Failed()) {
566 MOZ_ASSERT(false, "Unable to read structured clone in SendMessage");
569 aResult
.AppendElement(ret
);
573 nsresult
nsFrameMessageManager::DispatchAsyncMessageInternal(
574 JSContext
* aCx
, const nsAString
& aMessage
, StructuredCloneData
& aData
) {
575 if (mIsBroadcaster
) {
576 uint32_t len
= mChildManagers
.Length();
577 for (uint32_t i
= 0; i
< len
; ++i
) {
578 mChildManagers
[i
]->DispatchAsyncMessageInternal(aCx
, aMessage
, aData
);
584 return NS_ERROR_NOT_INITIALIZED
;
587 nsresult rv
= mCallback
->DoSendAsyncMessage(aMessage
, aData
);
594 void nsFrameMessageManager::DispatchAsyncMessage(
595 JSContext
* aCx
, const nsAString
& aMessageName
, JS::Handle
<JS::Value
> aObj
,
596 JS::Handle
<JS::Value
> aTransfers
, ErrorResult
& aError
) {
597 StructuredCloneData data
;
598 if (!aObj
.isUndefined() &&
599 !GetParamsForMessage(aCx
, aObj
, aTransfers
, data
)) {
600 aError
.Throw(NS_ERROR_DOM_DATA_CLONE_ERR
);
604 profiler_add_marker("SendAsyncMessage", geckoprofiler::category::IPC
, {},
605 FrameMessageMarker
{}, aMessageName
, false);
608 if (data
.DataLength()) {
609 MessageManagerFuzzer::TryMutate(aCx
, aMessageName
, &data
, aTransfers
);
613 aError
= DispatchAsyncMessageInternal(aCx
, aMessageName
, data
);
616 class MMListenerRemover
{
618 explicit MMListenerRemover(nsFrameMessageManager
* aMM
)
619 : mWasHandlingMessage(aMM
->mHandlingMessage
), mMM(aMM
) {
620 mMM
->mHandlingMessage
= true;
622 ~MMListenerRemover() {
623 if (!mWasHandlingMessage
) {
624 mMM
->mHandlingMessage
= false;
625 if (mMM
->mDisconnected
) {
626 mMM
->mListeners
.Clear();
631 bool mWasHandlingMessage
;
632 RefPtr
<nsFrameMessageManager
> mMM
;
635 void nsFrameMessageManager::ReceiveMessage(
636 nsISupports
* aTarget
, nsFrameLoader
* aTargetFrameLoader
, bool aTargetClosed
,
637 const nsAString
& aMessage
, bool aIsSync
, StructuredCloneData
* aCloneData
,
638 nsTArray
<StructuredCloneData
>* aRetVal
, ErrorResult
& aError
) {
640 profiler_add_marker("ReceiveMessage", geckoprofiler::category::IPC
, {},
641 FrameMessageMarker
{}, aMessage
, aIsSync
);
643 nsAutoTObserverArray
<nsMessageListenerInfo
, 1>* listeners
=
644 mListeners
.Get(aMessage
);
646 MMListenerRemover
lr(this);
648 nsAutoTObserverArray
<nsMessageListenerInfo
, 1>::EndLimitedIterator
iter(
650 while (iter
.HasMore()) {
651 nsMessageListenerInfo
& listener
= iter
.GetNext();
652 // Remove mListeners[i] if it's an expired weak listener.
653 nsCOMPtr
<nsISupports
> weakListener
;
654 if (listener
.mWeakListener
) {
655 weakListener
= do_QueryReferent(listener
.mWeakListener
);
662 if (!listener
.mListenWhenClosed
&& aTargetClosed
) {
666 JS::RootingContext
* rcx
= RootingCx();
667 JS::Rooted
<JSObject
*> object(rcx
);
668 JS::Rooted
<JSObject
*> objectGlobal(rcx
);
670 RefPtr
<MessageListener
> webIDLListener
;
672 webIDLListener
= listener
.mStrongListener
;
673 object
= webIDLListener
->CallbackOrNull();
674 objectGlobal
= webIDLListener
->CallbackGlobalOrNull();
676 nsCOMPtr
<nsIXPConnectWrappedJS
> wrappedJS
=
677 do_QueryInterface(weakListener
);
682 object
= wrappedJS
->GetJSObject();
683 objectGlobal
= wrappedJS
->GetJSObjectGlobal();
690 AutoEntryScript
aes(js::UncheckedUnwrap(object
),
691 "message manager handler");
692 JSContext
* cx
= aes
.cx();
694 // We passed the unwrapped object to AutoEntryScript so we now need to
695 // enter the realm of the global object that represents the realm of our
697 JSAutoRealm
ar(cx
, objectGlobal
);
699 RootedDictionary
<ReceiveMessageArgument
> argument(cx
);
701 JS::Rooted
<JS::Value
> json(cx
, JS::NullValue());
702 if (aCloneData
&& aCloneData
->DataLength()) {
703 aCloneData
->Read(cx
, &json
, aError
);
704 if (NS_WARN_IF(aError
.Failed())) {
705 aError
.SuppressException();
706 JS_ClearPendingException(cx
);
710 argument
.mData
= json
;
711 argument
.mJson
= json
;
713 // Get cloned MessagePort from StructuredCloneData.
715 Sequence
<OwningNonNull
<MessagePort
>> ports
;
716 if (!aCloneData
->TakeTransferredPortsAsSequence(ports
)) {
717 aError
.Throw(NS_ERROR_FAILURE
);
720 argument
.mPorts
.Construct(std::move(ports
));
723 argument
.mName
= aMessage
;
724 argument
.mSync
= aIsSync
;
725 argument
.mTarget
= aTarget
;
726 if (aTargetFrameLoader
) {
727 argument
.mTargetFrameLoader
.Construct(*aTargetFrameLoader
);
730 JS::Rooted
<JS::Value
> thisValue(cx
, JS::UndefinedValue());
732 if (JS::IsCallable(object
)) {
733 // A small hack to get 'this' value right on content side where
734 // messageManager is wrapped in BrowserChildMessageManager's global.
735 nsCOMPtr
<nsISupports
> defaultThisValue
;
737 defaultThisValue
= do_QueryObject(this);
739 defaultThisValue
= aTarget
;
741 js::AssertSameCompartment(cx
, object
);
742 aError
= nsContentUtils::WrapNative(cx
, defaultThisValue
, &thisValue
);
743 if (aError
.Failed()) {
748 JS::Rooted
<JS::Value
> rval(cx
, JS::UndefinedValue());
749 if (webIDLListener
) {
750 webIDLListener
->ReceiveMessage(thisValue
, argument
, &rval
, aError
);
751 if (aError
.Failed()) {
752 // At this point the call to ReceiveMessage will have reported any
753 // exceptions (we kept the default of eReportExceptions). We suppress
754 // the failure in the ErrorResult and continue.
755 aError
.SuppressException();
759 JS::Rooted
<JS::Value
> funval(cx
);
760 if (JS::IsCallable(object
)) {
761 // If the listener is a JS function:
762 funval
.setObject(*object
);
764 // If the listener is a JS object which has receiveMessage function:
765 if (!JS_GetProperty(cx
, object
, "receiveMessage", &funval
) ||
766 !funval
.isObject()) {
767 aError
.Throw(NS_ERROR_UNEXPECTED
);
771 // Check if the object is even callable.
772 if (!JS::IsCallable(&funval
.toObject())) {
773 aError
.Throw(NS_ERROR_UNEXPECTED
);
776 thisValue
.setObject(*object
);
779 JS::Rooted
<JS::Value
> argv(cx
);
780 if (!ToJSValue(cx
, argument
, &argv
)) {
781 aError
.Throw(NS_ERROR_UNEXPECTED
);
786 JS::Rooted
<JSObject
*> thisObject(cx
, thisValue
.toObjectOrNull());
787 js::AssertSameCompartment(cx
, thisObject
);
788 if (!JS_CallFunctionValue(cx
, thisObject
, funval
,
789 JS::HandleValueArray(argv
), &rval
)) {
790 // Because the AutoEntryScript is inside the loop this continue will
791 // make us report any exceptions (after which we'll move on to the
799 StructuredCloneData
* data
= aRetVal
->AppendElement();
800 data
->InitScope(JS::StructuredCloneScope::DifferentProcess
);
801 data
->Write(cx
, rval
, aError
);
802 if (NS_WARN_IF(aError
.Failed())) {
803 aRetVal
->RemoveLastElement();
805 aMessage
+ nsLiteralString(
806 u
": message reply cannot be cloned. Are "
807 "you trying to send an XPCOM object?");
809 nsCOMPtr
<nsIConsoleService
> console(
810 do_GetService(NS_CONSOLESERVICE_CONTRACTID
));
812 nsCOMPtr
<nsIScriptError
> error(
813 do_CreateInstance(NS_SCRIPTERROR_CONTRACTID
));
814 error
->Init(msg
, u
""_ns
, u
""_ns
, 0, 0, nsIScriptError::warningFlag
,
815 "chrome javascript"_ns
, false /* from private window */,
816 true /* from chrome context */);
817 console
->LogMessage(error
);
820 JS_ClearPendingException(cx
);
827 RefPtr
<nsFrameMessageManager
> kungFuDeathGrip
= GetParentManager();
828 if (kungFuDeathGrip
) {
829 kungFuDeathGrip
->ReceiveMessage(aTarget
, aTargetFrameLoader
, aTargetClosed
,
830 aMessage
, aIsSync
, aCloneData
, aRetVal
,
835 void nsFrameMessageManager::LoadPendingScripts(
836 nsFrameMessageManager
* aManager
, nsFrameMessageManager
* aChildMM
) {
837 // We have parent manager if we're a message broadcaster.
838 // In that case we want to load the pending scripts from all parent
839 // message managers in the hierarchy. Process the parent first so
840 // that pending scripts higher up in the hierarchy are loaded before others.
841 nsFrameMessageManager
* parentManager
= aManager
->GetParentManager();
843 LoadPendingScripts(parentManager
, aChildMM
);
846 for (uint32_t i
= 0; i
< aManager
->mPendingScripts
.Length(); ++i
) {
847 aChildMM
->LoadScript(aManager
->mPendingScripts
[i
], false,
848 aManager
->mPendingScriptsGlobalStates
[i
],
853 void nsFrameMessageManager::LoadPendingScripts() {
854 RefPtr
<nsFrameMessageManager
> kungfuDeathGrip
= this;
855 LoadPendingScripts(this, this);
858 void nsFrameMessageManager::SetCallback(MessageManagerCallback
* aCallback
) {
859 MOZ_ASSERT(!mIsBroadcaster
|| !mCallback
,
860 "Broadcasters cannot have callbacks!");
861 if (aCallback
&& mCallback
!= aCallback
) {
862 mCallback
= aCallback
;
864 mOwnedCallback
= WrapUnique(aCallback
);
869 void nsFrameMessageManager::Close() {
871 if (nsCOMPtr
<nsIObserverService
> obs
=
872 mozilla::services::GetObserverService()) {
873 obs
->NotifyWhenScriptSafe(this, "message-manager-close", nullptr);
878 mOwnedCallback
= nullptr;
881 void nsFrameMessageManager::Disconnect(bool aRemoveFromParent
) {
882 // Notify message-manager-close if we haven't already.
885 if (!mDisconnected
) {
886 if (nsCOMPtr
<nsIObserverService
> obs
=
887 mozilla::services::GetObserverService()) {
888 obs
->NotifyWhenScriptSafe(this, "message-manager-disconnect", nullptr);
892 ClearParentManager(aRemoveFromParent
);
894 mDisconnected
= true;
895 if (!mHandlingMessage
) {
900 void nsFrameMessageManager::SetInitialProcessData(
901 JS::Handle
<JS::Value
> aInitialData
) {
902 MOZ_ASSERT(!mChrome
);
903 MOZ_ASSERT(mIsProcessManager
);
904 MOZ_ASSERT(aInitialData
.isObject());
905 mInitialProcessData
= aInitialData
;
908 void nsFrameMessageManager::GetInitialProcessData(
909 JSContext
* aCx
, JS::MutableHandle
<JS::Value
> aInitialProcessData
,
910 ErrorResult
& aError
) {
911 MOZ_ASSERT(mIsProcessManager
);
912 MOZ_ASSERT_IF(mChrome
, IsBroadcaster());
914 JS::Rooted
<JS::Value
> init(aCx
, mInitialProcessData
);
915 if (mChrome
&& init
.isUndefined()) {
916 // We create the initial object in the junk scope. If we created it in a
917 // normal realm, that realm would leak until shutdown.
918 JS::Rooted
<JSObject
*> global(aCx
, xpc::PrivilegedJunkScope());
919 JSAutoRealm
ar(aCx
, global
);
921 JS::Rooted
<JSObject
*> obj(aCx
, JS_NewPlainObject(aCx
));
923 aError
.NoteJSContextException(aCx
);
927 mInitialProcessData
.setObject(*obj
);
928 init
.setObject(*obj
);
931 if (!mChrome
&& XRE_IsParentProcess()) {
932 // This is the cpmm in the parent process. We should use the same object as
933 // the ppmm. Create it first through do_GetService and use the cached
934 // pointer in sParentProcessManager.
935 nsCOMPtr
<nsISupports
> ppmm
=
936 do_GetService("@mozilla.org/parentprocessmessagemanager;1");
937 sParentProcessManager
->GetInitialProcessData(aCx
, &init
, aError
);
938 if (aError
.Failed()) {
941 mInitialProcessData
= init
;
944 if (!JS_WrapValue(aCx
, &init
)) {
945 aError
.NoteJSContextException(aCx
);
948 aInitialProcessData
.set(init
);
951 WritableSharedMap
* nsFrameMessageManager::SharedData() {
952 if (!mChrome
|| !mIsProcessManager
) {
953 MOZ_ASSERT(false, "Should only call this binding method on ppmm");
957 mSharedData
= new WritableSharedMap();
962 already_AddRefed
<ProcessMessageManager
>
963 nsFrameMessageManager::GetProcessMessageManager(ErrorResult
& aError
) {
964 RefPtr
<ProcessMessageManager
> pmm
;
966 pmm
= mCallback
->GetProcessMessageManager();
971 void nsFrameMessageManager::GetRemoteType(nsACString
& aRemoteType
,
972 ErrorResult
& aError
) const {
973 aRemoteType
.Truncate();
975 mCallback
->DoGetRemoteType(aRemoteType
, aError
);
981 struct MessageManagerReferentCount
{
982 MessageManagerReferentCount() : mStrong(0), mWeakAlive(0), mWeakDead(0) {}
986 nsTArray
<nsString
> mSuspectMessages
;
987 nsTHashMap
<nsStringHashKey
, uint32_t> mMessageCounter
;
992 namespace mozilla::dom
{
994 class MessageManagerReporter final
: public nsIMemoryReporter
{
995 ~MessageManagerReporter() = default;
999 NS_DECL_NSIMEMORYREPORTER
1001 static const size_t kSuspectReferentCount
= 300;
1004 void CountReferents(nsFrameMessageManager
* aMessageManager
,
1005 MessageManagerReferentCount
* aReferentCount
);
1008 NS_IMPL_ISUPPORTS(MessageManagerReporter
, nsIMemoryReporter
)
1010 void MessageManagerReporter::CountReferents(
1011 nsFrameMessageManager
* aMessageManager
,
1012 MessageManagerReferentCount
* aReferentCount
) {
1013 for (const auto& entry
: aMessageManager
->mListeners
) {
1014 nsAutoTObserverArray
<nsMessageListenerInfo
, 1>* listeners
= entry
.GetWeak();
1015 uint32_t listenerCount
= listeners
->Length();
1016 if (listenerCount
== 0) {
1020 nsString
key(entry
.GetKey());
1021 const uint32_t currentCount
=
1022 (aReferentCount
->mMessageCounter
.LookupOrInsert(key
, 0) +=
1025 // Keep track of messages that have a suspiciously large
1026 // number of referents (symptom of leak).
1027 if (currentCount
>= MessageManagerReporter::kSuspectReferentCount
) {
1028 aReferentCount
->mSuspectMessages
.AppendElement(key
);
1031 for (uint32_t i
= 0; i
< listenerCount
; ++i
) {
1032 const nsMessageListenerInfo
& listenerInfo
= listeners
->ElementAt(i
);
1033 if (listenerInfo
.mWeakListener
) {
1034 nsCOMPtr
<nsISupports
> referent
=
1035 do_QueryReferent(listenerInfo
.mWeakListener
);
1037 aReferentCount
->mWeakAlive
++;
1039 aReferentCount
->mWeakDead
++;
1042 aReferentCount
->mStrong
++;
1047 // Add referent count in child managers because the listeners
1048 // participate in messages dispatched from parent message manager.
1049 for (uint32_t i
= 0; i
< aMessageManager
->mChildManagers
.Length(); ++i
) {
1050 RefPtr
<nsFrameMessageManager
> mm
= aMessageManager
->mChildManagers
[i
];
1051 CountReferents(mm
, aReferentCount
);
1055 static void ReportReferentCount(
1056 const char* aManagerType
, const MessageManagerReferentCount
& aReferentCount
,
1057 nsIHandleReportCallback
* aHandleReport
, nsISupports
* aData
) {
1058 #define REPORT(_path, _amount, _desc) \
1060 aHandleReport->Callback(""_ns, _path, nsIMemoryReporter::KIND_OTHER, \
1061 nsIMemoryReporter::UNITS_COUNT, _amount, _desc, \
1065 REPORT(nsPrintfCString("message-manager/referent/%s/strong", aManagerType
),
1066 aReferentCount
.mStrong
,
1067 nsPrintfCString("The number of strong referents held by the message "
1068 "manager in the %s manager.",
1071 nsPrintfCString("message-manager/referent/%s/weak/alive", aManagerType
),
1072 aReferentCount
.mWeakAlive
,
1073 nsPrintfCString("The number of weak referents that are still alive "
1074 "held by the message manager in the %s manager.",
1076 REPORT(nsPrintfCString("message-manager/referent/%s/weak/dead", aManagerType
),
1077 aReferentCount
.mWeakDead
,
1078 nsPrintfCString("The number of weak referents that are dead "
1079 "held by the message manager in the %s manager.",
1082 for (uint32_t i
= 0; i
< aReferentCount
.mSuspectMessages
.Length(); i
++) {
1083 const uint32_t totalReferentCount
=
1084 aReferentCount
.mMessageCounter
.Get(aReferentCount
.mSuspectMessages
[i
]);
1085 NS_ConvertUTF16toUTF8
suspect(aReferentCount
.mSuspectMessages
[i
]);
1086 REPORT(nsPrintfCString("message-manager-suspect/%s/referent(message=%s)",
1087 aManagerType
, suspect
.get()),
1089 nsPrintfCString("A message in the %s message manager with a "
1090 "suspiciously large number of referents (symptom "
1098 static StaticRefPtr
<ChromeMessageBroadcaster
> sGlobalMessageManager
;
1101 MessageManagerReporter::CollectReports(nsIHandleReportCallback
* aHandleReport
,
1102 nsISupports
* aData
, bool aAnonymize
) {
1103 if (XRE_IsParentProcess() && sGlobalMessageManager
) {
1104 MessageManagerReferentCount count
;
1105 CountReferents(sGlobalMessageManager
, &count
);
1106 ReportReferentCount("global-manager", count
, aHandleReport
, aData
);
1109 if (nsFrameMessageManager::sParentProcessManager
) {
1110 MessageManagerReferentCount count
;
1111 CountReferents(nsFrameMessageManager::sParentProcessManager
, &count
);
1112 ReportReferentCount("parent-process-manager", count
, aHandleReport
, aData
);
1115 if (nsFrameMessageManager::sChildProcessManager
) {
1116 MessageManagerReferentCount count
;
1117 CountReferents(nsFrameMessageManager::sChildProcessManager
, &count
);
1118 ReportReferentCount("child-process-manager", count
, aHandleReport
, aData
);
1124 } // namespace mozilla::dom
1126 already_AddRefed
<ChromeMessageBroadcaster
>
1127 nsFrameMessageManager::GetGlobalMessageManager() {
1128 RefPtr
<ChromeMessageBroadcaster
> mm
;
1129 if (sGlobalMessageManager
) {
1130 mm
= sGlobalMessageManager
;
1132 sGlobalMessageManager
= mm
=
1133 new ChromeMessageBroadcaster(MessageManagerFlags::MM_GLOBAL
);
1134 ClearOnShutdown(&sGlobalMessageManager
);
1135 RegisterStrongMemoryReporter(new MessageManagerReporter());
1140 nsresult
NS_NewGlobalMessageManager(nsISupports
** aResult
) {
1141 *aResult
= nsFrameMessageManager::GetGlobalMessageManager().take();
1145 nsTHashMap
<nsStringHashKey
, nsMessageManagerScriptHolder
*>*
1146 nsMessageManagerScriptExecutor::sCachedScripts
= nullptr;
1147 StaticRefPtr
<nsScriptCacheCleaner
>
1148 nsMessageManagerScriptExecutor::sScriptCacheCleaner
;
1150 void nsMessageManagerScriptExecutor::DidCreateScriptLoader() {
1151 if (!sCachedScripts
) {
1153 new nsTHashMap
<nsStringHashKey
, nsMessageManagerScriptHolder
*>;
1154 sScriptCacheCleaner
= new nsScriptCacheCleaner();
1159 void nsMessageManagerScriptExecutor::PurgeCache() {
1160 if (sCachedScripts
) {
1161 NS_ASSERTION(sCachedScripts
!= nullptr, "Need cached scripts");
1162 for (auto iter
= sCachedScripts
->Iter(); !iter
.Done(); iter
.Next()) {
1170 void nsMessageManagerScriptExecutor::Shutdown() {
1171 if (sCachedScripts
) {
1174 delete sCachedScripts
;
1175 sCachedScripts
= nullptr;
1176 sScriptCacheCleaner
= nullptr;
1180 static void FillCompileOptionsForCachedStencil(JS::CompileOptions
& aOptions
) {
1181 ScriptPreloader::FillCompileOptionsForCachedStencil(aOptions
);
1182 aOptions
.setNonSyntacticScope(true);
1185 void nsMessageManagerScriptExecutor::LoadScriptInternal(
1186 JS::Handle
<JSObject
*> aMessageManager
, const nsAString
& aURL
,
1187 bool aRunInUniqueScope
) {
1188 AUTO_PROFILER_LABEL_DYNAMIC_LOSSY_NSSTRING(
1189 "nsMessageManagerScriptExecutor::LoadScriptInternal", OTHER
, aURL
);
1191 if (!sCachedScripts
) {
1195 RefPtr
<JS::Stencil
> stencil
;
1196 nsMessageManagerScriptHolder
* holder
= sCachedScripts
->Get(aURL
);
1198 stencil
= holder
->mStencil
;
1201 TryCacheLoadAndCompileScript(aURL
, aRunInUniqueScope
, aMessageManager
);
1204 AutoEntryScript
aes(aMessageManager
, "message manager script load");
1205 JSContext
* cx
= aes
.cx();
1207 JS::CompileOptions
options(cx
);
1208 FillCompileOptionsForCachedStencil(options
);
1209 JS::InstantiateOptions
instantiateOptions(options
);
1210 JS::Rooted
<JSScript
*> script(
1211 cx
, JS::InstantiateGlobalStencil(cx
, instantiateOptions
, stencil
));
1214 if (aRunInUniqueScope
) {
1215 JS::Rooted
<JSObject
*> scope(cx
);
1216 bool ok
= js::ExecuteInFrameScriptEnvironment(cx
, aMessageManager
,
1219 // Force the scope to stay alive.
1220 mAnonymousGlobalScopes
.AppendElement(scope
);
1223 JS::Rooted
<JS::Value
> rval(cx
);
1224 JS::RootedVector
<JSObject
*> envChain(cx
);
1225 if (!envChain
.append(aMessageManager
)) {
1228 Unused
<< JS_ExecuteScript(cx
, envChain
, script
, &rval
);
1234 already_AddRefed
<JS::Stencil
>
1235 nsMessageManagerScriptExecutor::TryCacheLoadAndCompileScript(
1236 const nsAString
& aURL
, bool aRunInUniqueScope
,
1237 JS::Handle
<JSObject
*> aMessageManager
) {
1238 nsCString url
= NS_ConvertUTF16toUTF8(aURL
);
1239 nsCOMPtr
<nsIURI
> uri
;
1240 nsresult rv
= NS_NewURI(getter_AddRefs(uri
), url
);
1241 if (NS_FAILED(rv
)) {
1246 rv
= NS_URIChainHasFlags(uri
, nsIProtocolHandler::URI_IS_LOCAL_RESOURCE
,
1248 if (NS_FAILED(rv
) || !hasFlags
) {
1249 NS_WARNING("Will not load a frame script!");
1253 // If this script won't be cached, or there is only one of this type of
1254 // message manager per process, treat this script as run-once. Run-once
1255 // scripts can be compiled directly for the target global, and will be dropped
1256 // from the preloader cache after they're executed and serialized.
1258 // NOTE: This does not affect the JS::CompileOptions. We generate the same
1259 // bytecode as though it were run multiple times. This is required for the
1260 // batch decoding from ScriptPreloader to work.
1261 bool isRunOnce
= IsProcessScoped();
1263 // We don't cache data: scripts!
1264 nsAutoCString scheme
;
1265 uri
->GetScheme(scheme
);
1266 bool isCacheable
= !scheme
.EqualsLiteral("data");
1267 bool useScriptPreloader
= isCacheable
;
1269 // If the script will be reused in this session, compile it in the compilation
1270 // scope instead of the current global to avoid keeping the current
1271 // compartment alive.
1273 if (!jsapi
.Init(isRunOnce
? aMessageManager
: xpc::CompilationScope())) {
1276 JSContext
* cx
= jsapi
.cx();
1278 RefPtr
<JS::Stencil
> stencil
;
1279 if (useScriptPreloader
) {
1280 nsAutoCString cachePath
;
1281 rv
= scache::PathifyURI(CACHE_PREFIX("script"), uri
, cachePath
);
1282 NS_ENSURE_SUCCESS(rv
, nullptr);
1284 JS::DecodeOptions decodeOptions
;
1285 ScriptPreloader::FillDecodeOptionsForCachedStencil(decodeOptions
);
1286 stencil
= ScriptPreloader::GetChildSingleton().GetCachedStencil(
1287 cx
, decodeOptions
, cachePath
);
1291 nsCOMPtr
<nsIChannel
> channel
;
1292 NS_NewChannel(getter_AddRefs(channel
), uri
,
1293 nsContentUtils::GetSystemPrincipal(),
1294 nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL
,
1295 nsIContentPolicy::TYPE_INTERNAL_FRAME_MESSAGEMANAGER_SCRIPT
);
1301 nsCOMPtr
<nsIInputStream
> input
;
1302 rv
= channel
->Open(getter_AddRefs(input
));
1303 NS_ENSURE_SUCCESS(rv
, nullptr);
1304 nsString dataString
;
1305 UniquePtr
<Utf8Unit
[], JS::FreePolicy
> dataStringBuf
;
1306 size_t dataStringLength
= 0;
1310 if (NS_FAILED(NS_ReadInputStreamToString(input
, buffer
, -1, &written
))) {
1314 uint32_t size
= (uint32_t)std::min(written
, (uint64_t)UINT32_MAX
);
1315 ScriptLoader::ConvertToUTF8(channel
, (uint8_t*)buffer
.get(), size
, u
""_ns
,
1316 nullptr, dataStringBuf
, dataStringLength
);
1319 if (!dataStringBuf
) {
1323 JS::CompileOptions
options(cx
);
1324 FillCompileOptionsForCachedStencil(options
);
1325 options
.setFileAndLine(url
.get(), 1);
1327 // If we are not encoding to the ScriptPreloader cache, we can now relax the
1328 // compile options and use the JS syntax-parser for lower latency.
1329 if (!useScriptPreloader
|| !ScriptPreloader::GetChildSingleton().Active()) {
1330 options
.setSourceIsLazy(false);
1333 JS::SourceText
<Utf8Unit
> srcBuf
;
1334 if (!srcBuf
.init(cx
, std::move(dataStringBuf
), dataStringLength
)) {
1338 stencil
= JS::CompileGlobalScriptToStencil(cx
, options
, srcBuf
);
1343 if (isCacheable
&& !isRunOnce
) {
1344 // Store into our cache only when we compile it here.
1345 auto* holder
= new nsMessageManagerScriptHolder(stencil
);
1346 sCachedScripts
->InsertOrUpdate(aURL
, holder
);
1350 // The above shouldn't touch any options for instantiation.
1351 JS::InstantiateOptions
instantiateOptions(options
);
1352 instantiateOptions
.assertDefault();
1356 MOZ_ASSERT(stencil
);
1358 if (useScriptPreloader
) {
1359 nsAutoCString cachePath
;
1360 rv
= scache::PathifyURI(CACHE_PREFIX("script"), uri
, cachePath
);
1361 NS_ENSURE_SUCCESS(rv
, nullptr);
1362 ScriptPreloader::GetChildSingleton().NoteStencil(url
, cachePath
, stencil
,
1366 return stencil
.forget();
1369 void nsMessageManagerScriptExecutor::Trace(const TraceCallbacks
& aCallbacks
,
1371 for (size_t i
= 0, length
= mAnonymousGlobalScopes
.Length(); i
< length
;
1373 aCallbacks
.Trace(&mAnonymousGlobalScopes
[i
], "mAnonymousGlobalScopes[i]",
1378 void nsMessageManagerScriptExecutor::Unlink() {
1379 ImplCycleCollectionUnlink(mAnonymousGlobalScopes
);
1382 bool nsMessageManagerScriptExecutor::Init() {
1383 DidCreateScriptLoader();
1387 void nsMessageManagerScriptExecutor::MarkScopesForCC() {
1388 for (uint32_t i
= 0; i
< mAnonymousGlobalScopes
.Length(); ++i
) {
1389 mAnonymousGlobalScopes
[i
].exposeToActiveJS();
1393 NS_IMPL_ISUPPORTS(nsScriptCacheCleaner
, nsIObserver
)
1395 ChildProcessMessageManager
* nsFrameMessageManager::sChildProcessManager
=
1397 ParentProcessMessageManager
* nsFrameMessageManager::sParentProcessManager
=
1399 nsFrameMessageManager
* nsFrameMessageManager::sSameProcessParentManager
=
1402 class nsAsyncMessageToSameProcessChild
: public nsSameProcessAsyncMessageBase
,
1405 nsAsyncMessageToSameProcessChild()
1406 : mozilla::Runnable("nsAsyncMessageToSameProcessChild") {}
1407 NS_IMETHOD
Run() override
{
1408 nsFrameMessageManager
* ppm
=
1409 nsFrameMessageManager::GetChildProcessManager();
1410 ReceiveMessage(ppm
, nullptr, ppm
);
1416 * Send messages to an imaginary child process in a single-process scenario.
1418 class SameParentProcessMessageManagerCallback
: public MessageManagerCallback
{
1420 SameParentProcessMessageManagerCallback() {
1421 MOZ_COUNT_CTOR(SameParentProcessMessageManagerCallback
);
1423 ~SameParentProcessMessageManagerCallback() override
{
1424 MOZ_COUNT_DTOR(SameParentProcessMessageManagerCallback
);
1427 bool DoLoadMessageManagerScript(const nsAString
& aURL
,
1428 bool aRunInGlobalScope
) override
{
1429 auto* global
= ContentProcessMessageManager::Get();
1430 MOZ_ASSERT(!aRunInGlobalScope
);
1431 global
->LoadScript(aURL
);
1435 nsresult
DoSendAsyncMessage(const nsAString
& aMessage
,
1436 StructuredCloneData
& aData
) override
{
1437 RefPtr
<nsAsyncMessageToSameProcessChild
> ev
=
1438 new nsAsyncMessageToSameProcessChild();
1440 nsresult rv
= ev
->Init(aMessage
, aData
);
1441 if (NS_FAILED(rv
)) {
1444 rv
= NS_DispatchToCurrentThread(ev
);
1445 if (NS_FAILED(rv
)) {
1453 * Send messages to the parent process.
1455 class ChildProcessMessageManagerCallback
: public MessageManagerCallback
{
1457 ChildProcessMessageManagerCallback() {
1458 MOZ_COUNT_CTOR(ChildProcessMessageManagerCallback
);
1460 ~ChildProcessMessageManagerCallback() override
{
1461 MOZ_COUNT_DTOR(ChildProcessMessageManagerCallback
);
1464 bool DoSendBlockingMessage(const nsAString
& aMessage
,
1465 StructuredCloneData
& aData
,
1466 nsTArray
<StructuredCloneData
>* aRetVal
) override
{
1467 mozilla::dom::ContentChild
* cc
= mozilla::dom::ContentChild::GetSingleton();
1471 ClonedMessageData data
;
1472 if (!BuildClonedMessageData(aData
, data
)) {
1475 return cc
->SendSyncMessage(PromiseFlatString(aMessage
), data
, aRetVal
);
1478 nsresult
DoSendAsyncMessage(const nsAString
& aMessage
,
1479 StructuredCloneData
& aData
) override
{
1480 mozilla::dom::ContentChild
* cc
= mozilla::dom::ContentChild::GetSingleton();
1484 ClonedMessageData data
;
1485 if (!BuildClonedMessageData(aData
, data
)) {
1486 return NS_ERROR_DOM_DATA_CLONE_ERR
;
1488 if (!cc
->SendAsyncMessage(PromiseFlatString(aMessage
), data
)) {
1489 return NS_ERROR_UNEXPECTED
;
1496 class nsAsyncMessageToSameProcessParent
1497 : public nsSameProcessAsyncMessageBase
,
1498 public SameProcessMessageQueue::Runnable
{
1500 nsAsyncMessageToSameProcessParent() = default;
1501 nsresult
HandleMessage() override
{
1502 nsFrameMessageManager
* ppm
=
1503 nsFrameMessageManager::sSameProcessParentManager
;
1504 ReceiveMessage(ppm
, nullptr, ppm
);
1510 * Send messages to the imaginary parent process in a single-process scenario.
1512 class SameChildProcessMessageManagerCallback
: public MessageManagerCallback
{
1514 SameChildProcessMessageManagerCallback() {
1515 MOZ_COUNT_CTOR(SameChildProcessMessageManagerCallback
);
1517 ~SameChildProcessMessageManagerCallback() override
{
1518 MOZ_COUNT_DTOR(SameChildProcessMessageManagerCallback
);
1521 bool DoSendBlockingMessage(const nsAString
& aMessage
,
1522 StructuredCloneData
& aData
,
1523 nsTArray
<StructuredCloneData
>* aRetVal
) override
{
1524 SameProcessMessageQueue
* queue
= SameProcessMessageQueue::Get();
1527 if (nsFrameMessageManager::sSameProcessParentManager
) {
1528 RefPtr
<nsFrameMessageManager
> ppm
=
1529 nsFrameMessageManager::sSameProcessParentManager
;
1530 ppm
->ReceiveMessage(ppm
, nullptr, aMessage
, true, &aData
, aRetVal
,
1536 nsresult
DoSendAsyncMessage(const nsAString
& aMessage
,
1537 StructuredCloneData
& aData
) override
{
1538 SameProcessMessageQueue
* queue
= SameProcessMessageQueue::Get();
1539 RefPtr
<nsAsyncMessageToSameProcessParent
> ev
=
1540 new nsAsyncMessageToSameProcessParent();
1541 nsresult rv
= ev
->Init(aMessage
, aData
);
1543 if (NS_FAILED(rv
)) {
1551 // This creates the global parent process message manager.
1552 nsresult
NS_NewParentProcessMessageManager(nsISupports
** aResult
) {
1553 NS_ASSERTION(!nsFrameMessageManager::sParentProcessManager
,
1554 "Re-creating sParentProcessManager");
1555 RefPtr
<ParentProcessMessageManager
> mm
= new ParentProcessMessageManager();
1556 nsFrameMessageManager::sParentProcessManager
= mm
;
1557 nsFrameMessageManager::NewProcessMessageManager(
1558 false); // Create same process message manager.
1563 ProcessMessageManager
* nsFrameMessageManager::NewProcessMessageManager(
1565 if (!nsFrameMessageManager::sParentProcessManager
) {
1566 nsCOMPtr
<nsISupports
> dummy
=
1567 do_GetService("@mozilla.org/parentprocessmessagemanager;1");
1570 MOZ_ASSERT(nsFrameMessageManager::sParentProcessManager
,
1571 "parent process manager not created");
1572 ProcessMessageManager
* mm
;
1574 // Callback is set in ContentParent::InitInternal so that the process has
1575 // already started when we send pending scripts.
1576 mm
= new ProcessMessageManager(
1577 nullptr, nsFrameMessageManager::sParentProcessManager
);
1580 new ProcessMessageManager(new SameParentProcessMessageManagerCallback(),
1581 nsFrameMessageManager::sParentProcessManager
,
1582 MessageManagerFlags::MM_OWNSCALLBACK
);
1583 mm
->SetOsPid(base::GetCurrentProcId());
1584 sSameProcessParentManager
= mm
;
1589 nsresult
NS_NewChildProcessMessageManager(nsISupports
** aResult
) {
1590 NS_ASSERTION(!nsFrameMessageManager::GetChildProcessManager(),
1591 "Re-creating sChildProcessManager");
1593 MessageManagerCallback
* cb
;
1594 if (XRE_IsParentProcess()) {
1595 cb
= new SameChildProcessMessageManagerCallback();
1597 cb
= new ChildProcessMessageManagerCallback();
1598 RegisterStrongMemoryReporter(new MessageManagerReporter());
1600 auto* mm
= new ChildProcessMessageManager(cb
);
1601 nsFrameMessageManager::SetChildProcessManager(mm
);
1602 auto global
= MakeRefPtr
<ContentProcessMessageManager
>(mm
);
1603 NS_ENSURE_TRUE(global
->Init(), NS_ERROR_UNEXPECTED
);
1604 return CallQueryInterface(global
, aResult
);
1607 void nsFrameMessageManager::MarkForCC() {
1608 for (const auto& entry
: mListeners
) {
1609 nsAutoTObserverArray
<nsMessageListenerInfo
, 1>* listeners
= entry
.GetWeak();
1610 uint32_t count
= listeners
->Length();
1611 for (uint32_t i
= 0; i
< count
; i
++) {
1612 MessageListener
* strongListener
= listeners
->ElementAt(i
).mStrongListener
;
1613 if (strongListener
) {
1614 strongListener
->MarkForCC();
1619 if (mRefCnt
.IsPurple()) {
1620 mRefCnt
.RemovePurple();
1624 nsSameProcessAsyncMessageBase::nsSameProcessAsyncMessageBase()
1626 : mCalledInit(false)
1631 nsresult
nsSameProcessAsyncMessageBase::Init(const nsAString
& aMessage
,
1632 StructuredCloneData
& aData
) {
1633 if (!mData
.Copy(aData
)) {
1634 Telemetry::Accumulate(Telemetry::IPC_SAME_PROCESS_MESSAGE_COPY_OOM_KB
,
1635 aData
.DataLength());
1636 return NS_ERROR_OUT_OF_MEMORY
;
1639 mMessage
= aMessage
;
1647 void nsSameProcessAsyncMessageBase::ReceiveMessage(
1648 nsISupports
* aTarget
, nsFrameLoader
* aTargetFrameLoader
,
1649 nsFrameMessageManager
* aManager
) {
1650 // Make sure that we have called Init() and it has succeeded.
1651 MOZ_ASSERT(mCalledInit
);
1653 RefPtr
<nsFrameMessageManager
> mm
= aManager
;
1654 mm
->ReceiveMessage(aTarget
, aTargetFrameLoader
, mMessage
, false, &mData
,
1655 nullptr, IgnoreErrors());