nsFrameMessageManager.h (12705B)
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 <string.h> 11 12 #include "ErrorList.h" 13 #include "js/TypeDecls.h" 14 #include "js/Value.h" 15 #include "js/experimental/JSStencil.h" 16 #include "mozilla/AlreadyAddRefed.h" 17 #include "mozilla/RefPtr.h" 18 #include "mozilla/Services.h" 19 #include "mozilla/StaticPtr.h" 20 #include "mozilla/TypedEnumBits.h" 21 #include "mozilla/UniquePtr.h" 22 #include "mozilla/dom/ipc/StructuredCloneData.h" 23 #include "nsCOMPtr.h" 24 #include "nsClassHashtable.h" 25 #include "nsCycleCollectionParticipant.h" 26 #include "nsHashKeys.h" 27 #include "nsIMessageManager.h" 28 #include "nsIObserver.h" 29 #include "nsIObserverService.h" 30 #include "nsISupports.h" 31 #include "nsIWeakReferenceUtils.h" 32 #include "nsStringFwd.h" 33 #include "nsTArray.h" 34 #include "nsTHashMap.h" 35 #include "nsTObserverArray.h" 36 #include "nscore.h" 37 38 class nsFrameLoader; 39 class nsIRunnable; 40 41 namespace mozilla { 42 43 class ErrorResult; 44 45 namespace dom { 46 47 class ChildProcessMessageManager; 48 class ChromeMessageBroadcaster; 49 class ClonedMessageData; 50 class MessageBroadcaster; 51 class MessageListener; 52 class MessageListenerManager; 53 class MessageManagerReporter; 54 class ParentProcessMessageManager; 55 class ProcessMessageManager; 56 57 namespace ipc { 58 59 class MessageManagerCallback; 60 class WritableSharedMap; 61 62 enum class MessageManagerFlags { 63 MM_NONE = 0, 64 MM_CHROME = 1, 65 MM_GLOBAL = 2, 66 MM_PROCESSMANAGER = 4, 67 MM_BROADCASTER = 8, 68 MM_OWNSCALLBACK = 16 69 }; 70 MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(MessageManagerFlags); 71 72 void UnpackClonedMessageData(const ClonedMessageData& aClonedData, 73 StructuredCloneData& aData); 74 75 } // namespace ipc 76 } // namespace dom 77 } // namespace mozilla 78 79 struct nsMessageListenerInfo { 80 bool operator==(const nsMessageListenerInfo& aOther) const { 81 return &aOther == this; 82 } 83 84 // If mWeakListener is null then mStrongListener holds a MessageListener. 85 // If mWeakListener is non-null then mStrongListener contains null. 86 RefPtr<mozilla::dom::MessageListener> mStrongListener; 87 nsWeakPtr mWeakListener; 88 bool mListenWhenClosed; 89 }; 90 91 class nsFrameMessageManager : public nsIMessageSender { 92 friend class mozilla::dom::MessageManagerReporter; 93 using StructuredCloneData = mozilla::dom::ipc::StructuredCloneData; 94 95 protected: 96 using MessageManagerFlags = mozilla::dom::ipc::MessageManagerFlags; 97 98 nsFrameMessageManager(mozilla::dom::ipc::MessageManagerCallback* aCallback, 99 MessageManagerFlags aFlags); 100 101 virtual ~nsFrameMessageManager(); 102 103 public: 104 explicit nsFrameMessageManager( 105 mozilla::dom::ipc::MessageManagerCallback* aCallback) 106 : nsFrameMessageManager(aCallback, MessageManagerFlags::MM_NONE) {} 107 108 NS_DECL_CYCLE_COLLECTING_ISUPPORTS 109 NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(nsFrameMessageManager) 110 111 void MarkForCC(); 112 113 // MessageListenerManager 114 void AddMessageListener(const nsAString& aMessageName, 115 mozilla::dom::MessageListener& aListener, 116 bool aListenWhenClosed, mozilla::ErrorResult& aError); 117 void RemoveMessageListener(const nsAString& aMessageName, 118 mozilla::dom::MessageListener& aListener, 119 mozilla::ErrorResult& aError); 120 void AddWeakMessageListener(const nsAString& aMessageName, 121 mozilla::dom::MessageListener& aListener, 122 mozilla::ErrorResult& aError); 123 void RemoveWeakMessageListener(const nsAString& aMessageName, 124 mozilla::dom::MessageListener& aListener, 125 mozilla::ErrorResult& aError); 126 127 // MessageSender 128 void SendAsyncMessage(JSContext* aCx, const nsAString& aMessageName, 129 JS::Handle<JS::Value> aObj, 130 JS::Handle<JS::Value> aTransfers, 131 mozilla::ErrorResult& aError) { 132 DispatchAsyncMessage(aCx, aMessageName, aObj, aTransfers, aError); 133 } 134 already_AddRefed<mozilla::dom::ProcessMessageManager> 135 GetProcessMessageManager(mozilla::ErrorResult& aError); 136 void GetRemoteType(nsACString& aRemoteType, 137 mozilla::ErrorResult& aError) const; 138 139 // SyncMessageSender 140 void SendSyncMessage(JSContext* aCx, const nsAString& aMessageName, 141 JS::Handle<JS::Value> aObj, nsTArray<JS::Value>& aResult, 142 mozilla::ErrorResult& aError); 143 144 // GlobalProcessScriptLoader 145 void GetInitialProcessData(JSContext* aCx, 146 JS::MutableHandle<JS::Value> aInitialProcessData, 147 mozilla::ErrorResult& aError); 148 149 mozilla::dom::ipc::WritableSharedMap* SharedData(); 150 151 NS_DECL_NSIMESSAGESENDER 152 153 static mozilla::dom::ProcessMessageManager* NewProcessMessageManager( 154 bool aIsRemote); 155 156 void ReceiveMessage( 157 nsISupports* aTarget, nsFrameLoader* aTargetFrameLoader, 158 const nsAString& aMessage, bool aIsSync, StructuredCloneData* aCloneData, 159 nsTArray<mozilla::UniquePtr<StructuredCloneData>>* aRetVal, 160 mozilla::ErrorResult& aError) { 161 ReceiveMessage(aTarget, aTargetFrameLoader, mClosed, aMessage, aIsSync, 162 aCloneData, aRetVal, aError); 163 } 164 165 void Disconnect(bool aRemoveFromParent = true); 166 void Close(); 167 168 void SetCallback(mozilla::dom::ipc::MessageManagerCallback* aCallback); 169 170 mozilla::dom::ipc::MessageManagerCallback* GetCallback() { return mCallback; } 171 172 nsresult DispatchAsyncMessageInternal(JSContext* aCx, 173 const nsAString& aMessage, 174 StructuredCloneData& aData); 175 bool IsGlobal() { return mGlobal; } 176 bool IsBroadcaster() { return mIsBroadcaster; } 177 bool IsChrome() { return mChrome; } 178 179 // GetGlobalMessageManager creates the global message manager if it hasn't 180 // been yet. 181 static already_AddRefed<mozilla::dom::ChromeMessageBroadcaster> 182 GetGlobalMessageManager(); 183 static mozilla::dom::ParentProcessMessageManager* GetParentProcessManager() { 184 return sParentProcessManager; 185 } 186 static mozilla::dom::ChildProcessMessageManager* GetChildProcessManager() { 187 return sChildProcessManager; 188 } 189 static void SetChildProcessManager( 190 mozilla::dom::ChildProcessMessageManager* aManager) { 191 sChildProcessManager = aManager; 192 } 193 194 static bool GetParamsForMessage(JSContext* aCx, const JS::Value& aValue, 195 const JS::Value& aTransfer, 196 StructuredCloneData& aData); 197 198 void SetInitialProcessData(JS::Handle<JS::Value> aInitialData); 199 200 void LoadPendingScripts(); 201 202 protected: 203 friend class MMListenerRemover; 204 205 virtual mozilla::dom::MessageBroadcaster* GetParentManager() { 206 return nullptr; 207 } 208 virtual void ClearParentManager(bool aRemove) {} 209 210 void DispatchAsyncMessage(JSContext* aCx, const nsAString& aMessageName, 211 JS::Handle<JS::Value> aObj, 212 JS::Handle<JS::Value> aTransfers, 213 mozilla::ErrorResult& aError); 214 215 void ReceiveMessage( 216 nsISupports* aTarget, nsFrameLoader* aTargetFrameLoader, 217 bool aTargetClosed, const nsAString& aMessage, bool aIsSync, 218 StructuredCloneData* aCloneData, 219 nsTArray<mozilla::UniquePtr<StructuredCloneData>>* aRetVal, 220 mozilla::ErrorResult& aError); 221 222 void LoadScript(const nsAString& aURL, bool aAllowDelayedLoad, 223 bool aRunInGlobalScope, mozilla::ErrorResult& aError); 224 void RemoveDelayedScript(const nsAString& aURL); 225 void GetDelayedScripts(JSContext* aCx, nsTArray<nsTArray<JS::Value>>& aList, 226 mozilla::ErrorResult& aError); 227 228 // We keep the message listeners as arrays in a hastable indexed by the 229 // message name. That gives us fast lookups in ReceiveMessage(). 230 nsClassHashtable<nsStringHashKey, 231 nsAutoTObserverArray<nsMessageListenerInfo, 1>> 232 mListeners; 233 nsTArray<RefPtr<mozilla::dom::MessageListenerManager>> mChildManagers; 234 bool mChrome; // true if we're in the chrome process 235 bool mGlobal; // true if we're the global frame message manager 236 bool mIsProcessManager; // true if the message manager belongs to the process 237 // realm 238 bool mIsBroadcaster; // true if the message manager is a broadcaster 239 bool mOwnsCallback; 240 bool mHandlingMessage; 241 bool mClosed; // true if we can no longer send messages 242 bool mDisconnected; 243 mozilla::dom::ipc::MessageManagerCallback* mCallback; 244 mozilla::UniquePtr<mozilla::dom::ipc::MessageManagerCallback> mOwnedCallback; 245 nsTArray<nsString> mPendingScripts; 246 nsTArray<bool> mPendingScriptsGlobalStates; 247 JS::Heap<JS::Value> mInitialProcessData; 248 RefPtr<mozilla::dom::ipc::WritableSharedMap> mSharedData; 249 250 void LoadPendingScripts(nsFrameMessageManager* aManager, 251 nsFrameMessageManager* aChildMM); 252 253 public: 254 static mozilla::dom::ParentProcessMessageManager* sParentProcessManager; 255 static nsFrameMessageManager* sSameProcessParentManager; 256 static nsTArray<nsCOMPtr<nsIRunnable>>* sPendingSameProcessAsyncMessages; 257 258 private: 259 static mozilla::dom::ChildProcessMessageManager* sChildProcessManager; 260 }; 261 262 /* A helper class for taking care of many details for async message sending 263 within a single process. Intended to be used like so: 264 265 class MyAsyncMessage : public nsSameProcessAsyncMessageBase, public Runnable 266 { 267 NS_IMETHOD Run() { 268 ReceiveMessage(..., ...); 269 return NS_OK; 270 } 271 }; 272 273 274 RefPtr<nsSameProcessAsyncMessageBase> ev = new MyAsyncMessage(); 275 nsresult rv = ev->Init(...); 276 if (NS_SUCCEEDED(rv)) { 277 NS_DispatchToMainThread(ev); 278 } 279 */ 280 class nsSameProcessAsyncMessageBase { 281 public: 282 using StructuredCloneData = mozilla::dom::ipc::StructuredCloneData; 283 284 nsSameProcessAsyncMessageBase(); 285 nsresult Init(const nsAString& aMessage, StructuredCloneData& aData); 286 287 void ReceiveMessage(nsISupports* aTarget, nsFrameLoader* aTargetFrameLoader, 288 nsFrameMessageManager* aManager); 289 290 private: 291 nsSameProcessAsyncMessageBase(const nsSameProcessAsyncMessageBase&); 292 293 nsString mMessage; 294 StructuredCloneData mData; 295 #ifdef DEBUG 296 bool mCalledInit; 297 #endif 298 }; 299 300 class nsScriptCacheCleaner; 301 302 struct nsMessageManagerScriptHolder { 303 explicit nsMessageManagerScriptHolder(JS::Stencil* aStencil) 304 : mStencil(aStencil) { 305 MOZ_COUNT_CTOR(nsMessageManagerScriptHolder); 306 } 307 308 MOZ_COUNTED_DTOR(nsMessageManagerScriptHolder) 309 310 RefPtr<JS::Stencil> mStencil; 311 }; 312 313 class nsMessageManagerScriptExecutor { 314 public: 315 static void PurgeCache(); 316 static void Shutdown(); 317 318 void MarkScopesForCC(); 319 320 protected: 321 friend class nsMessageManagerScriptCx; 322 nsMessageManagerScriptExecutor() { 323 MOZ_COUNT_CTOR(nsMessageManagerScriptExecutor); 324 } 325 MOZ_COUNTED_DTOR(nsMessageManagerScriptExecutor) 326 327 void DidCreateScriptLoader(); 328 void LoadScriptInternal(JS::Handle<JSObject*> aMessageManager, 329 const nsAString& aURL, bool aRunInUniqueScope); 330 already_AddRefed<JS::Stencil> TryCacheLoadAndCompileScript( 331 const nsAString& aURL, bool aRunInUniqueScope, 332 JS::Handle<JSObject*> aMessageManager); 333 bool Init(); 334 void Trace(const TraceCallbacks& aCallbacks, void* aClosure); 335 void Unlink(); 336 AutoTArray<JS::Heap<JSObject*>, 2> mAnonymousGlobalScopes; 337 338 // Returns true if this is a process message manager. There should only be a 339 // single process message manager per session, so instances of this type will 340 // optimize their script loading to avoid unnecessary duplication. 341 virtual bool IsProcessScoped() const { return false; } 342 343 static nsTHashMap<nsStringHashKey, nsMessageManagerScriptHolder*>* 344 sCachedScripts; 345 static mozilla::StaticRefPtr<nsScriptCacheCleaner> sScriptCacheCleaner; 346 }; 347 348 class nsScriptCacheCleaner final : public nsIObserver { 349 ~nsScriptCacheCleaner() = default; 350 351 NS_DECL_ISUPPORTS 352 353 nsScriptCacheCleaner() { 354 nsCOMPtr<nsIObserverService> obsSvc = 355 mozilla::services::GetObserverService(); 356 if (obsSvc) { 357 obsSvc->AddObserver(this, "xpcom-shutdown", false); 358 } 359 } 360 361 NS_IMETHOD Observe(nsISupports* aSubject, const char* aTopic, 362 const char16_t* aData) override { 363 if (strcmp("xpcom-shutdown", aTopic) == 0) { 364 nsMessageManagerScriptExecutor::Shutdown(); 365 } 366 return NS_OK; 367 } 368 }; 369 370 #endif