Bug 541076 - add minidump id and plugin name to PluginCrashed event. p=ted+dolske, r=jst, r=ted

This commit is contained in:
Ted Mielczarek 2010-02-09 17:05:31 -08:00
parent 5dc97f65d8
commit 2a33518c3c
8 changed files with 198 additions and 15 deletions

View File

@ -124,5 +124,6 @@ interface nsIObjectLoadingContent : nsISupports
*/
[noscript] nsIFrame getPrintFrame();
[noscript] void pluginCrashed();
[noscript] void pluginCrashed(in AString pluginName,
in boolean submittedCrashReport);
};

View File

@ -44,9 +44,13 @@
// Interface headers
#include "imgILoader.h"
#include "nsEventDispatcher.h"
#include "nsIContent.h"
#include "nsIDocShell.h"
#include "nsIDocument.h"
#include "nsIDOMDataContainerEvent.h"
#include "nsIDOMDocumentEvent.h"
#include "nsIDOMEventTarget.h"
#include "nsIExternalProtocolHandler.h"
#include "nsIEventStateManager.h"
#include "nsIObjectFrame.h"
@ -54,6 +58,7 @@
#include "nsIPluginHost.h"
#include "nsIPluginInstance.h"
#include "nsIPresShell.h"
#include "nsIPrivateDOMEvent.h"
#include "nsIScriptGlobalObject.h"
#include "nsIScriptSecurityManager.h"
#include "nsIStreamConverterService.h"
@ -79,6 +84,7 @@
#include "nsNetUtil.h"
#include "nsMimeTypes.h"
#include "nsStyleUtil.h"
#include "nsGUIEvent.h"
// Concrete classes
#include "nsFrameLoader.h"
@ -200,9 +206,6 @@ nsPluginErrorEvent::Run()
case ePluginOutdated:
type = NS_LITERAL_STRING("PluginOutdated");
break;
case ePluginCrashed:
type = NS_LITERAL_STRING("PluginCrashed");
break;
default:
return NS_OK;
}
@ -212,6 +215,79 @@ nsPluginErrorEvent::Run()
return NS_OK;
}
/**
* A task for firing PluginCrashed DOM Events.
*/
class nsPluginCrashedEvent : public nsRunnable {
public:
nsCOMPtr<nsIContent> mContent;
nsString mPluginName;
PRBool mSubmittedCrashReport;
nsPluginCrashedEvent(nsIContent* aContent,
const nsAString& aPluginName,
PRBool submittedCrashReport)
: mContent(aContent),
mPluginName(aPluginName),
mSubmittedCrashReport(submittedCrashReport)
{}
~nsPluginCrashedEvent() {}
NS_IMETHOD Run();
};
NS_IMETHODIMP
nsPluginCrashedEvent::Run()
{
LOG(("OBJLC []: Firing plugin crashed event for content %p\n",
mContent.get()));
nsCOMPtr<nsIDOMDocumentEvent> domEventDoc =
do_QueryInterface(mContent->GetDocument());
if (!domEventDoc) {
NS_WARNING("Couldn't get document for PluginCrashed event!");
return NS_OK;
}
nsCOMPtr<nsIDOMEvent> event;
domEventDoc->CreateEvent(NS_LITERAL_STRING("datacontainerevents"),
getter_AddRefs(event));
nsCOMPtr<nsIPrivateDOMEvent> privateEvent(do_QueryInterface(event));
nsCOMPtr<nsIDOMDataContainerEvent> containerEvent(do_QueryInterface(event));
if (!privateEvent || !containerEvent) {
NS_WARNING("Couldn't QI event for PluginCrashed event!");
return NS_OK;
}
event->InitEvent(NS_LITERAL_STRING("PluginCrashed"), PR_TRUE, PR_TRUE);
privateEvent->SetTrusted(PR_TRUE);
privateEvent->GetInternalNSEvent()->flags |= NS_EVENT_FLAG_ONLY_CHROME_DISPATCH;
nsCOMPtr<nsIWritableVariant> variant;
// add a "pluginName" property to this event
variant = do_CreateInstance("@mozilla.org/variant;1");
if (!variant) {
NS_WARNING("Couldn't create pluginName variant for PluginCrashed event!");
return NS_OK;
}
variant->SetAsAString(mPluginName);
containerEvent->SetData(NS_LITERAL_STRING("pluginName"), variant);
// add a "submittedCrashReport" property to this event
variant = do_CreateInstance("@mozilla.org/variant;1");
if (!variant) {
NS_WARNING("Couldn't create crashSubmit variant for PluginCrashed event!");
return NS_OK;
}
variant->SetAsBool(mSubmittedCrashReport);
containerEvent->SetData(NS_LITERAL_STRING("submittedCrashReport"), variant);
nsEventDispatcher::DispatchDOMEvent(mContent, nsnull, event, nsnull, nsnull);
return NS_OK;
}
class AutoNotifier {
public:
AutoNotifier(nsObjectLoadingContent* aContent, PRBool aNotify) :
@ -1932,12 +2008,19 @@ nsObjectLoadingContent::SetAbsoluteScreenPosition(nsIDOMElement* element,
}
NS_IMETHODIMP
nsObjectLoadingContent::PluginCrashed()
nsObjectLoadingContent::PluginCrashed(const nsAString& pluginName,
PRBool submittedCrashReport)
{
AutoNotifier notifier(this, PR_TRUE);
UnloadContent();
mFallbackReason = ePluginCrashed;
nsCOMPtr<nsIContent> thisContent = do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
FirePluginError(thisContent, mFallbackReason);
nsCOMPtr<nsIRunnable> ev = new nsPluginCrashedEvent(thisContent,
pluginName,
submittedCrashReport);
nsresult rv = NS_DispatchToCurrentThread(ev);
if (NS_FAILED(rv)) {
NS_WARNING("failed to dispatch nsPluginCrashedEvent");
}
return NS_OK;
}

View File

@ -50,6 +50,24 @@ using namespace mozilla::plugins;
PR_STATIC_ASSERT(sizeof(NPIdentifier) == sizeof(void*));
class PluginCrashed : public nsRunnable
{
public:
PluginCrashed(nsNPAPIPlugin* plugin,
const nsString& dumpID)
: mDumpID(dumpID),
mPlugin(plugin) { }
NS_IMETHOD Run() {
mPlugin->PluginCrashed(mDumpID);
return NS_OK;
}
private:
nsNPAPIPlugin* mPlugin;
nsString mDumpID;
};
// static
PluginLibrary*
PluginModuleParent::LoadModule(const char* aFilePath)
@ -160,8 +178,13 @@ PluginModuleParent::ActorDestroy(ActorDestroyReason why)
switch (why) {
case AbnormalShutdown: {
nsCOMPtr<nsIFile> dump;
nsAutoString dumpID;
if (GetMinidump(getter_AddRefs(dump))) {
WriteExtraDataForMinidump(dump);
if (NS_SUCCEEDED(dump->GetLeafName(dumpID))) {
dumpID.Replace(dumpID.Length() - 4, 4,
NS_LITERAL_STRING(""));
}
}
else {
NS_WARNING("[PluginModuleParent::ActorDestroy] abnormal shutdown without minidump!");
@ -172,8 +195,7 @@ PluginModuleParent::ActorDestroy(ActorDestroyReason why)
// and potentially modify the actor child list while enumerating it.
if (mPlugin) {
nsCOMPtr<nsIRunnable> r =
new nsRunnableMethod<nsNPAPIPlugin>(
mPlugin, &nsNPAPIPlugin::PluginCrashed);
new PluginCrashed(mPlugin, dumpID);
NS_DispatchToMainThread(r);
}
break;

View File

@ -300,10 +300,10 @@ nsNPAPIPlugin::SetPluginRefNum(short aRefNum)
#ifdef MOZ_IPC
void
nsNPAPIPlugin::PluginCrashed()
nsNPAPIPlugin::PluginCrashed(const nsAString& dumpID)
{
nsRefPtr<nsPluginHost> host = dont_AddRef(nsPluginHost::GetInst());
host->PluginCrashed(this);
host->PluginCrashed(this, dumpID);
}
#endif

View File

@ -93,8 +93,9 @@ public:
#ifdef MOZ_IPC
// The IPC mechanism notifies the nsNPAPIPlugin if the plugin crashes and is
// no longer usable.
void PluginCrashed();
// no longer usable. dumpID is the ID of a minidump that was written,
// or empty if no minidump was written.
void PluginCrashed(const nsAString& dumpID);
#endif
protected:

View File

@ -89,6 +89,7 @@
#include "nsVersionComparator.h"
#include "nsIPrivateBrowsingService.h"
#include "nsIObjectLoadingContent.h"
#include "nsIWritablePropertyBag2.h"
#include "nsEnumeratorUtils.h"
#include "nsXPCOM.h"
@ -5207,7 +5208,7 @@ NS_IMETHODIMP nsPluginHost::Notify(nsITimer* timer)
#ifdef MOZ_IPC
void
nsPluginHost::PluginCrashed(nsNPAPIPlugin* aPlugin)
nsPluginHost::PluginCrashed(nsNPAPIPlugin* aPlugin, const nsAString& dumpID)
{
nsPluginTag* pluginTag = FindTagForPlugin(aPlugin);
if (!pluginTag) {
@ -5215,6 +5216,17 @@ nsPluginHost::PluginCrashed(nsNPAPIPlugin* aPlugin)
return;
}
// Notify the app's observer that a plugin crashed so it can submit a crashreport.
PRBool submittedCrashReport = PR_FALSE;
nsCOMPtr<nsIObserverService> obsService = do_GetService("@mozilla.org/observer-service;1");
nsCOMPtr<nsIWritablePropertyBag2> propbag = do_CreateInstance("@mozilla.org/hash-property-bag;1");
if (obsService && propbag) {
propbag->SetPropertyAsAString(NS_LITERAL_STRING("minidumpID"), dumpID);
obsService->NotifyObservers(propbag, "plugin-crashed", nsnull);
// see if an observer submitted a crash report.
propbag->GetPropertyAsBool(NS_LITERAL_STRING("submittedCrashReport"), &submittedCrashReport);
}
// Invalidate each nsPluginInstanceTag for the crashed plugin
for (PRUint32 i = mInstanceTags.Length(); i > 0; i--) {
@ -5225,7 +5237,8 @@ nsPluginHost::PluginCrashed(nsNPAPIPlugin* aPlugin)
instanceTag->mInstance->GetDOMElement(getter_AddRefs(domElement));
nsCOMPtr<nsIObjectLoadingContent> objectContent(do_QueryInterface(domElement));
if (objectContent) {
objectContent->PluginCrashed();
objectContent->PluginCrashed(NS_ConvertUTF8toUTF16(pluginTag->mName),
submittedCrashReport);
}
instanceTag->mInstance->Stop();

View File

@ -160,7 +160,7 @@ public:
void RemoveIdleTimeTarget(nsIPluginInstanceOwner* objectFrame);
#ifdef MOZ_IPC
void PluginCrashed(nsNPAPIPlugin* plugin);
void PluginCrashed(nsNPAPIPlugin* plugin, const nsAString& dumpID);
#endif
nsPluginInstanceTag *FindInstanceTag(nsIPluginInstance *instance);

View File

@ -19,12 +19,71 @@ SimpleTest.waitForExplicitFinish();
var success = false;
var observerFired = false;
var testObserver = {
observe: function(subject, topic, data) {
observerFired = true;
ok(true, "Observer fired");
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
is(topic, "plugin-crashed", "Checking correct topic");
is(data, null, "Checking null data");
ok((subject instanceof Components.interfaces.nsIPropertyBag2), "got Propbag");
ok((subject instanceof Components.interfaces.nsIWritablePropertyBag2),
"got writable Propbag");
var id = subject.getPropertyAsAString("minidumpID");
isnot(id, "", "got a non-empty crash id");
let directoryService =
Components.classes["@mozilla.org/file/directory_service;1"].
getService(Components.interfaces.nsIProperties);
let profD = directoryService.get("ProfD", Components.interfaces.nsIFile);
profD.append("minidumps");
let dumpFile = profD.clone();
dumpFile.append(id + ".dmp");
ok(dumpFile.exists(), "minidump exists");
let extraFile = profD.clone();
extraFile.append(id + ".extra");
ok(extraFile.exists(), "extra file exists");
// cleanup, to be nice
dumpFile.remove(false);
extraFile.remove(false);
},
QueryInterface: function(iid) {
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
if (iid.equals(Components.interfaces.nsIObserver) ||
iid.equals(Components.interfaces.nsISupportsWeakReference) ||
iid.equals(Components.interfaces.nsISupports))
return this;
throw Components.results.NS_NOINTERFACE;
}
};
function onPluginCrashed(aEvent) {
ok(true, "Plugin crashed notification received");
ok(observerFired, "Observer should have fired first");
is(aEvent.type, "PluginCrashed", "event is correct type");
var pluginElement = document.getElementById("plugin1");
is (pluginElement, aEvent.target, "Plugin crashed event target is plugin element");
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
ok(aEvent instanceof Components.interfaces.nsIDOMDataContainerEvent,
"plugin crashed event has the right interface");
var pluginName = aEvent.getData("pluginName");
is(pluginName, "Test Plug-in");
var didReport = aEvent.getData("submittedCrashReport");
// The app itself may or may not have decided to submit the report, so
// allow either true or false here.
ok((didReport == true || didReport == false), "event said crash report was submitted");
var os = Components.classes["@mozilla.org/observer-service;1"].
getService(Components.interfaces.nsIObserverService);
os.removeObserver(testObserver, "plugin-crashed");
SimpleTest.finish();
}
@ -38,6 +97,10 @@ function runTests() {
return;
}
var os = Components.classes["@mozilla.org/observer-service;1"].
getService(Components.interfaces.nsIObserverService);
os.addObserver(testObserver, "plugin-crashed", true);
document.addEventListener("PluginCrashed", onPluginCrashed, false);
var pluginElement = document.getElementById("plugin1");