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/. */
6 
7 #ifndef nsFrameMessageManager_h__
8 #define nsFrameMessageManager_h__
9 
10 #include "nsIMessageManager.h"
11 #include "nsIObserver.h"
12 #include "nsCOMPtr.h"
13 #include "nsAutoPtr.h"
14 #include "nsCOMArray.h"
15 #include "nsTArray.h"
16 #include "nsIAtom.h"
17 #include "nsCycleCollectionParticipant.h"
18 #include "nsTArray.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 "nsWeakPtr.h"
28 #include "mozilla/Attributes.h"
29 #include "js/RootingAPI.h"
30 #include "nsTObserverArray.h"
31 #include "mozilla/dom/SameProcessMessageQueue.h"
32 #include "mozilla/dom/ipc/StructuredCloneData.h"
33 #include "mozilla/jsipc/CpowHolder.h"
34 
35 class nsIFrameLoader;
36 
37 namespace mozilla {
38 namespace dom {
39 
40 class nsIContentParent;
41 class nsIContentChild;
42 class ClonedMessageData;
43 class MessageManagerReporter;
44 
45 namespace ipc {
46 
47 enum MessageManagerFlags {
48   MM_CHILD = 0,
49   MM_CHROME = 1,
50   MM_GLOBAL = 2,
51   MM_PROCESSMANAGER = 4,
52   MM_BROADCASTER = 8,
53   MM_OWNSCALLBACK = 16
54 };
55 
56 class MessageManagerCallback
57 {
58 public:
~MessageManagerCallback()59   virtual ~MessageManagerCallback() {}
60 
DoLoadMessageManagerScript(const nsAString & aURL,bool aRunInGlobalScope)61   virtual bool DoLoadMessageManagerScript(const nsAString& aURL, bool aRunInGlobalScope)
62   {
63     return true;
64   }
65 
DoSendBlockingMessage(JSContext * aCx,const nsAString & aMessage,StructuredCloneData & aData,JS::Handle<JSObject * > aCpows,nsIPrincipal * aPrincipal,nsTArray<StructuredCloneData> * aRetVal,bool aIsSync)66   virtual bool DoSendBlockingMessage(JSContext* aCx,
67                                      const nsAString& aMessage,
68                                      StructuredCloneData& aData,
69                                      JS::Handle<JSObject*> aCpows,
70                                      nsIPrincipal* aPrincipal,
71                                      nsTArray<StructuredCloneData>* aRetVal,
72                                      bool aIsSync)
73   {
74     return true;
75   }
76 
DoSendAsyncMessage(JSContext * aCx,const nsAString & aMessage,StructuredCloneData & aData,JS::Handle<JSObject * > aCpows,nsIPrincipal * aPrincipal)77   virtual nsresult DoSendAsyncMessage(JSContext* aCx,
78                                       const nsAString& aMessage,
79                                       StructuredCloneData& aData,
80                                       JS::Handle<JSObject*> aCpows,
81                                       nsIPrincipal* aPrincipal)
82   {
83     return NS_OK;
84   }
85 
CheckPermission(const nsAString & aPermission)86   virtual bool CheckPermission(const nsAString& aPermission)
87   {
88     return false;
89   }
90 
CheckManifestURL(const nsAString & aManifestURL)91   virtual bool CheckManifestURL(const nsAString& aManifestURL)
92   {
93     return false;
94   }
95 
CheckAppHasPermission(const nsAString & aPermission)96   virtual bool CheckAppHasPermission(const nsAString& aPermission)
97   {
98     return false;
99   }
100 
CheckAppHasStatus(unsigned short aStatus)101   virtual bool CheckAppHasStatus(unsigned short aStatus)
102   {
103     return false;
104   }
105 
KillChild()106   virtual bool KillChild()
107   {
108     // By default, does nothing.
109     return false;
110   }
111 
GetProcessMessageManager()112   virtual nsIMessageSender* GetProcessMessageManager() const
113   {
114     return nullptr;
115   }
116 
117 protected:
118   bool BuildClonedMessageDataForParent(nsIContentParent* aParent,
119                                        StructuredCloneData& aData,
120                                        ClonedMessageData& aClonedData);
121   bool BuildClonedMessageDataForChild(nsIContentChild* aChild,
122                                       StructuredCloneData& aData,
123                                       ClonedMessageData& aClonedData);
124 };
125 
126 void UnpackClonedMessageDataForParent(const ClonedMessageData& aClonedData,
127                                       StructuredCloneData& aData);
128 
129 void UnpackClonedMessageDataForChild(const ClonedMessageData& aClonedData,
130                                      StructuredCloneData& aData);
131 
132 } // namespace ipc
133 } // namespace dom
134 } // namespace mozilla
135 
136 struct nsMessageListenerInfo
137 {
138   bool operator==(const nsMessageListenerInfo& aOther) const
139   {
140     return &aOther == this;
141   }
142 
143   // Exactly one of mStrongListener and mWeakListener must be non-null.
144   nsCOMPtr<nsIMessageListener> mStrongListener;
145   nsWeakPtr mWeakListener;
146   bool mListenWhenClosed;
147 };
148 
149 
150 class MOZ_STACK_CLASS SameProcessCpowHolder : public mozilla::jsipc::CpowHolder
151 {
152 public:
SameProcessCpowHolder(JS::RootingContext * aRootingCx,JS::Handle<JSObject * > aObj)153   SameProcessCpowHolder(JS::RootingContext* aRootingCx, JS::Handle<JSObject*> aObj)
154     : mObj(aRootingCx, aObj)
155   {
156   }
157 
158   virtual bool ToObject(JSContext* aCx, JS::MutableHandle<JSObject*> aObjp)
159     override;
160 
161 private:
162   JS::Rooted<JSObject*> mObj;
163 };
164 
165 class nsFrameMessageManager final : public nsIContentFrameMessageManager,
166                                     public nsIMessageBroadcaster,
167                                     public nsIFrameScriptLoader,
168                                     public nsIGlobalProcessScriptLoader,
169                                     public nsIProcessChecker
170 {
171   friend class mozilla::dom::MessageManagerReporter;
172   typedef mozilla::dom::ipc::StructuredCloneData StructuredCloneData;
173 public:
174   nsFrameMessageManager(mozilla::dom::ipc::MessageManagerCallback* aCallback,
175                         nsFrameMessageManager* aParentManager,
176                         /* mozilla::dom::ipc::MessageManagerFlags */ uint32_t aFlags);
177 
178 private:
179   ~nsFrameMessageManager();
180 
181 public:
182   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
183   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(nsFrameMessageManager,
184                                                          nsIContentFrameMessageManager)
185   NS_DECL_NSIMESSAGELISTENERMANAGER
186   NS_DECL_NSIMESSAGESENDER
187   NS_DECL_NSIMESSAGEBROADCASTER
188   NS_DECL_NSISYNCMESSAGESENDER
189   NS_DECL_NSIMESSAGEMANAGERGLOBAL
190   NS_DECL_NSICONTENTFRAMEMESSAGEMANAGER
191   NS_DECL_NSIFRAMESCRIPTLOADER
192   NS_DECL_NSIPROCESSSCRIPTLOADER
193   NS_DECL_NSIGLOBALPROCESSSCRIPTLOADER
194   NS_DECL_NSIPROCESSCHECKER
195 
196   static nsFrameMessageManager*
197   NewProcessMessageManager(bool aIsRemote);
198 
199   nsresult ReceiveMessage(nsISupports* aTarget, nsIFrameLoader* aTargetFrameLoader,
200                           const nsAString& aMessage,
201                           bool aIsSync, StructuredCloneData* aCloneData,
202                           mozilla::jsipc::CpowHolder* aCpows, nsIPrincipal* aPrincipal,
203                           nsTArray<StructuredCloneData>* aRetVal);
204 
205   void AddChildManager(nsFrameMessageManager* aManager);
RemoveChildManager(nsFrameMessageManager * aManager)206   void RemoveChildManager(nsFrameMessageManager* aManager)
207   {
208     mChildManagers.RemoveObject(aManager);
209   }
210   void Disconnect(bool aRemoveFromParent = true);
211   void Close();
212 
213   void InitWithCallback(mozilla::dom::ipc::MessageManagerCallback* aCallback);
214   void SetCallback(mozilla::dom::ipc::MessageManagerCallback* aCallback);
215 
GetCallback()216   mozilla::dom::ipc::MessageManagerCallback* GetCallback()
217   {
218     return mCallback;
219   }
220 
221   nsresult DispatchAsyncMessage(const nsAString& aMessageName,
222                                 const JS::Value& aJSON,
223                                 const JS::Value& aObjects,
224                                 nsIPrincipal* aPrincipal,
225                                 const JS::Value& aTransfers,
226                                 JSContext* aCx,
227                                 uint8_t aArgc);
228 
229   nsresult DispatchAsyncMessageInternal(JSContext* aCx,
230                                         const nsAString& aMessage,
231                                         StructuredCloneData& aData,
232                                         JS::Handle<JSObject*> aCpows,
233                                         nsIPrincipal* aPrincipal);
234   void RemoveFromParent();
GetParentManager()235   nsFrameMessageManager* GetParentManager() { return mParentManager; }
SetParentManager(nsFrameMessageManager * aParent)236   void SetParentManager(nsFrameMessageManager* aParent)
237   {
238     NS_ASSERTION(!mParentManager, "We have parent manager already!");
239     NS_ASSERTION(mChrome, "Should not set parent manager!");
240     mParentManager = aParent;
241   }
IsGlobal()242   bool IsGlobal() { return mGlobal; }
IsBroadcaster()243   bool IsBroadcaster() { return mIsBroadcaster; }
244 
GetParentProcessManager()245   static nsFrameMessageManager* GetParentProcessManager()
246   {
247     return sParentProcessManager;
248   }
GetChildProcessManager()249   static nsFrameMessageManager* GetChildProcessManager()
250   {
251     return sChildProcessManager;
252   }
SetChildProcessManager(nsFrameMessageManager * aManager)253   static void SetChildProcessManager(nsFrameMessageManager* aManager)
254   {
255     sChildProcessManager = aManager;
256   }
257 
258   void SetInitialProcessData(JS::HandleValue aInitialData);
259 
260   void LoadPendingScripts();
261 
262 private:
263   nsresult SendMessage(const nsAString& aMessageName,
264                        JS::Handle<JS::Value> aJSON,
265                        JS::Handle<JS::Value> aObjects,
266                        nsIPrincipal* aPrincipal,
267                        JSContext* aCx,
268                        uint8_t aArgc,
269                        JS::MutableHandle<JS::Value> aRetval,
270                        bool aIsSync);
271 
272   nsresult ReceiveMessage(nsISupports* aTarget, nsIFrameLoader* aTargetFrameLoader,
273                           bool aTargetClosed, const nsAString& aMessage,
274                           bool aIsSync, StructuredCloneData* aCloneData,
275                           mozilla::jsipc::CpowHolder* aCpows, nsIPrincipal* aPrincipal,
276                           nsTArray<StructuredCloneData>* aRetVal);
277 
278   NS_IMETHOD LoadScript(const nsAString& aURL,
279                         bool aAllowDelayedLoad,
280                         bool aRunInGlobalScope);
281   NS_IMETHOD RemoveDelayedScript(const nsAString& aURL);
282   NS_IMETHOD GetDelayedScripts(JSContext* aCx, JS::MutableHandle<JS::Value> aList);
283 
284 protected:
285   friend class MMListenerRemover;
286   // We keep the message listeners as arrays in a hastable indexed by the
287   // message name. That gives us fast lookups in ReceiveMessage().
288   nsClassHashtable<nsStringHashKey,
289                    nsAutoTObserverArray<nsMessageListenerInfo, 1>> mListeners;
290   nsCOMArray<nsIContentFrameMessageManager> mChildManagers;
291   bool mChrome;     // true if we're in the chrome process
292   bool mGlobal;     // true if we're the global frame message manager
293   bool mIsProcessManager; // true if the message manager belongs to the process realm
294   bool mIsBroadcaster; // true if the message manager is a broadcaster
295   bool mOwnsCallback;
296   bool mHandlingMessage;
297   bool mClosed;    // true if we can no longer send messages
298   bool mDisconnected;
299   mozilla::dom::ipc::MessageManagerCallback* mCallback;
300   nsAutoPtr<mozilla::dom::ipc::MessageManagerCallback> mOwnedCallback;
301   RefPtr<nsFrameMessageManager> mParentManager;
302   nsTArray<nsString> mPendingScripts;
303   nsTArray<bool> mPendingScriptsGlobalStates;
304   JS::Heap<JS::Value> mInitialProcessData;
305 
306   void LoadPendingScripts(nsFrameMessageManager* aManager,
307                           nsFrameMessageManager* aChildMM);
308 public:
309   static nsFrameMessageManager* sParentProcessManager;
310   static nsFrameMessageManager* sSameProcessParentManager;
311   static nsTArray<nsCOMPtr<nsIRunnable> >* sPendingSameProcessAsyncMessages;
312 private:
313   static nsFrameMessageManager* sChildProcessManager;
314   enum ProcessCheckerType {
315     PROCESS_CHECKER_PERMISSION,
316     PROCESS_CHECKER_MANIFEST_URL,
317     ASSERT_APP_HAS_PERMISSION
318   };
319   nsresult AssertProcessInternal(ProcessCheckerType aType,
320                                  const nsAString& aCapability,
321                                  bool* aValid);
322 };
323 
324 /* A helper class for taking care of many details for async message sending
325    within a single process.  Intended to be used like so:
326 
327    class MyAsyncMessage : public nsSameProcessAsyncMessageBase, public Runnable
328    {
329      NS_IMETHOD Run() override {
330        ReceiveMessage(..., ...);
331        return NS_OK;
332      }
333    };
334 
335 
336    RefPtr<nsSameProcessAsyncMessageBase> ev = new MyAsyncMessage();
337    nsresult rv = ev->Init(...);
338    if (NS_SUCCEEDED(rv)) {
339      NS_DispatchToMainThread(ev);
340    }
341 */
342 class nsSameProcessAsyncMessageBase
343 {
344 public:
345   typedef mozilla::dom::ipc::StructuredCloneData StructuredCloneData;
346 
347   nsSameProcessAsyncMessageBase(JS::RootingContext* aRootingCx,
348                                 JS::Handle<JSObject*> aCpows);
349   nsresult Init(const nsAString& aMessage,
350                 StructuredCloneData& aData,
351                 nsIPrincipal* aPrincipal);
352 
353   void ReceiveMessage(nsISupports* aTarget, nsIFrameLoader* aTargetFrameLoader,
354                       nsFrameMessageManager* aManager);
355 private:
356   nsSameProcessAsyncMessageBase(const nsSameProcessAsyncMessageBase&);
357 
358   JS::RootingContext* mRootingCx;
359   nsString mMessage;
360   StructuredCloneData mData;
361   JS::PersistentRooted<JSObject*> mCpows;
362   nsCOMPtr<nsIPrincipal> mPrincipal;
363 #ifdef DEBUG
364   bool mCalledInit;
365 #endif
366 };
367 
368 class nsScriptCacheCleaner;
369 
370 struct nsMessageManagerScriptHolder
371 {
nsMessageManagerScriptHoldernsMessageManagerScriptHolder372   nsMessageManagerScriptHolder(JSContext* aCx,
373                                JSScript* aScript,
374                                bool aRunInGlobalScope)
375    : mScript(aCx, aScript), mRunInGlobalScope(aRunInGlobalScope)
376   { MOZ_COUNT_CTOR(nsMessageManagerScriptHolder); }
377 
~nsMessageManagerScriptHoldernsMessageManagerScriptHolder378   ~nsMessageManagerScriptHolder()
379   { MOZ_COUNT_DTOR(nsMessageManagerScriptHolder); }
380 
WillRunInGlobalScopensMessageManagerScriptHolder381   bool WillRunInGlobalScope() { return mRunInGlobalScope; }
382 
383   JS::PersistentRooted<JSScript*> mScript;
384   bool mRunInGlobalScope;
385 };
386 
387 class nsMessageManagerScriptExecutor
388 {
389 public:
390   static void PurgeCache();
391   static void Shutdown();
GetGlobal()392   already_AddRefed<nsIXPConnectJSObjectHolder> GetGlobal()
393   {
394     nsCOMPtr<nsIXPConnectJSObjectHolder> ref = mGlobal;
395     return ref.forget();
396   }
397 
398   void MarkScopesForCC();
399 protected:
400   friend class nsMessageManagerScriptCx;
nsMessageManagerScriptExecutor()401   nsMessageManagerScriptExecutor() { MOZ_COUNT_CTOR(nsMessageManagerScriptExecutor); }
~nsMessageManagerScriptExecutor()402   ~nsMessageManagerScriptExecutor() { MOZ_COUNT_DTOR(nsMessageManagerScriptExecutor); }
403 
404   void DidCreateGlobal();
405   void LoadScriptInternal(const nsAString& aURL, bool aRunInGlobalScope);
406   void TryCacheLoadAndCompileScript(const nsAString& aURL,
407                                     bool aRunInGlobalScope,
408                                     bool aShouldCache,
409                                     JS::MutableHandle<JSScript*> aScriptp);
410   void TryCacheLoadAndCompileScript(const nsAString& aURL,
411                                     bool aRunInGlobalScope);
412   bool InitChildGlobalInternal(nsISupports* aScope, const nsACString& aID);
413   void Trace(const TraceCallbacks& aCallbacks, void* aClosure);
414   nsCOMPtr<nsIXPConnectJSObjectHolder> mGlobal;
415   nsCOMPtr<nsIPrincipal> mPrincipal;
416   AutoTArray<JS::Heap<JSObject*>, 2> mAnonymousGlobalScopes;
417 
418   static nsDataHashtable<nsStringHashKey, nsMessageManagerScriptHolder*>* sCachedScripts;
419   static mozilla::StaticRefPtr<nsScriptCacheCleaner> sScriptCacheCleaner;
420 };
421 
422 class nsScriptCacheCleaner final : public nsIObserver
423 {
~nsScriptCacheCleaner()424   ~nsScriptCacheCleaner() {}
425 
426   NS_DECL_ISUPPORTS
427 
nsScriptCacheCleaner()428   nsScriptCacheCleaner()
429   {
430     nsCOMPtr<nsIObserverService> obsSvc = mozilla::services::GetObserverService();
431     if (obsSvc) {
432       obsSvc->AddObserver(this, "message-manager-flush-caches", false);
433       obsSvc->AddObserver(this, "xpcom-shutdown", false);
434     }
435   }
436 
Observe(nsISupports * aSubject,const char * aTopic,const char16_t * aData)437   NS_IMETHOD Observe(nsISupports *aSubject,
438                      const char *aTopic,
439                      const char16_t *aData) override
440   {
441     if (strcmp("message-manager-flush-caches", aTopic) == 0) {
442       nsMessageManagerScriptExecutor::PurgeCache();
443     } else if (strcmp("xpcom-shutdown", aTopic) == 0) {
444       nsMessageManagerScriptExecutor::Shutdown();
445     }
446     return NS_OK;
447   }
448 };
449 
450 #endif
451