michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 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: #include "base/basictypes.h" michael@0: michael@0: #include "nsFrameMessageManager.h" michael@0: michael@0: #include "AppProcessChecker.h" michael@0: #include "ContentChild.h" michael@0: #include "ContentParent.h" michael@0: #include "nsContentUtils.h" michael@0: #include "nsCxPusher.h" michael@0: #include "nsError.h" michael@0: #include "nsIXPConnect.h" michael@0: #include "jsapi.h" michael@0: #include "nsJSUtils.h" michael@0: #include "nsJSPrincipals.h" michael@0: #include "nsNetUtil.h" michael@0: #include "nsScriptLoader.h" michael@0: #include "nsFrameLoader.h" michael@0: #include "nsIXULRuntime.h" michael@0: #include "nsIScriptError.h" michael@0: #include "nsIConsoleService.h" michael@0: #include "nsIMemoryReporter.h" michael@0: #include "nsIProtocolHandler.h" michael@0: #include "nsIScriptSecurityManager.h" michael@0: #include "nsIJSRuntimeService.h" michael@0: #include "nsIDOMClassInfo.h" michael@0: #include "nsIDOMFile.h" michael@0: #include "xpcpublic.h" michael@0: #include "mozilla/Preferences.h" michael@0: #include "mozilla/dom/StructuredCloneUtils.h" michael@0: #include "mozilla/dom/PBlobChild.h" michael@0: #include "mozilla/dom/PBlobParent.h" michael@0: #include "JavaScriptChild.h" michael@0: #include "JavaScriptParent.h" michael@0: #include "mozilla/dom/DOMStringList.h" michael@0: #include "nsPrintfCString.h" michael@0: #include michael@0: michael@0: #ifdef ANDROID michael@0: #include michael@0: #endif michael@0: #ifdef XP_WIN michael@0: #include michael@0: # if defined(SendMessage) michael@0: # undef SendMessage michael@0: # endif michael@0: #endif michael@0: michael@0: using namespace mozilla; michael@0: using namespace mozilla::dom; michael@0: using namespace mozilla::dom::ipc; michael@0: michael@0: static PLDHashOperator michael@0: CycleCollectorTraverseListeners(const nsAString& aKey, michael@0: nsAutoTObserverArray* aListeners, michael@0: void* aCb) michael@0: { michael@0: nsCycleCollectionTraversalCallback* cb = michael@0: static_cast (aCb); michael@0: uint32_t count = aListeners->Length(); michael@0: for (uint32_t i = 0; i < count; ++i) { michael@0: NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, "listeners[i] mStrongListener"); michael@0: cb->NoteXPCOMChild(aListeners->ElementAt(i).mStrongListener.get()); michael@0: } michael@0: return PL_DHASH_NEXT; michael@0: } michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_CLASS(nsFrameMessageManager) michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsFrameMessageManager) michael@0: tmp->mListeners.EnumerateRead(CycleCollectorTraverseListeners, michael@0: static_cast(&cb)); michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChildManagers) michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsFrameMessageManager) michael@0: tmp->mListeners.Clear(); michael@0: for (int32_t i = tmp->mChildManagers.Count(); i > 0; --i) { michael@0: static_cast(tmp->mChildManagers[i - 1])-> michael@0: Disconnect(false); michael@0: } michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK(mChildManagers) michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK_END michael@0: michael@0: michael@0: NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsFrameMessageManager) michael@0: NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContentFrameMessageManager) michael@0: michael@0: /* nsFrameMessageManager implements nsIMessageSender and nsIMessageBroadcaster, michael@0: * both of which descend from nsIMessageListenerManager. QI'ing to michael@0: * nsIMessageListenerManager is therefore ambiguous and needs explicit casts michael@0: * depending on which child interface applies. */ michael@0: NS_INTERFACE_MAP_ENTRY_AGGREGATED(nsIMessageListenerManager, michael@0: (mIsBroadcaster ? michael@0: static_cast( michael@0: static_cast(this)) : michael@0: static_cast( michael@0: static_cast(this)))) michael@0: michael@0: /* Message managers in child process implement nsIMessageSender and michael@0: nsISyncMessageSender. Message managers in the chrome process are michael@0: either broadcasters (if they have subordinate/child message michael@0: managers) or they're simple message senders. */ michael@0: NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsISyncMessageSender, !mChrome) michael@0: NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIMessageSender, !mChrome || !mIsBroadcaster) michael@0: NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIMessageBroadcaster, mChrome && mIsBroadcaster) michael@0: michael@0: /* nsIContentFrameMessageManager is accessible only in TabChildGlobal. */ michael@0: NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIContentFrameMessageManager, michael@0: !mChrome && !mIsProcessManager) michael@0: michael@0: /* Frame message managers (non-process message managers) support nsIFrameScriptLoader. */ michael@0: NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIFrameScriptLoader, michael@0: mChrome && !mIsProcessManager) michael@0: michael@0: /* Message senders in the chrome process support nsIProcessChecker. */ michael@0: NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIProcessChecker, michael@0: mChrome && !mIsBroadcaster) michael@0: michael@0: NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO_CONDITIONAL(ChromeMessageBroadcaster, michael@0: mChrome && mIsBroadcaster) michael@0: NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO_CONDITIONAL(ChromeMessageSender, michael@0: mChrome && !mIsBroadcaster) michael@0: NS_INTERFACE_MAP_END michael@0: michael@0: NS_IMPL_CYCLE_COLLECTING_ADDREF(nsFrameMessageManager) michael@0: NS_IMPL_CYCLE_COLLECTING_RELEASE(nsFrameMessageManager) michael@0: michael@0: enum ActorFlavorEnum { michael@0: Parent = 0, michael@0: Child michael@0: }; michael@0: michael@0: template michael@0: struct BlobTraits michael@0: { }; michael@0: michael@0: template <> michael@0: struct BlobTraits michael@0: { michael@0: typedef mozilla::dom::BlobParent BlobType; michael@0: typedef mozilla::dom::PBlobParent ProtocolType; michael@0: typedef mozilla::dom::ContentParent ConcreteContentManagerType; michael@0: }; michael@0: michael@0: template <> michael@0: struct BlobTraits michael@0: { michael@0: typedef mozilla::dom::BlobChild BlobType; michael@0: typedef mozilla::dom::PBlobChild ProtocolType; michael@0: typedef mozilla::dom::ContentChild ConcreteContentManagerType; michael@0: }; michael@0: michael@0: template michael@0: struct DataBlobs michael@0: { }; michael@0: michael@0: template<> michael@0: struct DataBlobs michael@0: { michael@0: typedef BlobTraits::ProtocolType ProtocolType; michael@0: michael@0: static InfallibleTArray& Blobs(ClonedMessageData& aData) michael@0: { michael@0: return aData.blobsParent(); michael@0: } michael@0: michael@0: static const InfallibleTArray& Blobs(const ClonedMessageData& aData) michael@0: { michael@0: return aData.blobsParent(); michael@0: } michael@0: }; michael@0: michael@0: template<> michael@0: struct DataBlobs michael@0: { michael@0: typedef BlobTraits::ProtocolType ProtocolType; michael@0: michael@0: static InfallibleTArray& Blobs(ClonedMessageData& aData) michael@0: { michael@0: return aData.blobsChild(); michael@0: } michael@0: michael@0: static const InfallibleTArray& Blobs(const ClonedMessageData& aData) michael@0: { michael@0: return aData.blobsChild(); michael@0: } michael@0: }; michael@0: michael@0: template michael@0: static bool michael@0: BuildClonedMessageData(typename BlobTraits::ConcreteContentManagerType* aManager, michael@0: const StructuredCloneData& aData, michael@0: ClonedMessageData& aClonedData) michael@0: { michael@0: SerializedStructuredCloneBuffer& buffer = aClonedData.data(); michael@0: buffer.data = aData.mData; michael@0: buffer.dataLength = aData.mDataLength; michael@0: const nsTArray >& blobs = aData.mClosure.mBlobs; michael@0: if (!blobs.IsEmpty()) { michael@0: typedef typename BlobTraits::ProtocolType ProtocolType; michael@0: InfallibleTArray& blobList = DataBlobs::Blobs(aClonedData); michael@0: uint32_t length = blobs.Length(); michael@0: blobList.SetCapacity(length); michael@0: for (uint32_t i = 0; i < length; ++i) { michael@0: typename BlobTraits::BlobType* protocolActor = michael@0: aManager->GetOrCreateActorForBlob(blobs[i]); michael@0: if (!protocolActor) { michael@0: return false; michael@0: } michael@0: blobList.AppendElement(protocolActor); michael@0: } michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: MessageManagerCallback::BuildClonedMessageDataForParent(ContentParent* aParent, michael@0: const StructuredCloneData& aData, michael@0: ClonedMessageData& aClonedData) michael@0: { michael@0: return BuildClonedMessageData(aParent, aData, aClonedData); michael@0: } michael@0: michael@0: bool michael@0: MessageManagerCallback::BuildClonedMessageDataForChild(ContentChild* aChild, michael@0: const StructuredCloneData& aData, michael@0: ClonedMessageData& aClonedData) michael@0: { michael@0: return BuildClonedMessageData(aChild, aData, aClonedData); michael@0: } michael@0: michael@0: template michael@0: static StructuredCloneData michael@0: UnpackClonedMessageData(const ClonedMessageData& aData) michael@0: { michael@0: const SerializedStructuredCloneBuffer& buffer = aData.data(); michael@0: typedef typename BlobTraits::ProtocolType ProtocolType; michael@0: const InfallibleTArray& blobs = DataBlobs::Blobs(aData); michael@0: StructuredCloneData cloneData; michael@0: cloneData.mData = buffer.data; michael@0: cloneData.mDataLength = buffer.dataLength; michael@0: if (!blobs.IsEmpty()) { michael@0: uint32_t length = blobs.Length(); michael@0: cloneData.mClosure.mBlobs.SetCapacity(length); michael@0: for (uint32_t i = 0; i < length; ++i) { michael@0: auto* blob = michael@0: static_cast::BlobType*>(blobs[i]); michael@0: MOZ_ASSERT(blob); michael@0: nsCOMPtr domBlob = blob->GetBlob(); michael@0: MOZ_ASSERT(domBlob); michael@0: cloneData.mClosure.mBlobs.AppendElement(domBlob); michael@0: } michael@0: } michael@0: return cloneData; michael@0: } michael@0: michael@0: StructuredCloneData michael@0: mozilla::dom::ipc::UnpackClonedMessageDataForParent(const ClonedMessageData& aData) michael@0: { michael@0: return UnpackClonedMessageData(aData); michael@0: } michael@0: michael@0: StructuredCloneData michael@0: mozilla::dom::ipc::UnpackClonedMessageDataForChild(const ClonedMessageData& aData) michael@0: { michael@0: return UnpackClonedMessageData(aData); michael@0: } michael@0: michael@0: bool michael@0: SameProcessCpowHolder::ToObject(JSContext* aCx, michael@0: JS::MutableHandle aObjp) michael@0: { michael@0: if (!mObj) { michael@0: return true; michael@0: } michael@0: michael@0: aObjp.set(mObj); michael@0: return JS_WrapObject(aCx, aObjp); michael@0: } michael@0: michael@0: // nsIMessageListenerManager michael@0: michael@0: NS_IMETHODIMP michael@0: nsFrameMessageManager::AddMessageListener(const nsAString& aMessage, michael@0: nsIMessageListener* aListener) michael@0: { michael@0: nsAutoTObserverArray* listeners = michael@0: mListeners.Get(aMessage); michael@0: if (!listeners) { michael@0: listeners = new nsAutoTObserverArray(); michael@0: mListeners.Put(aMessage, listeners); michael@0: } else { michael@0: uint32_t len = listeners->Length(); michael@0: for (uint32_t i = 0; i < len; ++i) { michael@0: if (listeners->ElementAt(i).mStrongListener == aListener) { michael@0: return NS_OK; michael@0: } michael@0: } michael@0: } michael@0: michael@0: nsMessageListenerInfo* entry = listeners->AppendElement(); michael@0: NS_ENSURE_TRUE(entry, NS_ERROR_OUT_OF_MEMORY); michael@0: entry->mStrongListener = aListener; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsFrameMessageManager::RemoveMessageListener(const nsAString& aMessage, michael@0: nsIMessageListener* aListener) michael@0: { michael@0: nsAutoTObserverArray* listeners = michael@0: mListeners.Get(aMessage); michael@0: if (!listeners) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: uint32_t len = listeners->Length(); michael@0: for (uint32_t i = 0; i < len; ++i) { michael@0: if (listeners->ElementAt(i).mStrongListener == aListener) { michael@0: listeners->RemoveElementAt(i); michael@0: return NS_OK; michael@0: } michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: #ifdef DEBUG michael@0: typedef struct michael@0: { michael@0: nsCOMPtr mCanonical; michael@0: nsWeakPtr mWeak; michael@0: } CanonicalCheckerParams; michael@0: michael@0: static PLDHashOperator michael@0: CanonicalChecker(const nsAString& aKey, michael@0: nsAutoTObserverArray* aListeners, michael@0: void* aParams) michael@0: { michael@0: CanonicalCheckerParams* params = michael@0: static_cast (aParams); michael@0: michael@0: uint32_t count = aListeners->Length(); michael@0: for (uint32_t i = 0; i < count; i++) { michael@0: if (!aListeners->ElementAt(i).mWeakListener) { michael@0: continue; michael@0: } michael@0: nsCOMPtr otherCanonical = michael@0: do_QueryReferent(aListeners->ElementAt(i).mWeakListener); michael@0: MOZ_ASSERT((params->mCanonical == otherCanonical) == michael@0: (params->mWeak == aListeners->ElementAt(i).mWeakListener)); michael@0: } michael@0: return PL_DHASH_NEXT; michael@0: } michael@0: #endif michael@0: michael@0: NS_IMETHODIMP michael@0: nsFrameMessageManager::AddWeakMessageListener(const nsAString& aMessage, michael@0: nsIMessageListener* aListener) michael@0: { michael@0: nsWeakPtr weak = do_GetWeakReference(aListener); michael@0: NS_ENSURE_TRUE(weak, NS_ERROR_NO_INTERFACE); michael@0: michael@0: #ifdef DEBUG michael@0: // It's technically possible that one object X could give two different michael@0: // nsIWeakReference*'s when you do_GetWeakReference(X). We really don't want michael@0: // this to happen; it will break e.g. RemoveWeakMessageListener. So let's michael@0: // check that we're not getting ourselves into that situation. michael@0: nsCOMPtr canonical = do_QueryInterface(aListener); michael@0: CanonicalCheckerParams params; michael@0: params.mCanonical = canonical; michael@0: params.mWeak = weak; michael@0: mListeners.EnumerateRead(CanonicalChecker, (void*)¶ms); michael@0: #endif michael@0: michael@0: nsAutoTObserverArray* listeners = michael@0: mListeners.Get(aMessage); michael@0: if (!listeners) { michael@0: listeners = new nsAutoTObserverArray(); michael@0: mListeners.Put(aMessage, listeners); michael@0: } else { michael@0: uint32_t len = listeners->Length(); michael@0: for (uint32_t i = 0; i < len; ++i) { michael@0: if (listeners->ElementAt(i).mWeakListener == weak) { michael@0: return NS_OK; michael@0: } michael@0: } michael@0: } michael@0: michael@0: nsMessageListenerInfo* entry = listeners->AppendElement(); michael@0: entry->mWeakListener = weak; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsFrameMessageManager::RemoveWeakMessageListener(const nsAString& aMessage, michael@0: nsIMessageListener* aListener) michael@0: { michael@0: nsWeakPtr weak = do_GetWeakReference(aListener); michael@0: NS_ENSURE_TRUE(weak, NS_OK); michael@0: michael@0: nsAutoTObserverArray* listeners = michael@0: mListeners.Get(aMessage); michael@0: if (!listeners) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: uint32_t len = listeners->Length(); michael@0: for (uint32_t i = 0; i < len; ++i) { michael@0: if (listeners->ElementAt(i).mWeakListener == weak) { michael@0: listeners->RemoveElementAt(i); michael@0: return NS_OK; michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: // nsIFrameScriptLoader michael@0: michael@0: NS_IMETHODIMP michael@0: nsFrameMessageManager::LoadFrameScript(const nsAString& aURL, michael@0: bool aAllowDelayedLoad, michael@0: bool aRunInGlobalScope) michael@0: { michael@0: // FIXME: Bug 673569 is currently disabled. michael@0: aRunInGlobalScope = true; michael@0: michael@0: if (aAllowDelayedLoad) { michael@0: if (IsGlobal() || IsWindowLevel()) { michael@0: // Cache for future windows or frames michael@0: mPendingScripts.AppendElement(aURL); michael@0: mPendingScriptsGlobalStates.AppendElement(aRunInGlobalScope); michael@0: } else if (!mCallback) { michael@0: // We're frame message manager, which isn't connected yet. michael@0: mPendingScripts.AppendElement(aURL); michael@0: mPendingScriptsGlobalStates.AppendElement(aRunInGlobalScope); michael@0: return NS_OK; michael@0: } michael@0: } michael@0: michael@0: if (mCallback) { michael@0: #ifdef DEBUG_smaug michael@0: printf("Will load %s \n", NS_ConvertUTF16toUTF8(aURL).get()); michael@0: #endif michael@0: NS_ENSURE_TRUE(mCallback->DoLoadFrameScript(aURL, aRunInGlobalScope), michael@0: NS_ERROR_FAILURE); michael@0: } michael@0: michael@0: for (int32_t i = 0; i < mChildManagers.Count(); ++i) { michael@0: nsRefPtr mm = michael@0: static_cast(mChildManagers[i]); michael@0: if (mm) { michael@0: // Use false here, so that child managers don't cache the script, which michael@0: // is already cached in the parent. michael@0: mm->LoadFrameScript(aURL, false, aRunInGlobalScope); michael@0: } michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsFrameMessageManager::RemoveDelayedFrameScript(const nsAString& aURL) michael@0: { michael@0: for (uint32_t i = 0; i < mPendingScripts.Length(); ++i) { michael@0: if (mPendingScripts[i] == aURL) { michael@0: mPendingScripts.RemoveElementAt(i); michael@0: mPendingScriptsGlobalStates.RemoveElementAt(i); michael@0: break; michael@0: } michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsFrameMessageManager::GetDelayedFrameScripts(JSContext* aCx, JS::MutableHandle aList) michael@0: { michael@0: // Frame message managers may return an incomplete list because scripts michael@0: // that were loaded after it was connected are not added to the list. michael@0: if (!IsGlobal() && !IsWindowLevel()) { michael@0: NS_WARNING("Cannot retrieve list of pending frame scripts for frame" michael@0: "message managers as it may be incomplete"); michael@0: return NS_ERROR_NOT_IMPLEMENTED; michael@0: } michael@0: michael@0: JS::Rooted array(aCx, JS_NewArrayObject(aCx, mPendingScripts.Length())); michael@0: NS_ENSURE_TRUE(array, NS_ERROR_OUT_OF_MEMORY); michael@0: michael@0: JS::Rooted url(aCx); michael@0: JS::Rooted pair(aCx); michael@0: for (uint32_t i = 0; i < mPendingScripts.Length(); ++i) { michael@0: url = JS_NewUCStringCopyN(aCx, mPendingScripts[i].get(), mPendingScripts[i].Length()); michael@0: NS_ENSURE_TRUE(url, NS_ERROR_OUT_OF_MEMORY); michael@0: michael@0: JS::AutoValueArray<2> pairElts(aCx); michael@0: pairElts[0].setString(url); michael@0: pairElts[1].setBoolean(mPendingScriptsGlobalStates[i]); michael@0: michael@0: pair = JS_NewArrayObject(aCx, pairElts); michael@0: NS_ENSURE_TRUE(pair, NS_ERROR_OUT_OF_MEMORY); michael@0: michael@0: NS_ENSURE_TRUE(JS_SetElement(aCx, array, i, pair), michael@0: NS_ERROR_OUT_OF_MEMORY); michael@0: } michael@0: michael@0: aList.setObject(*array); michael@0: return NS_OK; michael@0: } michael@0: michael@0: static bool michael@0: JSONCreator(const jschar* aBuf, uint32_t aLen, void* aData) michael@0: { michael@0: nsAString* result = static_cast(aData); michael@0: result->Append(static_cast(aBuf), michael@0: static_cast(aLen)); michael@0: return true; michael@0: } michael@0: michael@0: static bool michael@0: GetParamsForMessage(JSContext* aCx, michael@0: const JS::Value& aJSON, michael@0: JSAutoStructuredCloneBuffer& aBuffer, michael@0: StructuredCloneClosure& aClosure) michael@0: { michael@0: JS::Rooted v(aCx, aJSON); michael@0: if (WriteStructuredClone(aCx, v, aBuffer, aClosure)) { michael@0: return true; michael@0: } michael@0: JS_ClearPendingException(aCx); michael@0: michael@0: // Not clonable, try JSON michael@0: //XXX This is ugly but currently structured cloning doesn't handle michael@0: // properly cases when interface is implemented in JS and used michael@0: // as a dictionary. michael@0: nsAutoString json; michael@0: NS_ENSURE_TRUE(JS_Stringify(aCx, &v, JS::NullPtr(), JS::NullHandleValue, michael@0: JSONCreator, &json), false); michael@0: NS_ENSURE_TRUE(!json.IsEmpty(), false); michael@0: michael@0: JS::Rooted val(aCx, JS::NullValue()); michael@0: NS_ENSURE_TRUE(JS_ParseJSON(aCx, static_cast(json.get()), michael@0: json.Length(), &val), false); michael@0: michael@0: return WriteStructuredClone(aCx, val, aBuffer, aClosure); michael@0: } michael@0: michael@0: michael@0: // nsISyncMessageSender michael@0: michael@0: static bool sSendingSyncMessage = false; michael@0: michael@0: NS_IMETHODIMP michael@0: nsFrameMessageManager::SendSyncMessage(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: { michael@0: return SendMessage(aMessageName, aJSON, aObjects, aPrincipal, aCx, aArgc, michael@0: aRetval, true); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsFrameMessageManager::SendRpcMessage(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: { michael@0: return SendMessage(aMessageName, aJSON, aObjects, aPrincipal, aCx, aArgc, michael@0: aRetval, false); michael@0: } michael@0: michael@0: nsresult michael@0: nsFrameMessageManager::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: { michael@0: NS_ASSERTION(!IsGlobal(), "Should not call SendSyncMessage in chrome"); michael@0: NS_ASSERTION(!IsWindowLevel(), "Should not call SendSyncMessage in chrome"); michael@0: NS_ASSERTION(!mParentManager, "Should not have parent manager in content!"); michael@0: michael@0: aRetval.setUndefined(); michael@0: NS_ENSURE_TRUE(mCallback, NS_ERROR_NOT_INITIALIZED); michael@0: michael@0: if (sSendingSyncMessage && aIsSync) { michael@0: // No kind of blocking send should be issued on top of a sync message. michael@0: return NS_ERROR_UNEXPECTED; michael@0: } michael@0: michael@0: StructuredCloneData data; michael@0: JSAutoStructuredCloneBuffer buffer; michael@0: if (aArgc >= 2 && michael@0: !GetParamsForMessage(aCx, aJSON, buffer, data.mClosure)) { michael@0: return NS_ERROR_DOM_DATA_CLONE_ERR; michael@0: } michael@0: data.mData = buffer.data(); michael@0: data.mDataLength = buffer.nbytes(); michael@0: michael@0: JS::Rooted objects(aCx); michael@0: if (aArgc >= 3 && aObjects.isObject()) { michael@0: objects = &aObjects.toObject(); michael@0: } michael@0: michael@0: InfallibleTArray retval; michael@0: michael@0: sSendingSyncMessage |= aIsSync; michael@0: bool rv = mCallback->DoSendBlockingMessage(aCx, aMessageName, data, objects, michael@0: aPrincipal, &retval, aIsSync); michael@0: if (aIsSync) { michael@0: sSendingSyncMessage = false; michael@0: } michael@0: michael@0: if (!rv) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: uint32_t len = retval.Length(); michael@0: JS::Rooted dataArray(aCx, JS_NewArrayObject(aCx, len)); michael@0: NS_ENSURE_TRUE(dataArray, NS_ERROR_OUT_OF_MEMORY); michael@0: michael@0: for (uint32_t i = 0; i < len; ++i) { michael@0: if (retval[i].IsEmpty()) { michael@0: continue; michael@0: } michael@0: michael@0: JS::Rooted ret(aCx); michael@0: if (!JS_ParseJSON(aCx, static_cast(retval[i].get()), michael@0: retval[i].Length(), &ret)) { michael@0: return NS_ERROR_UNEXPECTED; michael@0: } michael@0: NS_ENSURE_TRUE(JS_SetElement(aCx, dataArray, i, ret), michael@0: NS_ERROR_OUT_OF_MEMORY); michael@0: } michael@0: michael@0: aRetval.setObject(*dataArray); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsFrameMessageManager::DispatchAsyncMessageInternal(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: if (mIsBroadcaster) { michael@0: int32_t len = mChildManagers.Count(); michael@0: for (int32_t i = 0; i < len; ++i) { michael@0: static_cast(mChildManagers[i])-> michael@0: DispatchAsyncMessageInternal(aCx, aMessage, aData, aCpows, aPrincipal); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_ENSURE_TRUE(mCallback, NS_ERROR_NOT_INITIALIZED); michael@0: if (!mCallback->DoSendAsyncMessage(aCx, aMessage, aData, aCpows, aPrincipal)) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsFrameMessageManager::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: { michael@0: StructuredCloneData data; michael@0: JSAutoStructuredCloneBuffer buffer; michael@0: michael@0: if (aArgc >= 2 && michael@0: !GetParamsForMessage(aCx, aJSON, buffer, data.mClosure)) { michael@0: return NS_ERROR_DOM_DATA_CLONE_ERR; michael@0: } michael@0: michael@0: JS::Rooted objects(aCx); michael@0: if (aArgc >= 3 && aObjects.isObject()) { michael@0: objects = &aObjects.toObject(); michael@0: } michael@0: michael@0: data.mData = buffer.data(); michael@0: data.mDataLength = buffer.nbytes(); michael@0: michael@0: return DispatchAsyncMessageInternal(aCx, aMessageName, data, objects, michael@0: aPrincipal); michael@0: } michael@0: michael@0: michael@0: // nsIMessageSender michael@0: michael@0: NS_IMETHODIMP michael@0: nsFrameMessageManager::SendAsyncMessage(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: { michael@0: return DispatchAsyncMessage(aMessageName, aJSON, aObjects, aPrincipal, aCx, michael@0: aArgc); michael@0: } michael@0: michael@0: michael@0: // nsIMessageBroadcaster michael@0: michael@0: NS_IMETHODIMP michael@0: nsFrameMessageManager::BroadcastAsyncMessage(const nsAString& aMessageName, michael@0: JS::Handle aJSON, michael@0: JS::Handle aObjects, michael@0: JSContext* aCx, michael@0: uint8_t aArgc) michael@0: { michael@0: return DispatchAsyncMessage(aMessageName, aJSON, aObjects, nullptr, aCx, michael@0: aArgc); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsFrameMessageManager::GetChildCount(uint32_t* aChildCount) michael@0: { michael@0: *aChildCount = static_cast(mChildManagers.Count()); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsFrameMessageManager::GetChildAt(uint32_t aIndex, michael@0: nsIMessageListenerManager** aMM) michael@0: { michael@0: *aMM = nullptr; michael@0: nsCOMPtr mm = michael@0: do_QueryInterface(mChildManagers.SafeObjectAt(static_cast(aIndex))); michael@0: mm.swap(*aMM); michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: // nsIContentFrameMessageManager michael@0: michael@0: NS_IMETHODIMP michael@0: nsFrameMessageManager::Dump(const nsAString& aStr) michael@0: { michael@0: #ifdef ANDROID michael@0: __android_log_print(ANDROID_LOG_INFO, "Gecko", "%s", NS_ConvertUTF16toUTF8(aStr).get()); michael@0: #endif michael@0: #ifdef XP_WIN michael@0: if (IsDebuggerPresent()) { michael@0: OutputDebugStringW(PromiseFlatString(aStr).get()); michael@0: } michael@0: #endif michael@0: fputs(NS_ConvertUTF16toUTF8(aStr).get(), stdout); michael@0: fflush(stdout); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsFrameMessageManager::PrivateNoteIntentionalCrash() michael@0: { michael@0: return NS_ERROR_NOT_IMPLEMENTED; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsFrameMessageManager::GetContent(nsIDOMWindow** aContent) michael@0: { michael@0: *aContent = nullptr; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsFrameMessageManager::GetDocShell(nsIDocShell** aDocShell) michael@0: { michael@0: *aDocShell = nullptr; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsFrameMessageManager::Btoa(const nsAString& aBinaryData, michael@0: nsAString& aAsciiBase64String) michael@0: { michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsFrameMessageManager::Atob(const nsAString& aAsciiString, michael@0: nsAString& aBinaryData) michael@0: { michael@0: return NS_OK; michael@0: } michael@0: michael@0: // nsIProcessChecker michael@0: michael@0: nsresult michael@0: nsFrameMessageManager::AssertProcessInternal(ProcessCheckerType aType, michael@0: const nsAString& aCapability, michael@0: bool* aValid) michael@0: { michael@0: *aValid = false; michael@0: michael@0: // This API is only supported for message senders in the chrome process. michael@0: if (!mChrome || mIsBroadcaster) { michael@0: return NS_ERROR_NOT_IMPLEMENTED; michael@0: } michael@0: if (!mCallback) { michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: } michael@0: switch (aType) { michael@0: case PROCESS_CHECKER_PERMISSION: michael@0: *aValid = mCallback->CheckPermission(aCapability); michael@0: break; michael@0: case PROCESS_CHECKER_MANIFEST_URL: michael@0: *aValid = mCallback->CheckManifestURL(aCapability); michael@0: break; michael@0: case ASSERT_APP_HAS_PERMISSION: michael@0: *aValid = mCallback->CheckAppHasPermission(aCapability); michael@0: break; michael@0: default: michael@0: break; michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsFrameMessageManager::AssertPermission(const nsAString& aPermission, michael@0: bool* aHasPermission) michael@0: { michael@0: return AssertProcessInternal(PROCESS_CHECKER_PERMISSION, michael@0: aPermission, michael@0: aHasPermission); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsFrameMessageManager::AssertContainApp(const nsAString& aManifestURL, michael@0: bool* aHasManifestURL) michael@0: { michael@0: return AssertProcessInternal(PROCESS_CHECKER_MANIFEST_URL, michael@0: aManifestURL, michael@0: aHasManifestURL); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsFrameMessageManager::AssertAppHasPermission(const nsAString& aPermission, michael@0: bool* aHasPermission) michael@0: { michael@0: return AssertProcessInternal(ASSERT_APP_HAS_PERMISSION, michael@0: aPermission, michael@0: aHasPermission); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsFrameMessageManager::AssertAppHasStatus(unsigned short aStatus, michael@0: bool* aHasStatus) michael@0: { michael@0: *aHasStatus = false; michael@0: michael@0: // This API is only supported for message senders in the chrome process. michael@0: if (!mChrome || mIsBroadcaster) { michael@0: return NS_ERROR_NOT_IMPLEMENTED; michael@0: } michael@0: if (!mCallback) { michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: } michael@0: *aHasStatus = mCallback->CheckAppHasStatus(aStatus); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: class MMListenerRemover michael@0: { michael@0: public: michael@0: MMListenerRemover(nsFrameMessageManager* aMM) michael@0: : mWasHandlingMessage(aMM->mHandlingMessage) michael@0: , mMM(aMM) michael@0: { michael@0: mMM->mHandlingMessage = true; michael@0: } michael@0: ~MMListenerRemover() michael@0: { michael@0: if (!mWasHandlingMessage) { michael@0: mMM->mHandlingMessage = false; michael@0: if (mMM->mDisconnected) { michael@0: mMM->mListeners.Clear(); michael@0: } michael@0: } michael@0: } michael@0: michael@0: bool mWasHandlingMessage; michael@0: nsRefPtr mMM; michael@0: }; michael@0: michael@0: michael@0: // nsIMessageListener michael@0: michael@0: nsresult michael@0: nsFrameMessageManager::ReceiveMessage(nsISupports* aTarget, michael@0: const nsAString& aMessage, michael@0: bool aIsSync, michael@0: const StructuredCloneData* aCloneData, michael@0: CpowHolder* aCpows, michael@0: nsIPrincipal* aPrincipal, michael@0: InfallibleTArray* aJSONRetVal) michael@0: { michael@0: AutoSafeJSContext cx; michael@0: nsAutoTObserverArray* listeners = michael@0: mListeners.Get(aMessage); michael@0: if (listeners) { michael@0: michael@0: MMListenerRemover lr(this); michael@0: michael@0: nsAutoTObserverArray::EndLimitedIterator michael@0: iter(*listeners); michael@0: while(iter.HasMore()) { michael@0: nsMessageListenerInfo& listener = iter.GetNext(); michael@0: // Remove mListeners[i] if it's an expired weak listener. michael@0: nsCOMPtr weakListener; michael@0: if (listener.mWeakListener) { michael@0: weakListener = do_QueryReferent(listener.mWeakListener); michael@0: if (!weakListener) { michael@0: listeners->RemoveElement(listener); michael@0: continue; michael@0: } michael@0: } michael@0: michael@0: nsCOMPtr wrappedJS; michael@0: if (weakListener) { michael@0: wrappedJS = do_QueryInterface(weakListener); michael@0: } else { michael@0: wrappedJS = do_QueryInterface(listener.mStrongListener); michael@0: } michael@0: michael@0: if (!wrappedJS) { michael@0: continue; michael@0: } michael@0: JS::Rooted object(cx, wrappedJS->GetJSObject()); michael@0: if (!object) { michael@0: continue; michael@0: } michael@0: JSAutoCompartment ac(cx, object); michael@0: michael@0: // The parameter for the listener function. michael@0: JS::Rooted param(cx, michael@0: JS_NewObject(cx, nullptr, JS::NullPtr(), JS::NullPtr())); michael@0: NS_ENSURE_TRUE(param, NS_ERROR_OUT_OF_MEMORY); michael@0: michael@0: JS::Rooted targetv(cx); michael@0: js::AssertSameCompartment(cx, object); michael@0: nsresult rv = nsContentUtils::WrapNative(cx, aTarget, &targetv); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: JS::Rooted cpows(cx); michael@0: if (aCpows) { michael@0: if (!aCpows->ToObject(cx, &cpows)) { michael@0: return NS_ERROR_UNEXPECTED; michael@0: } michael@0: } michael@0: michael@0: if (!cpows) { michael@0: cpows = JS_NewObject(cx, nullptr, JS::NullPtr(), JS::NullPtr()); michael@0: if (!cpows) { michael@0: return NS_ERROR_UNEXPECTED; michael@0: } michael@0: } michael@0: michael@0: JS::Rooted cpowsv(cx, JS::ObjectValue(*cpows)); michael@0: michael@0: JS::Rooted json(cx, JS::NullValue()); michael@0: if (aCloneData && aCloneData->mDataLength && michael@0: !ReadStructuredClone(cx, *aCloneData, &json)) { michael@0: JS_ClearPendingException(cx); michael@0: return NS_OK; michael@0: } michael@0: JS::Rooted jsMessage(cx, michael@0: JS_NewUCStringCopyN(cx, michael@0: static_cast(aMessage.BeginReading()), michael@0: aMessage.Length())); michael@0: NS_ENSURE_TRUE(jsMessage, NS_ERROR_OUT_OF_MEMORY); michael@0: JS::Rooted syncv(cx, JS::BooleanValue(aIsSync)); michael@0: JS_DefineProperty(cx, param, "target", targetv, JSPROP_ENUMERATE); michael@0: JS_DefineProperty(cx, param, "name", jsMessage, JSPROP_ENUMERATE); michael@0: JS_DefineProperty(cx, param, "sync", syncv, JSPROP_ENUMERATE); michael@0: JS_DefineProperty(cx, param, "json", json, JSPROP_ENUMERATE); // deprecated michael@0: JS_DefineProperty(cx, param, "data", json, JSPROP_ENUMERATE); michael@0: JS_DefineProperty(cx, param, "objects", cpowsv, JSPROP_ENUMERATE); michael@0: michael@0: // message.principal == null michael@0: if (!aPrincipal) { michael@0: JS_DefineProperty(cx, param, "principal", JS::UndefinedHandleValue, JSPROP_ENUMERATE); michael@0: } michael@0: michael@0: // message.principal = { appId: , origin: , isInBrowserElement: } michael@0: else { michael@0: JS::Rooted principalObj(cx, michael@0: JS_NewObject(cx, nullptr, JS::NullPtr(), JS::NullPtr())); michael@0: michael@0: uint32_t appId; michael@0: nsresult rv = aPrincipal->GetAppId(&appId); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: JS_DefineProperty(cx, principalObj, "appId", appId, JSPROP_ENUMERATE); michael@0: michael@0: nsCString origin; michael@0: rv = aPrincipal->GetOrigin(getter_Copies(origin)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: JS::Rooted originStr(cx, JS_NewStringCopyN(cx, origin.get(), origin.Length())); michael@0: NS_ENSURE_TRUE(originStr, NS_ERROR_OUT_OF_MEMORY); michael@0: JS_DefineProperty(cx, principalObj, "origin", originStr, JSPROP_ENUMERATE); michael@0: michael@0: bool browser; michael@0: rv = aPrincipal->GetIsInBrowserElement(&browser); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: JS::Rooted browserValue(cx, JS::BooleanValue(browser)); michael@0: JS_DefineProperty(cx, principalObj, "isInBrowserElement", browserValue, JSPROP_ENUMERATE); michael@0: michael@0: JS_DefineProperty(cx, param, "principal", principalObj, JSPROP_ENUMERATE); michael@0: } michael@0: michael@0: JS::Rooted thisValue(cx, JS::UndefinedValue()); michael@0: michael@0: JS::Rooted funval(cx); michael@0: if (JS_ObjectIsCallable(cx, object)) { michael@0: // If the listener is a JS function: michael@0: funval.setObject(*object); michael@0: michael@0: // A small hack to get 'this' value right on content side where michael@0: // messageManager is wrapped in TabChildGlobal. michael@0: nsCOMPtr defaultThisValue; michael@0: if (mChrome) { michael@0: defaultThisValue = do_QueryObject(this); michael@0: } else { michael@0: defaultThisValue = aTarget; michael@0: } michael@0: js::AssertSameCompartment(cx, object); michael@0: nsresult rv = nsContentUtils::WrapNative(cx, defaultThisValue, &thisValue); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } else { michael@0: // If the listener is a JS object which has receiveMessage function: michael@0: if (!JS_GetProperty(cx, object, "receiveMessage", &funval) || michael@0: !funval.isObject()) michael@0: return NS_ERROR_UNEXPECTED; michael@0: michael@0: // Check if the object is even callable. michael@0: NS_ENSURE_STATE(JS_ObjectIsCallable(cx, &funval.toObject())); michael@0: thisValue.setObject(*object); michael@0: } michael@0: michael@0: JS::Rooted rval(cx, JSVAL_VOID); michael@0: JS::Rooted argv(cx, JS::ObjectValue(*param)); michael@0: michael@0: { michael@0: JS::Rooted thisObject(cx, thisValue.toObjectOrNull()); michael@0: michael@0: JSAutoCompartment tac(cx, thisObject); michael@0: if (!JS_WrapValue(cx, &argv)) { michael@0: return NS_ERROR_UNEXPECTED; michael@0: } michael@0: michael@0: if (!JS_CallFunctionValue(cx, thisObject, funval, argv, &rval)) { michael@0: nsJSUtils::ReportPendingException(cx); michael@0: continue; michael@0: } michael@0: if (aJSONRetVal) { michael@0: nsString json; michael@0: if (!JS_Stringify(cx, &rval, JS::NullPtr(), JS::NullHandleValue, michael@0: JSONCreator, &json)) { michael@0: nsJSUtils::ReportPendingException(cx); michael@0: continue; michael@0: } michael@0: aJSONRetVal->AppendElement(json); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: nsRefPtr kungfuDeathGrip = mParentManager; michael@0: return mParentManager ? mParentManager->ReceiveMessage(aTarget, aMessage, michael@0: aIsSync, aCloneData, michael@0: aCpows, aPrincipal, michael@0: aJSONRetVal) : NS_OK; michael@0: } michael@0: michael@0: void michael@0: nsFrameMessageManager::AddChildManager(nsFrameMessageManager* aManager) michael@0: { michael@0: mChildManagers.AppendObject(aManager); michael@0: michael@0: nsRefPtr kungfuDeathGrip = this; michael@0: nsRefPtr kungfuDeathGrip2 = aManager; michael@0: // We have parent manager if we're a window message manager. michael@0: // In that case we want to load the pending scripts from global michael@0: // message manager. michael@0: if (mParentManager) { michael@0: nsRefPtr globalMM = mParentManager; michael@0: for (uint32_t i = 0; i < globalMM->mPendingScripts.Length(); ++i) { michael@0: aManager->LoadFrameScript(globalMM->mPendingScripts[i], false, michael@0: globalMM->mPendingScriptsGlobalStates[i]); michael@0: } michael@0: } michael@0: for (uint32_t i = 0; i < mPendingScripts.Length(); ++i) { michael@0: aManager->LoadFrameScript(mPendingScripts[i], false, michael@0: mPendingScriptsGlobalStates[i]); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsFrameMessageManager::SetCallback(MessageManagerCallback* aCallback) michael@0: { michael@0: MOZ_ASSERT(!mIsBroadcaster || !mCallback, michael@0: "Broadcasters cannot have callbacks!"); michael@0: if (aCallback && mCallback != aCallback) { michael@0: mCallback = aCallback; michael@0: if (mOwnsCallback) { michael@0: mOwnedCallback = aCallback; michael@0: } michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsFrameMessageManager::InitWithCallback(MessageManagerCallback* aCallback) michael@0: { michael@0: if (mCallback) { michael@0: // Initialization should only happen once. michael@0: return; michael@0: } michael@0: michael@0: SetCallback(aCallback); michael@0: michael@0: // First load global scripts by adding this to parent manager. michael@0: if (mParentManager) { michael@0: mParentManager->AddChildManager(this); michael@0: } michael@0: michael@0: for (uint32_t i = 0; i < mPendingScripts.Length(); ++i) { michael@0: LoadFrameScript(mPendingScripts[i], false, mPendingScriptsGlobalStates[i]); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsFrameMessageManager::RemoveFromParent() michael@0: { michael@0: if (mParentManager) { michael@0: mParentManager->RemoveChildManager(this); michael@0: } michael@0: mParentManager = nullptr; michael@0: mCallback = nullptr; michael@0: mOwnedCallback = nullptr; michael@0: } michael@0: michael@0: void michael@0: nsFrameMessageManager::Disconnect(bool aRemoveFromParent) michael@0: { michael@0: if (!mDisconnected) { michael@0: nsCOMPtr obs = mozilla::services::GetObserverService(); michael@0: if (obs) { michael@0: obs->NotifyObservers(NS_ISUPPORTS_CAST(nsIContentFrameMessageManager*, this), michael@0: "message-manager-disconnect", nullptr); michael@0: } michael@0: } michael@0: if (mParentManager && aRemoveFromParent) { michael@0: mParentManager->RemoveChildManager(this); michael@0: } michael@0: mDisconnected = true; michael@0: mParentManager = nullptr; michael@0: mCallback = nullptr; michael@0: mOwnedCallback = nullptr; michael@0: if (!mHandlingMessage) { michael@0: mListeners.Clear(); michael@0: } michael@0: } michael@0: michael@0: namespace { michael@0: michael@0: struct MessageManagerReferentCount michael@0: { michael@0: MessageManagerReferentCount() : mStrong(0), mWeakAlive(0), mWeakDead(0) {} michael@0: size_t mStrong; michael@0: size_t mWeakAlive; michael@0: size_t mWeakDead; michael@0: nsTArray mSuspectMessages; michael@0: nsDataHashtable mMessageCounter; michael@0: }; michael@0: michael@0: } // anonymous namespace michael@0: michael@0: namespace mozilla { michael@0: namespace dom { michael@0: michael@0: class MessageManagerReporter MOZ_FINAL : public nsIMemoryReporter michael@0: { michael@0: public: michael@0: NS_DECL_ISUPPORTS michael@0: NS_DECL_NSIMEMORYREPORTER michael@0: michael@0: static const size_t kSuspectReferentCount = 300; michael@0: protected: michael@0: void CountReferents(nsFrameMessageManager* aMessageManager, michael@0: MessageManagerReferentCount* aReferentCount); michael@0: }; michael@0: michael@0: NS_IMPL_ISUPPORTS(MessageManagerReporter, nsIMemoryReporter) michael@0: michael@0: static PLDHashOperator michael@0: CollectMessageListenerData(const nsAString& aKey, michael@0: nsAutoTObserverArray* aListeners, michael@0: void* aData) michael@0: { michael@0: MessageManagerReferentCount* referentCount = michael@0: static_cast(aData); michael@0: michael@0: uint32_t listenerCount = aListeners->Length(); michael@0: if (!listenerCount) { michael@0: return PL_DHASH_NEXT; michael@0: } michael@0: michael@0: nsString key(aKey); michael@0: uint32_t oldCount = 0; michael@0: referentCount->mMessageCounter.Get(key, &oldCount); michael@0: uint32_t currentCount = oldCount + listenerCount; michael@0: referentCount->mMessageCounter.Put(key, currentCount); michael@0: michael@0: // Keep track of messages that have a suspiciously large michael@0: // number of referents (symptom of leak). michael@0: if (currentCount == MessageManagerReporter::kSuspectReferentCount) { michael@0: referentCount->mSuspectMessages.AppendElement(key); michael@0: } michael@0: michael@0: for (uint32_t i = 0; i < listenerCount; ++i) { michael@0: const nsMessageListenerInfo& listenerInfo = michael@0: aListeners->ElementAt(i); michael@0: if (listenerInfo.mWeakListener) { michael@0: nsCOMPtr referent = michael@0: do_QueryReferent(listenerInfo.mWeakListener); michael@0: if (referent) { michael@0: referentCount->mWeakAlive++; michael@0: } else { michael@0: referentCount->mWeakDead++; michael@0: } michael@0: } else { michael@0: referentCount->mStrong++; michael@0: } michael@0: } michael@0: return PL_DHASH_NEXT; michael@0: } michael@0: michael@0: void michael@0: MessageManagerReporter::CountReferents(nsFrameMessageManager* aMessageManager, michael@0: MessageManagerReferentCount* aReferentCount) michael@0: { michael@0: aMessageManager->mListeners.EnumerateRead(CollectMessageListenerData, michael@0: aReferentCount); michael@0: michael@0: // Add referent count in child managers because the listeners michael@0: // participate in messages dispatched from parent message manager. michael@0: for (uint32_t i = 0; i < aMessageManager->mChildManagers.Length(); ++i) { michael@0: nsRefPtr mm = michael@0: static_cast(aMessageManager->mChildManagers[i]); michael@0: CountReferents(mm, aReferentCount); michael@0: } michael@0: } michael@0: michael@0: static nsresult michael@0: ReportReferentCount(const char* aManagerType, michael@0: const MessageManagerReferentCount& aReferentCount, michael@0: nsIMemoryReporterCallback* aCb, michael@0: nsISupports* aClosure) michael@0: { michael@0: #define REPORT(_path, _amount, _desc) \ michael@0: do { \ michael@0: nsresult rv; \ michael@0: rv = aCb->Callback(EmptyCString(), _path, \ michael@0: nsIMemoryReporter::KIND_OTHER, \ michael@0: nsIMemoryReporter::UNITS_COUNT, _amount, \ michael@0: _desc, aClosure); \ michael@0: NS_ENSURE_SUCCESS(rv, rv); \ michael@0: } while (0) michael@0: michael@0: REPORT(nsPrintfCString("message-manager/referent/%s/strong", aManagerType), michael@0: aReferentCount.mStrong, michael@0: nsPrintfCString("The number of strong referents held by the message " michael@0: "manager in the %s manager.", aManagerType)); michael@0: REPORT(nsPrintfCString("message-manager/referent/%s/weak/alive", aManagerType), michael@0: aReferentCount.mWeakAlive, michael@0: nsPrintfCString("The number of weak referents that are still alive " michael@0: "held by the message manager in the %s manager.", michael@0: aManagerType)); michael@0: REPORT(nsPrintfCString("message-manager/referent/%s/weak/dead", aManagerType), michael@0: aReferentCount.mWeakDead, michael@0: nsPrintfCString("The number of weak referents that are dead " michael@0: "held by the message manager in the %s manager.", michael@0: aManagerType)); michael@0: michael@0: for (uint32_t i = 0; i < aReferentCount.mSuspectMessages.Length(); i++) { michael@0: uint32_t totalReferentCount = 0; michael@0: aReferentCount.mMessageCounter.Get(aReferentCount.mSuspectMessages[i], michael@0: &totalReferentCount); michael@0: NS_ConvertUTF16toUTF8 suspect(aReferentCount.mSuspectMessages[i]); michael@0: REPORT(nsPrintfCString("message-manager-suspect/%s/referent(message=%s)", michael@0: aManagerType, suspect.get()), totalReferentCount, michael@0: nsPrintfCString("A message in the %s message manager with a " michael@0: "suspiciously large number of referents (symptom " michael@0: "of a leak).", aManagerType)); michael@0: } michael@0: michael@0: #undef REPORT michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: MessageManagerReporter::CollectReports(nsIMemoryReporterCallback* aCb, michael@0: nsISupports* aClosure) michael@0: { michael@0: nsresult rv; michael@0: michael@0: if (XRE_GetProcessType() == GeckoProcessType_Default) { michael@0: nsCOMPtr globalmm = michael@0: do_GetService("@mozilla.org/globalmessagemanager;1"); michael@0: if (globalmm) { michael@0: nsRefPtr mm = michael@0: static_cast(globalmm.get()); michael@0: MessageManagerReferentCount count; michael@0: CountReferents(mm, &count); michael@0: rv = ReportReferentCount("global-manager", count, aCb, aClosure); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: } michael@0: michael@0: if (nsFrameMessageManager::sParentProcessManager) { michael@0: MessageManagerReferentCount count; michael@0: CountReferents(nsFrameMessageManager::sParentProcessManager, &count); michael@0: rv = ReportReferentCount("parent-process-manager", count, aCb, aClosure); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: michael@0: if (nsFrameMessageManager::sChildProcessManager) { michael@0: MessageManagerReferentCount count; michael@0: CountReferents(nsFrameMessageManager::sChildProcessManager, &count); michael@0: rv = ReportReferentCount("child-process-manager", count, aCb, aClosure); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: } // namespace dom michael@0: } // namespace mozilla michael@0: michael@0: nsresult michael@0: NS_NewGlobalMessageManager(nsIMessageBroadcaster** aResult) michael@0: { michael@0: NS_ENSURE_TRUE(XRE_GetProcessType() == GeckoProcessType_Default, michael@0: NS_ERROR_NOT_AVAILABLE); michael@0: nsFrameMessageManager* mm = new nsFrameMessageManager(nullptr, michael@0: nullptr, michael@0: MM_CHROME | MM_GLOBAL | MM_BROADCASTER); michael@0: RegisterStrongMemoryReporter(new MessageManagerReporter()); michael@0: return CallQueryInterface(mm, aResult); michael@0: } michael@0: michael@0: nsDataHashtable* michael@0: nsFrameScriptExecutor::sCachedScripts = nullptr; michael@0: nsScriptCacheCleaner* nsFrameScriptExecutor::sScriptCacheCleaner = nullptr; michael@0: michael@0: void michael@0: nsFrameScriptExecutor::DidCreateGlobal() michael@0: { michael@0: NS_ASSERTION(mGlobal, "Should have mGlobal!"); michael@0: if (!sCachedScripts) { michael@0: sCachedScripts = michael@0: new nsDataHashtable; michael@0: michael@0: nsRefPtr scriptCacheCleaner = michael@0: new nsScriptCacheCleaner(); michael@0: scriptCacheCleaner.forget(&sScriptCacheCleaner); michael@0: } michael@0: } michael@0: michael@0: static PLDHashOperator michael@0: RemoveCachedScriptEntry(const nsAString& aKey, michael@0: nsFrameScriptObjectExecutorHolder*& aData, michael@0: void* aUserArg) michael@0: { michael@0: delete aData; michael@0: return PL_DHASH_REMOVE; michael@0: } michael@0: michael@0: // static michael@0: void michael@0: nsFrameScriptExecutor::Shutdown() michael@0: { michael@0: if (sCachedScripts) { michael@0: AutoSafeJSContext cx; michael@0: NS_ASSERTION(sCachedScripts != nullptr, "Need cached scripts"); michael@0: sCachedScripts->Enumerate(RemoveCachedScriptEntry, nullptr); michael@0: michael@0: delete sCachedScripts; michael@0: sCachedScripts = nullptr; michael@0: michael@0: nsRefPtr scriptCacheCleaner; michael@0: scriptCacheCleaner.swap(sScriptCacheCleaner); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsFrameScriptExecutor::LoadFrameScriptInternal(const nsAString& aURL, michael@0: bool aRunInGlobalScope) michael@0: { michael@0: if (!mGlobal || !sCachedScripts) { michael@0: return; michael@0: } michael@0: michael@0: AutoSafeJSContext cx; michael@0: JS::Rooted script(cx); michael@0: JS::Rooted funobj(cx); michael@0: michael@0: nsFrameScriptObjectExecutorHolder* holder = sCachedScripts->Get(aURL); michael@0: if (holder && holder->WillRunInGlobalScope() == aRunInGlobalScope) { michael@0: script = holder->mScript; michael@0: funobj = holder->mFunction; michael@0: } else { michael@0: // Don't put anything in the cache if we already have an entry michael@0: // with a different WillRunInGlobalScope() value. michael@0: bool shouldCache = !holder; michael@0: TryCacheLoadAndCompileScript(aURL, aRunInGlobalScope, michael@0: shouldCache, &script, &funobj); michael@0: } michael@0: michael@0: JS::Rooted global(cx, mGlobal->GetJSObject()); michael@0: if (global) { michael@0: JSAutoCompartment ac(cx, global); michael@0: bool ok = true; michael@0: if (funobj) { michael@0: JS::Rooted method(cx, JS_CloneFunctionObject(cx, funobj, global)); michael@0: if (!method) { michael@0: return; michael@0: } michael@0: JS::Rooted rval(cx); michael@0: JS::Rooted methodVal(cx, JS::ObjectValue(*method)); michael@0: ok = JS_CallFunctionValue(cx, global, methodVal, michael@0: JS::HandleValueArray::empty(), &rval); michael@0: } else if (script) { michael@0: ok = JS::CloneAndExecuteScript(cx, global, script); michael@0: } michael@0: michael@0: if (!ok) { michael@0: nsJSUtils::ReportPendingException(cx); michael@0: } michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsFrameScriptExecutor::TryCacheLoadAndCompileScript(const nsAString& aURL, michael@0: bool aRunInGlobalScope, michael@0: bool aShouldCache, michael@0: JS::MutableHandle aScriptp, michael@0: JS::MutableHandle aFunp) michael@0: { michael@0: nsCString url = NS_ConvertUTF16toUTF8(aURL); michael@0: nsCOMPtr uri; michael@0: nsresult rv = NS_NewURI(getter_AddRefs(uri), url); michael@0: if (NS_FAILED(rv)) { michael@0: return; michael@0: } michael@0: michael@0: bool hasFlags; michael@0: rv = NS_URIChainHasFlags(uri, michael@0: nsIProtocolHandler::URI_IS_LOCAL_RESOURCE, michael@0: &hasFlags); michael@0: if (NS_FAILED(rv) || !hasFlags) { michael@0: NS_WARNING("Will not load a frame script!"); michael@0: return; michael@0: } michael@0: michael@0: nsCOMPtr channel; michael@0: NS_NewChannel(getter_AddRefs(channel), uri); michael@0: if (!channel) { michael@0: return; michael@0: } michael@0: michael@0: nsCOMPtr input; michael@0: channel->Open(getter_AddRefs(input)); michael@0: nsString dataString; michael@0: jschar* dataStringBuf = nullptr; michael@0: size_t dataStringLength = 0; michael@0: uint64_t avail64 = 0; michael@0: if (input && NS_SUCCEEDED(input->Available(&avail64)) && avail64) { michael@0: if (avail64 > UINT32_MAX) { michael@0: return; michael@0: } michael@0: nsCString buffer; michael@0: uint32_t avail = (uint32_t)std::min(avail64, (uint64_t)UINT32_MAX); michael@0: if (NS_FAILED(NS_ReadInputStreamToString(input, buffer, avail))) { michael@0: return; michael@0: } michael@0: nsScriptLoader::ConvertToUTF16(channel, (uint8_t*)buffer.get(), avail, michael@0: EmptyString(), nullptr, michael@0: dataStringBuf, dataStringLength); michael@0: } michael@0: michael@0: JS::SourceBufferHolder srcBuf(dataStringBuf, dataStringLength, michael@0: JS::SourceBufferHolder::GiveOwnership); michael@0: michael@0: if (dataStringBuf && dataStringLength > 0) { michael@0: AutoSafeJSContext cx; michael@0: JS::Rooted global(cx, mGlobal->GetJSObject()); michael@0: if (global) { michael@0: JSAutoCompartment ac(cx, global); michael@0: JS::CompileOptions options(cx); michael@0: options.setFileAndLine(url.get(), 1); michael@0: JS::Rooted script(cx); michael@0: JS::Rooted funobj(cx); michael@0: if (aRunInGlobalScope) { michael@0: options.setNoScriptRval(true); michael@0: if (!JS::Compile(cx, JS::NullPtr(), options, srcBuf, &script)) { michael@0: return; michael@0: } michael@0: } else { michael@0: JS::Rooted fun(cx); michael@0: if (!JS::CompileFunction(cx, JS::NullPtr(), options, michael@0: nullptr, 0, nullptr, /* name, nargs, args */ michael@0: srcBuf, &fun)) michael@0: { michael@0: return; michael@0: } michael@0: funobj = JS_GetFunctionObject(fun); michael@0: } michael@0: michael@0: if (!script && !funobj) { michael@0: return; michael@0: } michael@0: michael@0: aScriptp.set(script); michael@0: aFunp.set(funobj); michael@0: michael@0: nsAutoCString scheme; michael@0: uri->GetScheme(scheme); michael@0: // We don't cache data: scripts! michael@0: if (aShouldCache && !scheme.EqualsLiteral("data")) { michael@0: nsFrameScriptObjectExecutorHolder* holder; michael@0: michael@0: // Root the object also for caching. michael@0: if (script) { michael@0: holder = new nsFrameScriptObjectExecutorHolder(cx, script); michael@0: } else { michael@0: holder = new nsFrameScriptObjectExecutorHolder(cx, funobj); michael@0: } michael@0: sCachedScripts->Put(aURL, holder); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsFrameScriptExecutor::TryCacheLoadAndCompileScript(const nsAString& aURL, michael@0: bool aRunInGlobalScope) michael@0: { michael@0: AutoSafeJSContext cx; michael@0: JS::Rooted script(cx); michael@0: JS::Rooted funobj(cx); michael@0: TryCacheLoadAndCompileScript(aURL, aRunInGlobalScope, true, &script, &funobj); michael@0: } michael@0: michael@0: bool michael@0: nsFrameScriptExecutor::InitTabChildGlobalInternal(nsISupports* aScope, michael@0: const nsACString& aID) michael@0: { michael@0: michael@0: nsCOMPtr runtimeSvc = michael@0: do_GetService("@mozilla.org/js/xpc/RuntimeService;1"); michael@0: NS_ENSURE_TRUE(runtimeSvc, false); michael@0: michael@0: JSRuntime* rt = nullptr; michael@0: runtimeSvc->GetRuntime(&rt); michael@0: NS_ENSURE_TRUE(rt, false); michael@0: michael@0: AutoSafeJSContext cx; michael@0: nsContentUtils::GetSecurityManager()->GetSystemPrincipal(getter_AddRefs(mPrincipal)); michael@0: michael@0: nsIXPConnect* xpc = nsContentUtils::XPConnect(); michael@0: const uint32_t flags = nsIXPConnect::INIT_JS_STANDARD_CLASSES; michael@0: michael@0: JS::CompartmentOptions options; michael@0: options.setZone(JS::SystemZone) michael@0: .setVersion(JSVERSION_LATEST); michael@0: michael@0: nsresult rv = michael@0: xpc->InitClassesWithNewWrappedGlobal(cx, aScope, mPrincipal, michael@0: flags, options, getter_AddRefs(mGlobal)); michael@0: NS_ENSURE_SUCCESS(rv, false); michael@0: michael@0: michael@0: JS::Rooted global(cx, mGlobal->GetJSObject()); michael@0: NS_ENSURE_TRUE(global, false); michael@0: michael@0: // Set the location information for the new global, so that tools like michael@0: // about:memory may use that information. michael@0: xpc::SetLocationForGlobal(global, aID); michael@0: michael@0: DidCreateGlobal(); michael@0: return true; michael@0: } michael@0: michael@0: NS_IMPL_ISUPPORTS(nsScriptCacheCleaner, nsIObserver) michael@0: michael@0: nsFrameMessageManager* nsFrameMessageManager::sChildProcessManager = nullptr; michael@0: nsFrameMessageManager* nsFrameMessageManager::sParentProcessManager = nullptr; michael@0: nsFrameMessageManager* nsFrameMessageManager::sSameProcessParentManager = nullptr; michael@0: nsTArray >* nsFrameMessageManager::sPendingSameProcessAsyncMessages = nullptr; michael@0: michael@0: class nsAsyncMessageToSameProcessChild : public nsSameProcessAsyncMessageBase, michael@0: public nsRunnable michael@0: { michael@0: public: michael@0: nsAsyncMessageToSameProcessChild(JSContext* aCx, michael@0: const nsAString& aMessage, michael@0: const StructuredCloneData& aData, michael@0: JS::Handle aCpows, michael@0: nsIPrincipal* aPrincipal) michael@0: : nsSameProcessAsyncMessageBase(aCx, aMessage, aData, aCpows, aPrincipal) michael@0: { michael@0: } michael@0: michael@0: NS_IMETHOD Run() michael@0: { michael@0: nsFrameMessageManager* ppm = nsFrameMessageManager::sChildProcessManager; michael@0: ReceiveMessage(static_cast(ppm), ppm); michael@0: return NS_OK; michael@0: } michael@0: }; michael@0: michael@0: michael@0: /** michael@0: * Send messages to an imaginary child process in a single-process scenario. michael@0: */ michael@0: class SameParentProcessMessageManagerCallback : public MessageManagerCallback michael@0: { michael@0: public: michael@0: SameParentProcessMessageManagerCallback() michael@0: { michael@0: MOZ_COUNT_CTOR(SameParentProcessMessageManagerCallback); michael@0: } michael@0: virtual ~SameParentProcessMessageManagerCallback() michael@0: { michael@0: MOZ_COUNT_DTOR(SameParentProcessMessageManagerCallback); 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: nsRefPtr ev = michael@0: new nsAsyncMessageToSameProcessChild(aCx, aMessage, aData, aCpows, michael@0: aPrincipal); michael@0: NS_DispatchToCurrentThread(ev); michael@0: return true; michael@0: } michael@0: michael@0: bool CheckPermission(const nsAString& aPermission) michael@0: { michael@0: // In a single-process scenario, the child always has all capabilities. michael@0: return true; michael@0: } michael@0: michael@0: bool CheckManifestURL(const nsAString& aManifestURL) michael@0: { michael@0: // In a single-process scenario, the child always has all capabilities. michael@0: return true; michael@0: } michael@0: michael@0: bool CheckAppHasPermission(const nsAString& aPermission) michael@0: { michael@0: // In a single-process scenario, the child always has all capabilities. michael@0: return true; michael@0: } michael@0: michael@0: virtual bool CheckAppHasStatus(unsigned short aStatus) michael@0: { michael@0: // In a single-process scenario, the child always has all capabilities. michael@0: return true; michael@0: } michael@0: }; michael@0: michael@0: michael@0: /** michael@0: * Send messages to the parent process. michael@0: */ michael@0: class ChildProcessMessageManagerCallback : public MessageManagerCallback michael@0: { michael@0: public: michael@0: ChildProcessMessageManagerCallback() michael@0: { michael@0: MOZ_COUNT_CTOR(ChildProcessMessageManagerCallback); michael@0: } michael@0: virtual ~ChildProcessMessageManagerCallback() michael@0: { michael@0: MOZ_COUNT_DTOR(ChildProcessMessageManagerCallback); michael@0: } michael@0: michael@0: virtual bool DoSendBlockingMessage(JSContext* aCx, michael@0: const nsAString& aMessage, michael@0: const mozilla::dom::StructuredCloneData& aData, michael@0: JS::Handle aCpows, michael@0: nsIPrincipal* aPrincipal, michael@0: InfallibleTArray* aJSONRetVal, michael@0: bool aIsSync) MOZ_OVERRIDE michael@0: { michael@0: mozilla::dom::ContentChild* cc = michael@0: mozilla::dom::ContentChild::GetSingleton(); michael@0: if (!cc) { michael@0: return true; michael@0: } michael@0: ClonedMessageData data; michael@0: if (!BuildClonedMessageDataForChild(cc, aData, data)) { michael@0: return false; michael@0: } michael@0: InfallibleTArray cpows; michael@0: if (!cc->GetCPOWManager()->Wrap(aCx, aCpows, &cpows)) { michael@0: return false; michael@0: } michael@0: if (aIsSync) { michael@0: return cc->SendSyncMessage(PromiseFlatString(aMessage), data, cpows, michael@0: aPrincipal, aJSONRetVal); michael@0: } michael@0: return cc->CallRpcMessage(PromiseFlatString(aMessage), data, cpows, michael@0: aPrincipal, aJSONRetVal); michael@0: } michael@0: michael@0: virtual bool DoSendAsyncMessage(JSContext* aCx, michael@0: const nsAString& aMessage, michael@0: const mozilla::dom::StructuredCloneData& aData, michael@0: JS::Handle aCpows, michael@0: nsIPrincipal* aPrincipal) MOZ_OVERRIDE michael@0: { michael@0: mozilla::dom::ContentChild* cc = michael@0: mozilla::dom::ContentChild::GetSingleton(); michael@0: if (!cc) { michael@0: return true; michael@0: } michael@0: ClonedMessageData data; michael@0: if (!BuildClonedMessageDataForChild(cc, aData, data)) { michael@0: return false; michael@0: } michael@0: InfallibleTArray cpows; michael@0: if (!cc->GetCPOWManager()->Wrap(aCx, aCpows, &cpows)) { michael@0: return false; michael@0: } michael@0: return cc->SendAsyncMessage(PromiseFlatString(aMessage), data, cpows, michael@0: aPrincipal); michael@0: } michael@0: michael@0: }; michael@0: michael@0: michael@0: class nsAsyncMessageToSameProcessParent : public nsSameProcessAsyncMessageBase, michael@0: public nsRunnable michael@0: { michael@0: public: michael@0: nsAsyncMessageToSameProcessParent(JSContext* aCx, michael@0: const nsAString& aMessage, michael@0: const StructuredCloneData& aData, michael@0: JS::Handle aCpows, michael@0: nsIPrincipal* aPrincipal) michael@0: : nsSameProcessAsyncMessageBase(aCx, aMessage, aData, aCpows, aPrincipal) michael@0: { michael@0: } michael@0: michael@0: NS_IMETHOD Run() michael@0: { michael@0: if (nsFrameMessageManager::sPendingSameProcessAsyncMessages) { michael@0: nsFrameMessageManager::sPendingSameProcessAsyncMessages->RemoveElement(this); michael@0: } michael@0: nsFrameMessageManager* ppm = nsFrameMessageManager::sSameProcessParentManager; michael@0: ReceiveMessage(static_cast(ppm), ppm); michael@0: return NS_OK; michael@0: } michael@0: }; michael@0: michael@0: /** michael@0: * Send messages to the imaginary parent process in a single-process scenario. michael@0: */ michael@0: class SameChildProcessMessageManagerCallback : public MessageManagerCallback michael@0: { michael@0: public: michael@0: SameChildProcessMessageManagerCallback() michael@0: { michael@0: MOZ_COUNT_CTOR(SameChildProcessMessageManagerCallback); michael@0: } michael@0: virtual ~SameChildProcessMessageManagerCallback() michael@0: { michael@0: MOZ_COUNT_DTOR(SameChildProcessMessageManagerCallback); michael@0: } michael@0: michael@0: virtual bool DoSendBlockingMessage(JSContext* aCx, michael@0: const nsAString& aMessage, michael@0: const mozilla::dom::StructuredCloneData& aData, michael@0: JS::Handle aCpows, michael@0: nsIPrincipal* aPrincipal, michael@0: InfallibleTArray* aJSONRetVal, michael@0: bool aIsSync) MOZ_OVERRIDE michael@0: { michael@0: nsTArray > asyncMessages; michael@0: if (nsFrameMessageManager::sPendingSameProcessAsyncMessages) { michael@0: asyncMessages.SwapElements(*nsFrameMessageManager::sPendingSameProcessAsyncMessages); michael@0: uint32_t len = asyncMessages.Length(); michael@0: for (uint32_t i = 0; i < len; ++i) { michael@0: nsCOMPtr async = asyncMessages[i]; michael@0: async->Run(); michael@0: } michael@0: } michael@0: if (nsFrameMessageManager::sSameProcessParentManager) { michael@0: SameProcessCpowHolder cpows(js::GetRuntime(aCx), aCpows); michael@0: nsRefPtr ppm = nsFrameMessageManager::sSameProcessParentManager; michael@0: ppm->ReceiveMessage(static_cast(ppm.get()), aMessage, michael@0: true, &aData, &cpows, aPrincipal, aJSONRetVal); 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 mozilla::dom::StructuredCloneData& aData, michael@0: JS::Handle aCpows, michael@0: nsIPrincipal* aPrincipal) michael@0: { michael@0: if (!nsFrameMessageManager::sPendingSameProcessAsyncMessages) { michael@0: nsFrameMessageManager::sPendingSameProcessAsyncMessages = new nsTArray >; michael@0: } michael@0: nsCOMPtr ev = michael@0: new nsAsyncMessageToSameProcessParent(aCx, aMessage, aData, aCpows, aPrincipal); michael@0: nsFrameMessageManager::sPendingSameProcessAsyncMessages->AppendElement(ev); michael@0: NS_DispatchToCurrentThread(ev); michael@0: return true; michael@0: } michael@0: michael@0: }; michael@0: michael@0: michael@0: // This creates the global parent process message manager. michael@0: nsresult michael@0: NS_NewParentProcessMessageManager(nsIMessageBroadcaster** aResult) michael@0: { michael@0: NS_ASSERTION(!nsFrameMessageManager::sParentProcessManager, michael@0: "Re-creating sParentProcessManager"); michael@0: NS_ENSURE_TRUE(XRE_GetProcessType() == GeckoProcessType_Default, michael@0: NS_ERROR_NOT_AVAILABLE); michael@0: nsRefPtr mm = new nsFrameMessageManager(nullptr, michael@0: nullptr, michael@0: MM_CHROME | MM_PROCESSMANAGER | MM_BROADCASTER); michael@0: nsFrameMessageManager::sParentProcessManager = mm; michael@0: nsFrameMessageManager::NewProcessMessageManager(nullptr); // Create same process message manager. michael@0: return CallQueryInterface(mm, aResult); michael@0: } michael@0: michael@0: michael@0: nsFrameMessageManager* michael@0: nsFrameMessageManager::NewProcessMessageManager(mozilla::dom::ContentParent* aProcess) michael@0: { michael@0: if (!nsFrameMessageManager::sParentProcessManager) { michael@0: nsCOMPtr dummy = michael@0: do_GetService("@mozilla.org/parentprocessmessagemanager;1"); michael@0: } michael@0: michael@0: MOZ_ASSERT(nsFrameMessageManager::sParentProcessManager, michael@0: "parent process manager not created"); michael@0: nsFrameMessageManager* mm; michael@0: if (aProcess) { michael@0: mm = new nsFrameMessageManager(aProcess, michael@0: nsFrameMessageManager::sParentProcessManager, michael@0: MM_CHROME | MM_PROCESSMANAGER); michael@0: } else { michael@0: mm = new nsFrameMessageManager(new SameParentProcessMessageManagerCallback(), michael@0: nsFrameMessageManager::sParentProcessManager, michael@0: MM_CHROME | MM_PROCESSMANAGER | MM_OWNSCALLBACK); michael@0: sSameProcessParentManager = mm; michael@0: } michael@0: return mm; michael@0: } michael@0: michael@0: nsresult michael@0: NS_NewChildProcessMessageManager(nsISyncMessageSender** aResult) michael@0: { michael@0: NS_ASSERTION(!nsFrameMessageManager::sChildProcessManager, michael@0: "Re-creating sChildProcessManager"); michael@0: michael@0: MessageManagerCallback* cb; michael@0: if (XRE_GetProcessType() == GeckoProcessType_Default) { michael@0: cb = new SameChildProcessMessageManagerCallback(); michael@0: } else { michael@0: cb = new ChildProcessMessageManagerCallback(); michael@0: RegisterStrongMemoryReporter(new MessageManagerReporter()); michael@0: } michael@0: nsFrameMessageManager* mm = new nsFrameMessageManager(cb, michael@0: nullptr, michael@0: MM_PROCESSMANAGER | MM_OWNSCALLBACK); michael@0: nsFrameMessageManager::sChildProcessManager = mm; michael@0: return CallQueryInterface(mm, aResult); michael@0: } michael@0: michael@0: static PLDHashOperator michael@0: CycleCollectorMarkListeners(const nsAString& aKey, michael@0: nsAutoTObserverArray* aListeners, michael@0: void* aData) michael@0: { michael@0: uint32_t count = aListeners->Length(); michael@0: for (uint32_t i = 0; i < count; i++) { michael@0: if (aListeners->ElementAt(i).mStrongListener) { michael@0: xpc_TryUnmarkWrappedGrayObject(aListeners->ElementAt(i).mStrongListener); michael@0: } michael@0: } michael@0: return PL_DHASH_NEXT; michael@0: } michael@0: michael@0: bool michael@0: nsFrameMessageManager::MarkForCC() michael@0: { michael@0: mListeners.EnumerateRead(CycleCollectorMarkListeners, nullptr); michael@0: michael@0: if (mRefCnt.IsPurple()) { michael@0: mRefCnt.RemovePurple(); michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: nsSameProcessAsyncMessageBase::nsSameProcessAsyncMessageBase(JSContext* aCx, michael@0: const nsAString& aMessage, michael@0: const StructuredCloneData& aData, michael@0: JS::Handle aCpows, michael@0: nsIPrincipal* aPrincipal) michael@0: : mRuntime(js::GetRuntime(aCx)), michael@0: mMessage(aMessage), michael@0: mCpows(aCx, aCpows), michael@0: mPrincipal(aPrincipal) michael@0: { michael@0: if (aData.mDataLength && !mData.copy(aData.mData, aData.mDataLength)) { michael@0: NS_RUNTIMEABORT("OOM"); michael@0: } michael@0: mClosure = aData.mClosure; michael@0: } michael@0: michael@0: void michael@0: nsSameProcessAsyncMessageBase::ReceiveMessage(nsISupports* aTarget, michael@0: nsFrameMessageManager* aManager) michael@0: { michael@0: if (aManager) { michael@0: StructuredCloneData data; michael@0: data.mData = mData.data(); michael@0: data.mDataLength = mData.nbytes(); michael@0: data.mClosure = mClosure; michael@0: michael@0: SameProcessCpowHolder cpows(mRuntime, mCpows); michael@0: michael@0: nsRefPtr mm = aManager; michael@0: mm->ReceiveMessage(aTarget, mMessage, false, &data, &cpows, michael@0: mPrincipal, nullptr); michael@0: } michael@0: }