1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:set ts=2 sw=2 sts=2 et cindent: */
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 mozilla_dom_JSActor_h
8 #define mozilla_dom_JSActor_h
9 
10 #include "js/TypeDecls.h"
11 #include "ipc/EnumSerializer.h"
12 #include "mozilla/Attributes.h"
13 #include "mozilla/dom/PromiseNativeHandler.h"
14 #include "nsCycleCollectionParticipant.h"
15 #include "nsTHashMap.h"
16 #include "nsWrapperCache.h"
17 
18 class nsIGlobalObject;
19 class nsQueryJSActor;
20 
21 namespace mozilla {
22 class ErrorResult;
23 
24 namespace dom {
25 
26 namespace ipc {
27 class StructuredCloneData;
28 }
29 
30 class JSActorManager;
31 class JSActorMessageMeta;
32 class QueryPromiseHandler;
33 
34 enum class JSActorMessageKind {
35   Message,
36   Query,
37   QueryResolve,
38   QueryReject,
39   EndGuard_,
40 };
41 
42 // Common base class for JSWindowActor{Parent,Child}.
43 class JSActor : public nsISupports, public nsWrapperCache {
44  public:
45   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
46   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(JSActor)
47 
48   explicit JSActor(nsISupports* aGlobal = nullptr);
49 
Name()50   const nsCString& Name() const { return mName; }
GetName(nsCString & aName)51   void GetName(nsCString& aName) { aName = Name(); }
52 
53   void SendAsyncMessage(JSContext* aCx, const nsAString& aMessageName,
54                         JS::Handle<JS::Value> aObj, ErrorResult& aRv);
55 
56   already_AddRefed<Promise> SendQuery(JSContext* aCx,
57                                       const nsAString& aMessageName,
58                                       JS::Handle<JS::Value> aObj,
59                                       ErrorResult& aRv);
60 
GetParentObject()61   nsIGlobalObject* GetParentObject() const { return mGlobal; };
62 
63  protected:
64   // Send the message described by the structured clone data |aData|, and the
65   // message metadata |aMetadata|. The underlying transport should call the
66   // |ReceiveMessage| method on the other side asynchronously.
67   virtual void SendRawMessage(const JSActorMessageMeta& aMetadata,
68                               Maybe<ipc::StructuredCloneData>&& aData,
69                               Maybe<ipc::StructuredCloneData>&& aStack,
70                               ErrorResult& aRv) = 0;
71 
72   // Check if a message is so large that IPC will probably crash if we try to
73   // send it. If it is too large, record telemetry about the message.
74   static bool AllowMessage(const JSActorMessageMeta& aMetadata,
75                            size_t aDataLength);
76 
77   // Helper method to send an in-process raw message.
78   using OtherSideCallback = std::function<already_AddRefed<JSActorManager>()>;
79   static void SendRawMessageInProcess(const JSActorMessageMeta& aMeta,
80                                       Maybe<ipc::StructuredCloneData>&& aData,
81                                       Maybe<ipc::StructuredCloneData>&& aStack,
82                                       OtherSideCallback&& aGetOtherSide);
83 
84   virtual ~JSActor() = default;
85 
86   void SetName(const nsACString& aName);
87 
CanSend()88   bool CanSend() const { return mCanSend; }
89 
90   void ThrowStateErrorForGetter(const char* aName, ErrorResult& aRv) const;
91 
92   void StartDestroy();
93   void AfterDestroy();
94 
95   enum class CallbackFunction { DidDestroy, ActorCreated };
96   void InvokeCallback(CallbackFunction callback);
97 
98   virtual void ClearManager() = 0;
99 
100  private:
101   friend class JSActorManager;
102   friend class ::nsQueryJSActor;  // for QueryInterfaceActor
103 
104   nsresult QueryInterfaceActor(const nsIID& aIID, void** aPtr);
105 
106   // Called by JSActorManager when they receive raw message data destined for
107   // this actor.
108   void ReceiveMessage(JSContext* aCx, const JSActorMessageMeta& aMetadata,
109                       JS::Handle<JS::Value> aData, ErrorResult& aRv);
110   void ReceiveQuery(JSContext* aCx, const JSActorMessageMeta& aMetadata,
111                     JS::Handle<JS::Value> aData, ErrorResult& aRv);
112   void ReceiveQueryReply(JSContext* aCx, const JSActorMessageMeta& aMetadata,
113                          JS::Handle<JS::Value> aData, ErrorResult& aRv);
114 
115   // Call the actual `ReceiveMessage` method, and get the return value.
116   void CallReceiveMessage(JSContext* aCx, const JSActorMessageMeta& aMetadata,
117                           JS::Handle<JS::Value> aData,
118                           JS::MutableHandle<JS::Value> aRetVal,
119                           ErrorResult& aRv);
120 
121   // Helper object used while processing query messages to send the final reply
122   // message.
123   class QueryHandler final : public PromiseNativeHandler {
124    public:
125     NS_DECL_CYCLE_COLLECTING_ISUPPORTS
126     NS_DECL_CYCLE_COLLECTION_CLASS(QueryHandler)
127 
128     QueryHandler(JSActor* aActor, const JSActorMessageMeta& aMetadata,
129                  Promise* aPromise);
130 
131     void RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue,
132                           ErrorResult& aRv) override;
133 
134     void ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue,
135                           ErrorResult& aRv) override;
136 
137    private:
138     ~QueryHandler() = default;
139 
140     void SendReply(JSContext* aCx, JSActorMessageKind aKind,
141                    Maybe<ipc::StructuredCloneData>&& aData);
142 
143     RefPtr<JSActor> mActor;
144     RefPtr<Promise> mPromise;
145     nsString mMessageName;
146     uint64_t mQueryId;
147   };
148 
149   // A query which hasn't been resolved yet, along with metadata about what
150   // query the promise is for.
151   struct PendingQuery {
152     RefPtr<Promise> mPromise;
153     nsString mMessageName;
154   };
155 
156   nsCOMPtr<nsIGlobalObject> mGlobal;
157   nsCOMPtr<nsISupports> mWrappedJS;
158   nsCString mName;
159   nsTHashMap<nsUint64HashKey, PendingQuery> mPendingQueries;
160   uint64_t mNextQueryId = 0;
161   bool mCanSend = true;
162 };
163 
164 }  // namespace dom
165 }  // namespace mozilla
166 
167 namespace IPC {
168 
169 template <>
170 struct ParamTraits<mozilla::dom::JSActorMessageKind>
171     : public ContiguousEnumSerializer<
172           mozilla::dom::JSActorMessageKind,
173           mozilla::dom::JSActorMessageKind::Message,
174           mozilla::dom::JSActorMessageKind::EndGuard_> {};
175 
176 }  // namespace IPC
177 
178 #endif  // !defined(mozilla_dom_JSActor_h)
179