Add RPC message support to messagemanager (bug 910493 part 2, r=smaug).

This commit is contained in:
David Anderson 2013-10-01 09:15:06 -07:00
parent 47ae6f0cc8
commit 0c515ae00c
16 changed files with 270 additions and 62 deletions

View File

@ -289,7 +289,7 @@ interface nsIMessageBroadcaster : nsIMessageListenerManager
nsIMessageListenerManager getChildAt(in unsigned long aIndex);
};
[scriptable, builtinclass, uuid(83be5862-2996-4685-ae7d-ae25bd795d50)]
[scriptable, builtinclass, uuid(79eeb70f-58e3-4d32-b46f-106f42ada12b)]
interface nsISyncMessageSender : nsIMessageSender
{
/**
@ -301,6 +301,20 @@ interface nsISyncMessageSender : nsIMessageSender
jsval sendSyncMessage([optional] in AString messageName,
[optional] in jsval obj,
[optional] in jsval objects);
/**
* Like |sendSyncMessage()|, except re-entrant. New RPC messages may be
* issued even if, earlier on the call stack, we are waiting for a reply
* to an earlier sendRpcMessage() call.
*
* Both sendSyncMessage and sendRpcMessage will block until a reply is
* received, but they may be temporarily interrupted to process an urgent
* incoming message (such as a CPOW request).
*/
[implicit_jscontext, optional_argc]
jsval sendRpcMessage([optional] in AString messageName,
[optional] in jsval obj,
[optional] in jsval objects);
};
[scriptable, builtinclass, uuid(894ff2d4-39a3-4df8-9d76-8ee329975488)]

View File

