Closed Bug 585173 Opened 14 years ago Closed 14 years ago

Add "process message manager"

Categories

(Core :: IPC, defect)

x86
All
defect
Not set
normal

Tracking

()

RESOLVED FIXED
Tracking Status
fennec 2.0b1+ ---

People

(Reporter: smaug, Assigned: smaug)

References

Details

Attachments

(1 file, 2 obsolete files)

This would be pretty similar to the current message managers, but
would allow communication without knowing anything about xul:browsers or 
content windows.
The use case is communication from content process JS components to chrome process.
Blocks: 584842
Whats the status on this? Is there something we can help with?
This isn't assigned to anyone ;)
I think this should be WONTFIX, unless there is a clear and compelling use case for it.
I am thinking about the toolkit components that are written in js.  Callers of these things are be in JS or in C++.  For example, lets consider the content pref service.  It is used by native code in the child process.  For example, it does something like: 

nsCOMPtr<nsIContentPrefService> cpsvc = do_GetService("@mozilla.org/content-pref/service;1", &rv);

The caller has no idea if it is script or c++.  The implementation:

 - is a js component that lives in the content process.
 - directly accesses sqlite
 - doesn't know anything about windows/docshells

So, one approach is to #ifdef and add a check in all callers to toolkit componetns to to see if we are in the child process, and if we are, proxy over using some new protocol / pidl method.

Another approach would be to simple let the toolkit js component in the child process message over to the parent.

Another approach would be to have the js component use the frame message manager.  somehow.  This would require the js component in the child process to figure out which window or docshell it should use.  This might be a bit of guessing -- or it might just be to grab the top most docshell.
Assignee: nobody → doug.turner
Attached patch patch (obsolete) — Splinter Review
So this adds process manager to chrome and content processes.
In chrome process it uses nsIFrameMessageManager interface and in
content process it has also sendSyncMessage.

var ppm = Components.classes["@mozilla.org/parentprocessmessagemanager;1"]
                    .getService(Components.interfaces.nsIFrameMessageManager);

var cpm = Components.classes["@mozilla.org/childprocessmessagemanager;1"]
                    .getService(Components.interfaces.nsISyncMessageSender);

Note, be careful when using the API. It has similar memory management limitations as (also global) observer service; listeners should be
removed manually unless they should stay alive until shutdown.
Assignee: doug.turner → Olli.Pettay
Attachment #468659 - Flags: review?(doug.turner)
Comment on attachment 468659 [details] [diff] [review]
patch


>+static PRBool
>+IsChromeProcess()
>+{
>+  nsCOMPtr<nsIXULRuntime> rt = do_GetService("@mozilla.org/xre/runtime;1");
>+  if (rt) {
>+    PRUint32 type = nsIXULRuntime::PROCESS_TYPE_DEFAULT;
>+    rt->GetProcessType(&type);
>+    return type == nsIXULRuntime::PROCESS_TYPE_DEFAULT;

Maybe add NS_ABORT_IF_FALSE(rt, "We should have a xre runtime");

also, no need to set type before calling GetProcessType -- it will set the value and not fail.

>+  NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIContentFrameMessageManager,
>+                                     !mChrome && !mIsProcessManager)
>+  NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsISyncMessageSender, !mChrome)
>+  NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIChromeFrameMessageManager,
>+                                     mChrome && !mIsProcessManager)


This is getting more complex and probably worth a comment specifically explaining why we do not return success when mIsProcessManager is nonnull.

>+ContentChild::RecvAsyncMessage(const nsString& aMsg, const nsString& aJSON)
>+{
>+  nsRefPtr<nsFrameMessageManager> cpm = nsFrameMessageManager::sChildProcessManager;
>+  if (cpm) {
>+    cpm->ReceiveMessage(static_cast<nsIContentFrameMessageManager*>(cpm.get()),
>+                        aMsg, PR_FALSE, aJSON, nsnull, nsnull);
>+  }
>+  return true;
>+}

if cpm is null, shouldn't we return false? 


>+bool
>+ContentParent::RecvSyncMessage(const nsString& aMsg, const nsString& aJSON,
>+                               nsTArray<nsString>* aRetvals)
>+{
>+  nsRefPtr<nsFrameMessageManager> ppm = nsFrameMessageManager::sParentProcessManager;
>+  if (ppm) {
>+    ppm->ReceiveMessage(static_cast<nsIContentFrameMessageManager*>(ppm.get()),
>+                        aMsg,PR_TRUE, aJSON, nsnull, aRetvals);
>+  }
>+  return true;
>+}

and here.


