|
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 "nsIObserverService.h" |
|
25 #include "nsThreadUtils.h" |
|
26 #include "nsWeakPtr.h" |
|
27 #include "mozilla/Attributes.h" |
|
28 #include "js/RootingAPI.h" |
|
29 #include "nsTObserverArray.h" |
|
30 #include "mozilla/dom/StructuredCloneUtils.h" |
|
31 |
|
32 namespace mozilla { |
|
33 namespace dom { |
|
34 |
|
35 class ContentParent; |
|
36 class ContentChild; |
|
37 class ClonedMessageData; |
|
38 class MessageManagerReporter; |
|
39 |
|
40 namespace ipc { |
|
41 |
|
42 enum MessageManagerFlags { |
|
43 MM_CHILD = 0, |
|
44 MM_CHROME = 1, |
|
45 MM_GLOBAL = 2, |
|
46 MM_PROCESSMANAGER = 4, |
|
47 MM_BROADCASTER = 8, |
|
48 MM_OWNSCALLBACK = 16 |
|
49 }; |
|
50 |
|
51 class MessageManagerCallback |
|
52 { |
|
53 public: |
|
54 virtual ~MessageManagerCallback() {} |
|
55 |
|
56 virtual bool DoLoadFrameScript(const nsAString& aURL, bool aRunInGlobalScope) |
|
57 { |
|
58 return true; |
|
59 } |
|
60 |
|
61 virtual bool DoSendBlockingMessage(JSContext* aCx, |
|
62 const nsAString& aMessage, |
|
63 const StructuredCloneData& aData, |
|
64 JS::Handle<JSObject*> aCpows, |
|
65 nsIPrincipal* aPrincipal, |
|
66 InfallibleTArray<nsString>* aJSONRetVal, |
|
67 bool aIsSync) |
|
68 { |
|
69 return true; |
|
70 } |
|
71 |
|
72 virtual bool DoSendAsyncMessage(JSContext* aCx, |
|
73 const nsAString& aMessage, |
|
74 const StructuredCloneData& aData, |
|
75 JS::Handle<JSObject*> aCpows, |
|
76 nsIPrincipal* aPrincipal) |
|
77 { |
|
78 return true; |
|
79 } |
|
80 |
|
81 virtual bool CheckPermission(const nsAString& aPermission) |
|
82 { |
|
83 return false; |
|
84 } |
|
85 |
|
86 virtual bool CheckManifestURL(const nsAString& aManifestURL) |
|
87 { |
|
88 return false; |
|
89 } |
|
90 |
|
91 virtual bool CheckAppHasPermission(const nsAString& aPermission) |
|
92 { |
|
93 return false; |
|
94 } |
|
95 |
|
96 virtual bool CheckAppHasStatus(unsigned short aStatus) |
|
97 { |
|
98 return false; |
|
99 } |
|
100 |
|
101 protected: |
|
102 bool BuildClonedMessageDataForParent(ContentParent* aParent, |
|
103 const StructuredCloneData& aData, |
|
104 ClonedMessageData& aClonedData); |
|
105 bool BuildClonedMessageDataForChild(ContentChild* aChild, |
|
106 const StructuredCloneData& aData, |
|
107 ClonedMessageData& aClonedData); |
|
108 }; |
|
109 |
|
110 StructuredCloneData UnpackClonedMessageDataForParent(const ClonedMessageData& aData); |
|
111 StructuredCloneData UnpackClonedMessageDataForChild(const ClonedMessageData& aData); |
|
112 |
|
113 } // namespace ipc |
|
114 } // namespace dom |
|
115 } // namespace mozilla |
|
116 |
|
117 class nsAXPCNativeCallContext; |
|
118 |
|
119 struct nsMessageListenerInfo |
|
120 { |
|
121 bool operator==(const nsMessageListenerInfo& aOther) const |
|
122 { |
|
123 return &aOther == this; |
|
124 } |
|
125 |
|
126 // Exactly one of mStrongListener and mWeakListener must be non-null. |
|
127 nsCOMPtr<nsIMessageListener> mStrongListener; |
|
128 nsWeakPtr mWeakListener; |
|
129 }; |
|
130 |
|
131 class CpowHolder |
|
132 { |
|
133 public: |
|
134 virtual bool ToObject(JSContext* cx, JS::MutableHandle<JSObject*> objp) = 0; |
|
135 }; |
|
136 |
|
137 class MOZ_STACK_CLASS SameProcessCpowHolder : public CpowHolder |
|
138 { |
|
139 public: |
|
140 SameProcessCpowHolder(JSRuntime *aRuntime, JS::Handle<JSObject*> aObj) |
|
141 : mObj(aRuntime, aObj) |
|
142 { |
|
143 } |
|
144 |
|
145 bool ToObject(JSContext* aCx, JS::MutableHandle<JSObject*> aObjp); |
|
146 |
|
147 private: |
|
148 JS::Rooted<JSObject*> mObj; |
|
149 }; |
|
150 |
|
151 class nsFrameMessageManager MOZ_FINAL : public nsIContentFrameMessageManager, |
|
152 public nsIMessageBroadcaster, |
|
153 public nsIFrameScriptLoader, |
|
154 public nsIProcessChecker |
|
155 { |
|
156 friend class mozilla::dom::MessageManagerReporter; |
|
157 typedef mozilla::dom::StructuredCloneData StructuredCloneData; |
|
158 public: |
|
159 nsFrameMessageManager(mozilla::dom::ipc::MessageManagerCallback* aCallback, |
|
160 nsFrameMessageManager* aParentManager, |
|
161 /* mozilla::dom::ipc::MessageManagerFlags */ uint32_t aFlags) |
|
162 : mChrome(!!(aFlags & mozilla::dom::ipc::MM_CHROME)), |
|
163 mGlobal(!!(aFlags & mozilla::dom::ipc::MM_GLOBAL)), |
|
164 mIsProcessManager(!!(aFlags & mozilla::dom::ipc::MM_PROCESSMANAGER)), |
|
165 mIsBroadcaster(!!(aFlags & mozilla::dom::ipc::MM_BROADCASTER)), |
|
166 mOwnsCallback(!!(aFlags & mozilla::dom::ipc::MM_OWNSCALLBACK)), |
|
167 mHandlingMessage(false), |
|
168 mDisconnected(false), |
|
169 mCallback(aCallback), |
|
170 mParentManager(aParentManager) |
|
171 { |
|
172 NS_ASSERTION(mChrome || !aParentManager, "Should not set parent manager!"); |
|
173 NS_ASSERTION(!mIsBroadcaster || !mCallback, |
|
174 "Broadcasters cannot have callbacks!"); |
|
175 // This is a bit hackish. When parent manager is global, we want |
|
176 // to attach the window message manager to it immediately. |
|
177 // Is it just the frame message manager which waits until the |
|
178 // content process is running. |
|
179 if (mParentManager && (mCallback || IsWindowLevel())) { |
|
180 mParentManager->AddChildManager(this); |
|
181 } |
|
182 if (mOwnsCallback) { |
|
183 mOwnedCallback = aCallback; |
|
184 } |
|
185 } |
|
186 |
|
187 ~nsFrameMessageManager() |
|
188 { |
|
189 for (int32_t i = mChildManagers.Count(); i > 0; --i) { |
|
190 static_cast<nsFrameMessageManager*>(mChildManagers[i - 1])-> |
|
191 Disconnect(false); |
|
192 } |
|
193 if (mIsProcessManager) { |
|
194 if (this == sParentProcessManager) { |
|
195 sParentProcessManager = nullptr; |
|
196 } |
|
197 if (this == sChildProcessManager) { |
|
198 sChildProcessManager = nullptr; |
|
199 delete sPendingSameProcessAsyncMessages; |
|
200 sPendingSameProcessAsyncMessages = nullptr; |
|
201 } |
|
202 if (this == sSameProcessParentManager) { |
|
203 sSameProcessParentManager = nullptr; |
|
204 } |
|
205 } |
|
206 } |
|
207 |
|
208 NS_DECL_CYCLE_COLLECTING_ISUPPORTS |
|
209 NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsFrameMessageManager, |
|
210 nsIContentFrameMessageManager) |
|
211 NS_DECL_NSIMESSAGELISTENERMANAGER |
|
212 NS_DECL_NSIMESSAGESENDER |
|
213 NS_DECL_NSIMESSAGEBROADCASTER |
|
214 NS_DECL_NSISYNCMESSAGESENDER |
|
215 NS_DECL_NSICONTENTFRAMEMESSAGEMANAGER |
|
216 NS_DECL_NSIFRAMESCRIPTLOADER |
|
217 NS_DECL_NSIPROCESSCHECKER |
|
218 |
|
219 static nsFrameMessageManager* |
|
220 NewProcessMessageManager(mozilla::dom::ContentParent* aProcess); |
|
221 |
|
222 nsresult ReceiveMessage(nsISupports* aTarget, const nsAString& aMessage, |
|
223 bool aIsSync, const StructuredCloneData* aCloneData, |
|
224 CpowHolder* aCpows, nsIPrincipal* aPrincipal, |
|
225 InfallibleTArray<nsString>* aJSONRetVal); |
|
226 |
|
227 void AddChildManager(nsFrameMessageManager* aManager); |
|
228 void RemoveChildManager(nsFrameMessageManager* aManager) |
|
229 { |
|
230 mChildManagers.RemoveObject(aManager); |
|
231 } |
|
232 void Disconnect(bool aRemoveFromParent = true); |
|
233 |
|
234 void InitWithCallback(mozilla::dom::ipc::MessageManagerCallback* aCallback); |
|
235 void SetCallback(mozilla::dom::ipc::MessageManagerCallback* aCallback); |
|
236 mozilla::dom::ipc::MessageManagerCallback* GetCallback() |
|
237 { |
|
238 return mCallback; |
|
239 } |
|
240 |
|
241 nsresult DispatchAsyncMessage(const nsAString& aMessageName, |
|
242 const JS::Value& aJSON, |
|
243 const JS::Value& aObjects, |
|
244 nsIPrincipal* aPrincipal, |
|
245 JSContext* aCx, |
|
246 uint8_t aArgc); |
|
247 nsresult DispatchAsyncMessageInternal(JSContext* aCx, |
|
248 const nsAString& aMessage, |
|
249 const StructuredCloneData& aData, |
|
250 JS::Handle<JSObject*> aCpows, |
|
251 nsIPrincipal* aPrincipal); |
|
252 void RemoveFromParent(); |
|
253 nsFrameMessageManager* GetParentManager() { return mParentManager; } |
|
254 void SetParentManager(nsFrameMessageManager* aParent) |
|
255 { |
|
256 NS_ASSERTION(!mParentManager, "We have parent manager already!"); |
|
257 NS_ASSERTION(mChrome, "Should not set parent manager!"); |
|
258 mParentManager = aParent; |
|
259 } |
|
260 bool IsGlobal() { return mGlobal; } |
|
261 bool IsWindowLevel() { return mParentManager && mParentManager->IsGlobal(); } |
|
262 |
|
263 static nsFrameMessageManager* GetParentProcessManager() |
|
264 { |
|
265 return sParentProcessManager; |
|
266 } |
|
267 static nsFrameMessageManager* GetChildProcessManager() |
|
268 { |
|
269 return sChildProcessManager; |
|
270 } |
|
271 private: |
|
272 nsresult SendMessage(const nsAString& aMessageName, |
|
273 JS::Handle<JS::Value> aJSON, |
|
274 JS::Handle<JS::Value> aObjects, |
|
275 nsIPrincipal* aPrincipal, |
|
276 JSContext* aCx, |
|
277 uint8_t aArgc, |
|
278 JS::MutableHandle<JS::Value> aRetval, |
|
279 bool aIsSync); |
|
280 protected: |
|
281 friend class MMListenerRemover; |
|
282 // We keep the message listeners as arrays in a hastable indexed by the |
|
283 // message name. That gives us fast lookups in ReceiveMessage(). |
|
284 nsClassHashtable<nsStringHashKey, |
|
285 nsAutoTObserverArray<nsMessageListenerInfo, 1>> mListeners; |
|
286 nsCOMArray<nsIContentFrameMessageManager> mChildManagers; |
|
287 bool mChrome; // true if we're in the chrome process |
|
288 bool mGlobal; // true if we're the global frame message manager |
|
289 bool mIsProcessManager; // true if the message manager belongs to the process realm |
|
290 bool mIsBroadcaster; // true if the message manager is a broadcaster |
|
291 bool mOwnsCallback; |
|
292 bool mHandlingMessage; |
|
293 bool mDisconnected; |
|
294 mozilla::dom::ipc::MessageManagerCallback* mCallback; |
|
295 nsAutoPtr<mozilla::dom::ipc::MessageManagerCallback> mOwnedCallback; |
|
296 nsFrameMessageManager* mParentManager; |
|
297 nsTArray<nsString> mPendingScripts; |
|
298 nsTArray<bool> mPendingScriptsGlobalStates; |
|
299 public: |
|
300 static nsFrameMessageManager* sParentProcessManager; |
|
301 static nsFrameMessageManager* sChildProcessManager; |
|
302 static nsFrameMessageManager* sSameProcessParentManager; |
|
303 static nsTArray<nsCOMPtr<nsIRunnable> >* sPendingSameProcessAsyncMessages; |
|
304 private: |
|
305 enum ProcessCheckerType { |
|
306 PROCESS_CHECKER_PERMISSION, |
|
307 PROCESS_CHECKER_MANIFEST_URL, |
|
308 ASSERT_APP_HAS_PERMISSION |
|
309 }; |
|
310 nsresult AssertProcessInternal(ProcessCheckerType aType, |
|
311 const nsAString& aCapability, |
|
312 bool* aValid); |
|
313 }; |
|
314 |
|
315 /* A helper class for taking care of many details for async message sending |
|
316 within a single process. Intended to be used like so: |
|
317 |
|
318 class MyAsyncMessage : public nsSameProcessAsyncMessageBase, public nsRunnable |
|
319 { |
|
320 // Initialize nsSameProcessAsyncMessageBase... |
|
321 |
|
322 NS_IMETHOD Run() { |
|
323 ReceiveMessage(..., ...); |
|
324 return NS_OK; |
|
325 } |
|
326 }; |
|
327 */ |
|
328 class nsSameProcessAsyncMessageBase |
|
329 { |
|
330 typedef mozilla::dom::StructuredCloneClosure StructuredCloneClosure; |
|
331 |
|
332 public: |
|
333 typedef mozilla::dom::StructuredCloneData StructuredCloneData; |
|
334 |
|
335 nsSameProcessAsyncMessageBase(JSContext* aCx, |
|
336 const nsAString& aMessage, |
|
337 const StructuredCloneData& aData, |
|
338 JS::Handle<JSObject*> aCpows, |
|
339 nsIPrincipal* aPrincipal); |
|
340 |
|
341 void ReceiveMessage(nsISupports* aTarget, nsFrameMessageManager* aManager); |
|
342 |
|
343 private: |
|
344 nsSameProcessAsyncMessageBase(const nsSameProcessAsyncMessageBase&); |
|
345 |
|
346 JSRuntime* mRuntime; |
|
347 nsString mMessage; |
|
348 JSAutoStructuredCloneBuffer mData; |
|
349 StructuredCloneClosure mClosure; |
|
350 JS::PersistentRooted<JSObject*> mCpows; |
|
351 nsCOMPtr<nsIPrincipal> mPrincipal; |
|
352 }; |
|
353 |
|
354 class nsScriptCacheCleaner; |
|
355 |
|
356 struct nsFrameScriptObjectExecutorHolder |
|
357 { |
|
358 nsFrameScriptObjectExecutorHolder(JSContext* aCx, JSScript* aScript) |
|
359 : mScript(aCx, aScript), mFunction(aCx, nullptr) |
|
360 { MOZ_COUNT_CTOR(nsFrameScriptObjectExecutorHolder); } |
|
361 |
|
362 nsFrameScriptObjectExecutorHolder(JSContext* aCx, JSObject* aFunction) |
|
363 : mScript(aCx, nullptr), mFunction(aCx, aFunction) |
|
364 { MOZ_COUNT_CTOR(nsFrameScriptObjectExecutorHolder); } |
|
365 |
|
366 ~nsFrameScriptObjectExecutorHolder() |
|
367 { MOZ_COUNT_DTOR(nsFrameScriptObjectExecutorHolder); } |
|
368 |
|
369 bool WillRunInGlobalScope() { return mScript; } |
|
370 |
|
371 JS::PersistentRooted<JSScript*> mScript; |
|
372 JS::PersistentRooted<JSObject*> mFunction; |
|
373 }; |
|
374 |
|
375 class nsFrameScriptObjectExecutorStackHolder; |
|
376 |
|
377 class nsFrameScriptExecutor |
|
378 { |
|
379 public: |
|
380 static void Shutdown(); |
|
381 already_AddRefed<nsIXPConnectJSObjectHolder> GetGlobal() |
|
382 { |
|
383 nsCOMPtr<nsIXPConnectJSObjectHolder> ref = mGlobal; |
|
384 return ref.forget(); |
|
385 } |
|
386 protected: |
|
387 friend class nsFrameScriptCx; |
|
388 nsFrameScriptExecutor() |
|
389 { MOZ_COUNT_CTOR(nsFrameScriptExecutor); } |
|
390 ~nsFrameScriptExecutor() |
|
391 { MOZ_COUNT_DTOR(nsFrameScriptExecutor); } |
|
392 void DidCreateGlobal(); |
|
393 void LoadFrameScriptInternal(const nsAString& aURL, bool aRunInGlobalScope); |
|
394 void TryCacheLoadAndCompileScript(const nsAString& aURL, |
|
395 bool aRunInGlobalScope, |
|
396 bool aShouldCache, |
|
397 JS::MutableHandle<JSScript*> aScriptp, |
|
398 JS::MutableHandle<JSObject*> aFunp); |
|
399 void TryCacheLoadAndCompileScript(const nsAString& aURL, |
|
400 bool aRunInGlobalScope); |
|
401 bool InitTabChildGlobalInternal(nsISupports* aScope, const nsACString& aID); |
|
402 nsCOMPtr<nsIXPConnectJSObjectHolder> mGlobal; |
|
403 nsCOMPtr<nsIPrincipal> mPrincipal; |
|
404 static nsDataHashtable<nsStringHashKey, nsFrameScriptObjectExecutorHolder*>* sCachedScripts; |
|
405 static nsScriptCacheCleaner* sScriptCacheCleaner; |
|
406 }; |
|
407 |
|
408 class nsScriptCacheCleaner MOZ_FINAL : public nsIObserver |
|
409 { |
|
410 NS_DECL_ISUPPORTS |
|
411 |
|
412 nsScriptCacheCleaner() |
|
413 { |
|
414 nsCOMPtr<nsIObserverService> obsSvc = mozilla::services::GetObserverService(); |
|
415 if (obsSvc) |
|
416 obsSvc->AddObserver(this, "xpcom-shutdown", false); |
|
417 } |
|
418 |
|
419 NS_IMETHODIMP Observe(nsISupports *aSubject, |
|
420 const char *aTopic, |
|
421 const char16_t *aData) |
|
422 { |
|
423 nsFrameScriptExecutor::Shutdown(); |
|
424 return NS_OK; |
|
425 } |
|
426 }; |
|
427 |
|
428 #endif |