diff options
Diffstat (limited to 'dom/base/nsFrameMessageManager.h')
-rw-r--r-- | dom/base/nsFrameMessageManager.h | 450 |
1 files changed, 450 insertions, 0 deletions
diff --git a/dom/base/nsFrameMessageManager.h b/dom/base/nsFrameMessageManager.h new file mode 100644 index 000000000..077389a49 --- /dev/null +++ b/dom/base/nsFrameMessageManager.h @@ -0,0 +1,450 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nsFrameMessageManager_h__ +#define nsFrameMessageManager_h__ + +#include "nsIMessageManager.h" +#include "nsIObserver.h" +#include "nsCOMPtr.h" +#include "nsAutoPtr.h" +#include "nsCOMArray.h" +#include "nsTArray.h" +#include "nsIAtom.h" +#include "nsCycleCollectionParticipant.h" +#include "nsTArray.h" +#include "nsIPrincipal.h" +#include "nsIXPConnect.h" +#include "nsDataHashtable.h" +#include "nsClassHashtable.h" +#include "mozilla/Services.h" +#include "mozilla/StaticPtr.h" +#include "nsIObserverService.h" +#include "nsThreadUtils.h" +#include "nsWeakPtr.h" +#include "mozilla/Attributes.h" +#include "js/RootingAPI.h" +#include "nsTObserverArray.h" +#include "mozilla/dom/SameProcessMessageQueue.h" +#include "mozilla/dom/ipc/StructuredCloneData.h" +#include "mozilla/jsipc/CpowHolder.h" + +class nsIFrameLoader; + +namespace mozilla { +namespace dom { + +class nsIContentParent; +class nsIContentChild; +class ClonedMessageData; +class MessageManagerReporter; + +namespace ipc { + +enum MessageManagerFlags { + MM_CHILD = 0, + MM_CHROME = 1, + MM_GLOBAL = 2, + MM_PROCESSMANAGER = 4, + MM_BROADCASTER = 8, + MM_OWNSCALLBACK = 16 +}; + +class MessageManagerCallback +{ +public: + virtual ~MessageManagerCallback() {} + + virtual bool DoLoadMessageManagerScript(const nsAString& aURL, bool aRunInGlobalScope) + { + return true; + } + + virtual bool DoSendBlockingMessage(JSContext* aCx, + const nsAString& aMessage, + StructuredCloneData& aData, + JS::Handle<JSObject*> aCpows, + nsIPrincipal* aPrincipal, + nsTArray<StructuredCloneData>* aRetVal, + bool aIsSync) + { + return true; + } + + virtual nsresult DoSendAsyncMessage(JSContext* aCx, + const nsAString& aMessage, + StructuredCloneData& aData, + JS::Handle<JSObject*> aCpows, + nsIPrincipal* aPrincipal) + { + return NS_OK; + } + + virtual bool CheckPermission(const nsAString& aPermission) + { + return false; + } + + virtual bool CheckManifestURL(const nsAString& aManifestURL) + { + return false; + } + + virtual bool CheckAppHasPermission(const nsAString& aPermission) + { + return false; + } + + virtual bool CheckAppHasStatus(unsigned short aStatus) + { + return false; + } + + virtual bool KillChild() + { + // By default, does nothing. + return false; + } + + virtual nsIMessageSender* GetProcessMessageManager() const + { + return nullptr; + } + +protected: + bool BuildClonedMessageDataForParent(nsIContentParent* aParent, + StructuredCloneData& aData, + ClonedMessageData& aClonedData); + bool BuildClonedMessageDataForChild(nsIContentChild* aChild, + StructuredCloneData& aData, + ClonedMessageData& aClonedData); +}; + +void UnpackClonedMessageDataForParent(const ClonedMessageData& aClonedData, + StructuredCloneData& aData); + +void UnpackClonedMessageDataForChild(const ClonedMessageData& aClonedData, + StructuredCloneData& aData); + +} // namespace ipc +} // namespace dom +} // namespace mozilla + +struct nsMessageListenerInfo +{ + bool operator==(const nsMessageListenerInfo& aOther) const + { + return &aOther == this; + } + + // Exactly one of mStrongListener and mWeakListener must be non-null. + nsCOMPtr<nsIMessageListener> mStrongListener; + nsWeakPtr mWeakListener; + bool mListenWhenClosed; +}; + + +class MOZ_STACK_CLASS SameProcessCpowHolder : public mozilla::jsipc::CpowHolder +{ +public: + SameProcessCpowHolder(JS::RootingContext* aRootingCx, JS::Handle<JSObject*> aObj) + : mObj(aRootingCx, aObj) + { + } + + virtual bool ToObject(JSContext* aCx, JS::MutableHandle<JSObject*> aObjp) + override; + +private: + JS::Rooted<JSObject*> mObj; +}; + +class nsFrameMessageManager final : public nsIContentFrameMessageManager, + public nsIMessageBroadcaster, + public nsIFrameScriptLoader, + public nsIGlobalProcessScriptLoader, + public nsIProcessChecker +{ + friend class mozilla::dom::MessageManagerReporter; + typedef mozilla::dom::ipc::StructuredCloneData StructuredCloneData; +public: + nsFrameMessageManager(mozilla::dom::ipc::MessageManagerCallback* aCallback, + nsFrameMessageManager* aParentManager, + /* mozilla::dom::ipc::MessageManagerFlags */ uint32_t aFlags); + +private: + ~nsFrameMessageManager(); + +public: + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(nsFrameMessageManager, + nsIContentFrameMessageManager) + NS_DECL_NSIMESSAGELISTENERMANAGER + NS_DECL_NSIMESSAGESENDER + NS_DECL_NSIMESSAGEBROADCASTER + NS_DECL_NSISYNCMESSAGESENDER + NS_DECL_NSIMESSAGEMANAGERGLOBAL + NS_DECL_NSICONTENTFRAMEMESSAGEMANAGER + NS_DECL_NSIFRAMESCRIPTLOADER + NS_DECL_NSIPROCESSSCRIPTLOADER + NS_DECL_NSIGLOBALPROCESSSCRIPTLOADER + NS_DECL_NSIPROCESSCHECKER + + static nsFrameMessageManager* + NewProcessMessageManager(bool aIsRemote); + + nsresult ReceiveMessage(nsISupports* aTarget, nsIFrameLoader* aTargetFrameLoader, + const nsAString& aMessage, + bool aIsSync, StructuredCloneData* aCloneData, + mozilla::jsipc::CpowHolder* aCpows, nsIPrincipal* aPrincipal, + nsTArray<StructuredCloneData>* aRetVal); + + void AddChildManager(nsFrameMessageManager* aManager); + void RemoveChildManager(nsFrameMessageManager* aManager) + { + mChildManagers.RemoveObject(aManager); + } + void Disconnect(bool aRemoveFromParent = true); + void Close(); + + void InitWithCallback(mozilla::dom::ipc::MessageManagerCallback* aCallback); + void SetCallback(mozilla::dom::ipc::MessageManagerCallback* aCallback); + + mozilla::dom::ipc::MessageManagerCallback* GetCallback() + { + return mCallback; + } + + nsresult DispatchAsyncMessage(const nsAString& aMessageName, + const JS::Value& aJSON, + const JS::Value& aObjects, + nsIPrincipal* aPrincipal, + const JS::Value& aTransfers, + JSContext* aCx, + uint8_t aArgc); + + nsresult DispatchAsyncMessageInternal(JSContext* aCx, + const nsAString& aMessage, + StructuredCloneData& aData, + JS::Handle<JSObject*> aCpows, + nsIPrincipal* aPrincipal); + void RemoveFromParent(); + nsFrameMessageManager* GetParentManager() { return mParentManager; } + void SetParentManager(nsFrameMessageManager* aParent) + { + NS_ASSERTION(!mParentManager, "We have parent manager already!"); + NS_ASSERTION(mChrome, "Should not set parent manager!"); + mParentManager = aParent; + } + bool IsGlobal() { return mGlobal; } + bool IsBroadcaster() { return mIsBroadcaster; } + + static nsFrameMessageManager* GetParentProcessManager() + { + return sParentProcessManager; + } + static nsFrameMessageManager* GetChildProcessManager() + { + return sChildProcessManager; + } + static void SetChildProcessManager(nsFrameMessageManager* aManager) + { + sChildProcessManager = aManager; + } + + void SetInitialProcessData(JS::HandleValue aInitialData); + + void LoadPendingScripts(); + +private: + nsresult SendMessage(const nsAString& aMessageName, + JS::Handle<JS::Value> aJSON, + JS::Handle<JS::Value> aObjects, + nsIPrincipal* aPrincipal, + JSContext* aCx, + uint8_t aArgc, + JS::MutableHandle<JS::Value> aRetval, + bool aIsSync); + + nsresult ReceiveMessage(nsISupports* aTarget, nsIFrameLoader* aTargetFrameLoader, + bool aTargetClosed, const nsAString& aMessage, + bool aIsSync, StructuredCloneData* aCloneData, + mozilla::jsipc::CpowHolder* aCpows, nsIPrincipal* aPrincipal, + nsTArray<StructuredCloneData>* aRetVal); + + NS_IMETHOD LoadScript(const nsAString& aURL, + bool aAllowDelayedLoad, + bool aRunInGlobalScope); + NS_IMETHOD RemoveDelayedScript(const nsAString& aURL); + NS_IMETHOD GetDelayedScripts(JSContext* aCx, JS::MutableHandle<JS::Value> aList); + +protected: + friend class MMListenerRemover; + // We keep the message listeners as arrays in a hastable indexed by the + // message name. That gives us fast lookups in ReceiveMessage(). + nsClassHashtable<nsStringHashKey, + nsAutoTObserverArray<nsMessageListenerInfo, 1>> mListeners; + nsCOMArray<nsIContentFrameMessageManager> mChildManagers; + bool mChrome; // true if we're in the chrome process + bool mGlobal; // true if we're the global frame message manager + bool mIsProcessManager; // true if the message manager belongs to the process realm + bool mIsBroadcaster; // true if the message manager is a broadcaster + bool mOwnsCallback; + bool mHandlingMessage; + bool mClosed; // true if we can no longer send messages + bool mDisconnected; + mozilla::dom::ipc::MessageManagerCallback* mCallback; + nsAutoPtr<mozilla::dom::ipc::MessageManagerCallback> mOwnedCallback; + RefPtr<nsFrameMessageManager> mParentManager; + nsTArray<nsString> mPendingScripts; + nsTArray<bool> mPendingScriptsGlobalStates; + JS::Heap<JS::Value> mInitialProcessData; + + void LoadPendingScripts(nsFrameMessageManager* aManager, + nsFrameMessageManager* aChildMM); +public: + static nsFrameMessageManager* sParentProcessManager; + static nsFrameMessageManager* sSameProcessParentManager; + static nsTArray<nsCOMPtr<nsIRunnable> >* sPendingSameProcessAsyncMessages; +private: + static nsFrameMessageManager* sChildProcessManager; + enum ProcessCheckerType { + PROCESS_CHECKER_PERMISSION, + PROCESS_CHECKER_MANIFEST_URL, + ASSERT_APP_HAS_PERMISSION + }; + nsresult AssertProcessInternal(ProcessCheckerType aType, + const nsAString& aCapability, + bool* aValid); +}; + +/* A helper class for taking care of many details for async message sending + within a single process. Intended to be used like so: + + class MyAsyncMessage : public nsSameProcessAsyncMessageBase, public Runnable + { + NS_IMETHOD Run() override { + ReceiveMessage(..., ...); + return NS_OK; + } + }; + + + RefPtr<nsSameProcessAsyncMessageBase> ev = new MyAsyncMessage(); + nsresult rv = ev->Init(...); + if (NS_SUCCEEDED(rv)) { + NS_DispatchToMainThread(ev); + } +*/ +class nsSameProcessAsyncMessageBase +{ +public: + typedef mozilla::dom::ipc::StructuredCloneData StructuredCloneData; + + nsSameProcessAsyncMessageBase(JS::RootingContext* aRootingCx, + JS::Handle<JSObject*> aCpows); + nsresult Init(const nsAString& aMessage, + StructuredCloneData& aData, + nsIPrincipal* aPrincipal); + + void ReceiveMessage(nsISupports* aTarget, nsIFrameLoader* aTargetFrameLoader, + nsFrameMessageManager* aManager); +private: + nsSameProcessAsyncMessageBase(const nsSameProcessAsyncMessageBase&); + + JS::RootingContext* mRootingCx; + nsString mMessage; + StructuredCloneData mData; + JS::PersistentRooted<JSObject*> mCpows; + nsCOMPtr<nsIPrincipal> mPrincipal; +#ifdef DEBUG + bool mCalledInit; +#endif +}; + +class nsScriptCacheCleaner; + +struct nsMessageManagerScriptHolder +{ + nsMessageManagerScriptHolder(JSContext* aCx, + JSScript* aScript, + bool aRunInGlobalScope) + : mScript(aCx, aScript), mRunInGlobalScope(aRunInGlobalScope) + { MOZ_COUNT_CTOR(nsMessageManagerScriptHolder); } + + ~nsMessageManagerScriptHolder() + { MOZ_COUNT_DTOR(nsMessageManagerScriptHolder); } + + bool WillRunInGlobalScope() { return mRunInGlobalScope; } + + JS::PersistentRooted<JSScript*> mScript; + bool mRunInGlobalScope; +}; + +class nsMessageManagerScriptExecutor +{ +public: + static void PurgeCache(); + static void Shutdown(); + already_AddRefed<nsIXPConnectJSObjectHolder> GetGlobal() + { + nsCOMPtr<nsIXPConnectJSObjectHolder> ref = mGlobal; + return ref.forget(); + } + + void MarkScopesForCC(); +protected: + friend class nsMessageManagerScriptCx; + nsMessageManagerScriptExecutor() { MOZ_COUNT_CTOR(nsMessageManagerScriptExecutor); } + ~nsMessageManagerScriptExecutor() { MOZ_COUNT_DTOR(nsMessageManagerScriptExecutor); } + + void DidCreateGlobal(); + void LoadScriptInternal(const nsAString& aURL, bool aRunInGlobalScope); + void TryCacheLoadAndCompileScript(const nsAString& aURL, + bool aRunInGlobalScope, + bool aShouldCache, + JS::MutableHandle<JSScript*> aScriptp); + void TryCacheLoadAndCompileScript(const nsAString& aURL, + bool aRunInGlobalScope); + bool InitChildGlobalInternal(nsISupports* aScope, const nsACString& aID); + void Trace(const TraceCallbacks& aCallbacks, void* aClosure); + nsCOMPtr<nsIXPConnectJSObjectHolder> mGlobal; + nsCOMPtr<nsIPrincipal> mPrincipal; + AutoTArray<JS::Heap<JSObject*>, 2> mAnonymousGlobalScopes; + + static nsDataHashtable<nsStringHashKey, nsMessageManagerScriptHolder*>* sCachedScripts; + static mozilla::StaticRefPtr<nsScriptCacheCleaner> sScriptCacheCleaner; +}; + +class nsScriptCacheCleaner final : public nsIObserver +{ + ~nsScriptCacheCleaner() {} + + NS_DECL_ISUPPORTS + + nsScriptCacheCleaner() + { + nsCOMPtr<nsIObserverService> obsSvc = mozilla::services::GetObserverService(); + if (obsSvc) { + obsSvc->AddObserver(this, "message-manager-flush-caches", false); + obsSvc->AddObserver(this, "xpcom-shutdown", false); + } + } + + NS_IMETHOD Observe(nsISupports *aSubject, + const char *aTopic, + const char16_t *aData) override + { + if (strcmp("message-manager-flush-caches", aTopic) == 0) { + nsMessageManagerScriptExecutor::PurgeCache(); + } else if (strcmp("xpcom-shutdown", aTopic) == 0) { + nsMessageManagerScriptExecutor::Shutdown(); + } + return NS_OK; + } +}; + +#endif |