Source code

Revision control

Other Tools

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
#include "base/basictypes.h"
8
9
#include "nsFrameMessageManager.h"
10
11
#include "ContentChild.h"
12
#include "GeckoProfiler.h"
13
#include "nsASCIIMask.h"
14
#include "nsContentUtils.h"
15
#include "nsError.h"
16
#include "nsIXPConnect.h"
17
#include "jsapi.h"
18
#include "jsfriendapi.h"
19
#include "nsJSUtils.h"
20
#include "nsJSPrincipals.h"
21
#include "nsNetUtil.h"
22
#include "mozilla/dom/ScriptLoader.h"
23
#include "nsFrameLoader.h"
24
#include "nsIInputStream.h"
25
#include "nsIScriptError.h"
26
#include "nsIConsoleService.h"
27
#include "nsIMemoryReporter.h"
28
#include "nsIProtocolHandler.h"
29
#include "xpcpublic.h"
30
#include "js/CompilationAndEvaluation.h"
31
#include "js/JSON.h"
32
#include "js/SourceText.h"
33
#include "mozilla/ClearOnShutdown.h"
34
#include "mozilla/CycleCollectedJSContext.h"
35
#include "mozilla/Preferences.h"
36
#include "mozilla/ScriptPreloader.h"
37
#include "mozilla/Telemetry.h"
38
#include "mozilla/dom/ChildProcessMessageManager.h"
39
#include "mozilla/dom/ChromeMessageBroadcaster.h"
40
#include "mozilla/dom/File.h"
41
#include "mozilla/dom/MessageManagerBinding.h"
42
#include "mozilla/dom/MessagePort.h"
43
#include "mozilla/dom/ContentParent.h"
44
#include "mozilla/dom/ContentProcessMessageManager.h"
45
#include "mozilla/dom/ParentProcessMessageManager.h"
46
#include "mozilla/dom/PermissionMessageUtils.h"
47
#include "mozilla/dom/ProcessMessageManager.h"
48
#include "mozilla/dom/SameProcessMessageQueue.h"
49
#include "mozilla/dom/ScriptSettings.h"
50
#include "mozilla/dom/ToJSValue.h"
51
#include "mozilla/dom/ipc/SharedMap.h"
52
#include "mozilla/dom/ipc/StructuredCloneData.h"
53
#include "mozilla/dom/DOMStringList.h"
54
#include "mozilla/jsipc/CrossProcessObjectWrappers.h"
55
#include "mozilla/recordreplay/ParentIPC.h"
56
#include "nsPrintfCString.h"
57
#include "nsXULAppAPI.h"
58
#include "nsQueryObject.h"
59
#include "xpcprivate.h"
60
#include <algorithm>
61
#include "chrome/common/ipc_channel.h" // for IPC::Channel::kMaximumMessageSize
62
63
#ifdef XP_WIN
64
# if defined(SendMessage)
65
# undef SendMessage
66
# endif
67
#endif
68
69
#ifdef FUZZING
70
# include "MessageManagerFuzzer.h"
71
#endif
72
73
using namespace mozilla;
74
using namespace mozilla::dom;
75
using namespace mozilla::dom::ipc;
76
77
nsFrameMessageManager::nsFrameMessageManager(MessageManagerCallback* aCallback,
78
MessageManagerFlags aFlags)
79
: mChrome(aFlags & MessageManagerFlags::MM_CHROME),
80
mGlobal(aFlags & MessageManagerFlags::MM_GLOBAL),
81
mIsProcessManager(aFlags & MessageManagerFlags::MM_PROCESSMANAGER),
82
mIsBroadcaster(aFlags & MessageManagerFlags::MM_BROADCASTER),
83
mOwnsCallback(aFlags & MessageManagerFlags::MM_OWNSCALLBACK),
84
mHandlingMessage(false),
85
mClosed(false),
86
mDisconnected(false),
87
mCallback(aCallback) {
88
NS_ASSERTION(!mIsBroadcaster || !mCallback,
89
"Broadcasters cannot have callbacks!");
90
if (mOwnsCallback) {
91
mOwnedCallback = aCallback;
92
}
93
}
94
95
nsFrameMessageManager::~nsFrameMessageManager() {
96
for (int32_t i = mChildManagers.Length(); i > 0; --i) {
97
mChildManagers[i - 1]->Disconnect(false);
98
}
99
if (mIsProcessManager) {
100
if (this == sParentProcessManager) {
101
sParentProcessManager = nullptr;
102
}
103
if (this == sChildProcessManager) {
104
sChildProcessManager = nullptr;
105
delete mozilla::dom::SameProcessMessageQueue::Get();
106
}
107
if (this == sSameProcessParentManager) {
108
sSameProcessParentManager = nullptr;
109
}
110
}
111
}
112
113
inline void ImplCycleCollectionTraverse(
114
nsCycleCollectionTraversalCallback& aCallback,
115
nsMessageListenerInfo& aField, const char* aName, uint32_t aFlags = 0) {
116
ImplCycleCollectionTraverse(aCallback, aField.mStrongListener, aName, aFlags);
117
ImplCycleCollectionTraverse(aCallback, aField.mWeakListener, aName, aFlags);
118
}
119
120
NS_IMPL_CYCLE_COLLECTION_CLASS(nsFrameMessageManager)
121
122
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsFrameMessageManager)
123
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mListeners)
124
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChildManagers)
125
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSharedData)
126
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
127
128
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsFrameMessageManager)
129
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mInitialProcessData)
130
NS_IMPL_CYCLE_COLLECTION_TRACE_END
131
132
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsFrameMessageManager)
133
NS_IMPL_CYCLE_COLLECTION_UNLINK(mListeners)
134
for (int32_t i = tmp->mChildManagers.Length(); i > 0; --i) {
135
tmp->mChildManagers[i - 1]->Disconnect(false);
136
}
137
NS_IMPL_CYCLE_COLLECTION_UNLINK(mChildManagers)
138
NS_IMPL_CYCLE_COLLECTION_UNLINK(mSharedData)
139
tmp->mInitialProcessData.setNull();
140
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
141
142
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsFrameMessageManager)
143
NS_INTERFACE_MAP_ENTRY(nsISupports)
144
145
/* Message managers in child process implement nsIMessageSender.
146
Message managers in the chrome process are
147
either broadcasters (if they have subordinate/child message
148
managers) or they're simple message senders. */
149
NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIMessageSender,
150
!mChrome || !mIsBroadcaster)
151
152
NS_INTERFACE_MAP_END
153
154
NS_IMPL_CYCLE_COLLECTING_ADDREF(nsFrameMessageManager)
155
NS_IMPL_CYCLE_COLLECTING_RELEASE(nsFrameMessageManager)
156
157
void MessageManagerCallback::DoGetRemoteType(nsAString& aRemoteType,
158
ErrorResult& aError) const {
159
aRemoteType.Truncate();
160
mozilla::dom::ProcessMessageManager* parent = GetProcessMessageManager();
161
if (!parent) {
162
return;
163
}
164
165
parent->GetRemoteType(aRemoteType, aError);
166
}
167
168
bool MessageManagerCallback::BuildClonedMessageDataForParent(
169
ContentParent* aParent, StructuredCloneData& aData,
170
ClonedMessageData& aClonedData) {
171
return aData.BuildClonedMessageDataForParent(aParent, aClonedData);
172
}
173
174
bool MessageManagerCallback::BuildClonedMessageDataForChild(
175
ContentChild* aChild, StructuredCloneData& aData,
176
ClonedMessageData& aClonedData) {
177
return aData.BuildClonedMessageDataForChild(aChild, aClonedData);
178
}
179
180
void mozilla::dom::ipc::UnpackClonedMessageDataForParent(
181
const ClonedMessageData& aClonedData, StructuredCloneData& aData) {
182
aData.BorrowFromClonedMessageDataForParent(aClonedData);
183
}
184
185
void mozilla::dom::ipc::UnpackClonedMessageDataForChild(
186
const ClonedMessageData& aClonedData, StructuredCloneData& aData) {
187
aData.BorrowFromClonedMessageDataForChild(aClonedData);
188
}
189
190
bool SameProcessCpowHolder::ToObject(JSContext* aCx,
191
JS::MutableHandle<JSObject*> aObjp) {
192
if (!mObj) {
193
return true;
194
}
195
196
aObjp.set(mObj);
197
return JS_WrapObject(aCx, aObjp);
198
}
199
200
void nsFrameMessageManager::AddMessageListener(const nsAString& aMessageName,
201
MessageListener& aListener,
202
bool aListenWhenClosed,
203
ErrorResult& aError) {
204
auto& listeners = mListeners.LookupForAdd(aMessageName).OrInsert([]() {
205
return new nsAutoTObserverArray<nsMessageListenerInfo, 1>();
206
});
207
uint32_t len = listeners->Length();
208
for (uint32_t i = 0; i < len; ++i) {
209
MessageListener* strongListener = listeners->ElementAt(i).mStrongListener;
210
if (strongListener && *strongListener == aListener) {
211
return;
212
}
213
}
214
215
nsMessageListenerInfo* entry = listeners->AppendElement();
216
entry->mStrongListener = &aListener;
217
entry->mListenWhenClosed = aListenWhenClosed;
218
}
219
220
void nsFrameMessageManager::RemoveMessageListener(const nsAString& aMessageName,
221
MessageListener& aListener,
222
ErrorResult& aError) {
223
nsAutoTObserverArray<nsMessageListenerInfo, 1>* listeners =
224
mListeners.Get(aMessageName);
225
if (listeners) {
226
uint32_t len = listeners->Length();
227
for (uint32_t i = 0; i < len; ++i) {
228
MessageListener* strongListener = listeners->ElementAt(i).mStrongListener;
229
if (strongListener && *strongListener == aListener) {
230
listeners->RemoveElementAt(i);
231
return;
232
}
233
}
234
}
235
}
236
237
static already_AddRefed<nsISupports> ToXPCOMMessageListener(
238
MessageListener& aListener) {
239
return CallbackObjectHolder<mozilla::dom::MessageListener, nsISupports>(
240
&aListener)
241
.ToXPCOMCallback();
242
}
243
244
void nsFrameMessageManager::AddWeakMessageListener(
245
const nsAString& aMessageName, MessageListener& aListener,
246
ErrorResult& aError) {
247
nsCOMPtr<nsISupports> listener(ToXPCOMMessageListener(aListener));
248
nsWeakPtr weak = do_GetWeakReference(listener);
249
if (!weak) {
250
aError.Throw(NS_ERROR_NO_INTERFACE);
251
return;
252
}
253
254
#ifdef DEBUG
255
// It's technically possible that one object X could give two different
256
// nsIWeakReference*'s when you do_GetWeakReference(X). We really don't want
257
// this to happen; it will break e.g. RemoveWeakMessageListener. So let's
258
// check that we're not getting ourselves into that situation.
259
nsCOMPtr<nsISupports> canonical = do_QueryInterface(listener);
260
for (auto iter = mListeners.Iter(); !iter.Done(); iter.Next()) {
261
nsAutoTObserverArray<nsMessageListenerInfo, 1>* listeners = iter.UserData();
262
uint32_t count = listeners->Length();
263
for (uint32_t i = 0; i < count; i++) {
264
nsWeakPtr weakListener = listeners->ElementAt(i).mWeakListener;
265
if (weakListener) {
266
nsCOMPtr<nsISupports> otherCanonical = do_QueryReferent(weakListener);
267
MOZ_ASSERT((canonical == otherCanonical) == (weak == weakListener));
268
}
269
}
270
}
271
#endif
272
273
auto& listeners = mListeners.LookupForAdd(aMessageName).OrInsert([]() {
274
return new nsAutoTObserverArray<nsMessageListenerInfo, 1>();
275
});
276
uint32_t len = listeners->Length();
277
for (uint32_t i = 0; i < len; ++i) {
278
if (listeners->ElementAt(i).mWeakListener == weak) {
279
return;
280
}
281
}
282
283
nsMessageListenerInfo* entry = listeners->AppendElement();
284
entry->mWeakListener = weak;
285
entry->mListenWhenClosed = false;
286
}
287
288
void nsFrameMessageManager::RemoveWeakMessageListener(
289
const nsAString& aMessageName, MessageListener& aListener,
290
ErrorResult& aError) {
291
nsCOMPtr<nsISupports> listener(ToXPCOMMessageListener(aListener));
292
nsWeakPtr weak = do_GetWeakReference(listener);
293
if (!weak) {
294
aError.Throw(NS_ERROR_NO_INTERFACE);
295
return;
296
}
297
298
nsAutoTObserverArray<nsMessageListenerInfo, 1>* listeners =
299
mListeners.Get(aMessageName);
300
if (!listeners) {
301
return;
302
}
303
304
uint32_t len = listeners->Length();
305
for (uint32_t i = 0; i < len; ++i) {
306
if (listeners->ElementAt(i).mWeakListener == weak) {
307
listeners->RemoveElementAt(i);
308
return;
309
}
310
}
311
}
312
313
void nsFrameMessageManager::LoadScript(const nsAString& aURL,
314
bool aAllowDelayedLoad,
315
bool aRunInGlobalScope,
316
ErrorResult& aError) {
317
if (aAllowDelayedLoad) {
318
// Cache for future windows or frames
319
mPendingScripts.AppendElement(aURL);
320
mPendingScriptsGlobalStates.AppendElement(aRunInGlobalScope);
321
}
322
323
if (mCallback) {
324
#ifdef DEBUG_smaug
325
printf("Will load %s \n", NS_ConvertUTF16toUTF8(aURL).get());
326
#endif
327
if (!mCallback->DoLoadMessageManagerScript(aURL, aRunInGlobalScope)) {
328
aError.Throw(NS_ERROR_FAILURE);
329
return;
330
}
331
}
332
333
for (uint32_t i = 0; i < mChildManagers.Length(); ++i) {
334
RefPtr<nsFrameMessageManager> mm = mChildManagers[i];
335
if (mm) {
336
// Use false here, so that child managers don't cache the script, which
337
// is already cached in the parent.
338
mm->LoadScript(aURL, false, aRunInGlobalScope, IgnoreErrors());
339
}
340
}
341
}
342
343
void nsFrameMessageManager::RemoveDelayedScript(const nsAString& aURL) {
344
for (uint32_t i = 0; i < mPendingScripts.Length(); ++i) {
345
if (mPendingScripts[i] == aURL) {
346
mPendingScripts.RemoveElementAt(i);
347
mPendingScriptsGlobalStates.RemoveElementAt(i);
348
break;
349
}
350
}
351
}
352
353
void nsFrameMessageManager::GetDelayedScripts(
354
JSContext* aCx, nsTArray<nsTArray<JS::Value>>& aList, ErrorResult& aError) {
355
// Frame message managers may return an incomplete list because scripts
356
// that were loaded after it was connected are not added to the list.
357
if (!IsGlobal() && !IsBroadcaster()) {
358
NS_WARNING(
359
"Cannot retrieve list of pending frame scripts for frame"
360
"message managers as it may be incomplete");
361
aError.Throw(NS_ERROR_NOT_IMPLEMENTED);
362
return;
363
}
364
365
aError.MightThrowJSException();
366
367
aList.SetCapacity(mPendingScripts.Length());
368
for (uint32_t i = 0; i < mPendingScripts.Length(); ++i) {
369
JS::Rooted<JS::Value> url(aCx);
370
if (!ToJSValue(aCx, mPendingScripts[i], &url)) {
371
aError.NoteJSContextException(aCx);
372
return;
373
}
374
375
nsTArray<JS::Value>* array = aList.AppendElement(2);
376
array->AppendElement(url);
377
array->AppendElement(JS::BooleanValue(mPendingScriptsGlobalStates[i]));
378
}
379
}
380
381
/* static */
382
bool nsFrameMessageManager::GetParamsForMessage(JSContext* aCx,
383
const JS::Value& aValue,
384
const JS::Value& aTransfer,
385
StructuredCloneData& aData) {
386
// First try to use structured clone on the whole thing.
387
JS::RootedValue v(aCx, aValue);
388
JS::RootedValue t(aCx, aTransfer);
389
ErrorResult rv;
390
aData.Write(aCx, v, t, rv);
391
if (!rv.Failed()) {
392
return true;
393
}
394
395
rv.SuppressException();
396
JS_ClearPendingException(aCx);
397
398
nsCOMPtr<nsIConsoleService> console(
399
do_GetService(NS_CONSOLESERVICE_CONTRACTID));
400
if (console) {
401
nsAutoString filename;
402
uint32_t lineno = 0, column = 0;
403
nsJSUtils::GetCallingLocation(aCx, filename, &lineno, &column);
404
nsCOMPtr<nsIScriptError> error(
405
do_CreateInstance(NS_SCRIPTERROR_CONTRACTID));
406
error->Init(NS_LITERAL_STRING("Sending message that cannot be cloned. Are "
407
"you trying to send an XPCOM object?"),
408
filename, EmptyString(), lineno, column,
409
nsIScriptError::warningFlag, "chrome javascript",
410
false /* from private window */,
411
true /* from chrome context */);
412
console->LogMessage(error);
413
}
414
415
// Not clonable, try JSON
416
// XXX This is ugly but currently structured cloning doesn't handle
417
// properly cases when interface is implemented in JS and used
418
// as a dictionary.
419
nsAutoString json;
420
NS_ENSURE_TRUE(nsContentUtils::StringifyJSON(aCx, &v, json), false);
421
NS_ENSURE_TRUE(!json.IsEmpty(), false);
422
423
JS::Rooted<JS::Value> val(aCx, JS::NullValue());
424
NS_ENSURE_TRUE(JS_ParseJSON(aCx, static_cast<const char16_t*>(json.get()),
425
json.Length(), &val),
426
false);
427
428
aData.Write(aCx, val, rv);
429
if (NS_WARN_IF(rv.Failed())) {
430
rv.SuppressException();
431
return false;
432
}
433
434
return true;
435
}
436
437
static bool sSendingSyncMessage = false;
438
439
static bool AllowMessage(size_t aDataLength, const nsAString& aMessageName) {
440
// A message includes more than structured clone data, so subtract
441
// 20KB to make it more likely that a message within this bound won't
442
// result in an overly large IPC message.
443
static const size_t kMaxMessageSize =
444
IPC::Channel::kMaximumMessageSize - 20 * 1024;
445
return aDataLength < kMaxMessageSize;
446
}
447
448
void nsFrameMessageManager::SendMessage(
449
JSContext* aCx, const nsAString& aMessageName, JS::Handle<JS::Value> aObj,
450
JS::Handle<JSObject*> aObjects, nsIPrincipal* aPrincipal, bool aIsSync,
451
nsTArray<JS::Value>& aResult, ErrorResult& aError) {
452
NS_ASSERTION(!IsGlobal(), "Should not call SendSyncMessage in chrome");
453
NS_ASSERTION(!IsBroadcaster(), "Should not call SendSyncMessage in chrome");
454
NS_ASSERTION(!GetParentManager(),
455
"Should not have parent manager in content!");
456
457
AUTO_PROFILER_LABEL_DYNAMIC_LOSSY_NSSTRING(
458
"nsFrameMessageManager::SendMessage", OTHER, aMessageName);
459
460
if (sSendingSyncMessage && aIsSync) {
461
// No kind of blocking send should be issued on top of a sync message.
462
aError.Throw(NS_ERROR_UNEXPECTED);
463
return;
464
}
465
466
StructuredCloneData data;
467
if (!aObj.isUndefined() &&
468
!GetParamsForMessage(aCx, aObj, JS::UndefinedHandleValue, data)) {
469
aError.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
470
return;
471
}
472
473
#ifdef FUZZING
474
if (data.DataLength() > 0) {
475
MessageManagerFuzzer::TryMutate(aCx, aMessageName, &data,
476
JS::UndefinedHandleValue);
477
}
478
#endif
479
480
if (!AllowMessage(data.DataLength(), aMessageName)) {
481
aError.Throw(NS_ERROR_FAILURE);
482
return;
483
}
484
485
if (!mCallback) {
486
aError.Throw(NS_ERROR_NOT_INITIALIZED);
487
return;
488
}
489
490
nsTArray<StructuredCloneData> retval;
491
492
TimeStamp start = TimeStamp::Now();
493
sSendingSyncMessage |= aIsSync;
494
bool ok = mCallback->DoSendBlockingMessage(aCx, aMessageName, data, aObjects,
495
aPrincipal, &retval, aIsSync);
496
if (aIsSync) {
497
sSendingSyncMessage = false;
498
}
499
500
uint32_t latencyMs = round((TimeStamp::Now() - start).ToMilliseconds());
501
if (latencyMs >= kMinTelemetrySyncMessageManagerLatencyMs) {
502
NS_ConvertUTF16toUTF8 messageName(aMessageName);
503
// NOTE: We need to strip digit characters from the message name in order to
504
// avoid a large number of buckets due to generated names from addons (such
505
// as "ublock:sb:{N}"). See bug 1348113 comment 10.
506
messageName.StripTaggedASCII(ASCIIMask::Mask0to9());
507
Telemetry::Accumulate(Telemetry::IPC_SYNC_MESSAGE_MANAGER_LATENCY_MS,
508
messageName, latencyMs);
509
}
510
511
if (!ok) {
512
return;
513
}
514
515
uint32_t len = retval.Length();
516
aResult.SetCapacity(len);
517
for (uint32_t i = 0; i < len; ++i) {
518
JS::Rooted<JS::Value> ret(aCx);
519
retval[i].Read(aCx, &ret, aError);
520
if (aError.Failed()) {
521
MOZ_ASSERT(false, "Unable to read structured clone in SendMessage");
522
return;
523
}
524
aResult.AppendElement(ret);
525
}
526
}
527
528
nsresult nsFrameMessageManager::DispatchAsyncMessageInternal(
529
JSContext* aCx, const nsAString& aMessage, StructuredCloneData& aData,
530
JS::Handle<JSObject*> aCpows, nsIPrincipal* aPrincipal) {
531
if (mIsBroadcaster) {
532
uint32_t len = mChildManagers.Length();
533
for (uint32_t i = 0; i < len; ++i) {
534
mChildManagers[i]->DispatchAsyncMessageInternal(aCx, aMessage, aData,
535
aCpows, aPrincipal);
536
}
537
return NS_OK;
538
}
539
540
if (!mCallback) {
541
return NS_ERROR_NOT_INITIALIZED;
542
}
543
544
nsresult rv =
545
mCallback->DoSendAsyncMessage(aCx, aMessage, aData, aCpows, aPrincipal);
546
if (NS_FAILED(rv)) {
547
return rv;
548
}
549
return NS_OK;
550
}
551
552
void nsFrameMessageManager::DispatchAsyncMessage(
553
JSContext* aCx, const nsAString& aMessageName, JS::Handle<JS::Value> aObj,
554
JS::Handle<JSObject*> aObjects, nsIPrincipal* aPrincipal,
555
JS::Handle<JS::Value> aTransfers, ErrorResult& aError) {
556
StructuredCloneData data;
557
if (!aObj.isUndefined() &&
558
!GetParamsForMessage(aCx, aObj, aTransfers, data)) {
559
aError.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
560
return;
561
}
562
563
#ifdef FUZZING
564
if (data.DataLength()) {
565
MessageManagerFuzzer::TryMutate(aCx, aMessageName, &data, aTransfers);
566
}
567
#endif
568
569
if (!AllowMessage(data.DataLength(), aMessageName)) {
570
aError.Throw(NS_ERROR_FAILURE);
571
return;
572
}
573
574
aError = DispatchAsyncMessageInternal(aCx, aMessageName, data, aObjects,
575
aPrincipal);
576
}
577
578
class MMListenerRemover {
579
public:
580
explicit MMListenerRemover(nsFrameMessageManager* aMM)
581
: mWasHandlingMessage(aMM->mHandlingMessage), mMM(aMM) {
582
mMM->mHandlingMessage = true;
583
}
584
~MMListenerRemover() {
585
if (!mWasHandlingMessage) {
586
mMM->mHandlingMessage = false;
587
if (mMM->mDisconnected) {
588
mMM->mListeners.Clear();
589
}
590
}
591
}
592
593
bool mWasHandlingMessage;
594
RefPtr<nsFrameMessageManager> mMM;
595
};
596
597
// When recording or replaying, return whether a message should be received in
598
// the middleman process instead of the recording/replaying process.
599
static bool DirectMessageToMiddleman(const nsAString& aMessage) {
600
// Middleman processes run developer tools server code and need to receive
601
// debugger related messages. The session store flush message needs to be
602
// received in order to cleanly shutdown the process.
603
return (StringBeginsWith(aMessage, NS_LITERAL_STRING("debug:")) &&
604
recordreplay::parent::DebuggerRunsInMiddleman()) ||
605
aMessage.EqualsLiteral("SessionStore:flush");
606
}
607
608
void nsFrameMessageManager::ReceiveMessage(
609
nsISupports* aTarget, nsFrameLoader* aTargetFrameLoader, bool aTargetClosed,
610
const nsAString& aMessage, bool aIsSync, StructuredCloneData* aCloneData,
611
mozilla::jsipc::CpowHolder* aCpows, nsIPrincipal* aPrincipal,
612
nsTArray<StructuredCloneData>* aRetVal, ErrorResult& aError) {
613
// If we are recording or replaying, we will end up here in both the
614
// middleman process and the recording/replaying process. Ignore the message
615
// in one of the processes, so that it is only received in one place.
616
if (recordreplay::IsRecordingOrReplaying()) {
617
if (DirectMessageToMiddleman(aMessage)) {
618
return;
619
}
620
} else if (recordreplay::IsMiddleman()) {
621
if (!DirectMessageToMiddleman(aMessage)) {
622
return;
623
}
624
}
625
626
MOZ_ASSERT(aTarget);
627
628
nsAutoTObserverArray<nsMessageListenerInfo, 1>* listeners =
629
mListeners.Get(aMessage);
630
if (listeners) {
631
MMListenerRemover lr(this);
632
633
nsAutoTObserverArray<nsMessageListenerInfo, 1>::EndLimitedIterator iter(
634
*listeners);
635
while (iter.HasMore()) {
636
nsMessageListenerInfo& listener = iter.GetNext();
637
// Remove mListeners[i] if it's an expired weak listener.
638
nsCOMPtr<nsISupports> weakListener;
639
if (listener.mWeakListener) {
640
weakListener = do_QueryReferent(listener.mWeakListener);
641
if (!weakListener) {
642
listeners->RemoveElement(listener);
643
continue;
644
}
645
}
646
647
if (!listener.mListenWhenClosed && aTargetClosed) {
648
continue;
649
}
650
651
JS::RootingContext* rcx = RootingCx();
652
JS::Rooted<JSObject*> object(rcx);
653
JS::Rooted<JSObject*> objectGlobal(rcx);
654
655
RefPtr<MessageListener> webIDLListener;
656
if (!weakListener) {
657
webIDLListener = listener.mStrongListener;
658
object = webIDLListener->CallbackOrNull();
659
objectGlobal = webIDLListener->CallbackGlobalOrNull();
660
} else {
661
nsCOMPtr<nsIXPConnectWrappedJS> wrappedJS =
662
do_QueryInterface(weakListener);
663
if (!wrappedJS) {
664
continue;
665
}
666
667
object = wrappedJS->GetJSObject();
668
objectGlobal = wrappedJS->GetJSObjectGlobal();
669
}
670
671
if (!object) {
672
continue;
673
}
674
675
AutoEntryScript aes(js::UncheckedUnwrap(object),
676
"message manager handler");
677
JSContext* cx = aes.cx();
678
679
// We passed the unwrapped object to AutoEntryScript so we now need to
680
// enter the realm of the global object that represents the realm of our
681
// callback.
682
JSAutoRealm ar(cx, objectGlobal);
683
684
RootedDictionary<ReceiveMessageArgument> argument(cx);
685
686
JS::Rooted<JSObject*> cpows(cx);
687
if (aCpows && !aCpows->ToObject(cx, &cpows)) {
688
aError.Throw(NS_ERROR_UNEXPECTED);
689
return;
690
}
691
692
if (!cpows) {
693
cpows = JS_NewPlainObject(cx);
694
if (!cpows) {
695
aError.Throw(NS_ERROR_UNEXPECTED);
696
return;
697
}
698
}
699
argument.mObjects = cpows;
700
701
JS::Rooted<JS::Value> json(cx, JS::NullValue());
702
if (aCloneData && aCloneData->DataLength()) {
703
aCloneData->Read(cx, &json, aError);
704
if (NS_WARN_IF(aError.Failed())) {
705
aError.SuppressException();
706
JS_ClearPendingException(cx);
707
return;
708
}
709
}
710
argument.mData = json;
711
argument.mJson = json;
712
713
// Get cloned MessagePort from StructuredCloneData.
714
if (aCloneData) {
715
Sequence<OwningNonNull<MessagePort>> ports;
716
if (!aCloneData->TakeTransferredPortsAsSequence(ports)) {
717
aError.Throw(NS_ERROR_FAILURE);
718
return;
719
}
720
argument.mPorts.Construct(std::move(ports));
721
}
722
723
argument.mName = aMessage;
724
argument.mPrincipal = aPrincipal;
725
argument.mSync = aIsSync;
726
argument.mTarget = aTarget;
727
if (aTargetFrameLoader) {
728
argument.mTargetFrameLoader.Construct(*aTargetFrameLoader);
729
}
730
731
JS::Rooted<JS::Value> thisValue(cx, JS::UndefinedValue());
732
733
if (JS::IsCallable(object)) {
734
// A small hack to get 'this' value right on content side where
735
// messageManager is wrapped in BrowserChildMessageManager's global.
736
nsCOMPtr<nsISupports> defaultThisValue;
737
if (mChrome) {
738
defaultThisValue = do_QueryObject(this);
739
} else {
740
defaultThisValue = aTarget;
741
}
742
js::AssertSameCompartment(cx, object);
743
aError = nsContentUtils::WrapNative(cx, defaultThisValue, &thisValue);
744
if (aError.Failed()) {
745
return;
746
}
747
}
748
749
JS::Rooted<JS::Value> rval(cx, JS::UndefinedValue());
750
if (webIDLListener) {
751
webIDLListener->ReceiveMessage(thisValue, argument, &rval, aError);
752
if (aError.Failed()) {
753
// At this point the call to ReceiveMessage will have reported any
754
// exceptions (we kept the default of eReportExceptions). We suppress
755
// the failure in the ErrorResult and continue.
756
aError.SuppressException();
757
continue;
758
}
759
} else {
760
JS::Rooted<JS::Value> funval(cx);
761
if (JS::IsCallable(object)) {
762
// If the listener is a JS function:
763
funval.setObject(*object);
764
} else {
765
// If the listener is a JS object which has receiveMessage function:
766
if (!JS_GetProperty(cx, object, "receiveMessage", &funval) ||
767
!funval.isObject()) {
768
aError.Throw(NS_ERROR_UNEXPECTED);
769
return;
770
}
771
772
// Check if the object is even callable.
773
if (!JS::IsCallable(&funval.toObject())) {
774
aError.Throw(NS_ERROR_UNEXPECTED);
775
return;
776
}
777
thisValue.setObject(*object);
778
}
779
780
JS::Rooted<JS::Value> argv(cx);
781
if (!ToJSValue(cx, argument, &argv)) {
782
aError.Throw(NS_ERROR_UNEXPECTED);
783
return;
784
}
785
786
{
787
JS::Rooted<JSObject*> thisObject(cx, thisValue.toObjectOrNull());
788
js::AssertSameCompartment(cx, thisObject);
789
if (!JS_CallFunctionValue(cx, thisObject, funval,
790
JS::HandleValueArray(argv), &rval)) {
791
// Because the AutoEntryScript is inside the loop this continue will
792
// make us report any exceptions (after which we'll move on to the
793
// next listener).
794
continue;
795
}
796
}
797
}
798
799
if (aRetVal) {
800
StructuredCloneData* data = aRetVal->AppendElement();
801
data->InitScope(JS::StructuredCloneScope::DifferentProcess);
802
data->Write(cx, rval, aError);
803
if (NS_WARN_IF(aError.Failed())) {
804
aRetVal->RemoveLastElement();
805
nsString msg = aMessage + NS_LITERAL_STRING(
806
": message reply cannot be cloned. Are "
807
"you trying to send an XPCOM object?");
808
809
nsCOMPtr<nsIConsoleService> console(
810
do_GetService(NS_CONSOLESERVICE_CONTRACTID));
811
if (console) {
812
nsCOMPtr<nsIScriptError> error(
813
do_CreateInstance(NS_SCRIPTERROR_CONTRACTID));
814
error->Init(msg, EmptyString(), EmptyString(), 0, 0,
815
nsIScriptError::warningFlag, "chrome javascript",
816
false /* from private window */,
817
true /* from chrome context */);
818
console->LogMessage(error);
819
}
820
821
JS_ClearPendingException(cx);
822
continue;
823
}
824
}
825
}
826
}
827
828
RefPtr<nsFrameMessageManager> kungFuDeathGrip = GetParentManager();
829
if (kungFuDeathGrip) {
830
kungFuDeathGrip->ReceiveMessage(aTarget, aTargetFrameLoader, aTargetClosed,
831
aMessage, aIsSync, aCloneData, aCpows,
832
aPrincipal, aRetVal, aError);
833
}
834
}
835
836
void nsFrameMessageManager::LoadPendingScripts(
837
nsFrameMessageManager* aManager, nsFrameMessageManager* aChildMM) {
838
// We have parent manager if we're a message broadcaster.
839
// In that case we want to load the pending scripts from all parent
840
// message managers in the hierarchy. Process the parent first so
841
// that pending scripts higher up in the hierarchy are loaded before others.
842
nsFrameMessageManager* parentManager = aManager->GetParentManager();
843
if (parentManager) {
844
LoadPendingScripts(parentManager, aChildMM);
845
}
846
847
for (uint32_t i = 0; i < aManager->mPendingScripts.Length(); ++i) {
848
aChildMM->LoadScript(aManager->mPendingScripts[i], false,
849
aManager->mPendingScriptsGlobalStates[i],
850
IgnoreErrors());
851
}
852
}
853
854
void nsFrameMessageManager::LoadPendingScripts() {
855
RefPtr<nsFrameMessageManager> kungfuDeathGrip = this;
856
LoadPendingScripts(this, this);
857
}
858
859
void nsFrameMessageManager::SetCallback(MessageManagerCallback* aCallback) {
860
MOZ_ASSERT(!mIsBroadcaster || !mCallback,
861
"Broadcasters cannot have callbacks!");
862
if (aCallback && mCallback != aCallback) {
863
mCallback = aCallback;
864
if (mOwnsCallback) {
865
mOwnedCallback = aCallback;
866
}
867
}
868
}
869
870
void nsFrameMessageManager::Close() {
871
if (!mClosed) {
872
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
873
if (obs) {
874
obs->NotifyObservers(this, "message-manager-close", nullptr);
875
}
876
}
877
mClosed = true;
878
mCallback = nullptr;
879
mOwnedCallback = nullptr;
880
}
881
882
void nsFrameMessageManager::Disconnect(bool aRemoveFromParent) {
883
// Notify message-manager-close if we haven't already.
884
Close();
885
886
if (!mDisconnected) {
887
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
888
if (obs) {
889
obs->NotifyObservers(this, "message-manager-disconnect", nullptr);
890
}
891
}
892
893
ClearParentManager(aRemoveFromParent);
894
895
mDisconnected = true;
896
if (!mHandlingMessage) {
897
mListeners.Clear();
898
}
899
}
900
901
void nsFrameMessageManager::SetInitialProcessData(
902
JS::HandleValue aInitialData) {
903
MOZ_ASSERT(!mChrome);
904
MOZ_ASSERT(mIsProcessManager);
905
MOZ_ASSERT(aInitialData.isObject());
906
mInitialProcessData = aInitialData;
907
}
908
909
void nsFrameMessageManager::GetInitialProcessData(
910
JSContext* aCx, JS::MutableHandle<JS::Value> aInitialProcessData,
911
ErrorResult& aError) {
912
MOZ_ASSERT(mIsProcessManager);
913
MOZ_ASSERT_IF(mChrome, IsBroadcaster());
914
915
JS::RootedValue init(aCx, mInitialProcessData);
916
if (mChrome && init.isUndefined()) {
917
// We create the initial object in the junk scope. If we created it in a
918
// normal realm, that realm would leak until shutdown.
919
JS::RootedObject global(aCx, xpc::PrivilegedJunkScope());
920
JSAutoRealm ar(aCx, global);
921
922
JS::RootedObject obj(aCx, JS_NewPlainObject(aCx));
923
if (!obj) {
924
aError.NoteJSContextException(aCx);
925
return;
926
}
927
928
mInitialProcessData.setObject(*obj);
929
init.setObject(*obj);
930
}
931
932
if (!mChrome && XRE_IsParentProcess()) {
933
// This is the cpmm in the parent process. We should use the same object as
934
// the ppmm. Create it first through do_GetService and use the cached
935
// pointer in sParentProcessManager.
936
nsCOMPtr<nsISupports> ppmm =
937
do_GetService("@mozilla.org/parentprocessmessagemanager;1");
938
sParentProcessManager->GetInitialProcessData(aCx, &init, aError);
939
if (aError.Failed()) {
940
return;
941
}
942
mInitialProcessData = init;
943
}
944
945
if (!JS_WrapValue(aCx, &init)) {
946
aError.NoteJSContextException(aCx);
947
return;
948
}
949
aInitialProcessData.set(init);
950
}
951
952
WritableSharedMap* nsFrameMessageManager::SharedData() {
953
if (!mChrome || !mIsProcessManager) {
954
MOZ_ASSERT(false, "Should only call this binding method on ppmm");
955
return nullptr;
956
}
957
if (!mSharedData) {
958
mSharedData = new WritableSharedMap();
959
}
960
return mSharedData;
961
}
962
963
already_AddRefed<ProcessMessageManager>
964
nsFrameMessageManager::GetProcessMessageManager(ErrorResult& aError) {
965
RefPtr<ProcessMessageManager> pmm;
966
if (mCallback) {
967
pmm = mCallback->GetProcessMessageManager();
968
}
969
return pmm.forget();
970
}
971
972
void nsFrameMessageManager::GetRemoteType(nsAString& aRemoteType,
973
ErrorResult& aError) const {
974
aRemoteType.Truncate();
975
if (mCallback) {
976
mCallback->DoGetRemoteType(aRemoteType, aError);
977
}
978
}
979
980
namespace {
981
982
struct MessageManagerReferentCount {
983
MessageManagerReferentCount() : mStrong(0), mWeakAlive(0), mWeakDead(0) {}
984
size_t mStrong;
985
size_t mWeakAlive;
986
size_t mWeakDead;
987
nsTArray<nsString> mSuspectMessages;
988
nsDataHashtable<nsStringHashKey, uint32_t> mMessageCounter;
989
};
990
991
} // namespace
992
993
namespace mozilla::dom {
994
995
class MessageManagerReporter final : public nsIMemoryReporter {
996
~MessageManagerReporter() = default;
997
998
public:
999
NS_DECL_ISUPPORTS
1000
NS_DECL_NSIMEMORYREPORTER
1001
1002
static const size_t kSuspectReferentCount = 300;
1003
1004
protected:
1005
void CountReferents(nsFrameMessageManager* aMessageManager,
1006
MessageManagerReferentCount* aReferentCount);
1007
};
1008
1009
NS_IMPL_ISUPPORTS(MessageManagerReporter, nsIMemoryReporter)
1010
1011
void MessageManagerReporter::CountReferents(
1012
nsFrameMessageManager* aMessageManager,
1013
MessageManagerReferentCount* aReferentCount) {
1014
for (auto it = aMessageManager->mListeners.Iter(); !it.Done(); it.Next()) {
1015
nsAutoTObserverArray<nsMessageListenerInfo, 1>* listeners = it.UserData();
1016
uint32_t listenerCount = listeners->Length();
1017
if (listenerCount == 0) {
1018
continue;
1019
}
1020
1021
nsString key(it.Key());
1022
uint32_t oldCount = 0;
1023
aReferentCount->mMessageCounter.Get(key, &oldCount);
1024
uint32_t currentCount = oldCount + listenerCount;
1025
aReferentCount->mMessageCounter.Put(key, currentCount);
1026
1027
// Keep track of messages that have a suspiciously large
1028
// number of referents (symptom of leak).
1029
if (currentCount >= MessageManagerReporter::kSuspectReferentCount) {
1030
aReferentCount->mSuspectMessages.AppendElement(key);
1031
}
1032
1033
for (uint32_t i = 0; i < listenerCount; ++i) {
1034
const nsMessageListenerInfo& listenerInfo = listeners->ElementAt(i);
1035
if (listenerInfo.mWeakListener) {
1036
nsCOMPtr<nsISupports> referent =
1037
do_QueryReferent(listenerInfo.mWeakListener);
1038
if (referent) {
1039
aReferentCount->mWeakAlive++;
1040
} else {
1041
aReferentCount->mWeakDead++;
1042
}
1043
} else {
1044
aReferentCount->mStrong++;
1045
}
1046
}
1047
}
1048
1049
// Add referent count in child managers because the listeners
1050
// participate in messages dispatched from parent message manager.
1051
for (uint32_t i = 0; i < aMessageManager->mChildManagers.Length(); ++i) {
1052
RefPtr<nsFrameMessageManager> mm = aMessageManager->mChildManagers[i];
1053
CountReferents(mm, aReferentCount);
1054
}
1055
}
1056
1057
static void ReportReferentCount(
1058
const char* aManagerType, const MessageManagerReferentCount& aReferentCount,
1059
nsIHandleReportCallback* aHandleReport, nsISupports* aData) {
1060
#define REPORT(_path, _amount, _desc) \
1061
do { \
1062
aHandleReport->Callback( \
1063
EmptyCString(), _path, nsIMemoryReporter::KIND_OTHER, \
1064
nsIMemoryReporter::UNITS_COUNT, _amount, _desc, aData); \
1065
} while (0)
1066
1067
REPORT(nsPrintfCString("message-manager/referent/%s/strong", aManagerType),
1068
aReferentCount.mStrong,
1069
nsPrintfCString("The number of strong referents held by the message "
1070
"manager in the %s manager.",
1071
aManagerType));
1072
REPORT(
1073
nsPrintfCString("message-manager/referent/%s/weak/alive", aManagerType),
1074
aReferentCount.mWeakAlive,
1075
nsPrintfCString("The number of weak referents that are still alive "
1076
"held by the message manager in the %s manager.",
1077
aManagerType));
1078
REPORT(nsPrintfCString("message-manager/referent/%s/weak/dead", aManagerType),
1079
aReferentCount.mWeakDead,
1080
nsPrintfCString("The number of weak referents that are dead "
1081
"held by the message manager in the %s manager.",
1082
aManagerType));
1083
1084
for (uint32_t i = 0; i < aReferentCount.mSuspectMessages.Length(); i++) {
1085
uint32_t totalReferentCount = 0;
1086
aReferentCount.mMessageCounter.Get(aReferentCount.mSuspectMessages[i],
1087
&totalReferentCount);
1088
NS_ConvertUTF16toUTF8 suspect(aReferentCount.mSuspectMessages[i]);
1089
REPORT(nsPrintfCString("message-manager-suspect/%s/referent(message=%s)",
1090
aManagerType, suspect.get()),
1091
totalReferentCount,
1092
nsPrintfCString("A message in the %s message manager with a "
1093
"suspiciously large number of referents (symptom "
1094
"of a leak).",
1095
aManagerType));
1096
}
1097
1098
#undef REPORT
1099
}
1100
1101
static StaticRefPtr<ChromeMessageBroadcaster> sGlobalMessageManager;
1102
1103
NS_IMETHODIMP
1104
MessageManagerReporter::CollectReports(nsIHandleReportCallback* aHandleReport,
1105
nsISupports* aData, bool aAnonymize) {
1106
if (XRE_IsParentProcess() && sGlobalMessageManager) {
1107
MessageManagerReferentCount count;
1108
CountReferents(sGlobalMessageManager, &count);
1109
ReportReferentCount("global-manager", count, aHandleReport, aData);
1110
}
1111
1112
if (nsFrameMessageManager::sParentProcessManager) {
1113
MessageManagerReferentCount count;
1114
CountReferents(nsFrameMessageManager::sParentProcessManager, &count);
1115
ReportReferentCount("parent-process-manager", count, aHandleReport, aData);
1116
}
1117
1118
if (nsFrameMessageManager::sChildProcessManager) {
1119
MessageManagerReferentCount count;
1120
CountReferents(nsFrameMessageManager::sChildProcessManager, &count);
1121
ReportReferentCount("child-process-manager", count, aHandleReport, aData);
1122
}
1123
1124
return NS_OK;
1125
}
1126
1127
} // namespace mozilla::dom
1128
1129
already_AddRefed<ChromeMessageBroadcaster>
1130
nsFrameMessageManager::GetGlobalMessageManager() {
1131
RefPtr<ChromeMessageBroadcaster> mm;
1132
if (sGlobalMessageManager) {
1133
mm = sGlobalMessageManager;
1134
} else {
1135
sGlobalMessageManager = mm =
1136
new ChromeMessageBroadcaster(MessageManagerFlags::MM_GLOBAL);
1137
ClearOnShutdown(&sGlobalMessageManager);
1138
RegisterStrongMemoryReporter(new MessageManagerReporter());
1139
}
1140
return mm.forget();
1141
}
1142
1143
nsresult NS_NewGlobalMessageManager(nsISupports** aResult) {
1144
*aResult = nsFrameMessageManager::GetGlobalMessageManager().take();
1145
return NS_OK;
1146
}
1147
1148
nsDataHashtable<nsStringHashKey, nsMessageManagerScriptHolder*>*
1149
nsMessageManagerScriptExecutor::sCachedScripts = nullptr;
1150
StaticRefPtr<nsScriptCacheCleaner>
1151
nsMessageManagerScriptExecutor::sScriptCacheCleaner;
1152
1153
void nsMessageManagerScriptExecutor::DidCreateScriptLoader() {
1154
if (!sCachedScripts) {
1155
sCachedScripts =
1156
new nsDataHashtable<nsStringHashKey, nsMessageManagerScriptHolder*>;
1157
sScriptCacheCleaner = new nsScriptCacheCleaner();
1158
}
1159
}
1160
1161
// static
1162
void nsMessageManagerScriptExecutor::PurgeCache() {
1163
if (sCachedScripts) {
1164
NS_ASSERTION(sCachedScripts != nullptr, "Need cached scripts");
1165
for (auto iter = sCachedScripts->Iter(); !iter.Done(); iter.Next()) {
1166
delete iter.Data();
1167
iter.Remove();
1168
}
1169
}
1170
}
1171
1172
// static
1173
void nsMessageManagerScriptExecutor::Shutdown() {
1174
if (sCachedScripts) {
1175
PurgeCache();
1176
1177
delete sCachedScripts;
1178
sCachedScripts = nullptr;
1179
sScriptCacheCleaner = nullptr;
1180
}
1181
}
1182
1183
void nsMessageManagerScriptExecutor::LoadScriptInternal(
1184
JS::Handle<JSObject*> aMessageManager, const nsAString& aURL,
1185
bool aRunInUniqueScope) {
1186
AUTO_PROFILER_LABEL_DYNAMIC_LOSSY_NSSTRING(
1187
"nsMessageManagerScriptExecutor::LoadScriptInternal", OTHER, aURL);
1188
1189
if (!sCachedScripts) {
1190
return;
1191
}
1192
1193
JS::RootingContext* rcx = RootingCx();
1194
JS::Rooted<JSScript*> script(rcx);
1195
1196
nsMessageManagerScriptHolder* holder = sCachedScripts->Get(aURL);
1197
if (holder) {
1198
script = holder->mScript;
1199
} else {
1200
TryCacheLoadAndCompileScript(aURL, aRunInUniqueScope, true, aMessageManager,
1201
&script);
1202
}
1203
1204
AutoEntryScript aes(aMessageManager, "message manager script load");
1205
JSContext* cx = aes.cx();
1206
if (script) {
1207
if (aRunInUniqueScope) {
1208
JS::Rooted<JSObject*> scope(cx);
1209
bool ok = js::ExecuteInFrameScriptEnvironment(cx, aMessageManager, script,
1210
&scope);
1211
if (ok) {
1212
// Force the scope to stay alive.
1213
mAnonymousGlobalScopes.AppendElement(scope);
1214
}
1215
} else {
1216
JS::RootedValue rval(cx);
1217
JS::RootedVector<JSObject*> envChain(cx);
1218
if (!envChain.append(aMessageManager)) {
1219
return;
1220
}
1221
JS::CloneAndExecuteScript(cx, envChain, script, &rval);
1222
}
1223
}
1224
}
1225
1226
void nsMessageManagerScriptExecutor::TryCacheLoadAndCompileScript(
1227
const nsAString& aURL, bool aRunInUniqueScope, bool aShouldCache,
1228
JS::Handle<JSObject*> aMessageManager,
1229
JS::MutableHandle<JSScript*> aScriptp) {
1230
nsCString url = NS_ConvertUTF16toUTF8(aURL);
1231
nsCOMPtr<nsIURI> uri;
1232
nsresult rv = NS_NewURI(getter_AddRefs(uri), url);
1233
if (NS_FAILED(rv)) {
1234
return;
1235
}
1236
1237
bool hasFlags;
1238
rv = NS_URIChainHasFlags(uri, nsIProtocolHandler::URI_IS_LOCAL_RESOURCE,
1239
&hasFlags);
1240
if (NS_FAILED(rv) || !hasFlags) {
1241
NS_WARNING("Will not load a frame script!");
1242
return;
1243
}
1244
1245
// If this script won't be cached, or there is only one of this type of
1246
// message manager per process, treat this script as run-once. Run-once
1247
// scripts can be compiled directly for the target global, and will be dropped
1248
// from the preloader cache after they're executed and serialized.
1249
bool isRunOnce = !aShouldCache || IsProcessScoped();
1250
1251
// If the script will be reused in this session, compile it in the compilation
1252
// scope instead of the current global to avoid keeping the current
1253
// compartment alive.
1254
AutoJSAPI jsapi;
1255
if (!jsapi.Init(isRunOnce ? aMessageManager : xpc::CompilationScope())) {
1256
return;
1257
}
1258
JSContext* cx = jsapi.cx();
1259
JS::Rooted<JSScript*> script(cx);
1260
1261
script = ScriptPreloader::GetChildSingleton().GetCachedScript(cx, url);
1262
1263
if (!script) {
1264
nsCOMPtr<nsIChannel> channel;
1265
NS_NewChannel(getter_AddRefs(channel), uri,
1266
nsContentUtils::GetSystemPrincipal(),
1267
nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
1268
nsIContentPolicy::TYPE_OTHER);
1269
1270
if (!channel) {
1271
return;
1272
}
1273
1274
nsCOMPtr<nsIInputStream> input;
1275
rv = channel->Open(getter_AddRefs(input));
1276
NS_ENSURE_SUCCESS_VOID(rv);
1277
nsString dataString;
1278
char16_t* dataStringBuf = nullptr;
1279
size_t dataStringLength = 0;
1280
if (input) {
1281
nsCString buffer;
1282
uint64_t written;
1283
if (NS_FAILED(NS_ReadInputStreamToString(input, buffer, -1, &written))) {
1284
return;
1285
}
1286
1287
uint32_t size = (uint32_t)std::min(written, (uint64_t)UINT32_MAX);
1288
ScriptLoader::ConvertToUTF16(channel, (uint8_t*)buffer.get(), size,
1289
EmptyString(), nullptr, dataStringBuf,
1290
dataStringLength);
1291
}
1292
1293
if (!dataStringBuf || dataStringLength == 0) {
1294
return;
1295
}
1296
1297
JS::UniqueTwoByteChars srcChars(dataStringBuf);
1298
1299
JS::SourceText<char16_t> srcBuf;
1300
if (!srcBuf.init(cx, std::move(srcChars), dataStringLength)) {
1301
return;
1302
}
1303
1304
JS::CompileOptions options(cx);
1305
options.setFileAndLine(url.get(), 1);
1306
options.setNoScriptRval(true);
1307
1308
script = JS::CompileForNonSyntacticScope(cx, options, srcBuf);
1309
if (!script) {
1310
return;
1311
}
1312
}
1313
1314
MOZ_ASSERT(script);
1315
aScriptp.set(script);
1316
1317
nsAutoCString scheme;
1318
uri->GetScheme(scheme);
1319
// We don't cache data: scripts!
1320
if (aShouldCache && !scheme.EqualsLiteral("data")) {
1321
ScriptPreloader::GetChildSingleton().NoteScript(url, url, script,
1322
isRunOnce);
1323
1324
// If this script will only run once per process, only cache it in the
1325
// preloader cache, not the session cache.
1326
if (!isRunOnce) {
1327
// Root the object also for caching.
1328
auto* holder = new nsMessageManagerScriptHolder(cx, script);
1329
sCachedScripts->Put(aURL, holder);
1330
}
1331
}
1332
}
1333
1334
void nsMessageManagerScriptExecutor::Trace(const TraceCallbacks& aCallbacks,
1335
void* aClosure) {
1336
for (size_t i = 0, length = mAnonymousGlobalScopes.Length(); i < length;
1337
++i) {
1338
aCallbacks.Trace(&mAnonymousGlobalScopes[i], "mAnonymousGlobalScopes[i]",
1339
aClosure);
1340
}
1341
}
1342
1343
void nsMessageManagerScriptExecutor::Unlink() {
1344
ImplCycleCollectionUnlink(mAnonymousGlobalScopes);
1345
}
1346
1347
bool nsMessageManagerScriptExecutor::Init() {
1348
DidCreateScriptLoader();
1349
return true;
1350
}
1351
1352
void nsMessageManagerScriptExecutor::MarkScopesForCC() {
1353
for (uint32_t i = 0; i < mAnonymousGlobalScopes.Length(); ++i) {
1354
mAnonymousGlobalScopes[i].exposeToActiveJS();
1355
}
1356
}
1357
1358
NS_IMPL_ISUPPORTS(nsScriptCacheCleaner, nsIObserver)
1359
1360
ChildProcessMessageManager* nsFrameMessageManager::sChildProcessManager =
1361
nullptr;
1362
ParentProcessMessageManager* nsFrameMessageManager::sParentProcessManager =
1363
nullptr;
1364
nsFrameMessageManager* nsFrameMessageManager::sSameProcessParentManager =
1365
nullptr;
1366
1367
class nsAsyncMessageToSameProcessChild : public nsSameProcessAsyncMessageBase,
1368
public Runnable {
1369
public:
1370
nsAsyncMessageToSameProcessChild(JS::RootingContext* aRootingCx,
1371
JS::Handle<JSObject*> aCpows)
1372
: nsSameProcessAsyncMessageBase(aRootingCx, aCpows),
1373
mozilla::Runnable("nsAsyncMessageToSameProcessChild") {}
1374
NS_IMETHOD Run() override {
1375
nsFrameMessageManager* ppm =
1376
nsFrameMessageManager::GetChildProcessManager();
1377
ReceiveMessage(ppm, nullptr, ppm);
1378
return NS_OK;
1379
}
1380
};
1381
1382
/**
1383
* Send messages to an imaginary child process in a single-process scenario.
1384
*/
1385
class SameParentProcessMessageManagerCallback : public MessageManagerCallback {
1386
public:
1387
SameParentProcessMessageManagerCallback() {
1388
MOZ_COUNT_CTOR(SameParentProcessMessageManagerCallback);
1389
}
1390
~SameParentProcessMessageManagerCallback() override {
1391
MOZ_COUNT_DTOR(SameParentProcessMessageManagerCallback);
1392
}
1393
1394
bool DoLoadMessageManagerScript(const nsAString& aURL,
1395
bool aRunInGlobalScope) override {
1396
auto* global = ContentProcessMessageManager::Get();
1397
MOZ_ASSERT(!aRunInGlobalScope);
1398
global->LoadScript(aURL);
1399
return true;
1400
}
1401
1402
nsresult DoSendAsyncMessage(JSContext* aCx, const nsAString& aMessage,
1403
StructuredCloneData& aData,
1404
JS::Handle<JSObject*> aCpows,
1405
nsIPrincipal* aPrincipal) override {
1406
JS::RootingContext* rcx = JS::RootingContext::get(aCx);
1407
RefPtr<nsAsyncMessageToSameProcessChild> ev =
1408
new nsAsyncMessageToSameProcessChild(rcx, aCpows);
1409
1410
nsresult rv = ev->Init(aMessage, aData, aPrincipal);
1411
if (NS_FAILED(rv)) {
1412
return rv;
1413
}
1414
rv = NS_DispatchToCurrentThread(ev);
1415
if (NS_FAILED(rv)) {
1416
return rv;
1417
}
1418
return NS_OK;
1419
}
1420
};
1421
1422
/**
1423
* Send messages to the parent process.
1424
*/
1425
class ChildProcessMessageManagerCallback : public MessageManagerCallback {
1426
public:
1427
ChildProcessMessageManagerCallback() {
1428
MOZ_COUNT_CTOR(ChildProcessMessageManagerCallback);
1429
}
1430
~ChildProcessMessageManagerCallback() override {
1431
MOZ_COUNT_DTOR(ChildProcessMessageManagerCallback);
1432
}
1433
1434
bool DoSendBlockingMessage(JSContext* aCx, const nsAString& aMessage,
1435
StructuredCloneData& aData,
1436
JS::Handle<JSObject*> aCpows,
1437
nsIPrincipal* aPrincipal,
1438
nsTArray<StructuredCloneData>* aRetVal,
1439
bool aIsSync) override {
1440
mozilla::dom::ContentChild* cc = mozilla::dom::ContentChild::GetSingleton();
1441
if (!cc) {
1442
return true;
1443
}
1444
ClonedMessageData data;
1445
if (!BuildClonedMessageDataForChild(cc, aData, data)) {
1446
return false;
1447
}
1448
nsTArray<mozilla::jsipc::CpowEntry> cpows;
1449
if (aCpows && !cc->GetCPOWManager()->Wrap(aCx, aCpows, &cpows)) {
1450
return false;
1451
}
1452
if (aIsSync) {
1453
return cc->SendSyncMessage(PromiseFlatString(aMessage), data, cpows,
1454
IPC::Principal(aPrincipal), aRetVal);
1455
}
1456
return cc->SendRpcMessage(PromiseFlatString(aMessage), data, cpows,
1457
IPC::Principal(aPrincipal), aRetVal);
1458
}
1459
1460
nsresult DoSendAsyncMessage(JSContext* aCx, const nsAString& aMessage,
1461
StructuredCloneData& aData,
1462
JS::Handle<JSObject*> aCpows,
1463
nsIPrincipal* aPrincipal) override {
1464
mozilla::dom::ContentChild* cc = mozilla::dom::ContentChild::GetSingleton();
1465
if (!cc) {
1466
return NS_OK;
1467
}
1468
ClonedMessageData data;
1469
if (!BuildClonedMessageDataForChild(cc, aData, data)) {
1470
return NS_ERROR_DOM_DATA_CLONE_ERR;
1471
}
1472
nsTArray<mozilla::jsipc::CpowEntry> cpows;
1473
if (aCpows && !cc->GetCPOWManager()->Wrap(aCx, aCpows, &cpows)) {
1474
return NS_ERROR_UNEXPECTED;
1475
}
1476
if (!cc->SendAsyncMessage(PromiseFlatString(aMessage), cpows,
1477
IPC::Principal(aPrincipal), data)) {
1478
return NS_ERROR_UNEXPECTED;
1479
}
1480
1481
return NS_OK;
1482
}
1483
};
1484
1485
class nsAsyncMessageToSameProcessParent
1486
: public nsSameProcessAsyncMessageBase,
1487
public SameProcessMessageQueue::Runnable {
1488
public:
1489
nsAsyncMessageToSameProcessParent(JS::RootingContext* aRootingCx,
1490
JS::Handle<JSObject*> aCpows)
1491
: nsSameProcessAsyncMessageBase(aRootingCx, aCpows) {}
1492
nsresult HandleMessage() override {
1493
nsFrameMessageManager* ppm =
1494
nsFrameMessageManager::sSameProcessParentManager;
1495
ReceiveMessage(ppm, nullptr, ppm);
1496
return NS_OK;
1497
}
1498
};
1499
1500
/**
1501
* Send messages to the imaginary parent process in a single-process scenario.
1502
*/
1503
class SameChildProcessMessageManagerCallback : public MessageManagerCallback {
1504
public:
1505
SameChildProcessMessageManagerCallback() {
1506
MOZ_COUNT_CTOR(SameChildProcessMessageManagerCallback);
1507
}
1508
~SameChildProcessMessageManagerCallback() override {
1509
MOZ_COUNT_DTOR(SameChildProcessMessageManagerCallback);
1510
}
1511
1512
bool DoSendBlockingMessage(JSContext* aCx, const nsAString& aMessage,
1513
StructuredCloneData& aData,
1514
JS::Handle<JSObject*> aCpows,
1515
nsIPrincipal* aPrincipal,
1516
nsTArray<StructuredCloneData>* aRetVal,
1517
bool aIsSync) override {
1518
SameProcessMessageQueue* queue = SameProcessMessageQueue::Get();
1519
queue->Flush();
1520
1521
if (nsFrameMessageManager::sSameProcessParentManager) {
1522
SameProcessCpowHolder cpows(JS::RootingContext::get(aCx), aCpows);
1523
RefPtr<nsFrameMessageManager> ppm =
1524
nsFrameMessageManager::sSameProcessParentManager;
1525
ppm->ReceiveMessage(ppm, nullptr, aMessage, true, &aData, &cpows,
1526
aPrincipal, aRetVal, IgnoreErrors());
1527
}
1528
return true;
1529
}
1530
1531
nsresult DoSendAsyncMessage(JSContext* aCx, const nsAString& aMessage,
1532
StructuredCloneData& aData,
1533
JS::Handle<JSObject*> aCpows,
1534
nsIPrincipal* aPrincipal) override {
1535
SameProcessMessageQueue* queue = SameProcessMessageQueue::Get();
1536
JS::RootingContext* rcx = JS::RootingContext::get(aCx);
1537
RefPtr<nsAsyncMessageToSameProcessParent> ev =
1538
new nsAsyncMessageToSameProcessParent(rcx, aCpows);
1539
nsresult rv = ev->Init(aMessage, aData, aPrincipal);
1540
1541
if (NS_FAILED(rv)) {
1542
return rv;
1543
}
1544
queue->Push(ev);
1545
return NS_OK;
1546
}
1547
};
1548
1549
// This creates the global parent process message manager.
1550
nsresult NS_NewParentProcessMessageManager(nsISupports** aResult) {
1551
NS_ASSERTION(!nsFrameMessageManager::sParentProcessManager,
1552
"Re-creating sParentProcessManager");
1553
RefPtr<ParentProcessMessageManager> mm = new ParentProcessMessageManager();
1554
nsFrameMessageManager::sParentProcessManager = mm;
1555
nsFrameMessageManager::NewProcessMessageManager(
1556
false); // Create same process message manager.
1557
mm.forget(aResult);
1558
return NS_OK;
1559
}
1560
1561
ProcessMessageManager* nsFrameMessageManager::NewProcessMessageManager(
1562
bool aIsRemote) {
1563
if (!nsFrameMessageManager::sParentProcessManager) {
1564
nsCOMPtr<nsISupports> dummy =
1565
do_GetService("@mozilla.org/parentprocessmessagemanager;1");
1566
}
1567
1568
MOZ_ASSERT(nsFrameMessageManager::sParentProcessManager,
1569
"parent process manager not created");
1570
ProcessMessageManager* mm;
1571
if (aIsRemote) {
1572
// Callback is set in ContentParent::InitInternal so that the process has
1573
// already started when we send pending scripts.
1574
mm = new ProcessMessageManager(
1575
nullptr, nsFrameMessageManager::sParentProcessManager);
1576
} else {
1577
mm =
1578
new ProcessMessageManager(new SameParentProcessMessageManagerCallback(),
1579
nsFrameMessageManager::sParentProcessManager,
1580
MessageManagerFlags::MM_OWNSCALLBACK);
1581
mm->SetOsPid(base::GetCurrentProcId());
1582
sSameProcessParentManager = mm;
1583
}
1584
return mm;
1585
}
1586
1587
nsresult NS_NewChildProcessMessageManager(nsISupports** aResult) {
1588
NS_ASSERTION(!nsFrameMessageManager::GetChildProcessManager(),
1589
"Re-creating sChildProcessManager");
1590
1591
MessageManagerCallback* cb;
1592
if (XRE_IsParentProcess()) {
1593
cb = new SameChildProcessMessageManagerCallback();
1594
} else {
1595
cb = new ChildProcessMessageManagerCallback();
1596
RegisterStrongMemoryReporter(new MessageManagerReporter());
1597
}
1598
auto* mm = new ChildProcessMessageManager(cb);
1599
nsFrameMessageManager::SetChildProcessManager(mm);
1600
auto global = MakeRefPtr<ContentProcessMessageManager>(mm);
1601
NS_ENSURE_TRUE(global->Init(), NS_ERROR_UNEXPECTED);
1602
return CallQueryInterface(global, aResult);
1603
}
1604
1605
void nsFrameMessageManager::MarkForCC() {
1606
for (auto iter = mListeners.Iter(); !iter.Done(); iter.Next()) {
1607
nsAutoTObserverArray<nsMessageListenerInfo, 1>* listeners = iter.UserData();
1608
uint32_t count = listeners->Length();
1609
for (uint32_t i = 0; i < count; i++) {
1610
MessageListener* strongListener = listeners->ElementAt(i).mStrongListener;
1611
if (strongListener) {
1612
strongListener->MarkForCC();
1613
}
1614
}
1615
}
1616
1617
if (mRefCnt.IsPurple()) {
1618
mRefCnt.RemovePurple();
1619
}
1620
}
1621
1622
nsSameProcessAsyncMessageBase::nsSameProcessAsyncMessageBase(
1623
JS::RootingContext* aRootingCx, JS::Handle<JSObject*> aCpows)
1624
: mCpows(aRootingCx, aCpows)
1625
#ifdef DEBUG
1626
,
1627
mCalledInit(false)
1628
#endif
1629
{
1630
}
1631
1632
nsresult nsSameProcessAsyncMessageBase::Init(const nsAString& aMessage,
1633
StructuredCloneData& aData,
1634
nsIPrincipal* aPrincipal) {
1635
if (!mData.Copy(aData)) {
1636
Telemetry::Accumulate(Telemetry::IPC_SAME_PROCESS_MESSAGE_COPY_OOM_KB,
1637
aData.DataLength());
1638
return NS_ERROR_OUT_OF_MEMORY;
1639
}
1640
1641
mMessage = aMessage;
1642
mPrincipal = aPrincipal;
1643
#ifdef DEBUG
1644
mCalledInit = true;
1645
#endif
1646
1647
return NS_OK;
1648
}
1649
1650
void nsSameProcessAsyncMessageBase::ReceiveMessage(
1651
nsISupports* aTarget, nsFrameLoader* aTargetFrameLoader,
1652
nsFrameMessageManager* aManager) {
1653
// Make sure that we have called Init() and it has succeeded.
1654
MOZ_ASSERT(mCalledInit);
1655
if (aManager) {
1656
SameProcessCpowHolder cpows(RootingCx(), mCpows);
1657
1658
RefPtr<nsFrameMessageManager> mm = aManager;
1659
mm->ReceiveMessage(aTarget, aTargetFrameLoader, mMessage, false, &mData,
1660
&cpows, mPrincipal, nullptr, IgnoreErrors());
1661
}
1662
}