|
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
|
2 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
3 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
5 |
|
6 #include "base/basictypes.h" |
|
7 |
|
8 #include "nsFrameMessageManager.h" |
|
9 |
|
10 #include "AppProcessChecker.h" |
|
11 #include "ContentChild.h" |
|
12 #include "ContentParent.h" |
|
13 #include "nsContentUtils.h" |
|
14 #include "nsCxPusher.h" |
|
15 #include "nsError.h" |
|
16 #include "nsIXPConnect.h" |
|
17 #include "jsapi.h" |
|
18 #include "nsJSUtils.h" |
|
19 #include "nsJSPrincipals.h" |
|
20 #include "nsNetUtil.h" |
|
21 #include "nsScriptLoader.h" |
|
22 #include "nsFrameLoader.h" |
|
23 #include "nsIXULRuntime.h" |
|
24 #include "nsIScriptError.h" |
|
25 #include "nsIConsoleService.h" |
|
26 #include "nsIMemoryReporter.h" |
|
27 #include "nsIProtocolHandler.h" |
|
28 #include "nsIScriptSecurityManager.h" |
|
29 #include "nsIJSRuntimeService.h" |
|
30 #include "nsIDOMClassInfo.h" |
|
31 #include "nsIDOMFile.h" |
|
32 #include "xpcpublic.h" |
|
33 #include "mozilla/Preferences.h" |
|
34 #include "mozilla/dom/StructuredCloneUtils.h" |
|
35 #include "mozilla/dom/PBlobChild.h" |
|
36 #include "mozilla/dom/PBlobParent.h" |
|
37 #include "JavaScriptChild.h" |
|
38 #include "JavaScriptParent.h" |
|
39 #include "mozilla/dom/DOMStringList.h" |
|
40 #include "nsPrintfCString.h" |
|
41 #include <algorithm> |
|
42 |
|
43 #ifdef ANDROID |
|
44 #include <android/log.h> |
|
45 #endif |
|
46 #ifdef XP_WIN |
|
47 #include <windows.h> |
|
48 # if defined(SendMessage) |
|
49 # undef SendMessage |
|
50 # endif |
|
51 #endif |
|
52 |
|
53 using namespace mozilla; |
|
54 using namespace mozilla::dom; |
|
55 using namespace mozilla::dom::ipc; |
|
56 |
|
57 static PLDHashOperator |
|
58 CycleCollectorTraverseListeners(const nsAString& aKey, |
|
59 nsAutoTObserverArray<nsMessageListenerInfo, 1>* aListeners, |
|
60 void* aCb) |
|
61 { |
|
62 nsCycleCollectionTraversalCallback* cb = |
|
63 static_cast<nsCycleCollectionTraversalCallback*> (aCb); |
|
64 uint32_t count = aListeners->Length(); |
|
65 for (uint32_t i = 0; i < count; ++i) { |
|
66 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, "listeners[i] mStrongListener"); |
|
67 cb->NoteXPCOMChild(aListeners->ElementAt(i).mStrongListener.get()); |
|
68 } |
|
69 return PL_DHASH_NEXT; |
|
70 } |
|
71 |
|
72 NS_IMPL_CYCLE_COLLECTION_CLASS(nsFrameMessageManager) |
|
73 |
|
74 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsFrameMessageManager) |
|
75 tmp->mListeners.EnumerateRead(CycleCollectorTraverseListeners, |
|
76 static_cast<void*>(&cb)); |
|
77 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChildManagers) |
|
78 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END |
|
79 |
|
80 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsFrameMessageManager) |
|
81 tmp->mListeners.Clear(); |
|
82 for (int32_t i = tmp->mChildManagers.Count(); i > 0; --i) { |
|
83 static_cast<nsFrameMessageManager*>(tmp->mChildManagers[i - 1])-> |
|
84 Disconnect(false); |
|
85 } |
|
86 NS_IMPL_CYCLE_COLLECTION_UNLINK(mChildManagers) |
|
87 NS_IMPL_CYCLE_COLLECTION_UNLINK_END |
|
88 |
|
89 |
|
90 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsFrameMessageManager) |
|
91 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContentFrameMessageManager) |
|
92 |
|
93 /* nsFrameMessageManager implements nsIMessageSender and nsIMessageBroadcaster, |
|
94 * both of which descend from nsIMessageListenerManager. QI'ing to |
|
95 * nsIMessageListenerManager is therefore ambiguous and needs explicit casts |
|
96 * depending on which child interface applies. */ |
|
97 NS_INTERFACE_MAP_ENTRY_AGGREGATED(nsIMessageListenerManager, |
|
98 (mIsBroadcaster ? |
|
99 static_cast<nsIMessageListenerManager*>( |
|
100 static_cast<nsIMessageBroadcaster*>(this)) : |
|
101 static_cast<nsIMessageListenerManager*>( |
|
102 static_cast<nsIMessageSender*>(this)))) |
|
103 |
|
104 /* Message managers in child process implement nsIMessageSender and |
|
105 nsISyncMessageSender. Message managers in the chrome process are |
|
106 either broadcasters (if they have subordinate/child message |
|
107 managers) or they're simple message senders. */ |
|
108 NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsISyncMessageSender, !mChrome) |
|
109 NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIMessageSender, !mChrome || !mIsBroadcaster) |
|
110 NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIMessageBroadcaster, mChrome && mIsBroadcaster) |
|
111 |
|
112 /* nsIContentFrameMessageManager is accessible only in TabChildGlobal. */ |
|
113 NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIContentFrameMessageManager, |
|
114 !mChrome && !mIsProcessManager) |
|
115 |
|
116 /* Frame message managers (non-process message managers) support nsIFrameScriptLoader. */ |
|
117 NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIFrameScriptLoader, |
|
118 mChrome && !mIsProcessManager) |
|
119 |
|
120 /* Message senders in the chrome process support nsIProcessChecker. */ |
|
121 NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIProcessChecker, |
|
122 mChrome && !mIsBroadcaster) |
|
123 |
|
124 NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO_CONDITIONAL(ChromeMessageBroadcaster, |
|
125 mChrome && mIsBroadcaster) |
|
126 NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO_CONDITIONAL(ChromeMessageSender, |
|
127 mChrome && !mIsBroadcaster) |
|
128 NS_INTERFACE_MAP_END |
|
129 |
|
130 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsFrameMessageManager) |
|
131 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsFrameMessageManager) |
|
132 |
|
133 enum ActorFlavorEnum { |
|
134 Parent = 0, |
|
135 Child |
|
136 }; |
|
137 |
|
138 template <ActorFlavorEnum> |
|
139 struct BlobTraits |
|
140 { }; |
|
141 |
|
142 template <> |
|
143 struct BlobTraits<Parent> |
|
144 { |
|
145 typedef mozilla::dom::BlobParent BlobType; |
|
146 typedef mozilla::dom::PBlobParent ProtocolType; |
|
147 typedef mozilla::dom::ContentParent ConcreteContentManagerType; |
|
148 }; |
|
149 |
|
150 template <> |
|
151 struct BlobTraits<Child> |
|
152 { |
|
153 typedef mozilla::dom::BlobChild BlobType; |
|
154 typedef mozilla::dom::PBlobChild ProtocolType; |
|
155 typedef mozilla::dom::ContentChild ConcreteContentManagerType; |
|
156 }; |
|
157 |
|
158 template<ActorFlavorEnum> |
|
159 struct DataBlobs |
|
160 { }; |
|
161 |
|
162 template<> |
|
163 struct DataBlobs<Parent> |
|
164 { |
|
165 typedef BlobTraits<Parent>::ProtocolType ProtocolType; |
|
166 |
|
167 static InfallibleTArray<ProtocolType*>& Blobs(ClonedMessageData& aData) |
|
168 { |
|
169 return aData.blobsParent(); |
|
170 } |
|
171 |
|
172 static const InfallibleTArray<ProtocolType*>& Blobs(const ClonedMessageData& aData) |
|
173 { |
|
174 return aData.blobsParent(); |
|
175 } |
|
176 }; |
|
177 |
|
178 template<> |
|
179 struct DataBlobs<Child> |
|
180 { |
|
181 typedef BlobTraits<Child>::ProtocolType ProtocolType; |
|
182 |
|
183 static InfallibleTArray<ProtocolType*>& Blobs(ClonedMessageData& aData) |
|
184 { |
|
185 return aData.blobsChild(); |
|
186 } |
|
187 |
|
188 static const InfallibleTArray<ProtocolType*>& Blobs(const ClonedMessageData& aData) |
|
189 { |
|
190 return aData.blobsChild(); |
|
191 } |
|
192 }; |
|
193 |
|
194 template<ActorFlavorEnum Flavor> |
|
195 static bool |
|
196 BuildClonedMessageData(typename BlobTraits<Flavor>::ConcreteContentManagerType* aManager, |
|
197 const StructuredCloneData& aData, |
|
198 ClonedMessageData& aClonedData) |
|
199 { |
|
200 SerializedStructuredCloneBuffer& buffer = aClonedData.data(); |
|
201 buffer.data = aData.mData; |
|
202 buffer.dataLength = aData.mDataLength; |
|
203 const nsTArray<nsCOMPtr<nsIDOMBlob> >& blobs = aData.mClosure.mBlobs; |
|
204 if (!blobs.IsEmpty()) { |
|
205 typedef typename BlobTraits<Flavor>::ProtocolType ProtocolType; |
|
206 InfallibleTArray<ProtocolType*>& blobList = DataBlobs<Flavor>::Blobs(aClonedData); |
|
207 uint32_t length = blobs.Length(); |
|
208 blobList.SetCapacity(length); |
|
209 for (uint32_t i = 0; i < length; ++i) { |
|
210 typename BlobTraits<Flavor>::BlobType* protocolActor = |
|
211 aManager->GetOrCreateActorForBlob(blobs[i]); |
|
212 if (!protocolActor) { |
|
213 return false; |
|
214 } |
|
215 blobList.AppendElement(protocolActor); |
|
216 } |
|
217 } |
|
218 return true; |
|
219 } |
|
220 |
|
221 bool |
|
222 MessageManagerCallback::BuildClonedMessageDataForParent(ContentParent* aParent, |
|
223 const StructuredCloneData& aData, |
|
224 ClonedMessageData& aClonedData) |
|
225 { |
|
226 return BuildClonedMessageData<Parent>(aParent, aData, aClonedData); |
|
227 } |
|
228 |
|
229 bool |
|
230 MessageManagerCallback::BuildClonedMessageDataForChild(ContentChild* aChild, |
|
231 const StructuredCloneData& aData, |
|
232 ClonedMessageData& aClonedData) |
|
233 { |
|
234 return BuildClonedMessageData<Child>(aChild, aData, aClonedData); |
|
235 } |
|
236 |
|
237 template<ActorFlavorEnum Flavor> |
|
238 static StructuredCloneData |
|
239 UnpackClonedMessageData(const ClonedMessageData& aData) |
|
240 { |
|
241 const SerializedStructuredCloneBuffer& buffer = aData.data(); |
|
242 typedef typename BlobTraits<Flavor>::ProtocolType ProtocolType; |
|
243 const InfallibleTArray<ProtocolType*>& blobs = DataBlobs<Flavor>::Blobs(aData); |
|
244 StructuredCloneData cloneData; |
|
245 cloneData.mData = buffer.data; |
|
246 cloneData.mDataLength = buffer.dataLength; |
|
247 if (!blobs.IsEmpty()) { |
|
248 uint32_t length = blobs.Length(); |
|
249 cloneData.mClosure.mBlobs.SetCapacity(length); |
|
250 for (uint32_t i = 0; i < length; ++i) { |
|
251 auto* blob = |
|
252 static_cast<typename BlobTraits<Flavor>::BlobType*>(blobs[i]); |
|
253 MOZ_ASSERT(blob); |
|
254 nsCOMPtr<nsIDOMBlob> domBlob = blob->GetBlob(); |
|
255 MOZ_ASSERT(domBlob); |
|
256 cloneData.mClosure.mBlobs.AppendElement(domBlob); |
|
257 } |
|
258 } |
|
259 return cloneData; |
|
260 } |
|
261 |
|
262 StructuredCloneData |
|
263 mozilla::dom::ipc::UnpackClonedMessageDataForParent(const ClonedMessageData& aData) |
|
264 { |
|
265 return UnpackClonedMessageData<Parent>(aData); |
|
266 } |
|
267 |
|
268 StructuredCloneData |
|
269 mozilla::dom::ipc::UnpackClonedMessageDataForChild(const ClonedMessageData& aData) |
|
270 { |
|
271 return UnpackClonedMessageData<Child>(aData); |
|
272 } |
|
273 |
|
274 bool |
|
275 SameProcessCpowHolder::ToObject(JSContext* aCx, |
|
276 JS::MutableHandle<JSObject*> aObjp) |
|
277 { |
|
278 if (!mObj) { |
|
279 return true; |
|
280 } |
|
281 |
|
282 aObjp.set(mObj); |
|
283 return JS_WrapObject(aCx, aObjp); |
|
284 } |
|
285 |
|
286 // nsIMessageListenerManager |
|
287 |
|
288 NS_IMETHODIMP |
|
289 nsFrameMessageManager::AddMessageListener(const nsAString& aMessage, |
|
290 nsIMessageListener* aListener) |
|
291 { |
|
292 nsAutoTObserverArray<nsMessageListenerInfo, 1>* listeners = |
|
293 mListeners.Get(aMessage); |
|
294 if (!listeners) { |
|
295 listeners = new nsAutoTObserverArray<nsMessageListenerInfo, 1>(); |
|
296 mListeners.Put(aMessage, listeners); |
|
297 } else { |
|
298 uint32_t len = listeners->Length(); |
|
299 for (uint32_t i = 0; i < len; ++i) { |
|
300 if (listeners->ElementAt(i).mStrongListener == aListener) { |
|
301 return NS_OK; |
|
302 } |
|
303 } |
|
304 } |
|
305 |
|
306 nsMessageListenerInfo* entry = listeners->AppendElement(); |
|
307 NS_ENSURE_TRUE(entry, NS_ERROR_OUT_OF_MEMORY); |
|
308 entry->mStrongListener = aListener; |
|
309 return NS_OK; |
|
310 } |
|
311 |
|
312 NS_IMETHODIMP |
|
313 nsFrameMessageManager::RemoveMessageListener(const nsAString& aMessage, |
|
314 nsIMessageListener* aListener) |
|
315 { |
|
316 nsAutoTObserverArray<nsMessageListenerInfo, 1>* listeners = |
|
317 mListeners.Get(aMessage); |
|
318 if (!listeners) { |
|
319 return NS_OK; |
|
320 } |
|
321 |
|
322 uint32_t len = listeners->Length(); |
|
323 for (uint32_t i = 0; i < len; ++i) { |
|
324 if (listeners->ElementAt(i).mStrongListener == aListener) { |
|
325 listeners->RemoveElementAt(i); |
|
326 return NS_OK; |
|
327 } |
|
328 } |
|
329 return NS_OK; |
|
330 } |
|
331 |
|
332 #ifdef DEBUG |
|
333 typedef struct |
|
334 { |
|
335 nsCOMPtr<nsISupports> mCanonical; |
|
336 nsWeakPtr mWeak; |
|
337 } CanonicalCheckerParams; |
|
338 |
|
339 static PLDHashOperator |
|
340 CanonicalChecker(const nsAString& aKey, |
|
341 nsAutoTObserverArray<nsMessageListenerInfo, 1>* aListeners, |
|
342 void* aParams) |
|
343 { |
|
344 CanonicalCheckerParams* params = |
|
345 static_cast<CanonicalCheckerParams*> (aParams); |
|
346 |
|
347 uint32_t count = aListeners->Length(); |
|
348 for (uint32_t i = 0; i < count; i++) { |
|
349 if (!aListeners->ElementAt(i).mWeakListener) { |
|
350 continue; |
|
351 } |
|
352 nsCOMPtr<nsISupports> otherCanonical = |
|
353 do_QueryReferent(aListeners->ElementAt(i).mWeakListener); |
|
354 MOZ_ASSERT((params->mCanonical == otherCanonical) == |
|
355 (params->mWeak == aListeners->ElementAt(i).mWeakListener)); |
|
356 } |
|
357 return PL_DHASH_NEXT; |
|
358 } |
|
359 #endif |
|
360 |
|
361 NS_IMETHODIMP |
|
362 nsFrameMessageManager::AddWeakMessageListener(const nsAString& aMessage, |
|
363 nsIMessageListener* aListener) |
|
364 { |
|
365 nsWeakPtr weak = do_GetWeakReference(aListener); |
|
366 NS_ENSURE_TRUE(weak, NS_ERROR_NO_INTERFACE); |
|
367 |
|
368 #ifdef DEBUG |
|
369 // It's technically possible that one object X could give two different |
|
370 // nsIWeakReference*'s when you do_GetWeakReference(X). We really don't want |
|
371 // this to happen; it will break e.g. RemoveWeakMessageListener. So let's |
|
372 // check that we're not getting ourselves into that situation. |
|
373 nsCOMPtr<nsISupports> canonical = do_QueryInterface(aListener); |
|
374 CanonicalCheckerParams params; |
|
375 params.mCanonical = canonical; |
|
376 params.mWeak = weak; |
|
377 mListeners.EnumerateRead(CanonicalChecker, (void*)¶ms); |
|
378 #endif |
|
379 |
|
380 nsAutoTObserverArray<nsMessageListenerInfo, 1>* listeners = |
|
381 mListeners.Get(aMessage); |
|
382 if (!listeners) { |
|
383 listeners = new nsAutoTObserverArray<nsMessageListenerInfo, 1>(); |
|
384 mListeners.Put(aMessage, listeners); |
|
385 } else { |
|
386 uint32_t len = listeners->Length(); |
|
387 for (uint32_t i = 0; i < len; ++i) { |
|
388 if (listeners->ElementAt(i).mWeakListener == weak) { |
|
389 return NS_OK; |
|
390 } |
|
391 } |
|
392 } |
|
393 |
|
394 nsMessageListenerInfo* entry = listeners->AppendElement(); |
|
395 entry->mWeakListener = weak; |
|
396 return NS_OK; |
|
397 } |
|
398 |
|
399 NS_IMETHODIMP |
|
400 nsFrameMessageManager::RemoveWeakMessageListener(const nsAString& aMessage, |
|
401 nsIMessageListener* aListener) |
|
402 { |
|
403 nsWeakPtr weak = do_GetWeakReference(aListener); |
|
404 NS_ENSURE_TRUE(weak, NS_OK); |
|
405 |
|
406 nsAutoTObserverArray<nsMessageListenerInfo, 1>* listeners = |
|
407 mListeners.Get(aMessage); |
|
408 if (!listeners) { |
|
409 return NS_OK; |
|
410 } |
|
411 |
|
412 uint32_t len = listeners->Length(); |
|
413 for (uint32_t i = 0; i < len; ++i) { |
|
414 if (listeners->ElementAt(i).mWeakListener == weak) { |
|
415 listeners->RemoveElementAt(i); |
|
416 return NS_OK; |
|
417 } |
|
418 } |
|
419 |
|
420 return NS_OK; |
|
421 } |
|
422 |
|
423 // nsIFrameScriptLoader |
|
424 |
|
425 NS_IMETHODIMP |
|
426 nsFrameMessageManager::LoadFrameScript(const nsAString& aURL, |
|
427 bool aAllowDelayedLoad, |
|
428 bool aRunInGlobalScope) |
|
429 { |
|
430 // FIXME: Bug 673569 is currently disabled. |
|
431 aRunInGlobalScope = true; |
|
432 |
|
433 if (aAllowDelayedLoad) { |
|
434 if (IsGlobal() || IsWindowLevel()) { |
|
435 // Cache for future windows or frames |
|
436 mPendingScripts.AppendElement(aURL); |
|
437 mPendingScriptsGlobalStates.AppendElement(aRunInGlobalScope); |
|
438 } else if (!mCallback) { |
|
439 // We're frame message manager, which isn't connected yet. |
|
440 mPendingScripts.AppendElement(aURL); |
|
441 mPendingScriptsGlobalStates.AppendElement(aRunInGlobalScope); |
|
442 return NS_OK; |
|
443 } |
|
444 } |
|
445 |
|
446 if (mCallback) { |
|
447 #ifdef DEBUG_smaug |
|
448 printf("Will load %s \n", NS_ConvertUTF16toUTF8(aURL).get()); |
|
449 #endif |
|
450 NS_ENSURE_TRUE(mCallback->DoLoadFrameScript(aURL, aRunInGlobalScope), |
|
451 NS_ERROR_FAILURE); |
|
452 } |
|
453 |
|
454 for (int32_t i = 0; i < mChildManagers.Count(); ++i) { |
|
455 nsRefPtr<nsFrameMessageManager> mm = |
|
456 static_cast<nsFrameMessageManager*>(mChildManagers[i]); |
|
457 if (mm) { |
|
458 // Use false here, so that child managers don't cache the script, which |
|
459 // is already cached in the parent. |
|
460 mm->LoadFrameScript(aURL, false, aRunInGlobalScope); |
|
461 } |
|
462 } |
|
463 return NS_OK; |
|
464 } |
|
465 |
|
466 NS_IMETHODIMP |
|
467 nsFrameMessageManager::RemoveDelayedFrameScript(const nsAString& aURL) |
|
468 { |
|
469 for (uint32_t i = 0; i < mPendingScripts.Length(); ++i) { |
|
470 if (mPendingScripts[i] == aURL) { |
|
471 mPendingScripts.RemoveElementAt(i); |
|
472 mPendingScriptsGlobalStates.RemoveElementAt(i); |
|
473 break; |
|
474 } |
|
475 } |
|
476 return NS_OK; |
|
477 } |
|
478 |
|
479 NS_IMETHODIMP |
|
480 nsFrameMessageManager::GetDelayedFrameScripts(JSContext* aCx, JS::MutableHandle<JS::Value> aList) |
|
481 { |
|
482 // Frame message managers may return an incomplete list because scripts |
|
483 // that were loaded after it was connected are not added to the list. |
|
484 if (!IsGlobal() && !IsWindowLevel()) { |
|
485 NS_WARNING("Cannot retrieve list of pending frame scripts for frame" |
|
486 "message managers as it may be incomplete"); |
|
487 return NS_ERROR_NOT_IMPLEMENTED; |
|
488 } |
|
489 |
|
490 JS::Rooted<JSObject*> array(aCx, JS_NewArrayObject(aCx, mPendingScripts.Length())); |
|
491 NS_ENSURE_TRUE(array, NS_ERROR_OUT_OF_MEMORY); |
|
492 |
|
493 JS::Rooted<JSString*> url(aCx); |
|
494 JS::Rooted<JSObject*> pair(aCx); |
|
495 for (uint32_t i = 0; i < mPendingScripts.Length(); ++i) { |
|
496 url = JS_NewUCStringCopyN(aCx, mPendingScripts[i].get(), mPendingScripts[i].Length()); |
|
497 NS_ENSURE_TRUE(url, NS_ERROR_OUT_OF_MEMORY); |
|
498 |
|
499 JS::AutoValueArray<2> pairElts(aCx); |
|
500 pairElts[0].setString(url); |
|
501 pairElts[1].setBoolean(mPendingScriptsGlobalStates[i]); |
|
502 |
|
503 pair = JS_NewArrayObject(aCx, pairElts); |
|
504 NS_ENSURE_TRUE(pair, NS_ERROR_OUT_OF_MEMORY); |
|
505 |
|
506 NS_ENSURE_TRUE(JS_SetElement(aCx, array, i, pair), |
|
507 NS_ERROR_OUT_OF_MEMORY); |
|
508 } |
|
509 |
|
510 aList.setObject(*array); |
|
511 return NS_OK; |
|
512 } |
|
513 |
|
514 static bool |
|
515 JSONCreator(const jschar* aBuf, uint32_t aLen, void* aData) |
|
516 { |
|
517 nsAString* result = static_cast<nsAString*>(aData); |
|
518 result->Append(static_cast<const char16_t*>(aBuf), |
|
519 static_cast<uint32_t>(aLen)); |
|
520 return true; |
|
521 } |
|
522 |
|
523 static bool |
|
524 GetParamsForMessage(JSContext* aCx, |
|
525 const JS::Value& aJSON, |
|
526 JSAutoStructuredCloneBuffer& aBuffer, |
|
527 StructuredCloneClosure& aClosure) |
|
528 { |
|
529 JS::Rooted<JS::Value> v(aCx, aJSON); |
|
530 if (WriteStructuredClone(aCx, v, aBuffer, aClosure)) { |
|
531 return true; |
|
532 } |
|
533 JS_ClearPendingException(aCx); |
|
534 |
|
535 // Not clonable, try JSON |
|
536 //XXX This is ugly but currently structured cloning doesn't handle |
|
537 // properly cases when interface is implemented in JS and used |
|
538 // as a dictionary. |
|
539 nsAutoString json; |
|
540 NS_ENSURE_TRUE(JS_Stringify(aCx, &v, JS::NullPtr(), JS::NullHandleValue, |
|
541 JSONCreator, &json), false); |
|
542 NS_ENSURE_TRUE(!json.IsEmpty(), false); |
|
543 |
|
544 JS::Rooted<JS::Value> val(aCx, JS::NullValue()); |
|
545 NS_ENSURE_TRUE(JS_ParseJSON(aCx, static_cast<const jschar*>(json.get()), |
|
546 json.Length(), &val), false); |
|
547 |
|
548 return WriteStructuredClone(aCx, val, aBuffer, aClosure); |
|
549 } |
|
550 |
|
551 |
|
552 // nsISyncMessageSender |
|
553 |
|
554 static bool sSendingSyncMessage = false; |
|
555 |
|
556 NS_IMETHODIMP |
|
557 nsFrameMessageManager::SendSyncMessage(const nsAString& aMessageName, |
|
558 JS::Handle<JS::Value> aJSON, |
|
559 JS::Handle<JS::Value> aObjects, |
|
560 nsIPrincipal* aPrincipal, |
|
561 JSContext* aCx, |
|
562 uint8_t aArgc, |
|
563 JS::MutableHandle<JS::Value> aRetval) |
|
564 { |
|
565 return SendMessage(aMessageName, aJSON, aObjects, aPrincipal, aCx, aArgc, |
|
566 aRetval, true); |
|
567 } |
|
568 |
|
569 NS_IMETHODIMP |
|
570 nsFrameMessageManager::SendRpcMessage(const nsAString& aMessageName, |
|
571 JS::Handle<JS::Value> aJSON, |
|
572 JS::Handle<JS::Value> aObjects, |
|
573 nsIPrincipal* aPrincipal, |
|
574 JSContext* aCx, |
|
575 uint8_t aArgc, |
|
576 JS::MutableHandle<JS::Value> aRetval) |
|
577 { |
|
578 return SendMessage(aMessageName, aJSON, aObjects, aPrincipal, aCx, aArgc, |
|
579 aRetval, false); |
|
580 } |
|
581 |
|
582 nsresult |
|
583 nsFrameMessageManager::SendMessage(const nsAString& aMessageName, |
|
584 JS::Handle<JS::Value> aJSON, |
|
585 JS::Handle<JS::Value> aObjects, |
|
586 nsIPrincipal* aPrincipal, |
|
587 JSContext* aCx, |
|
588 uint8_t aArgc, |
|
589 JS::MutableHandle<JS::Value> aRetval, |
|
590 bool aIsSync) |
|
591 { |
|
592 NS_ASSERTION(!IsGlobal(), "Should not call SendSyncMessage in chrome"); |
|
593 NS_ASSERTION(!IsWindowLevel(), "Should not call SendSyncMessage in chrome"); |
|
594 NS_ASSERTION(!mParentManager, "Should not have parent manager in content!"); |
|
595 |
|
596 aRetval.setUndefined(); |
|
597 NS_ENSURE_TRUE(mCallback, NS_ERROR_NOT_INITIALIZED); |
|
598 |
|
599 if (sSendingSyncMessage && aIsSync) { |
|
600 // No kind of blocking send should be issued on top of a sync message. |
|
601 return NS_ERROR_UNEXPECTED; |
|
602 } |
|
603 |
|
604 StructuredCloneData data; |
|
605 JSAutoStructuredCloneBuffer buffer; |
|
606 if (aArgc >= 2 && |
|
607 !GetParamsForMessage(aCx, aJSON, buffer, data.mClosure)) { |
|
608 return NS_ERROR_DOM_DATA_CLONE_ERR; |
|
609 } |
|
610 data.mData = buffer.data(); |
|
611 data.mDataLength = buffer.nbytes(); |
|
612 |
|
613 JS::Rooted<JSObject*> objects(aCx); |
|
614 if (aArgc >= 3 && aObjects.isObject()) { |
|
615 objects = &aObjects.toObject(); |
|
616 } |
|
617 |
|
618 InfallibleTArray<nsString> retval; |
|
619 |
|
620 sSendingSyncMessage |= aIsSync; |
|
621 bool rv = mCallback->DoSendBlockingMessage(aCx, aMessageName, data, objects, |
|
622 aPrincipal, &retval, aIsSync); |
|
623 if (aIsSync) { |
|
624 sSendingSyncMessage = false; |
|
625 } |
|
626 |
|
627 if (!rv) { |
|
628 return NS_OK; |
|
629 } |
|
630 |
|
631 uint32_t len = retval.Length(); |
|
632 JS::Rooted<JSObject*> dataArray(aCx, JS_NewArrayObject(aCx, len)); |
|
633 NS_ENSURE_TRUE(dataArray, NS_ERROR_OUT_OF_MEMORY); |
|
634 |
|
635 for (uint32_t i = 0; i < len; ++i) { |
|
636 if (retval[i].IsEmpty()) { |
|
637 continue; |
|
638 } |
|
639 |
|
640 JS::Rooted<JS::Value> ret(aCx); |
|
641 if (!JS_ParseJSON(aCx, static_cast<const jschar*>(retval[i].get()), |
|
642 retval[i].Length(), &ret)) { |
|
643 return NS_ERROR_UNEXPECTED; |
|
644 } |
|
645 NS_ENSURE_TRUE(JS_SetElement(aCx, dataArray, i, ret), |
|
646 NS_ERROR_OUT_OF_MEMORY); |
|
647 } |
|
648 |
|
649 aRetval.setObject(*dataArray); |
|
650 return NS_OK; |
|
651 } |
|
652 |
|
653 nsresult |
|
654 nsFrameMessageManager::DispatchAsyncMessageInternal(JSContext* aCx, |
|
655 const nsAString& aMessage, |
|
656 const StructuredCloneData& aData, |
|
657 JS::Handle<JSObject *> aCpows, |
|
658 nsIPrincipal* aPrincipal) |
|
659 { |
|
660 if (mIsBroadcaster) { |
|
661 int32_t len = mChildManagers.Count(); |
|
662 for (int32_t i = 0; i < len; ++i) { |
|
663 static_cast<nsFrameMessageManager*>(mChildManagers[i])-> |
|
664 DispatchAsyncMessageInternal(aCx, aMessage, aData, aCpows, aPrincipal); |
|
665 } |
|
666 return NS_OK; |
|
667 } |
|
668 |
|
669 NS_ENSURE_TRUE(mCallback, NS_ERROR_NOT_INITIALIZED); |
|
670 if (!mCallback->DoSendAsyncMessage(aCx, aMessage, aData, aCpows, aPrincipal)) { |
|
671 return NS_ERROR_FAILURE; |
|
672 } |
|
673 return NS_OK; |
|
674 } |
|
675 |
|
676 nsresult |
|
677 nsFrameMessageManager::DispatchAsyncMessage(const nsAString& aMessageName, |
|
678 const JS::Value& aJSON, |
|
679 const JS::Value& aObjects, |
|
680 nsIPrincipal* aPrincipal, |
|
681 JSContext* aCx, |
|
682 uint8_t aArgc) |
|
683 { |
|
684 StructuredCloneData data; |
|
685 JSAutoStructuredCloneBuffer buffer; |
|
686 |
|
687 if (aArgc >= 2 && |
|
688 !GetParamsForMessage(aCx, aJSON, buffer, data.mClosure)) { |
|
689 return NS_ERROR_DOM_DATA_CLONE_ERR; |
|
690 } |
|
691 |
|
692 JS::Rooted<JSObject*> objects(aCx); |
|
693 if (aArgc >= 3 && aObjects.isObject()) { |
|
694 objects = &aObjects.toObject(); |
|
695 } |
|
696 |
|
697 data.mData = buffer.data(); |
|
698 data.mDataLength = buffer.nbytes(); |
|
699 |
|
700 return DispatchAsyncMessageInternal(aCx, aMessageName, data, objects, |
|
701 aPrincipal); |
|
702 } |
|
703 |
|
704 |
|
705 // nsIMessageSender |
|
706 |
|
707 NS_IMETHODIMP |
|
708 nsFrameMessageManager::SendAsyncMessage(const nsAString& aMessageName, |
|
709 JS::Handle<JS::Value> aJSON, |
|
710 JS::Handle<JS::Value> aObjects, |
|
711 nsIPrincipal* aPrincipal, |
|
712 JSContext* aCx, |
|
713 uint8_t aArgc) |
|
714 { |
|
715 return DispatchAsyncMessage(aMessageName, aJSON, aObjects, aPrincipal, aCx, |
|
716 aArgc); |
|
717 } |
|
718 |
|
719 |
|
720 // nsIMessageBroadcaster |
|
721 |
|
722 NS_IMETHODIMP |
|
723 nsFrameMessageManager::BroadcastAsyncMessage(const nsAString& aMessageName, |
|
724 JS::Handle<JS::Value> aJSON, |
|
725 JS::Handle<JS::Value> aObjects, |
|
726 JSContext* aCx, |
|
727 uint8_t aArgc) |
|
728 { |
|
729 return DispatchAsyncMessage(aMessageName, aJSON, aObjects, nullptr, aCx, |
|
730 aArgc); |
|
731 } |
|
732 |
|
733 NS_IMETHODIMP |
|
734 nsFrameMessageManager::GetChildCount(uint32_t* aChildCount) |
|
735 { |
|
736 *aChildCount = static_cast<uint32_t>(mChildManagers.Count()); |
|
737 return NS_OK; |
|
738 } |
|
739 |
|
740 NS_IMETHODIMP |
|
741 nsFrameMessageManager::GetChildAt(uint32_t aIndex, |
|
742 nsIMessageListenerManager** aMM) |
|
743 { |
|
744 *aMM = nullptr; |
|
745 nsCOMPtr<nsIMessageListenerManager> mm = |
|
746 do_QueryInterface(mChildManagers.SafeObjectAt(static_cast<uint32_t>(aIndex))); |
|
747 mm.swap(*aMM); |
|
748 return NS_OK; |
|
749 } |
|
750 |
|
751 |
|
752 // nsIContentFrameMessageManager |
|
753 |
|
754 NS_IMETHODIMP |
|
755 nsFrameMessageManager::Dump(const nsAString& aStr) |
|
756 { |
|
757 #ifdef ANDROID |
|
758 __android_log_print(ANDROID_LOG_INFO, "Gecko", "%s", NS_ConvertUTF16toUTF8(aStr).get()); |
|
759 #endif |
|
760 #ifdef XP_WIN |
|
761 if (IsDebuggerPresent()) { |
|
762 OutputDebugStringW(PromiseFlatString(aStr).get()); |
|
763 } |
|
764 #endif |
|
765 fputs(NS_ConvertUTF16toUTF8(aStr).get(), stdout); |
|
766 fflush(stdout); |
|
767 return NS_OK; |
|
768 } |
|
769 |
|
770 NS_IMETHODIMP |
|
771 nsFrameMessageManager::PrivateNoteIntentionalCrash() |
|
772 { |
|
773 return NS_ERROR_NOT_IMPLEMENTED; |
|
774 } |
|
775 |
|
776 NS_IMETHODIMP |
|
777 nsFrameMessageManager::GetContent(nsIDOMWindow** aContent) |
|
778 { |
|
779 *aContent = nullptr; |
|
780 return NS_OK; |
|
781 } |
|
782 |
|
783 NS_IMETHODIMP |
|
784 nsFrameMessageManager::GetDocShell(nsIDocShell** aDocShell) |
|
785 { |
|
786 *aDocShell = nullptr; |
|
787 return NS_OK; |
|
788 } |
|
789 |
|
790 NS_IMETHODIMP |
|
791 nsFrameMessageManager::Btoa(const nsAString& aBinaryData, |
|
792 nsAString& aAsciiBase64String) |
|
793 { |
|
794 return NS_OK; |
|
795 } |
|
796 |
|
797 NS_IMETHODIMP |
|
798 nsFrameMessageManager::Atob(const nsAString& aAsciiString, |
|
799 nsAString& aBinaryData) |
|
800 { |
|
801 return NS_OK; |
|
802 } |
|
803 |
|
804 // nsIProcessChecker |
|
805 |
|
806 nsresult |
|
807 nsFrameMessageManager::AssertProcessInternal(ProcessCheckerType aType, |
|
808 const nsAString& aCapability, |
|
809 bool* aValid) |
|
810 { |
|
811 *aValid = false; |
|
812 |
|
813 // This API is only supported for message senders in the chrome process. |
|
814 if (!mChrome || mIsBroadcaster) { |
|
815 return NS_ERROR_NOT_IMPLEMENTED; |
|
816 } |
|
817 if (!mCallback) { |
|
818 return NS_ERROR_NOT_AVAILABLE; |
|
819 } |
|
820 switch (aType) { |
|
821 case PROCESS_CHECKER_PERMISSION: |
|
822 *aValid = mCallback->CheckPermission(aCapability); |
|
823 break; |
|
824 case PROCESS_CHECKER_MANIFEST_URL: |
|
825 *aValid = mCallback->CheckManifestURL(aCapability); |
|
826 break; |
|
827 case ASSERT_APP_HAS_PERMISSION: |
|
828 *aValid = mCallback->CheckAppHasPermission(aCapability); |
|
829 break; |
|
830 default: |
|
831 break; |
|
832 } |
|
833 return NS_OK; |
|
834 } |
|
835 |
|
836 NS_IMETHODIMP |
|
837 nsFrameMessageManager::AssertPermission(const nsAString& aPermission, |
|
838 bool* aHasPermission) |
|
839 { |
|
840 return AssertProcessInternal(PROCESS_CHECKER_PERMISSION, |
|
841 aPermission, |
|
842 aHasPermission); |
|
843 } |
|
844 |
|
845 NS_IMETHODIMP |
|
846 nsFrameMessageManager::AssertContainApp(const nsAString& aManifestURL, |
|
847 bool* aHasManifestURL) |
|
848 { |
|
849 return AssertProcessInternal(PROCESS_CHECKER_MANIFEST_URL, |
|
850 aManifestURL, |
|
851 aHasManifestURL); |
|
852 } |
|
853 |
|
854 NS_IMETHODIMP |
|
855 nsFrameMessageManager::AssertAppHasPermission(const nsAString& aPermission, |
|
856 bool* aHasPermission) |
|
857 { |
|
858 return AssertProcessInternal(ASSERT_APP_HAS_PERMISSION, |
|
859 aPermission, |
|
860 aHasPermission); |
|
861 } |
|
862 |
|
863 NS_IMETHODIMP |
|
864 nsFrameMessageManager::AssertAppHasStatus(unsigned short aStatus, |
|
865 bool* aHasStatus) |
|
866 { |
|
867 *aHasStatus = false; |
|
868 |
|
869 // This API is only supported for message senders in the chrome process. |
|
870 if (!mChrome || mIsBroadcaster) { |
|
871 return NS_ERROR_NOT_IMPLEMENTED; |
|
872 } |
|
873 if (!mCallback) { |
|
874 return NS_ERROR_NOT_AVAILABLE; |
|
875 } |
|
876 *aHasStatus = mCallback->CheckAppHasStatus(aStatus); |
|
877 |
|
878 return NS_OK; |
|
879 } |
|
880 |
|
881 class MMListenerRemover |
|
882 { |
|
883 public: |
|
884 MMListenerRemover(nsFrameMessageManager* aMM) |
|
885 : mWasHandlingMessage(aMM->mHandlingMessage) |
|
886 , mMM(aMM) |
|
887 { |
|
888 mMM->mHandlingMessage = true; |
|
889 } |
|
890 ~MMListenerRemover() |
|
891 { |
|
892 if (!mWasHandlingMessage) { |
|
893 mMM->mHandlingMessage = false; |
|
894 if (mMM->mDisconnected) { |
|
895 mMM->mListeners.Clear(); |
|
896 } |
|
897 } |
|
898 } |
|
899 |
|
900 bool mWasHandlingMessage; |
|
901 nsRefPtr<nsFrameMessageManager> mMM; |
|
902 }; |
|
903 |
|
904 |
|
905 // nsIMessageListener |
|
906 |
|
907 nsresult |
|
908 nsFrameMessageManager::ReceiveMessage(nsISupports* aTarget, |
|
909 const nsAString& aMessage, |
|
910 bool aIsSync, |
|
911 const StructuredCloneData* aCloneData, |
|
912 CpowHolder* aCpows, |
|
913 nsIPrincipal* aPrincipal, |
|
914 InfallibleTArray<nsString>* aJSONRetVal) |
|
915 { |
|
916 AutoSafeJSContext cx; |
|
917 nsAutoTObserverArray<nsMessageListenerInfo, 1>* listeners = |
|
918 mListeners.Get(aMessage); |
|
919 if (listeners) { |
|
920 |
|
921 MMListenerRemover lr(this); |
|
922 |
|
923 nsAutoTObserverArray<nsMessageListenerInfo, 1>::EndLimitedIterator |
|
924 iter(*listeners); |
|
925 while(iter.HasMore()) { |
|
926 nsMessageListenerInfo& listener = iter.GetNext(); |
|
927 // Remove mListeners[i] if it's an expired weak listener. |
|
928 nsCOMPtr<nsISupports> weakListener; |
|
929 if (listener.mWeakListener) { |
|
930 weakListener = do_QueryReferent(listener.mWeakListener); |
|
931 if (!weakListener) { |
|
932 listeners->RemoveElement(listener); |
|
933 continue; |
|
934 } |
|
935 } |
|
936 |
|
937 nsCOMPtr<nsIXPConnectWrappedJS> wrappedJS; |
|
938 if (weakListener) { |
|
939 wrappedJS = do_QueryInterface(weakListener); |
|
940 } else { |
|
941 wrappedJS = do_QueryInterface(listener.mStrongListener); |
|
942 } |
|
943 |
|
944 if (!wrappedJS) { |
|
945 continue; |
|
946 } |
|
947 JS::Rooted<JSObject*> object(cx, wrappedJS->GetJSObject()); |
|
948 if (!object) { |
|
949 continue; |
|
950 } |
|
951 JSAutoCompartment ac(cx, object); |
|
952 |
|
953 // The parameter for the listener function. |
|
954 JS::Rooted<JSObject*> param(cx, |
|
955 JS_NewObject(cx, nullptr, JS::NullPtr(), JS::NullPtr())); |
|
956 NS_ENSURE_TRUE(param, NS_ERROR_OUT_OF_MEMORY); |
|
957 |
|
958 JS::Rooted<JS::Value> targetv(cx); |
|
959 js::AssertSameCompartment(cx, object); |
|
960 nsresult rv = nsContentUtils::WrapNative(cx, aTarget, &targetv); |
|
961 NS_ENSURE_SUCCESS(rv, rv); |
|
962 |
|
963 JS::Rooted<JSObject*> cpows(cx); |
|
964 if (aCpows) { |
|
965 if (!aCpows->ToObject(cx, &cpows)) { |
|
966 return NS_ERROR_UNEXPECTED; |
|
967 } |
|
968 } |
|
969 |
|
970 if (!cpows) { |
|
971 cpows = JS_NewObject(cx, nullptr, JS::NullPtr(), JS::NullPtr()); |
|
972 if (!cpows) { |
|
973 return NS_ERROR_UNEXPECTED; |
|
974 } |
|
975 } |
|
976 |
|
977 JS::Rooted<JS::Value> cpowsv(cx, JS::ObjectValue(*cpows)); |
|
978 |
|
979 JS::Rooted<JS::Value> json(cx, JS::NullValue()); |
|
980 if (aCloneData && aCloneData->mDataLength && |
|
981 !ReadStructuredClone(cx, *aCloneData, &json)) { |
|
982 JS_ClearPendingException(cx); |
|
983 return NS_OK; |
|
984 } |
|
985 JS::Rooted<JSString*> jsMessage(cx, |
|
986 JS_NewUCStringCopyN(cx, |
|
987 static_cast<const jschar*>(aMessage.BeginReading()), |
|
988 aMessage.Length())); |
|
989 NS_ENSURE_TRUE(jsMessage, NS_ERROR_OUT_OF_MEMORY); |
|
990 JS::Rooted<JS::Value> syncv(cx, JS::BooleanValue(aIsSync)); |
|
991 JS_DefineProperty(cx, param, "target", targetv, JSPROP_ENUMERATE); |
|
992 JS_DefineProperty(cx, param, "name", jsMessage, JSPROP_ENUMERATE); |
|
993 JS_DefineProperty(cx, param, "sync", syncv, JSPROP_ENUMERATE); |
|
994 JS_DefineProperty(cx, param, "json", json, JSPROP_ENUMERATE); // deprecated |
|
995 JS_DefineProperty(cx, param, "data", json, JSPROP_ENUMERATE); |
|
996 JS_DefineProperty(cx, param, "objects", cpowsv, JSPROP_ENUMERATE); |
|
997 |
|
998 // message.principal == null |
|
999 if (!aPrincipal) { |
|
1000 JS_DefineProperty(cx, param, "principal", JS::UndefinedHandleValue, JSPROP_ENUMERATE); |
|
1001 } |
|
1002 |
|
1003 // message.principal = { appId: <id>, origin: <origin>, isInBrowserElement: <something> } |
|
1004 else { |
|
1005 JS::Rooted<JSObject*> principalObj(cx, |
|
1006 JS_NewObject(cx, nullptr, JS::NullPtr(), JS::NullPtr())); |
|
1007 |
|
1008 uint32_t appId; |
|
1009 nsresult rv = aPrincipal->GetAppId(&appId); |
|
1010 NS_ENSURE_SUCCESS(rv, rv); |
|
1011 |
|
1012 JS_DefineProperty(cx, principalObj, "appId", appId, JSPROP_ENUMERATE); |
|
1013 |
|
1014 nsCString origin; |
|
1015 rv = aPrincipal->GetOrigin(getter_Copies(origin)); |
|
1016 NS_ENSURE_SUCCESS(rv, rv); |
|
1017 |
|
1018 JS::Rooted<JSString*> originStr(cx, JS_NewStringCopyN(cx, origin.get(), origin.Length())); |
|
1019 NS_ENSURE_TRUE(originStr, NS_ERROR_OUT_OF_MEMORY); |
|
1020 JS_DefineProperty(cx, principalObj, "origin", originStr, JSPROP_ENUMERATE); |
|
1021 |
|
1022 bool browser; |
|
1023 rv = aPrincipal->GetIsInBrowserElement(&browser); |
|
1024 NS_ENSURE_SUCCESS(rv, rv); |
|
1025 |
|
1026 JS::Rooted<JS::Value> browserValue(cx, JS::BooleanValue(browser)); |
|
1027 JS_DefineProperty(cx, principalObj, "isInBrowserElement", browserValue, JSPROP_ENUMERATE); |
|
1028 |
|
1029 JS_DefineProperty(cx, param, "principal", principalObj, JSPROP_ENUMERATE); |
|
1030 } |
|
1031 |
|
1032 JS::Rooted<JS::Value> thisValue(cx, JS::UndefinedValue()); |
|
1033 |
|
1034 JS::Rooted<JS::Value> funval(cx); |
|
1035 if (JS_ObjectIsCallable(cx, object)) { |
|
1036 // If the listener is a JS function: |
|
1037 funval.setObject(*object); |
|
1038 |
|
1039 // A small hack to get 'this' value right on content side where |
|
1040 // messageManager is wrapped in TabChildGlobal. |
|
1041 nsCOMPtr<nsISupports> defaultThisValue; |
|
1042 if (mChrome) { |
|
1043 defaultThisValue = do_QueryObject(this); |
|
1044 } else { |
|
1045 defaultThisValue = aTarget; |
|
1046 } |
|
1047 js::AssertSameCompartment(cx, object); |
|
1048 nsresult rv = nsContentUtils::WrapNative(cx, defaultThisValue, &thisValue); |
|
1049 NS_ENSURE_SUCCESS(rv, rv); |
|
1050 } else { |
|
1051 // If the listener is a JS object which has receiveMessage function: |
|
1052 if (!JS_GetProperty(cx, object, "receiveMessage", &funval) || |
|
1053 !funval.isObject()) |
|
1054 return NS_ERROR_UNEXPECTED; |
|
1055 |
|
1056 // Check if the object is even callable. |
|
1057 NS_ENSURE_STATE(JS_ObjectIsCallable(cx, &funval.toObject())); |
|
1058 thisValue.setObject(*object); |
|
1059 } |
|
1060 |
|
1061 JS::Rooted<JS::Value> rval(cx, JSVAL_VOID); |
|
1062 JS::Rooted<JS::Value> argv(cx, JS::ObjectValue(*param)); |
|
1063 |
|
1064 { |
|
1065 JS::Rooted<JSObject*> thisObject(cx, thisValue.toObjectOrNull()); |
|
1066 |
|
1067 JSAutoCompartment tac(cx, thisObject); |
|
1068 if (!JS_WrapValue(cx, &argv)) { |
|
1069 return NS_ERROR_UNEXPECTED; |
|
1070 } |
|
1071 |
|
1072 if (!JS_CallFunctionValue(cx, thisObject, funval, argv, &rval)) { |
|
1073 nsJSUtils::ReportPendingException(cx); |
|
1074 continue; |
|
1075 } |
|
1076 if (aJSONRetVal) { |
|
1077 nsString json; |
|
1078 if (!JS_Stringify(cx, &rval, JS::NullPtr(), JS::NullHandleValue, |
|
1079 JSONCreator, &json)) { |
|
1080 nsJSUtils::ReportPendingException(cx); |
|
1081 continue; |
|
1082 } |
|
1083 aJSONRetVal->AppendElement(json); |
|
1084 } |
|
1085 } |
|
1086 } |
|
1087 } |
|
1088 nsRefPtr<nsFrameMessageManager> kungfuDeathGrip = mParentManager; |
|
1089 return mParentManager ? mParentManager->ReceiveMessage(aTarget, aMessage, |
|
1090 aIsSync, aCloneData, |
|
1091 aCpows, aPrincipal, |
|
1092 aJSONRetVal) : NS_OK; |
|
1093 } |
|
1094 |
|
1095 void |
|
1096 nsFrameMessageManager::AddChildManager(nsFrameMessageManager* aManager) |
|
1097 { |
|
1098 mChildManagers.AppendObject(aManager); |
|
1099 |
|
1100 nsRefPtr<nsFrameMessageManager> kungfuDeathGrip = this; |
|
1101 nsRefPtr<nsFrameMessageManager> kungfuDeathGrip2 = aManager; |
|
1102 // We have parent manager if we're a window message manager. |
|
1103 // In that case we want to load the pending scripts from global |
|
1104 // message manager. |
|
1105 if (mParentManager) { |
|
1106 nsRefPtr<nsFrameMessageManager> globalMM = mParentManager; |
|
1107 for (uint32_t i = 0; i < globalMM->mPendingScripts.Length(); ++i) { |
|
1108 aManager->LoadFrameScript(globalMM->mPendingScripts[i], false, |
|
1109 globalMM->mPendingScriptsGlobalStates[i]); |
|
1110 } |
|
1111 } |
|
1112 for (uint32_t i = 0; i < mPendingScripts.Length(); ++i) { |
|
1113 aManager->LoadFrameScript(mPendingScripts[i], false, |
|
1114 mPendingScriptsGlobalStates[i]); |
|
1115 } |
|
1116 } |
|
1117 |
|
1118 void |
|
1119 nsFrameMessageManager::SetCallback(MessageManagerCallback* aCallback) |
|
1120 { |
|
1121 MOZ_ASSERT(!mIsBroadcaster || !mCallback, |
|
1122 "Broadcasters cannot have callbacks!"); |
|
1123 if (aCallback && mCallback != aCallback) { |
|
1124 mCallback = aCallback; |
|
1125 if (mOwnsCallback) { |
|
1126 mOwnedCallback = aCallback; |
|
1127 } |
|
1128 } |
|
1129 } |
|
1130 |
|
1131 void |
|
1132 nsFrameMessageManager::InitWithCallback(MessageManagerCallback* aCallback) |
|
1133 { |
|
1134 if (mCallback) { |
|
1135 // Initialization should only happen once. |
|
1136 return; |
|
1137 } |
|
1138 |
|
1139 SetCallback(aCallback); |
|
1140 |
|
1141 // First load global scripts by adding this to parent manager. |
|
1142 if (mParentManager) { |
|
1143 mParentManager->AddChildManager(this); |
|
1144 } |
|
1145 |
|
1146 for (uint32_t i = 0; i < mPendingScripts.Length(); ++i) { |
|
1147 LoadFrameScript(mPendingScripts[i], false, mPendingScriptsGlobalStates[i]); |
|
1148 } |
|
1149 } |
|
1150 |
|
1151 void |
|
1152 nsFrameMessageManager::RemoveFromParent() |
|
1153 { |
|
1154 if (mParentManager) { |
|
1155 mParentManager->RemoveChildManager(this); |
|
1156 } |
|
1157 mParentManager = nullptr; |
|
1158 mCallback = nullptr; |
|
1159 mOwnedCallback = nullptr; |
|
1160 } |
|
1161 |
|
1162 void |
|
1163 nsFrameMessageManager::Disconnect(bool aRemoveFromParent) |
|
1164 { |
|
1165 if (!mDisconnected) { |
|
1166 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); |
|
1167 if (obs) { |
|
1168 obs->NotifyObservers(NS_ISUPPORTS_CAST(nsIContentFrameMessageManager*, this), |
|
1169 "message-manager-disconnect", nullptr); |
|
1170 } |
|
1171 } |
|
1172 if (mParentManager && aRemoveFromParent) { |
|
1173 mParentManager->RemoveChildManager(this); |
|
1174 } |
|
1175 mDisconnected = true; |
|
1176 mParentManager = nullptr; |
|
1177 mCallback = nullptr; |
|
1178 mOwnedCallback = nullptr; |
|
1179 if (!mHandlingMessage) { |
|
1180 mListeners.Clear(); |
|
1181 } |
|
1182 } |
|
1183 |
|
1184 namespace { |
|
1185 |
|
1186 struct MessageManagerReferentCount |
|
1187 { |
|
1188 MessageManagerReferentCount() : mStrong(0), mWeakAlive(0), mWeakDead(0) {} |
|
1189 size_t mStrong; |
|
1190 size_t mWeakAlive; |
|
1191 size_t mWeakDead; |
|
1192 nsTArray<nsString> mSuspectMessages; |
|
1193 nsDataHashtable<nsStringHashKey, uint32_t> mMessageCounter; |
|
1194 }; |
|
1195 |
|
1196 } // anonymous namespace |
|
1197 |
|
1198 namespace mozilla { |
|
1199 namespace dom { |
|
1200 |
|
1201 class MessageManagerReporter MOZ_FINAL : public nsIMemoryReporter |
|
1202 { |
|
1203 public: |
|
1204 NS_DECL_ISUPPORTS |
|
1205 NS_DECL_NSIMEMORYREPORTER |
|
1206 |
|
1207 static const size_t kSuspectReferentCount = 300; |
|
1208 protected: |
|
1209 void CountReferents(nsFrameMessageManager* aMessageManager, |
|
1210 MessageManagerReferentCount* aReferentCount); |
|
1211 }; |
|
1212 |
|
1213 NS_IMPL_ISUPPORTS(MessageManagerReporter, nsIMemoryReporter) |
|
1214 |
|
1215 static PLDHashOperator |
|
1216 CollectMessageListenerData(const nsAString& aKey, |
|
1217 nsAutoTObserverArray<nsMessageListenerInfo, 1>* aListeners, |
|
1218 void* aData) |
|
1219 { |
|
1220 MessageManagerReferentCount* referentCount = |
|
1221 static_cast<MessageManagerReferentCount*>(aData); |
|
1222 |
|
1223 uint32_t listenerCount = aListeners->Length(); |
|
1224 if (!listenerCount) { |
|
1225 return PL_DHASH_NEXT; |
|
1226 } |
|
1227 |
|
1228 nsString key(aKey); |
|
1229 uint32_t oldCount = 0; |
|
1230 referentCount->mMessageCounter.Get(key, &oldCount); |
|
1231 uint32_t currentCount = oldCount + listenerCount; |
|
1232 referentCount->mMessageCounter.Put(key, currentCount); |
|
1233 |
|
1234 // Keep track of messages that have a suspiciously large |
|
1235 // number of referents (symptom of leak). |
|
1236 if (currentCount == MessageManagerReporter::kSuspectReferentCount) { |
|
1237 referentCount->mSuspectMessages.AppendElement(key); |
|
1238 } |
|
1239 |
|
1240 for (uint32_t i = 0; i < listenerCount; ++i) { |
|
1241 const nsMessageListenerInfo& listenerInfo = |
|
1242 aListeners->ElementAt(i); |
|
1243 if (listenerInfo.mWeakListener) { |
|
1244 nsCOMPtr<nsISupports> referent = |
|
1245 do_QueryReferent(listenerInfo.mWeakListener); |
|
1246 if (referent) { |
|
1247 referentCount->mWeakAlive++; |
|
1248 } else { |
|
1249 referentCount->mWeakDead++; |
|
1250 } |
|
1251 } else { |
|
1252 referentCount->mStrong++; |
|
1253 } |
|
1254 } |
|
1255 return PL_DHASH_NEXT; |
|
1256 } |
|
1257 |
|
1258 void |
|
1259 MessageManagerReporter::CountReferents(nsFrameMessageManager* aMessageManager, |
|
1260 MessageManagerReferentCount* aReferentCount) |
|
1261 { |
|
1262 aMessageManager->mListeners.EnumerateRead(CollectMessageListenerData, |
|
1263 aReferentCount); |
|
1264 |
|
1265 // Add referent count in child managers because the listeners |
|
1266 // participate in messages dispatched from parent message manager. |
|
1267 for (uint32_t i = 0; i < aMessageManager->mChildManagers.Length(); ++i) { |
|
1268 nsRefPtr<nsFrameMessageManager> mm = |
|
1269 static_cast<nsFrameMessageManager*>(aMessageManager->mChildManagers[i]); |
|
1270 CountReferents(mm, aReferentCount); |
|
1271 } |
|
1272 } |
|
1273 |
|
1274 static nsresult |
|
1275 ReportReferentCount(const char* aManagerType, |
|
1276 const MessageManagerReferentCount& aReferentCount, |
|
1277 nsIMemoryReporterCallback* aCb, |
|
1278 nsISupports* aClosure) |
|
1279 { |
|
1280 #define REPORT(_path, _amount, _desc) \ |
|
1281 do { \ |
|
1282 nsresult rv; \ |
|
1283 rv = aCb->Callback(EmptyCString(), _path, \ |
|
1284 nsIMemoryReporter::KIND_OTHER, \ |
|
1285 nsIMemoryReporter::UNITS_COUNT, _amount, \ |
|
1286 _desc, aClosure); \ |
|
1287 NS_ENSURE_SUCCESS(rv, rv); \ |
|
1288 } while (0) |
|
1289 |
|
1290 REPORT(nsPrintfCString("message-manager/referent/%s/strong", aManagerType), |
|
1291 aReferentCount.mStrong, |
|
1292 nsPrintfCString("The number of strong referents held by the message " |
|
1293 "manager in the %s manager.", aManagerType)); |
|
1294 REPORT(nsPrintfCString("message-manager/referent/%s/weak/alive", aManagerType), |
|
1295 aReferentCount.mWeakAlive, |
|
1296 nsPrintfCString("The number of weak referents that are still alive " |
|
1297 "held by the message manager in the %s manager.", |
|
1298 aManagerType)); |
|
1299 REPORT(nsPrintfCString("message-manager/referent/%s/weak/dead", aManagerType), |
|
1300 aReferentCount.mWeakDead, |
|
1301 nsPrintfCString("The number of weak referents that are dead " |
|
1302 "held by the message manager in the %s manager.", |
|
1303 aManagerType)); |
|
1304 |
|
1305 for (uint32_t i = 0; i < aReferentCount.mSuspectMessages.Length(); i++) { |
|
1306 uint32_t totalReferentCount = 0; |
|
1307 aReferentCount.mMessageCounter.Get(aReferentCount.mSuspectMessages[i], |
|
1308 &totalReferentCount); |
|
1309 NS_ConvertUTF16toUTF8 suspect(aReferentCount.mSuspectMessages[i]); |
|
1310 REPORT(nsPrintfCString("message-manager-suspect/%s/referent(message=%s)", |
|
1311 aManagerType, suspect.get()), totalReferentCount, |
|
1312 nsPrintfCString("A message in the %s message manager with a " |
|
1313 "suspiciously large number of referents (symptom " |
|
1314 "of a leak).", aManagerType)); |
|
1315 } |
|
1316 |
|
1317 #undef REPORT |
|
1318 |
|
1319 return NS_OK; |
|
1320 } |
|
1321 |
|
1322 NS_IMETHODIMP |
|
1323 MessageManagerReporter::CollectReports(nsIMemoryReporterCallback* aCb, |
|
1324 nsISupports* aClosure) |
|
1325 { |
|
1326 nsresult rv; |
|
1327 |
|
1328 if (XRE_GetProcessType() == GeckoProcessType_Default) { |
|
1329 nsCOMPtr<nsIMessageBroadcaster> globalmm = |
|
1330 do_GetService("@mozilla.org/globalmessagemanager;1"); |
|
1331 if (globalmm) { |
|
1332 nsRefPtr<nsFrameMessageManager> mm = |
|
1333 static_cast<nsFrameMessageManager*>(globalmm.get()); |
|
1334 MessageManagerReferentCount count; |
|
1335 CountReferents(mm, &count); |
|
1336 rv = ReportReferentCount("global-manager", count, aCb, aClosure); |
|
1337 NS_ENSURE_SUCCESS(rv, rv); |
|
1338 } |
|
1339 } |
|
1340 |
|
1341 if (nsFrameMessageManager::sParentProcessManager) { |
|
1342 MessageManagerReferentCount count; |
|
1343 CountReferents(nsFrameMessageManager::sParentProcessManager, &count); |
|
1344 rv = ReportReferentCount("parent-process-manager", count, aCb, aClosure); |
|
1345 NS_ENSURE_SUCCESS(rv, rv); |
|
1346 } |
|
1347 |
|
1348 if (nsFrameMessageManager::sChildProcessManager) { |
|
1349 MessageManagerReferentCount count; |
|
1350 CountReferents(nsFrameMessageManager::sChildProcessManager, &count); |
|
1351 rv = ReportReferentCount("child-process-manager", count, aCb, aClosure); |
|
1352 NS_ENSURE_SUCCESS(rv, rv); |
|
1353 } |
|
1354 |
|
1355 return NS_OK; |
|
1356 } |
|
1357 |
|
1358 } // namespace dom |
|
1359 } // namespace mozilla |
|
1360 |
|
1361 nsresult |
|
1362 NS_NewGlobalMessageManager(nsIMessageBroadcaster** aResult) |
|
1363 { |
|
1364 NS_ENSURE_TRUE(XRE_GetProcessType() == GeckoProcessType_Default, |
|
1365 NS_ERROR_NOT_AVAILABLE); |
|
1366 nsFrameMessageManager* mm = new nsFrameMessageManager(nullptr, |
|
1367 nullptr, |
|
1368 MM_CHROME | MM_GLOBAL | MM_BROADCASTER); |
|
1369 RegisterStrongMemoryReporter(new MessageManagerReporter()); |
|
1370 return CallQueryInterface(mm, aResult); |
|
1371 } |
|
1372 |
|
1373 nsDataHashtable<nsStringHashKey, nsFrameScriptObjectExecutorHolder*>* |
|
1374 nsFrameScriptExecutor::sCachedScripts = nullptr; |
|
1375 nsScriptCacheCleaner* nsFrameScriptExecutor::sScriptCacheCleaner = nullptr; |
|
1376 |
|
1377 void |
|
1378 nsFrameScriptExecutor::DidCreateGlobal() |
|
1379 { |
|
1380 NS_ASSERTION(mGlobal, "Should have mGlobal!"); |
|
1381 if (!sCachedScripts) { |
|
1382 sCachedScripts = |
|
1383 new nsDataHashtable<nsStringHashKey, nsFrameScriptObjectExecutorHolder*>; |
|
1384 |
|
1385 nsRefPtr<nsScriptCacheCleaner> scriptCacheCleaner = |
|
1386 new nsScriptCacheCleaner(); |
|
1387 scriptCacheCleaner.forget(&sScriptCacheCleaner); |
|
1388 } |
|
1389 } |
|
1390 |
|
1391 static PLDHashOperator |
|
1392 RemoveCachedScriptEntry(const nsAString& aKey, |
|
1393 nsFrameScriptObjectExecutorHolder*& aData, |
|
1394 void* aUserArg) |
|
1395 { |
|
1396 delete aData; |
|
1397 return PL_DHASH_REMOVE; |
|
1398 } |
|
1399 |
|
1400 // static |
|
1401 void |
|
1402 nsFrameScriptExecutor::Shutdown() |
|
1403 { |
|
1404 if (sCachedScripts) { |
|
1405 AutoSafeJSContext cx; |
|
1406 NS_ASSERTION(sCachedScripts != nullptr, "Need cached scripts"); |
|
1407 sCachedScripts->Enumerate(RemoveCachedScriptEntry, nullptr); |
|
1408 |
|
1409 delete sCachedScripts; |
|
1410 sCachedScripts = nullptr; |
|
1411 |
|
1412 nsRefPtr<nsScriptCacheCleaner> scriptCacheCleaner; |
|
1413 scriptCacheCleaner.swap(sScriptCacheCleaner); |
|
1414 } |
|
1415 } |
|
1416 |
|
1417 void |
|
1418 nsFrameScriptExecutor::LoadFrameScriptInternal(const nsAString& aURL, |
|
1419 bool aRunInGlobalScope) |
|
1420 { |
|
1421 if (!mGlobal || !sCachedScripts) { |
|
1422 return; |
|
1423 } |
|
1424 |
|
1425 AutoSafeJSContext cx; |
|
1426 JS::Rooted<JSScript*> script(cx); |
|
1427 JS::Rooted<JSObject*> funobj(cx); |
|
1428 |
|
1429 nsFrameScriptObjectExecutorHolder* holder = sCachedScripts->Get(aURL); |
|
1430 if (holder && holder->WillRunInGlobalScope() == aRunInGlobalScope) { |
|
1431 script = holder->mScript; |
|
1432 funobj = holder->mFunction; |
|
1433 } else { |
|
1434 // Don't put anything in the cache if we already have an entry |
|
1435 // with a different WillRunInGlobalScope() value. |
|
1436 bool shouldCache = !holder; |
|
1437 TryCacheLoadAndCompileScript(aURL, aRunInGlobalScope, |
|
1438 shouldCache, &script, &funobj); |
|
1439 } |
|
1440 |
|
1441 JS::Rooted<JSObject*> global(cx, mGlobal->GetJSObject()); |
|
1442 if (global) { |
|
1443 JSAutoCompartment ac(cx, global); |
|
1444 bool ok = true; |
|
1445 if (funobj) { |
|
1446 JS::Rooted<JSObject*> method(cx, JS_CloneFunctionObject(cx, funobj, global)); |
|
1447 if (!method) { |
|
1448 return; |
|
1449 } |
|
1450 JS::Rooted<JS::Value> rval(cx); |
|
1451 JS::Rooted<JS::Value> methodVal(cx, JS::ObjectValue(*method)); |
|
1452 ok = JS_CallFunctionValue(cx, global, methodVal, |
|
1453 JS::HandleValueArray::empty(), &rval); |
|
1454 } else if (script) { |
|
1455 ok = JS::CloneAndExecuteScript(cx, global, script); |
|
1456 } |
|
1457 |
|
1458 if (!ok) { |
|
1459 nsJSUtils::ReportPendingException(cx); |
|
1460 } |
|
1461 } |
|
1462 } |
|
1463 |
|
1464 void |
|
1465 nsFrameScriptExecutor::TryCacheLoadAndCompileScript(const nsAString& aURL, |
|
1466 bool aRunInGlobalScope, |
|
1467 bool aShouldCache, |
|
1468 JS::MutableHandle<JSScript*> aScriptp, |
|
1469 JS::MutableHandle<JSObject*> aFunp) |
|
1470 { |
|
1471 nsCString url = NS_ConvertUTF16toUTF8(aURL); |
|
1472 nsCOMPtr<nsIURI> uri; |
|
1473 nsresult rv = NS_NewURI(getter_AddRefs(uri), url); |
|
1474 if (NS_FAILED(rv)) { |
|
1475 return; |
|
1476 } |
|
1477 |
|
1478 bool hasFlags; |
|
1479 rv = NS_URIChainHasFlags(uri, |
|
1480 nsIProtocolHandler::URI_IS_LOCAL_RESOURCE, |
|
1481 &hasFlags); |
|
1482 if (NS_FAILED(rv) || !hasFlags) { |
|
1483 NS_WARNING("Will not load a frame script!"); |
|
1484 return; |
|
1485 } |
|
1486 |
|
1487 nsCOMPtr<nsIChannel> channel; |
|
1488 NS_NewChannel(getter_AddRefs(channel), uri); |
|
1489 if (!channel) { |
|
1490 return; |
|
1491 } |
|
1492 |
|
1493 nsCOMPtr<nsIInputStream> input; |
|
1494 channel->Open(getter_AddRefs(input)); |
|
1495 nsString dataString; |
|
1496 jschar* dataStringBuf = nullptr; |
|
1497 size_t dataStringLength = 0; |
|
1498 uint64_t avail64 = 0; |
|
1499 if (input && NS_SUCCEEDED(input->Available(&avail64)) && avail64) { |
|
1500 if (avail64 > UINT32_MAX) { |
|
1501 return; |
|
1502 } |
|
1503 nsCString buffer; |
|
1504 uint32_t avail = (uint32_t)std::min(avail64, (uint64_t)UINT32_MAX); |
|
1505 if (NS_FAILED(NS_ReadInputStreamToString(input, buffer, avail))) { |
|
1506 return; |
|
1507 } |
|
1508 nsScriptLoader::ConvertToUTF16(channel, (uint8_t*)buffer.get(), avail, |
|
1509 EmptyString(), nullptr, |
|
1510 dataStringBuf, dataStringLength); |
|
1511 } |
|
1512 |
|
1513 JS::SourceBufferHolder srcBuf(dataStringBuf, dataStringLength, |
|
1514 JS::SourceBufferHolder::GiveOwnership); |
|
1515 |
|
1516 if (dataStringBuf && dataStringLength > 0) { |
|
1517 AutoSafeJSContext cx; |
|
1518 JS::Rooted<JSObject*> global(cx, mGlobal->GetJSObject()); |
|
1519 if (global) { |
|
1520 JSAutoCompartment ac(cx, global); |
|
1521 JS::CompileOptions options(cx); |
|
1522 options.setFileAndLine(url.get(), 1); |
|
1523 JS::Rooted<JSScript*> script(cx); |
|
1524 JS::Rooted<JSObject*> funobj(cx); |
|
1525 if (aRunInGlobalScope) { |
|
1526 options.setNoScriptRval(true); |
|
1527 if (!JS::Compile(cx, JS::NullPtr(), options, srcBuf, &script)) { |
|
1528 return; |
|
1529 } |
|
1530 } else { |
|
1531 JS::Rooted<JSFunction *> fun(cx); |
|
1532 if (!JS::CompileFunction(cx, JS::NullPtr(), options, |
|
1533 nullptr, 0, nullptr, /* name, nargs, args */ |
|
1534 srcBuf, &fun)) |
|
1535 { |
|
1536 return; |
|
1537 } |
|
1538 funobj = JS_GetFunctionObject(fun); |
|
1539 } |
|
1540 |
|
1541 if (!script && !funobj) { |
|
1542 return; |
|
1543 } |
|
1544 |
|
1545 aScriptp.set(script); |
|
1546 aFunp.set(funobj); |
|
1547 |
|
1548 nsAutoCString scheme; |
|
1549 uri->GetScheme(scheme); |
|
1550 // We don't cache data: scripts! |
|
1551 if (aShouldCache && !scheme.EqualsLiteral("data")) { |
|
1552 nsFrameScriptObjectExecutorHolder* holder; |
|
1553 |
|
1554 // Root the object also for caching. |
|
1555 if (script) { |
|
1556 holder = new nsFrameScriptObjectExecutorHolder(cx, script); |
|
1557 } else { |
|
1558 holder = new nsFrameScriptObjectExecutorHolder(cx, funobj); |
|
1559 } |
|
1560 sCachedScripts->Put(aURL, holder); |
|
1561 } |
|
1562 } |
|
1563 } |
|
1564 } |
|
1565 |
|
1566 void |
|
1567 nsFrameScriptExecutor::TryCacheLoadAndCompileScript(const nsAString& aURL, |
|
1568 bool aRunInGlobalScope) |
|
1569 { |
|
1570 AutoSafeJSContext cx; |
|
1571 JS::Rooted<JSScript*> script(cx); |
|
1572 JS::Rooted<JSObject*> funobj(cx); |
|
1573 TryCacheLoadAndCompileScript(aURL, aRunInGlobalScope, true, &script, &funobj); |
|
1574 } |
|
1575 |
|
1576 bool |
|
1577 nsFrameScriptExecutor::InitTabChildGlobalInternal(nsISupports* aScope, |
|
1578 const nsACString& aID) |
|
1579 { |
|
1580 |
|
1581 nsCOMPtr<nsIJSRuntimeService> runtimeSvc = |
|
1582 do_GetService("@mozilla.org/js/xpc/RuntimeService;1"); |
|
1583 NS_ENSURE_TRUE(runtimeSvc, false); |
|
1584 |
|
1585 JSRuntime* rt = nullptr; |
|
1586 runtimeSvc->GetRuntime(&rt); |
|
1587 NS_ENSURE_TRUE(rt, false); |
|
1588 |
|
1589 AutoSafeJSContext cx; |
|
1590 nsContentUtils::GetSecurityManager()->GetSystemPrincipal(getter_AddRefs(mPrincipal)); |
|
1591 |
|
1592 nsIXPConnect* xpc = nsContentUtils::XPConnect(); |
|
1593 const uint32_t flags = nsIXPConnect::INIT_JS_STANDARD_CLASSES; |
|
1594 |
|
1595 JS::CompartmentOptions options; |
|
1596 options.setZone(JS::SystemZone) |
|
1597 .setVersion(JSVERSION_LATEST); |
|
1598 |
|
1599 nsresult rv = |
|
1600 xpc->InitClassesWithNewWrappedGlobal(cx, aScope, mPrincipal, |
|
1601 flags, options, getter_AddRefs(mGlobal)); |
|
1602 NS_ENSURE_SUCCESS(rv, false); |
|
1603 |
|
1604 |
|
1605 JS::Rooted<JSObject*> global(cx, mGlobal->GetJSObject()); |
|
1606 NS_ENSURE_TRUE(global, false); |
|
1607 |
|
1608 // Set the location information for the new global, so that tools like |
|
1609 // about:memory may use that information. |
|
1610 xpc::SetLocationForGlobal(global, aID); |
|
1611 |
|
1612 DidCreateGlobal(); |
|
1613 return true; |
|
1614 } |
|
1615 |
|
1616 NS_IMPL_ISUPPORTS(nsScriptCacheCleaner, nsIObserver) |
|
1617 |
|
1618 nsFrameMessageManager* nsFrameMessageManager::sChildProcessManager = nullptr; |
|
1619 nsFrameMessageManager* nsFrameMessageManager::sParentProcessManager = nullptr; |
|
1620 nsFrameMessageManager* nsFrameMessageManager::sSameProcessParentManager = nullptr; |
|
1621 nsTArray<nsCOMPtr<nsIRunnable> >* nsFrameMessageManager::sPendingSameProcessAsyncMessages = nullptr; |
|
1622 |
|
1623 class nsAsyncMessageToSameProcessChild : public nsSameProcessAsyncMessageBase, |
|
1624 public nsRunnable |
|
1625 { |
|
1626 public: |
|
1627 nsAsyncMessageToSameProcessChild(JSContext* aCx, |
|
1628 const nsAString& aMessage, |
|
1629 const StructuredCloneData& aData, |
|
1630 JS::Handle<JSObject *> aCpows, |
|
1631 nsIPrincipal* aPrincipal) |
|
1632 : nsSameProcessAsyncMessageBase(aCx, aMessage, aData, aCpows, aPrincipal) |
|
1633 { |
|
1634 } |
|
1635 |
|
1636 NS_IMETHOD Run() |
|
1637 { |
|
1638 nsFrameMessageManager* ppm = nsFrameMessageManager::sChildProcessManager; |
|
1639 ReceiveMessage(static_cast<nsIContentFrameMessageManager*>(ppm), ppm); |
|
1640 return NS_OK; |
|
1641 } |
|
1642 }; |
|
1643 |
|
1644 |
|
1645 /** |
|
1646 * Send messages to an imaginary child process in a single-process scenario. |
|
1647 */ |
|
1648 class SameParentProcessMessageManagerCallback : public MessageManagerCallback |
|
1649 { |
|
1650 public: |
|
1651 SameParentProcessMessageManagerCallback() |
|
1652 { |
|
1653 MOZ_COUNT_CTOR(SameParentProcessMessageManagerCallback); |
|
1654 } |
|
1655 virtual ~SameParentProcessMessageManagerCallback() |
|
1656 { |
|
1657 MOZ_COUNT_DTOR(SameParentProcessMessageManagerCallback); |
|
1658 } |
|
1659 |
|
1660 virtual bool DoSendAsyncMessage(JSContext* aCx, |
|
1661 const nsAString& aMessage, |
|
1662 const StructuredCloneData& aData, |
|
1663 JS::Handle<JSObject *> aCpows, |
|
1664 nsIPrincipal* aPrincipal) |
|
1665 { |
|
1666 nsRefPtr<nsIRunnable> ev = |
|
1667 new nsAsyncMessageToSameProcessChild(aCx, aMessage, aData, aCpows, |
|
1668 aPrincipal); |
|
1669 NS_DispatchToCurrentThread(ev); |
|
1670 return true; |
|
1671 } |
|
1672 |
|
1673 bool CheckPermission(const nsAString& aPermission) |
|
1674 { |
|
1675 // In a single-process scenario, the child always has all capabilities. |
|
1676 return true; |
|
1677 } |
|
1678 |
|
1679 bool CheckManifestURL(const nsAString& aManifestURL) |
|
1680 { |
|
1681 // In a single-process scenario, the child always has all capabilities. |
|
1682 return true; |
|
1683 } |
|
1684 |
|
1685 bool CheckAppHasPermission(const nsAString& aPermission) |
|
1686 { |
|
1687 // In a single-process scenario, the child always has all capabilities. |
|
1688 return true; |
|
1689 } |
|
1690 |
|
1691 virtual bool CheckAppHasStatus(unsigned short aStatus) |
|
1692 { |
|
1693 // In a single-process scenario, the child always has all capabilities. |
|
1694 return true; |
|
1695 } |
|
1696 }; |
|
1697 |
|
1698 |
|
1699 /** |
|
1700 * Send messages to the parent process. |
|
1701 */ |
|
1702 class ChildProcessMessageManagerCallback : public MessageManagerCallback |
|
1703 { |
|
1704 public: |
|
1705 ChildProcessMessageManagerCallback() |
|
1706 { |
|
1707 MOZ_COUNT_CTOR(ChildProcessMessageManagerCallback); |
|
1708 } |
|
1709 virtual ~ChildProcessMessageManagerCallback() |
|
1710 { |
|
1711 MOZ_COUNT_DTOR(ChildProcessMessageManagerCallback); |
|
1712 } |
|
1713 |
|
1714 virtual bool DoSendBlockingMessage(JSContext* aCx, |
|
1715 const nsAString& aMessage, |
|
1716 const mozilla::dom::StructuredCloneData& aData, |
|
1717 JS::Handle<JSObject *> aCpows, |
|
1718 nsIPrincipal* aPrincipal, |
|
1719 InfallibleTArray<nsString>* aJSONRetVal, |
|
1720 bool aIsSync) MOZ_OVERRIDE |
|
1721 { |
|
1722 mozilla::dom::ContentChild* cc = |
|
1723 mozilla::dom::ContentChild::GetSingleton(); |
|
1724 if (!cc) { |
|
1725 return true; |
|
1726 } |
|
1727 ClonedMessageData data; |
|
1728 if (!BuildClonedMessageDataForChild(cc, aData, data)) { |
|
1729 return false; |
|
1730 } |
|
1731 InfallibleTArray<mozilla::jsipc::CpowEntry> cpows; |
|
1732 if (!cc->GetCPOWManager()->Wrap(aCx, aCpows, &cpows)) { |
|
1733 return false; |
|
1734 } |
|
1735 if (aIsSync) { |
|
1736 return cc->SendSyncMessage(PromiseFlatString(aMessage), data, cpows, |
|
1737 aPrincipal, aJSONRetVal); |
|
1738 } |
|
1739 return cc->CallRpcMessage(PromiseFlatString(aMessage), data, cpows, |
|
1740 aPrincipal, aJSONRetVal); |
|
1741 } |
|
1742 |
|
1743 virtual bool DoSendAsyncMessage(JSContext* aCx, |
|
1744 const nsAString& aMessage, |
|
1745 const mozilla::dom::StructuredCloneData& aData, |
|
1746 JS::Handle<JSObject *> aCpows, |
|
1747 nsIPrincipal* aPrincipal) MOZ_OVERRIDE |
|
1748 { |
|
1749 mozilla::dom::ContentChild* cc = |
|
1750 mozilla::dom::ContentChild::GetSingleton(); |
|
1751 if (!cc) { |
|
1752 return true; |
|
1753 } |
|
1754 ClonedMessageData data; |
|
1755 if (!BuildClonedMessageDataForChild(cc, aData, data)) { |
|
1756 return false; |
|
1757 } |
|
1758 InfallibleTArray<mozilla::jsipc::CpowEntry> cpows; |
|
1759 if (!cc->GetCPOWManager()->Wrap(aCx, aCpows, &cpows)) { |
|
1760 return false; |
|
1761 } |
|
1762 return cc->SendAsyncMessage(PromiseFlatString(aMessage), data, cpows, |
|
1763 aPrincipal); |
|
1764 } |
|
1765 |
|
1766 }; |
|
1767 |
|
1768 |
|
1769 class nsAsyncMessageToSameProcessParent : public nsSameProcessAsyncMessageBase, |
|
1770 public nsRunnable |
|
1771 { |
|
1772 public: |
|
1773 nsAsyncMessageToSameProcessParent(JSContext* aCx, |
|
1774 const nsAString& aMessage, |
|
1775 const StructuredCloneData& aData, |
|
1776 JS::Handle<JSObject *> aCpows, |
|
1777 nsIPrincipal* aPrincipal) |
|
1778 : nsSameProcessAsyncMessageBase(aCx, aMessage, aData, aCpows, aPrincipal) |
|
1779 { |
|
1780 } |
|
1781 |
|
1782 NS_IMETHOD Run() |
|
1783 { |
|
1784 if (nsFrameMessageManager::sPendingSameProcessAsyncMessages) { |
|
1785 nsFrameMessageManager::sPendingSameProcessAsyncMessages->RemoveElement(this); |
|
1786 } |
|
1787 nsFrameMessageManager* ppm = nsFrameMessageManager::sSameProcessParentManager; |
|
1788 ReceiveMessage(static_cast<nsIContentFrameMessageManager*>(ppm), ppm); |
|
1789 return NS_OK; |
|
1790 } |
|
1791 }; |
|
1792 |
|
1793 /** |
|
1794 * Send messages to the imaginary parent process in a single-process scenario. |
|
1795 */ |
|
1796 class SameChildProcessMessageManagerCallback : public MessageManagerCallback |
|
1797 { |
|
1798 public: |
|
1799 SameChildProcessMessageManagerCallback() |
|
1800 { |
|
1801 MOZ_COUNT_CTOR(SameChildProcessMessageManagerCallback); |
|
1802 } |
|
1803 virtual ~SameChildProcessMessageManagerCallback() |
|
1804 { |
|
1805 MOZ_COUNT_DTOR(SameChildProcessMessageManagerCallback); |
|
1806 } |
|
1807 |
|
1808 virtual bool DoSendBlockingMessage(JSContext* aCx, |
|
1809 const nsAString& aMessage, |
|
1810 const mozilla::dom::StructuredCloneData& aData, |
|
1811 JS::Handle<JSObject *> aCpows, |
|
1812 nsIPrincipal* aPrincipal, |
|
1813 InfallibleTArray<nsString>* aJSONRetVal, |
|
1814 bool aIsSync) MOZ_OVERRIDE |
|
1815 { |
|
1816 nsTArray<nsCOMPtr<nsIRunnable> > asyncMessages; |
|
1817 if (nsFrameMessageManager::sPendingSameProcessAsyncMessages) { |
|
1818 asyncMessages.SwapElements(*nsFrameMessageManager::sPendingSameProcessAsyncMessages); |
|
1819 uint32_t len = asyncMessages.Length(); |
|
1820 for (uint32_t i = 0; i < len; ++i) { |
|
1821 nsCOMPtr<nsIRunnable> async = asyncMessages[i]; |
|
1822 async->Run(); |
|
1823 } |
|
1824 } |
|
1825 if (nsFrameMessageManager::sSameProcessParentManager) { |
|
1826 SameProcessCpowHolder cpows(js::GetRuntime(aCx), aCpows); |
|
1827 nsRefPtr<nsFrameMessageManager> ppm = nsFrameMessageManager::sSameProcessParentManager; |
|
1828 ppm->ReceiveMessage(static_cast<nsIContentFrameMessageManager*>(ppm.get()), aMessage, |
|
1829 true, &aData, &cpows, aPrincipal, aJSONRetVal); |
|
1830 } |
|
1831 return true; |
|
1832 } |
|
1833 |
|
1834 virtual bool DoSendAsyncMessage(JSContext* aCx, |
|
1835 const nsAString& aMessage, |
|
1836 const mozilla::dom::StructuredCloneData& aData, |
|
1837 JS::Handle<JSObject *> aCpows, |
|
1838 nsIPrincipal* aPrincipal) |
|
1839 { |
|
1840 if (!nsFrameMessageManager::sPendingSameProcessAsyncMessages) { |
|
1841 nsFrameMessageManager::sPendingSameProcessAsyncMessages = new nsTArray<nsCOMPtr<nsIRunnable> >; |
|
1842 } |
|
1843 nsCOMPtr<nsIRunnable> ev = |
|
1844 new nsAsyncMessageToSameProcessParent(aCx, aMessage, aData, aCpows, aPrincipal); |
|
1845 nsFrameMessageManager::sPendingSameProcessAsyncMessages->AppendElement(ev); |
|
1846 NS_DispatchToCurrentThread(ev); |
|
1847 return true; |
|
1848 } |
|
1849 |
|
1850 }; |
|
1851 |
|
1852 |
|
1853 // This creates the global parent process message manager. |
|
1854 nsresult |
|
1855 NS_NewParentProcessMessageManager(nsIMessageBroadcaster** aResult) |
|
1856 { |
|
1857 NS_ASSERTION(!nsFrameMessageManager::sParentProcessManager, |
|
1858 "Re-creating sParentProcessManager"); |
|
1859 NS_ENSURE_TRUE(XRE_GetProcessType() == GeckoProcessType_Default, |
|
1860 NS_ERROR_NOT_AVAILABLE); |
|
1861 nsRefPtr<nsFrameMessageManager> mm = new nsFrameMessageManager(nullptr, |
|
1862 nullptr, |
|
1863 MM_CHROME | MM_PROCESSMANAGER | MM_BROADCASTER); |
|
1864 nsFrameMessageManager::sParentProcessManager = mm; |
|
1865 nsFrameMessageManager::NewProcessMessageManager(nullptr); // Create same process message manager. |
|
1866 return CallQueryInterface(mm, aResult); |
|
1867 } |
|
1868 |
|
1869 |
|
1870 nsFrameMessageManager* |
|
1871 nsFrameMessageManager::NewProcessMessageManager(mozilla::dom::ContentParent* aProcess) |
|
1872 { |
|
1873 if (!nsFrameMessageManager::sParentProcessManager) { |
|
1874 nsCOMPtr<nsIMessageBroadcaster> dummy = |
|
1875 do_GetService("@mozilla.org/parentprocessmessagemanager;1"); |
|
1876 } |
|
1877 |
|
1878 MOZ_ASSERT(nsFrameMessageManager::sParentProcessManager, |
|
1879 "parent process manager not created"); |
|
1880 nsFrameMessageManager* mm; |
|
1881 if (aProcess) { |
|
1882 mm = new nsFrameMessageManager(aProcess, |
|
1883 nsFrameMessageManager::sParentProcessManager, |
|
1884 MM_CHROME | MM_PROCESSMANAGER); |
|
1885 } else { |
|
1886 mm = new nsFrameMessageManager(new SameParentProcessMessageManagerCallback(), |
|
1887 nsFrameMessageManager::sParentProcessManager, |
|
1888 MM_CHROME | MM_PROCESSMANAGER | MM_OWNSCALLBACK); |
|
1889 sSameProcessParentManager = mm; |
|
1890 } |
|
1891 return mm; |
|
1892 } |
|
1893 |
|
1894 nsresult |
|
1895 NS_NewChildProcessMessageManager(nsISyncMessageSender** aResult) |
|
1896 { |
|
1897 NS_ASSERTION(!nsFrameMessageManager::sChildProcessManager, |
|
1898 "Re-creating sChildProcessManager"); |
|
1899 |
|
1900 MessageManagerCallback* cb; |
|
1901 if (XRE_GetProcessType() == GeckoProcessType_Default) { |
|
1902 cb = new SameChildProcessMessageManagerCallback(); |
|
1903 } else { |
|
1904 cb = new ChildProcessMessageManagerCallback(); |
|
1905 RegisterStrongMemoryReporter(new MessageManagerReporter()); |
|
1906 } |
|
1907 nsFrameMessageManager* mm = new nsFrameMessageManager(cb, |
|
1908 nullptr, |
|
1909 MM_PROCESSMANAGER | MM_OWNSCALLBACK); |
|
1910 nsFrameMessageManager::sChildProcessManager = mm; |
|
1911 return CallQueryInterface(mm, aResult); |
|
1912 } |
|
1913 |
|
1914 static PLDHashOperator |
|
1915 CycleCollectorMarkListeners(const nsAString& aKey, |
|
1916 nsAutoTObserverArray<nsMessageListenerInfo, 1>* aListeners, |
|
1917 void* aData) |
|
1918 { |
|
1919 uint32_t count = aListeners->Length(); |
|
1920 for (uint32_t i = 0; i < count; i++) { |
|
1921 if (aListeners->ElementAt(i).mStrongListener) { |
|
1922 xpc_TryUnmarkWrappedGrayObject(aListeners->ElementAt(i).mStrongListener); |
|
1923 } |
|
1924 } |
|
1925 return PL_DHASH_NEXT; |
|
1926 } |
|
1927 |
|
1928 bool |
|
1929 nsFrameMessageManager::MarkForCC() |
|
1930 { |
|
1931 mListeners.EnumerateRead(CycleCollectorMarkListeners, nullptr); |
|
1932 |
|
1933 if (mRefCnt.IsPurple()) { |
|
1934 mRefCnt.RemovePurple(); |
|
1935 } |
|
1936 return true; |
|
1937 } |
|
1938 |
|
1939 nsSameProcessAsyncMessageBase::nsSameProcessAsyncMessageBase(JSContext* aCx, |
|
1940 const nsAString& aMessage, |
|
1941 const StructuredCloneData& aData, |
|
1942 JS::Handle<JSObject*> aCpows, |
|
1943 nsIPrincipal* aPrincipal) |
|
1944 : mRuntime(js::GetRuntime(aCx)), |
|
1945 mMessage(aMessage), |
|
1946 mCpows(aCx, aCpows), |
|
1947 mPrincipal(aPrincipal) |
|
1948 { |
|
1949 if (aData.mDataLength && !mData.copy(aData.mData, aData.mDataLength)) { |
|
1950 NS_RUNTIMEABORT("OOM"); |
|
1951 } |
|
1952 mClosure = aData.mClosure; |
|
1953 } |
|
1954 |
|
1955 void |
|
1956 nsSameProcessAsyncMessageBase::ReceiveMessage(nsISupports* aTarget, |
|
1957 nsFrameMessageManager* aManager) |
|
1958 { |
|
1959 if (aManager) { |
|
1960 StructuredCloneData data; |
|
1961 data.mData = mData.data(); |
|
1962 data.mDataLength = mData.nbytes(); |
|
1963 data.mClosure = mClosure; |
|
1964 |
|
1965 SameProcessCpowHolder cpows(mRuntime, mCpows); |
|
1966 |
|
1967 nsRefPtr<nsFrameMessageManager> mm = aManager; |
|
1968 mm->ReceiveMessage(aTarget, mMessage, false, &data, &cpows, |
|
1969 mPrincipal, nullptr); |
|
1970 } |
|
1971 } |