>+bool
>+ContentParent::RecvAsyncMessage(const nsString& aMsg, const nsString& aJSON)
>+{
>+  nsRefPtr<nsFrameMessageManager> ppm = nsFrameMessageManager::sParentProcessManager;
>+  if (ppm) {
>+    ppm->ReceiveMessage(static_cast<nsIContentFrameMessageManager*>(ppm.get()),
>+                        aMsg, PR_FALSE, aJSON, nsnull, nsnull);
>+  }
>+  return true;
>+}

same.


>diff --git a/dom/ipc/remote-test.js b/dom/ipc/remote-test.js
>--- a/dom/ipc/remote-test.js
>+++ b/dom/ipc/remote-test.js
>@@ -1,11 +1,20 @@
> dump("Loading remote script!\n");
> dump(content + "\n");
> 
>+var cpm = Components.classes["@mozilla.org/childprocessmessagemanager;1"]

nit: i suggested the name, but maybe it is more than a mouthful.  how about:

@mozilla.org/message-manager/child;1

you could also have:

@mozilla.org/message-manager/global;1


I'd really like to see a test js component that uses this code.  Maybe it isn't strictly needed, but that is basically what we hope to use this for.
Attachment #468659 - Flags: review?(doug.turner) → review-
I tested this patch by writing an xpcshell test for another bug. It works nicely. Some questions though:

