Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/base/nsFrameMessageManager.h
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
3
/* This Source Code Form is subject to the terms of the Mozilla Public
4
 * License, v. 2.0. If a copy of the MPL was not distributed with this
5
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
#ifndef nsFrameMessageManager_h__
8
#define nsFrameMessageManager_h__
9
10
#include "nsIMessageManager.h"
11
#include "nsIObserver.h"
12
#include "nsCOMPtr.h"
13
#include "nsAutoPtr.h"
14
#include "nsCOMArray.h"
15
#include "nsTArray.h"
16
#include "nsAtom.h"
17
#include "nsCycleCollectionParticipant.h"
18
#include "nsTArray.h"
19
#include "nsIPrincipal.h"
20
#include "nsIXPConnect.h"
21
#include "nsDataHashtable.h"
22
#include "nsClassHashtable.h"
23
#include "mozilla/Services.h"
24
#include "mozilla/StaticPtr.h"
25
#include "nsIObserverService.h"
26
#include "nsThreadUtils.h"
27
#include "nsWeakPtr.h"
28
#include "mozilla/Attributes.h"
29
#include "js/RootingAPI.h"
30
#include "nsTObserverArray.h"
31
#include "mozilla/TypedEnumBits.h"
32
#include "mozilla/dom/CallbackObject.h"
33
#include "mozilla/dom/SameProcessMessageQueue.h"
34
#include "mozilla/dom/ipc/StructuredCloneData.h"
35
#include "mozilla/jsipc/CpowHolder.h"
36
37
class nsFrameLoader;
38
39
namespace mozilla {
40
41
namespace ipc {
42
  class FileDescriptor;
43
}
44
45
namespace dom {
46
47
class nsIContentParent;
48
class nsIContentChild;
49
class ChildProcessMessageManager;
50
class ChromeMessageBroadcaster;
51
class ClonedMessageData;
52
class MessageBroadcaster;
53
class MessageListener;
54
class MessageListenerManager;
55
class MessageManagerReporter;
56
template<typename T> class Optional;
57
class ParentProcessMessageManager;
58
class ProcessMessageManager;
59
60
namespace ipc {
61
62
class WritableSharedMap;
63
64
// Note: we round the time we spend to the nearest millisecond. So a min value
65
// of 1 ms actually captures from 500us and above.
66
static const uint32_t kMinTelemetrySyncMessageManagerLatencyMs = 1;
67
68
enum class MessageManagerFlags {
69
  MM_NONE = 0,
70
  MM_CHROME = 1,
71
  MM_GLOBAL = 2,
72
  MM_PROCESSMANAGER = 4,
73
  MM_BROADCASTER = 8,
74
  MM_OWNSCALLBACK = 16
75
};
76
MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(MessageManagerFlags);
77
78
class MessageManagerCallback
79
{
80
public:
81
0
  virtual ~MessageManagerCallback() {}
82
83
  virtual bool DoLoadMessageManagerScript(const nsAString& aURL, bool aRunInGlobalScope)
84
0
  {
85
0
    return true;
86
0
  }
87
88
  virtual bool DoSendBlockingMessage(JSContext* aCx,
89
                                     const nsAString& aMessage,
90
                                     StructuredCloneData& aData,
91
                                     JS::Handle<JSObject*> aCpows,
92
                                     nsIPrincipal* aPrincipal,
93
                                     nsTArray<StructuredCloneData>* aRetVal,
94
                                     bool aIsSync)
95
0
  {
96
0
    return true;
97
0
  }
98
99
  virtual nsresult DoSendAsyncMessage(JSContext* aCx,
100
                                      const nsAString& aMessage,
101
                                      StructuredCloneData& aData,
102
                                      JS::Handle<JSObject*> aCpows,
103
                                      nsIPrincipal* aPrincipal)
104
0
  {
105
0
    return NS_OK;
106
0
  }
107
108
  virtual mozilla::dom::ProcessMessageManager* GetProcessMessageManager() const
109
0
  {
110
0
    return nullptr;
111
0
  }
112
113
  virtual void DoGetRemoteType(nsAString& aRemoteType,
114
                               ErrorResult& aError) const;
115
116
protected:
117
  bool BuildClonedMessageDataForParent(nsIContentParent* aParent,
118
                                       StructuredCloneData& aData,
119
                                       ClonedMessageData& aClonedData);
120
  bool BuildClonedMessageDataForChild(nsIContentChild* aChild,
121
                                      StructuredCloneData& aData,
122
                                      ClonedMessageData& aClonedData);
123
};
124
125
void UnpackClonedMessageDataForParent(const ClonedMessageData& aClonedData,
126
                                      StructuredCloneData& aData);
127
128
void UnpackClonedMessageDataForChild(const ClonedMessageData& aClonedData,
129
                                     StructuredCloneData& aData);
130
131
} // namespace ipc
132
} // namespace dom
133
} // namespace mozilla
134
135
struct nsMessageListenerInfo
136
{
137
  bool operator==(const nsMessageListenerInfo& aOther) const
138
0
  {
139
0
    return &aOther == this;
140
0
  }
141
142
  // If mWeakListener is null then mStrongListener holds a MessageListener.
143
  // If mWeakListener is non-null then mStrongListener contains null.
144
  RefPtr<mozilla::dom::MessageListener> mStrongListener;
145
  nsWeakPtr mWeakListener;
146
  bool mListenWhenClosed;
147
};
148
149
class MOZ_STACK_CLASS SameProcessCpowHolder : public mozilla::jsipc::CpowHolder
150
{
151
public:
152
  SameProcessCpowHolder(JS::RootingContext* aRootingCx, JS::Handle<JSObject*> aObj)
153
    : mObj(aRootingCx, aObj)
154
0
  {
155
0
  }
156
157
  virtual bool ToObject(JSContext* aCx, JS::MutableHandle<JSObject*> aObjp)
158
    override;
159
160
private:
161
  JS::Rooted<JSObject*> mObj;
162
};
163
164
class nsFrameMessageManager : public nsIMessageSender
165
{
166
  friend class mozilla::dom::MessageManagerReporter;
167
  typedef mozilla::dom::ipc::StructuredCloneData StructuredCloneData;
168
169
protected:
170
  typedef mozilla::dom::ipc::MessageManagerFlags MessageManagerFlags;
171
172
  nsFrameMessageManager(mozilla::dom::ipc::MessageManagerCallback* aCallback,
173
                        MessageManagerFlags aFlags);
174
175
  virtual ~nsFrameMessageManager();
176
177
public:
178
  explicit nsFrameMessageManager(mozilla::dom::ipc::MessageManagerCallback* aCallback)
179
    : nsFrameMessageManager(aCallback, MessageManagerFlags::MM_NONE)
180
0
  {}
181
182
  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
183
  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(nsFrameMessageManager)
184
185
  void MarkForCC();
186
187
  // MessageListenerManager
188
  void AddMessageListener(const nsAString& aMessageName,
189
                          mozilla::dom::MessageListener& aListener,
190
                          bool aListenWhenClosed,
191
                          mozilla::ErrorResult& aError);
192
  void RemoveMessageListener(const nsAString& aMessageName,
193
                             mozilla::dom::MessageListener& aListener,
194
                             mozilla::ErrorResult& aError);
195
  void AddWeakMessageListener(const nsAString& aMessageName,
196
                              mozilla::dom::MessageListener& aListener,
197
                              mozilla::ErrorResult& aError);
198
  void RemoveWeakMessageListener(const nsAString& aMessageName,
199
                                 mozilla::dom::MessageListener& aListener,
200
                                 mozilla::ErrorResult& aError);
201
202
  // MessageSender
203
  void SendAsyncMessage(JSContext* aCx, const nsAString& aMessageName,
204
                        JS::Handle<JS::Value> aObj,
205
                        JS::Handle<JSObject*> aObjects,
206
                        nsIPrincipal* aPrincipal,
207
                        JS::Handle<JS::Value> aTransfers,
208
                        mozilla::ErrorResult& aError)
209
0
  {
210
0
    DispatchAsyncMessage(aCx, aMessageName, aObj, aObjects, aPrincipal, aTransfers,
211
0
                         aError);
212
0
  }
213
  already_AddRefed<mozilla::dom::ProcessMessageManager>
214
    GetProcessMessageManager(mozilla::ErrorResult& aError);
215
  void GetRemoteType(nsAString& aRemoteType, mozilla::ErrorResult& aError) const;
216
217
  // SyncMessageSender
218
  void SendSyncMessage(JSContext* aCx, const nsAString& aMessageName,
219
                       JS::Handle<JS::Value> aObj,
220
                       JS::Handle<JSObject*> aObjects,
221
                       nsIPrincipal* aPrincipal,
222
                       nsTArray<JS::Value>& aResult,
223
                       mozilla::ErrorResult& aError)
224
0
  {
225
0
    SendMessage(aCx, aMessageName, aObj, aObjects, aPrincipal, true, aResult, aError);
226
0
  }
227
  void SendRpcMessage(JSContext* aCx, const nsAString& aMessageName,
228
                      JS::Handle<JS::Value> aObj,
229
                      JS::Handle<JSObject*> aObjects,
230
                      nsIPrincipal* aPrincipal,
231
                      nsTArray<JS::Value>& aResult,
232
                      mozilla::ErrorResult& aError)
233
0
  {
234
0
    SendMessage(aCx, aMessageName, aObj, aObjects, aPrincipal, false, aResult, aError);
235
0
  }
236
237
  // GlobalProcessScriptLoader
238
  void GetInitialProcessData(JSContext* aCx,
239
                             JS::MutableHandle<JS::Value> aInitialProcessData,
240
                             mozilla::ErrorResult& aError);
241
242
  mozilla::dom::ipc::WritableSharedMap* SharedData();
243
244
  NS_DECL_NSIMESSAGESENDER
245
246
  static mozilla::dom::ProcessMessageManager* NewProcessMessageManager(bool aIsRemote);
247
248
  void ReceiveMessage(nsISupports* aTarget, nsFrameLoader* aTargetFrameLoader,
249
                      const nsAString& aMessage, bool aIsSync,
250
                      StructuredCloneData* aCloneData, mozilla::jsipc::CpowHolder* aCpows,
251
                      nsIPrincipal* aPrincipal, nsTArray<StructuredCloneData>* aRetVal,
252
                      mozilla::ErrorResult& aError)
253
0
  {
254
0
    ReceiveMessage(aTarget, aTargetFrameLoader, mClosed, aMessage, aIsSync, aCloneData,
255
0
                   aCpows, aPrincipal, aRetVal, aError);
256
0
  }
257
258
  void Disconnect(bool aRemoveFromParent = true);
259
  void Close();
260
261
  void SetCallback(mozilla::dom::ipc::MessageManagerCallback* aCallback);
262
263
  mozilla::dom::ipc::MessageManagerCallback* GetCallback()
264
0
  {
265
0
    return mCallback;
266
0
  }
267
268
  nsresult DispatchAsyncMessageInternal(JSContext* aCx,
269
                                        const nsAString& aMessage,
270
                                        StructuredCloneData& aData,
271
                                        JS::Handle<JSObject*> aCpows,
272
                                        nsIPrincipal* aPrincipal);
273
0
  bool IsGlobal() { return mGlobal; }
274
0
  bool IsBroadcaster() { return mIsBroadcaster; }
275
  bool IsChrome() { return mChrome; }
276
277
  // GetGlobalMessageManager creates the global message manager if it hasn't been yet.
278
  static already_AddRefed<mozilla::dom::ChromeMessageBroadcaster>
279
    GetGlobalMessageManager();
280
  static mozilla::dom::ParentProcessMessageManager* GetParentProcessManager()
281
  {
282
    return sParentProcessManager;
283
  }
284
  static mozilla::dom::ChildProcessMessageManager* GetChildProcessManager()
285
0
  {
286
0
    return sChildProcessManager;
287
0
  }
288
  static void SetChildProcessManager(mozilla::dom::ChildProcessMessageManager* aManager)
289
0
  {
290
0
    sChildProcessManager = aManager;
291
0
  }
292
293
  void SetInitialProcessData(JS::HandleValue aInitialData);
294
295
  void LoadPendingScripts();
296
297
protected:
298
  friend class MMListenerRemover;
299
300
  virtual mozilla::dom::MessageBroadcaster* GetParentManager()
301
0
  {
302
0
    return nullptr;
303
0
  }
304
  virtual void ClearParentManager(bool aRemove)
305
0
  {
306
0
  }
307
308
  void DispatchAsyncMessage(JSContext* aCx, const nsAString& aMessageName,
309
                            JS::Handle<JS::Value> aObj,
310
                            JS::Handle<JSObject*> aObjects,
311
                            nsIPrincipal* aPrincipal,
312
                            JS::Handle<JS::Value> aTransfers,
313
                            mozilla::ErrorResult& aError);
314
315
  void SendMessage(JSContext* aCx, const nsAString& aMessageName,
316
                   JS::Handle<JS::Value> aObj, JS::Handle<JSObject*> aObjects,
317
                   nsIPrincipal* aPrincipal, bool aIsSync, nsTArray<JS::Value>& aResult,
318
                   mozilla::ErrorResult& aError);
319
320
  void ReceiveMessage(nsISupports* aTarget, nsFrameLoader* aTargetFrameLoader,
321
                      bool aTargetClosed, const nsAString& aMessage, bool aIsSync,
322
                      StructuredCloneData* aCloneData, mozilla::jsipc::CpowHolder* aCpows,
323
                      nsIPrincipal* aPrincipal, nsTArray<StructuredCloneData>* aRetVal,
324
                      mozilla::ErrorResult& aError);
325
326
  void LoadScript(const nsAString& aURL, bool aAllowDelayedLoad,
327
                  bool aRunInGlobalScope, mozilla::ErrorResult& aError);
328
  void RemoveDelayedScript(const nsAString& aURL);
329
  void GetDelayedScripts(JSContext* aCx, nsTArray<nsTArray<JS::Value>>& aList,
330
                         mozilla::ErrorResult& aError);
331
332
  // We keep the message listeners as arrays in a hastable indexed by the
333
  // message name. That gives us fast lookups in ReceiveMessage().
334
  nsClassHashtable<nsStringHashKey,
335
                   nsAutoTObserverArray<nsMessageListenerInfo, 1>> mListeners;
336
  nsTArray<RefPtr<mozilla::dom::MessageListenerManager>> mChildManagers;
337
  bool mChrome;     // true if we're in the chrome process
338
  bool mGlobal;     // true if we're the global frame message manager
339
  bool mIsProcessManager; // true if the message manager belongs to the process realm
340
  bool mIsBroadcaster; // true if the message manager is a broadcaster
341
  bool mOwnsCallback;
342
  bool mHandlingMessage;
343
  bool mClosed;    // true if we can no longer send messages
344
  bool mDisconnected;
345
  mozilla::dom::ipc::MessageManagerCallback* mCallback;
346
  nsAutoPtr<mozilla::dom::ipc::MessageManagerCallback> mOwnedCallback;
347
  nsTArray<nsString> mPendingScripts;
348
  nsTArray<bool> mPendingScriptsGlobalStates;
349
  JS::Heap<JS::Value> mInitialProcessData;
350
  RefPtr<mozilla::dom::ipc::WritableSharedMap> mSharedData;
351
352
  void LoadPendingScripts(nsFrameMessageManager* aManager,
353
                          nsFrameMessageManager* aChildMM);
354
public:
355
  static mozilla::dom::ParentProcessMessageManager* sParentProcessManager;
356
  static nsFrameMessageManager* sSameProcessParentManager;
357
  static nsTArray<nsCOMPtr<nsIRunnable> >* sPendingSameProcessAsyncMessages;
358
private:
359
  static mozilla::dom::ChildProcessMessageManager* sChildProcessManager;
360
};
361
362
/* A helper class for taking care of many details for async message sending
363
   within a single process.  Intended to be used like so:
364
365
   class MyAsyncMessage : public nsSameProcessAsyncMessageBase, public Runnable
366
   {
367
     NS_IMETHOD Run() {
368
       ReceiveMessage(..., ...);
369
       return NS_OK;
370
     }
371
   };
372
373
374
   RefPtr<nsSameProcessAsyncMessageBase> ev = new MyAsyncMessage();
375
   nsresult rv = ev->Init(...);
376
   if (NS_SUCCEEDED(rv)) {
377
     NS_DispatchToMainThread(ev);
378
   }
379
*/
380
class nsSameProcessAsyncMessageBase
381
{
382
public:
383
  typedef mozilla::dom::ipc::StructuredCloneData StructuredCloneData;
384
385
  nsSameProcessAsyncMessageBase(JS::RootingContext* aRootingCx,
386
                                JS::Handle<JSObject*> aCpows);
387
  nsresult Init(const nsAString& aMessage,
388
                StructuredCloneData& aData,
389
                nsIPrincipal* aPrincipal);
390
391
  void ReceiveMessage(nsISupports* aTarget, nsFrameLoader* aTargetFrameLoader,
392
                      nsFrameMessageManager* aManager);
393
private:
394
  nsSameProcessAsyncMessageBase(const nsSameProcessAsyncMessageBase&);
395
396
  nsString mMessage;
397
  StructuredCloneData mData;
398
  JS::PersistentRooted<JSObject*> mCpows;
399
  nsCOMPtr<nsIPrincipal> mPrincipal;
400
#ifdef DEBUG
401
  bool mCalledInit;
402
#endif
403
};
404
405
class nsScriptCacheCleaner;
406
407
struct nsMessageManagerScriptHolder
408
{
409
  nsMessageManagerScriptHolder(JSContext* aCx,
410
                               JSScript* aScript)
411
   : mScript(aCx, aScript)
412
0
  { MOZ_COUNT_CTOR(nsMessageManagerScriptHolder); }
413
414
  ~nsMessageManagerScriptHolder()
415
0
  { MOZ_COUNT_DTOR(nsMessageManagerScriptHolder); }
416
417
  JS::PersistentRooted<JSScript*> mScript;
418
};
419
420
class nsMessageManagerScriptExecutor
421
{
422
public:
423
  static void PurgeCache();
424
  static void Shutdown();
425
426
  void MarkScopesForCC();
427
protected:
428
  friend class nsMessageManagerScriptCx;
429
0
  nsMessageManagerScriptExecutor() { MOZ_COUNT_CTOR(nsMessageManagerScriptExecutor); }
430
0
  ~nsMessageManagerScriptExecutor() { MOZ_COUNT_DTOR(nsMessageManagerScriptExecutor); }
431
432
  void DidCreateScriptLoader();
433
  void LoadScriptInternal(JS::Handle<JSObject*> aMessageManager, const nsAString& aURL,
434
                          bool aRunInUniqueScope);
435
  void TryCacheLoadAndCompileScript(const nsAString& aURL,
436
                                    bool aRunInUniqueScope,
437
                                    bool aShouldCache,
438
                                    JS::Handle<JSObject*> aMessageManager,
439
                                    JS::MutableHandle<JSScript*> aScriptp);
440
  bool Init();
441
  void Trace(const TraceCallbacks& aCallbacks, void* aClosure);
442
  void Unlink();
443
  nsCOMPtr<nsIPrincipal> mPrincipal;
444
  AutoTArray<JS::Heap<JSObject*>, 2> mAnonymousGlobalScopes;
445
446
  // Returns true if this is a process message manager. There should only be a
447
  // single process message manager per session, so instances of this type will
448
  // optimize their script loading to avoid unnecessary duplication.
449
  virtual bool IsProcessScoped() const
450
0
  {
451
0
    return false;
452
0
  }
453
454
  static nsDataHashtable<nsStringHashKey, nsMessageManagerScriptHolder*>* sCachedScripts;
455
  static mozilla::StaticRefPtr<nsScriptCacheCleaner> sScriptCacheCleaner;
456
};
457
458
class nsScriptCacheCleaner final : public nsIObserver
459
{
460
0
  ~nsScriptCacheCleaner() {}
461
462
  NS_DECL_ISUPPORTS
463
464
  nsScriptCacheCleaner()
465
0
  {
466
0
    nsCOMPtr<nsIObserverService> obsSvc = mozilla::services::GetObserverService();
467
0
    if (obsSvc) {
468
0
      obsSvc->AddObserver(this, "message-manager-flush-caches", false);
469
0
      obsSvc->AddObserver(this, "xpcom-shutdown", false);
470
0
    }
471
0
  }
472
473
  NS_IMETHOD Observe(nsISupports *aSubject,
474
                     const char *aTopic,
475
                     const char16_t *aData) override
476
0
  {
477
0
    if (strcmp("message-manager-flush-caches", aTopic) == 0) {
478
0
      nsMessageManagerScriptExecutor::PurgeCache();
479
0
    } else if (strcmp("xpcom-shutdown", aTopic) == 0) {
480
0
      nsMessageManagerScriptExecutor::Shutdown();
481
0
    }
482
0
    return NS_OK;
483
0
  }
484
};
485
486
#endif