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 #ifndef nsFrameMessageManager_h__
8 #define nsFrameMessageManager_h__
13 #include "ErrorList.h"
14 #include "js/RootingAPI.h"
15 #include "js/TypeDecls.h"
17 #include "mozilla/AlreadyAddRefed.h"
18 #include "mozilla/Assertions.h"
19 #include "mozilla/RefPtr.h"
20 #include "mozilla/Services.h"
21 #include "mozilla/StaticPtr.h"
22 #include "mozilla/TypedEnumBits.h"
23 #include "mozilla/UniquePtr.h"
24 #include "mozilla/dom/ipc/StructuredCloneData.h"
26 #include "nsClassHashtable.h"
27 #include "nsCycleCollectionParticipant.h"
28 #include "nsTHashMap.h"
29 #include "nsHashKeys.h"
30 #include "nsIMessageManager.h"
31 #include "nsIObserver.h"
32 #include "nsIObserverService.h"
33 #include "nsISupports.h"
34 #include "nsIWeakReferenceUtils.h"
35 #include "nsStringFwd.h"
37 #include "nsTObserverArray.h"
49 class ChildProcessMessageManager
;
50 class ChromeMessageBroadcaster
;
51 class ClonedMessageData
;
52 class MessageBroadcaster
;
53 class MessageListener
;
54 class MessageListenerManager
;
55 class MessageManagerReporter
;
56 class ParentProcessMessageManager
;
57 class ProcessMessageManager
;
61 class MessageManagerCallback
;
62 class WritableSharedMap
;
64 // Note: we round the time we spend to the nearest millisecond. So a min value
65 // of 1 ms actually captures from 500us and above.
66 static const uint32_t kMinTelemetrySyncMessageManagerLatencyMs
= 1;
68 enum class MessageManagerFlags
{
72 MM_PROCESSMANAGER
= 4,
76 MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(MessageManagerFlags
);
78 void UnpackClonedMessageDataForParent(const ClonedMessageData
& aClonedData
,
79 StructuredCloneData
& aData
);
81 void UnpackClonedMessageDataForChild(const ClonedMessageData
& aClonedData
,
82 StructuredCloneData
& aData
);
86 } // namespace mozilla
88 struct nsMessageListenerInfo
{
89 bool operator==(const nsMessageListenerInfo
& aOther
) const {
90 return &aOther
== this;
93 // If mWeakListener is null then mStrongListener holds a MessageListener.
94 // If mWeakListener is non-null then mStrongListener contains null.
95 RefPtr
<mozilla::dom::MessageListener
> mStrongListener
;
96 nsWeakPtr mWeakListener
;
97 bool mListenWhenClosed
;
100 class nsFrameMessageManager
: public nsIMessageSender
{
101 friend class mozilla::dom::MessageManagerReporter
;
102 using StructuredCloneData
= mozilla::dom::ipc::StructuredCloneData
;
105 using MessageManagerFlags
= mozilla::dom::ipc::MessageManagerFlags
;
107 nsFrameMessageManager(mozilla::dom::ipc::MessageManagerCallback
* aCallback
,
108 MessageManagerFlags aFlags
);
110 virtual ~nsFrameMessageManager();
113 explicit nsFrameMessageManager(
114 mozilla::dom::ipc::MessageManagerCallback
* aCallback
)
115 : nsFrameMessageManager(aCallback
, MessageManagerFlags::MM_NONE
) {}
117 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
118 NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(nsFrameMessageManager
)
122 // MessageListenerManager
123 void AddMessageListener(const nsAString
& aMessageName
,
124 mozilla::dom::MessageListener
& aListener
,
125 bool aListenWhenClosed
, mozilla::ErrorResult
& aError
);
126 void RemoveMessageListener(const nsAString
& aMessageName
,
127 mozilla::dom::MessageListener
& aListener
,
128 mozilla::ErrorResult
& aError
);
129 void AddWeakMessageListener(const nsAString
& aMessageName
,
130 mozilla::dom::MessageListener
& aListener
,
131 mozilla::ErrorResult
& aError
);
132 void RemoveWeakMessageListener(const nsAString
& aMessageName
,
133 mozilla::dom::MessageListener
& aListener
,
134 mozilla::ErrorResult
& aError
);
137 void SendAsyncMessage(JSContext
* aCx
, const nsAString
& aMessageName
,
138 JS::Handle
<JS::Value
> aObj
,
139 JS::Handle
<JS::Value
> aTransfers
,
140 mozilla::ErrorResult
& aError
) {
141 DispatchAsyncMessage(aCx
, aMessageName
, aObj
, aTransfers
, aError
);
143 already_AddRefed
<mozilla::dom::ProcessMessageManager
>
144 GetProcessMessageManager(mozilla::ErrorResult
& aError
);
145 void GetRemoteType(nsACString
& aRemoteType
,
146 mozilla::ErrorResult
& aError
) const;
149 void SendSyncMessage(JSContext
* aCx
, const nsAString
& aMessageName
,
150 JS::Handle
<JS::Value
> aObj
, nsTArray
<JS::Value
>& aResult
,
151 mozilla::ErrorResult
& aError
);
153 // GlobalProcessScriptLoader
154 void GetInitialProcessData(JSContext
* aCx
,
155 JS::MutableHandle
<JS::Value
> aInitialProcessData
,
156 mozilla::ErrorResult
& aError
);
158 mozilla::dom::ipc::WritableSharedMap
* SharedData();
160 NS_DECL_NSIMESSAGESENDER
162 static mozilla::dom::ProcessMessageManager
* NewProcessMessageManager(
165 void ReceiveMessage(nsISupports
* aTarget
, nsFrameLoader
* aTargetFrameLoader
,
166 const nsAString
& aMessage
, bool aIsSync
,
167 StructuredCloneData
* aCloneData
,
168 nsTArray
<StructuredCloneData
>* aRetVal
,
169 mozilla::ErrorResult
& aError
) {
170 ReceiveMessage(aTarget
, aTargetFrameLoader
, mClosed
, aMessage
, aIsSync
,
171 aCloneData
, aRetVal
, aError
);
174 void Disconnect(bool aRemoveFromParent
= true);
177 void SetCallback(mozilla::dom::ipc::MessageManagerCallback
* aCallback
);
179 mozilla::dom::ipc::MessageManagerCallback
* GetCallback() { return mCallback
; }
181 nsresult
DispatchAsyncMessageInternal(JSContext
* aCx
,
182 const nsAString
& aMessage
,
183 StructuredCloneData
& aData
);
184 bool IsGlobal() { return mGlobal
; }
185 bool IsBroadcaster() { return mIsBroadcaster
; }
186 bool IsChrome() { return mChrome
; }
188 // GetGlobalMessageManager creates the global message manager if it hasn't
190 static already_AddRefed
<mozilla::dom::ChromeMessageBroadcaster
>
191 GetGlobalMessageManager();
192 static mozilla::dom::ParentProcessMessageManager
* GetParentProcessManager() {
193 return sParentProcessManager
;
195 static mozilla::dom::ChildProcessMessageManager
* GetChildProcessManager() {
196 return sChildProcessManager
;
198 static void SetChildProcessManager(
199 mozilla::dom::ChildProcessMessageManager
* aManager
) {
200 sChildProcessManager
= aManager
;
203 static bool GetParamsForMessage(JSContext
* aCx
, const JS::Value
& aValue
,
204 const JS::Value
& aTransfer
,
205 StructuredCloneData
& aData
);
207 void SetInitialProcessData(JS::HandleValue aInitialData
);
209 void LoadPendingScripts();
212 friend class MMListenerRemover
;
214 virtual mozilla::dom::MessageBroadcaster
* GetParentManager() {
217 virtual void ClearParentManager(bool aRemove
) {}
219 void DispatchAsyncMessage(JSContext
* aCx
, const nsAString
& aMessageName
,
220 JS::Handle
<JS::Value
> aObj
,
221 JS::Handle
<JS::Value
> aTransfers
,
222 mozilla::ErrorResult
& aError
);
224 void ReceiveMessage(nsISupports
* aTarget
, nsFrameLoader
* aTargetFrameLoader
,
225 bool aTargetClosed
, const nsAString
& aMessage
,
226 bool aIsSync
, StructuredCloneData
* aCloneData
,
227 nsTArray
<StructuredCloneData
>* aRetVal
,
228 mozilla::ErrorResult
& aError
);
230 void LoadScript(const nsAString
& aURL
, bool aAllowDelayedLoad
,
231 bool aRunInGlobalScope
, mozilla::ErrorResult
& aError
);
232 void RemoveDelayedScript(const nsAString
& aURL
);
233 void GetDelayedScripts(JSContext
* aCx
, nsTArray
<nsTArray
<JS::Value
>>& aList
,
234 mozilla::ErrorResult
& aError
);
236 // We keep the message listeners as arrays in a hastable indexed by the
237 // message name. That gives us fast lookups in ReceiveMessage().
238 nsClassHashtable
<nsStringHashKey
,
239 nsAutoTObserverArray
<nsMessageListenerInfo
, 1>>
241 nsTArray
<RefPtr
<mozilla::dom::MessageListenerManager
>> mChildManagers
;
242 bool mChrome
; // true if we're in the chrome process
243 bool mGlobal
; // true if we're the global frame message manager
244 bool mIsProcessManager
; // true if the message manager belongs to the process
246 bool mIsBroadcaster
; // true if the message manager is a broadcaster
248 bool mHandlingMessage
;
249 bool mClosed
; // true if we can no longer send messages
251 mozilla::dom::ipc::MessageManagerCallback
* mCallback
;
252 mozilla::UniquePtr
<mozilla::dom::ipc::MessageManagerCallback
> mOwnedCallback
;
253 nsTArray
<nsString
> mPendingScripts
;
254 nsTArray
<bool> mPendingScriptsGlobalStates
;
255 JS::Heap
<JS::Value
> mInitialProcessData
;
256 RefPtr
<mozilla::dom::ipc::WritableSharedMap
> mSharedData
;
258 void LoadPendingScripts(nsFrameMessageManager
* aManager
,
259 nsFrameMessageManager
* aChildMM
);
262 static mozilla::dom::ParentProcessMessageManager
* sParentProcessManager
;
263 static nsFrameMessageManager
* sSameProcessParentManager
;
264 static nsTArray
<nsCOMPtr
<nsIRunnable
>>* sPendingSameProcessAsyncMessages
;
267 static mozilla::dom::ChildProcessMessageManager
* sChildProcessManager
;
270 /* A helper class for taking care of many details for async message sending
271 within a single process. Intended to be used like so:
273 class MyAsyncMessage : public nsSameProcessAsyncMessageBase, public Runnable
276 ReceiveMessage(..., ...);
282 RefPtr<nsSameProcessAsyncMessageBase> ev = new MyAsyncMessage();
283 nsresult rv = ev->Init(...);
284 if (NS_SUCCEEDED(rv)) {
285 NS_DispatchToMainThread(ev);
288 class nsSameProcessAsyncMessageBase
{
290 using StructuredCloneData
= mozilla::dom::ipc::StructuredCloneData
;
292 nsSameProcessAsyncMessageBase();
293 nsresult
Init(const nsAString
& aMessage
, StructuredCloneData
& aData
);
295 void ReceiveMessage(nsISupports
* aTarget
, nsFrameLoader
* aTargetFrameLoader
,
296 nsFrameMessageManager
* aManager
);
299 nsSameProcessAsyncMessageBase(const nsSameProcessAsyncMessageBase
&);
302 StructuredCloneData mData
;
308 class nsScriptCacheCleaner
;
310 struct nsMessageManagerScriptHolder
{
311 nsMessageManagerScriptHolder(JSContext
* aCx
, JSScript
* aScript
)
312 : mScript(aCx
, aScript
) {
313 MOZ_COUNT_CTOR(nsMessageManagerScriptHolder
);
316 MOZ_COUNTED_DTOR(nsMessageManagerScriptHolder
)
318 JS::PersistentRooted
<JSScript
*> mScript
;
321 class nsMessageManagerScriptExecutor
{
323 static void PurgeCache();
324 static void Shutdown();
326 void MarkScopesForCC();
329 friend class nsMessageManagerScriptCx
;
330 nsMessageManagerScriptExecutor() {
331 MOZ_COUNT_CTOR(nsMessageManagerScriptExecutor
);
333 MOZ_COUNTED_DTOR(nsMessageManagerScriptExecutor
)
335 void DidCreateScriptLoader();
336 void LoadScriptInternal(JS::Handle
<JSObject
*> aMessageManager
,
337 const nsAString
& aURL
, bool aRunInUniqueScope
);
338 void TryCacheLoadAndCompileScript(const nsAString
& aURL
,
339 bool aRunInUniqueScope
, bool aShouldCache
,
340 JS::Handle
<JSObject
*> aMessageManager
,
341 JS::MutableHandle
<JSScript
*> aScriptp
);
343 void Trace(const TraceCallbacks
& aCallbacks
, void* aClosure
);
345 AutoTArray
<JS::Heap
<JSObject
*>, 2> mAnonymousGlobalScopes
;
347 // Returns true if this is a process message manager. There should only be a
348 // single process message manager per session, so instances of this type will
349 // optimize their script loading to avoid unnecessary duplication.
350 virtual bool IsProcessScoped() const { return false; }
352 static nsTHashMap
<nsStringHashKey
, nsMessageManagerScriptHolder
*>*
354 static mozilla::StaticRefPtr
<nsScriptCacheCleaner
> sScriptCacheCleaner
;
357 class nsScriptCacheCleaner final
: public nsIObserver
{
358 ~nsScriptCacheCleaner() = default;
362 nsScriptCacheCleaner() {
363 nsCOMPtr
<nsIObserverService
> obsSvc
=
364 mozilla::services::GetObserverService();
366 obsSvc
->AddObserver(this, "message-manager-flush-caches", false);
367 obsSvc
->AddObserver(this, "xpcom-shutdown", false);
371 NS_IMETHOD
Observe(nsISupports
* aSubject
, const char* aTopic
,
372 const char16_t
* aData
) override
{
373 if (strcmp("message-manager-flush-caches", aTopic
) == 0) {
374 nsMessageManagerScriptExecutor::PurgeCache();
375 } else if (strcmp("xpcom-shutdown", aTopic
) == 0) {
376 nsMessageManagerScriptExecutor::Shutdown();