gecko/dom/plugins/ipc/PluginModuleParent.cpp
Benjamin Smedberg d19c5bb416 Bug 771251 - OOP crash reporting accesses the directory service off the main thread. In addition, the first design of the crash injector callback meant that we're dropping some set of Flash crashes which happen during an RPC call. r=ted
* Fix the directory service usage by saving the pending directory path from OOPInit.
* Force clients to call OOPInit on the main thread.
* Make injected crashes available via TakeMinidumpForChild and give each crash a sequence number so that we can pick the earliest crash from the three possibilities; delete the other two to avoid polluting about:crashes
2012-07-10 22:20:05 -04:00

1325 lines
35 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: sw=4 ts=4 et :
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifdef MOZ_WIDGET_GTK
#include <glib.h>
#elif XP_MACOSX
#include "PluginInterposeOSX.h"
#include "PluginUtilsOSX.h"
#endif
#ifdef MOZ_WIDGET_QT
#include <QtCore/QCoreApplication>
#include <QtCore/QEventLoop>
#endif
#include "base/process_util.h"
#include "mozilla/Preferences.h"
#include "mozilla/unused.h"
#include "mozilla/ipc/SyncChannel.h"
#include "mozilla/plugins/PluginModuleParent.h"
#include "mozilla/plugins/BrowserStreamParent.h"
#include "mozilla/dom/PCrashReporterParent.h"
#include "PluginIdentifierParent.h"
#include "nsAutoPtr.h"
#include "nsCRT.h"
#include "nsNPAPIPlugin.h"
#include "nsIFile.h"
#ifdef XP_WIN
#include "mozilla/widget/AudioSession.h"
#endif
#include "sampler.h"
using base::KillProcess;
using mozilla::PluginLibrary;
using mozilla::ipc::SyncChannel;
using mozilla::dom::PCrashReporterParent;
using mozilla::dom::CrashReporterParent;
using namespace mozilla;
using namespace mozilla::plugins;
using namespace mozilla::plugins::parent;
#ifdef MOZ_CRASHREPORTER
#include "mozilla/dom/CrashReporterParent.h"
using namespace CrashReporter;
#endif
static const char kChildTimeoutPref[] = "dom.ipc.plugins.timeoutSecs";
static const char kParentTimeoutPref[] = "dom.ipc.plugins.parentTimeoutSecs";
static const char kLaunchTimeoutPref[] = "dom.ipc.plugins.processLaunchTimeoutSecs";
template<>
struct RunnableMethodTraits<mozilla::plugins::PluginModuleParent>
{
typedef mozilla::plugins::PluginModuleParent Class;
static void RetainCallee(Class* obj) { }
static void ReleaseCallee(Class* obj) { }
};
// static
PluginLibrary*
PluginModuleParent::LoadModule(const char* aFilePath)
{
PLUGIN_LOG_DEBUG_FUNCTION;
PRInt32 prefSecs = Preferences::GetInt(kLaunchTimeoutPref, 0);
// Block on the child process being launched and initialized.
nsAutoPtr<PluginModuleParent> parent(new PluginModuleParent(aFilePath));
bool launched = parent->mSubprocess->Launch(prefSecs * 1000);
if (!launched) {
// Need to set this so the destructor doesn't complain.
parent->mShutdown = true;
return nsnull;
}
parent->Open(parent->mSubprocess->GetChannel(),
parent->mSubprocess->GetChildProcessHandle());
TimeoutChanged(kChildTimeoutPref, parent);
#ifdef MOZ_CRASHREPORTER
// If this fails, we're having IPC troubles, and we're doomed anyways.
if (!CrashReporterParent::CreateCrashReporter(parent.get())) {
parent->mShutdown = true;
return nsnull;
}
#endif
return parent.forget();
}
PluginModuleParent::PluginModuleParent(const char* aFilePath)
: mSubprocess(new PluginProcessParent(aFilePath))
, mShutdown(false)
, mClearSiteDataSupported(false)
, mGetSitesWithDataSupported(false)
, mNPNIface(NULL)
, mPlugin(NULL)
, mTaskFactory(this)
#ifdef MOZ_CRASHREPORTER_INJECTOR
, mFlashProcess1(0)
, mFlashProcess2(0)
#endif
{
NS_ASSERTION(mSubprocess, "Out of memory!");
mIdentifiers.Init();
Preferences::RegisterCallback(TimeoutChanged, kChildTimeoutPref, this);
Preferences::RegisterCallback(TimeoutChanged, kParentTimeoutPref, this);
}
PluginModuleParent::~PluginModuleParent()
{
NS_ASSERTION(OkToCleanup(), "unsafe destruction");
if (!mShutdown) {
NS_WARNING("Plugin host deleted the module without shutting down.");
NPError err;
NP_Shutdown(&err);
}
NS_ASSERTION(mShutdown, "NP_Shutdown didn't");
if (mSubprocess) {
mSubprocess->Delete();
mSubprocess = nsnull;
}
#ifdef MOZ_CRASHREPORTER_INJECTOR
if (mFlashProcess1)
UnregisterInjectorCallback(mFlashProcess1);
if (mFlashProcess2)
UnregisterInjectorCallback(mFlashProcess2);
#endif
Preferences::UnregisterCallback(TimeoutChanged, kChildTimeoutPref, this);
Preferences::UnregisterCallback(TimeoutChanged, kParentTimeoutPref, this);
}
#ifdef MOZ_CRASHREPORTER
void
PluginModuleParent::WriteExtraDataForMinidump(AnnotationTable& notes)
{
typedef nsDependentCString CS;
// Get the plugin filename, try to get just the file leafname
const std::string& pluginFile = mSubprocess->GetPluginFilePath();
size_t filePos = pluginFile.rfind(FILE_PATH_SEPARATOR);
if (filePos == std::string::npos)
filePos = 0;
else
filePos++;
notes.Put(CS("PluginFilename"), CS(pluginFile.substr(filePos).c_str()));
//TODO: add plugin name and version: bug 539841
// (as PluginName, PluginVersion)
notes.Put(CS("PluginName"), CS(""));
notes.Put(CS("PluginVersion"), CS(""));
CrashReporterParent* crashReporter = CrashReporter();
if (crashReporter) {
const nsString& hangID = crashReporter->HangID();
if (!hangID.IsEmpty())
notes.Put(CS("HangID"), NS_ConvertUTF16toUTF8(hangID));
}
}
#endif // MOZ_CRASHREPORTER
int
PluginModuleParent::TimeoutChanged(const char* aPref, void* aModule)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thead!");
if (!strcmp(aPref, kChildTimeoutPref)) {
// The timeout value used by the parent for children
PRInt32 timeoutSecs = Preferences::GetInt(kChildTimeoutPref, 0);
int32 timeoutMs = (timeoutSecs > 0) ? (1000 * timeoutSecs) :
SyncChannel::kNoTimeout;
static_cast<PluginModuleParent*>(aModule)->SetReplyTimeoutMs(timeoutMs);
} else if (!strcmp(aPref, kParentTimeoutPref)) {
// The timeout value used by the child for its parent
PRInt32 timeoutSecs = Preferences::GetInt(kParentTimeoutPref, 0);
unused << static_cast<PluginModuleParent*>(aModule)->SendSetParentHangTimeout(timeoutSecs);
}
return 0;
}
void
PluginModuleParent::CleanupFromTimeout()
{
if (!mShutdown && OkToCleanup())
Close();
}
bool
PluginModuleParent::ShouldContinueFromReplyTimeout()
{
#ifdef MOZ_CRASHREPORTER
CrashReporterParent* crashReporter = CrashReporter();
if (crashReporter->GeneratePairedMinidump(this)) {
mBrowserDumpID = crashReporter->ParentDumpID();
mPluginDumpID = crashReporter->ChildDumpID();
PLUGIN_LOG_DEBUG(
("generated paired browser/plugin minidumps: %s/%s (ID=%s)",
NS_ConvertUTF16toUTF8(mBrowserDumpID).get(),
NS_ConvertUTF16toUTF8(mPluginDumpID).get(),
NS_ConvertUTF16toUTF8(crashReporter->HangID()).get()));
} else {
NS_WARNING("failed to capture paired minidumps from hang");
}
#endif
// this must run before the error notification from the channel,
// or not at all
MessageLoop::current()->PostTask(
FROM_HERE,
mTaskFactory.NewRunnableMethod(
&PluginModuleParent::CleanupFromTimeout));
if (!KillProcess(OtherProcess(), 1, false))
NS_WARNING("failed to kill subprocess!");
return false;
}
#ifdef MOZ_CRASHREPORTER
CrashReporterParent*
PluginModuleParent::CrashReporter()
{
return static_cast<CrashReporterParent*>(ManagedPCrashReporterParent()[0]);
}
#endif
#ifdef MOZ_CRASHREPORTER
static void
RemoveMinidump(nsIFile* minidump)
{
if (!minidump)
return;
minidump->Remove(false);
nsCOMPtr<nsIFile> extraFile;
if (GetExtraFileForMinidump(minidump,
getter_AddRefs(extraFile))) {
extraFile->Remove(true);
}
}
void
PluginModuleParent::ProcessFirstMinidump()
{
CrashReporterParent* crashReporter = CrashReporter();
if (!crashReporter)
return;
AnnotationTable notes;
notes.Init(4);
WriteExtraDataForMinidump(notes);
if (!mPluginDumpID.IsEmpty() && !mBrowserDumpID.IsEmpty()) {
crashReporter->GenerateHangCrashReport(&notes);
return;
}
PRUint32 sequence = PR_UINT32_MAX;
nsCOMPtr<nsIFile> dumpFile;
nsCAutoString flashProcessType;
TakeMinidump(getter_AddRefs(dumpFile), &sequence);
#ifdef MOZ_CRASHREPORTER_INJECTOR
nsCOMPtr<nsIFile> childDumpFile;
PRUint32 childSequence;
if (mFlashProcess1 &&
TakeMinidumpForChild(mFlashProcess1,
getter_AddRefs(childDumpFile),
&childSequence)) {
if (childSequence < sequence) {
RemoveMinidump(dumpFile);
dumpFile = childDumpFile;
sequence = childSequence;
flashProcessType.AssignLiteral("Broker");
}
else {
RemoveMinidump(childDumpFile);
}
}
if (mFlashProcess2 &&
TakeMinidumpForChild(mFlashProcess2,
getter_AddRefs(childDumpFile),
&childSequence)) {
if (childSequence < sequence) {
RemoveMinidump(dumpFile);
dumpFile = childDumpFile;
sequence = childSequence;
flashProcessType.AssignLiteral("Sandbox");
}
else {
RemoveMinidump(childDumpFile);
}
}
#endif
if (!dumpFile) {
NS_WARNING("[PluginModuleParent::ActorDestroy] abnormal shutdown without minidump!");
return;
}
PLUGIN_LOG_DEBUG(("got child minidump: %s",
NS_ConvertUTF16toUTF8(mPluginDumpID).get()));
GetIDFromMinidump(dumpFile, mPluginDumpID);
if (!flashProcessType.IsEmpty()) {
notes.Put(NS_LITERAL_CSTRING("FlashProcessDump"), flashProcessType);
}
crashReporter->GenerateCrashReportForMinidump(dumpFile, &notes);
}
#endif
void
PluginModuleParent::ActorDestroy(ActorDestroyReason why)
{
switch (why) {
case AbnormalShutdown: {
#ifdef MOZ_CRASHREPORTER
ProcessFirstMinidump();
#endif
mShutdown = true;
// Defer the PluginCrashed method so that we don't re-enter
// and potentially modify the actor child list while enumerating it.
if (mPlugin)
MessageLoop::current()->PostTask(
FROM_HERE,
mTaskFactory.NewRunnableMethod(
&PluginModuleParent::NotifyPluginCrashed));
break;
}
case NormalShutdown:
mShutdown = true;
break;
default:
NS_ERROR("Unexpected shutdown reason for toplevel actor.");
}
}
void
PluginModuleParent::NotifyPluginCrashed()
{
if (!OkToCleanup()) {
// there's still plugin code on the C++ stack. try again
MessageLoop::current()->PostDelayedTask(
FROM_HERE,
mTaskFactory.NewRunnableMethod(
&PluginModuleParent::NotifyPluginCrashed), 10);
return;
}
if (mPlugin)
mPlugin->PluginCrashed(mPluginDumpID, mBrowserDumpID);
}
PPluginIdentifierParent*
PluginModuleParent::AllocPPluginIdentifier(const nsCString& aString,
const int32_t& aInt,
const bool& aTemporary)
{
if (aTemporary) {
NS_ERROR("Plugins don't create temporary identifiers.");
return NULL; // should abort the plugin
}
NPIdentifier npident = aString.IsVoid() ?
mozilla::plugins::parent::_getintidentifier(aInt) :
mozilla::plugins::parent::_getstringidentifier(aString.get());
if (!npident) {
NS_WARNING("Failed to get identifier!");
return nsnull;
}
PluginIdentifierParent* ident = new PluginIdentifierParent(npident, false);
mIdentifiers.Put(npident, ident);
return ident;
}
bool
PluginModuleParent::DeallocPPluginIdentifier(PPluginIdentifierParent* aActor)
{
delete aActor;
return true;
}
PPluginInstanceParent*
PluginModuleParent::AllocPPluginInstance(const nsCString& aMimeType,
const uint16_t& aMode,
const InfallibleTArray<nsCString>& aNames,
const InfallibleTArray<nsCString>& aValues,
NPError* rv)
{
NS_ERROR("Not reachable!");
return NULL;
}
bool
PluginModuleParent::DeallocPPluginInstance(PPluginInstanceParent* aActor)
{
PLUGIN_LOG_DEBUG_METHOD;
delete aActor;
return true;
}
void
PluginModuleParent::SetPluginFuncs(NPPluginFuncs* aFuncs)
{
aFuncs->version = (NP_VERSION_MAJOR << 8) | NP_VERSION_MINOR;
aFuncs->javaClass = nsnull;
// Gecko should always call these functions through a PluginLibrary object.
aFuncs->newp = NULL;
aFuncs->clearsitedata = NULL;
aFuncs->getsiteswithdata = NULL;
aFuncs->destroy = NPP_Destroy;
aFuncs->setwindow = NPP_SetWindow;
aFuncs->newstream = NPP_NewStream;
aFuncs->destroystream = NPP_DestroyStream;
aFuncs->asfile = NPP_StreamAsFile;
aFuncs->writeready = NPP_WriteReady;
aFuncs->write = NPP_Write;
aFuncs->print = NPP_Print;
aFuncs->event = NPP_HandleEvent;
aFuncs->urlnotify = NPP_URLNotify;
aFuncs->getvalue = NPP_GetValue;
aFuncs->setvalue = NPP_SetValue;
aFuncs->gotfocus = NULL;
aFuncs->lostfocus = NULL;
aFuncs->urlredirectnotify = NULL;
// Provide 'NPP_URLRedirectNotify', 'NPP_ClearSiteData', and
// 'NPP_GetSitesWithData' functionality if it is supported by the plugin.
bool urlRedirectSupported = false;
unused << CallOptionalFunctionsSupported(&urlRedirectSupported,
&mClearSiteDataSupported,
&mGetSitesWithDataSupported);
if (urlRedirectSupported) {
aFuncs->urlredirectnotify = NPP_URLRedirectNotify;
}
}
NPError
PluginModuleParent::NPP_Destroy(NPP instance,
NPSavedData** /*saved*/)
{
// FIXME/cjones:
// (1) send a "destroy" message to the child
// (2) the child shuts down its instance
// (3) remove both parent and child IDs from map
// (4) free parent
PLUGIN_LOG_DEBUG_FUNCTION;
PluginInstanceParent* parentInstance =
static_cast<PluginInstanceParent*>(instance->pdata);
if (!parentInstance)
return NPERR_NO_ERROR;
NPError retval = parentInstance->Destroy();
instance->pdata = nsnull;
unused << PluginInstanceParent::Call__delete__(parentInstance);
return retval;
}
NPError
PluginModuleParent::NPP_NewStream(NPP instance, NPMIMEType type,
NPStream* stream, NPBool seekable,
uint16_t* stype)
{
SAMPLE_LABEL("PluginModuleParent", "NPP_NewStream");
PluginInstanceParent* i = InstCast(instance);
if (!i)
return NPERR_GENERIC_ERROR;
return i->NPP_NewStream(type, stream, seekable,
stype);
}
NPError
PluginModuleParent::NPP_SetWindow(NPP instance, NPWindow* window)
{
PluginInstanceParent* i = InstCast(instance);
if (!i)
return NPERR_GENERIC_ERROR;
return i->NPP_SetWindow(window);
}
NPError
PluginModuleParent::NPP_DestroyStream(NPP instance,
NPStream* stream,
NPReason reason)
{
PluginInstanceParent* i = InstCast(instance);
if (!i)
return NPERR_GENERIC_ERROR;
return i->NPP_DestroyStream(stream, reason);
}
int32_t
PluginModuleParent::NPP_WriteReady(NPP instance,
NPStream* stream)
{
BrowserStreamParent* s = StreamCast(instance, stream);
if (!s)
return -1;
return s->WriteReady();
}
int32_t
PluginModuleParent::NPP_Write(NPP instance,
NPStream* stream,
int32_t offset,
int32_t len,
void* buffer)
{
BrowserStreamParent* s = StreamCast(instance, stream);
if (!s)
return -1;
return s->Write(offset, len, buffer);
}
void
PluginModuleParent::NPP_StreamAsFile(NPP instance,
NPStream* stream,
const char* fname)
{
BrowserStreamParent* s = StreamCast(instance, stream);
if (!s)
return;
s->StreamAsFile(fname);
}
void
PluginModuleParent::NPP_Print(NPP instance, NPPrint* platformPrint)
{
PluginInstanceParent* i = InstCast(instance);
if (i)
i->NPP_Print(platformPrint);
}
int16_t
PluginModuleParent::NPP_HandleEvent(NPP instance, void* event)
{
PluginInstanceParent* i = InstCast(instance);
if (!i)
return false;
return i->NPP_HandleEvent(event);
}
void
PluginModuleParent::NPP_URLNotify(NPP instance, const char* url,
NPReason reason, void* notifyData)
{
PluginInstanceParent* i = InstCast(instance);
if (!i)
return;
i->NPP_URLNotify(url, reason, notifyData);
}
NPError
PluginModuleParent::NPP_GetValue(NPP instance,
NPPVariable variable, void *ret_value)
{
PluginInstanceParent* i = InstCast(instance);
if (!i)
return NPERR_GENERIC_ERROR;
return i->NPP_GetValue(variable, ret_value);
}
NPError
PluginModuleParent::NPP_SetValue(NPP instance, NPNVariable variable,
void *value)
{
PluginInstanceParent* i = InstCast(instance);
if (!i)
return NPERR_GENERIC_ERROR;
return i->NPP_SetValue(variable, value);
}
bool
PluginModuleParent::RecvBackUpXResources(const FileDescriptor& aXSocketFd)
{
#ifndef MOZ_X11
NS_RUNTIMEABORT("This message only makes sense on X11 platforms");
#else
NS_ABORT_IF_FALSE(0 > mPluginXSocketFdDup.get(),
"Already backed up X resources??");
int fd = aXSocketFd.fd; // Copy to discard |const| qualifier
mPluginXSocketFdDup.forget();
mPluginXSocketFdDup.reset(fd);
#endif
return true;
}
void
PluginModuleParent::NPP_URLRedirectNotify(NPP instance, const char* url,
int32_t status, void* notifyData)
{
PluginInstanceParent* i = InstCast(instance);
if (!i)
return;
i->NPP_URLRedirectNotify(url, status, notifyData);
}
bool
PluginModuleParent::AnswerNPN_UserAgent(nsCString* userAgent)
{
*userAgent = NullableString(mNPNIface->uagent(nsnull));
return true;
}
PluginIdentifierParent*
PluginModuleParent::GetIdentifierForNPIdentifier(NPP npp, NPIdentifier aIdentifier)
{
PluginIdentifierParent* ident;
if (mIdentifiers.Get(aIdentifier, &ident)) {
if (ident->IsTemporary()) {
ident->AddTemporaryRef();
}
return ident;
}
nsCString string;
int32_t intval = -1;
bool temporary = false;
if (mozilla::plugins::parent::_identifierisstring(aIdentifier)) {
NPUTF8* chars =
mozilla::plugins::parent::_utf8fromidentifier(aIdentifier);
if (!chars) {
return nsnull;
}
string.Adopt(chars);
temporary = !NPStringIdentifierIsPermanent(npp, aIdentifier);
}
else {
intval = mozilla::plugins::parent::_intfromidentifier(aIdentifier);
string.SetIsVoid(true);
}
ident = new PluginIdentifierParent(aIdentifier, temporary);
if (!SendPPluginIdentifierConstructor(ident, string, intval, temporary))
return nsnull;
if (!temporary) {
mIdentifiers.Put(aIdentifier, ident);
}
return ident;
}
PluginInstanceParent*
PluginModuleParent::InstCast(NPP instance)
{
PluginInstanceParent* ip =
static_cast<PluginInstanceParent*>(instance->pdata);
// If the plugin crashed and the PluginInstanceParent was deleted,
// instance->pdata will be NULL.
if (!ip)
return NULL;
if (instance != ip->mNPP) {
NS_RUNTIMEABORT("Corrupted plugin data.");
}
return ip;
}
BrowserStreamParent*
PluginModuleParent::StreamCast(NPP instance,
NPStream* s)
{
PluginInstanceParent* ip = InstCast(instance);
if (!ip)
return NULL;
BrowserStreamParent* sp =
static_cast<BrowserStreamParent*>(static_cast<AStream*>(s->pdata));
if (sp->mNPP != ip || s != sp->mStream) {
NS_RUNTIMEABORT("Corrupted plugin stream data.");
}
return sp;
}
bool
PluginModuleParent::HasRequiredFunctions()
{
return true;
}
nsresult
PluginModuleParent::AsyncSetWindow(NPP instance, NPWindow* window)
{
PluginInstanceParent* i = InstCast(instance);
if (!i)
return NS_ERROR_FAILURE;
return i->AsyncSetWindow(window);
}
#if defined(MOZ_WIDGET_QT) && (MOZ_PLATFORM_MAEMO == 6)
nsresult
PluginModuleParent::HandleGUIEvent(NPP instance,
const nsGUIEvent& anEvent,
bool* handled)
{
PluginInstanceParent* i = InstCast(instance);
if (!i)
return NS_ERROR_FAILURE;
return i->HandleGUIEvent(anEvent, handled);
}
#endif
nsresult
PluginModuleParent::GetImageContainer(NPP instance,
mozilla::layers::ImageContainer** aContainer)
{
PluginInstanceParent* i = InstCast(instance);
return !i ? NS_ERROR_FAILURE : i->GetImageContainer(aContainer);
}
nsresult
PluginModuleParent::GetImageSize(NPP instance,
nsIntSize* aSize)
{
PluginInstanceParent* i = InstCast(instance);
return !i ? NS_ERROR_FAILURE : i->GetImageSize(aSize);
}
nsresult
PluginModuleParent::SetBackgroundUnknown(NPP instance)
{
PluginInstanceParent* i = InstCast(instance);
if (!i)
return NS_ERROR_FAILURE;
return i->SetBackgroundUnknown();
}
nsresult
PluginModuleParent::BeginUpdateBackground(NPP instance,
const nsIntRect& aRect,
gfxContext** aCtx)
{
PluginInstanceParent* i = InstCast(instance);
if (!i)
return NS_ERROR_FAILURE;
return i->BeginUpdateBackground(aRect, aCtx);
}
nsresult
PluginModuleParent::EndUpdateBackground(NPP instance,
gfxContext* aCtx,
const nsIntRect& aRect)
{
PluginInstanceParent* i = InstCast(instance);
if (!i)
return NS_ERROR_FAILURE;
return i->EndUpdateBackground(aCtx, aRect);
}
#if defined(XP_UNIX) && !defined(XP_MACOSX) && !defined(MOZ_WIDGET_GONK)
nsresult
PluginModuleParent::NP_Initialize(NPNetscapeFuncs* bFuncs, NPPluginFuncs* pFuncs, NPError* error)
{
PLUGIN_LOG_DEBUG_METHOD;
mNPNIface = bFuncs;
if (mShutdown) {
*error = NPERR_GENERIC_ERROR;
return NS_ERROR_FAILURE;
}
uint32_t flags = 0;
if (!CallNP_Initialize(flags, error)) {
return NS_ERROR_FAILURE;
}
else if (*error != NPERR_NO_ERROR) {
return NS_OK;
}
SetPluginFuncs(pFuncs);
return NS_OK;
}
#else
nsresult
PluginModuleParent::NP_Initialize(NPNetscapeFuncs* bFuncs, NPError* error)
{
PLUGIN_LOG_DEBUG_METHOD;
mNPNIface = bFuncs;
if (mShutdown) {
*error = NPERR_GENERIC_ERROR;
return NS_ERROR_FAILURE;
}
uint32_t flags = 0;
#ifdef XP_WIN
flags |= kAllowAsyncDrawing;
#endif
if (!CallNP_Initialize(flags, error))
return NS_ERROR_FAILURE;
#if defined XP_WIN
// Send the info needed to join the chrome process's audio session to the
// plugin process
nsID id;
nsString sessionName;
nsString iconPath;
if (NS_SUCCEEDED(mozilla::widget::GetAudioSessionData(id, sessionName,
iconPath)))
unused << SendSetAudioSessionData(id, sessionName, iconPath);
#endif
#ifdef MOZ_CRASHREPORTER_INJECTOR
InitializeInjector();
#endif
return NS_OK;
}
#endif
nsresult
PluginModuleParent::NP_Shutdown(NPError* error)
{
PLUGIN_LOG_DEBUG_METHOD;
if (mShutdown) {
*error = NPERR_GENERIC_ERROR;
return NS_ERROR_FAILURE;
}
bool ok = CallNP_Shutdown(error);
// if NP_Shutdown() is nested within another RPC call, this will
// break things. but lord help us if we're doing that anyway; the
// plugin dso will have been unloaded on the other side by the
// CallNP_Shutdown() message
Close();
return ok ? NS_OK : NS_ERROR_FAILURE;
}
nsresult
PluginModuleParent::NP_GetMIMEDescription(const char** mimeDesc)
{
PLUGIN_LOG_DEBUG_METHOD;
*mimeDesc = "application/x-foobar";
return NS_OK;
}
nsresult
PluginModuleParent::NP_GetValue(void *future, NPPVariable aVariable,
void *aValue, NPError* error)
{
PR_LOG(gPluginLog, PR_LOG_WARNING, ("%s Not implemented, requested variable %i", __FUNCTION__,
(int) aVariable));
//TODO: implement this correctly
*error = NPERR_GENERIC_ERROR;
return NS_OK;
}
#if defined(XP_WIN) || defined(XP_MACOSX) || defined(XP_OS2)
nsresult
PluginModuleParent::NP_GetEntryPoints(NPPluginFuncs* pFuncs, NPError* error)
{
NS_ASSERTION(pFuncs, "Null pointer!");
// We need to have the child process update its function table
// here by actually calling NP_GetEntryPoints since the parent's
// function table can reflect NULL entries in the child's table.
if (!CallNP_GetEntryPoints(error)) {
return NS_ERROR_FAILURE;
}
else if (*error != NPERR_NO_ERROR) {
return NS_OK;
}
SetPluginFuncs(pFuncs);
return NS_OK;
}
#endif
nsresult
PluginModuleParent::NPP_New(NPMIMEType pluginType, NPP instance,
uint16_t mode, int16_t argc, char* argn[],
char* argv[], NPSavedData* saved,
NPError* error)
{
PLUGIN_LOG_DEBUG_METHOD;
if (mShutdown) {
*error = NPERR_GENERIC_ERROR;
return NS_ERROR_FAILURE;
}
// create the instance on the other side
InfallibleTArray<nsCString> names;
InfallibleTArray<nsCString> values;
for (int i = 0; i < argc; ++i) {
names.AppendElement(NullableString(argn[i]));
values.AppendElement(NullableString(argv[i]));
}
PluginInstanceParent* parentInstance =
new PluginInstanceParent(this, instance,
nsDependentCString(pluginType), mNPNIface);
if (!parentInstance->Init()) {
delete parentInstance;
return NS_ERROR_FAILURE;
}
instance->pdata = parentInstance;
if (!CallPPluginInstanceConstructor(parentInstance,
nsDependentCString(pluginType), mode,
names, values, error)) {
// |parentInstance| is automatically deleted.
instance->pdata = nsnull;
// if IPC is down, we'll get an immediate "failed" return, but
// without *error being set. So make sure that the error
// condition is signaled to nsNPAPIPluginInstance
if (NPERR_NO_ERROR == *error)
*error = NPERR_GENERIC_ERROR;
return NS_ERROR_FAILURE;
}
if (*error != NPERR_NO_ERROR) {
NPP_Destroy(instance, 0);
return NS_ERROR_FAILURE;
}
TimeoutChanged(kParentTimeoutPref, this);
return NS_OK;
}
nsresult
PluginModuleParent::NPP_ClearSiteData(const char* site, uint64_t flags,
uint64_t maxAge)
{
if (!mClearSiteDataSupported)
return NS_ERROR_NOT_AVAILABLE;
NPError result;
if (!CallNPP_ClearSiteData(NullableString(site), flags, maxAge, &result))
return NS_ERROR_FAILURE;
switch (result) {
case NPERR_NO_ERROR:
return NS_OK;
case NPERR_TIME_RANGE_NOT_SUPPORTED:
return NS_ERROR_PLUGIN_TIME_RANGE_NOT_SUPPORTED;
case NPERR_MALFORMED_SITE:
return NS_ERROR_INVALID_ARG;
default:
return NS_ERROR_FAILURE;
}
}
nsresult
PluginModuleParent::NPP_GetSitesWithData(InfallibleTArray<nsCString>& result)
{
if (!mGetSitesWithDataSupported)
return NS_ERROR_NOT_AVAILABLE;
if (!CallNPP_GetSitesWithData(&result))
return NS_ERROR_FAILURE;
return NS_OK;
}
#if defined(XP_MACOSX)
nsresult
PluginModuleParent::IsRemoteDrawingCoreAnimation(NPP instance, bool *aDrawing)
{
PluginInstanceParent* i = InstCast(instance);
if (!i)
return NS_ERROR_FAILURE;
return i->IsRemoteDrawingCoreAnimation(aDrawing);
}
#endif
bool
PluginModuleParent::AnswerNPN_GetValue_WithBoolReturn(const NPNVariable& aVariable,
NPError* aError,
bool* aBoolVal)
{
NPBool boolVal = false;
*aError = mozilla::plugins::parent::_getvalue(nsnull, aVariable, &boolVal);
*aBoolVal = boolVal ? true : false;
return true;
}
#if defined(MOZ_WIDGET_QT)
static const int kMaxtimeToProcessEvents = 30;
bool
PluginModuleParent::AnswerProcessSomeEvents()
{
PLUGIN_LOG_DEBUG(("Spinning mini nested loop ..."));
QCoreApplication::processEvents(QEventLoop::AllEvents, kMaxtimeToProcessEvents);
PLUGIN_LOG_DEBUG(("... quitting mini nested loop"));
return true;
}
#elif defined(XP_MACOSX)
bool
PluginModuleParent::AnswerProcessSomeEvents()
{
mozilla::plugins::PluginUtilsOSX::InvokeNativeEventLoop();
return true;
}
#elif !defined(MOZ_WIDGET_GTK)
bool
PluginModuleParent::AnswerProcessSomeEvents()
{
NS_RUNTIMEABORT("unreached");
return false;
}
#else
static const int kMaxChancesToProcessEvents = 20;
bool
PluginModuleParent::AnswerProcessSomeEvents()
{
PLUGIN_LOG_DEBUG(("Spinning mini nested loop ..."));
int i = 0;
for (; i < kMaxChancesToProcessEvents; ++i)
if (!g_main_context_iteration(NULL, FALSE))
break;
PLUGIN_LOG_DEBUG(("... quitting mini nested loop; processed %i tasks", i));
return true;
}
#endif
bool
PluginModuleParent::RecvProcessNativeEventsInRPCCall()
{
PLUGIN_LOG_DEBUG(("%s", FULLFUNCTION));
#if defined(OS_WIN)
ProcessNativeEventsInRPCCall();
return true;
#else
NS_NOTREACHED(
"PluginModuleParent::RecvProcessNativeEventsInRPCCall not implemented!");
return false;
#endif
}
void
PluginModuleParent::ProcessRemoteNativeEventsInRPCCall()
{
#if defined(OS_WIN)
unused << SendProcessNativeEventsInRPCCall();
return;
#endif
NS_NOTREACHED(
"PluginModuleParent::ProcessRemoteNativeEventsInRPCCall not implemented!");
}
bool
PluginModuleParent::RecvPluginShowWindow(const uint32_t& aWindowId, const bool& aModal,
const int32_t& aX, const int32_t& aY,
const size_t& aWidth, const size_t& aHeight)
{
PLUGIN_LOG_DEBUG(("%s", FULLFUNCTION));
#if defined(XP_MACOSX)
CGRect windowBound = ::CGRectMake(aX, aY, aWidth, aHeight);
mac_plugin_interposing::parent::OnPluginShowWindow(aWindowId, windowBound, aModal);
return true;
#else
NS_NOTREACHED(
"PluginInstanceParent::RecvPluginShowWindow not implemented!");
return false;
#endif
}
bool
PluginModuleParent::RecvPluginHideWindow(const uint32_t& aWindowId)
{
PLUGIN_LOG_DEBUG(("%s", FULLFUNCTION));
#if defined(XP_MACOSX)
mac_plugin_interposing::parent::OnPluginHideWindow(aWindowId, OtherSidePID());
return true;
#else
NS_NOTREACHED(
"PluginInstanceParent::RecvPluginHideWindow not implemented!");
return false;
#endif
}
PCrashReporterParent*
PluginModuleParent::AllocPCrashReporter(mozilla::dom::NativeThreadId* id,
PRUint32* processType)
{
#ifdef MOZ_CRASHREPORTER
return new CrashReporterParent();
#else
return nsnull;
#endif
}
bool
PluginModuleParent::DeallocPCrashReporter(PCrashReporterParent* actor)
{
delete actor;
return true;
}
bool
PluginModuleParent::RecvSetCursor(const NSCursorInfo& aCursorInfo)
{
PLUGIN_LOG_DEBUG(("%s", FULLFUNCTION));
#if defined(XP_MACOSX)
mac_plugin_interposing::parent::OnSetCursor(aCursorInfo);
return true;
#else
NS_NOTREACHED(
"PluginInstanceParent::RecvSetCursor not implemented!");
return false;
#endif
}
bool
PluginModuleParent::RecvShowCursor(const bool& aShow)
{
PLUGIN_LOG_DEBUG(("%s", FULLFUNCTION));
#if defined(XP_MACOSX)
mac_plugin_interposing::parent::OnShowCursor(aShow);
return true;
#else
NS_NOTREACHED(
"PluginInstanceParent::RecvShowCursor not implemented!");
return false;
#endif
}
bool
PluginModuleParent::RecvPushCursor(const NSCursorInfo& aCursorInfo)
{
PLUGIN_LOG_DEBUG(("%s", FULLFUNCTION));
#if defined(XP_MACOSX)
mac_plugin_interposing::parent::OnPushCursor(aCursorInfo);
return true;
#else
NS_NOTREACHED(
"PluginInstanceParent::RecvPushCursor not implemented!");
return false;
#endif
}
bool
PluginModuleParent::RecvPopCursor()
{
PLUGIN_LOG_DEBUG(("%s", FULLFUNCTION));
#if defined(XP_MACOSX)
mac_plugin_interposing::parent::OnPopCursor();
return true;
#else
NS_NOTREACHED(
"PluginInstanceParent::RecvPopCursor not implemented!");
return false;
#endif
}
bool
PluginModuleParent::RecvGetNativeCursorsSupported(bool* supported)
{
PLUGIN_LOG_DEBUG(("%s", FULLFUNCTION));
#if defined(XP_MACOSX)
*supported =
Preferences::GetBool("dom.ipc.plugins.nativeCursorSupport", false);
return true;
#else
NS_NOTREACHED(
"PluginInstanceParent::RecvGetNativeCursorSupportLevel not implemented!");
return false;
#endif
}
bool
PluginModuleParent::RecvNPN_SetException(PPluginScriptableObjectParent* aActor,
const nsCString& aMessage)
{
PLUGIN_LOG_DEBUG(("%s", FULLFUNCTION));
NPObject* aNPObj = NULL;
if (aActor) {
aNPObj = static_cast<PluginScriptableObjectParent*>(aActor)->GetObject(true);
if (!aNPObj) {
NS_ERROR("Failed to get object!");
return false;
}
}
mozilla::plugins::parent::_setexception(aNPObj, NullableStringGet(aMessage));
return true;
}
bool
PluginModuleParent::RecvNPN_ReloadPlugins(const bool& aReloadPages)
{
PLUGIN_LOG_DEBUG(("%s", FULLFUNCTION));
mozilla::plugins::parent::_reloadplugins(aReloadPages);
return true;
}
#ifdef MOZ_CRASHREPORTER_INJECTOR
// We only add the crash reporter to subprocess which have the filename
// FlashPlayerPlugin*
#define FLASH_PROCESS_PREFIX "FLASHPLAYERPLUGIN"
static DWORD
GetFlashChildOfPID(DWORD pid, HANDLE snapshot)
{
PROCESSENTRY32 entry = {
sizeof(entry)
};
for (BOOL ok = Process32First(snapshot, &entry);
ok;
ok = Process32Next(snapshot, &entry)) {
if (entry.th32ParentProcessID == pid) {
nsString name(entry.szExeFile);
ToUpperCase(name);
if (StringBeginsWith(name, NS_LITERAL_STRING(FLASH_PROCESS_PREFIX))) {
return entry.th32ProcessID;
}
}
}
return 0;
}
// We only look for child processes of the Flash plugin, NPSWF*
#define FLASH_PLUGIN_PREFIX "NPSWF"
void
PluginModuleParent::InitializeInjector()
{
if (!Preferences::GetBool("dom.ipc.plugins.flash.subprocess.crashreporter.enabled", false))
return;
nsCString path(Process()->GetPluginFilePath().c_str());
ToUpperCase(path);
PRInt32 lastSlash = path.RFindCharInSet("\\/");
if (kNotFound == lastSlash)
return;
if (!StringBeginsWith(Substring(path, lastSlash + 1),
NS_LITERAL_CSTRING(FLASH_PLUGIN_PREFIX)))
return;
HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (INVALID_HANDLE_VALUE == snapshot)
return;
DWORD pluginProcessPID = GetProcessId(Process()->GetChildProcessHandle());
mFlashProcess1 = GetFlashChildOfPID(pluginProcessPID, snapshot);
if (mFlashProcess1) {
InjectCrashReporterIntoProcess(mFlashProcess1, this);
mFlashProcess2 = GetFlashChildOfPID(mFlashProcess1, snapshot);
if (mFlashProcess2) {
InjectCrashReporterIntoProcess(mFlashProcess2, this);
}
}
}
void
PluginModuleParent::OnCrash(DWORD processID)
{
GetIPCChannel()->CloseWithError();
KillProcess(OtherProcess(), 1, false);
}
#endif // MOZ_CRASHREPORTER_INJECTOR