Backed out 14 changesets (bug 1865005, bug 1864168, bug 1864155, bug 1862814, bug...
[gecko.git] / dom / base / nsFrameMessageManager.cpp
blobfdf6b4775b566f287d31b1209fda45be92ea1448
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"
9 #include <algorithm>
10 #include <cmath>
11 #include <cstddef>
12 #include <cstdint>
13 #include <new>
14 #include <utility>
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"
26 #include "js/JSON.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"
34 #include "jsapi.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"
76 #include "nsCOMPtr.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"
83 #include "nsDebug.h"
84 #include "nsError.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"
99 #include "nsIURI.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"
118 #include "nscore.h"
119 #include "xpcpublic.h"
121 #ifdef XP_WIN
122 # if defined(SendMessage)
123 # undef SendMessage
124 # endif
125 #endif
127 #ifdef FUZZING
128 # include "MessageManagerFuzzer.h"
129 #endif
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,
141 bool aIsSync) {
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}");
154 return schema;
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),
168 mClosed(false),
169 mDisconnected(false),
170 mCallback(aCallback) {
171 NS_ASSERTION(!mIsBroadcaster || !mCallback,
172 "Broadcasters cannot have callbacks!");
173 if (mOwnsCallback) {
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)
235 NS_INTERFACE_MAP_END
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();
244 if (!parent) {
245 return;
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) {
270 return;
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);
284 if (listeners) {
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);
290 return;
296 static already_AddRefed<nsISupports> ToXPCOMMessageListener(
297 MessageListener& aListener) {
298 return CallbackObjectHolder<mozilla::dom::MessageListener, nsISupports>(
299 &aListener)
300 .ToXPCOMCallback();
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);
308 if (!weak) {
309 aError.Throw(NS_ERROR_NO_INTERFACE);
310 return;
313 #ifdef DEBUG
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;
324 if (weakListener) {
325 nsCOMPtr<nsISupports> otherCanonical = do_QueryReferent(weakListener);
326 MOZ_ASSERT((canonical == otherCanonical) == (weak == weakListener));
330 #endif
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) {
336 return;
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);
350 if (!weak) {
351 aError.Throw(NS_ERROR_NO_INTERFACE);
352 return;
355 nsAutoTObserverArray<nsMessageListenerInfo, 1>* listeners =
356 mListeners.Get(aMessageName);
357 if (!listeners) {
358 return;
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);
365 return;
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);
380 if (mCallback) {
381 #ifdef DEBUG_smaug
382 printf("Will load %s \n", NS_ConvertUTF16toUTF8(aURL).get());
383 #endif
384 if (!mCallback->DoLoadMessageManagerScript(aURL, aRunInGlobalScope)) {
385 aError.Throw(NS_ERROR_FAILURE);
386 return;
390 for (uint32_t i = 0; i < mChildManagers.Length(); ++i) {
391 RefPtr<nsFrameMessageManager> mm = mChildManagers[i];
392 if (mm) {
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);
405 break;
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()) {
415 NS_WARNING(
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);
419 return;
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);
429 return;
432 nsTArray<JS::Value>* array = aList.AppendElement(2);
433 array->AppendElement(url);
434 array->AppendElement(JS::BooleanValue(mPendingScriptsGlobalStates[i]));
438 /* static */
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);
446 ErrorResult rv;
447 aData.Write(aCx, v, t, JS::CloneDataPolicy(), rv);
448 if (!rv.Failed()) {
449 return true;
452 rv.SuppressException();
453 JS_ClearPendingException(aCx);
455 nsCOMPtr<nsIConsoleService> console(
456 do_GetService(NS_CONSOLESERVICE_CONTRACTID));
457 if (console) {
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));
463 error->Init(
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
475 // as a dictionary.
476 nsAutoString json;
477 NS_ENSURE_TRUE(
478 nsContentUtils::StringifyJSON(aCx, v, json, UndefinedIsNullStringLiteral),
479 false);
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),
485 false);
487 aData.Write(aCx, val, rv);
488 if (NS_WARN_IF(rv.Failed())) {
489 rv.SuppressException();
490 return false;
493 return true;
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);
516 return;
519 StructuredCloneData data;
520 if (!aObj.isUndefined() &&
521 !GetParamsForMessage(aCx, aObj, JS::UndefinedHandleValue, data)) {
522 aError.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
523 return;
526 #ifdef FUZZING
527 if (data.DataLength() > 0) {
528 MessageManagerFuzzer::TryMutate(aCx, aMessageName, &data,
529 JS::UndefinedHandleValue);
531 #endif
533 if (!mCallback) {
534 aError.Throw(NS_ERROR_NOT_INITIALIZED);
535 return;
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);
556 if (!ok) {
557 return;
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");
567 return;
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);
580 return NS_OK;
583 if (!mCallback) {
584 return NS_ERROR_NOT_INITIALIZED;
587 nsresult rv = mCallback->DoSendAsyncMessage(aMessage, aData);
588 if (NS_FAILED(rv)) {
589 return rv;
591 return NS_OK;
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);
601 return;
604 profiler_add_marker("SendAsyncMessage", geckoprofiler::category::IPC, {},
605 FrameMessageMarker{}, aMessageName, false);
607 #ifdef FUZZING
608 if (data.DataLength()) {
609 MessageManagerFuzzer::TryMutate(aCx, aMessageName, &data, aTransfers);
611 #endif
613 aError = DispatchAsyncMessageInternal(aCx, aMessageName, data);
616 class MMListenerRemover {
617 public:
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) {
639 MOZ_ASSERT(aTarget);
640 profiler_add_marker("ReceiveMessage", geckoprofiler::category::IPC, {},
641 FrameMessageMarker{}, aMessage, aIsSync);
643 nsAutoTObserverArray<nsMessageListenerInfo, 1>* listeners =
644 mListeners.Get(aMessage);
645 if (listeners) {
646 MMListenerRemover lr(this);
648 nsAutoTObserverArray<nsMessageListenerInfo, 1>::EndLimitedIterator iter(
649 *listeners);
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);
656 if (!weakListener) {
657 iter.Remove();
658 continue;
662 if (!listener.mListenWhenClosed && aTargetClosed) {
663 continue;
666 JS::RootingContext* rcx = RootingCx();
667 JS::Rooted<JSObject*> object(rcx);
668 JS::Rooted<JSObject*> objectGlobal(rcx);
670 RefPtr<MessageListener> webIDLListener;
671 if (!weakListener) {
672 webIDLListener = listener.mStrongListener;
673 object = webIDLListener->CallbackOrNull();
674 objectGlobal = webIDLListener->CallbackGlobalOrNull();
675 } else {
676 nsCOMPtr<nsIXPConnectWrappedJS> wrappedJS =
677 do_QueryInterface(weakListener);
678 if (!wrappedJS) {
679 continue;
682 object = wrappedJS->GetJSObject();
683 objectGlobal = wrappedJS->GetJSObjectGlobal();
686 if (!object) {
687 continue;
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
696 // callback.
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);
707 return;
710 argument.mData = json;
711 argument.mJson = json;
713 // Get cloned MessagePort from StructuredCloneData.
714 if (aCloneData) {
715 Sequence<OwningNonNull<MessagePort>> ports;
716 if (!aCloneData->TakeTransferredPortsAsSequence(ports)) {
717 aError.Throw(NS_ERROR_FAILURE);
718 return;
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;
736 if (mChrome) {
737 defaultThisValue = do_QueryObject(this);
738 } else {
739 defaultThisValue = aTarget;
741 js::AssertSameCompartment(cx, object);
742 aError = nsContentUtils::WrapNative(cx, defaultThisValue, &thisValue);
743 if (aError.Failed()) {
744 return;
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();
756 continue;
758 } else {
759 JS::Rooted<JS::Value> funval(cx);
760 if (JS::IsCallable(object)) {
761 // If the listener is a JS function:
762 funval.setObject(*object);
763 } else {
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);
768 return;
771 // Check if the object is even callable.
772 if (!JS::IsCallable(&funval.toObject())) {
773 aError.Throw(NS_ERROR_UNEXPECTED);
774 return;
776 thisValue.setObject(*object);
779 JS::Rooted<JS::Value> argv(cx);
780 if (!ToJSValue(cx, argument, &argv)) {
781 aError.Throw(NS_ERROR_UNEXPECTED);
782 return;
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
792 // next listener).
793 continue;
798 if (aRetVal) {
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();
804 nsString msg =
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));
811 if (console) {
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);
821 continue;
827 RefPtr<nsFrameMessageManager> kungFuDeathGrip = GetParentManager();
828 if (kungFuDeathGrip) {
829 kungFuDeathGrip->ReceiveMessage(aTarget, aTargetFrameLoader, aTargetClosed,
830 aMessage, aIsSync, aCloneData, aRetVal,
831 aError);
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();
842 if (parentManager) {
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],
849 IgnoreErrors());
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;
863 if (mOwnsCallback) {
864 mOwnedCallback = WrapUnique(aCallback);
869 void nsFrameMessageManager::Close() {
870 if (!mClosed) {
871 if (nsCOMPtr<nsIObserverService> obs =
872 mozilla::services::GetObserverService()) {
873 obs->NotifyWhenScriptSafe(this, "message-manager-close", nullptr);
876 mClosed = true;
877 mCallback = nullptr;
878 mOwnedCallback = nullptr;
881 void nsFrameMessageManager::Disconnect(bool aRemoveFromParent) {
882 // Notify message-manager-close if we haven't already.
883 Close();
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) {
896 mListeners.Clear();
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));
922 if (!obj) {
923 aError.NoteJSContextException(aCx);
924 return;
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()) {
939 return;
941 mInitialProcessData = init;
944 if (!JS_WrapValue(aCx, &init)) {
945 aError.NoteJSContextException(aCx);
946 return;
948 aInitialProcessData.set(init);
951 WritableSharedMap* nsFrameMessageManager::SharedData() {
952 if (!mChrome || !mIsProcessManager) {
953 MOZ_ASSERT(false, "Should only call this binding method on ppmm");
954 return nullptr;
956 if (!mSharedData) {
957 mSharedData = new WritableSharedMap();
959 return mSharedData;
962 already_AddRefed<ProcessMessageManager>
963 nsFrameMessageManager::GetProcessMessageManager(ErrorResult& aError) {
964 RefPtr<ProcessMessageManager> pmm;
965 if (mCallback) {
966 pmm = mCallback->GetProcessMessageManager();
968 return pmm.forget();
971 void nsFrameMessageManager::GetRemoteType(nsACString& aRemoteType,
972 ErrorResult& aError) const {
973 aRemoteType.Truncate();
974 if (mCallback) {
975 mCallback->DoGetRemoteType(aRemoteType, aError);
979 namespace {
981 struct MessageManagerReferentCount {
982 MessageManagerReferentCount() : mStrong(0), mWeakAlive(0), mWeakDead(0) {}
983 size_t mStrong;
984 size_t mWeakAlive;
985 size_t mWeakDead;
986 nsTArray<nsString> mSuspectMessages;
987 nsTHashMap<nsStringHashKey, uint32_t> mMessageCounter;
990 } // namespace
992 namespace mozilla::dom {
994 class MessageManagerReporter final : public nsIMemoryReporter {
995 ~MessageManagerReporter() = default;
997 public:
998 NS_DECL_ISUPPORTS
999 NS_DECL_NSIMEMORYREPORTER
1001 static const size_t kSuspectReferentCount = 300;
1003 protected:
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) {
1017 continue;
1020 nsString key(entry.GetKey());
1021 const uint32_t currentCount =
1022 (aReferentCount->mMessageCounter.LookupOrInsert(key, 0) +=
1023 listenerCount);
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);
1036 if (referent) {
1037 aReferentCount->mWeakAlive++;
1038 } else {
1039 aReferentCount->mWeakDead++;
1041 } else {
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) \
1059 do { \
1060 aHandleReport->Callback(""_ns, _path, nsIMemoryReporter::KIND_OTHER, \
1061 nsIMemoryReporter::UNITS_COUNT, _amount, _desc, \
1062 aData); \
1063 } while (0)
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.",
1069 aManagerType));
1070 REPORT(
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.",
1075 aManagerType));
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.",
1080 aManagerType));
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()),
1088 totalReferentCount,
1089 nsPrintfCString("A message in the %s message manager with a "
1090 "suspiciously large number of referents (symptom "
1091 "of a leak).",
1092 aManagerType));
1095 #undef REPORT
1098 static StaticRefPtr<ChromeMessageBroadcaster> sGlobalMessageManager;
1100 NS_IMETHODIMP
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);
1121 return NS_OK;
1124 } // namespace mozilla::dom
1126 already_AddRefed<ChromeMessageBroadcaster>
1127 nsFrameMessageManager::GetGlobalMessageManager() {
1128 RefPtr<ChromeMessageBroadcaster> mm;
1129 if (sGlobalMessageManager) {
1130 mm = sGlobalMessageManager;
1131 } else {
1132 sGlobalMessageManager = mm =
1133 new ChromeMessageBroadcaster(MessageManagerFlags::MM_GLOBAL);
1134 ClearOnShutdown(&sGlobalMessageManager);
1135 RegisterStrongMemoryReporter(new MessageManagerReporter());
1137 return mm.forget();
1140 nsresult NS_NewGlobalMessageManager(nsISupports** aResult) {
1141 *aResult = nsFrameMessageManager::GetGlobalMessageManager().take();
1142 return NS_OK;
1145 nsTHashMap<nsStringHashKey, nsMessageManagerScriptHolder*>*
1146 nsMessageManagerScriptExecutor::sCachedScripts = nullptr;
1147 StaticRefPtr<nsScriptCacheCleaner>
1148 nsMessageManagerScriptExecutor::sScriptCacheCleaner;
1150 void nsMessageManagerScriptExecutor::DidCreateScriptLoader() {
1151 if (!sCachedScripts) {
1152 sCachedScripts =
1153 new nsTHashMap<nsStringHashKey, nsMessageManagerScriptHolder*>;
1154 sScriptCacheCleaner = new nsScriptCacheCleaner();
1158 // static
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()) {
1163 delete iter.Data();
1164 iter.Remove();
1169 // static
1170 void nsMessageManagerScriptExecutor::Shutdown() {
1171 if (sCachedScripts) {
1172 PurgeCache();
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) {
1192 return;
1195 RefPtr<JS::Stencil> stencil;
1196 nsMessageManagerScriptHolder* holder = sCachedScripts->Get(aURL);
1197 if (holder) {
1198 stencil = holder->mStencil;
1199 } else {
1200 stencil =
1201 TryCacheLoadAndCompileScript(aURL, aRunInUniqueScope, aMessageManager);
1204 AutoEntryScript aes(aMessageManager, "message manager script load");
1205 JSContext* cx = aes.cx();
1206 if (stencil) {
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));
1213 if (script) {
1214 if (aRunInUniqueScope) {
1215 JS::Rooted<JSObject*> scope(cx);
1216 bool ok = js::ExecuteInFrameScriptEnvironment(cx, aMessageManager,
1217 script, &scope);
1218 if (ok) {
1219 // Force the scope to stay alive.
1220 mAnonymousGlobalScopes.AppendElement(scope);
1222 } else {
1223 JS::Rooted<JS::Value> rval(cx);
1224 JS::RootedVector<JSObject*> envChain(cx);
1225 if (!envChain.append(aMessageManager)) {
1226 return;
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)) {
1242 return nullptr;
1245 bool hasFlags;
1246 rv = NS_URIChainHasFlags(uri, nsIProtocolHandler::URI_IS_LOCAL_RESOURCE,
1247 &hasFlags);
1248 if (NS_FAILED(rv) || !hasFlags) {
1249 NS_WARNING("Will not load a frame script!");
1250 return nullptr;
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.
1272 AutoJSAPI jsapi;
1273 if (!jsapi.Init(isRunOnce ? aMessageManager : xpc::CompilationScope())) {
1274 return nullptr;
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);
1290 if (!stencil) {
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);
1297 if (!channel) {
1298 return nullptr;
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;
1307 if (input) {
1308 nsCString buffer;
1309 uint64_t written;
1310 if (NS_FAILED(NS_ReadInputStreamToString(input, buffer, -1, &written))) {
1311 return nullptr;
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) {
1320 return nullptr;
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)) {
1335 return nullptr;
1338 stencil = JS::CompileGlobalScriptToStencil(cx, options, srcBuf);
1339 if (!stencil) {
1340 return nullptr;
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);
1349 #ifdef DEBUG
1350 // The above shouldn't touch any options for instantiation.
1351 JS::InstantiateOptions instantiateOptions(options);
1352 instantiateOptions.assertDefault();
1353 #endif
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,
1363 isRunOnce);
1366 return stencil.forget();
1369 void nsMessageManagerScriptExecutor::Trace(const TraceCallbacks& aCallbacks,
1370 void* aClosure) {
1371 for (size_t i = 0, length = mAnonymousGlobalScopes.Length(); i < length;
1372 ++i) {
1373 aCallbacks.Trace(&mAnonymousGlobalScopes[i], "mAnonymousGlobalScopes[i]",
1374 aClosure);
1378 void nsMessageManagerScriptExecutor::Unlink() {
1379 ImplCycleCollectionUnlink(mAnonymousGlobalScopes);
1382 bool nsMessageManagerScriptExecutor::Init() {
1383 DidCreateScriptLoader();
1384 return true;
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 =
1396 nullptr;
1397 ParentProcessMessageManager* nsFrameMessageManager::sParentProcessManager =
1398 nullptr;
1399 nsFrameMessageManager* nsFrameMessageManager::sSameProcessParentManager =
1400 nullptr;
1402 class nsAsyncMessageToSameProcessChild : public nsSameProcessAsyncMessageBase,
1403 public Runnable {
1404 public:
1405 nsAsyncMessageToSameProcessChild()
1406 : mozilla::Runnable("nsAsyncMessageToSameProcessChild") {}
1407 NS_IMETHOD Run() override {
1408 nsFrameMessageManager* ppm =
1409 nsFrameMessageManager::GetChildProcessManager();
1410 ReceiveMessage(ppm, nullptr, ppm);
1411 return NS_OK;
1416 * Send messages to an imaginary child process in a single-process scenario.
1418 class SameParentProcessMessageManagerCallback : public MessageManagerCallback {
1419 public:
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);
1432 return true;
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)) {
1442 return rv;
1444 rv = NS_DispatchToCurrentThread(ev);
1445 if (NS_FAILED(rv)) {
1446 return rv;
1448 return NS_OK;
1453 * Send messages to the parent process.
1455 class ChildProcessMessageManagerCallback : public MessageManagerCallback {
1456 public:
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();
1468 if (!cc) {
1469 return true;
1471 ClonedMessageData data;
1472 if (!BuildClonedMessageData(aData, data)) {
1473 return false;
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();
1481 if (!cc) {
1482 return NS_OK;
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;
1492 return NS_OK;
1496 class nsAsyncMessageToSameProcessParent
1497 : public nsSameProcessAsyncMessageBase,
1498 public SameProcessMessageQueue::Runnable {
1499 public:
1500 nsAsyncMessageToSameProcessParent() = default;
1501 nsresult HandleMessage() override {
1502 nsFrameMessageManager* ppm =
1503 nsFrameMessageManager::sSameProcessParentManager;
1504 ReceiveMessage(ppm, nullptr, ppm);
1505 return NS_OK;
1510 * Send messages to the imaginary parent process in a single-process scenario.
1512 class SameChildProcessMessageManagerCallback : public MessageManagerCallback {
1513 public:
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();
1525 queue->Flush();
1527 if (nsFrameMessageManager::sSameProcessParentManager) {
1528 RefPtr<nsFrameMessageManager> ppm =
1529 nsFrameMessageManager::sSameProcessParentManager;
1530 ppm->ReceiveMessage(ppm, nullptr, aMessage, true, &aData, aRetVal,
1531 IgnoreErrors());
1533 return true;
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)) {
1544 return rv;
1546 queue->Push(ev);
1547 return NS_OK;
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.
1559 mm.forget(aResult);
1560 return NS_OK;
1563 ProcessMessageManager* nsFrameMessageManager::NewProcessMessageManager(
1564 bool aIsRemote) {
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;
1573 if (aIsRemote) {
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);
1578 } else {
1579 mm =
1580 new ProcessMessageManager(new SameParentProcessMessageManagerCallback(),
1581 nsFrameMessageManager::sParentProcessManager,
1582 MessageManagerFlags::MM_OWNSCALLBACK);
1583 mm->SetOsPid(base::GetCurrentProcId());
1584 sSameProcessParentManager = mm;
1586 return 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();
1596 } else {
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()
1625 #ifdef DEBUG
1626 : mCalledInit(false)
1627 #endif
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;
1640 #ifdef DEBUG
1641 mCalledInit = true;
1642 #endif
1644 return NS_OK;
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);
1652 if (aManager) {
1653 RefPtr<nsFrameMessageManager> mm = aManager;
1654 mm->ReceiveMessage(aTarget, aTargetFrameLoader, mMessage, false, &mData,
1655 nullptr, IgnoreErrors());