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/experimental/JSStencil.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 UnpackClonedMessageData(const ClonedMessageData
& aClonedData
,
79 StructuredCloneData
& aData
);
83 } // namespace mozilla
85 struct nsMessageListenerInfo
{
86 bool operator==(const nsMessageListenerInfo
& aOther
) const {
87 return &aOther
== this;
90 // If mWeakListener is null then mStrongListener holds a MessageListener.
91 // If mWeakListener is non-null then mStrongListener contains null.
92 RefPtr
<mozilla::dom::MessageListener
> mStrongListener
;
93 nsWeakPtr mWeakListener
;
94 bool mListenWhenClosed
;
97 class nsFrameMessageManager
: public nsIMessageSender
{
98 friend class mozilla::dom::MessageManagerReporter
;
99 using StructuredCloneData
= mozilla::dom::ipc::StructuredCloneData
;
102 using MessageManagerFlags
= mozilla::dom::ipc::MessageManagerFlags
;
104 nsFrameMessageManager(mozilla::dom::ipc::MessageManagerCallback
* aCallback
,
105 MessageManagerFlags aFlags
);
107 virtual ~nsFrameMessageManager();
110 explicit nsFrameMessageManager(
111 mozilla::dom::ipc::MessageManagerCallback
* aCallback
)
112 : nsFrameMessageManager(aCallback
, MessageManagerFlags::MM_NONE
) {}
114 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
115 NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(nsFrameMessageManager
)
119 // MessageListenerManager
120 void AddMessageListener(const nsAString
& aMessageName
,
121 mozilla::dom::MessageListener
& aListener
,
122 bool aListenWhenClosed
, mozilla::ErrorResult
& aError
);
123 void RemoveMessageListener(const nsAString
& aMessageName
,
124 mozilla::dom::MessageListener
& aListener
,
125 mozilla::ErrorResult
& aError
);
126 void AddWeakMessageListener(const nsAString
& aMessageName
,
127 mozilla::dom::MessageListener
& aListener
,
128 mozilla::ErrorResult
& aError
);
129 void RemoveWeakMessageListener(const nsAString
& aMessageName
,
130 mozilla::dom::MessageListener
& aListener
,
131 mozilla::ErrorResult
& aError
);
134 void SendAsyncMessage(JSContext
* aCx
, const nsAString
& aMessageName
,
135 JS::Handle
<JS::Value
> aObj
,
136 JS::Handle
<JS::Value
> aTransfers
,
137 mozilla::ErrorResult
& aError
) {
138 DispatchAsyncMessage(aCx
, aMessageName
, aObj
, aTransfers
, aError
);
140 already_AddRefed
<mozilla::dom::ProcessMessageManager
>
141 GetProcessMessageManager(mozilla::ErrorResult
& aError
);
142 void GetRemoteType(nsACString
& aRemoteType
,
143 mozilla::ErrorResult
& aError
) const;
146 void SendSyncMessage(JSContext
* aCx
, const nsAString
& aMessageName
,
147 JS::Handle
<JS::Value
> aObj
, nsTArray
<JS::Value
>& aResult
,
148 mozilla::ErrorResult
& aError
);
150 // GlobalProcessScriptLoader
151 void GetInitialProcessData(JSContext
* aCx
,
152 JS::MutableHandle
<JS::Value
> aInitialProcessData
,
153 mozilla::ErrorResult
& aError
);
155 mozilla::dom::ipc::WritableSharedMap
* SharedData();
157 NS_DECL_NSIMESSAGESENDER
159 static mozilla::dom::ProcessMessageManager
* NewProcessMessageManager(
162 void ReceiveMessage(nsISupports
* aTarget
, nsFrameLoader
* aTargetFrameLoader
,
163 const nsAString
& aMessage
, bool aIsSync
,
164 StructuredCloneData
* aCloneData
,
165 nsTArray
<StructuredCloneData
>* aRetVal
,
166 mozilla::ErrorResult
& aError
) {
167 ReceiveMessage(aTarget
, aTargetFrameLoader
, mClosed
, aMessage
, aIsSync
,
168 aCloneData
, aRetVal
, aError
);
171 void Disconnect(bool aRemoveFromParent
= true);
174 void SetCallback(mozilla::dom::ipc::MessageManagerCallback
* aCallback
);
176 mozilla::dom::ipc::MessageManagerCallback
* GetCallback() { return mCallback
; }
178 nsresult
DispatchAsyncMessageInternal(JSContext
* aCx
,
179 const nsAString
& aMessage
,
180 StructuredCloneData
& aData
);
181 bool IsGlobal() { return mGlobal
; }
182 bool IsBroadcaster() { return mIsBroadcaster
; }
183 bool IsChrome() { return mChrome
; }
185 // GetGlobalMessageManager creates the global message manager if it hasn't
187 static already_AddRefed
<mozilla::dom::ChromeMessageBroadcaster
>
188 GetGlobalMessageManager();
189 static mozilla::dom::ParentProcessMessageManager
* GetParentProcessManager() {
190 return sParentProcessManager
;
192 static mozilla::dom::ChildProcessMessageManager
* GetChildProcessManager() {
193 return sChildProcessManager
;
195 static void SetChildProcessManager(
196 mozilla::dom::ChildProcessMessageManager
* aManager
) {
197 sChildProcessManager
= aManager
;
200 static bool GetParamsForMessage(JSContext
* aCx
, const JS::Value
& aValue
,
201 const JS::Value
& aTransfer
,
202 StructuredCloneData
& aData
);
204 void SetInitialProcessData(JS::Handle
<JS::Value
> aInitialData
);
206 void LoadPendingScripts();
209 friend class MMListenerRemover
;
211 virtual mozilla::dom::MessageBroadcaster
* GetParentManager() {
214 virtual void ClearParentManager(bool aRemove
) {}
216 void DispatchAsyncMessage(JSContext
* aCx
, const nsAString
& aMessageName
,
217 JS::Handle
<JS::Value
> aObj
,
218 JS::Handle
<JS::Value
> aTransfers
,
219 mozilla::ErrorResult
& aError
);
221 void ReceiveMessage(nsISupports
* aTarget
, nsFrameLoader
* aTargetFrameLoader
,
222 bool aTargetClosed
, const nsAString
& aMessage
,
223 bool aIsSync
, StructuredCloneData
* aCloneData
,
224 nsTArray
<StructuredCloneData
>* aRetVal
,
225 mozilla::ErrorResult
& aError
);
227 void LoadScript(const nsAString
& aURL
, bool aAllowDelayedLoad
,
228 bool aRunInGlobalScope
, mozilla::ErrorResult
& aError
);
229 void RemoveDelayedScript(const nsAString
& aURL
);
230 void GetDelayedScripts(JSContext
* aCx
, nsTArray
<nsTArray
<JS::Value
>>& aList
,
231 mozilla::ErrorResult
& aError
);
233 // We keep the message listeners as arrays in a hastable indexed by the
234 // message name. That gives us fast lookups in ReceiveMessage().
235 nsClassHashtable
<nsStringHashKey
,
236 nsAutoTObserverArray
<nsMessageListenerInfo
, 1>>
238 nsTArray
<RefPtr
<mozilla::dom::MessageListenerManager
>> mChildManagers
;
239 bool mChrome
; // true if we're in the chrome process
240 bool mGlobal
; // true if we're the global frame message manager
241 bool mIsProcessManager
; // true if the message manager belongs to the process
243 bool mIsBroadcaster
; // true if the message manager is a broadcaster
245 bool mHandlingMessage
;
246 bool mClosed
; // true if we can no longer send messages
248 mozilla::dom::ipc::MessageManagerCallback
* mCallback
;
249 mozilla::UniquePtr
<mozilla::dom::ipc::MessageManagerCallback
> mOwnedCallback
;
250 nsTArray
<nsString
> mPendingScripts
;
251 nsTArray
<bool> mPendingScriptsGlobalStates
;
252 JS::Heap
<JS::Value
> mInitialProcessData
;
253 RefPtr
<mozilla::dom::ipc::WritableSharedMap
> mSharedData
;
255 void LoadPendingScripts(nsFrameMessageManager
* aManager
,
256 nsFrameMessageManager
* aChildMM
);
259 static mozilla::dom::ParentProcessMessageManager
* sParentProcessManager
;
260 static nsFrameMessageManager
* sSameProcessParentManager
;
261 static nsTArray
<nsCOMPtr
<nsIRunnable
>>* sPendingSameProcessAsyncMessages
;
264 static mozilla::dom::ChildProcessMessageManager
* sChildProcessManager
;
267 /* A helper class for taking care of many details for async message sending
268 within a single process. Intended to be used like so:
270 class MyAsyncMessage : public nsSameProcessAsyncMessageBase, public Runnable
273 ReceiveMessage(..., ...);
279 RefPtr<nsSameProcessAsyncMessageBase> ev = new MyAsyncMessage();
280 nsresult rv = ev->Init(...);
281 if (NS_SUCCEEDED(rv)) {
282 NS_DispatchToMainThread(ev);
285 class nsSameProcessAsyncMessageBase
{
287 using StructuredCloneData
= mozilla::dom::ipc::StructuredCloneData
;
289 nsSameProcessAsyncMessageBase();
290 nsresult
Init(const nsAString
& aMessage
, StructuredCloneData
& aData
);
292 void ReceiveMessage(nsISupports
* aTarget
, nsFrameLoader
* aTargetFrameLoader
,
293 nsFrameMessageManager
* aManager
);
296 nsSameProcessAsyncMessageBase(const nsSameProcessAsyncMessageBase
&);
299 StructuredCloneData mData
;
305 class nsScriptCacheCleaner
;
307 struct nsMessageManagerScriptHolder
{
308 explicit nsMessageManagerScriptHolder(JS::Stencil
* aStencil
)
309 : mStencil(aStencil
) {
310 MOZ_COUNT_CTOR(nsMessageManagerScriptHolder
);
313 MOZ_COUNTED_DTOR(nsMessageManagerScriptHolder
)
315 RefPtr
<JS::Stencil
> mStencil
;
318 class nsMessageManagerScriptExecutor
{
320 static void PurgeCache();
321 static void Shutdown();
323 void MarkScopesForCC();
326 friend class nsMessageManagerScriptCx
;
327 nsMessageManagerScriptExecutor() {
328 MOZ_COUNT_CTOR(nsMessageManagerScriptExecutor
);
330 MOZ_COUNTED_DTOR(nsMessageManagerScriptExecutor
)
332 void DidCreateScriptLoader();
333 void LoadScriptInternal(JS::Handle
<JSObject
*> aMessageManager
,
334 const nsAString
& aURL
, bool aRunInUniqueScope
);
335 already_AddRefed
<JS::Stencil
> TryCacheLoadAndCompileScript(
336 const nsAString
& aURL
, bool aRunInUniqueScope
,
337 JS::Handle
<JSObject
*> aMessageManager
);
339 void Trace(const TraceCallbacks
& aCallbacks
, void* aClosure
);
341 AutoTArray
<JS::Heap
<JSObject
*>, 2> mAnonymousGlobalScopes
;
343 // Returns true if this is a process message manager. There should only be a
344 // single process message manager per session, so instances of this type will
345 // optimize their script loading to avoid unnecessary duplication.
346 virtual bool IsProcessScoped() const { return false; }
348 static nsTHashMap
<nsStringHashKey
, nsMessageManagerScriptHolder
*>*
350 static mozilla::StaticRefPtr
<nsScriptCacheCleaner
> sScriptCacheCleaner
;
353 class nsScriptCacheCleaner final
: public nsIObserver
{
354 ~nsScriptCacheCleaner() = default;
358 nsScriptCacheCleaner() {
359 nsCOMPtr
<nsIObserverService
> obsSvc
=
360 mozilla::services::GetObserverService();
362 obsSvc
->AddObserver(this, "xpcom-shutdown", false);
366 NS_IMETHOD
Observe(nsISupports
* aSubject
, const char* aTopic
,
367 const char16_t
* aData
) override
{
368 if (strcmp("xpcom-shutdown", aTopic
) == 0) {
369 nsMessageManagerScriptExecutor::Shutdown();