michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim: set ts=8 sts=2 et sw=2 tw=80: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #ifndef nsFrameMessageManager_h__ michael@0: #define nsFrameMessageManager_h__ michael@0: michael@0: #include "nsIMessageManager.h" michael@0: #include "nsIObserver.h" michael@0: #include "nsCOMPtr.h" michael@0: #include "nsAutoPtr.h" michael@0: #include "nsCOMArray.h" michael@0: #include "nsTArray.h" michael@0: #include "nsIAtom.h" michael@0: #include "nsCycleCollectionParticipant.h" michael@0: #include "nsTArray.h" michael@0: #include "nsIPrincipal.h" michael@0: #include "nsIXPConnect.h" michael@0: #include "nsDataHashtable.h" michael@0: #include "nsClassHashtable.h" michael@0: #include "mozilla/Services.h" michael@0: #include "nsIObserverService.h" michael@0: #include "nsThreadUtils.h" michael@0: #include "nsWeakPtr.h" michael@0: #include "mozilla/Attributes.h" michael@0: #include "js/RootingAPI.h" michael@0: #include "nsTObserverArray.h" michael@0: #include "mozilla/dom/StructuredCloneUtils.h" michael@0: michael@0: namespace mozilla { michael@0: namespace dom { michael@0: michael@0: class ContentParent; michael@0: class ContentChild; michael@0: class ClonedMessageData; michael@0: class MessageManagerReporter; michael@0: michael@0: namespace ipc { michael@0: michael@0: enum MessageManagerFlags { michael@0: MM_CHILD = 0, michael@0: MM_CHROME = 1, michael@0: MM_GLOBAL = 2, michael@0: MM_PROCESSMANAGER = 4, michael@0: MM_BROADCASTER = 8, michael@0: MM_OWNSCALLBACK = 16 michael@0: }; michael@0: michael@0: class MessageManagerCallback michael@0: { michael@0: public: michael@0: virtual ~MessageManagerCallback() {} michael@0: michael@0: virtual bool DoLoadFrameScript(const nsAString& aURL, bool aRunInGlobalScope) michael@0: { michael@0: return true; michael@0: } michael@0: michael@0: virtual bool DoSendBlockingMessage(JSContext* aCx, michael@0: const nsAString& aMessage, michael@0: const StructuredCloneData& aData, michael@0: JS::Handle aCpows, michael@0: nsIPrincipal* aPrincipal, michael@0: InfallibleTArray* aJSONRetVal, michael@0: bool aIsSync) michael@0: { michael@0: return true; michael@0: } michael@0: michael@0: virtual bool DoSendAsyncMessage(JSContext* aCx, michael@0: const nsAString& aMessage, michael@0: const StructuredCloneData& aData, michael@0: JS::Handle aCpows, michael@0: nsIPrincipal* aPrincipal) michael@0: { michael@0: return true; michael@0: } michael@0: michael@0: virtual bool CheckPermission(const nsAString& aPermission) michael@0: { michael@0: return false; michael@0: } michael@0: michael@0: virtual bool CheckManifestURL(const nsAString& aManifestURL) michael@0: { michael@0: return false; michael@0: } michael@0: michael@0: virtual bool CheckAppHasPermission(const nsAString& aPermission) michael@0: { michael@0: return false; michael@0: } michael@0: michael@0: virtual bool CheckAppHasStatus(unsigned short aStatus) michael@0: { michael@0: return false; michael@0: } michael@0: michael@0: protected: michael@0: bool BuildClonedMessageDataForParent(ContentParent* aParent, michael@0: const StructuredCloneData& aData, michael@0: ClonedMessageData& aClonedData); michael@0: bool BuildClonedMessageDataForChild(ContentChild* aChild, michael@0: const StructuredCloneData& aData, michael@0: ClonedMessageData& aClonedData); michael@0: }; michael@0: michael@0: StructuredCloneData UnpackClonedMessageDataForParent(const ClonedMessageData& aData); michael@0: StructuredCloneData UnpackClonedMessageDataForChild(const ClonedMessageData& aData); michael@0: michael@0: } // namespace ipc michael@0: } // namespace dom michael@0: } // namespace mozilla michael@0: michael@0: class nsAXPCNativeCallContext; michael@0: michael@0: struct nsMessageListenerInfo michael@0: { michael@0: bool operator==(const nsMessageListenerInfo& aOther) const michael@0: { michael@0: return &aOther == this; michael@0: } michael@0: michael@0: // Exactly one of mStrongListener and mWeakListener must be non-null. michael@0: nsCOMPtr mStrongListener; michael@0: nsWeakPtr mWeakListener; michael@0: }; michael@0: michael@0: class CpowHolder michael@0: { michael@0: public: michael@0: virtual bool ToObject(JSContext* cx, JS::MutableHandle objp) = 0; michael@0: }; michael@0: michael@0: class MOZ_STACK_CLASS SameProcessCpowHolder : public CpowHolder michael@0: { michael@0: public: michael@0: SameProcessCpowHolder(JSRuntime *aRuntime, JS::Handle aObj) michael@0: : mObj(aRuntime, aObj) michael@0: { michael@0: } michael@0: michael@0: bool ToObject(JSContext* aCx, JS::MutableHandle aObjp); michael@0: michael@0: private: michael@0: JS::Rooted mObj; michael@0: }; michael@0: michael@0: class nsFrameMessageManager MOZ_FINAL : public nsIContentFrameMessageManager, michael@0: public nsIMessageBroadcaster, michael@0: public nsIFrameScriptLoader, michael@0: public nsIProcessChecker michael@0: { michael@0: friend class mozilla::dom::MessageManagerReporter; michael@0: typedef mozilla::dom::StructuredCloneData StructuredCloneData; michael@0: public: michael@0: nsFrameMessageManager(mozilla::dom::ipc::MessageManagerCallback* aCallback, michael@0: nsFrameMessageManager* aParentManager, michael@0: /* mozilla::dom::ipc::MessageManagerFlags */ uint32_t aFlags) michael@0: : mChrome(!!(aFlags & mozilla::dom::ipc::MM_CHROME)), michael@0: mGlobal(!!(aFlags & mozilla::dom::ipc::MM_GLOBAL)), michael@0: mIsProcessManager(!!(aFlags & mozilla::dom::ipc::MM_PROCESSMANAGER)), michael@0: mIsBroadcaster(!!(aFlags & mozilla::dom::ipc::MM_BROADCASTER)), michael@0: mOwnsCallback(!!(aFlags & mozilla::dom::ipc::MM_OWNSCALLBACK)), michael@0: mHandlingMessage(false), michael@0: mDisconnected(false), michael@0: mCallback(aCallback), michael@0: mParentManager(aParentManager) michael@0: { michael@0: NS_ASSERTION(mChrome || !aParentManager, "Should not set parent manager!"); michael@0: NS_ASSERTION(!mIsBroadcaster || !mCallback, michael@0: "Broadcasters cannot have callbacks!"); michael@0: // This is a bit hackish. When parent manager is global, we want michael@0: // to attach the window message manager to it immediately. michael@0: // Is it just the frame message manager which waits until the michael@0: // content process is running. michael@0: if (mParentManager && (mCallback || IsWindowLevel())) { michael@0: mParentManager->AddChildManager(this); michael@0: } michael@0: if (mOwnsCallback) { michael@0: mOwnedCallback = aCallback; michael@0: } michael@0: } michael@0: michael@0: ~nsFrameMessageManager() michael@0: { michael@0: for (int32_t i = mChildManagers.Count(); i > 0; --i) { michael@0: static_cast(mChildManagers[i - 1])-> michael@0: Disconnect(false); michael@0: } michael@0: if (mIsProcessManager) { michael@0: if (this == sParentProcessManager) { michael@0: sParentProcessManager = nullptr; michael@0: } michael@0: if (this == sChildProcessManager) { michael@0: sChildProcessManager = nullptr; michael@0: delete sPendingSameProcessAsyncMessages; michael@0: sPendingSameProcessAsyncMessages = nullptr; michael@0: } michael@0: if (this == sSameProcessParentManager) { michael@0: sSameProcessParentManager = nullptr; michael@0: } michael@0: } michael@0: } michael@0: michael@0: NS_DECL_CYCLE_COLLECTING_ISUPPORTS michael@0: NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsFrameMessageManager, michael@0: nsIContentFrameMessageManager) michael@0: NS_DECL_NSIMESSAGELISTENERMANAGER michael@0: NS_DECL_NSIMESSAGESENDER michael@0: NS_DECL_NSIMESSAGEBROADCASTER michael@0: NS_DECL_NSISYNCMESSAGESENDER michael@0: NS_DECL_NSICONTENTFRAMEMESSAGEMANAGER michael@0: NS_DECL_NSIFRAMESCRIPTLOADER michael@0: NS_DECL_NSIPROCESSCHECKER michael@0: michael@0: static nsFrameMessageManager* michael@0: NewProcessMessageManager(mozilla::dom::ContentParent* aProcess); michael@0: michael@0: nsresult ReceiveMessage(nsISupports* aTarget, const nsAString& aMessage, michael@0: bool aIsSync, const StructuredCloneData* aCloneData, michael@0: CpowHolder* aCpows, nsIPrincipal* aPrincipal, michael@0: InfallibleTArray* aJSONRetVal); michael@0: michael@0: void AddChildManager(nsFrameMessageManager* aManager); michael@0: void RemoveChildManager(nsFrameMessageManager* aManager) michael@0: { michael@0: mChildManagers.RemoveObject(aManager); michael@0: } michael@0: void Disconnect(bool aRemoveFromParent = true); michael@0: michael@0: void InitWithCallback(mozilla::dom::ipc::MessageManagerCallback* aCallback); michael@0: void SetCallback(mozilla::dom::ipc::MessageManagerCallback* aCallback); michael@0: mozilla::dom::ipc::MessageManagerCallback* GetCallback() michael@0: { michael@0: return mCallback; michael@0: } michael@0: michael@0: nsresult DispatchAsyncMessage(const nsAString& aMessageName, michael@0: const JS::Value& aJSON, michael@0: const JS::Value& aObjects, michael@0: nsIPrincipal* aPrincipal, michael@0: JSContext* aCx, michael@0: uint8_t aArgc); michael@0: nsresult DispatchAsyncMessageInternal(JSContext* aCx, michael@0: const nsAString& aMessage, michael@0: const StructuredCloneData& aData, michael@0: JS::Handle aCpows, michael@0: nsIPrincipal* aPrincipal); michael@0: void RemoveFromParent(); michael@0: nsFrameMessageManager* GetParentManager() { return mParentManager; } michael@0: void SetParentManager(nsFrameMessageManager* aParent) michael@0: { michael@0: NS_ASSERTION(!mParentManager, "We have parent manager already!"); michael@0: NS_ASSERTION(mChrome, "Should not set parent manager!"); michael@0: mParentManager = aParent; michael@0: } michael@0: bool IsGlobal() { return mGlobal; } michael@0: bool IsWindowLevel() { return mParentManager && mParentManager->IsGlobal(); } michael@0: michael@0: static nsFrameMessageManager* GetParentProcessManager() michael@0: { michael@0: return sParentProcessManager; michael@0: } michael@0: static nsFrameMessageManager* GetChildProcessManager() michael@0: { michael@0: return sChildProcessManager; michael@0: } michael@0: private: michael@0: nsresult SendMessage(const nsAString& aMessageName, michael@0: JS::Handle aJSON, michael@0: JS::Handle aObjects, michael@0: nsIPrincipal* aPrincipal, michael@0: JSContext* aCx, michael@0: uint8_t aArgc, michael@0: JS::MutableHandle aRetval, michael@0: bool aIsSync); michael@0: protected: michael@0: friend class MMListenerRemover; michael@0: // We keep the message listeners as arrays in a hastable indexed by the michael@0: // message name. That gives us fast lookups in ReceiveMessage(). michael@0: nsClassHashtable> mListeners; michael@0: nsCOMArray mChildManagers; michael@0: bool mChrome; // true if we're in the chrome process michael@0: bool mGlobal; // true if we're the global frame message manager michael@0: bool mIsProcessManager; // true if the message manager belongs to the process realm michael@0: bool mIsBroadcaster; // true if the message manager is a broadcaster michael@0: bool mOwnsCallback; michael@0: bool mHandlingMessage; michael@0: bool mDisconnected; michael@0: mozilla::dom::ipc::MessageManagerCallback* mCallback; michael@0: nsAutoPtr mOwnedCallback; michael@0: nsFrameMessageManager* mParentManager; michael@0: nsTArray mPendingScripts; michael@0: nsTArray mPendingScriptsGlobalStates; michael@0: public: michael@0: static nsFrameMessageManager* sParentProcessManager; michael@0: static nsFrameMessageManager* sChildProcessManager; michael@0: static nsFrameMessageManager* sSameProcessParentManager; michael@0: static nsTArray >* sPendingSameProcessAsyncMessages; michael@0: private: michael@0: enum ProcessCheckerType { michael@0: PROCESS_CHECKER_PERMISSION, michael@0: PROCESS_CHECKER_MANIFEST_URL, michael@0: ASSERT_APP_HAS_PERMISSION michael@0: }; michael@0: nsresult AssertProcessInternal(ProcessCheckerType aType, michael@0: const nsAString& aCapability, michael@0: bool* aValid); michael@0: }; michael@0: michael@0: /* A helper class for taking care of many details for async message sending michael@0: within a single process. Intended to be used like so: michael@0: michael@0: class MyAsyncMessage : public nsSameProcessAsyncMessageBase, public nsRunnable michael@0: { michael@0: // Initialize nsSameProcessAsyncMessageBase... michael@0: michael@0: NS_IMETHOD Run() { michael@0: ReceiveMessage(..., ...); michael@0: return NS_OK; michael@0: } michael@0: }; michael@0: */ michael@0: class nsSameProcessAsyncMessageBase michael@0: { michael@0: typedef mozilla::dom::StructuredCloneClosure StructuredCloneClosure; michael@0: michael@0: public: michael@0: typedef mozilla::dom::StructuredCloneData StructuredCloneData; michael@0: michael@0: nsSameProcessAsyncMessageBase(JSContext* aCx, michael@0: const nsAString& aMessage, michael@0: const StructuredCloneData& aData, michael@0: JS::Handle aCpows, michael@0: nsIPrincipal* aPrincipal); michael@0: michael@0: void ReceiveMessage(nsISupports* aTarget, nsFrameMessageManager* aManager); michael@0: michael@0: private: michael@0: nsSameProcessAsyncMessageBase(const nsSameProcessAsyncMessageBase&); michael@0: michael@0: JSRuntime* mRuntime; michael@0: nsString mMessage; michael@0: JSAutoStructuredCloneBuffer mData; michael@0: StructuredCloneClosure mClosure; michael@0: JS::PersistentRooted mCpows; michael@0: nsCOMPtr mPrincipal; michael@0: }; michael@0: michael@0: class nsScriptCacheCleaner; michael@0: michael@0: struct nsFrameScriptObjectExecutorHolder michael@0: { michael@0: nsFrameScriptObjectExecutorHolder(JSContext* aCx, JSScript* aScript) michael@0: : mScript(aCx, aScript), mFunction(aCx, nullptr) michael@0: { MOZ_COUNT_CTOR(nsFrameScriptObjectExecutorHolder); } michael@0: michael@0: nsFrameScriptObjectExecutorHolder(JSContext* aCx, JSObject* aFunction) michael@0: : mScript(aCx, nullptr), mFunction(aCx, aFunction) michael@0: { MOZ_COUNT_CTOR(nsFrameScriptObjectExecutorHolder); } michael@0: michael@0: ~nsFrameScriptObjectExecutorHolder() michael@0: { MOZ_COUNT_DTOR(nsFrameScriptObjectExecutorHolder); } michael@0: michael@0: bool WillRunInGlobalScope() { return mScript; } michael@0: michael@0: JS::PersistentRooted mScript; michael@0: JS::PersistentRooted mFunction; michael@0: }; michael@0: michael@0: class nsFrameScriptObjectExecutorStackHolder; michael@0: michael@0: class nsFrameScriptExecutor michael@0: { michael@0: public: michael@0: static void Shutdown(); michael@0: already_AddRefed GetGlobal() michael@0: { michael@0: nsCOMPtr ref = mGlobal; michael@0: return ref.forget(); michael@0: } michael@0: protected: michael@0: friend class nsFrameScriptCx; michael@0: nsFrameScriptExecutor() michael@0: { MOZ_COUNT_CTOR(nsFrameScriptExecutor); } michael@0: ~nsFrameScriptExecutor() michael@0: { MOZ_COUNT_DTOR(nsFrameScriptExecutor); } michael@0: void DidCreateGlobal(); michael@0: void LoadFrameScriptInternal(const nsAString& aURL, bool aRunInGlobalScope); michael@0: void TryCacheLoadAndCompileScript(const nsAString& aURL, michael@0: bool aRunInGlobalScope, michael@0: bool aShouldCache, michael@0: JS::MutableHandle aScriptp, michael@0: JS::MutableHandle aFunp); michael@0: void TryCacheLoadAndCompileScript(const nsAString& aURL, michael@0: bool aRunInGlobalScope); michael@0: bool InitTabChildGlobalInternal(nsISupports* aScope, const nsACString& aID); michael@0: nsCOMPtr mGlobal; michael@0: nsCOMPtr mPrincipal; michael@0: static nsDataHashtable* sCachedScripts; michael@0: static nsScriptCacheCleaner* sScriptCacheCleaner; michael@0: }; michael@0: michael@0: class nsScriptCacheCleaner MOZ_FINAL : public nsIObserver michael@0: { michael@0: NS_DECL_ISUPPORTS michael@0: michael@0: nsScriptCacheCleaner() michael@0: { michael@0: nsCOMPtr obsSvc = mozilla::services::GetObserverService(); michael@0: if (obsSvc) michael@0: obsSvc->AddObserver(this, "xpcom-shutdown", false); michael@0: } michael@0: michael@0: NS_IMETHODIMP Observe(nsISupports *aSubject, michael@0: const char *aTopic, michael@0: const char16_t *aData) michael@0: { michael@0: nsFrameScriptExecutor::Shutdown(); michael@0: return NS_OK; michael@0: } michael@0: }; michael@0: michael@0: #endif