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 "nsIAtom.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/dom/SameProcessMessageQueue.h" 32 #include "mozilla/dom/ipc/StructuredCloneData.h" 33 #include "mozilla/jsipc/CpowHolder.h" 34 35 class nsIFrameLoader; 36 37 namespace mozilla { 38 namespace dom { 39 40 class nsIContentParent; 41 class nsIContentChild; 42 class ClonedMessageData; 43 class MessageManagerReporter; 44 45 namespace ipc { 46 47 enum MessageManagerFlags { 48 MM_CHILD = 0, 49 MM_CHROME = 1, 50 MM_GLOBAL = 2, 51 MM_PROCESSMANAGER = 4, 52 MM_BROADCASTER = 8, 53 MM_OWNSCALLBACK = 16 54 }; 55 56 class MessageManagerCallback 57 { 58 public: ~MessageManagerCallback()59 virtual ~MessageManagerCallback() {} 60 DoLoadMessageManagerScript(const nsAString & aURL,bool aRunInGlobalScope)61 virtual bool DoLoadMessageManagerScript(const nsAString& aURL, bool aRunInGlobalScope) 62 { 63 return true; 64 } 65 DoSendBlockingMessage(JSContext * aCx,const nsAString & aMessage,StructuredCloneData & aData,JS::Handle<JSObject * > aCpows,nsIPrincipal * aPrincipal,nsTArray<StructuredCloneData> * aRetVal,bool aIsSync)66 virtual bool DoSendBlockingMessage(JSContext* aCx, 67 const nsAString& aMessage, 68 StructuredCloneData& aData, 69 JS::Handle<JSObject*> aCpows, 70 nsIPrincipal* aPrincipal, 71 nsTArray<StructuredCloneData>* aRetVal, 72 bool aIsSync) 73 { 74 return true; 75 } 76 DoSendAsyncMessage(JSContext * aCx,const nsAString & aMessage,StructuredCloneData & aData,JS::Handle<JSObject * > aCpows,nsIPrincipal * aPrincipal)77 virtual nsresult DoSendAsyncMessage(JSContext* aCx, 78 const nsAString& aMessage, 79 StructuredCloneData& aData, 80 JS::Handle<JSObject*> aCpows, 81 nsIPrincipal* aPrincipal) 82 { 83 return NS_OK; 84 } 85 CheckPermission(const nsAString & aPermission)86 virtual bool CheckPermission(const nsAString& aPermission) 87 { 88 return false; 89 } 90 CheckManifestURL(const nsAString & aManifestURL)91 virtual bool CheckManifestURL(const nsAString& aManifestURL) 92 { 93 return false; 94 } 95 CheckAppHasPermission(const nsAString & aPermission)96 virtual bool CheckAppHasPermission(const nsAString& aPermission) 97 { 98 return false; 99 } 100 CheckAppHasStatus(unsigned short aStatus)101 virtual bool CheckAppHasStatus(unsigned short aStatus) 102 { 103 return false; 104 } 105 KillChild()106 virtual bool KillChild() 107 { 108 // By default, does nothing. 109 return false; 110 } 111 GetProcessMessageManager()112 virtual nsIMessageSender* GetProcessMessageManager() const 113 { 114 return nullptr; 115 } 116 117 protected: 118 bool BuildClonedMessageDataForParent(nsIContentParent* aParent, 119 StructuredCloneData& aData, 120 ClonedMessageData& aClonedData); 121 bool BuildClonedMessageDataForChild(nsIContentChild* aChild, 122 StructuredCloneData& aData, 123 ClonedMessageData& aClonedData); 124 }; 125 126 void UnpackClonedMessageDataForParent(const ClonedMessageData& aClonedData, 127 StructuredCloneData& aData); 128 129 void UnpackClonedMessageDataForChild(const ClonedMessageData& aClonedData, 130 StructuredCloneData& aData); 131 132 } // namespace ipc 133 } // namespace dom 134 } // namespace mozilla 135 136 struct nsMessageListenerInfo 137 { 138 bool operator==(const nsMessageListenerInfo& aOther) const 139 { 140 return &aOther == this; 141 } 142 143 // Exactly one of mStrongListener and mWeakListener must be non-null. 144 nsCOMPtr<nsIMessageListener> mStrongListener; 145 nsWeakPtr mWeakListener; 146 bool mListenWhenClosed; 147 }; 148 149 150 class MOZ_STACK_CLASS SameProcessCpowHolder : public mozilla::jsipc::CpowHolder 151 { 152 public: SameProcessCpowHolder(JS::RootingContext * aRootingCx,JS::Handle<JSObject * > aObj)153 SameProcessCpowHolder(JS::RootingContext* aRootingCx, JS::Handle<JSObject*> aObj) 154 : mObj(aRootingCx, aObj) 155 { 156 } 157 158 virtual bool ToObject(JSContext* aCx, JS::MutableHandle<JSObject*> aObjp) 159 override; 160 161 private: 162 JS::Rooted<JSObject*> mObj; 163 }; 164 165 class nsFrameMessageManager final : public nsIContentFrameMessageManager, 166 public nsIMessageBroadcaster, 167 public nsIFrameScriptLoader, 168 public nsIGlobalProcessScriptLoader, 169 public nsIProcessChecker 170 { 171 friend class mozilla::dom::MessageManagerReporter; 172 typedef mozilla::dom::ipc::StructuredCloneData StructuredCloneData; 173 public: 174 nsFrameMessageManager(mozilla::dom::ipc::MessageManagerCallback* aCallback, 175 nsFrameMessageManager* aParentManager, 176 /* mozilla::dom::ipc::MessageManagerFlags */ uint32_t aFlags); 177 178 private: 179 ~nsFrameMessageManager(); 180 181 public: 182 NS_DECL_CYCLE_COLLECTING_ISUPPORTS 183 NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(nsFrameMessageManager, 184 nsIContentFrameMessageManager) 185 NS_DECL_NSIMESSAGELISTENERMANAGER 186 NS_DECL_NSIMESSAGESENDER 187 NS_DECL_NSIMESSAGEBROADCASTER 188 NS_DECL_NSISYNCMESSAGESENDER 189 NS_DECL_NSIMESSAGEMANAGERGLOBAL 190 NS_DECL_NSICONTENTFRAMEMESSAGEMANAGER 191 NS_DECL_NSIFRAMESCRIPTLOADER 192 NS_DECL_NSIPROCESSSCRIPTLOADER 193 NS_DECL_NSIGLOBALPROCESSSCRIPTLOADER 194 NS_DECL_NSIPROCESSCHECKER 195 196 static nsFrameMessageManager* 197 NewProcessMessageManager(bool aIsRemote); 198 199 nsresult ReceiveMessage(nsISupports* aTarget, nsIFrameLoader* aTargetFrameLoader, 200 const nsAString& aMessage, 201 bool aIsSync, StructuredCloneData* aCloneData, 202 mozilla::jsipc::CpowHolder* aCpows, nsIPrincipal* aPrincipal, 203 nsTArray<StructuredCloneData>* aRetVal); 204 205 void AddChildManager(nsFrameMessageManager* aManager); RemoveChildManager(nsFrameMessageManager * aManager)206 void RemoveChildManager(nsFrameMessageManager* aManager) 207 { 208 mChildManagers.RemoveObject(aManager); 209 } 210 void Disconnect(bool aRemoveFromParent = true); 211 void Close(); 212 213 void InitWithCallback(mozilla::dom::ipc::MessageManagerCallback* aCallback); 214 void SetCallback(mozilla::dom::ipc::MessageManagerCallback* aCallback); 215 GetCallback()216 mozilla::dom::ipc::MessageManagerCallback* GetCallback() 217 { 218 return mCallback; 219 } 220 221 nsresult DispatchAsyncMessage(const nsAString& aMessageName, 222 const JS::Value& aJSON, 223 const JS::Value& aObjects, 224 nsIPrincipal* aPrincipal, 225 const JS::Value& aTransfers, 226 JSContext* aCx, 227 uint8_t aArgc); 228 229 nsresult DispatchAsyncMessageInternal(JSContext* aCx, 230 const nsAString& aMessage, 231 StructuredCloneData& aData, 232 JS::Handle<JSObject*> aCpows, 233 nsIPrincipal* aPrincipal); 234 void RemoveFromParent(); GetParentManager()235 nsFrameMessageManager* GetParentManager() { return mParentManager; } SetParentManager(nsFrameMessageManager * aParent)236 void SetParentManager(nsFrameMessageManager* aParent) 237 { 238 NS_ASSERTION(!mParentManager, "We have parent manager already!"); 239 NS_ASSERTION(mChrome, "Should not set parent manager!"); 240 mParentManager = aParent; 241 } IsGlobal()242 bool IsGlobal() { return mGlobal; } IsBroadcaster()243 bool IsBroadcaster() { return mIsBroadcaster; } 244 GetParentProcessManager()245 static nsFrameMessageManager* GetParentProcessManager() 246 { 247 return sParentProcessManager; 248 } GetChildProcessManager()249 static nsFrameMessageManager* GetChildProcessManager() 250 { 251 return sChildProcessManager; 252 } SetChildProcessManager(nsFrameMessageManager * aManager)253 static void SetChildProcessManager(nsFrameMessageManager* aManager) 254 { 255 sChildProcessManager = aManager; 256 } 257 258 void SetInitialProcessData(JS::HandleValue aInitialData); 259 260 void LoadPendingScripts(); 261 262 private: 263 nsresult SendMessage(const nsAString& aMessageName, 264 JS::Handle<JS::Value> aJSON, 265 JS::Handle<JS::Value> aObjects, 266 nsIPrincipal* aPrincipal, 267 JSContext* aCx, 268 uint8_t aArgc, 269 JS::MutableHandle<JS::Value> aRetval, 270 bool aIsSync); 271 272 nsresult ReceiveMessage(nsISupports* aTarget, nsIFrameLoader* aTargetFrameLoader, 273 bool aTargetClosed, const nsAString& aMessage, 274 bool aIsSync, StructuredCloneData* aCloneData, 275 mozilla::jsipc::CpowHolder* aCpows, nsIPrincipal* aPrincipal, 276 nsTArray<StructuredCloneData>* aRetVal); 277 278 NS_IMETHOD LoadScript(const nsAString& aURL, 279 bool aAllowDelayedLoad, 280 bool aRunInGlobalScope); 281 NS_IMETHOD RemoveDelayedScript(const nsAString& aURL); 282 NS_IMETHOD GetDelayedScripts(JSContext* aCx, JS::MutableHandle<JS::Value> aList); 283 284 protected: 285 friend class MMListenerRemover; 286 // We keep the message listeners as arrays in a hastable indexed by the 287 // message name. That gives us fast lookups in ReceiveMessage(). 288 nsClassHashtable<nsStringHashKey, 289 nsAutoTObserverArray<nsMessageListenerInfo, 1>> mListeners; 290 nsCOMArray<nsIContentFrameMessageManager> mChildManagers; 291 bool mChrome; // true if we're in the chrome process 292 bool mGlobal; // true if we're the global frame message manager 293 bool mIsProcessManager; // true if the message manager belongs to the process realm 294 bool mIsBroadcaster; // true if the message manager is a broadcaster 295 bool mOwnsCallback; 296 bool mHandlingMessage; 297 bool mClosed; // true if we can no longer send messages 298 bool mDisconnected; 299 mozilla::dom::ipc::MessageManagerCallback* mCallback; 300 nsAutoPtr<mozilla::dom::ipc::MessageManagerCallback> mOwnedCallback; 301 RefPtr<nsFrameMessageManager> mParentManager; 302 nsTArray<nsString> mPendingScripts; 303 nsTArray<bool> mPendingScriptsGlobalStates; 304 JS::Heap<JS::Value> mInitialProcessData; 305 306 void LoadPendingScripts(nsFrameMessageManager* aManager, 307 nsFrameMessageManager* aChildMM); 308 public: 309 static nsFrameMessageManager* sParentProcessManager; 310 static nsFrameMessageManager* sSameProcessParentManager; 311 static nsTArray<nsCOMPtr<nsIRunnable> >* sPendingSameProcessAsyncMessages; 312 private: 313 static nsFrameMessageManager* sChildProcessManager; 314 enum ProcessCheckerType { 315 PROCESS_CHECKER_PERMISSION, 316 PROCESS_CHECKER_MANIFEST_URL, 317 ASSERT_APP_HAS_PERMISSION 318 }; 319 nsresult AssertProcessInternal(ProcessCheckerType aType, 320 const nsAString& aCapability, 321 bool* aValid); 322 }; 323 324 /* A helper class for taking care of many details for async message sending 325 within a single process. Intended to be used like so: 326 327 class MyAsyncMessage : public nsSameProcessAsyncMessageBase, public Runnable 328 { 329 NS_IMETHOD Run() override { 330 ReceiveMessage(..., ...); 331 return NS_OK; 332 } 333 }; 334 335 336 RefPtr<nsSameProcessAsyncMessageBase> ev = new MyAsyncMessage(); 337 nsresult rv = ev->Init(...); 338 if (NS_SUCCEEDED(rv)) { 339 NS_DispatchToMainThread(ev); 340 } 341 */ 342 class nsSameProcessAsyncMessageBase 343 { 344 public: 345 typedef mozilla::dom::ipc::StructuredCloneData StructuredCloneData; 346 347 nsSameProcessAsyncMessageBase(JS::RootingContext* aRootingCx, 348 JS::Handle<JSObject*> aCpows); 349 nsresult Init(const nsAString& aMessage, 350 StructuredCloneData& aData, 351 nsIPrincipal* aPrincipal); 352 353 void ReceiveMessage(nsISupports* aTarget, nsIFrameLoader* aTargetFrameLoader, 354 nsFrameMessageManager* aManager); 355 private: 356 nsSameProcessAsyncMessageBase(const nsSameProcessAsyncMessageBase&); 357 358 JS::RootingContext* mRootingCx; 359 nsString mMessage; 360 StructuredCloneData mData; 361 JS::PersistentRooted<JSObject*> mCpows; 362 nsCOMPtr<nsIPrincipal> mPrincipal; 363 #ifdef DEBUG 364 bool mCalledInit; 365 #endif 366 }; 367 368 class nsScriptCacheCleaner; 369 370 struct nsMessageManagerScriptHolder 371 { nsMessageManagerScriptHoldernsMessageManagerScriptHolder372 nsMessageManagerScriptHolder(JSContext* aCx, 373 JSScript* aScript, 374 bool aRunInGlobalScope) 375 : mScript(aCx, aScript), mRunInGlobalScope(aRunInGlobalScope) 376 { MOZ_COUNT_CTOR(nsMessageManagerScriptHolder); } 377 ~nsMessageManagerScriptHoldernsMessageManagerScriptHolder378 ~nsMessageManagerScriptHolder() 379 { MOZ_COUNT_DTOR(nsMessageManagerScriptHolder); } 380 WillRunInGlobalScopensMessageManagerScriptHolder381 bool WillRunInGlobalScope() { return mRunInGlobalScope; } 382 383 JS::PersistentRooted<JSScript*> mScript; 384 bool mRunInGlobalScope; 385 }; 386 387 class nsMessageManagerScriptExecutor 388 { 389 public: 390 static void PurgeCache(); 391 static void Shutdown(); GetGlobal()392 already_AddRefed<nsIXPConnectJSObjectHolder> GetGlobal() 393 { 394 nsCOMPtr<nsIXPConnectJSObjectHolder> ref = mGlobal; 395 return ref.forget(); 396 } 397 398 void MarkScopesForCC(); 399 protected: 400 friend class nsMessageManagerScriptCx; nsMessageManagerScriptExecutor()401 nsMessageManagerScriptExecutor() { MOZ_COUNT_CTOR(nsMessageManagerScriptExecutor); } ~nsMessageManagerScriptExecutor()402 ~nsMessageManagerScriptExecutor() { MOZ_COUNT_DTOR(nsMessageManagerScriptExecutor); } 403 404 void DidCreateGlobal(); 405 void LoadScriptInternal(const nsAString& aURL, bool aRunInGlobalScope); 406 void TryCacheLoadAndCompileScript(const nsAString& aURL, 407 bool aRunInGlobalScope, 408 bool aShouldCache, 409 JS::MutableHandle<JSScript*> aScriptp); 410 void TryCacheLoadAndCompileScript(const nsAString& aURL, 411 bool aRunInGlobalScope); 412 bool InitChildGlobalInternal(nsISupports* aScope, const nsACString& aID); 413 void Trace(const TraceCallbacks& aCallbacks, void* aClosure); 414 nsCOMPtr<nsIXPConnectJSObjectHolder> mGlobal; 415 nsCOMPtr<nsIPrincipal> mPrincipal; 416 AutoTArray<JS::Heap<JSObject*>, 2> mAnonymousGlobalScopes; 417 418 static nsDataHashtable<nsStringHashKey, nsMessageManagerScriptHolder*>* sCachedScripts; 419 static mozilla::StaticRefPtr<nsScriptCacheCleaner> sScriptCacheCleaner; 420 }; 421 422 class nsScriptCacheCleaner final : public nsIObserver 423 { ~nsScriptCacheCleaner()424 ~nsScriptCacheCleaner() {} 425 426 NS_DECL_ISUPPORTS 427 nsScriptCacheCleaner()428 nsScriptCacheCleaner() 429 { 430 nsCOMPtr<nsIObserverService> obsSvc = mozilla::services::GetObserverService(); 431 if (obsSvc) { 432 obsSvc->AddObserver(this, "message-manager-flush-caches", false); 433 obsSvc->AddObserver(this, "xpcom-shutdown", false); 434 } 435 } 436 Observe(nsISupports * aSubject,const char * aTopic,const char16_t * aData)437 NS_IMETHOD Observe(nsISupports *aSubject, 438 const char *aTopic, 439 const char16_t *aData) override 440 { 441 if (strcmp("message-manager-flush-caches", aTopic) == 0) { 442 nsMessageManagerScriptExecutor::PurgeCache(); 443 } else if (strcmp("xpcom-shutdown", aTopic) == 0) { 444 nsMessageManagerScriptExecutor::Shutdown(); 445 } 446 return NS_OK; 447 } 448 }; 449 450 #endif 451