Bug 777508 - Notify parent if child processes are terminated through message manager. r=cjones

This commit is contained in:
Philipp von Weitershausen 2012-09-27 22:43:24 -07:00
parent 213e5bb81b
commit 611ab85c83
6 changed files with 184 additions and 14 deletions

View File

@ -577,6 +577,7 @@ ifneq ($(OS_ARCH),WINNT)
ifndef MOZ_JAVA_COMPOSITOR
MOCHITEST_FILES_B += \
test_messagemanager_assertpermission.html \
test_child_process_shutdown_message.html \
$(NULL)
endif
endif

View File

@ -0,0 +1,136 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Test that processes that are shutdown send a 'process-shutdown'
message to their process message manager.</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body onload="runTests();">
<p id="display">
</p>
<div id="content" style="display: none">
</div>
<pre id="test">
<script class="testbody" type="application/javascript;version=1.8">
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = SpecialPowers.wrap(Components);
const APP_URL = "http://example.org";
const APP_MANIFEST = "http://example.org/manifest.webapp";
const CHILD_PROCESS_SHUTDOWN_MESSAGE = "child-process-shutdown";
let ppmm = Cc["@mozilla.org/parentprocessmessagemanager;1"]
.getService(Ci.nsIMessageBroadcaster);
/**
* Load the example.org app in an <iframe mozbrowser mozapp>
*/
function loadApp(callback) {
let iframe = document.createElement("iframe");
iframe.setAttribute("mozapp", APP_MANIFEST);
iframe.mozbrowser = true;
iframe.src = APP_URL;
document.getElementById("content").appendChild(iframe);
iframe.addEventListener("mozbrowserloadend", function onloadend() {
iframe.removeEventListener("mozbrowserloadend", onloadend);
callback(iframe);
});
}
/**
* Prepare the child process for an intentional crash. This is to keep
* the leak automation tools happy.
*
* This also allows us to acquire the process message manaager that
* corresponds to the process by sending a message to a frame script
* in the content process and having it reply to us via the child
* process message manager.
*/
function prepareProcess(frameMM, callback) {
let frameScript = 'data:,\
privateNoteIntentionalCrash();\
var cpmm = Components.classes["@mozilla.org/childprocessmessagemanager;1"]\
.getService(Components.interfaces.nsISyncMessageSender);\
addMessageListener("TestChild:Ohai", function receiveMessage(msg) {\
cpmm.sendAsyncMessage("TestChild:Ohai");\
});';
frameMM.loadFrameScript(frameScript, false);
frameMM.sendAsyncMessage("TestChild:Ohai");
ppmm.addMessageListener("TestChild:Ohai", function receiveMessage(msg) {
ppmm.removeMessageListener("TestChild:Ohai", receiveMessage);
msg = SpecialPowers.wrap(msg);
callback(msg.target);
});
}
/**
* Expects an OOP frame's process to shut down and report three
* events/messages: an error event on the browser element, and a
* 'child-process-shutdown' message on both the frame and process
* message managers.
*/
function expectFrameProcessShutdown(iframe, frameMM, processMM, callback) {
let msgCount = 0;
function countMessage() {
msgCount += 1;
if (msgCount == 3) {
ok(true, "Observed all three expected events.");
callback();
}
};
iframe.addEventListener("mozbrowsererror", function onerror(event) {
iframe.removeEventListener("mozbrowsererror", onerror);
is(event.detail.type, "fatal", "Observed expected event.");
countMessage();
});
processMM.addMessageListener(CHILD_PROCESS_SHUTDOWN_MESSAGE, function receiveMessage() {
processMM.removeMessageListener(CHILD_PROCESS_SHUTDOWN_MESSAGE, receiveMessage);
ok(true, "Received 'child-process-shutdown' message from process message manager.");
countMessage();
});
frameMM.addMessageListener(CHILD_PROCESS_SHUTDOWN_MESSAGE, function receiveMessage() {
frameMM.removeMessageListener(CHILD_PROCESS_SHUTDOWN_MESSAGE, receiveMessage);
ok(true, "Received 'child-process-shutdown' message from frame message manager.");
countMessage();
});
}
function runTests(callback) {
SpecialPowers.setBoolPref("dom.mozBrowserFramesEnabled", true);
SpecialPowers.setBoolPref("dom.ipc.browser_frames.oop_by_default", true);
SpecialPowers.addPermission("browser", true, window.document);
function tearDown() {
SpecialPowers.clearUserPref("dom.mozBrowserFramesEnabled");
SpecialPowers.clearUserPref("dom.ipc.browser_frames.oop_by_default");
SimpleTest.finish();
}
loadApp(function (iframe) {
// We want to make sure we get notified on both the frame and
// process message managers.
let frameMM = SpecialPowers.getBrowserFrameMessageManager(iframe);
prepareProcess(frameMM, function (processMM) {
// Let's kill the content process by asking for a permission
// that it doesn't have.
ok(!processMM.assertPermission("frobnaz"),
"Content child should not have this permission");
expectFrameProcessShutdown(iframe, frameMM, processMM, function () {
iframe.parentNode.removeChild(iframe);
tearDown();
});
});
});
}
</script>
</pre>
</body>
</html>