@ -41,6 +41,9 @@
#endif
#ifdef XP_WIN
#include <windows.h>
# if defined(SendMessage)
# undef SendMessage
# endif
#endif
using namespace mozilla;
@ -439,6 +442,8 @@ GetParamsForMessage(JSContext* aCx,
// nsISyncMessageSender
static bool sSendingSyncMessage = false;
NS_IMETHODIMP
nsFrameMessageManager::SendSyncMessage(const nsAString& aMessageName,
const JS::Value& aJSON,
@ -446,6 +451,29 @@ nsFrameMessageManager::SendSyncMessage(const nsAString& aMessageName,
JSContext* aCx,
uint8_t aArgc,
JS::Value* aRetval)
{
return SendMessage(aMessageName, aJSON, aObjects, aCx, aArgc, aRetval, true);
}
NS_IMETHODIMP
nsFrameMessageManager::SendRpcMessage(const nsAString& aMessageName,
const JS::Value& aJSON,
const JS::Value& aObjects,
JSContext* aCx,
uint8_t aArgc,
JS::Value* aRetval)
{
return SendMessage(aMessageName, aJSON, aObjects, aCx, aArgc, aRetval, false);
}
nsresult
nsFrameMessageManager::SendMessage(const nsAString& aMessageName,
const JS::Value& aJSON,
const JS::Value& aObjects,
JSContext* aCx,
uint8_t aArgc,
JS::Value* aRetval,
bool aIsSync)
{
NS_ASSERTION(!IsGlobal(), "Should not call SendSyncMessage in chrome");
NS_ASSERTION(!IsWindowLevel(), "Should not call SendSyncMessage in chrome");
@ -454,6 +482,11 @@ nsFrameMessageManager::SendSyncMessage(const nsAString& aMessageName,
*aRetval = JSVAL_VOID;
NS_ENSURE_TRUE(mCallback, NS_ERROR_NOT_INITIALIZED);
if (sSendingSyncMessage && aIsSync) {
// No kind of blocking send should be issued on top of a sync message.
return NS_ERROR_UNEXPECTED;
}
StructuredCloneData data;
JSAutoStructuredCloneBuffer buffer;
if (aArgc >= 2 &&
@ -469,27 +502,36 @@ nsFrameMessageManager::SendSyncMessage(const nsAString& aMessageName,
}
InfallibleTArray<nsString> retval;
if (mCallback->DoSendSyncMessage(aCx, aMessageName, data, objects, &retval)) {
uint32_t len = retval.Length();
JS::Rooted<JSObject*> dataArray(aCx, JS_NewArrayObject(aCx, len, nullptr));
NS_ENSURE_TRUE(dataArray, NS_ERROR_OUT_OF_MEMORY);
for (uint32_t i = 0; i < len; ++i) {
if (retval[i].IsEmpty()) {
continue;
}
sSendingSyncMessage |= aIsSync;
bool rv = mCallback->DoSendBlockingMessage(aCx, aMessageName, data, objects, &retval, aIsSync);
if (aIsSync) {
sSendingSyncMessage = false;
}
JS::Rooted<JS::Value> ret(aCx);
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);
if (!rv) {
return NS_OK;
}
uint32_t len = retval.Length();
JS::Rooted<JSObject*> dataArray(aCx, JS_NewArrayObject(aCx, len, nullptr));
NS_ENSURE_TRUE(dataArray, NS_ERROR_OUT_OF_MEMORY);
for (uint32_t i = 0; i < len; ++i) {
if (retval[i].IsEmpty()) {
continue;
}
*aRetval = OBJECT_TO_JSVAL(dataArray);
JS::Rooted<JS::Value> ret(aCx);
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;
}
@ -744,7 +786,7 @@ public:
nsresult
nsFrameMessageManager::ReceiveMessage(nsISupports* aTarget,
const nsAString& aMessage,
bool aSync,
bool aIsSync,
const StructuredCloneData* aCloneData,
CpowHolder* aCpows,
InfallibleTArray<nsString>* aJSONRetVal)
@ -824,7 +866,7 @@ nsFrameMessageManager::ReceiveMessage(nsISupports* aTarget,
JS_DefineProperty(ctx, param, "name",
STRING_TO_JSVAL(jsMessage), nullptr, nullptr, JSPROP_ENUMERATE);
JS_DefineProperty(ctx, param, "sync",
BOOLEAN_TO_JSVAL(aSync), nullptr, nullptr, JSPROP_ENUMERATE);
BOOLEAN_TO_JSVAL(aIsSync), nullptr, nullptr, JSPROP_ENUMERATE);
JS_DefineProperty(ctx, param, "json", json, nullptr, nullptr, JSPROP_ENUMERATE); // deprecated
JS_DefineProperty(ctx, param, "data", json, nullptr, nullptr, JSPROP_ENUMERATE);
JS_DefineProperty(ctx, param, "objects", cpowsv, nullptr, nullptr, JSPROP_ENUMERATE);
@ -865,8 +907,9 @@ nsFrameMessageManager::ReceiveMessage(nsISupports* aTarget,
JS::Rooted<JSObject*> thisObject(ctx, thisValue.toObjectOrNull());
JSAutoCompartment tac(ctx, thisObject);
if (!JS_WrapValue(ctx, argv.address()))
if (!JS_WrapValue(ctx, argv.address())) {
return NS_ERROR_UNEXPECTED;
}
JS_CallFunctionValue(ctx, thisObject,
funval, 1, argv.address(), rval.address());
@ -883,7 +926,7 @@ nsFrameMessageManager::ReceiveMessage(nsISupports* aTarget,
}
nsRefPtr<nsFrameMessageManager> kungfuDeathGrip = mParentManager;
return mParentManager ? mParentManager->ReceiveMessage(aTarget, aMessage,
aSync, aCloneData,
aIsSync, aCloneData,
aCpows,
aJSONRetVal) : NS_OK;
}
@ -1281,11 +1324,12 @@ public:
MOZ_COUNT_DTOR(ChildProcessMessageManagerCallback);
}
virtual bool DoSendSyncMessage(JSContext* aCx,
const nsAString& aMessage,
const mozilla::dom::StructuredCloneData& aData,
JS::Handle<JSObject *> aCpows,
InfallibleTArray<nsString>* aJSONRetVal)
virtual bool DoSendBlockingMessage(JSContext* aCx,
const nsAString& aMessage,
const mozilla::dom::StructuredCloneData& aData,
JS::Handle<JSObject *> aCpows,
InfallibleTArray<nsString>* aJSONRetVal,
bool aIsSync) MOZ_OVERRIDE
{
mozilla::dom::ContentChild* cc =
mozilla::dom::ContentChild::GetSingleton();
@ -1300,13 +1344,16 @@ public:
if (!cc->GetCPOWManager()->Wrap(aCx, aCpows, &cpows)) {
return false;
}
return cc->SendSyncMessage(nsString(aMessage), data, cpows, aJSONRetVal);
if (aIsSync) {
return cc->SendSyncMessage(nsString(aMessage), data, cpows, aJSONRetVal);
}
return cc->CallRpcMessage(nsString(aMessage), data, cpows, aJSONRetVal);
}
virtual bool DoSendAsyncMessage(JSContext* aCx,
const nsAString& aMessage,
const mozilla::dom::StructuredCloneData& aData,
JS::Handle<JSObject *> aCpows)
JS::Handle<JSObject *> aCpows) MOZ_OVERRIDE
{
mozilla::dom::ContentChild* cc =
mozilla::dom::ContentChild::GetSingleton();
@ -1396,11 +1443,12 @@ public:
MOZ_COUNT_DTOR(SameChildProcessMessageManagerCallback);
}
virtual bool DoSendSyncMessage(JSContext* aCx,
const nsAString& aMessage,
const mozilla::dom::StructuredCloneData& aData,
JS::Handle<JSObject *> aCpows,
InfallibleTArray<nsString>* aJSONRetVal)
virtual bool DoSendBlockingMessage(JSContext* aCx,
const nsAString& aMessage,
const mozilla::dom::StructuredCloneData& aData,
JS::Handle<JSObject *> aCpows,
InfallibleTArray<nsString>* aJSONRetVal,
bool aIsSync) MOZ_OVERRIDE
{
nsTArray<nsCOMPtr<nsIRunnable> > asyncMessages;
if (nsFrameMessageManager::sPendingSameProcessAsyncMessages) {

View File

@ -54,11 +54,12 @@ public:
return true;
}
virtual bool DoSendSyncMessage(JSContext* aCx,
const nsAString& aMessage,
const mozilla::dom::StructuredCloneData& aData,
JS::Handle<JSObject *> aCpows,
InfallibleTArray<nsString>* aJSONRetVal)
virtual bool DoSendBlockingMessage(JSContext* aCx,
const nsAString& aMessage,
const mozilla::dom::StructuredCloneData& aData,
JS::Handle<JSObject *> aCpows,
InfallibleTArray<nsString>* aJSONRetVal,
bool aIsSync)
{
return true;
}
@ -208,7 +209,7 @@ public:
NewProcessMessageManager(mozilla::dom::ContentParent* aProcess);
nsresult ReceiveMessage(nsISupports* aTarget, const nsAString& aMessage,
bool aSync, const StructuredCloneData* aCloneData,
bool aIsSync, const StructuredCloneData* aCloneData,
CpowHolder* aCpows,
InfallibleTArray<nsString>* aJSONRetVal);
@ -255,6 +256,14 @@ public:
{
return sChildProcessManager;
}
private:
nsresult SendMessage(const nsAString& aMessageName,
const JS::Value& aJSON,
const JS::Value& aObjects,
JSContext* aCx,
uint8_t aArgc,
JS::Value* aRetval,
bool aIsSync);
protected:
friend class MMListenerRemover;
nsTArray<nsMessageListenerInfo> mListeners;

View File

@ -26,11 +26,12 @@ using mozilla::dom::StructuredCloneData;
using mozilla::dom::StructuredCloneClosure;
bool
nsInProcessTabChildGlobal::DoSendSyncMessage(JSContext* aCx,
const nsAString& aMessage,
const mozilla::dom::StructuredCloneData& aData,
JS::Handle<JSObject *> aCpows,
InfallibleTArray<nsString>* aJSONRetVal)
nsInProcessTabChildGlobal::DoSendBlockingMessage(JSContext* aCx,
const nsAString& aMessage,
const mozilla::dom::StructuredCloneData& aData,
JS::Handle<JSObject *> aCpows,
InfallibleTArray<nsString>* aJSONRetVal,
bool aIsSync)
{
nsTArray<nsCOMPtr<nsIRunnable> > asyncMessages;
asyncMessages.SwapElements(mASyncMessages);

View File

@ -51,6 +51,17 @@ public:
? mMessageManager->SendSyncMessage(aMessageName, aObject, aRemote, aCx, aArgc, aRetval)
: NS_ERROR_NULL_POINTER;
}
NS_IMETHOD SendRpcMessage(const nsAString& aMessageName,
const JS::Value& aObject,
const JS::Value& aRemote,
JSContext* aCx,
uint8_t aArgc,
JS::Value* aRetval)
{
return mMessageManager
? mMessageManager->SendRpcMessage(aMessageName, aObject, aRemote, aCx, aArgc, aRetval)
: NS_ERROR_NULL_POINTER;
}
NS_IMETHOD GetContent(nsIDOMWindow** aContent) MOZ_OVERRIDE;
NS_IMETHOD GetDocShell(nsIDocShell** aDocShell) MOZ_OVERRIDE;
NS_IMETHOD Dump(const nsAString& aStr) MOZ_OVERRIDE
@ -68,11 +79,12 @@ public:
/**
* MessageManagerCallback methods that we override.
*/
virtual bool DoSendSyncMessage(JSContext* aCx,
const nsAString& aMessage,
const mozilla::dom::StructuredCloneData& aData,
JS::Handle<JSObject *> aCpows,
InfallibleTArray<nsString>* aJSONRetVal) MOZ_OVERRIDE;
virtual bool DoSendBlockingMessage(JSContext* aCx,
const nsAString& aMessage,
const mozilla::dom::StructuredCloneData& aData,
JS::Handle<JSObject *> aCpows,
InfallibleTArray<nsString>* aJSONRetVal,
bool aIsSync) MOZ_OVERRIDE;
virtual bool DoSendAsyncMessage(JSContext* aCx,
const nsAString& aMessage,
const mozilla::dom::StructuredCloneData& aData,

View File

@ -5,6 +5,8 @@ content.document.title = "Hello, Kitty";
(function start() {
sync_test();
async_test();
rpc_test();
nested_sync_test();
// The sync-ness of this call is important, because otherwise
// we tear down the child's document while we are
// still in the async test in the parent.
@ -72,3 +74,35 @@ function async_test()
async_obj);
}
function rpc_test()
{
dump('beginning cpow rpc test\n');
rpc_obj = make_object();
rpc_obj.data.reenter = function () {
sendRpcMessage("cpows:reenter", { }, { data: { valid: true } });
return "ok";
}
sendRpcMessage("cpows:rpc",
make_json(),
rpc_obj);
}
function nested_sync_test()
{
dump('beginning cpow nested sync test\n');
sync_obj = make_object();
sync_obj.data.reenter = function () {
let caught = false;
try {
sendSyncMessage("cpows:reenter_sync", { }, { });
} catch (e) {
caught = true;
}
if (!ok(caught, "should not allow nested sync"))
return "fail";
return "ok";
}
sendSyncMessage("cpows:nested_sync",
make_json(),
rpc_obj);
}

View File

@ -12,6 +12,7 @@
<script type="application/javascript"><![CDATA[
var test_state = "remote";
var test_node = null;
var reentered = false;
function ok(condition, message) {
return opener.wrappedJSObject.ok(condition, message);
@ -102,6 +103,34 @@
testCpowMessage(message);
}
function recvRpcMessage(message) {
ok(message.json.check == "ok", "correct json");
let data = message.objects.data;
// Sanity check.
ok(data.i === 5, "integer property");
// Check that we re-enter.
reentered = false;
let result = data.reenter();
ok(reentered, "re-entered rpc");
ok(result == "ok", "got correct result");
}
function recvReenterMessage(message) {
ok(message.objects.data.valid === true, "cpows work");
reentered = true;
}
function recvNestedSyncMessage(message) {
message.objects.data.reenter();
}
function recvReenterSyncMessage(message) {
ok(false, "should not have received re-entered sync message");
}
function recvFailMessage(message) {
ok(false, message.json.message);
}
@ -125,6 +154,11 @@
var mm = node.messageManager;
mm.addMessageListener("cpows:async", recvAsyncMessage);
mm.addMessageListener("cpows:sync", recvSyncMessage);
mm.addMessageListener("cpows:rpc", recvRpcMessage);
mm.addMessageListener("cpows:reenter", recvReenterMessage);
mm.addMessageListener("cpows:reenter", recvReenterMessage);
mm.addMessageListener("cpows:nested_sync", recvNestedSyncMessage);
mm.addMessageListener("cpows:reenter_sync", recvReenterSyncMessage);
mm.addMessageListener("cpows:done", recvDoneMessage);
mm.addMessageListener("cpows:fail", recvFailMessage);
mm.loadFrameScript("chrome://mochitests/content/chrome/content/base/test/chrome/cpows_child.js", true);

View File

@ -2946,6 +2946,22 @@ ContentParent::RecvSyncMessage(const nsString& aMsg,
return true;
}
bool
ContentParent::AnswerRpcMessage(const nsString& aMsg,
const ClonedMessageData& aData,
const InfallibleTArray<CpowEntry>& aCpows,
InfallibleTArray<nsString>* aRetvals)
{
nsRefPtr<nsFrameMessageManager> ppm = mMessageManager;
if (ppm) {
StructuredCloneData cloneData = ipc::UnpackClonedMessageDataForParent(aData);
CpowIdHolder cpows(GetCPOWManager(), aCpows);
ppm->ReceiveMessage(static_cast<nsIContentFrameMessageManager*>(ppm.get()),
aMsg, true, &cloneData, &cpows, aRetvals);
}
return true;
}
bool
ContentParent::RecvAsyncMessage(const nsString& aMsg,
const ClonedMessageData& aData,

View File

@ -422,6 +422,10 @@ private:
const ClonedMessageData& aData,
const InfallibleTArray<CpowEntry>& aCpows,
InfallibleTArray<nsString>* aRetvals);
virtual bool AnswerRpcMessage(const nsString& aMsg,
const ClonedMessageData& aData,
const InfallibleTArray<CpowEntry>& aCpows,
InfallibleTArray<nsString>* aRetvals);
virtual bool RecvAsyncMessage(const nsString& aMsg,
const ClonedMessageData& aData,
const InfallibleTArray<CpowEntry>& aCpows);

View File

@ -84,6 +84,9 @@ parent:
sync SyncMessage(nsString aMessage, ClonedMessageData aData, CpowEntry[] aCpows)
returns (nsString[] retval);
rpc RpcMessage(nsString aMessage, ClonedMessageData aData, CpowEntry[] aCpows)
returns (nsString[] retval);
/**
* The IME sequence number (seqno) parameter is used to make sure
* that a notification is discarded if it arrives at the chrome process

View File

@ -389,6 +389,9 @@ parent:
sync SyncMessage(nsString aMessage, ClonedMessageData aData, CpowEntry[] aCpows)
returns (nsString[] retval);
rpc RpcMessage(nsString aMessage, ClonedMessageData aData, CpowEntry[] aCpows)
returns (nsString[] retval);
ShowAlertNotification(nsString imageUrl,
nsString title,
nsString text,

View File

@ -2382,11 +2382,12 @@ TabChild::DeallocPIndexedDBChild(PIndexedDBChild* aActor)
}
bool
TabChild::DoSendSyncMessage(JSContext* aCx,
const nsAString& aMessage,
const StructuredCloneData& aData,
JS::Handle<JSObject *> aCpows,
InfallibleTArray<nsString>* aJSONRetVal)
TabChild::DoSendBlockingMessage(JSContext* aCx,
const nsAString& aMessage,
const StructuredCloneData& aData,
JS::Handle<JSObject *> aCpows,
InfallibleTArray<nsString>* aJSONRetVal,
bool aIsSync)
{
ContentChild* cc = Manager();
ClonedMessageData data;
@ -2399,7 +2400,9 @@ TabChild::DoSendSyncMessage(JSContext* aCx,
return false;
}
}
return SendSyncMessage(nsString(aMessage), data, cpows, aJSONRetVal);
if (aIsSync)
return SendSyncMessage(nsString(aMessage), data, cpows, aJSONRetVal);
return CallRpcMessage(nsString(aMessage), data, cpows, aJSONRetVal);
}
bool

View File

@ -78,6 +78,17 @@ public:
? mMessageManager->SendSyncMessage(aMessageName, aObject, aRemote, aCx, aArgc, aRetval)
: NS_ERROR_NULL_POINTER;
}
NS_IMETHOD SendRpcMessage(const nsAString& aMessageName,
const JS::Value& aObject,
const JS::Value& aRemote,
JSContext* aCx,
uint8_t aArgc,
JS::Value* aRetval)
{
return mMessageManager
? mMessageManager->SendRpcMessage(aMessageName, aObject, aRemote, aCx, aArgc, aRetval)
: NS_ERROR_NULL_POINTER;
}
NS_IMETHOD GetContent(nsIDOMWindow** aContent) MOZ_OVERRIDE;
NS_IMETHOD GetDocShell(nsIDocShell** aDocShell) MOZ_OVERRIDE;
NS_IMETHOD Dump(const nsAString& aStr) MOZ_OVERRIDE
@ -186,15 +197,16 @@ public:
/**
* MessageManagerCallback methods that we override.
*/
virtual bool DoSendSyncMessage(JSContext* aCx,
const nsAString& aMessage,
const mozilla::dom::StructuredCloneData& aData,
JS::Handle<JSObject *> aCpows,
InfallibleTArray<nsString>* aJSONRetVal);
virtual bool DoSendBlockingMessage(JSContext* aCx,
const nsAString& aMessage,
const mozilla::dom::StructuredCloneData& aData,
JS::Handle<JSObject *> aCpows,
InfallibleTArray<nsString>* aJSONRetVal,
bool aIsSync) MOZ_OVERRIDE;
virtual bool DoSendAsyncMessage(JSContext* aCx,
const nsAString& aMessage,
const mozilla::dom::StructuredCloneData& aData,
JS::Handle<JSObject *> aCpows);
JS::Handle<JSObject *> aCpows) MOZ_OVERRIDE;
virtual bool RecvLoadURL(const nsCString& uri);
virtual bool RecvCacheFileDescriptor(const nsString& aPath,

View File

@ -766,6 +766,17 @@ TabParent::RecvSyncMessage(const nsString& aMessage,
return ReceiveMessage(aMessage, true, &cloneData, &cpows, aJSONRetVal);
}
bool
TabParent::AnswerRpcMessage(const nsString& aMessage,
const ClonedMessageData& aData,
const InfallibleTArray<CpowEntry>& aCpows,
InfallibleTArray<nsString>* aJSONRetVal)
{
StructuredCloneData cloneData = ipc::UnpackClonedMessageDataForParent(aData);
CpowIdHolder cpows(static_cast<ContentParent*>(Manager())->GetCPOWManager(), aCpows);
return ReceiveMessage(aMessage, true, &cloneData, &cpows, aJSONRetVal);
}
bool
TabParent::RecvAsyncMessage(const nsString& aMessage,
const ClonedMessageData& aData,

View File

@ -120,6 +120,10 @@ public:
const ClonedMessageData& aData,
const InfallibleTArray<CpowEntry>& aCpows,
InfallibleTArray<nsString>* aJSONRetVal);
virtual bool AnswerRpcMessage(const nsString& aMessage,
const ClonedMessageData& aData,
const InfallibleTArray<CpowEntry>& aCpows,
InfallibleTArray<nsString>* aJSONRetVal);
virtual bool RecvAsyncMessage(const nsString& aMessage,
const ClonedMessageData& aData,
const InfallibleTArray<CpowEntry>& aCpows);

View File

@ -69,7 +69,7 @@ let ContentPolicyChild = {
let cpmm = Cc["@mozilla.org/childprocessmessagemanager;1"]
.getService(Ci.nsISyncMessageSender);
var rval = cpmm.sendSyncMessage("Addons:ContentPolicy:Run", {
var rval = cpmm.sendRpcMessage("Addons:ContentPolicy:Run", {
contentType: contentType,
mimeTypeGuess: mimeTypeGuess
}, {