1222 lines
40 KiB
C++
1222 lines
40 KiB
C++
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* 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/. */
|
|
|
|
#include "base/basictypes.h"
|
|
|
|
#include "nsFrameMessageManager.h"
|
|
|
|
#include "ContentChild.h"
|
|
#include "ContentParent.h"
|
|
#include "nsContentUtils.h"
|
|
#include "nsError.h"
|
|
#include "nsIXPConnect.h"
|
|
#include "jsapi.h"
|
|
#include "nsJSUtils.h"
|
|
#include "nsJSPrincipals.h"
|
|
#include "nsNetUtil.h"
|
|
#include "nsScriptLoader.h"
|
|
#include "nsIJSContextStack.h"
|
|
#include "nsIXULRuntime.h"
|
|
#include "nsIScriptError.h"
|
|
#include "nsIConsoleService.h"
|
|
#include "nsIProtocolHandler.h"
|
|
#include "nsIScriptSecurityManager.h"
|
|
#include "nsIJSRuntimeService.h"
|
|
#include "nsIDOMFile.h"
|
|
#include "xpcpublic.h"
|
|
#include "mozilla/Preferences.h"
|
|
#include "mozilla/dom/StructuredCloneUtils.h"
|
|
|
|
#ifdef ANDROID
|
|
#include <android/log.h>
|
|
#endif
|
|
#ifdef XP_WIN
|
|
#include <windows.h>
|
|
#endif
|
|
|
|
using namespace mozilla;
|
|
using namespace mozilla::dom;
|
|
|
|
static bool
|
|
IsChromeProcess()
|
|
{
|
|
nsCOMPtr<nsIXULRuntime> rt = do_GetService("@mozilla.org/xre/runtime;1");
|
|
if (!rt)
|
|
return true;
|
|
|
|
PRUint32 type;
|
|
rt->GetProcessType(&type);
|
|
return type == nsIXULRuntime::PROCESS_TYPE_DEFAULT;
|
|
}
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_CLASS(nsFrameMessageManager)
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsFrameMessageManager)
|
|
PRUint32 count = tmp->mListeners.Length();
|
|
for (PRUint32 i = 0; i < count; i++) {
|
|
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mListeners[i] mListener");
|
|
cb.NoteXPCOMChild(tmp->mListeners[i].mListener.get());
|
|
}
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMARRAY(mChildManagers)
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsFrameMessageManager)
|
|
tmp->mListeners.Clear();
|
|
for (PRInt32 i = tmp->mChildManagers.Count(); i > 0; --i) {
|
|
static_cast<nsFrameMessageManager*>(tmp->mChildManagers[i - 1])->
|
|
Disconnect(false);
|
|
}
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMARRAY(mChildManagers)
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
|
|
|
|
|
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsFrameMessageManager)
|
|
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContentFrameMessageManager)
|
|
NS_INTERFACE_MAP_ENTRY_AGGREGATED(nsIFrameMessageManager,
|
|
(mChrome ?
|
|
static_cast<nsIFrameMessageManager*>(
|
|
static_cast<nsIChromeFrameMessageManager*>(this)) :
|
|
static_cast<nsIFrameMessageManager*>(
|
|
static_cast<nsIContentFrameMessageManager*>(this))))
|
|
/* nsIContentFrameMessageManager is accessible only in TabChildGlobal. */
|
|
NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIContentFrameMessageManager,
|
|
!mChrome && !mIsProcessManager)
|
|
/* Message managers in child process support nsISyncMessageSender. */
|
|
NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsISyncMessageSender, !mChrome)
|
|
/* Message managers in chrome process support nsITreeItemFrameMessageManager. */
|
|
NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsITreeItemFrameMessageManager, mChrome)
|
|
/* Process message manager doesn't support nsIChromeFrameMessageManager. */
|
|
NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIChromeFrameMessageManager,
|
|
mChrome && !mIsProcessManager)
|
|
NS_INTERFACE_MAP_END
|
|
|
|
NS_IMPL_CYCLE_COLLECTING_ADDREF(nsFrameMessageManager)
|
|
NS_IMPL_CYCLE_COLLECTING_RELEASE(nsFrameMessageManager)
|
|
|
|
NS_IMETHODIMP
|
|
nsFrameMessageManager::AddMessageListener(const nsAString& aMessage,
|
|
nsIFrameMessageListener* aListener)
|
|
{
|
|
nsCOMPtr<nsIAtom> message = do_GetAtom(aMessage);
|
|
PRUint32 len = mListeners.Length();
|
|
for (PRUint32 i = 0; i < len; ++i) {
|
|
if (mListeners[i].mMessage == message &&
|
|
mListeners[i].mListener == aListener) {
|
|
return NS_OK;
|
|
}
|
|
}
|
|
nsMessageListenerInfo* entry = mListeners.AppendElement();
|
|
NS_ENSURE_TRUE(entry, NS_ERROR_OUT_OF_MEMORY);
|
|
entry->mMessage = message;
|
|
entry->mListener = aListener;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsFrameMessageManager::RemoveMessageListener(const nsAString& aMessage,
|
|
nsIFrameMessageListener* aListener)
|
|
{
|
|
nsCOMPtr<nsIAtom> message = do_GetAtom(aMessage);
|
|
PRUint32 len = mListeners.Length();
|
|
for (PRUint32 i = 0; i < len; ++i) {
|
|
if (mListeners[i].mMessage == message &&
|
|
mListeners[i].mListener == aListener) {
|
|
mListeners.RemoveElementAt(i);
|
|
return NS_OK;
|
|
}
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsFrameMessageManager::LoadFrameScript(const nsAString& aURL,
|
|
bool aAllowDelayedLoad)
|
|
{
|
|
if (aAllowDelayedLoad) {
|
|
if (IsGlobal() || IsWindowLevel()) {
|
|
// Cache for future windows or frames
|
|
mPendingScripts.AppendElement(aURL);
|
|
} else if (!mCallbackData) {
|
|
// We're frame message manager, which isn't connected yet.
|
|
mPendingScripts.AppendElement(aURL);
|
|
return NS_OK;
|
|
}
|
|
}
|
|
|
|
if (mCallbackData) {
|
|
#ifdef DEBUG_smaug
|
|
printf("Will load %s \n", NS_ConvertUTF16toUTF8(aURL).get());
|
|
#endif
|
|
NS_ENSURE_TRUE(mLoadScriptCallback(mCallbackData, aURL), NS_ERROR_FAILURE);
|
|
}
|
|
|
|
for (PRInt32 i = 0; i < mChildManagers.Count(); ++i) {
|
|
nsRefPtr<nsFrameMessageManager> mm =
|
|
static_cast<nsFrameMessageManager*>(mChildManagers[i]);
|
|
if (mm) {
|
|
// Use false here, so that child managers don't cache the script, which
|
|
// is already cached in the parent.
|
|
mm->LoadFrameScript(aURL, false);
|
|
}
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsFrameMessageManager::RemoveDelayedFrameScript(const nsAString& aURL)
|
|
{
|
|
mPendingScripts.RemoveElement(aURL);
|
|
return NS_OK;
|
|
}
|
|
|
|
static JSBool
|
|
JSONCreator(const jschar* aBuf, uint32_t aLen, void* aData)
|
|
{
|
|
nsAString* result = static_cast<nsAString*>(aData);
|
|
result->Append(static_cast<const PRUnichar*>(aBuf),
|
|
static_cast<PRUint32>(aLen));
|
|
return true;
|
|
}
|
|
|
|
static bool
|
|
GetParamsForMessage(JSContext* aCx,
|
|
const jsval& aObject,
|
|
JSAutoStructuredCloneBuffer& aBuffer,
|
|
StructuredCloneClosure& aClosure)
|
|
{
|
|
if (WriteStructuredClone(aCx, aObject, aBuffer, aClosure)) {
|
|
return true;
|
|
}
|
|
JS_ClearPendingException(aCx);
|
|
|
|
// Not clonable, try JSON
|
|
//XXX This is ugly but currently structured cloning doesn't handle
|
|
// properly cases when interface is implemented in JS and used
|
|
// as a dictionary.
|
|
nsAutoString json;
|
|
JSAutoRequest ar(aCx);
|
|
jsval v = aObject;
|
|
NS_ENSURE_TRUE(JS_Stringify(aCx, &v, nullptr, JSVAL_NULL, JSONCreator, &json), false);
|
|
NS_ENSURE_TRUE(!json.IsEmpty(), false);
|
|
|
|
jsval val = JSVAL_NULL;
|
|
NS_ENSURE_TRUE(JS_ParseJSON(aCx, static_cast<const jschar*>(PromiseFlatString(json).get()),
|
|
json.Length(), &val), false);
|
|
|
|
return WriteStructuredClone(aCx, val, aBuffer, aClosure);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsFrameMessageManager::SendSyncMessage(const nsAString& aMessageName,
|
|
const jsval& aObject,
|
|
JSContext* aCx,
|
|
PRUint8 aArgc,
|
|
jsval* aRetval)
|
|
{
|
|
NS_ASSERTION(!IsGlobal(), "Should not call SendSyncMessage in chrome");
|
|
NS_ASSERTION(!IsWindowLevel(), "Should not call SendSyncMessage in chrome");
|
|
NS_ASSERTION(!mParentManager, "Should not have parent manager in content!");
|
|
*aRetval = JSVAL_VOID;
|
|
if (mSyncCallback) {
|
|
NS_ENSURE_TRUE(mCallbackData, NS_ERROR_NOT_INITIALIZED);
|
|
StructuredCloneData data;
|
|
JSAutoStructuredCloneBuffer buffer;
|
|
if (aArgc >= 2 &&
|
|
!GetParamsForMessage(aCx, aObject, buffer, data.mClosure)) {
|
|
return NS_ERROR_DOM_DATA_CLONE_ERR;
|
|
}
|
|
data.mData = buffer.data();
|
|
data.mDataLength = buffer.nbytes();
|
|
|
|
InfallibleTArray<nsString> retval;
|
|
if (mSyncCallback(mCallbackData, aMessageName, data, &retval)) {
|
|
JSAutoRequest ar(aCx);
|
|
PRUint32 len = retval.Length();
|
|
JSObject* dataArray = JS_NewArrayObject(aCx, len, NULL);
|
|
NS_ENSURE_TRUE(dataArray, NS_ERROR_OUT_OF_MEMORY);
|
|
|
|
for (PRUint32 i = 0; i < len; ++i) {
|
|
if (retval[i].IsEmpty()) {
|
|
continue;
|
|
}
|
|
|
|
jsval ret = JSVAL_VOID;
|
|
if (!JS_ParseJSON(aCx, static_cast<const jschar*>(retval[i].get()),
|
|
retval[i].Length(), &ret)) {
|
|
return NS_ERROR_UNEXPECTED;
|
|
}
|
|
NS_ENSURE_TRUE(JS_SetElement(aCx, dataArray, i, &ret), NS_ERROR_OUT_OF_MEMORY);
|
|
}
|
|
|
|
*aRetval = OBJECT_TO_JSVAL(dataArray);
|
|
}
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
nsFrameMessageManager::SendAsyncMessageInternal(const nsAString& aMessage,
|
|
const StructuredCloneData& aData)
|
|
{
|
|
if (mAsyncCallback) {
|
|
NS_ENSURE_TRUE(mCallbackData, NS_ERROR_NOT_INITIALIZED);
|
|
mAsyncCallback(mCallbackData, aMessage, aData);
|
|
}
|
|
PRInt32 len = mChildManagers.Count();
|
|
for (PRInt32 i = 0; i < len; ++i) {
|
|
static_cast<nsFrameMessageManager*>(mChildManagers[i])->
|
|
SendAsyncMessageInternal(aMessage, aData);
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsFrameMessageManager::SendAsyncMessage(const nsAString& aMessageName,
|
|
const jsval& aObject,
|
|
JSContext* aCx,
|
|
PRUint8 aArgc)
|
|
{
|
|
StructuredCloneData data;
|
|
JSAutoStructuredCloneBuffer buffer;
|
|
|
|
if (aArgc >= 2 &&
|
|
!GetParamsForMessage(aCx, aObject, buffer, data.mClosure)) {
|
|
return NS_ERROR_DOM_DATA_CLONE_ERR;
|
|
}
|
|
|
|
data.mData = buffer.data();
|
|
data.mDataLength = buffer.nbytes();
|
|
|
|
return SendAsyncMessageInternal(aMessageName, data);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsFrameMessageManager::Dump(const nsAString& aStr)
|
|
{
|
|
#ifdef ANDROID
|
|
__android_log_print(ANDROID_LOG_INFO, "Gecko", "%s", NS_ConvertUTF16toUTF8(aStr).get());
|
|
#endif
|
|
#ifdef XP_WIN
|
|
if (IsDebuggerPresent()) {
|
|
OutputDebugStringW(PromiseFlatString(aStr).get());
|
|
}
|
|
#endif
|
|
fputs(NS_ConvertUTF16toUTF8(aStr).get(), stdout);
|
|
fflush(stdout);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsFrameMessageManager::PrivateNoteIntentionalCrash()
|
|
{
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsFrameMessageManager::GetContent(nsIDOMWindow** aContent)
|
|
{
|
|
*aContent = nullptr;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsFrameMessageManager::GetDocShell(nsIDocShell** aDocShell)
|
|
{
|
|
*aDocShell = nullptr;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsFrameMessageManager::GetChildCount(PRUint32* aChildCount)
|
|
{
|
|
*aChildCount = static_cast<PRUint32>(mChildManagers.Count());
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsFrameMessageManager::GetChildAt(PRUint32 aIndex,
|
|
nsITreeItemFrameMessageManager** aMM)
|
|
{
|
|
*aMM = nullptr;
|
|
nsCOMPtr<nsITreeItemFrameMessageManager> mm =
|
|
do_QueryInterface(mChildManagers.SafeObjectAt(static_cast<PRUint32>(aIndex)));
|
|
mm.swap(*aMM);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsFrameMessageManager::Btoa(const nsAString& aBinaryData,
|
|
nsAString& aAsciiBase64String)
|
|
{
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsFrameMessageManager::Atob(const nsAString& aAsciiString,
|
|
nsAString& aBinaryData)
|
|
{
|
|
return NS_OK;
|
|
}
|
|
|
|
class MMListenerRemover
|
|
{
|
|
public:
|
|
MMListenerRemover(nsFrameMessageManager* aMM)
|
|
: mWasHandlingMessage(aMM->mHandlingMessage)
|
|
, mMM(aMM)
|
|
{
|
|
mMM->mHandlingMessage = true;
|
|
}
|
|
~MMListenerRemover()
|
|
{
|
|
if (!mWasHandlingMessage) {
|
|
mMM->mHandlingMessage = false;
|
|
if (mMM->mDisconnected) {
|
|
mMM->mListeners.Clear();
|
|
}
|
|
}
|
|
}
|
|
|
|
bool mWasHandlingMessage;
|
|
nsRefPtr<nsFrameMessageManager> mMM;
|
|
};
|
|
|
|
nsresult
|
|
nsFrameMessageManager::ReceiveMessage(nsISupports* aTarget,
|
|
const nsAString& aMessage,
|
|
bool aSync,
|
|
const StructuredCloneData* aCloneData,
|
|
JSObject* aObjectsArray,
|
|
InfallibleTArray<nsString>* aJSONRetVal,
|
|
JSContext* aContext)
|
|
{
|
|
JSContext* ctx = mContext ? mContext : aContext;
|
|
if (!ctx) {
|
|
ctx = nsContentUtils::ThreadJSContextStack()->GetSafeJSContext();
|
|
}
|
|
if (mListeners.Length()) {
|
|
nsCOMPtr<nsIAtom> name = do_GetAtom(aMessage);
|
|
MMListenerRemover lr(this);
|
|
|
|
for (PRUint32 i = 0; i < mListeners.Length(); ++i) {
|
|
if (mListeners[i].mMessage == name) {
|
|
nsCOMPtr<nsIXPConnectWrappedJS> wrappedJS =
|
|
do_QueryInterface(mListeners[i].mListener);
|
|
if (!wrappedJS) {
|
|
continue;
|
|
}
|
|
JSObject* object = nullptr;
|
|
wrappedJS->GetJSObject(&object);
|
|
if (!object) {
|
|
continue;
|
|
}
|
|
nsCxPusher pusher;
|
|
NS_ENSURE_STATE(pusher.Push(ctx, false));
|
|
|
|
JSAutoRequest ar(ctx);
|
|
|
|
JSAutoEnterCompartment ac;
|
|
if (!ac.enter(ctx, object))
|
|
return NS_ERROR_FAILURE;
|
|
|
|
// The parameter for the listener function.
|
|
JSObject* param = JS_NewObject(ctx, NULL, NULL, NULL);
|
|
NS_ENSURE_TRUE(param, NS_ERROR_OUT_OF_MEMORY);
|
|
|
|
jsval targetv;
|
|
nsContentUtils::WrapNative(ctx,
|
|
JS_GetGlobalForObject(ctx, object),
|
|
aTarget, &targetv, nullptr, true);
|
|
|
|
// To keep compatibility with e10s message manager,
|
|
// define empty objects array.
|
|
if (!aObjectsArray) {
|
|
// Because we want JS messages to have always the same properties,
|
|
// create array even if len == 0.
|
|
aObjectsArray = JS_NewArrayObject(ctx, 0, NULL);
|
|
if (!aObjectsArray) {
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
}
|
|
|
|
JS::AutoValueRooter objectsv(ctx);
|
|
objectsv.set(OBJECT_TO_JSVAL(aObjectsArray));
|
|
if (!JS_WrapValue(ctx, objectsv.jsval_addr()))
|
|
return NS_ERROR_UNEXPECTED;
|
|
|
|
jsval json = JSVAL_NULL;
|
|
if (aCloneData && aCloneData->mDataLength &&
|
|
!ReadStructuredClone(ctx, *aCloneData, &json)) {
|
|
JS_ClearPendingException(ctx);
|
|
return NS_OK;
|
|
}
|
|
JSString* jsMessage =
|
|
JS_NewUCStringCopyN(ctx,
|
|
static_cast<const jschar*>(PromiseFlatString(aMessage).get()),
|
|
aMessage.Length());
|
|
NS_ENSURE_TRUE(jsMessage, NS_ERROR_OUT_OF_MEMORY);
|
|
JS_DefineProperty(ctx, param, "target", targetv, NULL, NULL, JSPROP_ENUMERATE);
|
|
JS_DefineProperty(ctx, param, "name",
|
|
STRING_TO_JSVAL(jsMessage), NULL, NULL, JSPROP_ENUMERATE);
|
|
JS_DefineProperty(ctx, param, "sync",
|
|
BOOLEAN_TO_JSVAL(aSync), NULL, NULL, JSPROP_ENUMERATE);
|
|
JS_DefineProperty(ctx, param, "json", json, NULL, NULL, JSPROP_ENUMERATE); // deprecated
|
|
JS_DefineProperty(ctx, param, "data", json, NULL, NULL, JSPROP_ENUMERATE);
|
|
JS_DefineProperty(ctx, param, "objects", objectsv.jsval_value(), NULL, NULL, JSPROP_ENUMERATE);
|
|
|
|
jsval thisValue = JSVAL_VOID;
|
|
|
|
JS::Value funval;
|
|
if (JS_ObjectIsCallable(ctx, object)) {
|
|
// If the listener is a JS function:
|
|
funval.setObject(*object);
|
|
|
|
// A small hack to get 'this' value right on content side where
|
|
// messageManager is wrapped in TabChildGlobal.
|
|
nsCOMPtr<nsISupports> defaultThisValue;
|
|
if (mChrome) {
|
|
defaultThisValue = do_QueryObject(this);
|
|
} else {
|
|
defaultThisValue = aTarget;
|
|
}
|
|
nsContentUtils::WrapNative(ctx,
|
|
JS_GetGlobalForObject(ctx, object),
|
|
defaultThisValue, &thisValue, nullptr, true);
|
|
} else {
|
|
// If the listener is a JS object which has receiveMessage function:
|
|
if (!JS_GetProperty(ctx, object, "receiveMessage", &funval) ||
|
|
!funval.isObject())
|
|
return NS_ERROR_UNEXPECTED;
|
|
|
|
// Check if the object is even callable.
|
|
NS_ENSURE_STATE(JS_ObjectIsCallable(ctx, &funval.toObject()));
|
|
thisValue.setObject(*object);
|
|
}
|
|
|
|
jsval rval = JSVAL_VOID;
|
|
|
|
JS::AutoValueRooter argv(ctx);
|
|
argv.set(OBJECT_TO_JSVAL(param));
|
|
|
|
{
|
|
JSAutoEnterCompartment tac;
|
|
|
|
JSObject* thisObject = JSVAL_TO_OBJECT(thisValue);
|
|
|
|
if (!tac.enter(ctx, thisObject) ||
|
|
!JS_WrapValue(ctx, argv.jsval_addr()))
|
|
return NS_ERROR_UNEXPECTED;
|
|
|
|
JS_CallFunctionValue(ctx, thisObject,
|
|
funval, 1, argv.jsval_addr(), &rval);
|
|
if (aJSONRetVal) {
|
|
nsString json;
|
|
if (JS_Stringify(ctx, &rval, nullptr, JSVAL_NULL,
|
|
JSONCreator, &json)) {
|
|
aJSONRetVal->AppendElement(json);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
nsRefPtr<nsFrameMessageManager> kungfuDeathGrip = mParentManager;
|
|
return mParentManager ? mParentManager->ReceiveMessage(aTarget, aMessage,
|
|
aSync, aCloneData,
|
|
aObjectsArray,
|
|
aJSONRetVal, mContext) : NS_OK;
|
|
}
|
|
|
|
void
|
|
nsFrameMessageManager::AddChildManager(nsFrameMessageManager* aManager,
|
|
bool aLoadScripts)
|
|
{
|
|
mChildManagers.AppendObject(aManager);
|
|
if (aLoadScripts) {
|
|
nsRefPtr<nsFrameMessageManager> kungfuDeathGrip = this;
|
|
nsRefPtr<nsFrameMessageManager> kungfuDeathGrip2 = aManager;
|
|
// We have parent manager if we're a window message manager.
|
|
// In that case we want to load the pending scripts from global
|
|
// message manager.
|
|
if (mParentManager) {
|
|
nsRefPtr<nsFrameMessageManager> globalMM = mParentManager;
|
|
for (PRUint32 i = 0; i < globalMM->mPendingScripts.Length(); ++i) {
|
|
aManager->LoadFrameScript(globalMM->mPendingScripts[i], false);
|
|
}
|
|
}
|
|
for (PRUint32 i = 0; i < mPendingScripts.Length(); ++i) {
|
|
aManager->LoadFrameScript(mPendingScripts[i], false);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
nsFrameMessageManager::SetCallbackData(void* aData, bool aLoadScripts)
|
|
{
|
|
if (aData && mCallbackData != aData) {
|
|
mCallbackData = aData;
|
|
// First load global scripts by adding this to parent manager.
|
|
if (mParentManager) {
|
|
mParentManager->AddChildManager(this, aLoadScripts);
|
|
}
|
|
if (aLoadScripts) {
|
|
for (PRUint32 i = 0; i < mPendingScripts.Length(); ++i) {
|
|
LoadFrameScript(mPendingScripts[i], false);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
nsFrameMessageManager::RemoveFromParent()
|
|
{
|
|
if (mParentManager) {
|
|
mParentManager->RemoveChildManager(this);
|
|
}
|
|
mParentManager = nullptr;
|
|
mCallbackData = nullptr;
|
|
mContext = nullptr;
|
|
}
|
|
|
|
void
|
|
nsFrameMessageManager::Disconnect(bool aRemoveFromParent)
|
|
{
|
|
if (mParentManager && aRemoveFromParent) {
|
|
mParentManager->RemoveChildManager(this);
|
|
}
|
|
mDisconnected = true;
|
|
mParentManager = nullptr;
|
|
mCallbackData = nullptr;
|
|
mContext = nullptr;
|
|
if (!mHandlingMessage) {
|
|
mListeners.Clear();
|
|
}
|
|
}
|
|
|
|
nsresult
|
|
NS_NewGlobalMessageManager(nsIChromeFrameMessageManager** aResult)
|
|
{
|
|
NS_ENSURE_TRUE(IsChromeProcess(), NS_ERROR_NOT_AVAILABLE);
|
|
nsFrameMessageManager* mm = new nsFrameMessageManager(true,
|
|
nullptr,
|
|
nullptr,
|
|
nullptr,
|
|
nullptr,
|
|
nullptr,
|
|
nullptr,
|
|
true);
|
|
NS_ENSURE_TRUE(mm, NS_ERROR_OUT_OF_MEMORY);
|
|
return CallQueryInterface(mm, aResult);
|
|
}
|
|
|
|
void
|
|
ContentScriptErrorReporter(JSContext* aCx,
|
|
const char* aMessage,
|
|
JSErrorReport* aReport)
|
|
{
|
|
nsresult rv;
|
|
nsCOMPtr<nsIScriptError> scriptError =
|
|
do_CreateInstance(NS_SCRIPTERROR_CONTRACTID, &rv);
|
|
if (NS_FAILED(rv)) {
|
|
return;
|
|
}
|
|
nsAutoString message, filename, line;
|
|
PRUint32 lineNumber, columnNumber, flags, errorNumber;
|
|
|
|
if (aReport) {
|
|
if (aReport->ucmessage) {
|
|
message.Assign(reinterpret_cast<const PRUnichar*>(aReport->ucmessage));
|
|
}
|
|
filename.AssignWithConversion(aReport->filename);
|
|
line.Assign(reinterpret_cast<const PRUnichar*>(aReport->uclinebuf));
|
|
lineNumber = aReport->lineno;
|
|
columnNumber = aReport->uctokenptr - aReport->uclinebuf;
|
|
flags = aReport->flags;
|
|
errorNumber = aReport->errorNumber;
|
|
} else {
|
|
lineNumber = columnNumber = errorNumber = 0;
|
|
flags = nsIScriptError::errorFlag | nsIScriptError::exceptionFlag;
|
|
}
|
|
|
|
if (message.IsEmpty()) {
|
|
message.AssignWithConversion(aMessage);
|
|
}
|
|
|
|
rv = scriptError->Init(message.get(), filename.get(), line.get(),
|
|
lineNumber, columnNumber, flags,
|
|
"Message manager content script");
|
|
if (NS_FAILED(rv)) {
|
|
return;
|
|
}
|
|
|
|
nsCOMPtr<nsIConsoleService> consoleService =
|
|
do_GetService(NS_CONSOLESERVICE_CONTRACTID);
|
|
if (consoleService) {
|
|
(void) consoleService->LogMessage(scriptError);
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
// Print it to stderr as well, for the benefit of those invoking
|
|
// mozilla with -console.
|
|
nsCAutoString error;
|
|
error.Assign("JavaScript ");
|
|
if (JSREPORT_IS_STRICT(flags)) {
|
|
error.Append("strict ");
|
|
}
|
|
if (JSREPORT_IS_WARNING(flags)) {
|
|
error.Append("warning: ");
|
|
} else {
|
|
error.Append("error: ");
|
|
}
|
|
error.Append(aReport->filename);
|
|
error.Append(", line ");
|
|
error.AppendInt(lineNumber, 10);
|
|
error.Append(": ");
|
|
if (aReport->ucmessage) {
|
|
AppendUTF16toUTF8(reinterpret_cast<const PRUnichar*>(aReport->ucmessage),
|
|
error);
|
|
} else {
|
|
error.Append(aMessage);
|
|
}
|
|
|
|
fprintf(stderr, "%s\n", error.get());
|
|
fflush(stderr);
|
|
#endif
|
|
}
|
|
|
|
nsDataHashtable<nsStringHashKey, nsFrameJSScriptExecutorHolder*>*
|
|
nsFrameScriptExecutor::sCachedScripts = nullptr;
|
|
nsRefPtr<nsScriptCacheCleaner> nsFrameScriptExecutor::sScriptCacheCleaner;
|
|
|
|
void
|
|
nsFrameScriptExecutor::DidCreateCx()
|
|
{
|
|
NS_ASSERTION(mCx, "Should have mCx!");
|
|
if (!sCachedScripts) {
|
|
sCachedScripts =
|
|
new nsDataHashtable<nsStringHashKey, nsFrameJSScriptExecutorHolder*>;
|
|
sCachedScripts->Init();
|
|
|
|
sScriptCacheCleaner = new nsScriptCacheCleaner();
|
|
}
|
|
}
|
|
|
|
void
|
|
nsFrameScriptExecutor::DestroyCx()
|
|
{
|
|
if (mCxStackRefCnt) {
|
|
mDelayedCxDestroy = true;
|
|
return;
|
|
}
|
|
mDelayedCxDestroy = false;
|
|
if (mCx) {
|
|
nsIXPConnect* xpc = nsContentUtils::XPConnect();
|
|
if (xpc) {
|
|
xpc->ReleaseJSContext(mCx, true);
|
|
} else {
|
|
JS_DestroyContext(mCx);
|
|
}
|
|
}
|
|
mCx = nullptr;
|
|
mGlobal = nullptr;
|
|
}
|
|
|
|
static PLDHashOperator
|
|
CachedScriptUnrooter(const nsAString& aKey,
|
|
nsFrameJSScriptExecutorHolder*& aData,
|
|
void* aUserArg)
|
|
{
|
|
JSContext* cx = static_cast<JSContext*>(aUserArg);
|
|
JS_RemoveScriptRoot(cx, &(aData->mScript));
|
|
delete aData;
|
|
return PL_DHASH_REMOVE;
|
|
}
|
|
|
|
// static
|
|
void
|
|
nsFrameScriptExecutor::Shutdown()
|
|
{
|
|
if (sCachedScripts) {
|
|
JSContext* cx = nsContentUtils::ThreadJSContextStack()->GetSafeJSContext();
|
|
if (cx) {
|
|
#ifdef DEBUG_smaug
|
|
printf("Will clear cached frame manager scripts!\n");
|
|
#endif
|
|
JSAutoRequest ar(cx);
|
|
NS_ASSERTION(sCachedScripts != nullptr, "Need cached scripts");
|
|
sCachedScripts->Enumerate(CachedScriptUnrooter, cx);
|
|
} else {
|
|
NS_WARNING("No context available. Leaking cached scripts!\n");
|
|
}
|
|
|
|
delete sCachedScripts;
|
|
sCachedScripts = nullptr;
|
|
|
|
sScriptCacheCleaner = nullptr;
|
|
}
|
|
}
|
|
|
|
void
|
|
nsFrameScriptExecutor::LoadFrameScriptInternal(const nsAString& aURL)
|
|
{
|
|
if (!mGlobal || !mCx || !sCachedScripts) {
|
|
return;
|
|
}
|
|
|
|
nsFrameJSScriptExecutorHolder* holder = sCachedScripts->Get(aURL);
|
|
if (holder) {
|
|
nsContentUtils::ThreadJSContextStack()->Push(mCx);
|
|
{
|
|
// Need to scope JSAutoRequest to happen after Push but before Pop,
|
|
// at least for now. See bug 584673.
|
|
JSAutoRequest ar(mCx);
|
|
JSObject* global = nullptr;
|
|
mGlobal->GetJSObject(&global);
|
|
if (global) {
|
|
(void) JS_ExecuteScript(mCx, global, holder->mScript, nullptr);
|
|
}
|
|
}
|
|
JSContext* unused;
|
|
nsContentUtils::ThreadJSContextStack()->Pop(&unused);
|
|
return;
|
|
}
|
|
|
|
nsCString url = NS_ConvertUTF16toUTF8(aURL);
|
|
nsCOMPtr<nsIURI> uri;
|
|
nsresult rv = NS_NewURI(getter_AddRefs(uri), url);
|
|
if (NS_FAILED(rv)) {
|
|
return;
|
|
}
|
|
|
|
bool hasFlags;
|
|
rv = NS_URIChainHasFlags(uri,
|
|
nsIProtocolHandler::URI_IS_LOCAL_RESOURCE,
|
|
&hasFlags);
|
|
if (NS_FAILED(rv) || !hasFlags) {
|
|
NS_WARNING("Will not load a frame script!");
|
|
return;
|
|
}
|
|
|
|
nsCOMPtr<nsIChannel> channel;
|
|
NS_NewChannel(getter_AddRefs(channel), uri);
|
|
if (!channel) {
|
|
return;
|
|
}
|
|
|
|
nsCOMPtr<nsIInputStream> input;
|
|
channel->Open(getter_AddRefs(input));
|
|
nsString dataString;
|
|
PRUint64 avail64 = 0;
|
|
if (input && NS_SUCCEEDED(input->Available(&avail64)) && avail64) {
|
|
if (avail64 > PR_UINT32_MAX) {
|
|
return;
|
|
}
|
|
nsCString buffer;
|
|
PRUint32 avail = (PRUint32)NS_MIN(avail64, (PRUint64)PR_UINT32_MAX);
|
|
if (NS_FAILED(NS_ReadInputStreamToString(input, buffer, avail))) {
|
|
return;
|
|
}
|
|
nsScriptLoader::ConvertToUTF16(channel, (PRUint8*)buffer.get(), avail,
|
|
EmptyString(), nullptr, dataString);
|
|
}
|
|
|
|
if (!dataString.IsEmpty()) {
|
|
nsContentUtils::ThreadJSContextStack()->Push(mCx);
|
|
{
|
|
// Need to scope JSAutoRequest to happen after Push but before Pop,
|
|
// at least for now. See bug 584673.
|
|
JSAutoRequest ar(mCx);
|
|
JSObject* global = nullptr;
|
|
mGlobal->GetJSObject(&global);
|
|
JSAutoEnterCompartment ac;
|
|
if (global && ac.enter(mCx, global)) {
|
|
uint32_t oldopts = JS_GetOptions(mCx);
|
|
JS_SetOptions(mCx, oldopts | JSOPTION_NO_SCRIPT_RVAL);
|
|
|
|
JSScript* script =
|
|
JS_CompileUCScriptForPrincipals(mCx, nullptr,
|
|
nsJSPrincipals::get(mPrincipal),
|
|
static_cast<const jschar*>(dataString.get()),
|
|
dataString.Length(),
|
|
url.get(), 1);
|
|
|
|
JS_SetOptions(mCx, oldopts);
|
|
|
|
if (script) {
|
|
nsCAutoString scheme;
|
|
uri->GetScheme(scheme);
|
|
// We don't cache data: scripts!
|
|
if (!scheme.EqualsLiteral("data")) {
|
|
nsFrameJSScriptExecutorHolder* holder =
|
|
new nsFrameJSScriptExecutorHolder(script);
|
|
// Root the object also for caching.
|
|
JS_AddNamedScriptRoot(mCx, &(holder->mScript),
|
|
"Cached message manager script");
|
|
sCachedScripts->Put(aURL, holder);
|
|
}
|
|
(void) JS_ExecuteScript(mCx, global, script, nullptr);
|
|
}
|
|
}
|
|
}
|
|
JSContext* unused;
|
|
nsContentUtils::ThreadJSContextStack()->Pop(&unused);
|
|
}
|
|
}
|
|
|
|
bool
|
|
nsFrameScriptExecutor::InitTabChildGlobalInternal(nsISupports* aScope)
|
|
{
|
|
|
|
nsCOMPtr<nsIJSRuntimeService> runtimeSvc =
|
|
do_GetService("@mozilla.org/js/xpc/RuntimeService;1");
|
|
NS_ENSURE_TRUE(runtimeSvc, false);
|
|
|
|
JSRuntime* rt = nullptr;
|
|
runtimeSvc->GetRuntime(&rt);
|
|
NS_ENSURE_TRUE(rt, false);
|
|
|
|
JSContext* cx = JS_NewContext(rt, 8192);
|
|
NS_ENSURE_TRUE(cx, false);
|
|
|
|
mCx = cx;
|
|
|
|
nsContentUtils::GetSecurityManager()->GetSystemPrincipal(getter_AddRefs(mPrincipal));
|
|
|
|
bool allowXML = Preferences::GetBool("javascript.options.xml.chrome");
|
|
JS_SetOptions(cx, JS_GetOptions(cx) |
|
|
JSOPTION_PRIVATE_IS_NSISUPPORTS |
|
|
(allowXML ? JSOPTION_ALLOW_XML : 0));
|
|
JS_SetVersion(cx, JSVERSION_LATEST);
|
|
JS_SetErrorReporter(cx, ContentScriptErrorReporter);
|
|
|
|
xpc_LocalizeContext(cx);
|
|
|
|
JSAutoRequest ar(cx);
|
|
nsIXPConnect* xpc = nsContentUtils::XPConnect();
|
|
const PRUint32 flags = nsIXPConnect::INIT_JS_STANDARD_CLASSES |
|
|
nsIXPConnect::FLAG_SYSTEM_GLOBAL_OBJECT;
|
|
|
|
|
|
JS_SetContextPrivate(cx, aScope);
|
|
|
|
nsresult rv =
|
|
xpc->InitClassesWithNewWrappedGlobal(cx, aScope, mPrincipal,
|
|
flags, getter_AddRefs(mGlobal));
|
|
NS_ENSURE_SUCCESS(rv, false);
|
|
|
|
|
|
JSObject* global = nullptr;
|
|
rv = mGlobal->GetJSObject(&global);
|
|
NS_ENSURE_SUCCESS(rv, false);
|
|
|
|
JS_SetGlobalObject(cx, global);
|
|
DidCreateCx();
|
|
return true;
|
|
}
|
|
|
|
// static
|
|
void
|
|
nsFrameScriptExecutor::Traverse(nsFrameScriptExecutor *tmp,
|
|
nsCycleCollectionTraversalCallback &cb)
|
|
{
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mGlobal)
|
|
nsIXPConnect* xpc = nsContentUtils::XPConnect();
|
|
if (xpc) {
|
|
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mCx");
|
|
xpc->NoteJSContext(tmp->mCx, cb);
|
|
}
|
|
}
|
|
|
|
NS_IMPL_ISUPPORTS1(nsScriptCacheCleaner, nsIObserver)
|
|
|
|
nsFrameMessageManager* nsFrameMessageManager::sChildProcessManager = nullptr;
|
|
nsFrameMessageManager* nsFrameMessageManager::sParentProcessManager = nullptr;
|
|
nsFrameMessageManager* nsFrameMessageManager::sSameProcessParentManager = nullptr;
|
|
nsTArray<nsCOMPtr<nsIRunnable> >* nsFrameMessageManager::sPendingSameProcessAsyncMessages = nullptr;
|
|
|
|
bool SendAsyncMessageToChildProcess(void* aCallbackData,
|
|
const nsAString& aMessage,
|
|
const StructuredCloneData& aData)
|
|
{
|
|
mozilla::dom::ContentParent* cp =
|
|
static_cast<mozilla::dom::ContentParent*>(aCallbackData);
|
|
NS_WARN_IF_FALSE(cp, "No child process!");
|
|
if (cp) {
|
|
ClonedMessageData data;
|
|
SerializedStructuredCloneBuffer& buffer = data.data();
|
|
buffer.data = aData.mData;
|
|
buffer.dataLength = aData.mDataLength;
|
|
const nsTArray<nsCOMPtr<nsIDOMBlob> >& blobs = aData.mClosure.mBlobs;
|
|
if (!blobs.IsEmpty()) {
|
|
InfallibleTArray<PBlobParent*>& blobParents = data.blobsParent();
|
|
PRUint32 length = blobs.Length();
|
|
blobParents.SetCapacity(length);
|
|
for (PRUint32 i = 0; i < length; ++i) {
|
|
BlobParent* blobParent = cp->GetOrCreateActorForBlob(blobs[i]);
|
|
if (!blobParent) {
|
|
return false;
|
|
}
|
|
blobParents.AppendElement(blobParent);
|
|
}
|
|
}
|
|
|
|
return cp->SendAsyncMessage(nsString(aMessage), data);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
class nsAsyncMessageToSameProcessChild : public nsRunnable
|
|
{
|
|
public:
|
|
nsAsyncMessageToSameProcessChild(const nsAString& aMessage,
|
|
const StructuredCloneData& aData)
|
|
: mMessage(aMessage)
|
|
{
|
|
if (aData.mDataLength && !mData.copy(aData.mData, aData.mDataLength)) {
|
|
NS_RUNTIMEABORT("OOM");
|
|
}
|
|
mClosure = aData.mClosure;
|
|
}
|
|
|
|
NS_IMETHOD Run()
|
|
{
|
|
if (nsFrameMessageManager::sChildProcessManager) {
|
|
StructuredCloneData data;
|
|
data.mData = mData.data();
|
|
data.mDataLength = mData.nbytes();
|
|
data.mClosure = mClosure;
|
|
|
|
nsRefPtr<nsFrameMessageManager> ppm = nsFrameMessageManager::sChildProcessManager;
|
|
ppm->ReceiveMessage(static_cast<nsIContentFrameMessageManager*>(ppm.get()), mMessage,
|
|
false, &data, nullptr, nullptr, nullptr);
|
|
}
|
|
return NS_OK;
|
|
}
|
|
nsString mMessage;
|
|
JSAutoStructuredCloneBuffer mData;
|
|
StructuredCloneClosure mClosure;
|
|
};
|
|
|
|
bool SendAsyncMessageToSameProcessChild(void* aCallbackData,
|
|
const nsAString& aMessage,
|
|
const StructuredCloneData& aData)
|
|
{
|
|
nsRefPtr<nsIRunnable> ev =
|
|
new nsAsyncMessageToSameProcessChild(aMessage, aData);
|
|
NS_DispatchToCurrentThread(ev);
|
|
return true;
|
|
}
|
|
|
|
bool SendSyncMessageToParentProcess(void* aCallbackData,
|
|
const nsAString& aMessage,
|
|
const StructuredCloneData& aData,
|
|
InfallibleTArray<nsString>* aJSONRetVal)
|
|
{
|
|
mozilla::dom::ContentChild* cc =
|
|
mozilla::dom::ContentChild::GetSingleton();
|
|
if (cc) {
|
|
ClonedMessageData data;
|
|
SerializedStructuredCloneBuffer& buffer = data.data();
|
|
buffer.data = aData.mData;
|
|
buffer.dataLength = aData.mDataLength;
|
|
const nsTArray<nsCOMPtr<nsIDOMBlob> >& blobs = aData.mClosure.mBlobs;
|
|
if (!blobs.IsEmpty()) {
|
|
InfallibleTArray<PBlobChild*>& blobChildList = data.blobsChild();
|
|
PRUint32 length = blobs.Length();
|
|
blobChildList.SetCapacity(length);
|
|
for (PRUint32 i = 0; i < length; ++i) {
|
|
BlobChild* blobChild = cc->GetOrCreateActorForBlob(blobs[i]);
|
|
if (!blobChild) {
|
|
return false;
|
|
}
|
|
blobChildList.AppendElement(blobChild);
|
|
}
|
|
}
|
|
return
|
|
cc->SendSyncMessage(nsString(aMessage), data, aJSONRetVal);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool SendSyncMessageToSameProcessParent(void* aCallbackData,
|
|
const nsAString& aMessage,
|
|
const StructuredCloneData& aData,
|
|
InfallibleTArray<nsString>* aJSONRetVal)
|
|
{
|
|
nsTArray<nsCOMPtr<nsIRunnable> > asyncMessages;
|
|
if (nsFrameMessageManager::sPendingSameProcessAsyncMessages) {
|
|
asyncMessages.SwapElements(*nsFrameMessageManager::sPendingSameProcessAsyncMessages);
|
|
PRUint32 len = asyncMessages.Length();
|
|
for (PRUint32 i = 0; i < len; ++i) {
|
|
nsCOMPtr<nsIRunnable> async = asyncMessages[i];
|
|
async->Run();
|
|
}
|
|
}
|
|
if (nsFrameMessageManager::sSameProcessParentManager) {
|
|
nsRefPtr<nsFrameMessageManager> ppm = nsFrameMessageManager::sSameProcessParentManager;
|
|
ppm->ReceiveMessage(static_cast<nsIContentFrameMessageManager*>(ppm.get()), aMessage,
|
|
true, &aData, nullptr, aJSONRetVal);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool SendAsyncMessageToParentProcess(void* aCallbackData,
|
|
const nsAString& aMessage,
|
|
const StructuredCloneData& aData)
|
|
{
|
|
mozilla::dom::ContentChild* cc =
|
|
mozilla::dom::ContentChild::GetSingleton();
|
|
if (cc) {
|
|
ClonedMessageData data;
|
|
SerializedStructuredCloneBuffer& buffer = data.data();
|
|
buffer.data = aData.mData;
|
|
buffer.dataLength = aData.mDataLength;
|
|
const nsTArray<nsCOMPtr<nsIDOMBlob> >& blobs = aData.mClosure.mBlobs;
|
|
if (!blobs.IsEmpty()) {
|
|
InfallibleTArray<PBlobChild*>& blobChildList = data.blobsChild();
|
|
PRUint32 length = blobs.Length();
|
|
blobChildList.SetCapacity(length);
|
|
for (PRUint32 i = 0; i < length; ++i) {
|
|
BlobChild* blobChild = cc->GetOrCreateActorForBlob(blobs[i]);
|
|
if (!blobChild) {
|
|
return false;
|
|
}
|
|
blobChildList.AppendElement(blobChild);
|
|
}
|
|
}
|
|
return cc->SendAsyncMessage(nsString(aMessage), data);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
class nsAsyncMessageToSameProcessParent : public nsRunnable
|
|
{
|
|
public:
|
|
nsAsyncMessageToSameProcessParent(const nsAString& aMessage,
|
|
const StructuredCloneData& aData)
|
|
: mMessage(aMessage)
|
|
{
|
|
if (aData.mDataLength && !mData.copy(aData.mData, aData.mDataLength)) {
|
|
NS_RUNTIMEABORT("OOM");
|
|
}
|
|
mClosure = aData.mClosure;
|
|
}
|
|
|
|
NS_IMETHOD Run()
|
|
{
|
|
if (nsFrameMessageManager::sPendingSameProcessAsyncMessages) {
|
|
nsFrameMessageManager::sPendingSameProcessAsyncMessages->RemoveElement(this);
|
|
}
|
|
if (nsFrameMessageManager::sSameProcessParentManager) {
|
|
StructuredCloneData data;
|
|
data.mData = mData.data();
|
|
data.mDataLength = mData.nbytes();
|
|
data.mClosure = mClosure;
|
|
|
|
nsRefPtr<nsFrameMessageManager> ppm =
|
|
nsFrameMessageManager::sSameProcessParentManager;
|
|
ppm->ReceiveMessage(static_cast<nsIContentFrameMessageManager*>(ppm.get()),
|
|
mMessage, false, &data, nullptr, nullptr, nullptr);
|
|
}
|
|
return NS_OK;
|
|
}
|
|
nsString mMessage;
|
|
JSAutoStructuredCloneBuffer mData;
|
|
StructuredCloneClosure mClosure;
|
|
};
|
|
|
|
bool SendAsyncMessageToSameProcessParent(void* aCallbackData,
|
|
const nsAString& aMessage,
|
|
const StructuredCloneData& aData)
|
|
{
|
|
if (!nsFrameMessageManager::sPendingSameProcessAsyncMessages) {
|
|
nsFrameMessageManager::sPendingSameProcessAsyncMessages = new nsTArray<nsCOMPtr<nsIRunnable> >;
|
|
}
|
|
nsCOMPtr<nsIRunnable> ev =
|
|
new nsAsyncMessageToSameProcessParent(aMessage, aData);
|
|
nsFrameMessageManager::sPendingSameProcessAsyncMessages->AppendElement(ev);
|
|
NS_DispatchToCurrentThread(ev);
|
|
return true;
|
|
}
|
|
|
|
// This creates the global parent process message manager.
|
|
nsresult
|
|
NS_NewParentProcessMessageManager(nsIFrameMessageManager** aResult)
|
|
{
|
|
NS_ASSERTION(!nsFrameMessageManager::sParentProcessManager,
|
|
"Re-creating sParentProcessManager");
|
|
NS_ENSURE_TRUE(IsChromeProcess(), NS_ERROR_NOT_AVAILABLE);
|
|
nsRefPtr<nsFrameMessageManager> mm = new nsFrameMessageManager(true,
|
|
nullptr,
|
|
nullptr,
|
|
nullptr,
|
|
nullptr,
|
|
nullptr,
|
|
nullptr,
|
|
false,
|
|
true);
|
|
NS_ENSURE_TRUE(mm, NS_ERROR_OUT_OF_MEMORY);
|
|
nsFrameMessageManager::sParentProcessManager = mm;
|
|
nsFrameMessageManager::NewProcessMessageManager(nullptr); // Create same process message manager.
|
|
return CallQueryInterface(mm, aResult);
|
|
}
|
|
|
|
nsFrameMessageManager*
|
|
nsFrameMessageManager::NewProcessMessageManager(mozilla::dom::ContentParent* aProcess)
|
|
{
|
|
if (!nsFrameMessageManager::sParentProcessManager) {
|
|
nsCOMPtr<nsIFrameMessageManager> dummy;
|
|
NS_NewParentProcessMessageManager(getter_AddRefs(dummy));
|
|
}
|
|
|
|
nsFrameMessageManager* mm = new nsFrameMessageManager(true,
|
|
nullptr,
|
|
aProcess ? SendAsyncMessageToChildProcess
|
|
: SendAsyncMessageToSameProcessChild,
|
|
nullptr,
|
|
aProcess ? static_cast<void*>(aProcess)
|
|
: static_cast<void*>(&nsFrameMessageManager::sChildProcessManager),
|
|
nsFrameMessageManager::sParentProcessManager,
|
|
nullptr,
|
|
false,
|
|
true);
|
|
if (!aProcess) {
|
|
sSameProcessParentManager = mm;
|
|
}
|
|
return mm;
|
|
}
|
|
|
|
nsresult
|
|
NS_NewChildProcessMessageManager(nsISyncMessageSender** aResult)
|
|
{
|
|
NS_ASSERTION(!nsFrameMessageManager::sChildProcessManager,
|
|
"Re-creating sChildProcessManager");
|
|
bool isChrome = IsChromeProcess();
|
|
nsFrameMessageManager* mm = new nsFrameMessageManager(false,
|
|
isChrome ? SendSyncMessageToSameProcessParent
|
|
: SendSyncMessageToParentProcess,
|
|
isChrome ? SendAsyncMessageToSameProcessParent
|
|
: SendAsyncMessageToParentProcess,
|
|
nullptr,
|
|
&nsFrameMessageManager::sChildProcessManager,
|
|
nullptr,
|
|
nullptr,
|
|
false,
|
|
true);
|
|
NS_ENSURE_TRUE(mm, NS_ERROR_OUT_OF_MEMORY);
|
|
nsFrameMessageManager::sChildProcessManager = mm;
|
|
return CallQueryInterface(mm, aResult);
|
|
}
|
|
|
|
bool
|
|
nsFrameMessageManager::MarkForCC()
|
|
{
|
|
PRUint32 len = mListeners.Length();
|
|
for (PRUint32 i = 0; i < len; ++i) {
|
|
xpc_TryUnmarkWrappedGrayObject(mListeners[i].mListener);
|
|
}
|
|
return true;
|
|
}
|