From e5c2639df5b0489dba033dc384e8dbe03f3e8f78 Mon Sep 17 00:00:00 2001 From: Olli Pettay Date: Sat, 27 Mar 2010 18:54:57 +0200 Subject: [PATCH] Bug 554941 - [E10s] CPOW for synchronous TabChildGlobal messages, r=bnewman --- content/base/src/nsFrameMessageManager.cpp | 5 +- content/base/src/nsFrameMessageManager.h | 3 + dom/ipc/ContentProcessParent.cpp | 20 +++++- dom/ipc/ContentProcessParent.h | 2 + dom/ipc/PIFrameEmbedding.ipdl | 5 +- dom/ipc/TabChild.cpp | 47 +++++++++++- dom/ipc/TabChild.h | 4 ++ dom/ipc/TabParent.cpp | 83 +++++++++++++++------- dom/ipc/TabParent.h | 14 +++- dom/ipc/remote-test.js | 2 +- dom/ipc/test.xul | 24 +++++-- js/src/ipc/ObjectWrapperChild.cpp | 2 +- 12 files changed, 171 insertions(+), 40 deletions(-) diff --git a/content/base/src/nsFrameMessageManager.cpp b/content/base/src/nsFrameMessageManager.cpp index 3694af19288..f18aeb59463 100644 --- a/content/base/src/nsFrameMessageManager.cpp +++ b/content/base/src/nsFrameMessageManager.cpp @@ -284,6 +284,7 @@ nsresult nsFrameMessageManager::ReceiveMessage(nsISupports* aTarget, const nsAString& aMessage, PRBool aSync, const nsAString& aJSON, + JSObject* aObjectsArray, nsTArray* aJSONRetVal) { if (mListeners.Length()) { @@ -347,6 +348,8 @@ nsFrameMessageManager::ReceiveMessage(nsISupports* aTarget, JS_DefineProperty(mContext, param, "sync", BOOLEAN_TO_JSVAL(aSync), NULL, NULL, JSPROP_ENUMERATE); JS_DefineProperty(mContext, param, "json", json, NULL, NULL, JSPROP_ENUMERATE); + JS_DefineProperty(mContext, param, "objects", OBJECT_TO_JSVAL(aObjectsArray), + NULL, NULL, JSPROP_ENUMERATE); jsval thisValue = JSVAL_VOID; nsAutoGCRoot resultGCRoot3(&thisValue, &rv); @@ -397,7 +400,7 @@ nsFrameMessageManager::ReceiveMessage(nsISupports* aTarget, } } return mParentManager ? mParentManager->ReceiveMessage(aTarget, aMessage, - aSync, aJSON, + aSync, aJSON, aObjectsArray, aJSONRetVal) : NS_OK; } diff --git a/content/base/src/nsFrameMessageManager.h b/content/base/src/nsFrameMessageManager.h index ed29af1ef04..9c2104b7001 100644 --- a/content/base/src/nsFrameMessageManager.h +++ b/content/base/src/nsFrameMessageManager.h @@ -48,6 +48,7 @@ class nsAXPCNativeCallContext; struct JSContext; +struct JSObject; struct nsMessageListenerInfo { @@ -103,6 +104,7 @@ public: nsresult ReceiveMessage(nsISupports* aTarget, const nsAString& aMessage, PRBool aSync, const nsAString& aJSON, + JSObject* aObjectsArray, nsTArray* aJSONRetVal); void AddChildManager(nsFrameMessageManager* aManager); void RemoveChildManager(nsFrameMessageManager* aManager) @@ -115,6 +117,7 @@ public: nsresult GetParamsForMessage(nsAString& aMessageName, nsAString& aJSON); nsresult SendAsyncMessageInternal(const nsAString& aMessage, const nsAString& aJSON); + JSContext* GetJSContext() { return mContext; } protected: nsTArray mListeners; nsCOMArray mChildManagers; diff --git a/dom/ipc/ContentProcessParent.cpp b/dom/ipc/ContentProcessParent.cpp index 0d0b9c581c7..1578891b51a 100644 --- a/dom/ipc/ContentProcessParent.cpp +++ b/dom/ipc/ContentProcessParent.cpp @@ -126,6 +126,7 @@ ContentProcessParent::DestroyTestShell(TestShellParent* aTestShell) ContentProcessParent::ContentProcessParent() : mMonitor("ContentProcessParent::mMonitor") , mRunToCompletionDepth(0) + , mShouldCallUnblockChild(false) { NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); mSubprocess = new GeckoChildProcessHost(GeckoProcessType_Content); @@ -217,6 +218,18 @@ ContentProcessParent::DeallocPNecko(PNeckoParent* necko) return true; } +void +ContentProcessParent::ReportChildAlreadyBlocked() +{ + if (!mRunToCompletionDepth) { +#ifdef DEBUG + printf("Running to completion...\n"); +#endif + mRunToCompletionDepth = 1; + mShouldCallUnblockChild = false; + } +} + bool ContentProcessParent::RequestRunToCompletion() { @@ -226,8 +239,8 @@ ContentProcessParent::RequestRunToCompletion() printf("Running to completion...\n"); #endif mRunToCompletionDepth = 1; + mShouldCallUnblockChild = true; } - return !!mRunToCompletionDepth; } @@ -266,7 +279,10 @@ ContentProcessParent::AfterProcessNextEvent(nsIThreadInternal *thread, #ifdef DEBUG printf("... ran to completion.\n"); #endif - UnblockChild(); + if (mShouldCallUnblockChild) { + mShouldCallUnblockChild = false; + UnblockChild(); + } } if (mOldObserver) diff --git a/dom/ipc/ContentProcessParent.h b/dom/ipc/ContentProcessParent.h index 3f3e9e9a620..bf3fab7f086 100644 --- a/dom/ipc/ContentProcessParent.h +++ b/dom/ipc/ContentProcessParent.h @@ -83,6 +83,7 @@ public: TestShellParent* CreateTestShell(); bool DestroyTestShell(TestShellParent* aTestShell); + void ReportChildAlreadyBlocked(); bool RequestRunToCompletion(); protected: @@ -113,6 +114,7 @@ private: GeckoChildProcessHost* mSubprocess; int mRunToCompletionDepth; + bool mShouldCallUnblockChild; nsCOMPtr mOldObserver; }; diff --git a/dom/ipc/PIFrameEmbedding.ipdl b/dom/ipc/PIFrameEmbedding.ipdl index 96d25343581..69b7a2dd4f2 100644 --- a/dom/ipc/PIFrameEmbedding.ipdl +++ b/dom/ipc/PIFrameEmbedding.ipdl @@ -41,6 +41,7 @@ include protocol "PContentProcess.ipdl"; include protocol "PDocumentRenderer.ipdl"; include protocol "PDocumentRendererShmem.ipdl"; include protocol "PContextWrapper.ipdl"; +include protocol "PObjectWrapper.ipdl"; include "mozilla/TabTypes.h"; include "TabMessageUtils.h"; @@ -94,7 +95,9 @@ parent: PContextWrapper(); - sync sendSyncMessageToParent(nsString aMessage, nsString aJSON) returns (nsString[] retval); + rpc sendSyncMessageToParent(nsString aMessage, nsString aJSON, PObjectWrapper[] aObjects) + returns (nsString[] retval); + sendAsyncMessageToParent(nsString aMessage, nsString aJSON); child: createWidget(MagicWindowHandle parentWidget); diff --git a/dom/ipc/TabChild.cpp b/dom/ipc/TabChild.cpp index 846c17ed5c7..93fc8ffb4b2 100644 --- a/dom/ipc/TabChild.cpp +++ b/dom/ipc/TabChild.cpp @@ -102,6 +102,7 @@ ContentListener::HandleEvent(nsIDOMEvent* aEvent) } TabChild::TabChild() +: mCx(nsnull), mContextWrapper(nsnull), mTabChildGlobal(nsnull) { printf("creating %d!\n", NS_IsMainThread()); } @@ -757,10 +758,9 @@ TabChild::RecvsendAsyncMessageToChild(const nsString& aMessage, const nsString& aJSON) { if (mTabChildGlobal) { - nsTArray dummy; static_cast(mTabChildGlobal->mMessageManager.get())-> ReceiveMessage(static_cast(mTabChildGlobal), - aMessage, PR_FALSE, aJSON, nsnull); + aMessage, PR_FALSE, aJSON, nsnull, nsnull); } return true; } @@ -846,16 +846,57 @@ TabChild::InitTabChildGlobal() JS_SetGlobalObject(cx, global); + mContextWrapper = new ContextWrapperChild(mCx); + SendPContextWrapperConstructor(mContextWrapper); + return true; } +nsresult +TabChild::GetObjectsForMessage(nsTArray& aObjects) +{ + nsAXPCNativeCallContext* ncc = nsnull; + nsresult rv = nsContentUtils::XPConnect()->GetCurrentNativeCallContext(&ncc); + NS_ENSURE_SUCCESS(rv, rv); + NS_ENSURE_STATE(ncc); + + JSContext* ctx = nsnull; + rv = ncc->GetJSContext(&ctx); + NS_ENSURE_SUCCESS(rv, rv); + + PRUint32 argc; + jsval* argv = nsnull; + ncc->GetArgc(&argc); + ncc->GetArgvPtr(&argv); + + JSAutoRequest ar(ctx); + + JSObject* obj = nsnull; + nsAutoGCRoot resultGCRoot(&obj, &rv); + NS_ENSURE_SUCCESS(rv, rv); + // First parameter is the message name, second is the JSON. + for (PRUint32 i = 2; i < argc; ++i) { + if (JSVAL_IS_OBJECT(argv[i])) { + obj = JSVAL_TO_OBJECT(argv[i]); + } else if (!JS_ValueToObject(ctx, argv[i], &obj)) { + obj = nsnull; + } + // GetOrCreateWrapper is null safe! + aObjects.AppendElement(mContextWrapper->GetOrCreateWrapper(obj)); + } + return NS_OK; +} + bool SendSyncMessageToParent(void* aCallbackData, const nsAString& aMessage, const nsAString& aJSON, nsTArray* aJSONRetVal) { + nsTArray objects; + static_cast(aCallbackData)->GetObjectsForMessage(objects); return static_cast(aCallbackData)-> - SendsendSyncMessageToParent(nsString(aMessage), nsString(aJSON), aJSONRetVal); + CallsendSyncMessageToParent(nsString(aMessage), nsString(aJSON), objects, + aJSONRetVal); } bool SendAsyncMessageToParent(void* aCallbackData, diff --git a/dom/ipc/TabChild.h b/dom/ipc/TabChild.h index dc3217d404c..9f37a143af3 100644 --- a/dom/ipc/TabChild.h +++ b/dom/ipc/TabChild.h @@ -75,6 +75,7 @@ namespace mozilla { namespace jsipc { class PContextWrapperChild; +class ContextWrapperChild; } namespace dom { @@ -244,6 +245,7 @@ public: virtual PContextWrapperChild* AllocPContextWrapper(); virtual bool DeallocPContextWrapper(PContextWrapperChild* actor); + nsresult GetObjectsForMessage(nsTArray& aObjects); private: bool InitTabChildGlobal(); @@ -253,6 +255,8 @@ private: JSContext* mCx; + mozilla::jsipc::ContextWrapperChild* mContextWrapper; + nsCOMPtr mChannel; TabChildGlobal* mTabChildGlobal; diff --git a/dom/ipc/TabParent.cpp b/dom/ipc/TabParent.cpp index 66484615d9f..de4216fd8ab 100644 --- a/dom/ipc/TabParent.cpp +++ b/dom/ipc/TabParent.cpp @@ -55,6 +55,8 @@ #include "nsIWebProgressListener2.h" #include "nsFrameLoader.h" #include "nsNetUtil.h" +#include "jsarray.h" +#include "nsContentUtils.h" using mozilla::ipc::DocumentRendererParent; using mozilla::ipc::DocumentRendererShmemParent; @@ -443,9 +445,20 @@ TabParent::GetGlobalJSObject(JSContext* cx, JSObject** globalp) ManagedPContextWrapperParent(cwps); if (cwps.Length() < 1) return false; - NS_ASSERTION(cwps.Length() == 1, "More than one PContextWrapper?"); + + // This is temporary until we decide whether to return + // TabChildGlobal or top level page's global object. + // Currently this returns the page global object. + // Note, TabChildGlobal's context doesn't report its global object here! + NS_ASSERTION(cwps.Length() <= 2, "More than two PContextWrappers?"); ContextWrapperParent* cwp = static_cast(cwps[0]); - return (cwp->GetGlobalJSObject(cx, globalp)); + if (cwp->GetGlobalObjectWrapper()) { + return cwp->GetGlobalJSObject(cx, globalp); + } else if (cwps.Length() == 2) { + cwp = static_cast(cwps[1]); + return cwp->GetGlobalJSObject(cx, globalp); + } + return false; } void @@ -469,40 +482,62 @@ TabParent::SendKeyEvent(const nsAString& aType, } bool -TabParent::RecvsendSyncMessageToParent(const nsString& aMessage, - const nsString& aJSON, - nsTArray* aJSONRetVal) +TabParent::AnswersendSyncMessageToParent(const nsString& aMessage, + const nsString& aJSON, + const nsTArray& aObjects, + nsTArray* aJSONRetVal) { - nsCOMPtr frameLoaderOwner = - do_QueryInterface(mFrameElement); - if (frameLoaderOwner) { - nsRefPtr frameLoader = frameLoaderOwner->GetFrameLoader(); - if (frameLoader && frameLoader->GetFrameMessageManager()) { - frameLoader->GetFrameMessageManager()->ReceiveMessage(mFrameElement, - aMessage, - PR_TRUE, - aJSON, - aJSONRetVal); - } - } - return true; + static_cast(Manager())->ReportChildAlreadyBlocked(); + return ReceiveMessage(aMessage, PR_TRUE, aJSON, &aObjects, aJSONRetVal); } bool TabParent::RecvsendAsyncMessageToParent(const nsString& aMessage, const nsString& aJSON) +{ + return ReceiveMessage(aMessage, PR_FALSE, aJSON, nsnull); +} + +bool +TabParent::ReceiveMessage(const nsString& aMessage, + PRBool aSync, + const nsString& aJSON, + const nsTArray* aObjects, + nsTArray* aJSONRetVal) { nsCOMPtr frameLoaderOwner = do_QueryInterface(mFrameElement); if (frameLoaderOwner) { nsRefPtr frameLoader = frameLoaderOwner->GetFrameLoader(); if (frameLoader && frameLoader->GetFrameMessageManager()) { - nsTArray dummy; - frameLoader->GetFrameMessageManager()->ReceiveMessage(mFrameElement, - aMessage, - PR_FALSE, - aJSON, - nsnull); + nsFrameMessageManager* manager = frameLoader->GetFrameMessageManager(); + JSContext* ctx = manager->GetJSContext(); + JSAutoRequest ar(ctx); + jsval* dest; + PRUint32 len = aObjects ? aObjects->Length() : 0; + // Because we want JS messages to have always the same properties, + // create array even if len == 0. + JSObject* objectsArray = + js_NewArrayObjectWithCapacity(ctx, len, &dest); + if (!objectsArray) { + return false; + } + + nsresult rv = NS_OK; + nsAutoGCRoot arrayGCRoot(&objectsArray, &rv); + NS_ENSURE_SUCCESS(rv, false); + for (PRUint32 i = 0; i < len; ++i) { + mozilla::jsipc::ObjectWrapperParent* wrapper = + static_cast(aObjects->ElementAt(i)); + dest[i] = OBJECT_TO_JSVAL(wrapper ? wrapper->GetJSObject(ctx) : nsnull); + } + + manager->ReceiveMessage(mFrameElement, + aMessage, + aSync, + aJSON, + objectsArray, + aJSONRetVal); } } return true; diff --git a/dom/ipc/TabParent.h b/dom/ipc/TabParent.h index 87cb30457c1..e5e7af9ab89 100644 --- a/dom/ipc/TabParent.h +++ b/dom/ipc/TabParent.h @@ -60,6 +60,7 @@ namespace mozilla { namespace jsipc { class PContextWrapperParent; +class PObjectWrapperParent; } namespace dom { @@ -114,9 +115,10 @@ public: bool* aAllowRefresh); virtual bool AnswercreateWindow(PIFrameEmbeddingParent** retval); - virtual bool RecvsendSyncMessageToParent(const nsString& aMessage, - const nsString& aJSON, - nsTArray* aJSONRetVal); + virtual bool AnswersendSyncMessageToParent(const nsString& aMessage, + const nsString& aJSON, + const nsTArray&, + nsTArray* aJSONRetVal); virtual bool RecvsendAsyncMessageToParent(const nsString& aMessage, const nsString& aJSON); @@ -163,6 +165,12 @@ public: NS_DECL_NSIWEBPROGRESS protected: + bool ReceiveMessage(const nsString& aMessage, + PRBool aSync, + const nsString& aJSON, + const nsTArray* aObjects, + nsTArray* aJSONRetVal = nsnull); + TabParentListenerInfo* GetListenerInfo(nsIWebProgressListener *aListener); nsIDOMElement* mFrameElement; diff --git a/dom/ipc/remote-test.js b/dom/ipc/remote-test.js index 2e66aee24fe..40255710493 100644 --- a/dom/ipc/remote-test.js +++ b/dom/ipc/remote-test.js @@ -15,7 +15,7 @@ addEventListener("click", dump(e.target + "\n"); if (e.target instanceof Components.interfaces.nsIDOMHTMLAnchorElement && dshell == docShell) { - var retval = sendSyncMessage("linkclick", { href : e.target.href }); + var retval = sendSyncMessage("linkclick", { href : e.target.href }, e, 123, "a string"); dump(uneval(retval[0]) + "\n"); // Test here also that both retvals are the same sendAsyncMessage("linkclick-reply-object", uneval(retval[0]) == uneval(retval[1]) ? retval[0] : ""); diff --git a/dom/ipc/test.xul b/dom/ipc/test.xul index d5e7b3f37eb..8bdc8812b0c 100644 --- a/dom/ipc/test.xul +++ b/dom/ipc/test.xul @@ -98,8 +98,19 @@ // 2. Test that adding message listener works, and that receiving a sync message works. messageManager.addMessageListener("linkclick", function(m) { - document.getElementById("messageLog").value = m.name + ": " + m.json.href; - return { message: "linkclick-received" }; + // This checks that json and CPOW sending works in sync messages. + if (m.objects.length == 3 && + m.objects[0].target.href == m.json.href && + m.objects[1] == 123 && + m.objects[2] == "a string") { + // Test that crossProcessObjectWrapper can be used in sync messages. + if ((document.getElementById('page') + .QueryInterface(Components.interfaces.nsIFrameLoaderOwner) + .crossProcessObjectWrapper + "").indexOf("Window") >= 0) { + document.getElementById("messageLog").value = m.name + ": " + m.json.href; + return { message: "linkclick-received" }; + } + } }); // 3. Test that returning multiple json results works. @@ -124,9 +135,14 @@ // 5. Final test to check that everything went ok. messageManager.addMessageListener("chrome-message-reply", function(m) { - // Check that 'this' and .target values are handled correctly. + // Check that 'this' and .target values are handled correctly + // Check also that crossProcessObjectWrapper can be used in + // asynchronous message handlers. if (m.target == document.getElementById("page") && - this == messageManager) { + this == messageManager && + (document.getElementById('page') + .QueryInterface(Components.interfaces.nsIFrameLoaderOwner) + .crossProcessObjectWrapper + "").indexOf("Window") >= 0) { // Check that the message properties are enumerable. var hasName = false; var hasSync = false; diff --git a/js/src/ipc/ObjectWrapperChild.cpp b/js/src/ipc/ObjectWrapperChild.cpp index 44082abe842..3c6edad8a8e 100644 --- a/js/src/ipc/ObjectWrapperChild.cpp +++ b/js/src/ipc/ObjectWrapperChild.cpp @@ -73,7 +73,7 @@ namespace { JSOPTION_DONT_REPORT_UNCAUGHT))) { JS_GUARD_OBJECT_NOTIFIER_INIT; - mStack.Push(cx); + mStack.Push(cx, PR_FALSE); } ~AutoContextPusher() {