View File

@ -19,6 +19,7 @@ const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = SpecialPowers.wrap
const APP_URL = "http://example.org";
const APP_MANIFEST = "http://example.org/manifest.webapp";
const CHILD_PROCESS_SHUTDOWN_MESSAGE = "child-process-shutdown";
let ppmm = Cc["@mozilla.org/parentprocessmessagemanager;1"]
.getService(Ci.nsIMessageBroadcaster);
@ -81,6 +82,41 @@ function prepareProcess(frameMM, callback) {
});
}
/**
* Expects an OOP frame's process to shut down and report three
* events/messages: an error event on the browser element, and a
* 'child-process-shutdown' message on both the frame and process
* message managers.
*/
function expectFrameProcessShutdown(iframe, frameMM, processMM, callback) {
let msgCount = 0;
function countMessage() {
msgCount += 1;
if (msgCount == 3) {
ok(true, "Observed all three expected events.");
callback();
}
};
iframe.addEventListener("mozbrowsererror", function onerror(event) {
iframe.removeEventListener("mozbrowsererror", onerror);
is(event.detail.type, "fatal", "Observed expected event.");
countMessage();
});
processMM.addMessageListener(CHILD_PROCESS_SHUTDOWN_MESSAGE, function receiveMessage() {
processMM.removeMessageListener(CHILD_PROCESS_SHUTDOWN_MESSAGE, receiveMessage);
ok(true, "Received 'child-process-shutdown' message from process message manager.");
countMessage();
});
frameMM.addMessageListener(CHILD_PROCESS_SHUTDOWN_MESSAGE, function receiveMessage() {
frameMM.removeMessageListener(CHILD_PROCESS_SHUTDOWN_MESSAGE, receiveMessage);
ok(true, "Received 'child-process-shutdown' message from frame message manager.");
countMessage();
});
}
function testSameProcess() {
// Assert permissions on the in-process child process message manager.
// It always has all permissions, including ones that were never
@ -106,13 +142,7 @@ function testFrameMessageManager() {
"Frame mm has assigned permission.");
ok(!frameMM.assertPermission("frobnaz"),
"Frame mm doesn't have non-existing permission.");
// The last permission check will result in the content process
// being killed.
iframe.addEventListener("mozbrowsererror", function onerror(event) {
iframe.removeEventListener("mozbrowsererror", onerror);
is(event.detail.type, "fatal", "Observed expected event.");
expectFrameProcessShutdown(iframe, frameMM, processMM, function () {
iframe.parentNode.removeChild(iframe);
runNextTest();
});
@ -130,13 +160,7 @@ function testChildProcessMessageManager() {
"Process mm has assigned permission.");
ok(!processMM.assertPermission("frobnaz"),
"Process mm doesn't have non-existing permission.");
// The last permission check will result in the content process
// being killed.
iframe.addEventListener("mozbrowsererror", function onerror(event) {
iframe.removeEventListener("mozbrowsererror", onerror);
is(event.detail.type, "fatal", "Observed expected event.");
expectFrameProcessShutdown(iframe, frameMM, processMM, function () {
iframe.parentNode.removeChild(iframe);
runNextTest();
});

View File

@ -573,6 +573,12 @@ struct DelayedDeleteContentParentTask : public nsRunnable
void
ContentParent::ActorDestroy(ActorDestroyReason why)
{
nsRefPtr<nsFrameMessageManager> ppm = mMessageManager;
if (ppm) {
ppm->ReceiveMessage(static_cast<nsIContentFrameMessageManager*>(ppm.get()),
CHILD_PROCESS_SHUTDOWN_MESSAGE, false,
nullptr, nullptr, nullptr);
}
nsCOMPtr<nsIThreadObserver>
kungFuDeathGrip(static_cast<nsIThreadObserver*>(this));
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();

View File

@ -26,6 +26,8 @@
#include "nsDataHashtable.h"
#include "nsHashKeys.h"
#define CHILD_PROCESS_SHUTDOWN_MESSAGE NS_LITERAL_STRING("child-process-shutdown")
class mozIApplication;
class nsIDOMBlob;

View File

@ -132,6 +132,7 @@ TabParent::ActorDestroy(ActorDestroyReason why)
}
nsRefPtr<nsFrameLoader> frameLoader = GetFrameLoader();
if (frameLoader) {
ReceiveMessage(CHILD_PROCESS_SHUTDOWN_MESSAGE, false, nullptr, nullptr);
frameLoader->DestroyChild();
if (why == AbnormalShutdown) {