Bug 1812499 [wpt PR 38184] - Simplify handling of name-to-subdir mapping in canvas...
[gecko.git] / dom / base / nsFrameMessageManager.cpp
blobb2a65db5ee33f6f1d67f3706029e433e831faaa9
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/Wrapper.h"
33 #include "jsapi.h"
34 #include "jsfriendapi.h"
35 #include "mozilla/AlreadyAddRefed.h"
36 #include "mozilla/Assertions.h"
37 #include "mozilla/ClearOnShutdown.h"
38 #include "mozilla/ErrorResult.h"
39 #include "mozilla/MacroForEach.h"
40 #include "mozilla/NotNull.h"
41 #include "mozilla/OwningNonNull.h"
42 #include "mozilla/RefPtr.h"
43 #include "mozilla/ScriptPreloader.h"
44 #include "mozilla/Services.h"
45 #include "mozilla/StaticPtr.h"
46 #include "mozilla/Telemetry.h"
47 #include "mozilla/TelemetryHistogramEnums.h"
48 #include "mozilla/TimeStamp.h"
49 #include "mozilla/TypedEnumBits.h"
50 #include "mozilla/UniquePtr.h"
51 #include "mozilla/dom/AutoEntryScript.h"
52 #include "mozilla/dom/BindingDeclarations.h"
53 #include "mozilla/dom/CallbackObject.h"
54 #include "mozilla/dom/ChildProcessMessageManager.h"
55 #include "mozilla/dom/ChromeMessageBroadcaster.h"
56 #include "mozilla/dom/ContentProcessMessageManager.h"
57 #include "mozilla/dom/DOMTypes.h"
58 #include "mozilla/dom/MessageBroadcaster.h"
59 #include "mozilla/dom/MessageListenerManager.h"
60 #include "mozilla/dom/MessageManagerBinding.h"
61 #include "mozilla/dom/MessagePort.h"
62 #include "mozilla/dom/ParentProcessMessageManager.h"
63 #include "mozilla/dom/ProcessMessageManager.h"
64 #include "mozilla/dom/RootedDictionary.h"
65 #include "mozilla/dom/SameProcessMessageQueue.h"
66 #include "mozilla/dom/ScriptLoader.h"
67 #include "mozilla/dom/ScriptSettings.h"
68 #include "mozilla/dom/ToJSValue.h"
69 #include "mozilla/dom/MessageManagerCallback.h"
70 #include "mozilla/dom/ipc/SharedMap.h"
71 #include "mozilla/dom/ipc/StructuredCloneData.h"
72 #include "mozilla/scache/StartupCacheUtils.h"
73 #include "nsASCIIMask.h"
74 #include "nsBaseHashtable.h"
75 #include "nsCOMPtr.h"
76 #include "nsClassHashtable.h"
77 #include "nsComponentManagerUtils.h"
78 #include "nsContentUtils.h"
79 #include "nsCycleCollectionNoteChild.h"
80 #include "nsCycleCollectionParticipant.h"
81 #include "nsTHashMap.h"
82 #include "nsDebug.h"
83 #include "nsError.h"
84 #include "nsHashKeys.h"
85 #include "nsIChannel.h"
86 #include "nsIConsoleService.h"
87 #include "nsIContentPolicy.h"
88 #include "nsIInputStream.h"
89 #include "nsILoadInfo.h"
90 #include "nsIMemoryReporter.h"
91 #include "nsIMessageManager.h"
92 #include "nsIObserver.h"
93 #include "nsIObserverService.h"
94 #include "nsIProtocolHandler.h"
95 #include "nsIScriptError.h"
96 #include "nsISupports.h"
97 #include "nsISupportsUtils.h"
98 #include "nsIURI.h"
99 #include "nsIWeakReferenceUtils.h"
100 #include "nsIXPConnect.h"
101 #include "nsJSUtils.h"
102 #include "nsLiteralString.h"
103 #include "nsNetUtil.h"
104 #include "nsPrintfCString.h"
105 #include "nsQueryObject.h"
106 #include "nsServiceManagerUtils.h"
107 #include "nsString.h"
108 #include "nsStringFlags.h"
109 #include "nsStringFwd.h"
110 #include "nsTArray.h"
111 #include "nsTLiteralString.h"
112 #include "nsTObserverArray.h"
113 #include "nsTPromiseFlatString.h"
114 #include "nsTStringRepr.h"
115 #include "nsThreadUtils.h"
116 #include "nsXULAppAPI.h"
117 #include "nscore.h"
118 #include "xpcpublic.h"
120 #ifdef XP_WIN
121 # if defined(SendMessage)
122 # undef SendMessage
123 # endif
124 #endif
126 #ifdef FUZZING
127 # include "MessageManagerFuzzer.h"
128 #endif
130 using namespace mozilla;
131 using namespace mozilla::dom;
132 using namespace mozilla::dom::ipc;
134 struct FrameMessageMarker {
135 static constexpr Span<const char> MarkerTypeName() {
136 return MakeStringSpan("FrameMessage");
138 static void StreamJSONMarkerData(baseprofiler::SpliceableJSONWriter& aWriter,
139 const ProfilerString16View& aMessageName,
140 bool aIsSync) {
141 aWriter.StringProperty("name", NS_ConvertUTF16toUTF8(aMessageName));
142 aWriter.BoolProperty("sync", aIsSync);
144 static MarkerSchema MarkerTypeDisplay() {
145 using MS = MarkerSchema;
146 MS schema{MS::Location::MarkerChart, MS::Location::MarkerTable};
147 schema.AddKeyLabelFormatSearchable(
148 "name", "Message Name", MS::Format::String, MS::Searchable::Searchable);
149 schema.AddKeyLabelFormat("sync", "Sync", MS::Format::String);
150 schema.SetTooltipLabel("FrameMessage - {marker.name}");
151 schema.SetTableLabel("{marker.name} - {marker.data.name}");
152 return schema;
156 #define CACHE_PREFIX(type) "mm/" type
158 nsFrameMessageManager::nsFrameMessageManager(MessageManagerCallback* aCallback,
159 MessageManagerFlags aFlags)
160 : mChrome(aFlags & MessageManagerFlags::MM_CHROME),
161 mGlobal(aFlags & MessageManagerFlags::MM_GLOBAL),
162 mIsProcessManager(aFlags & MessageManagerFlags::MM_PROCESSMANAGER),
163 mIsBroadcaster(aFlags & MessageManagerFlags::MM_BROADCASTER),
164 mOwnsCallback(aFlags & MessageManagerFlags::MM_OWNSCALLBACK),
165 mHandlingMessage(false),
166 mClosed(false),
167 mDisconnected(false),
168 mCallback(aCallback) {
169 NS_ASSERTION(!mIsBroadcaster || !mCallback,
170 "Broadcasters cannot have callbacks!");
171 if (mOwnsCallback) {
172 mOwnedCallback = WrapUnique(aCallback);
176 nsFrameMessageManager::~nsFrameMessageManager() {
177 for (int32_t i = mChildManagers.Length(); i > 0; --i) {
178 mChildManagers[i - 1]->Disconnect(false);
180 if (mIsProcessManager) {
181 if (this == sParentProcessManager) {
182 sParentProcessManager = nullptr;
184 if (this == sChildProcessManager) {
185 sChildProcessManager = nullptr;
186 delete mozilla::dom::SameProcessMessageQueue::Get();
188 if (this == sSameProcessParentManager) {
189 sSameProcessParentManager = nullptr;
194 inline void ImplCycleCollectionTraverse(
195 nsCycleCollectionTraversalCallback& aCallback,
196 nsMessageListenerInfo& aField, const char* aName, uint32_t aFlags = 0) {
197 ImplCycleCollectionTraverse(aCallback, aField.mStrongListener, aName, aFlags);
198 ImplCycleCollectionTraverse(aCallback, aField.mWeakListener, aName, aFlags);
201 NS_IMPL_CYCLE_COLLECTION_CLASS(nsFrameMessageManager)
203 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsFrameMessageManager)
204 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mListeners)
205 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChildManagers)
206 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSharedData)
207 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
209 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsFrameMessageManager)
210 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mInitialProcessData)
211 NS_IMPL_CYCLE_COLLECTION_TRACE_END
213 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsFrameMessageManager)
214 NS_IMPL_CYCLE_COLLECTION_UNLINK(mListeners)
215 for (int32_t i = tmp->mChildManagers.Length(); i > 0; --i) {
216 tmp->mChildManagers[i - 1]->Disconnect(false);
218 NS_IMPL_CYCLE_COLLECTION_UNLINK(mChildManagers)
219 NS_IMPL_CYCLE_COLLECTION_UNLINK(mSharedData)
220 tmp->mInitialProcessData.setNull();
221 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
223 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsFrameMessageManager)
224 NS_INTERFACE_MAP_ENTRY(nsISupports)
226 /* Message managers in child process implement nsIMessageSender.
227 Message managers in the chrome process are
228 either broadcasters (if they have subordinate/child message
229 managers) or they're simple message senders. */
230 NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIMessageSender,
231 !mChrome || !mIsBroadcaster)
233 NS_INTERFACE_MAP_END
235 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsFrameMessageManager)
236 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsFrameMessageManager)
238 void MessageManagerCallback::DoGetRemoteType(nsACString& aRemoteType,
239 ErrorResult& aError) const {
240 aRemoteType.Truncate();
241 mozilla::dom::ProcessMessageManager* parent = GetProcessMessageManager();
242 if (!parent) {
243 return;
246 parent->GetRemoteType(aRemoteType, aError);
249 bool MessageManagerCallback::BuildClonedMessageData(
250 StructuredCloneData& aData, ClonedMessageData& aClonedData) {
251 return aData.BuildClonedMessageData(aClonedData);
254 void mozilla::dom::ipc::UnpackClonedMessageData(
255 const ClonedMessageData& aClonedData, StructuredCloneData& aData) {
256 aData.BorrowFromClonedMessageData(aClonedData);
259 void nsFrameMessageManager::AddMessageListener(const nsAString& aMessageName,
260 MessageListener& aListener,
261 bool aListenWhenClosed,
262 ErrorResult& aError) {
263 auto* const listeners = mListeners.GetOrInsertNew(aMessageName);
264 uint32_t len = listeners->Length();
265 for (uint32_t i = 0; i < len; ++i) {
266 MessageListener* strongListener = listeners->ElementAt(i).mStrongListener;
267 if (strongListener && *strongListener == aListener) {
268 return;
272 nsMessageListenerInfo* entry = listeners->AppendElement();
273 entry->mStrongListener = &aListener;
274 entry->mListenWhenClosed = aListenWhenClosed;
277 void nsFrameMessageManager::RemoveMessageListener(const nsAString& aMessageName,
278 MessageListener& aListener,
279 ErrorResult& aError) {
280 nsAutoTObserverArray<nsMessageListenerInfo, 1>* listeners =
281 mListeners.Get(aMessageName);
282 if (listeners) {
283 uint32_t len = listeners->Length();
284 for (uint32_t i = 0; i < len; ++i) {
285 MessageListener* strongListener = listeners->ElementAt(i).mStrongListener;
286 if (strongListener && *strongListener == aListener) {
287 listeners->RemoveElementAt(i);
288 return;
294 static already_AddRefed<nsISupports> ToXPCOMMessageListener(
295 MessageListener& aListener) {
296 return CallbackObjectHolder<mozilla::dom::MessageListener, nsISupports>(
297 &aListener)
298 .ToXPCOMCallback();
301 void nsFrameMessageManager::AddWeakMessageListener(
302 const nsAString& aMessageName, MessageListener& aListener,
303 ErrorResult& aError) {
304 nsCOMPtr<nsISupports> listener(ToXPCOMMessageListener(aListener));
305 nsWeakPtr weak = do_GetWeakReference(listener);
306 if (!weak) {
307 aError.Throw(NS_ERROR_NO_INTERFACE);
308 return;
311 #ifdef DEBUG
312 // It's technically possible that one object X could give two different
313 // nsIWeakReference*'s when you do_GetWeakReference(X). We really don't want
314 // this to happen; it will break e.g. RemoveWeakMessageListener. So let's
315 // check that we're not getting ourselves into that situation.
316 nsCOMPtr<nsISupports> canonical = do_QueryInterface(listener);
317 for (const auto& entry : mListeners) {
318 nsAutoTObserverArray<nsMessageListenerInfo, 1>* listeners = entry.GetWeak();
319 uint32_t count = listeners->Length();
320 for (uint32_t i = 0; i < count; i++) {
321 nsWeakPtr weakListener = listeners->ElementAt(i).mWeakListener;
322 if (weakListener) {
323 nsCOMPtr<nsISupports> otherCanonical = do_QueryReferent(weakListener);
324 MOZ_ASSERT((canonical == otherCanonical) == (weak == weakListener));
328 #endif
330 auto* const listeners = mListeners.GetOrInsertNew(aMessageName);
331 uint32_t len = listeners->Length();
332 for (uint32_t i = 0; i < len; ++i) {
333 if (listeners->ElementAt(i).mWeakListener == weak) {
334 return;
338 nsMessageListenerInfo* entry = listeners->AppendElement();
339 entry->mWeakListener = weak;
340 entry->mListenWhenClosed = false;
343 void nsFrameMessageManager::RemoveWeakMessageListener(
344 const nsAString& aMessageName, MessageListener& aListener,
345 ErrorResult& aError) {
346 nsCOMPtr<nsISupports> listener(ToXPCOMMessageListener(aListener));
347 nsWeakPtr weak = do_GetWeakReference(listener);
348 if (!weak) {
349 aError.Throw(NS_ERROR_NO_INTERFACE);
350 return;
353 nsAutoTObserverArray<nsMessageListenerInfo, 1>* listeners =
354 mListeners.Get(aMessageName);
355 if (!listeners) {
356 return;
359 uint32_t len = listeners->Length();
360 for (uint32_t i = 0; i < len; ++i) {
361 if (listeners->ElementAt(i).mWeakListener == weak) {
362 listeners->RemoveElementAt(i);
363 return;
368 void nsFrameMessageManager::LoadScript(const nsAString& aURL,
369 bool aAllowDelayedLoad,
370 bool aRunInGlobalScope,
371 ErrorResult& aError) {
372 if (aAllowDelayedLoad) {
373 // Cache for future windows or frames
374 mPendingScripts.AppendElement(aURL);
375 mPendingScriptsGlobalStates.AppendElement(aRunInGlobalScope);
378 if (mCallback) {
379 #ifdef DEBUG_smaug
380 printf("Will load %s \n", NS_ConvertUTF16toUTF8(aURL).get());
381 #endif
382 if (!mCallback->DoLoadMessageManagerScript(aURL, aRunInGlobalScope)) {
383 aError.Throw(NS_ERROR_FAILURE);
384 return;
388 for (uint32_t i = 0; i < mChildManagers.Length(); ++i) {
389 RefPtr<nsFrameMessageManager> mm = mChildManagers[i];
390 if (mm) {
391 // Use false here, so that child managers don't cache the script, which
392 // is already cached in the parent.
393 mm->LoadScript(aURL, false, aRunInGlobalScope, IgnoreErrors());
398 void nsFrameMessageManager::RemoveDelayedScript(const nsAString& aURL) {
399 for (uint32_t i = 0; i < mPendingScripts.Length(); ++i) {
400 if (mPendingScripts[i] == aURL) {
401 mPendingScripts.RemoveElementAt(i);
402 mPendingScriptsGlobalStates.RemoveElementAt(i);
403 break;
408 void nsFrameMessageManager::GetDelayedScripts(
409 JSContext* aCx, nsTArray<nsTArray<JS::Value>>& aList, ErrorResult& aError) {
410 // Frame message managers may return an incomplete list because scripts
411 // that were loaded after it was connected are not added to the list.
412 if (!IsGlobal() && !IsBroadcaster()) {
413 NS_WARNING(
414 "Cannot retrieve list of pending frame scripts for frame"
415 "message managers as it may be incomplete");
416 aError.Throw(NS_ERROR_NOT_IMPLEMENTED);
417 return;
420 aError.MightThrowJSException();
422 aList.SetCapacity(mPendingScripts.Length());
423 for (uint32_t i = 0; i < mPendingScripts.Length(); ++i) {
424 JS::Rooted<JS::Value> url(aCx);
425 if (!ToJSValue(aCx, mPendingScripts[i], &url)) {
426 aError.NoteJSContextException(aCx);
427 return;
430 nsTArray<JS::Value>* array = aList.AppendElement(2);
431 array->AppendElement(url);
432 array->AppendElement(JS::BooleanValue(mPendingScriptsGlobalStates[i]));
436 /* static */
437 bool nsFrameMessageManager::GetParamsForMessage(JSContext* aCx,
438 const JS::Value& aValue,
439 const JS::Value& aTransfer,
440 StructuredCloneData& aData) {
441 // First try to use structured clone on the whole thing.
442 JS::Rooted<JS::Value> v(aCx, aValue);
443 JS::Rooted<JS::Value> t(aCx, aTransfer);
444 ErrorResult rv;
445 aData.Write(aCx, v, t, JS::CloneDataPolicy(), rv);
446 if (!rv.Failed()) {
447 return true;
450 rv.SuppressException();
451 JS_ClearPendingException(aCx);
453 nsCOMPtr<nsIConsoleService> console(
454 do_GetService(NS_CONSOLESERVICE_CONTRACTID));
455 if (console) {
456 nsAutoString filename;
457 uint32_t lineno = 0, column = 0;
458 nsJSUtils::GetCallingLocation(aCx, filename, &lineno, &column);
459 nsCOMPtr<nsIScriptError> error(
460 do_CreateInstance(NS_SCRIPTERROR_CONTRACTID));
461 error->Init(
462 u"Sending message that cannot be cloned. Are "
463 "you trying to send an XPCOM object?"_ns,
464 filename, u""_ns, lineno, column, nsIScriptError::warningFlag,
465 "chrome javascript"_ns, false /* from private window */,
466 true /* from chrome context */);
467 console->LogMessage(error);
470 // Not clonable, try JSON
471 // Bug 1749037 - This is ugly but currently structured cloning doesn't handle
472 // properly cases when interface is implemented in JS and used
473 // as a dictionary.
474 nsAutoString json;
475 NS_ENSURE_TRUE(nsContentUtils::StringifyJSON(aCx, &v, json), false);
476 NS_ENSURE_TRUE(!json.IsEmpty(), false);
478 JS::Rooted<JS::Value> val(aCx, JS::NullValue());
479 NS_ENSURE_TRUE(JS_ParseJSON(aCx, static_cast<const char16_t*>(json.get()),
480 json.Length(), &val),
481 false);
483 aData.Write(aCx, val, rv);
484 if (NS_WARN_IF(rv.Failed())) {
485 rv.SuppressException();
486 return false;
489 return true;
492 static bool sSendingSyncMessage = false;
494 void nsFrameMessageManager::SendSyncMessage(JSContext* aCx,
495 const nsAString& aMessageName,
496 JS::Handle<JS::Value> aObj,
497 nsTArray<JS::Value>& aResult,
498 ErrorResult& aError) {
499 NS_ASSERTION(!IsGlobal(), "Should not call SendSyncMessage in chrome");
500 NS_ASSERTION(!IsBroadcaster(), "Should not call SendSyncMessage in chrome");
501 NS_ASSERTION(!GetParentManager(),
502 "Should not have parent manager in content!");
504 AUTO_PROFILER_LABEL_DYNAMIC_LOSSY_NSSTRING(
505 "nsFrameMessageManager::SendMessage", OTHER, aMessageName);
506 profiler_add_marker("SendSyncMessage", geckoprofiler::category::IPC, {},
507 FrameMessageMarker{}, aMessageName, true);
509 if (sSendingSyncMessage) {
510 // No kind of blocking send should be issued on top of a sync message.
511 aError.Throw(NS_ERROR_UNEXPECTED);
512 return;
515 StructuredCloneData data;
516 if (!aObj.isUndefined() &&
517 !GetParamsForMessage(aCx, aObj, JS::UndefinedHandleValue, data)) {
518 aError.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
519 return;
522 #ifdef FUZZING
523 if (data.DataLength() > 0) {
524 MessageManagerFuzzer::TryMutate(aCx, aMessageName, &data,
525 JS::UndefinedHandleValue);
527 #endif
529 if (!mCallback) {
530 aError.Throw(NS_ERROR_NOT_INITIALIZED);
531 return;
534 nsTArray<StructuredCloneData> retval;
536 TimeStamp start = TimeStamp::Now();
537 sSendingSyncMessage = true;
538 bool ok = mCallback->DoSendBlockingMessage(aMessageName, data, &retval);
539 sSendingSyncMessage = false;
541 uint32_t latencyMs = round((TimeStamp::Now() - start).ToMilliseconds());
542 if (latencyMs >= kMinTelemetrySyncMessageManagerLatencyMs) {
543 NS_ConvertUTF16toUTF8 messageName(aMessageName);
544 // NOTE: We need to strip digit characters from the message name in order to
545 // avoid a large number of buckets due to generated names from addons (such
546 // as "ublock:sb:{N}"). See bug 1348113 comment 10.
547 messageName.StripTaggedASCII(ASCIIMask::Mask0to9());
548 Telemetry::Accumulate(Telemetry::IPC_SYNC_MESSAGE_MANAGER_LATENCY_MS,
549 messageName, latencyMs);
552 if (!ok) {
553 return;
556 uint32_t len = retval.Length();
557 aResult.SetCapacity(len);
558 for (uint32_t i = 0; i < len; ++i) {
559 JS::Rooted<JS::Value> ret(aCx);
560 retval[i].Read(aCx, &ret, aError);
561 if (aError.Failed()) {
562 MOZ_ASSERT(false, "Unable to read structured clone in SendMessage");
563 return;
565 aResult.AppendElement(ret);
569 nsresult nsFrameMessageManager::DispatchAsyncMessageInternal(
570 JSContext* aCx, const nsAString& aMessage, StructuredCloneData& aData) {
571 if (mIsBroadcaster) {
572 uint32_t len = mChildManagers.Length();
573 for (uint32_t i = 0; i < len; ++i) {
574 mChildManagers[i]->DispatchAsyncMessageInternal(aCx, aMessage, aData);
576 return NS_OK;
579 if (!mCallback) {
580 return NS_ERROR_NOT_INITIALIZED;
583 nsresult rv = mCallback->DoSendAsyncMessage(aMessage, aData);
584 if (NS_FAILED(rv)) {
585 return rv;
587 return NS_OK;
590 void nsFrameMessageManager::DispatchAsyncMessage(
591 JSContext* aCx, const nsAString& aMessageName, JS::Handle<JS::Value> aObj,
592 JS::Handle<JS::Value> aTransfers, ErrorResult& aError) {
593 StructuredCloneData data;
594 if (!aObj.isUndefined() &&
595 !GetParamsForMessage(aCx, aObj, aTransfers, data)) {
596 aError.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
597 return;
600 profiler_add_marker("SendAsyncMessage", geckoprofiler::category::IPC, {},
601 FrameMessageMarker{}, aMessageName, false);
603 #ifdef FUZZING
604 if (data.DataLength()) {
605 MessageManagerFuzzer::TryMutate(aCx, aMessageName, &data, aTransfers);
607 #endif
609 aError = DispatchAsyncMessageInternal(aCx, aMessageName, data);
612 class MMListenerRemover {
613 public:
614 explicit MMListenerRemover(nsFrameMessageManager* aMM)
615 : mWasHandlingMessage(aMM->mHandlingMessage), mMM(aMM) {
616 mMM->mHandlingMessage = true;
618 ~MMListenerRemover() {
619 if (!mWasHandlingMessage) {
620 mMM->mHandlingMessage = false;
621 if (mMM->mDisconnected) {
622 mMM->mListeners.Clear();
627 bool mWasHandlingMessage;
628 RefPtr<nsFrameMessageManager> mMM;
631 void nsFrameMessageManager::ReceiveMessage(
632 nsISupports* aTarget, nsFrameLoader* aTargetFrameLoader, bool aTargetClosed,
633 const nsAString& aMessage, bool aIsSync, StructuredCloneData* aCloneData,
634 nsTArray<StructuredCloneData>* aRetVal, ErrorResult& aError) {
635 MOZ_ASSERT(aTarget);
636 profiler_add_marker("ReceiveMessage", geckoprofiler::category::IPC, {},
637 FrameMessageMarker{}, aMessage, aIsSync);
639 nsAutoTObserverArray<nsMessageListenerInfo, 1>* listeners =
640 mListeners.Get(aMessage);
641 if (listeners) {
642 MMListenerRemover lr(this);
644 nsAutoTObserverArray<nsMessageListenerInfo, 1>::EndLimitedIterator iter(
645 *listeners);
646 while (iter.HasMore()) {
647 nsMessageListenerInfo& listener = iter.GetNext();
648 // Remove mListeners[i] if it's an expired weak listener.
649 nsCOMPtr<nsISupports> weakListener;
650 if (listener.mWeakListener) {
651 weakListener = do_QueryReferent(listener.mWeakListener);
652 if (!weakListener) {
653 iter.Remove();
654 continue;
658 if (!listener.mListenWhenClosed && aTargetClosed) {
659 continue;
662 JS::RootingContext* rcx = RootingCx();
663 JS::Rooted<JSObject*> object(rcx);
664 JS::Rooted<JSObject*> objectGlobal(rcx);
666 RefPtr<MessageListener> webIDLListener;
667 if (!weakListener) {
668 webIDLListener = listener.mStrongListener;
669 object = webIDLListener->CallbackOrNull();
670 objectGlobal = webIDLListener->CallbackGlobalOrNull();
671 } else {
672 nsCOMPtr<nsIXPConnectWrappedJS> wrappedJS =
673 do_QueryInterface(weakListener);
674 if (!wrappedJS) {
675 continue;
678 object = wrappedJS->GetJSObject();
679 objectGlobal = wrappedJS->GetJSObjectGlobal();
682 if (!object) {
683 continue;
686 AutoEntryScript aes(js::UncheckedUnwrap(object),
687 "message manager handler");
688 JSContext* cx = aes.cx();
690 // We passed the unwrapped object to AutoEntryScript so we now need to
691 // enter the realm of the global object that represents the realm of our
692 // callback.
693 JSAutoRealm ar(cx, objectGlobal);
695 RootedDictionary<ReceiveMessageArgument> argument(cx);
697 JS::Rooted<JS::Value> json(cx, JS::NullValue());
698 if (aCloneData && aCloneData->DataLength()) {
699 aCloneData->Read(cx, &json, aError);
700 if (NS_WARN_IF(aError.Failed())) {
701 aError.SuppressException();
702 JS_ClearPendingException(cx);
703 return;
706 argument.mData = json;
707 argument.mJson = json;
709 // Get cloned MessagePort from StructuredCloneData.
710 if (aCloneData) {
711 Sequence<OwningNonNull<MessagePort>> ports;
712 if (!aCloneData->TakeTransferredPortsAsSequence(ports)) {
713 aError.Throw(NS_ERROR_FAILURE);
714 return;
716 argument.mPorts.Construct(std::move(ports));
719 argument.mName = aMessage;
720 argument.mSync = aIsSync;
721 argument.mTarget = aTarget;
722 if (aTargetFrameLoader) {
723 argument.mTargetFrameLoader.Construct(*aTargetFrameLoader);
726 JS::Rooted<JS::Value> thisValue(cx, JS::UndefinedValue());
728 if (JS::IsCallable(object)) {
729 // A small hack to get 'this' value right on content side where
730 // messageManager is wrapped in BrowserChildMessageManager's global.
731 nsCOMPtr<nsISupports> defaultThisValue;
732 if (mChrome) {
733 defaultThisValue = do_QueryObject(this);
734 } else {
735 defaultThisValue = aTarget;
737 js::AssertSameCompartment(cx, object);
738 aError = nsContentUtils::WrapNative(cx, defaultThisValue, &thisValue);
739 if (aError.Failed()) {
740 return;
744 JS::Rooted<JS::Value> rval(cx, JS::UndefinedValue());
745 if (webIDLListener) {
746 webIDLListener->ReceiveMessage(thisValue, argument, &rval, aError);
747 if (aError.Failed()) {
748 // At this point the call to ReceiveMessage will have reported any
749 // exceptions (we kept the default of eReportExceptions). We suppress
750 // the failure in the ErrorResult and continue.
751 aError.SuppressException();
752 continue;
754 } else {
755 JS::Rooted<JS::Value> funval(cx);
756 if (JS::IsCallable(object)) {
757 // If the listener is a JS function:
758 funval.setObject(*object);
759 } else {
760 // If the listener is a JS object which has receiveMessage function:
761 if (!JS_GetProperty(cx, object, "receiveMessage", &funval) ||
762 !funval.isObject()) {
763 aError.Throw(NS_ERROR_UNEXPECTED);
764 return;
767 // Check if the object is even callable.
768 if (!JS::IsCallable(&funval.toObject())) {
769 aError.Throw(NS_ERROR_UNEXPECTED);
770 return;
772 thisValue.setObject(*object);
775 JS::Rooted<JS::Value> argv(cx);
776 if (!ToJSValue(cx, argument, &argv)) {
777 aError.Throw(NS_ERROR_UNEXPECTED);
778 return;
782 JS::Rooted<JSObject*> thisObject(cx, thisValue.toObjectOrNull());
783 js::AssertSameCompartment(cx, thisObject);
784 if (!JS_CallFunctionValue(cx, thisObject, funval,
785 JS::HandleValueArray(argv), &rval)) {
786 // Because the AutoEntryScript is inside the loop this continue will
787 // make us report any exceptions (after which we'll move on to the
788 // next listener).
789 continue;
794 if (aRetVal) {
795 StructuredCloneData* data = aRetVal->AppendElement();
796 data->InitScope(JS::StructuredCloneScope::DifferentProcess);
797 data->Write(cx, rval, aError);
798 if (NS_WARN_IF(aError.Failed())) {
799 aRetVal->RemoveLastElement();
800 nsString msg =
801 aMessage + nsLiteralString(
802 u": message reply cannot be cloned. Are "
803 "you trying to send an XPCOM object?");
805 nsCOMPtr<nsIConsoleService> console(
806 do_GetService(NS_CONSOLESERVICE_CONTRACTID));
807 if (console) {
808 nsCOMPtr<nsIScriptError> error(
809 do_CreateInstance(NS_SCRIPTERROR_CONTRACTID));
810 error->Init(msg, u""_ns, u""_ns, 0, 0, nsIScriptError::warningFlag,
811 "chrome javascript"_ns, false /* from private window */,
812 true /* from chrome context */);
813 console->LogMessage(error);
816 JS_ClearPendingException(cx);
817 continue;
823 RefPtr<nsFrameMessageManager> kungFuDeathGrip = GetParentManager();
824 if (kungFuDeathGrip) {
825 kungFuDeathGrip->ReceiveMessage(aTarget, aTargetFrameLoader, aTargetClosed,
826 aMessage, aIsSync, aCloneData, aRetVal,
827 aError);
831 void nsFrameMessageManager::LoadPendingScripts(
832 nsFrameMessageManager* aManager, nsFrameMessageManager* aChildMM) {
833 // We have parent manager if we're a message broadcaster.
834 // In that case we want to load the pending scripts from all parent
835 // message managers in the hierarchy. Process the parent first so
836 // that pending scripts higher up in the hierarchy are loaded before others.
837 nsFrameMessageManager* parentManager = aManager->GetParentManager();
838 if (parentManager) {
839 LoadPendingScripts(parentManager, aChildMM);
842 for (uint32_t i = 0; i < aManager->mPendingScripts.Length(); ++i) {
843 aChildMM->LoadScript(aManager->mPendingScripts[i], false,
844 aManager->mPendingScriptsGlobalStates[i],
845 IgnoreErrors());
849 void nsFrameMessageManager::LoadPendingScripts() {
850 RefPtr<nsFrameMessageManager> kungfuDeathGrip = this;
851 LoadPendingScripts(this, this);
854 void nsFrameMessageManager::SetCallback(MessageManagerCallback* aCallback) {
855 MOZ_ASSERT(!mIsBroadcaster || !mCallback,
856 "Broadcasters cannot have callbacks!");
857 if (aCallback && mCallback != aCallback) {
858 mCallback = aCallback;
859 if (mOwnsCallback) {
860 mOwnedCallback = WrapUnique(aCallback);
865 void nsFrameMessageManager::Close() {
866 if (!mClosed) {
867 if (nsCOMPtr<nsIObserverService> obs =
868 mozilla::services::GetObserverService()) {
869 obs->NotifyWhenScriptSafe(this, "message-manager-close", nullptr);
872 mClosed = true;
873 mCallback = nullptr;
874 mOwnedCallback = nullptr;
877 void nsFrameMessageManager::Disconnect(bool aRemoveFromParent) {
878 // Notify message-manager-close if we haven't already.
879 Close();
881 if (!mDisconnected) {
882 if (nsCOMPtr<nsIObserverService> obs =
883 mozilla::services::GetObserverService()) {
884 obs->NotifyWhenScriptSafe(this, "message-manager-disconnect", nullptr);
888 ClearParentManager(aRemoveFromParent);
890 mDisconnected = true;
891 if (!mHandlingMessage) {
892 mListeners.Clear();
896 void nsFrameMessageManager::SetInitialProcessData(
897 JS::Handle<JS::Value> aInitialData) {
898 MOZ_ASSERT(!mChrome);
899 MOZ_ASSERT(mIsProcessManager);
900 MOZ_ASSERT(aInitialData.isObject());
901 mInitialProcessData = aInitialData;
904 void nsFrameMessageManager::GetInitialProcessData(
905 JSContext* aCx, JS::MutableHandle<JS::Value> aInitialProcessData,
906 ErrorResult& aError) {
907 MOZ_ASSERT(mIsProcessManager);
908 MOZ_ASSERT_IF(mChrome, IsBroadcaster());
910 JS::Rooted<JS::Value> init(aCx, mInitialProcessData);
911 if (mChrome && init.isUndefined()) {
912 // We create the initial object in the junk scope. If we created it in a
913 // normal realm, that realm would leak until shutdown.
914 JS::Rooted<JSObject*> global(aCx, xpc::PrivilegedJunkScope());
915 JSAutoRealm ar(aCx, global);
917 JS::Rooted<JSObject*> obj(aCx, JS_NewPlainObject(aCx));
918 if (!obj) {
919 aError.NoteJSContextException(aCx);
920 return;
923 mInitialProcessData.setObject(*obj);
924 init.setObject(*obj);
927 if (!mChrome && XRE_IsParentProcess()) {
928 // This is the cpmm in the parent process. We should use the same object as
929 // the ppmm. Create it first through do_GetService and use the cached
930 // pointer in sParentProcessManager.
931 nsCOMPtr<nsISupports> ppmm =
932 do_GetService("@mozilla.org/parentprocessmessagemanager;1");
933 sParentProcessManager->GetInitialProcessData(aCx, &init, aError);
934 if (aError.Failed()) {
935 return;
937 mInitialProcessData = init;
940 if (!JS_WrapValue(aCx, &init)) {
941 aError.NoteJSContextException(aCx);
942 return;
944 aInitialProcessData.set(init);
947 WritableSharedMap* nsFrameMessageManager::SharedData() {
948 if (!mChrome || !mIsProcessManager) {
949 MOZ_ASSERT(false, "Should only call this binding method on ppmm");
950 return nullptr;
952 if (!mSharedData) {
953 mSharedData = new WritableSharedMap();
955 return mSharedData;
958 already_AddRefed<ProcessMessageManager>
959 nsFrameMessageManager::GetProcessMessageManager(ErrorResult& aError) {
960 RefPtr<ProcessMessageManager> pmm;
961 if (mCallback) {
962 pmm = mCallback->GetProcessMessageManager();
964 return pmm.forget();
967 void nsFrameMessageManager::GetRemoteType(nsACString& aRemoteType,
968 ErrorResult& aError) const {
969 aRemoteType.Truncate();
970 if (mCallback) {
971 mCallback->DoGetRemoteType(aRemoteType, aError);
975 namespace {
977 struct MessageManagerReferentCount {
978 MessageManagerReferentCount() : mStrong(0), mWeakAlive(0), mWeakDead(0) {}
979 size_t mStrong;
980 size_t mWeakAlive;
981 size_t mWeakDead;
982 nsTArray<nsString> mSuspectMessages;
983 nsTHashMap<nsStringHashKey, uint32_t> mMessageCounter;
986 } // namespace
988 namespace mozilla::dom {
990 class MessageManagerReporter final : public nsIMemoryReporter {
991 ~MessageManagerReporter() = default;
993 public:
994 NS_DECL_ISUPPORTS
995 NS_DECL_NSIMEMORYREPORTER
997 static const size_t kSuspectReferentCount = 300;
999 protected:
1000 void CountReferents(nsFrameMessageManager* aMessageManager,
1001 MessageManagerReferentCount* aReferentCount);
1004 NS_IMPL_ISUPPORTS(MessageManagerReporter, nsIMemoryReporter)
1006 void MessageManagerReporter::CountReferents(
1007 nsFrameMessageManager* aMessageManager,
1008 MessageManagerReferentCount* aReferentCount) {
1009 for (const auto& entry : aMessageManager->mListeners) {
1010 nsAutoTObserverArray<nsMessageListenerInfo, 1>* listeners = entry.GetWeak();
1011 uint32_t listenerCount = listeners->Length();
1012 if (listenerCount == 0) {
1013 continue;
1016 nsString key(entry.GetKey());
1017 const uint32_t currentCount =
1018 (aReferentCount->mMessageCounter.LookupOrInsert(key, 0) +=
1019 listenerCount);
1021 // Keep track of messages that have a suspiciously large
1022 // number of referents (symptom of leak).
1023 if (currentCount >= MessageManagerReporter::kSuspectReferentCount) {
1024 aReferentCount->mSuspectMessages.AppendElement(key);
1027 for (uint32_t i = 0; i < listenerCount; ++i) {
1028 const nsMessageListenerInfo& listenerInfo = listeners->ElementAt(i);
1029 if (listenerInfo.mWeakListener) {
1030 nsCOMPtr<nsISupports> referent =
1031 do_QueryReferent(listenerInfo.mWeakListener);
1032 if (referent) {
1033 aReferentCount->mWeakAlive++;
1034 } else {
1035 aReferentCount->mWeakDead++;
1037 } else {
1038 aReferentCount->mStrong++;
1043 // Add referent count in child managers because the listeners
1044 // participate in messages dispatched from parent message manager.
1045 for (uint32_t i = 0; i < aMessageManager->mChildManagers.Length(); ++i) {
1046 RefPtr<nsFrameMessageManager> mm = aMessageManager->mChildManagers[i];
1047 CountReferents(mm, aReferentCount);
1051 static void ReportReferentCount(
1052 const char* aManagerType, const MessageManagerReferentCount& aReferentCount,
1053 nsIHandleReportCallback* aHandleReport, nsISupports* aData) {
1054 #define REPORT(_path, _amount, _desc) \
1055 do { \
1056 aHandleReport->Callback(""_ns, _path, nsIMemoryReporter::KIND_OTHER, \
1057 nsIMemoryReporter::UNITS_COUNT, _amount, _desc, \
1058 aData); \
1059 } while (0)
1061 REPORT(nsPrintfCString("message-manager/referent/%s/strong", aManagerType),
1062 aReferentCount.mStrong,
1063 nsPrintfCString("The number of strong referents held by the message "
1064 "manager in the %s manager.",
1065 aManagerType));
1066 REPORT(
1067 nsPrintfCString("message-manager/referent/%s/weak/alive", aManagerType),
1068 aReferentCount.mWeakAlive,
1069 nsPrintfCString("The number of weak referents that are still alive "
1070 "held by the message manager in the %s manager.",
1071 aManagerType));
1072 REPORT(nsPrintfCString("message-manager/referent/%s/weak/dead", aManagerType),
1073 aReferentCount.mWeakDead,
1074 nsPrintfCString("The number of weak referents that are dead "
1075 "held by the message manager in the %s manager.",
1076 aManagerType));
1078 for (uint32_t i = 0; i < aReferentCount.mSuspectMessages.Length(); i++) {
1079 const uint32_t totalReferentCount =
1080 aReferentCount.mMessageCounter.Get(aReferentCount.mSuspectMessages[i]);
1081 NS_ConvertUTF16toUTF8 suspect(aReferentCount.mSuspectMessages[i]);
1082 REPORT(nsPrintfCString("message-manager-suspect/%s/referent(message=%s)",
1083 aManagerType, suspect.get()),
1084 totalReferentCount,
1085 nsPrintfCString("A message in the %s message manager with a "
1086 "suspiciously large number of referents (symptom "
1087 "of a leak).",
1088 aManagerType));
1091 #undef REPORT
1094 static StaticRefPtr<ChromeMessageBroadcaster> sGlobalMessageManager;
1096 NS_IMETHODIMP
1097 MessageManagerReporter::CollectReports(nsIHandleReportCallback* aHandleReport,
1098 nsISupports* aData, bool aAnonymize) {
1099 if (XRE_IsParentProcess() && sGlobalMessageManager) {
1100 MessageManagerReferentCount count;
1101 CountReferents(sGlobalMessageManager, &count);
1102 ReportReferentCount("global-manager", count, aHandleReport, aData);
1105 if (nsFrameMessageManager::sParentProcessManager) {
1106 MessageManagerReferentCount count;
1107 CountReferents(nsFrameMessageManager::sParentProcessManager, &count);
1108 ReportReferentCount("parent-process-manager", count, aHandleReport, aData);
1111 if (nsFrameMessageManager::sChildProcessManager) {
1112 MessageManagerReferentCount count;
1113 CountReferents(nsFrameMessageManager::sChildProcessManager, &count);
1114 ReportReferentCount("child-process-manager", count, aHandleReport, aData);
1117 return NS_OK;
1120 } // namespace mozilla::dom
1122 already_AddRefed<ChromeMessageBroadcaster>
1123 nsFrameMessageManager::GetGlobalMessageManager() {
1124 RefPtr<ChromeMessageBroadcaster> mm;
1125 if (sGlobalMessageManager) {
1126 mm = sGlobalMessageManager;
1127 } else {
1128 sGlobalMessageManager = mm =
1129 new ChromeMessageBroadcaster(MessageManagerFlags::MM_GLOBAL);
1130 ClearOnShutdown(&sGlobalMessageManager);
1131 RegisterStrongMemoryReporter(new MessageManagerReporter());
1133 return mm.forget();
1136 nsresult NS_NewGlobalMessageManager(nsISupports** aResult) {
1137 *aResult = nsFrameMessageManager::GetGlobalMessageManager().take();
1138 return NS_OK;
1141 nsTHashMap<nsStringHashKey, nsMessageManagerScriptHolder*>*
1142 nsMessageManagerScriptExecutor::sCachedScripts = nullptr;
1143 StaticRefPtr<nsScriptCacheCleaner>
1144 nsMessageManagerScriptExecutor::sScriptCacheCleaner;
1146 void nsMessageManagerScriptExecutor::DidCreateScriptLoader() {
1147 if (!sCachedScripts) {
1148 sCachedScripts =
1149 new nsTHashMap<nsStringHashKey, nsMessageManagerScriptHolder*>;
1150 sScriptCacheCleaner = new nsScriptCacheCleaner();
1154 // static
1155 void nsMessageManagerScriptExecutor::PurgeCache() {
1156 if (sCachedScripts) {
1157 NS_ASSERTION(sCachedScripts != nullptr, "Need cached scripts");
1158 for (auto iter = sCachedScripts->Iter(); !iter.Done(); iter.Next()) {
1159 delete iter.Data();
1160 iter.Remove();
1165 // static
1166 void nsMessageManagerScriptExecutor::Shutdown() {
1167 if (sCachedScripts) {
1168 PurgeCache();
1170 delete sCachedScripts;
1171 sCachedScripts = nullptr;
1172 sScriptCacheCleaner = nullptr;
1176 static void FillCompileOptionsForCachedStencil(JS::CompileOptions& aOptions) {
1177 ScriptPreloader::FillCompileOptionsForCachedStencil(aOptions);
1178 aOptions.setNonSyntacticScope(true);
1181 void nsMessageManagerScriptExecutor::LoadScriptInternal(
1182 JS::Handle<JSObject*> aMessageManager, const nsAString& aURL,
1183 bool aRunInUniqueScope) {
1184 AUTO_PROFILER_LABEL_DYNAMIC_LOSSY_NSSTRING(
1185 "nsMessageManagerScriptExecutor::LoadScriptInternal", OTHER, aURL);
1187 if (!sCachedScripts) {
1188 return;
1191 RefPtr<JS::Stencil> stencil;
1192 nsMessageManagerScriptHolder* holder = sCachedScripts->Get(aURL);
1193 if (holder) {
1194 stencil = holder->mStencil;
1195 } else {
1196 stencil =
1197 TryCacheLoadAndCompileScript(aURL, aRunInUniqueScope, aMessageManager);
1200 AutoEntryScript aes(aMessageManager, "message manager script load");
1201 JSContext* cx = aes.cx();
1202 if (stencil) {
1203 JS::CompileOptions options(cx);
1204 FillCompileOptionsForCachedStencil(options);
1205 JS::InstantiateOptions instantiateOptions(options);
1206 JS::Rooted<JSScript*> script(
1207 cx, JS::InstantiateGlobalStencil(cx, instantiateOptions, stencil));
1209 if (script) {
1210 if (aRunInUniqueScope) {
1211 JS::Rooted<JSObject*> scope(cx);
1212 bool ok = js::ExecuteInFrameScriptEnvironment(cx, aMessageManager,
1213 script, &scope);
1214 if (ok) {
1215 // Force the scope to stay alive.
1216 mAnonymousGlobalScopes.AppendElement(scope);
1218 } else {
1219 JS::Rooted<JS::Value> rval(cx);
1220 JS::RootedVector<JSObject*> envChain(cx);
1221 if (!envChain.append(aMessageManager)) {
1222 return;
1224 Unused << JS_ExecuteScript(cx, envChain, script, &rval);
1230 already_AddRefed<JS::Stencil>
1231 nsMessageManagerScriptExecutor::TryCacheLoadAndCompileScript(
1232 const nsAString& aURL, bool aRunInUniqueScope,
1233 JS::Handle<JSObject*> aMessageManager) {
1234 nsCString url = NS_ConvertUTF16toUTF8(aURL);
1235 nsCOMPtr<nsIURI> uri;
1236 nsresult rv = NS_NewURI(getter_AddRefs(uri), url);
1237 if (NS_FAILED(rv)) {
1238 return nullptr;
1241 bool hasFlags;
1242 rv = NS_URIChainHasFlags(uri, nsIProtocolHandler::URI_IS_LOCAL_RESOURCE,
1243 &hasFlags);
1244 if (NS_FAILED(rv) || !hasFlags) {
1245 NS_WARNING("Will not load a frame script!");
1246 return nullptr;
1249 // If this script won't be cached, or there is only one of this type of
1250 // message manager per process, treat this script as run-once. Run-once
1251 // scripts can be compiled directly for the target global, and will be dropped
1252 // from the preloader cache after they're executed and serialized.
1254 // NOTE: This does not affect the JS::CompileOptions. We generate the same
1255 // bytecode as though it were run multiple times. This is required for the
1256 // batch decoding from ScriptPreloader to work.
1257 bool isRunOnce = IsProcessScoped();
1259 // We don't cache data: scripts!
1260 nsAutoCString scheme;
1261 uri->GetScheme(scheme);
1262 bool isCacheable = !scheme.EqualsLiteral("data");
1263 bool useScriptPreloader = isCacheable;
1265 // If the script will be reused in this session, compile it in the compilation
1266 // scope instead of the current global to avoid keeping the current
1267 // compartment alive.
1268 AutoJSAPI jsapi;
1269 if (!jsapi.Init(isRunOnce ? aMessageManager : xpc::CompilationScope())) {
1270 return nullptr;
1272 JSContext* cx = jsapi.cx();
1274 RefPtr<JS::Stencil> stencil;
1275 if (useScriptPreloader) {
1276 nsAutoCString cachePath;
1277 rv = scache::PathifyURI(CACHE_PREFIX("script"), uri, cachePath);
1278 NS_ENSURE_SUCCESS(rv, nullptr);
1280 JS::DecodeOptions decodeOptions;
1281 ScriptPreloader::FillDecodeOptionsForCachedStencil(decodeOptions);
1282 stencil = ScriptPreloader::GetChildSingleton().GetCachedStencil(
1283 cx, decodeOptions, cachePath);
1286 if (!stencil) {
1287 nsCOMPtr<nsIChannel> channel;
1288 NS_NewChannel(getter_AddRefs(channel), uri,
1289 nsContentUtils::GetSystemPrincipal(),
1290 nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL,
1291 nsIContentPolicy::TYPE_INTERNAL_FRAME_MESSAGEMANAGER_SCRIPT);
1293 if (!channel) {
1294 return nullptr;
1297 nsCOMPtr<nsIInputStream> input;
1298 rv = channel->Open(getter_AddRefs(input));
1299 NS_ENSURE_SUCCESS(rv, nullptr);
1300 nsString dataString;
1301 Utf8Unit* dataStringBuf = nullptr;
1302 size_t dataStringLength = 0;
1303 if (input) {
1304 nsCString buffer;
1305 uint64_t written;
1306 if (NS_FAILED(NS_ReadInputStreamToString(input, buffer, -1, &written))) {
1307 return nullptr;
1310 uint32_t size = (uint32_t)std::min(written, (uint64_t)UINT32_MAX);
1311 ScriptLoader::ConvertToUTF8(channel, (uint8_t*)buffer.get(), size, u""_ns,
1312 nullptr, dataStringBuf, dataStringLength);
1315 if (!dataStringBuf) {
1316 return nullptr;
1319 JS::CompileOptions options(cx);
1320 FillCompileOptionsForCachedStencil(options);
1321 options.setFileAndLine(url.get(), 1);
1323 // If we are not encoding to the ScriptPreloader cache, we can now relax the
1324 // compile options and use the JS syntax-parser for lower latency.
1325 if (!useScriptPreloader || !ScriptPreloader::GetChildSingleton().Active()) {
1326 options.setSourceIsLazy(false);
1329 JS::SourceText<Utf8Unit> srcBuf;
1330 if (!srcBuf.init(cx, dataStringBuf, dataStringLength,
1331 JS::SourceOwnership::TakeOwnership)) {
1332 return nullptr;
1335 stencil = JS::CompileGlobalScriptToStencil(cx, options, srcBuf);
1336 if (!stencil) {
1337 return nullptr;
1340 if (isCacheable && !isRunOnce) {
1341 // Store into our cache only when we compile it here.
1342 auto* holder = new nsMessageManagerScriptHolder(stencil);
1343 sCachedScripts->InsertOrUpdate(aURL, holder);
1346 #ifdef DEBUG
1347 // The above shouldn't touch any options for instantiation.
1348 JS::InstantiateOptions instantiateOptions(options);
1349 instantiateOptions.assertDefault();
1350 #endif
1353 MOZ_ASSERT(stencil);
1355 if (useScriptPreloader) {
1356 nsAutoCString cachePath;
1357 rv = scache::PathifyURI(CACHE_PREFIX("script"), uri, cachePath);
1358 NS_ENSURE_SUCCESS(rv, nullptr);
1359 ScriptPreloader::GetChildSingleton().NoteStencil(url, cachePath, stencil,
1360 isRunOnce);
1363 return stencil.forget();
1366 void nsMessageManagerScriptExecutor::Trace(const TraceCallbacks& aCallbacks,
1367 void* aClosure) {
1368 for (size_t i = 0, length = mAnonymousGlobalScopes.Length(); i < length;
1369 ++i) {
1370 aCallbacks.Trace(&mAnonymousGlobalScopes[i], "mAnonymousGlobalScopes[i]",
1371 aClosure);
1375 void nsMessageManagerScriptExecutor::Unlink() {
1376 ImplCycleCollectionUnlink(mAnonymousGlobalScopes);
1379 bool nsMessageManagerScriptExecutor::Init() {
1380 DidCreateScriptLoader();
1381 return true;
1384 void nsMessageManagerScriptExecutor::MarkScopesForCC() {
1385 for (uint32_t i = 0; i < mAnonymousGlobalScopes.Length(); ++i) {
1386 mAnonymousGlobalScopes[i].exposeToActiveJS();
1390 NS_IMPL_ISUPPORTS(nsScriptCacheCleaner, nsIObserver)
1392 ChildProcessMessageManager* nsFrameMessageManager::sChildProcessManager =
1393 nullptr;
1394 ParentProcessMessageManager* nsFrameMessageManager::sParentProcessManager =
1395 nullptr;
1396 nsFrameMessageManager* nsFrameMessageManager::sSameProcessParentManager =
1397 nullptr;
1399 class nsAsyncMessageToSameProcessChild : public nsSameProcessAsyncMessageBase,
1400 public Runnable {
1401 public:
1402 nsAsyncMessageToSameProcessChild()
1403 : nsSameProcessAsyncMessageBase(),
1404 mozilla::Runnable("nsAsyncMessageToSameProcessChild") {}
1405 NS_IMETHOD Run() override {
1406 nsFrameMessageManager* ppm =
1407 nsFrameMessageManager::GetChildProcessManager();
1408 ReceiveMessage(ppm, nullptr, ppm);
1409 return NS_OK;
1414 * Send messages to an imaginary child process in a single-process scenario.
1416 class SameParentProcessMessageManagerCallback : public MessageManagerCallback {
1417 public:
1418 SameParentProcessMessageManagerCallback() {
1419 MOZ_COUNT_CTOR(SameParentProcessMessageManagerCallback);
1421 ~SameParentProcessMessageManagerCallback() override {
1422 MOZ_COUNT_DTOR(SameParentProcessMessageManagerCallback);
1425 bool DoLoadMessageManagerScript(const nsAString& aURL,
1426 bool aRunInGlobalScope) override {
1427 auto* global = ContentProcessMessageManager::Get();
1428 MOZ_ASSERT(!aRunInGlobalScope);
1429 global->LoadScript(aURL);
1430 return true;
1433 nsresult DoSendAsyncMessage(const nsAString& aMessage,
1434 StructuredCloneData& aData) override {
1435 RefPtr<nsAsyncMessageToSameProcessChild> ev =
1436 new nsAsyncMessageToSameProcessChild();
1438 nsresult rv = ev->Init(aMessage, aData);
1439 if (NS_FAILED(rv)) {
1440 return rv;
1442 rv = NS_DispatchToCurrentThread(ev);
1443 if (NS_FAILED(rv)) {
1444 return rv;
1446 return NS_OK;
1451 * Send messages to the parent process.
1453 class ChildProcessMessageManagerCallback : public MessageManagerCallback {
1454 public:
1455 ChildProcessMessageManagerCallback() {
1456 MOZ_COUNT_CTOR(ChildProcessMessageManagerCallback);
1458 ~ChildProcessMessageManagerCallback() override {
1459 MOZ_COUNT_DTOR(ChildProcessMessageManagerCallback);
1462 bool DoSendBlockingMessage(const nsAString& aMessage,
1463 StructuredCloneData& aData,
1464 nsTArray<StructuredCloneData>* aRetVal) override {
1465 mozilla::dom::ContentChild* cc = mozilla::dom::ContentChild::GetSingleton();
1466 if (!cc) {
1467 return true;
1469 ClonedMessageData data;
1470 if (!BuildClonedMessageData(aData, data)) {
1471 return false;
1473 return cc->SendSyncMessage(PromiseFlatString(aMessage), data, aRetVal);
1476 nsresult DoSendAsyncMessage(const nsAString& aMessage,
1477 StructuredCloneData& aData) override {
1478 mozilla::dom::ContentChild* cc = mozilla::dom::ContentChild::GetSingleton();
1479 if (!cc) {
1480 return NS_OK;
1482 ClonedMessageData data;
1483 if (!BuildClonedMessageData(aData, data)) {
1484 return NS_ERROR_DOM_DATA_CLONE_ERR;
1486 if (!cc->SendAsyncMessage(PromiseFlatString(aMessage), data)) {
1487 return NS_ERROR_UNEXPECTED;
1490 return NS_OK;
1494 class nsAsyncMessageToSameProcessParent
1495 : public nsSameProcessAsyncMessageBase,
1496 public SameProcessMessageQueue::Runnable {
1497 public:
1498 nsAsyncMessageToSameProcessParent() : nsSameProcessAsyncMessageBase() {}
1499 nsresult HandleMessage() override {
1500 nsFrameMessageManager* ppm =
1501 nsFrameMessageManager::sSameProcessParentManager;
1502 ReceiveMessage(ppm, nullptr, ppm);
1503 return NS_OK;
1508 * Send messages to the imaginary parent process in a single-process scenario.
1510 class SameChildProcessMessageManagerCallback : public MessageManagerCallback {
1511 public:
1512 SameChildProcessMessageManagerCallback() {
1513 MOZ_COUNT_CTOR(SameChildProcessMessageManagerCallback);
1515 ~SameChildProcessMessageManagerCallback() override {
1516 MOZ_COUNT_DTOR(SameChildProcessMessageManagerCallback);
1519 bool DoSendBlockingMessage(const nsAString& aMessage,
1520 StructuredCloneData& aData,
1521 nsTArray<StructuredCloneData>* aRetVal) override {
1522 SameProcessMessageQueue* queue = SameProcessMessageQueue::Get();
1523 queue->Flush();
1525 if (nsFrameMessageManager::sSameProcessParentManager) {
1526 RefPtr<nsFrameMessageManager> ppm =
1527 nsFrameMessageManager::sSameProcessParentManager;
1528 ppm->ReceiveMessage(ppm, nullptr, aMessage, true, &aData, aRetVal,
1529 IgnoreErrors());
1531 return true;
1534 nsresult DoSendAsyncMessage(const nsAString& aMessage,
1535 StructuredCloneData& aData) override {
1536 SameProcessMessageQueue* queue = SameProcessMessageQueue::Get();
1537 RefPtr<nsAsyncMessageToSameProcessParent> ev =
1538 new nsAsyncMessageToSameProcessParent();
1539 nsresult rv = ev->Init(aMessage, aData);
1541 if (NS_FAILED(rv)) {
1542 return rv;
1544 queue->Push(ev);
1545 return NS_OK;
1549 // This creates the global parent process message manager.
1550 nsresult NS_NewParentProcessMessageManager(nsISupports** aResult) {
1551 NS_ASSERTION(!nsFrameMessageManager::sParentProcessManager,
1552 "Re-creating sParentProcessManager");
1553 RefPtr<ParentProcessMessageManager> mm = new ParentProcessMessageManager();
1554 nsFrameMessageManager::sParentProcessManager = mm;
1555 nsFrameMessageManager::NewProcessMessageManager(
1556 false); // Create same process message manager.
1557 mm.forget(aResult);
1558 return NS_OK;
1561 ProcessMessageManager* nsFrameMessageManager::NewProcessMessageManager(
1562 bool aIsRemote) {
1563 if (!nsFrameMessageManager::sParentProcessManager) {
1564 nsCOMPtr<nsISupports> dummy =
1565 do_GetService("@mozilla.org/parentprocessmessagemanager;1");
1568 MOZ_ASSERT(nsFrameMessageManager::sParentProcessManager,
1569 "parent process manager not created");
1570 ProcessMessageManager* mm;
1571 if (aIsRemote) {
1572 // Callback is set in ContentParent::InitInternal so that the process has
1573 // already started when we send pending scripts.
1574 mm = new ProcessMessageManager(
1575 nullptr, nsFrameMessageManager::sParentProcessManager);
1576 } else {
1577 mm =
1578 new ProcessMessageManager(new SameParentProcessMessageManagerCallback(),
1579 nsFrameMessageManager::sParentProcessManager,
1580 MessageManagerFlags::MM_OWNSCALLBACK);
1581 mm->SetOsPid(base::GetCurrentProcId());
1582 sSameProcessParentManager = mm;
1584 return mm;
1587 nsresult NS_NewChildProcessMessageManager(nsISupports** aResult) {
1588 NS_ASSERTION(!nsFrameMessageManager::GetChildProcessManager(),
1589 "Re-creating sChildProcessManager");
1591 MessageManagerCallback* cb;
1592 if (XRE_IsParentProcess()) {
1593 cb = new SameChildProcessMessageManagerCallback();
1594 } else {
1595 cb = new ChildProcessMessageManagerCallback();
1596 RegisterStrongMemoryReporter(new MessageManagerReporter());
1598 auto* mm = new ChildProcessMessageManager(cb);
1599 nsFrameMessageManager::SetChildProcessManager(mm);
1600 auto global = MakeRefPtr<ContentProcessMessageManager>(mm);
1601 NS_ENSURE_TRUE(global->Init(), NS_ERROR_UNEXPECTED);
1602 return CallQueryInterface(global, aResult);
1605 void nsFrameMessageManager::MarkForCC() {
1606 for (const auto& entry : mListeners) {
1607 nsAutoTObserverArray<nsMessageListenerInfo, 1>* listeners = entry.GetWeak();
1608 uint32_t count = listeners->Length();
1609 for (uint32_t i = 0; i < count; i++) {
1610 MessageListener* strongListener = listeners->ElementAt(i).mStrongListener;
1611 if (strongListener) {
1612 strongListener->MarkForCC();
1617 if (mRefCnt.IsPurple()) {
1618 mRefCnt.RemovePurple();
1622 nsSameProcessAsyncMessageBase::nsSameProcessAsyncMessageBase()
1623 #ifdef DEBUG
1624 : mCalledInit(false)
1625 #endif
1629 nsresult nsSameProcessAsyncMessageBase::Init(const nsAString& aMessage,
1630 StructuredCloneData& aData) {
1631 if (!mData.Copy(aData)) {
1632 Telemetry::Accumulate(Telemetry::IPC_SAME_PROCESS_MESSAGE_COPY_OOM_KB,
1633 aData.DataLength());
1634 return NS_ERROR_OUT_OF_MEMORY;
1637 mMessage = aMessage;
1638 #ifdef DEBUG
1639 mCalledInit = true;
1640 #endif
1642 return NS_OK;
1645 void nsSameProcessAsyncMessageBase::ReceiveMessage(
1646 nsISupports* aTarget, nsFrameLoader* aTargetFrameLoader,
1647 nsFrameMessageManager* aManager) {
1648 // Make sure that we have called Init() and it has succeeded.
1649 MOZ_ASSERT(mCalledInit);
1650 if (aManager) {
1651 RefPtr<nsFrameMessageManager> mm = aManager;
1652 mm->ReceiveMessage(aTarget, aTargetFrameLoader, mMessage, false, &mData,
1653 nullptr, IgnoreErrors());