1. Why do we still have a globalmessagemanager? Doesn't the parentprocess one do the same thing?
2. In my xpcshell test, using the globalmessagemanager didn't work, only the parentprocess one. Otherwise the child sends messages and no one hears them. Is that intentional?
(In reply to comment #7)
> I tested this patch by writing an xpcshell test for another bug. It works
> nicely.
Great.

> 1. Why do we still have a globalmessagemanager? Doesn't the parentprocess one
> do the same thing?
No, it does not do the same thing.
global mm gets messages from a TabChildGlobal and the target for the messages
is the <xul:browser>. So message listener knows which "tab" sent the message.
That is not the case with process mm


> 2. In my xpcshell test, using the globalmessagemanager didn't work, only the
> parentprocess one. Otherwise the child sends messages and no one hears them. Is
> that intentional?
I don't understand this.
xpcshell test doesn't have (chrome) DOMWindow, right? globalmessagemanager
needs one. and it needs a remote xul:browser
(In reply to comment #8)
> (In reply to comment #7)
> > I tested this patch by writing an xpcshell test for another bug. It works
> > nicely.
> Great.
> 
> > 1. Why do we still have a globalmessagemanager? Doesn't the parentprocess one
> > do the same thing?
> No, it does not do the same thing.
> global mm gets messages from a TabChildGlobal and the target for the messages
> is the <xul:browser>. So message listener knows which "tab" sent the message.
> That is not the case with process mm
> 

Thanks for the explanation.

To make sure I understand: Can the parentprocessmanager respond to messages? (As it doesn't know their origin?) It can do so by responding in sync messages, of course - but I guess not in any other way?

> 
> > 2. In my xpcshell test, using the globalmessagemanager didn't work, only the
> > parentprocess one. Otherwise the child sends messages and no one hears them. Is
> > that intentional?
> I don't understand this.
> xpcshell test doesn't have (chrome) DOMWindow, right? globalmessagemanager
> needs one. and it needs a remote xul:browser

Yes, that was probably the issue. So - very nice, this patch lets us do messaging in xpcshell tests now, without DOMWindows or xul:browsers.

The xpcshell test I wrote is here if anyone wants to take a look, maybe it can be useful in writing tests for other bugs:

https://bug584842.bugzilla.mozilla.org/attachment.cgi?id=469620
(In reply to comment #9)

> To make sure I understand: Can the parentprocessmanager respond to messages?
> (As it doesn't know their origin?)
It does know their origin. The origin is the child process mm.

 It can do so by responding in sync messages,
> of course - but I guess not in any other way?
It can send async messages.
(In reply to comment #10)
> (In reply to comment #9)
> 
> > To make sure I understand: Can the parentprocessmanager respond to messages?
> > (As it doesn't know their origin?)
> It does know their origin. The origin is the child process mm.

Well yes, but there could be more than one (once we have multiple child processes).

> 
>  It can do so by responding in sync messages,
> > of course - but I guess not in any other way?
> It can send async messages.

Yes, but it wouldn't know to which child process to send it (again, once we have multiple child processes?).
Blocks: 584401
(In reply to comment #11)
> (In reply to comment #10)
> > (In reply to comment #9)
> > 
> > > To make sure I understand: Can the parentprocessmanager respond to messages?
> > > (As it doesn't know their origin?)
> > It does know their origin. The origin is the child process mm.
> 
> Well yes, but there could be more than one (once we have multiple child
> processes).
And when that happens, the API will change to include some process ID.


> Yes, but it wouldn't know to which child process to send it (again, once we
> have multiple child processes?).
Again, the API will change then.
We don't have multiple child processes yet.
Sounds good, just making sure I understood the current API.
I would like to have the option of multiple child processes in the Mozilla 2.0 branch series. Can we just design the API correctly now, so that we don't have to modify it again in a few months?
bsmedberg, i think that is out of scope for this specific bug.  Fennec needs something yesterday.  Multiple process support can be a follow up.
I think that the design decisions here are going to incur significant technical debt later, and that we should make the parent-side "global" message manager non-singleton in the following way:

Have a global observer notification when a new content process is created (either nsIObserver or something equivalent). That will be passed the parent-global message manager on which listeners can be installed.
Blocks: 591052
(In reply to comment #16)
> Have a global observer notification when a new content process is created
> (either nsIObserver or something equivalent).
> That will be passed the
> parent-global message manager on which listeners can be installed.
Not parent -global, but parent-process message manager.

But I don't understand what you mean.
The current API could be forwards compatible if we add the process id as an
optional parameter to sendAsyncMessage and add/removeMessageListener.
Also, when adding support for multiple child processes, we could extend
messages to contain .processID.
Then, when a new process is created, it could send a message to parent process, 
which would have a listener, which listens for messages from all processes.
And at that point parent process could add process specific listeners.

This all could be done afterwards.
Hrm, I was thinking that there would be one parent message manager per content process. Comment 18 is talking about having one global parent message manager, and adding some context parameter to the messages.

I tend to think one per-content-process will be the simpler solution, but I don't strongly care.
 
> if cpm is null, shouldn't we return false? 
> 
No. If no one has used process message manager, there we don't have one, and
then it is ok to just forget the message.
 
> @mozilla.org/message-manager/child;1
> 
> you could also have:
> 
> @mozilla.org/message-manager/global;1

I want to have "process" somewhere in the name, since process message managers
are a bit different than other message managers.
Attached patch patch (obsolete) — Splinter Review
I hope alon's tests are good enough for this too.
(well, this does have test.xul tests)
Attachment #468659 - Attachment is obsolete: true
Attachment #470402 - Flags: review?(doug.turner)
Comment on attachment 470402 [details] [diff] [review]
patch

>+  nsCOMPtr<nsIXULRuntime> rt = do_GetService("@mozilla.org/xre/runtime;1");
>+  NS_ABORT_IF_FALSE(rt, "We should have a xre runtime");

nit:
s/should/must

>+  if (!ctx) {
>+    nsContentUtils::ThreadJSContextStack()->GetSafeJSContext(&ctx);
>+  }

nit: no need for braces around one statement.

>+#ifdef MOZ_IPC
>+bool SendAsyncMessageToChildProcess(void* aCallbackData,
>+                                    const nsAString& aMessage,
>+                                    const nsAString& aJSON)
>+{
>+  mozilla::dom::ContentParent* cp =
>+    mozilla::dom::ContentParent::GetSingleton(PR_FALSE);
>+  if (cp) {
>+    return cp->SendAsyncMessage(nsString(aMessage), nsString(aJSON));
>+  }

Should we assert or something if cp is false.  I am worried a bit that someone will not realize the content process hasn't started and not understand that their events are dropped.

>-    NS_ASSERTION(mContext || (aChrome && !aParentManager),
>+    NS_ASSERTION(mContext || (aChrome && !aParentManager) || aProcessManager,
>                  "Should have mContext in non-global manager!");

probably should update this assertion text.


>+ContentParent::RecvSyncMessage(const nsString& aMsg, const nsString& aJSON,
>+                               nsTArray<nsString>* aRetvals)
>+{
>+  nsRefPtr<nsFrameMessageManager> ppm = nsFrameMessageManager::sParentProcessManager;
>+  if (ppm) {
>+    ppm->ReceiveMessage(static_cast<nsIContentFrameMessageManager*>(ppm.get()),

r+ w/ nits addressed.
Attachment #470402 - Flags: review?(doug.turner) → review+
> nit: no need for braces around one statement.
Per coding style even a single-line controlled statements should have braces.
Attached patch patchSplinter Review
Attachment #470402 - Attachment is obsolete: true
I guess this can't be pushed to m-c before b5
marking blocking because it blocks other b1 bugs.
tracking-fennec: --- → 2.0b1+
http://hg.mozilla.org/mozilla-central/rev/1d7e7ccda33c
Status: NEW → RESOLVED
Closed: 14 years ago
Resolution: --- → FIXED
After discussion with Olli, I pushed a bustage fix for some configurations of builds (e.g. shared, non-ipc like Thunderbird uses):

http://hg.mozilla.org/mozilla-central/rev/51d17027ff45
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: