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__
10 #include "nsIMessageManager.h"
11 #include "nsIObserver.h"
13 #include "nsAutoPtr.h"
14 #include "nsCOMArray.h"
17 #include "nsCycleCollectionParticipant.h"
19 #include "nsIPrincipal.h"
20 #include "nsIXPConnect.h"
21 #include "nsDataHashtable.h"
22 #include "nsClassHashtable.h"
23 #include "mozilla/Services.h"
24 #include "mozilla/StaticPtr.h"
25 #include "nsIObserverService.h"
26 #include "nsThreadUtils.h"
27 #include "nsIWeakReferenceUtils.h"
28 #include "mozilla/Attributes.h"
29 #include "js/RootingAPI.h"
30 #include "nsTObserverArray.h"
31 #include "mozilla/TypedEnumBits.h"
32 #include "mozilla/dom/CallbackObject.h"
33 #include "mozilla/dom/SameProcessMessageQueue.h"
34 #include "mozilla/dom/ipc/StructuredCloneData.h"
35 #include "mozilla/jsipc/CpowHolder.h"
49 class ChildProcessMessageManager
;
50 class ChromeMessageBroadcaster
;
51 class ClonedMessageData
;
52 class MessageBroadcaster
;
53 class MessageListener
;
54 class MessageListenerManager
;
55 class MessageManagerReporter
;
58 class ParentProcessMessageManager
;
59 class ProcessMessageManager
;
63 class WritableSharedMap
;
65 // Note: we round the time we spend to the nearest millisecond. So a min value
66 // of 1 ms actually captures from 500us and above.
67 static const uint32_t kMinTelemetrySyncMessageManagerLatencyMs
= 1;
69 enum class MessageManagerFlags
{
73 MM_PROCESSMANAGER
= 4,
77 MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(MessageManagerFlags
);
79 class MessageManagerCallback
{
81 virtual ~MessageManagerCallback() {}
83 virtual bool DoLoadMessageManagerScript(const nsAString
& aURL
,
84 bool aRunInGlobalScope
) {
88 virtual bool DoSendBlockingMessage(JSContext
* aCx
, const nsAString
& aMessage
,
89 StructuredCloneData
& aData
,
90 JS::Handle
<JSObject
*> aCpows
,
91 nsIPrincipal
* aPrincipal
,
92 nsTArray
<StructuredCloneData
>* aRetVal
,
97 virtual nsresult
DoSendAsyncMessage(JSContext
* aCx
, const nsAString
& aMessage
,
98 StructuredCloneData
& aData
,
99 JS::Handle
<JSObject
*> aCpows
,
100 nsIPrincipal
* aPrincipal
) {
104 virtual mozilla::dom::ProcessMessageManager
* GetProcessMessageManager()
109 virtual void DoGetRemoteType(nsAString
& aRemoteType
,
110 ErrorResult
& aError
) const;
113 bool BuildClonedMessageDataForParent(ContentParent
* aParent
,
114 StructuredCloneData
& aData
,
115 ClonedMessageData
& aClonedData
);
116 bool BuildClonedMessageDataForChild(ContentChild
* aChild
,
117 StructuredCloneData
& aData
,
118 ClonedMessageData
& aClonedData
);
121 void UnpackClonedMessageDataForParent(const ClonedMessageData
& aClonedData
,
122 StructuredCloneData
& aData
);
124 void UnpackClonedMessageDataForChild(const ClonedMessageData
& aClonedData
,
125 StructuredCloneData
& aData
);
129 } // namespace mozilla
131 struct nsMessageListenerInfo
{
132 bool operator==(const nsMessageListenerInfo
& aOther
) const {
133 return &aOther
== this;
136 // If mWeakListener is null then mStrongListener holds a MessageListener.
137 // If mWeakListener is non-null then mStrongListener contains null.
138 RefPtr
<mozilla::dom::MessageListener
> mStrongListener
;
139 nsWeakPtr mWeakListener
;
140 bool mListenWhenClosed
;
143 class MOZ_STACK_CLASS SameProcessCpowHolder
144 : public mozilla::jsipc::CpowHolder
{
146 SameProcessCpowHolder(JS::RootingContext
* aRootingCx
,
147 JS::Handle
<JSObject
*> aObj
)
148 : mObj(aRootingCx
, aObj
) {}
150 virtual bool ToObject(JSContext
* aCx
,
151 JS::MutableHandle
<JSObject
*> aObjp
) override
;
154 JS::Rooted
<JSObject
*> mObj
;
157 class nsFrameMessageManager
: public nsIMessageSender
{
158 friend class mozilla::dom::MessageManagerReporter
;
159 typedef mozilla::dom::ipc::StructuredCloneData StructuredCloneData
;
162 typedef mozilla::dom::ipc::MessageManagerFlags MessageManagerFlags
;
164 nsFrameMessageManager(mozilla::dom::ipc::MessageManagerCallback
* aCallback
,
165 MessageManagerFlags aFlags
);
167 virtual ~nsFrameMessageManager();
170 explicit nsFrameMessageManager(
171 mozilla::dom::ipc::MessageManagerCallback
* aCallback
)
172 : nsFrameMessageManager(aCallback
, MessageManagerFlags::MM_NONE
) {}
174 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
175 NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(nsFrameMessageManager
)
179 // MessageListenerManager
180 void AddMessageListener(const nsAString
& aMessageName
,
181 mozilla::dom::MessageListener
& aListener
,
182 bool aListenWhenClosed
, mozilla::ErrorResult
& aError
);
183 void RemoveMessageListener(const nsAString
& aMessageName
,
184 mozilla::dom::MessageListener
& aListener
,
185 mozilla::ErrorResult
& aError
);
186 void AddWeakMessageListener(const nsAString
& aMessageName
,
187 mozilla::dom::MessageListener
& aListener
,
188 mozilla::ErrorResult
& aError
);
189 void RemoveWeakMessageListener(const nsAString
& aMessageName
,
190 mozilla::dom::MessageListener
& aListener
,
191 mozilla::ErrorResult
& aError
);
194 void SendAsyncMessage(JSContext
* aCx
, const nsAString
& aMessageName
,
195 JS::Handle
<JS::Value
> aObj
,
196 JS::Handle
<JSObject
*> aObjects
,
197 nsIPrincipal
* aPrincipal
,
198 JS::Handle
<JS::Value
> aTransfers
,
199 mozilla::ErrorResult
& aError
) {
200 DispatchAsyncMessage(aCx
, aMessageName
, aObj
, aObjects
, aPrincipal
,
203 already_AddRefed
<mozilla::dom::ProcessMessageManager
>
204 GetProcessMessageManager(mozilla::ErrorResult
& aError
);
205 void GetRemoteType(nsAString
& aRemoteType
,
206 mozilla::ErrorResult
& aError
) const;
209 void SendSyncMessage(JSContext
* aCx
, const nsAString
& aMessageName
,
210 JS::Handle
<JS::Value
> aObj
,
211 JS::Handle
<JSObject
*> aObjects
, nsIPrincipal
* aPrincipal
,
212 nsTArray
<JS::Value
>& aResult
,
213 mozilla::ErrorResult
& aError
) {
214 SendMessage(aCx
, aMessageName
, aObj
, aObjects
, aPrincipal
, true, aResult
,
217 void SendRpcMessage(JSContext
* aCx
, const nsAString
& aMessageName
,
218 JS::Handle
<JS::Value
> aObj
,
219 JS::Handle
<JSObject
*> aObjects
, nsIPrincipal
* aPrincipal
,
220 nsTArray
<JS::Value
>& aResult
,
221 mozilla::ErrorResult
& aError
) {
222 SendMessage(aCx
, aMessageName
, aObj
, aObjects
, aPrincipal
, false, aResult
,
226 // GlobalProcessScriptLoader
227 void GetInitialProcessData(JSContext
* aCx
,
228 JS::MutableHandle
<JS::Value
> aInitialProcessData
,
229 mozilla::ErrorResult
& aError
);
231 mozilla::dom::ipc::WritableSharedMap
* SharedData();
233 NS_DECL_NSIMESSAGESENDER
235 static mozilla::dom::ProcessMessageManager
* NewProcessMessageManager(
238 void ReceiveMessage(nsISupports
* aTarget
, nsFrameLoader
* aTargetFrameLoader
,
239 const nsAString
& aMessage
, bool aIsSync
,
240 StructuredCloneData
* aCloneData
,
241 mozilla::jsipc::CpowHolder
* aCpows
,
242 nsIPrincipal
* aPrincipal
,
243 nsTArray
<StructuredCloneData
>* aRetVal
,
244 mozilla::ErrorResult
& aError
) {
245 ReceiveMessage(aTarget
, aTargetFrameLoader
, mClosed
, aMessage
, aIsSync
,
246 aCloneData
, aCpows
, aPrincipal
, aRetVal
, aError
);
249 void Disconnect(bool aRemoveFromParent
= true);
252 void SetCallback(mozilla::dom::ipc::MessageManagerCallback
* aCallback
);
254 mozilla::dom::ipc::MessageManagerCallback
* GetCallback() { return mCallback
; }
256 nsresult
DispatchAsyncMessageInternal(JSContext
* aCx
,
257 const nsAString
& aMessage
,
258 StructuredCloneData
& aData
,
259 JS::Handle
<JSObject
*> aCpows
,
260 nsIPrincipal
* aPrincipal
);
261 bool IsGlobal() { return mGlobal
; }
262 bool IsBroadcaster() { return mIsBroadcaster
; }
263 bool IsChrome() { return mChrome
; }
265 // GetGlobalMessageManager creates the global message manager if it hasn't
267 static already_AddRefed
<mozilla::dom::ChromeMessageBroadcaster
>
268 GetGlobalMessageManager();
269 static mozilla::dom::ParentProcessMessageManager
* GetParentProcessManager() {
270 return sParentProcessManager
;
272 static mozilla::dom::ChildProcessMessageManager
* GetChildProcessManager() {
273 return sChildProcessManager
;
275 static void SetChildProcessManager(
276 mozilla::dom::ChildProcessMessageManager
* aManager
) {
277 sChildProcessManager
= aManager
;
280 static bool GetParamsForMessage(JSContext
* aCx
, const JS::Value
& aValue
,
281 const JS::Value
& aTransfer
,
282 StructuredCloneData
& aData
);
284 void SetInitialProcessData(JS::HandleValue aInitialData
);
286 void LoadPendingScripts();
289 friend class MMListenerRemover
;
291 virtual mozilla::dom::MessageBroadcaster
* GetParentManager() {
294 virtual void ClearParentManager(bool aRemove
) {}
296 void DispatchAsyncMessage(JSContext
* aCx
, const nsAString
& aMessageName
,
297 JS::Handle
<JS::Value
> aObj
,
298 JS::Handle
<JSObject
*> aObjects
,
299 nsIPrincipal
* aPrincipal
,
300 JS::Handle
<JS::Value
> aTransfers
,
301 mozilla::ErrorResult
& aError
);
303 void SendMessage(JSContext
* aCx
, const nsAString
& aMessageName
,
304 JS::Handle
<JS::Value
> aObj
, JS::Handle
<JSObject
*> aObjects
,
305 nsIPrincipal
* aPrincipal
, bool aIsSync
,
306 nsTArray
<JS::Value
>& aResult
, mozilla::ErrorResult
& aError
);
308 void ReceiveMessage(nsISupports
* aTarget
, nsFrameLoader
* aTargetFrameLoader
,
309 bool aTargetClosed
, const nsAString
& aMessage
,
310 bool aIsSync
, StructuredCloneData
* aCloneData
,
311 mozilla::jsipc::CpowHolder
* aCpows
,
312 nsIPrincipal
* aPrincipal
,
313 nsTArray
<StructuredCloneData
>* aRetVal
,
314 mozilla::ErrorResult
& aError
);
316 void LoadScript(const nsAString
& aURL
, bool aAllowDelayedLoad
,
317 bool aRunInGlobalScope
, mozilla::ErrorResult
& aError
);
318 void RemoveDelayedScript(const nsAString
& aURL
);
319 void GetDelayedScripts(JSContext
* aCx
, nsTArray
<nsTArray
<JS::Value
>>& aList
,
320 mozilla::ErrorResult
& aError
);
322 // We keep the message listeners as arrays in a hastable indexed by the
323 // message name. That gives us fast lookups in ReceiveMessage().
324 nsClassHashtable
<nsStringHashKey
,
325 nsAutoTObserverArray
<nsMessageListenerInfo
, 1>>
327 nsTArray
<RefPtr
<mozilla::dom::MessageListenerManager
>> mChildManagers
;
328 bool mChrome
; // true if we're in the chrome process
329 bool mGlobal
; // true if we're the global frame message manager
330 bool mIsProcessManager
; // true if the message manager belongs to the process
332 bool mIsBroadcaster
; // true if the message manager is a broadcaster
334 bool mHandlingMessage
;
335 bool mClosed
; // true if we can no longer send messages
337 mozilla::dom::ipc::MessageManagerCallback
* mCallback
;
338 nsAutoPtr
<mozilla::dom::ipc::MessageManagerCallback
> mOwnedCallback
;
339 nsTArray
<nsString
> mPendingScripts
;
340 nsTArray
<bool> mPendingScriptsGlobalStates
;
341 JS::Heap
<JS::Value
> mInitialProcessData
;
342 RefPtr
<mozilla::dom::ipc::WritableSharedMap
> mSharedData
;
344 void LoadPendingScripts(nsFrameMessageManager
* aManager
,
345 nsFrameMessageManager
* aChildMM
);
348 static mozilla::dom::ParentProcessMessageManager
* sParentProcessManager
;
349 static nsFrameMessageManager
* sSameProcessParentManager
;
350 static nsTArray
<nsCOMPtr
<nsIRunnable
>>* sPendingSameProcessAsyncMessages
;
353 static mozilla::dom::ChildProcessMessageManager
* sChildProcessManager
;
356 /* A helper class for taking care of many details for async message sending
357 within a single process. Intended to be used like so:
359 class MyAsyncMessage : public nsSameProcessAsyncMessageBase, public Runnable
362 ReceiveMessage(..., ...);
368 RefPtr<nsSameProcessAsyncMessageBase> ev = new MyAsyncMessage();
369 nsresult rv = ev->Init(...);
370 if (NS_SUCCEEDED(rv)) {
371 NS_DispatchToMainThread(ev);
374 class nsSameProcessAsyncMessageBase
{
376 typedef mozilla::dom::ipc::StructuredCloneData StructuredCloneData
;
378 nsSameProcessAsyncMessageBase(JS::RootingContext
* aRootingCx
,
379 JS::Handle
<JSObject
*> aCpows
);
380 nsresult
Init(const nsAString
& aMessage
, StructuredCloneData
& aData
,
381 nsIPrincipal
* aPrincipal
);
383 void ReceiveMessage(nsISupports
* aTarget
, nsFrameLoader
* aTargetFrameLoader
,
384 nsFrameMessageManager
* aManager
);
387 nsSameProcessAsyncMessageBase(const nsSameProcessAsyncMessageBase
&);
390 StructuredCloneData mData
;
391 JS::PersistentRooted
<JSObject
*> mCpows
;
392 nsCOMPtr
<nsIPrincipal
> mPrincipal
;
398 class nsScriptCacheCleaner
;
400 struct nsMessageManagerScriptHolder
{
401 nsMessageManagerScriptHolder(JSContext
* aCx
, JSScript
* aScript
)
402 : mScript(aCx
, aScript
) {
403 MOZ_COUNT_CTOR(nsMessageManagerScriptHolder
);
406 ~nsMessageManagerScriptHolder() {
407 MOZ_COUNT_DTOR(nsMessageManagerScriptHolder
);
410 JS::PersistentRooted
<JSScript
*> mScript
;
413 class nsMessageManagerScriptExecutor
{
415 static void PurgeCache();
416 static void Shutdown();
418 void MarkScopesForCC();
421 friend class nsMessageManagerScriptCx
;
422 nsMessageManagerScriptExecutor() {
423 MOZ_COUNT_CTOR(nsMessageManagerScriptExecutor
);
425 ~nsMessageManagerScriptExecutor() {
426 MOZ_COUNT_DTOR(nsMessageManagerScriptExecutor
);
429 void DidCreateScriptLoader();
430 void LoadScriptInternal(JS::Handle
<JSObject
*> aMessageManager
,
431 const nsAString
& aURL
, bool aRunInUniqueScope
);
432 void TryCacheLoadAndCompileScript(const nsAString
& aURL
,
433 bool aRunInUniqueScope
, bool aShouldCache
,
434 JS::Handle
<JSObject
*> aMessageManager
,
435 JS::MutableHandle
<JSScript
*> aScriptp
);
437 void Trace(const TraceCallbacks
& aCallbacks
, void* aClosure
);
439 nsCOMPtr
<nsIPrincipal
> mPrincipal
;
440 AutoTArray
<JS::Heap
<JSObject
*>, 2> mAnonymousGlobalScopes
;
442 // Returns true if this is a process message manager. There should only be a
443 // single process message manager per session, so instances of this type will
444 // optimize their script loading to avoid unnecessary duplication.
445 virtual bool IsProcessScoped() const { return false; }
447 static nsDataHashtable
<nsStringHashKey
, nsMessageManagerScriptHolder
*>*
449 static mozilla::StaticRefPtr
<nsScriptCacheCleaner
> sScriptCacheCleaner
;
452 class nsScriptCacheCleaner final
: public nsIObserver
{
453 ~nsScriptCacheCleaner() {}
457 nsScriptCacheCleaner() {
458 nsCOMPtr
<nsIObserverService
> obsSvc
=
459 mozilla::services::GetObserverService();
461 obsSvc
->AddObserver(this, "message-manager-flush-caches", false);
462 obsSvc
->AddObserver(this, "xpcom-shutdown", false);
466 NS_IMETHOD
Observe(nsISupports
* aSubject
, const char* aTopic
,
467 const char16_t
* aData
) override
{
468 if (strcmp("message-manager-flush-caches", aTopic
) == 0) {
469 nsMessageManagerScriptExecutor::PurgeCache();
470 } else if (strcmp("xpcom-shutdown", aTopic
) == 0) {
471 nsMessageManagerScriptExecutor::Shutdown();