michael@0: /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- michael@0: * vim: sw=4 ts=4 et : michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #ifdef MOZ_WIDGET_QT michael@0: // Must be included first to avoid conflicts. michael@0: #include michael@0: #include michael@0: #include "NestedLoopTimer.h" michael@0: #endif michael@0: michael@0: #include "mozilla/plugins/PluginModuleParent.h" michael@0: michael@0: #include "base/process_util.h" michael@0: #include "mozilla/Attributes.h" michael@0: #include "mozilla/dom/PCrashReporterParent.h" michael@0: #include "mozilla/ipc/MessageChannel.h" michael@0: #include "mozilla/plugins/BrowserStreamParent.h" michael@0: #include "mozilla/plugins/PluginInstanceParent.h" michael@0: #include "mozilla/Preferences.h" michael@0: #include "mozilla/Services.h" michael@0: #include "mozilla/unused.h" michael@0: #include "nsAutoPtr.h" michael@0: #include "nsCRT.h" michael@0: #include "nsIFile.h" michael@0: #include "nsIObserverService.h" michael@0: #include "nsNPAPIPlugin.h" michael@0: #include "nsPrintfCString.h" michael@0: #include "PluginIdentifierParent.h" michael@0: #include "prsystem.h" michael@0: #include "GeckoProfiler.h" michael@0: michael@0: #ifdef XP_WIN michael@0: #include "PluginHangUIParent.h" michael@0: #include "mozilla/widget/AudioSession.h" michael@0: #endif michael@0: michael@0: #ifdef MOZ_ENABLE_PROFILER_SPS michael@0: #include "nsIProfileSaveEvent.h" michael@0: #endif michael@0: michael@0: #ifdef MOZ_WIDGET_GTK michael@0: #include michael@0: #elif XP_MACOSX michael@0: #include "PluginInterposeOSX.h" michael@0: #include "PluginUtilsOSX.h" michael@0: #endif michael@0: michael@0: using base::KillProcess; michael@0: michael@0: using mozilla::PluginLibrary; michael@0: using mozilla::ipc::MessageChannel; michael@0: using mozilla::dom::PCrashReporterParent; michael@0: using mozilla::dom::CrashReporterParent; michael@0: michael@0: using namespace mozilla; michael@0: using namespace mozilla::plugins; michael@0: using namespace mozilla::plugins::parent; michael@0: michael@0: #ifdef MOZ_CRASHREPORTER michael@0: #include "mozilla/dom/CrashReporterParent.h" michael@0: michael@0: using namespace CrashReporter; michael@0: #endif michael@0: michael@0: static const char kChildTimeoutPref[] = "dom.ipc.plugins.timeoutSecs"; michael@0: static const char kParentTimeoutPref[] = "dom.ipc.plugins.parentTimeoutSecs"; michael@0: static const char kLaunchTimeoutPref[] = "dom.ipc.plugins.processLaunchTimeoutSecs"; michael@0: #ifdef XP_WIN michael@0: static const char kHangUITimeoutPref[] = "dom.ipc.plugins.hangUITimeoutSecs"; michael@0: static const char kHangUIMinDisplayPref[] = "dom.ipc.plugins.hangUIMinDisplaySecs"; michael@0: #define CHILD_TIMEOUT_PREF kHangUITimeoutPref michael@0: #else michael@0: #define CHILD_TIMEOUT_PREF kChildTimeoutPref michael@0: #endif michael@0: michael@0: template<> michael@0: struct RunnableMethodTraits michael@0: { michael@0: typedef mozilla::plugins::PluginModuleParent Class; michael@0: static void RetainCallee(Class* obj) { } michael@0: static void ReleaseCallee(Class* obj) { } michael@0: }; michael@0: michael@0: // static michael@0: PluginLibrary* michael@0: PluginModuleParent::LoadModule(const char* aFilePath) michael@0: { michael@0: PLUGIN_LOG_DEBUG_FUNCTION; michael@0: michael@0: int32_t prefSecs = Preferences::GetInt(kLaunchTimeoutPref, 0); michael@0: michael@0: // Block on the child process being launched and initialized. michael@0: nsAutoPtr parent(new PluginModuleParent(aFilePath)); michael@0: bool launched = parent->mSubprocess->Launch(prefSecs * 1000); michael@0: if (!launched) { michael@0: // We never reached open michael@0: parent->mShutdown = true; michael@0: return nullptr; michael@0: } michael@0: parent->Open(parent->mSubprocess->GetChannel(), michael@0: parent->mSubprocess->GetChildProcessHandle()); michael@0: michael@0: // Request Windows message deferral behavior on our channel. This michael@0: // applies to the top level and all sub plugin protocols since they michael@0: // all share the same channel. michael@0: parent->GetIPCChannel()->SetChannelFlags(MessageChannel::REQUIRE_DEFERRED_MESSAGE_PROTECTION); michael@0: michael@0: TimeoutChanged(CHILD_TIMEOUT_PREF, parent); michael@0: michael@0: #ifdef MOZ_CRASHREPORTER michael@0: // If this fails, we're having IPC troubles, and we're doomed anyways. michael@0: if (!CrashReporterParent::CreateCrashReporter(parent.get())) { michael@0: parent->Close(); michael@0: return nullptr; michael@0: } michael@0: #ifdef XP_WIN michael@0: mozilla::MutexAutoLock lock(parent->mCrashReporterMutex); michael@0: parent->mCrashReporter = parent->CrashReporter(); michael@0: #endif michael@0: #endif michael@0: michael@0: return parent.forget(); michael@0: } michael@0: michael@0: michael@0: PluginModuleParent::PluginModuleParent(const char* aFilePath) michael@0: : mSubprocess(new PluginProcessParent(aFilePath)) michael@0: , mShutdown(false) michael@0: , mClearSiteDataSupported(false) michael@0: , mGetSitesWithDataSupported(false) michael@0: , mNPNIface(nullptr) michael@0: , mPlugin(nullptr) michael@0: , mTaskFactory(MOZ_THIS_IN_INITIALIZER_LIST()) michael@0: #ifdef XP_WIN michael@0: , mPluginCpuUsageOnHang() michael@0: , mHangUIParent(nullptr) michael@0: , mHangUIEnabled(true) michael@0: , mIsTimerReset(true) michael@0: #ifdef MOZ_CRASHREPORTER michael@0: , mCrashReporterMutex("PluginModuleParent::mCrashReporterMutex") michael@0: , mCrashReporter(nullptr) michael@0: #endif michael@0: #endif michael@0: #ifdef MOZ_CRASHREPORTER_INJECTOR michael@0: , mFlashProcess1(0) michael@0: , mFlashProcess2(0) michael@0: #endif michael@0: { michael@0: NS_ASSERTION(mSubprocess, "Out of memory!"); michael@0: michael@0: Preferences::RegisterCallback(TimeoutChanged, kChildTimeoutPref, this); michael@0: Preferences::RegisterCallback(TimeoutChanged, kParentTimeoutPref, this); michael@0: #ifdef XP_WIN michael@0: Preferences::RegisterCallback(TimeoutChanged, kHangUITimeoutPref, this); michael@0: Preferences::RegisterCallback(TimeoutChanged, kHangUIMinDisplayPref, this); michael@0: #endif michael@0: michael@0: #ifdef MOZ_ENABLE_PROFILER_SPS michael@0: InitPluginProfiling(); michael@0: #endif michael@0: } michael@0: michael@0: PluginModuleParent::~PluginModuleParent() michael@0: { michael@0: if (!OkToCleanup()) { michael@0: NS_RUNTIMEABORT("unsafe destruction"); michael@0: } michael@0: michael@0: #ifdef MOZ_ENABLE_PROFILER_SPS michael@0: ShutdownPluginProfiling(); michael@0: #endif michael@0: michael@0: if (!mShutdown) { michael@0: NS_WARNING("Plugin host deleted the module without shutting down."); michael@0: NPError err; michael@0: NP_Shutdown(&err); michael@0: } michael@0: michael@0: NS_ASSERTION(mShutdown, "NP_Shutdown didn't"); michael@0: michael@0: if (mSubprocess) { michael@0: mSubprocess->Delete(); michael@0: mSubprocess = nullptr; michael@0: } michael@0: michael@0: #ifdef MOZ_CRASHREPORTER_INJECTOR michael@0: if (mFlashProcess1) michael@0: UnregisterInjectorCallback(mFlashProcess1); michael@0: if (mFlashProcess2) michael@0: UnregisterInjectorCallback(mFlashProcess2); michael@0: #endif michael@0: michael@0: Preferences::UnregisterCallback(TimeoutChanged, kChildTimeoutPref, this); michael@0: Preferences::UnregisterCallback(TimeoutChanged, kParentTimeoutPref, this); michael@0: #ifdef XP_WIN michael@0: Preferences::UnregisterCallback(TimeoutChanged, kHangUITimeoutPref, this); michael@0: Preferences::UnregisterCallback(TimeoutChanged, kHangUIMinDisplayPref, this); michael@0: michael@0: if (mHangUIParent) { michael@0: delete mHangUIParent; michael@0: mHangUIParent = nullptr; michael@0: } michael@0: #endif michael@0: } michael@0: michael@0: #ifdef MOZ_CRASHREPORTER michael@0: void michael@0: PluginModuleParent::WriteExtraDataForMinidump(AnnotationTable& notes) michael@0: { michael@0: #ifdef XP_WIN michael@0: // mCrashReporterMutex is already held by the caller michael@0: mCrashReporterMutex.AssertCurrentThreadOwns(); michael@0: #endif michael@0: typedef nsDependentCString CS; michael@0: michael@0: // Get the plugin filename, try to get just the file leafname michael@0: const std::string& pluginFile = mSubprocess->GetPluginFilePath(); michael@0: size_t filePos = pluginFile.rfind(FILE_PATH_SEPARATOR); michael@0: if (filePos == std::string::npos) michael@0: filePos = 0; michael@0: else michael@0: filePos++; michael@0: notes.Put(NS_LITERAL_CSTRING("PluginFilename"), CS(pluginFile.substr(filePos).c_str())); michael@0: michael@0: nsCString pluginName; michael@0: nsCString pluginVersion; michael@0: michael@0: nsRefPtr ph = nsPluginHost::GetInst(); michael@0: if (ph) { michael@0: nsPluginTag* tag = ph->TagForPlugin(mPlugin); michael@0: if (tag) { michael@0: pluginName = tag->mName; michael@0: pluginVersion = tag->mVersion; michael@0: } michael@0: } michael@0: michael@0: notes.Put(NS_LITERAL_CSTRING("PluginName"), pluginName); michael@0: notes.Put(NS_LITERAL_CSTRING("PluginVersion"), pluginVersion); michael@0: michael@0: CrashReporterParent* crashReporter = CrashReporter(); michael@0: if (crashReporter) { michael@0: #ifdef XP_WIN michael@0: if (mPluginCpuUsageOnHang.Length() > 0) { michael@0: notes.Put(NS_LITERAL_CSTRING("NumberOfProcessors"), michael@0: nsPrintfCString("%d", PR_GetNumberOfProcessors())); michael@0: michael@0: nsCString cpuUsageStr; michael@0: cpuUsageStr.AppendFloat(std::ceil(mPluginCpuUsageOnHang[0] * 100) / 100); michael@0: notes.Put(NS_LITERAL_CSTRING("PluginCpuUsage"), cpuUsageStr); michael@0: michael@0: #ifdef MOZ_CRASHREPORTER_INJECTOR michael@0: for (uint32_t i=1; i 0) ? (1000 * aChildTimeout) : michael@0: MessageChannel::kNoTimeout; michael@0: SetReplyTimeoutMs(timeoutMs); michael@0: } michael@0: michael@0: void michael@0: PluginModuleParent::TimeoutChanged(const char* aPref, void* aModule) michael@0: { michael@0: NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); michael@0: #ifndef XP_WIN michael@0: if (!strcmp(aPref, kChildTimeoutPref)) { michael@0: // The timeout value used by the parent for children michael@0: int32_t timeoutSecs = Preferences::GetInt(kChildTimeoutPref, 0); michael@0: static_cast(aModule)->SetChildTimeout(timeoutSecs); michael@0: #else michael@0: if (!strcmp(aPref, kChildTimeoutPref) || michael@0: !strcmp(aPref, kHangUIMinDisplayPref) || michael@0: !strcmp(aPref, kHangUITimeoutPref)) { michael@0: static_cast(aModule)->EvaluateHangUIState(true); michael@0: #endif // XP_WIN michael@0: } else if (!strcmp(aPref, kParentTimeoutPref)) { michael@0: // The timeout value used by the child for its parent michael@0: int32_t timeoutSecs = Preferences::GetInt(kParentTimeoutPref, 0); michael@0: unused << static_cast(aModule)->SendSetParentHangTimeout(timeoutSecs); michael@0: } michael@0: } michael@0: michael@0: void michael@0: PluginModuleParent::CleanupFromTimeout(const bool aFromHangUI) michael@0: { michael@0: if (mShutdown) { michael@0: return; michael@0: } michael@0: michael@0: if (!OkToCleanup()) { michael@0: // there's still plugin code on the C++ stack, try again michael@0: MessageLoop::current()->PostDelayedTask( michael@0: FROM_HERE, michael@0: mTaskFactory.NewRunnableMethod( michael@0: &PluginModuleParent::CleanupFromTimeout, aFromHangUI), 10); michael@0: return; michael@0: } michael@0: michael@0: /* If the plugin container was terminated by the Plugin Hang UI, michael@0: then either the I/O thread detects a channel error, or the michael@0: main thread must set the error (whomever gets there first). michael@0: OTOH, if we terminate and return false from michael@0: ShouldContinueFromReplyTimeout, then the channel state has michael@0: already been set to ChannelTimeout and we should call the michael@0: regular Close function. */ michael@0: if (aFromHangUI) { michael@0: GetIPCChannel()->CloseWithError(); michael@0: } else { michael@0: Close(); michael@0: } michael@0: } michael@0: michael@0: #ifdef XP_WIN michael@0: namespace { michael@0: michael@0: uint64_t michael@0: FileTimeToUTC(const FILETIME& ftime) michael@0: { michael@0: ULARGE_INTEGER li; michael@0: li.LowPart = ftime.dwLowDateTime; michael@0: li.HighPart = ftime.dwHighDateTime; michael@0: return li.QuadPart; michael@0: } michael@0: michael@0: struct CpuUsageSamples michael@0: { michael@0: uint64_t sampleTimes[2]; michael@0: uint64_t cpuTimes[2]; michael@0: }; michael@0: michael@0: bool michael@0: GetProcessCpuUsage(const InfallibleTArray& processHandles, InfallibleTArray& cpuUsage) michael@0: { michael@0: InfallibleTArray samples(processHandles.Length()); michael@0: FILETIME creationTime, exitTime, kernelTime, userTime, currentTime; michael@0: BOOL res; michael@0: michael@0: for (uint32_t i = 0; i < processHandles.Length(); ++i) { michael@0: ::GetSystemTimeAsFileTime(¤tTime); michael@0: res = ::GetProcessTimes(processHandles[i], &creationTime, &exitTime, &kernelTime, &userTime); michael@0: if (!res) { michael@0: NS_WARNING("failed to get process times"); michael@0: return false; michael@0: } michael@0: michael@0: CpuUsageSamples s; michael@0: s.sampleTimes[0] = FileTimeToUTC(currentTime); michael@0: s.cpuTimes[0] = FileTimeToUTC(kernelTime) + FileTimeToUTC(userTime); michael@0: samples.AppendElement(s); michael@0: } michael@0: michael@0: // we already hung for a while, a little bit longer won't matter michael@0: ::Sleep(50); michael@0: michael@0: const int32_t numberOfProcessors = PR_GetNumberOfProcessors(); michael@0: michael@0: for (uint32_t i = 0; i < processHandles.Length(); ++i) { michael@0: ::GetSystemTimeAsFileTime(¤tTime); michael@0: res = ::GetProcessTimes(processHandles[i], &creationTime, &exitTime, &kernelTime, &userTime); michael@0: if (!res) { michael@0: NS_WARNING("failed to get process times"); michael@0: return false; michael@0: } michael@0: michael@0: samples[i].sampleTimes[1] = FileTimeToUTC(currentTime); michael@0: samples[i].cpuTimes[1] = FileTimeToUTC(kernelTime) + FileTimeToUTC(userTime); michael@0: michael@0: const uint64_t deltaSampleTime = samples[i].sampleTimes[1] - samples[i].sampleTimes[0]; michael@0: const uint64_t deltaCpuTime = samples[i].cpuTimes[1] - samples[i].cpuTimes[0]; michael@0: const float usage = 100.f * (float(deltaCpuTime) / deltaSampleTime) / numberOfProcessors; michael@0: cpuUsage.AppendElement(usage); michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: } // anonymous namespace michael@0: michael@0: void michael@0: PluginModuleParent::ExitedCxxStack() michael@0: { michael@0: FinishHangUI(); michael@0: } michael@0: michael@0: #endif // #ifdef XP_WIN michael@0: michael@0: #ifdef MOZ_CRASHREPORTER_INJECTOR michael@0: static bool michael@0: CreateFlashMinidump(DWORD processId, ThreadId childThread, michael@0: nsIFile* parentMinidump, const nsACString& name) michael@0: { michael@0: if (processId == 0) { michael@0: return false; michael@0: } michael@0: michael@0: base::ProcessHandle handle; michael@0: if (!base::OpenPrivilegedProcessHandle(processId, &handle)) { michael@0: return false; michael@0: } michael@0: michael@0: bool res = CreateAdditionalChildMinidump(handle, 0, parentMinidump, name); michael@0: base::CloseProcessHandle(handle); michael@0: michael@0: return res; michael@0: } michael@0: #endif michael@0: michael@0: bool michael@0: PluginModuleParent::ShouldContinueFromReplyTimeout() michael@0: { michael@0: #ifdef XP_WIN michael@0: if (LaunchHangUI()) { michael@0: return true; michael@0: } michael@0: // If LaunchHangUI returned false then we should proceed with the michael@0: // original plugin hang behaviour and kill the plugin container. michael@0: FinishHangUI(); michael@0: #endif // XP_WIN michael@0: TerminateChildProcess(MessageLoop::current()); michael@0: return false; michael@0: } michael@0: michael@0: void michael@0: PluginModuleParent::TerminateChildProcess(MessageLoop* aMsgLoop) michael@0: { michael@0: #ifdef MOZ_CRASHREPORTER michael@0: #ifdef XP_WIN michael@0: mozilla::MutexAutoLock lock(mCrashReporterMutex); michael@0: CrashReporterParent* crashReporter = mCrashReporter; michael@0: if (!crashReporter) { michael@0: // If mCrashReporter is null then the hang has ended, the plugin module michael@0: // is shutting down. There's nothing to do here. michael@0: return; michael@0: } michael@0: #else michael@0: CrashReporterParent* crashReporter = CrashReporter(); michael@0: #endif michael@0: crashReporter->AnnotateCrashReport(NS_LITERAL_CSTRING("PluginHang"), michael@0: NS_LITERAL_CSTRING("1")); michael@0: #ifdef XP_WIN michael@0: if (mHangUIParent) { michael@0: unsigned int hangUIDuration = mHangUIParent->LastShowDurationMs(); michael@0: if (hangUIDuration) { michael@0: nsPrintfCString strHangUIDuration("%u", hangUIDuration); michael@0: crashReporter->AnnotateCrashReport( michael@0: NS_LITERAL_CSTRING("PluginHangUIDuration"), michael@0: strHangUIDuration); michael@0: } michael@0: } michael@0: #endif // XP_WIN michael@0: if (crashReporter->GeneratePairedMinidump(this)) { michael@0: mPluginDumpID = crashReporter->ChildDumpID(); michael@0: PLUGIN_LOG_DEBUG( michael@0: ("generated paired browser/plugin minidumps: %s)", michael@0: NS_ConvertUTF16toUTF8(mPluginDumpID).get())); michael@0: michael@0: nsAutoCString additionalDumps("browser"); michael@0: michael@0: #ifdef MOZ_CRASHREPORTER_INJECTOR michael@0: nsCOMPtr pluginDumpFile; michael@0: michael@0: if (GetMinidumpForID(mPluginDumpID, getter_AddRefs(pluginDumpFile)) && michael@0: pluginDumpFile) { michael@0: nsCOMPtr childDumpFile; michael@0: michael@0: if (CreateFlashMinidump(mFlashProcess1, 0, pluginDumpFile, michael@0: NS_LITERAL_CSTRING("flash1"))) { michael@0: additionalDumps.Append(",flash1"); michael@0: } michael@0: if (CreateFlashMinidump(mFlashProcess2, 0, pluginDumpFile, michael@0: NS_LITERAL_CSTRING("flash2"))) { michael@0: additionalDumps.Append(",flash2"); michael@0: } michael@0: } michael@0: #endif michael@0: michael@0: crashReporter->AnnotateCrashReport( michael@0: NS_LITERAL_CSTRING("additional_minidumps"), michael@0: additionalDumps); michael@0: } else { michael@0: NS_WARNING("failed to capture paired minidumps from hang"); michael@0: } michael@0: #endif michael@0: michael@0: #ifdef XP_WIN michael@0: // collect cpu usage for plugin processes michael@0: michael@0: InfallibleTArray processHandles; michael@0: michael@0: processHandles.AppendElement(OtherProcess()); michael@0: michael@0: #ifdef MOZ_CRASHREPORTER_INJECTOR michael@0: { michael@0: base::ProcessHandle handle; michael@0: if (mFlashProcess1 && base::OpenProcessHandle(mFlashProcess1, &handle)) { michael@0: processHandles.AppendElement(handle); michael@0: } michael@0: if (mFlashProcess2 && base::OpenProcessHandle(mFlashProcess2, &handle)) { michael@0: processHandles.AppendElement(handle); michael@0: } michael@0: } michael@0: #endif michael@0: michael@0: if (!GetProcessCpuUsage(processHandles, mPluginCpuUsageOnHang)) { michael@0: mPluginCpuUsageOnHang.Clear(); michael@0: } michael@0: #endif michael@0: michael@0: // this must run before the error notification from the channel, michael@0: // or not at all michael@0: bool isFromHangUI = aMsgLoop != MessageLoop::current(); michael@0: aMsgLoop->PostTask( michael@0: FROM_HERE, michael@0: mTaskFactory.NewRunnableMethod( michael@0: &PluginModuleParent::CleanupFromTimeout, isFromHangUI)); michael@0: michael@0: if (!KillProcess(OtherProcess(), 1, false)) michael@0: NS_WARNING("failed to kill subprocess!"); michael@0: } michael@0: michael@0: #ifdef XP_WIN michael@0: void michael@0: PluginModuleParent::EvaluateHangUIState(const bool aReset) michael@0: { michael@0: int32_t minDispSecs = Preferences::GetInt(kHangUIMinDisplayPref, 10); michael@0: int32_t autoStopSecs = Preferences::GetInt(kChildTimeoutPref, 0); michael@0: int32_t timeoutSecs = 0; michael@0: if (autoStopSecs > 0 && autoStopSecs < minDispSecs) { michael@0: /* If we're going to automatically terminate the plugin within a michael@0: time frame shorter than minDispSecs, there's no point in michael@0: showing the hang UI; it would just flash briefly on the screen. */ michael@0: mHangUIEnabled = false; michael@0: } else { michael@0: timeoutSecs = Preferences::GetInt(kHangUITimeoutPref, 0); michael@0: mHangUIEnabled = timeoutSecs > 0; michael@0: } michael@0: if (mHangUIEnabled) { michael@0: if (aReset) { michael@0: mIsTimerReset = true; michael@0: SetChildTimeout(timeoutSecs); michael@0: return; michael@0: } else if (mIsTimerReset) { michael@0: /* The Hang UI is being shown, so now we're setting the michael@0: timeout to kChildTimeoutPref while we wait for a user michael@0: response. ShouldContinueFromReplyTimeout will fire michael@0: after (reply timeout / 2) seconds, which is not what michael@0: we want. Doubling the timeout value here so that we get michael@0: the right result. */ michael@0: autoStopSecs *= 2; michael@0: } michael@0: } michael@0: mIsTimerReset = false; michael@0: SetChildTimeout(autoStopSecs); michael@0: } michael@0: michael@0: bool michael@0: PluginModuleParent::GetPluginName(nsAString& aPluginName) michael@0: { michael@0: nsRefPtr host = nsPluginHost::GetInst(); michael@0: if (!host) { michael@0: return false; michael@0: } michael@0: nsPluginTag* pluginTag = host->TagForPlugin(mPlugin); michael@0: if (!pluginTag) { michael@0: return false; michael@0: } michael@0: CopyUTF8toUTF16(pluginTag->mName, aPluginName); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: PluginModuleParent::LaunchHangUI() michael@0: { michael@0: if (!mHangUIEnabled) { michael@0: return false; michael@0: } michael@0: if (mHangUIParent) { michael@0: if (mHangUIParent->IsShowing()) { michael@0: // We've already shown the UI but the timeout has expired again. michael@0: return false; michael@0: } michael@0: if (mHangUIParent->DontShowAgain()) { michael@0: return !mHangUIParent->WasLastHangStopped(); michael@0: } michael@0: delete mHangUIParent; michael@0: mHangUIParent = nullptr; michael@0: } michael@0: mHangUIParent = new PluginHangUIParent(this, michael@0: Preferences::GetInt(kHangUITimeoutPref, 0), michael@0: Preferences::GetInt(kChildTimeoutPref, 0)); michael@0: nsAutoString pluginName; michael@0: if (!GetPluginName(pluginName)) { michael@0: return false; michael@0: } michael@0: bool retval = mHangUIParent->Init(pluginName); michael@0: if (retval) { michael@0: /* Once the UI is shown we switch the timeout over to use michael@0: kChildTimeoutPref, allowing us to terminate a hung plugin michael@0: after kChildTimeoutPref seconds if the user doesn't respond to michael@0: the hang UI. */ michael@0: EvaluateHangUIState(false); michael@0: } michael@0: return retval; michael@0: } michael@0: michael@0: void michael@0: PluginModuleParent::FinishHangUI() michael@0: { michael@0: if (mHangUIEnabled && mHangUIParent) { michael@0: bool needsCancel = mHangUIParent->IsShowing(); michael@0: // If we're still showing, send a Cancel notification michael@0: if (needsCancel) { michael@0: mHangUIParent->Cancel(); michael@0: } michael@0: /* If we cancelled the UI or if the user issued a response, michael@0: we need to reset the child process timeout. */ michael@0: if (needsCancel || michael@0: !mIsTimerReset && mHangUIParent->WasShown()) { michael@0: /* We changed the timeout to kChildTimeoutPref when the plugin hang michael@0: UI was displayed. Now that we're finishing the UI, we need to michael@0: switch it back to kHangUITimeoutPref. */ michael@0: EvaluateHangUIState(true); michael@0: } michael@0: } michael@0: } michael@0: #endif // XP_WIN michael@0: michael@0: #ifdef MOZ_CRASHREPORTER michael@0: CrashReporterParent* michael@0: PluginModuleParent::CrashReporter() michael@0: { michael@0: return static_cast(ManagedPCrashReporterParent()[0]); michael@0: } michael@0: michael@0: #ifdef MOZ_CRASHREPORTER_INJECTOR michael@0: static void michael@0: RemoveMinidump(nsIFile* minidump) michael@0: { michael@0: if (!minidump) michael@0: return; michael@0: michael@0: minidump->Remove(false); michael@0: nsCOMPtr extraFile; michael@0: if (GetExtraFileForMinidump(minidump, michael@0: getter_AddRefs(extraFile))) { michael@0: extraFile->Remove(true); michael@0: } michael@0: } michael@0: #endif // MOZ_CRASHREPORTER_INJECTOR michael@0: michael@0: void michael@0: PluginModuleParent::ProcessFirstMinidump() michael@0: { michael@0: #ifdef XP_WIN michael@0: mozilla::MutexAutoLock lock(mCrashReporterMutex); michael@0: #endif michael@0: CrashReporterParent* crashReporter = CrashReporter(); michael@0: if (!crashReporter) michael@0: return; michael@0: michael@0: AnnotationTable notes(4); michael@0: WriteExtraDataForMinidump(notes); michael@0: michael@0: if (!mPluginDumpID.IsEmpty()) { michael@0: crashReporter->GenerateChildData(¬es); michael@0: return; michael@0: } michael@0: michael@0: uint32_t sequence = UINT32_MAX; michael@0: nsCOMPtr dumpFile; michael@0: nsAutoCString flashProcessType; michael@0: TakeMinidump(getter_AddRefs(dumpFile), &sequence); michael@0: michael@0: #ifdef MOZ_CRASHREPORTER_INJECTOR michael@0: nsCOMPtr childDumpFile; michael@0: uint32_t childSequence; michael@0: michael@0: if (mFlashProcess1 && michael@0: TakeMinidumpForChild(mFlashProcess1, michael@0: getter_AddRefs(childDumpFile), michael@0: &childSequence)) { michael@0: if (childSequence < sequence) { michael@0: RemoveMinidump(dumpFile); michael@0: dumpFile = childDumpFile; michael@0: sequence = childSequence; michael@0: flashProcessType.AssignLiteral("Broker"); michael@0: } michael@0: else { michael@0: RemoveMinidump(childDumpFile); michael@0: } michael@0: } michael@0: if (mFlashProcess2 && michael@0: TakeMinidumpForChild(mFlashProcess2, michael@0: getter_AddRefs(childDumpFile), michael@0: &childSequence)) { michael@0: if (childSequence < sequence) { michael@0: RemoveMinidump(dumpFile); michael@0: dumpFile = childDumpFile; michael@0: sequence = childSequence; michael@0: flashProcessType.AssignLiteral("Sandbox"); michael@0: } michael@0: else { michael@0: RemoveMinidump(childDumpFile); michael@0: } michael@0: } michael@0: #endif michael@0: michael@0: if (!dumpFile) { michael@0: NS_WARNING("[PluginModuleParent::ActorDestroy] abnormal shutdown without minidump!"); michael@0: return; michael@0: } michael@0: michael@0: PLUGIN_LOG_DEBUG(("got child minidump: %s", michael@0: NS_ConvertUTF16toUTF8(mPluginDumpID).get())); michael@0: michael@0: GetIDFromMinidump(dumpFile, mPluginDumpID); michael@0: if (!flashProcessType.IsEmpty()) { michael@0: notes.Put(NS_LITERAL_CSTRING("FlashProcessDump"), flashProcessType); michael@0: } michael@0: crashReporter->GenerateCrashReportForMinidump(dumpFile, ¬es); michael@0: } michael@0: #endif michael@0: michael@0: void michael@0: PluginModuleParent::ActorDestroy(ActorDestroyReason why) michael@0: { michael@0: switch (why) { michael@0: case AbnormalShutdown: { michael@0: #ifdef MOZ_CRASHREPORTER michael@0: ProcessFirstMinidump(); michael@0: #endif michael@0: michael@0: mShutdown = true; michael@0: // Defer the PluginCrashed method so that we don't re-enter michael@0: // and potentially modify the actor child list while enumerating it. michael@0: if (mPlugin) michael@0: MessageLoop::current()->PostTask( michael@0: FROM_HERE, michael@0: mTaskFactory.NewRunnableMethod( michael@0: &PluginModuleParent::NotifyPluginCrashed)); michael@0: break; michael@0: } michael@0: case NormalShutdown: michael@0: mShutdown = true; michael@0: break; michael@0: michael@0: default: michael@0: NS_RUNTIMEABORT("Unexpected shutdown reason for toplevel actor."); michael@0: } michael@0: } michael@0: michael@0: void michael@0: PluginModuleParent::NotifyPluginCrashed() michael@0: { michael@0: if (!OkToCleanup()) { michael@0: // there's still plugin code on the C++ stack. try again michael@0: MessageLoop::current()->PostDelayedTask( michael@0: FROM_HERE, michael@0: mTaskFactory.NewRunnableMethod( michael@0: &PluginModuleParent::NotifyPluginCrashed), 10); michael@0: return; michael@0: } michael@0: michael@0: if (mPlugin) michael@0: mPlugin->PluginCrashed(mPluginDumpID, mBrowserDumpID); michael@0: } michael@0: michael@0: PPluginIdentifierParent* michael@0: PluginModuleParent::AllocPPluginIdentifierParent(const nsCString& aString, michael@0: const int32_t& aInt, michael@0: const bool& aTemporary) michael@0: { michael@0: if (aTemporary) { michael@0: NS_ERROR("Plugins don't create temporary identifiers."); michael@0: return nullptr; // should abort the plugin michael@0: } michael@0: michael@0: NPIdentifier npident = aString.IsVoid() ? michael@0: mozilla::plugins::parent::_getintidentifier(aInt) : michael@0: mozilla::plugins::parent::_getstringidentifier(aString.get()); michael@0: michael@0: if (!npident) { michael@0: NS_WARNING("Failed to get identifier!"); michael@0: return nullptr; michael@0: } michael@0: michael@0: PluginIdentifierParent* ident = new PluginIdentifierParent(npident, false); michael@0: mIdentifiers.Put(npident, ident); michael@0: return ident; michael@0: } michael@0: michael@0: bool michael@0: PluginModuleParent::DeallocPPluginIdentifierParent(PPluginIdentifierParent* aActor) michael@0: { michael@0: delete aActor; michael@0: return true; michael@0: } michael@0: michael@0: PPluginInstanceParent* michael@0: PluginModuleParent::AllocPPluginInstanceParent(const nsCString& aMimeType, michael@0: const uint16_t& aMode, michael@0: const InfallibleTArray& aNames, michael@0: const InfallibleTArray& aValues, michael@0: NPError* rv) michael@0: { michael@0: NS_ERROR("Not reachable!"); michael@0: return nullptr; michael@0: } michael@0: michael@0: bool michael@0: PluginModuleParent::DeallocPPluginInstanceParent(PPluginInstanceParent* aActor) michael@0: { michael@0: PLUGIN_LOG_DEBUG_METHOD; michael@0: delete aActor; michael@0: return true; michael@0: } michael@0: michael@0: void michael@0: PluginModuleParent::SetPluginFuncs(NPPluginFuncs* aFuncs) michael@0: { michael@0: aFuncs->version = (NP_VERSION_MAJOR << 8) | NP_VERSION_MINOR; michael@0: aFuncs->javaClass = nullptr; michael@0: michael@0: // Gecko should always call these functions through a PluginLibrary object. michael@0: aFuncs->newp = nullptr; michael@0: aFuncs->clearsitedata = nullptr; michael@0: aFuncs->getsiteswithdata = nullptr; michael@0: michael@0: aFuncs->destroy = NPP_Destroy; michael@0: aFuncs->setwindow = NPP_SetWindow; michael@0: aFuncs->newstream = NPP_NewStream; michael@0: aFuncs->destroystream = NPP_DestroyStream; michael@0: aFuncs->asfile = NPP_StreamAsFile; michael@0: aFuncs->writeready = NPP_WriteReady; michael@0: aFuncs->write = NPP_Write; michael@0: aFuncs->print = NPP_Print; michael@0: aFuncs->event = NPP_HandleEvent; michael@0: aFuncs->urlnotify = NPP_URLNotify; michael@0: aFuncs->getvalue = NPP_GetValue; michael@0: aFuncs->setvalue = NPP_SetValue; michael@0: aFuncs->gotfocus = nullptr; michael@0: aFuncs->lostfocus = nullptr; michael@0: aFuncs->urlredirectnotify = nullptr; michael@0: michael@0: // Provide 'NPP_URLRedirectNotify', 'NPP_ClearSiteData', and michael@0: // 'NPP_GetSitesWithData' functionality if it is supported by the plugin. michael@0: bool urlRedirectSupported = false; michael@0: unused << CallOptionalFunctionsSupported(&urlRedirectSupported, michael@0: &mClearSiteDataSupported, michael@0: &mGetSitesWithDataSupported); michael@0: if (urlRedirectSupported) { michael@0: aFuncs->urlredirectnotify = NPP_URLRedirectNotify; michael@0: } michael@0: } michael@0: michael@0: NPError michael@0: PluginModuleParent::NPP_Destroy(NPP instance, michael@0: NPSavedData** /*saved*/) michael@0: { michael@0: // FIXME/cjones: michael@0: // (1) send a "destroy" message to the child michael@0: // (2) the child shuts down its instance michael@0: // (3) remove both parent and child IDs from map michael@0: // (4) free parent michael@0: PLUGIN_LOG_DEBUG_FUNCTION; michael@0: michael@0: PluginInstanceParent* parentInstance = michael@0: static_cast(instance->pdata); michael@0: michael@0: if (!parentInstance) michael@0: return NPERR_NO_ERROR; michael@0: michael@0: NPError retval = parentInstance->Destroy(); michael@0: instance->pdata = nullptr; michael@0: michael@0: unused << PluginInstanceParent::Call__delete__(parentInstance); michael@0: return retval; michael@0: } michael@0: michael@0: NPError michael@0: PluginModuleParent::NPP_NewStream(NPP instance, NPMIMEType type, michael@0: NPStream* stream, NPBool seekable, michael@0: uint16_t* stype) michael@0: { michael@0: PROFILER_LABEL("PluginModuleParent", "NPP_NewStream"); michael@0: PluginInstanceParent* i = InstCast(instance); michael@0: if (!i) michael@0: return NPERR_GENERIC_ERROR; michael@0: michael@0: return i->NPP_NewStream(type, stream, seekable, michael@0: stype); michael@0: } michael@0: michael@0: NPError michael@0: PluginModuleParent::NPP_SetWindow(NPP instance, NPWindow* window) michael@0: { michael@0: PluginInstanceParent* i = InstCast(instance); michael@0: if (!i) michael@0: return NPERR_GENERIC_ERROR; michael@0: michael@0: return i->NPP_SetWindow(window); michael@0: } michael@0: michael@0: NPError michael@0: PluginModuleParent::NPP_DestroyStream(NPP instance, michael@0: NPStream* stream, michael@0: NPReason reason) michael@0: { michael@0: PluginInstanceParent* i = InstCast(instance); michael@0: if (!i) michael@0: return NPERR_GENERIC_ERROR; michael@0: michael@0: return i->NPP_DestroyStream(stream, reason); michael@0: } michael@0: michael@0: int32_t michael@0: PluginModuleParent::NPP_WriteReady(NPP instance, michael@0: NPStream* stream) michael@0: { michael@0: BrowserStreamParent* s = StreamCast(instance, stream); michael@0: if (!s) michael@0: return -1; michael@0: michael@0: return s->WriteReady(); michael@0: } michael@0: michael@0: int32_t michael@0: PluginModuleParent::NPP_Write(NPP instance, michael@0: NPStream* stream, michael@0: int32_t offset, michael@0: int32_t len, michael@0: void* buffer) michael@0: { michael@0: BrowserStreamParent* s = StreamCast(instance, stream); michael@0: if (!s) michael@0: return -1; michael@0: michael@0: return s->Write(offset, len, buffer); michael@0: } michael@0: michael@0: void michael@0: PluginModuleParent::NPP_StreamAsFile(NPP instance, michael@0: NPStream* stream, michael@0: const char* fname) michael@0: { michael@0: BrowserStreamParent* s = StreamCast(instance, stream); michael@0: if (!s) michael@0: return; michael@0: michael@0: s->StreamAsFile(fname); michael@0: } michael@0: michael@0: void michael@0: PluginModuleParent::NPP_Print(NPP instance, NPPrint* platformPrint) michael@0: { michael@0: PluginInstanceParent* i = InstCast(instance); michael@0: if (i) michael@0: i->NPP_Print(platformPrint); michael@0: } michael@0: michael@0: int16_t michael@0: PluginModuleParent::NPP_HandleEvent(NPP instance, void* event) michael@0: { michael@0: PluginInstanceParent* i = InstCast(instance); michael@0: if (!i) michael@0: return false; michael@0: michael@0: return i->NPP_HandleEvent(event); michael@0: } michael@0: michael@0: void michael@0: PluginModuleParent::NPP_URLNotify(NPP instance, const char* url, michael@0: NPReason reason, void* notifyData) michael@0: { michael@0: PluginInstanceParent* i = InstCast(instance); michael@0: if (!i) michael@0: return; michael@0: michael@0: i->NPP_URLNotify(url, reason, notifyData); michael@0: } michael@0: michael@0: NPError michael@0: PluginModuleParent::NPP_GetValue(NPP instance, michael@0: NPPVariable variable, void *ret_value) michael@0: { michael@0: PluginInstanceParent* i = InstCast(instance); michael@0: if (!i) michael@0: return NPERR_GENERIC_ERROR; michael@0: michael@0: return i->NPP_GetValue(variable, ret_value); michael@0: } michael@0: michael@0: NPError michael@0: PluginModuleParent::NPP_SetValue(NPP instance, NPNVariable variable, michael@0: void *value) michael@0: { michael@0: PluginInstanceParent* i = InstCast(instance); michael@0: if (!i) michael@0: return NPERR_GENERIC_ERROR; michael@0: michael@0: return i->NPP_SetValue(variable, value); michael@0: } michael@0: michael@0: bool michael@0: PluginModuleParent::RecvBackUpXResources(const FileDescriptor& aXSocketFd) michael@0: { michael@0: #ifndef MOZ_X11 michael@0: NS_RUNTIMEABORT("This message only makes sense on X11 platforms"); michael@0: #else michael@0: NS_ABORT_IF_FALSE(0 > mPluginXSocketFdDup.get(), michael@0: "Already backed up X resources??"); michael@0: mPluginXSocketFdDup.forget(); michael@0: if (aXSocketFd.IsValid()) { michael@0: mPluginXSocketFdDup.reset(aXSocketFd.PlatformHandle()); michael@0: } michael@0: #endif michael@0: return true; michael@0: } michael@0: michael@0: void michael@0: PluginModuleParent::NPP_URLRedirectNotify(NPP instance, const char* url, michael@0: int32_t status, void* notifyData) michael@0: { michael@0: PluginInstanceParent* i = InstCast(instance); michael@0: if (!i) michael@0: return; michael@0: michael@0: i->NPP_URLRedirectNotify(url, status, notifyData); michael@0: } michael@0: michael@0: bool michael@0: PluginModuleParent::AnswerNPN_UserAgent(nsCString* userAgent) michael@0: { michael@0: *userAgent = NullableString(mNPNIface->uagent(nullptr)); michael@0: return true; michael@0: } michael@0: michael@0: PluginIdentifierParent* michael@0: PluginModuleParent::GetIdentifierForNPIdentifier(NPP npp, NPIdentifier aIdentifier) michael@0: { michael@0: PluginIdentifierParent* ident; michael@0: if (mIdentifiers.Get(aIdentifier, &ident)) { michael@0: if (ident->IsTemporary()) { michael@0: ident->AddTemporaryRef(); michael@0: } michael@0: return ident; michael@0: } michael@0: michael@0: nsCString string; michael@0: int32_t intval = -1; michael@0: bool temporary = false; michael@0: if (mozilla::plugins::parent::_identifierisstring(aIdentifier)) { michael@0: NPUTF8* chars = michael@0: mozilla::plugins::parent::_utf8fromidentifier(aIdentifier); michael@0: if (!chars) { michael@0: return nullptr; michael@0: } michael@0: string.Adopt(chars); michael@0: temporary = !NPStringIdentifierIsPermanent(npp, aIdentifier); michael@0: } michael@0: else { michael@0: intval = mozilla::plugins::parent::_intfromidentifier(aIdentifier); michael@0: string.SetIsVoid(true); michael@0: } michael@0: michael@0: ident = new PluginIdentifierParent(aIdentifier, temporary); michael@0: if (!SendPPluginIdentifierConstructor(ident, string, intval, temporary)) michael@0: return nullptr; michael@0: michael@0: if (!temporary) { michael@0: mIdentifiers.Put(aIdentifier, ident); michael@0: } michael@0: return ident; michael@0: } michael@0: michael@0: PluginInstanceParent* michael@0: PluginModuleParent::InstCast(NPP instance) michael@0: { michael@0: PluginInstanceParent* ip = michael@0: static_cast(instance->pdata); michael@0: michael@0: // If the plugin crashed and the PluginInstanceParent was deleted, michael@0: // instance->pdata will be nullptr. michael@0: if (!ip) michael@0: return nullptr; michael@0: michael@0: if (instance != ip->mNPP) { michael@0: NS_RUNTIMEABORT("Corrupted plugin data."); michael@0: } michael@0: return ip; michael@0: } michael@0: michael@0: BrowserStreamParent* michael@0: PluginModuleParent::StreamCast(NPP instance, michael@0: NPStream* s) michael@0: { michael@0: PluginInstanceParent* ip = InstCast(instance); michael@0: if (!ip) michael@0: return nullptr; michael@0: michael@0: BrowserStreamParent* sp = michael@0: static_cast(static_cast(s->pdata)); michael@0: if (sp->mNPP != ip || s != sp->mStream) { michael@0: NS_RUNTIMEABORT("Corrupted plugin stream data."); michael@0: } michael@0: return sp; michael@0: } michael@0: michael@0: bool michael@0: PluginModuleParent::HasRequiredFunctions() michael@0: { michael@0: return true; michael@0: } michael@0: michael@0: nsresult michael@0: PluginModuleParent::AsyncSetWindow(NPP instance, NPWindow* window) michael@0: { michael@0: PluginInstanceParent* i = InstCast(instance); michael@0: if (!i) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: return i->AsyncSetWindow(window); michael@0: } michael@0: michael@0: nsresult michael@0: PluginModuleParent::GetImageContainer(NPP instance, michael@0: mozilla::layers::ImageContainer** aContainer) michael@0: { michael@0: PluginInstanceParent* i = InstCast(instance); michael@0: return !i ? NS_ERROR_FAILURE : i->GetImageContainer(aContainer); michael@0: } michael@0: michael@0: nsresult michael@0: PluginModuleParent::GetImageSize(NPP instance, michael@0: nsIntSize* aSize) michael@0: { michael@0: PluginInstanceParent* i = InstCast(instance); michael@0: return !i ? NS_ERROR_FAILURE : i->GetImageSize(aSize); michael@0: } michael@0: michael@0: nsresult michael@0: PluginModuleParent::SetBackgroundUnknown(NPP instance) michael@0: { michael@0: PluginInstanceParent* i = InstCast(instance); michael@0: if (!i) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: return i->SetBackgroundUnknown(); michael@0: } michael@0: michael@0: nsresult michael@0: PluginModuleParent::BeginUpdateBackground(NPP instance, michael@0: const nsIntRect& aRect, michael@0: gfxContext** aCtx) michael@0: { michael@0: PluginInstanceParent* i = InstCast(instance); michael@0: if (!i) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: return i->BeginUpdateBackground(aRect, aCtx); michael@0: } michael@0: michael@0: nsresult michael@0: PluginModuleParent::EndUpdateBackground(NPP instance, michael@0: gfxContext* aCtx, michael@0: const nsIntRect& aRect) michael@0: { michael@0: PluginInstanceParent* i = InstCast(instance); michael@0: if (!i) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: return i->EndUpdateBackground(aCtx, aRect); michael@0: } michael@0: michael@0: #if defined(XP_UNIX) && !defined(XP_MACOSX) && !defined(MOZ_WIDGET_GONK) michael@0: nsresult michael@0: PluginModuleParent::NP_Initialize(NPNetscapeFuncs* bFuncs, NPPluginFuncs* pFuncs, NPError* error) michael@0: { michael@0: PLUGIN_LOG_DEBUG_METHOD; michael@0: michael@0: mNPNIface = bFuncs; michael@0: michael@0: if (mShutdown) { michael@0: *error = NPERR_GENERIC_ERROR; michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: uint32_t flags = 0; michael@0: michael@0: if (!CallNP_Initialize(flags, error)) { michael@0: Close(); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: else if (*error != NPERR_NO_ERROR) { michael@0: Close(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: SetPluginFuncs(pFuncs); michael@0: michael@0: return NS_OK; michael@0: } michael@0: #else michael@0: nsresult michael@0: PluginModuleParent::NP_Initialize(NPNetscapeFuncs* bFuncs, NPError* error) michael@0: { michael@0: PLUGIN_LOG_DEBUG_METHOD; michael@0: michael@0: mNPNIface = bFuncs; michael@0: michael@0: if (mShutdown) { michael@0: *error = NPERR_GENERIC_ERROR; michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: uint32_t flags = 0; michael@0: #ifdef XP_WIN michael@0: flags |= kAllowAsyncDrawing; michael@0: #endif michael@0: michael@0: if (!CallNP_Initialize(flags, error)) { michael@0: Close(); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: if (*error != NPERR_NO_ERROR) { michael@0: Close(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: #if defined XP_WIN michael@0: // Send the info needed to join the chrome process's audio session to the michael@0: // plugin process michael@0: nsID id; michael@0: nsString sessionName; michael@0: nsString iconPath; michael@0: michael@0: if (NS_SUCCEEDED(mozilla::widget::GetAudioSessionData(id, sessionName, michael@0: iconPath))) michael@0: unused << SendSetAudioSessionData(id, sessionName, iconPath); michael@0: #endif michael@0: michael@0: #ifdef MOZ_CRASHREPORTER_INJECTOR michael@0: InitializeInjector(); michael@0: #endif michael@0: michael@0: return NS_OK; michael@0: } michael@0: #endif michael@0: michael@0: nsresult michael@0: PluginModuleParent::NP_Shutdown(NPError* error) michael@0: { michael@0: PLUGIN_LOG_DEBUG_METHOD; michael@0: michael@0: if (mShutdown) { michael@0: *error = NPERR_GENERIC_ERROR; michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: bool ok = CallNP_Shutdown(error); michael@0: michael@0: // if NP_Shutdown() is nested within another interrupt call, this will michael@0: // break things. but lord help us if we're doing that anyway; the michael@0: // plugin dso will have been unloaded on the other side by the michael@0: // CallNP_Shutdown() message michael@0: Close(); michael@0: michael@0: return ok ? NS_OK : NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: nsresult michael@0: PluginModuleParent::NP_GetMIMEDescription(const char** mimeDesc) michael@0: { michael@0: PLUGIN_LOG_DEBUG_METHOD; michael@0: michael@0: *mimeDesc = "application/x-foobar"; michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: PluginModuleParent::NP_GetValue(void *future, NPPVariable aVariable, michael@0: void *aValue, NPError* error) michael@0: { michael@0: PR_LOG(GetPluginLog(), PR_LOG_WARNING, ("%s Not implemented, requested variable %i", __FUNCTION__, michael@0: (int) aVariable)); michael@0: michael@0: //TODO: implement this correctly michael@0: *error = NPERR_GENERIC_ERROR; michael@0: return NS_OK; michael@0: } michael@0: michael@0: #if defined(XP_WIN) || defined(XP_MACOSX) michael@0: nsresult michael@0: PluginModuleParent::NP_GetEntryPoints(NPPluginFuncs* pFuncs, NPError* error) michael@0: { michael@0: NS_ASSERTION(pFuncs, "Null pointer!"); michael@0: michael@0: // We need to have the child process update its function table michael@0: // here by actually calling NP_GetEntryPoints since the parent's michael@0: // function table can reflect nullptr entries in the child's table. michael@0: if (!CallNP_GetEntryPoints(error)) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: else if (*error != NPERR_NO_ERROR) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: SetPluginFuncs(pFuncs); michael@0: michael@0: return NS_OK; michael@0: } michael@0: #endif michael@0: michael@0: nsresult michael@0: PluginModuleParent::NPP_New(NPMIMEType pluginType, NPP instance, michael@0: uint16_t mode, int16_t argc, char* argn[], michael@0: char* argv[], NPSavedData* saved, michael@0: NPError* error) michael@0: { michael@0: PLUGIN_LOG_DEBUG_METHOD; michael@0: michael@0: if (mShutdown) { michael@0: *error = NPERR_GENERIC_ERROR; michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: // create the instance on the other side michael@0: InfallibleTArray names; michael@0: InfallibleTArray values; michael@0: michael@0: for (int i = 0; i < argc; ++i) { michael@0: names.AppendElement(NullableString(argn[i])); michael@0: values.AppendElement(NullableString(argv[i])); michael@0: } michael@0: michael@0: PluginInstanceParent* parentInstance = michael@0: new PluginInstanceParent(this, instance, michael@0: nsDependentCString(pluginType), mNPNIface); michael@0: michael@0: if (!parentInstance->Init()) { michael@0: delete parentInstance; michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: instance->pdata = parentInstance; michael@0: michael@0: if (!CallPPluginInstanceConstructor(parentInstance, michael@0: nsDependentCString(pluginType), mode, michael@0: names, values, error)) { michael@0: // |parentInstance| is automatically deleted. michael@0: instance->pdata = nullptr; michael@0: // if IPC is down, we'll get an immediate "failed" return, but michael@0: // without *error being set. So make sure that the error michael@0: // condition is signaled to nsNPAPIPluginInstance michael@0: if (NPERR_NO_ERROR == *error) michael@0: *error = NPERR_GENERIC_ERROR; michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: if (*error != NPERR_NO_ERROR) { michael@0: NPP_Destroy(instance, 0); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: TimeoutChanged(kParentTimeoutPref, this); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: PluginModuleParent::NPP_ClearSiteData(const char* site, uint64_t flags, michael@0: uint64_t maxAge) michael@0: { michael@0: if (!mClearSiteDataSupported) michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: michael@0: NPError result; michael@0: if (!CallNPP_ClearSiteData(NullableString(site), flags, maxAge, &result)) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: switch (result) { michael@0: case NPERR_NO_ERROR: michael@0: return NS_OK; michael@0: case NPERR_TIME_RANGE_NOT_SUPPORTED: michael@0: return NS_ERROR_PLUGIN_TIME_RANGE_NOT_SUPPORTED; michael@0: case NPERR_MALFORMED_SITE: michael@0: return NS_ERROR_INVALID_ARG; michael@0: default: michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: } michael@0: michael@0: nsresult michael@0: PluginModuleParent::NPP_GetSitesWithData(InfallibleTArray& result) michael@0: { michael@0: if (!mGetSitesWithDataSupported) michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: michael@0: if (!CallNPP_GetSitesWithData(&result)) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: #if defined(XP_MACOSX) michael@0: nsresult michael@0: PluginModuleParent::IsRemoteDrawingCoreAnimation(NPP instance, bool *aDrawing) michael@0: { michael@0: PluginInstanceParent* i = InstCast(instance); michael@0: if (!i) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: return i->IsRemoteDrawingCoreAnimation(aDrawing); michael@0: } michael@0: michael@0: nsresult michael@0: PluginModuleParent::ContentsScaleFactorChanged(NPP instance, double aContentsScaleFactor) michael@0: { michael@0: PluginInstanceParent* i = InstCast(instance); michael@0: if (!i) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: return i->ContentsScaleFactorChanged(aContentsScaleFactor); michael@0: } michael@0: #endif // #if defined(XP_MACOSX) michael@0: michael@0: bool michael@0: PluginModuleParent::AnswerNPN_GetValue_WithBoolReturn(const NPNVariable& aVariable, michael@0: NPError* aError, michael@0: bool* aBoolVal) michael@0: { michael@0: NPBool boolVal = false; michael@0: *aError = mozilla::plugins::parent::_getvalue(nullptr, aVariable, &boolVal); michael@0: *aBoolVal = boolVal ? true : false; michael@0: return true; michael@0: } michael@0: michael@0: #if defined(MOZ_WIDGET_QT) michael@0: static const int kMaxtimeToProcessEvents = 30; michael@0: bool michael@0: PluginModuleParent::AnswerProcessSomeEvents() michael@0: { michael@0: PLUGIN_LOG_DEBUG(("Spinning mini nested loop ...")); michael@0: QCoreApplication::processEvents(QEventLoop::AllEvents, kMaxtimeToProcessEvents); michael@0: michael@0: PLUGIN_LOG_DEBUG(("... quitting mini nested loop")); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: #elif defined(XP_MACOSX) michael@0: bool michael@0: PluginModuleParent::AnswerProcessSomeEvents() michael@0: { michael@0: mozilla::plugins::PluginUtilsOSX::InvokeNativeEventLoop(); michael@0: return true; michael@0: } michael@0: michael@0: #elif !defined(MOZ_WIDGET_GTK) michael@0: bool michael@0: PluginModuleParent::AnswerProcessSomeEvents() michael@0: { michael@0: NS_RUNTIMEABORT("unreached"); michael@0: return false; michael@0: } michael@0: michael@0: #else michael@0: static const int kMaxChancesToProcessEvents = 20; michael@0: michael@0: bool michael@0: PluginModuleParent::AnswerProcessSomeEvents() michael@0: { michael@0: PLUGIN_LOG_DEBUG(("Spinning mini nested loop ...")); michael@0: michael@0: int i = 0; michael@0: for (; i < kMaxChancesToProcessEvents; ++i) michael@0: if (!g_main_context_iteration(nullptr, FALSE)) michael@0: break; michael@0: michael@0: PLUGIN_LOG_DEBUG(("... quitting mini nested loop; processed %i tasks", i)); michael@0: michael@0: return true; michael@0: } michael@0: #endif michael@0: michael@0: bool michael@0: PluginModuleParent::RecvProcessNativeEventsInInterruptCall() michael@0: { michael@0: PLUGIN_LOG_DEBUG(("%s", FULLFUNCTION)); michael@0: #if defined(OS_WIN) michael@0: ProcessNativeEventsInInterruptCall(); michael@0: return true; michael@0: #else michael@0: NS_NOTREACHED( michael@0: "PluginModuleParent::RecvProcessNativeEventsInInterruptCall not implemented!"); michael@0: return false; michael@0: #endif michael@0: } michael@0: michael@0: void michael@0: PluginModuleParent::ProcessRemoteNativeEventsInInterruptCall() michael@0: { michael@0: #if defined(OS_WIN) michael@0: unused << SendProcessNativeEventsInInterruptCall(); michael@0: return; michael@0: #endif michael@0: NS_NOTREACHED( michael@0: "PluginModuleParent::ProcessRemoteNativeEventsInInterruptCall not implemented!"); michael@0: } michael@0: michael@0: bool michael@0: PluginModuleParent::RecvPluginShowWindow(const uint32_t& aWindowId, const bool& aModal, michael@0: const int32_t& aX, const int32_t& aY, michael@0: const size_t& aWidth, const size_t& aHeight) michael@0: { michael@0: PLUGIN_LOG_DEBUG(("%s", FULLFUNCTION)); michael@0: #if defined(XP_MACOSX) michael@0: CGRect windowBound = ::CGRectMake(aX, aY, aWidth, aHeight); michael@0: mac_plugin_interposing::parent::OnPluginShowWindow(aWindowId, windowBound, aModal); michael@0: return true; michael@0: #else michael@0: NS_NOTREACHED( michael@0: "PluginInstanceParent::RecvPluginShowWindow not implemented!"); michael@0: return false; michael@0: #endif michael@0: } michael@0: michael@0: bool michael@0: PluginModuleParent::RecvPluginHideWindow(const uint32_t& aWindowId) michael@0: { michael@0: PLUGIN_LOG_DEBUG(("%s", FULLFUNCTION)); michael@0: #if defined(XP_MACOSX) michael@0: mac_plugin_interposing::parent::OnPluginHideWindow(aWindowId, OtherSidePID()); michael@0: return true; michael@0: #else michael@0: NS_NOTREACHED( michael@0: "PluginInstanceParent::RecvPluginHideWindow not implemented!"); michael@0: return false; michael@0: #endif michael@0: } michael@0: michael@0: PCrashReporterParent* michael@0: PluginModuleParent::AllocPCrashReporterParent(mozilla::dom::NativeThreadId* id, michael@0: uint32_t* processType) michael@0: { michael@0: #ifdef MOZ_CRASHREPORTER michael@0: return new CrashReporterParent(); michael@0: #else michael@0: return nullptr; michael@0: #endif michael@0: } michael@0: michael@0: bool michael@0: PluginModuleParent::DeallocPCrashReporterParent(PCrashReporterParent* actor) michael@0: { michael@0: #ifdef MOZ_CRASHREPORTER michael@0: #ifdef XP_WIN michael@0: mozilla::MutexAutoLock lock(mCrashReporterMutex); michael@0: if (actor == static_cast(mCrashReporter)) { michael@0: mCrashReporter = nullptr; michael@0: } michael@0: #endif michael@0: #endif michael@0: delete actor; michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: PluginModuleParent::RecvSetCursor(const NSCursorInfo& aCursorInfo) michael@0: { michael@0: PLUGIN_LOG_DEBUG(("%s", FULLFUNCTION)); michael@0: #if defined(XP_MACOSX) michael@0: mac_plugin_interposing::parent::OnSetCursor(aCursorInfo); michael@0: return true; michael@0: #else michael@0: NS_NOTREACHED( michael@0: "PluginInstanceParent::RecvSetCursor not implemented!"); michael@0: return false; michael@0: #endif michael@0: } michael@0: michael@0: bool michael@0: PluginModuleParent::RecvShowCursor(const bool& aShow) michael@0: { michael@0: PLUGIN_LOG_DEBUG(("%s", FULLFUNCTION)); michael@0: #if defined(XP_MACOSX) michael@0: mac_plugin_interposing::parent::OnShowCursor(aShow); michael@0: return true; michael@0: #else michael@0: NS_NOTREACHED( michael@0: "PluginInstanceParent::RecvShowCursor not implemented!"); michael@0: return false; michael@0: #endif michael@0: } michael@0: michael@0: bool michael@0: PluginModuleParent::RecvPushCursor(const NSCursorInfo& aCursorInfo) michael@0: { michael@0: PLUGIN_LOG_DEBUG(("%s", FULLFUNCTION)); michael@0: #if defined(XP_MACOSX) michael@0: mac_plugin_interposing::parent::OnPushCursor(aCursorInfo); michael@0: return true; michael@0: #else michael@0: NS_NOTREACHED( michael@0: "PluginInstanceParent::RecvPushCursor not implemented!"); michael@0: return false; michael@0: #endif michael@0: } michael@0: michael@0: bool michael@0: PluginModuleParent::RecvPopCursor() michael@0: { michael@0: PLUGIN_LOG_DEBUG(("%s", FULLFUNCTION)); michael@0: #if defined(XP_MACOSX) michael@0: mac_plugin_interposing::parent::OnPopCursor(); michael@0: return true; michael@0: #else michael@0: NS_NOTREACHED( michael@0: "PluginInstanceParent::RecvPopCursor not implemented!"); michael@0: return false; michael@0: #endif michael@0: } michael@0: michael@0: bool michael@0: PluginModuleParent::RecvGetNativeCursorsSupported(bool* supported) michael@0: { michael@0: PLUGIN_LOG_DEBUG(("%s", FULLFUNCTION)); michael@0: #if defined(XP_MACOSX) michael@0: *supported = michael@0: Preferences::GetBool("dom.ipc.plugins.nativeCursorSupport", false); michael@0: return true; michael@0: #else michael@0: NS_NOTREACHED( michael@0: "PluginInstanceParent::RecvGetNativeCursorSupportLevel not implemented!"); michael@0: return false; michael@0: #endif michael@0: } michael@0: michael@0: bool michael@0: PluginModuleParent::RecvNPN_SetException(PPluginScriptableObjectParent* aActor, michael@0: const nsCString& aMessage) michael@0: { michael@0: PLUGIN_LOG_DEBUG(("%s", FULLFUNCTION)); michael@0: michael@0: NPObject* aNPObj = nullptr; michael@0: if (aActor) { michael@0: aNPObj = static_cast(aActor)->GetObject(true); michael@0: if (!aNPObj) { michael@0: NS_ERROR("Failed to get object!"); michael@0: return false; michael@0: } michael@0: } michael@0: mozilla::plugins::parent::_setexception(aNPObj, NullableStringGet(aMessage)); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: PluginModuleParent::RecvNPN_ReloadPlugins(const bool& aReloadPages) michael@0: { michael@0: PLUGIN_LOG_DEBUG(("%s", FULLFUNCTION)); michael@0: michael@0: mozilla::plugins::parent::_reloadplugins(aReloadPages); michael@0: return true; michael@0: } michael@0: michael@0: #ifdef MOZ_CRASHREPORTER_INJECTOR michael@0: michael@0: // We only add the crash reporter to subprocess which have the filename michael@0: // FlashPlayerPlugin* michael@0: #define FLASH_PROCESS_PREFIX "FLASHPLAYERPLUGIN" michael@0: michael@0: static DWORD michael@0: GetFlashChildOfPID(DWORD pid, HANDLE snapshot) michael@0: { michael@0: PROCESSENTRY32 entry = { michael@0: sizeof(entry) michael@0: }; michael@0: for (BOOL ok = Process32First(snapshot, &entry); michael@0: ok; michael@0: ok = Process32Next(snapshot, &entry)) { michael@0: if (entry.th32ParentProcessID == pid) { michael@0: nsString name(entry.szExeFile); michael@0: ToUpperCase(name); michael@0: if (StringBeginsWith(name, NS_LITERAL_STRING(FLASH_PROCESS_PREFIX))) { michael@0: return entry.th32ProcessID; michael@0: } michael@0: } michael@0: } michael@0: return 0; michael@0: } michael@0: michael@0: // We only look for child processes of the Flash plugin, NPSWF* michael@0: #define FLASH_PLUGIN_PREFIX "NPSWF" michael@0: michael@0: void michael@0: PluginModuleParent::InitializeInjector() michael@0: { michael@0: if (!Preferences::GetBool("dom.ipc.plugins.flash.subprocess.crashreporter.enabled", false)) michael@0: return; michael@0: michael@0: nsCString path(Process()->GetPluginFilePath().c_str()); michael@0: ToUpperCase(path); michael@0: int32_t lastSlash = path.RFindCharInSet("\\/"); michael@0: if (kNotFound == lastSlash) michael@0: return; michael@0: michael@0: if (!StringBeginsWith(Substring(path, lastSlash + 1), michael@0: NS_LITERAL_CSTRING(FLASH_PLUGIN_PREFIX))) michael@0: return; michael@0: michael@0: HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); michael@0: if (INVALID_HANDLE_VALUE == snapshot) michael@0: return; michael@0: michael@0: DWORD pluginProcessPID = GetProcessId(Process()->GetChildProcessHandle()); michael@0: mFlashProcess1 = GetFlashChildOfPID(pluginProcessPID, snapshot); michael@0: if (mFlashProcess1) { michael@0: InjectCrashReporterIntoProcess(mFlashProcess1, this); michael@0: michael@0: mFlashProcess2 = GetFlashChildOfPID(mFlashProcess1, snapshot); michael@0: if (mFlashProcess2) { michael@0: InjectCrashReporterIntoProcess(mFlashProcess2, this); michael@0: } michael@0: } michael@0: } michael@0: michael@0: void michael@0: PluginModuleParent::OnCrash(DWORD processID) michael@0: { michael@0: if (!mShutdown) { michael@0: GetIPCChannel()->CloseWithError(); michael@0: KillProcess(OtherProcess(), 1, false); michael@0: } michael@0: } michael@0: michael@0: #endif // MOZ_CRASHREPORTER_INJECTOR michael@0: michael@0: #ifdef MOZ_ENABLE_PROFILER_SPS michael@0: class PluginProfilerObserver MOZ_FINAL : public nsIObserver, michael@0: public nsSupportsWeakReference michael@0: { michael@0: public: michael@0: NS_DECL_ISUPPORTS michael@0: NS_DECL_NSIOBSERVER michael@0: michael@0: explicit PluginProfilerObserver(PluginModuleParent* pmp) michael@0: : mPmp(pmp) michael@0: {} michael@0: michael@0: private: michael@0: PluginModuleParent* mPmp; michael@0: }; michael@0: michael@0: NS_IMPL_ISUPPORTS(PluginProfilerObserver, nsIObserver, nsISupportsWeakReference) michael@0: michael@0: NS_IMETHODIMP michael@0: PluginProfilerObserver::Observe(nsISupports *aSubject, michael@0: const char *aTopic, michael@0: const char16_t *aData) michael@0: { michael@0: nsCOMPtr pse = do_QueryInterface(aSubject); michael@0: if (pse) { michael@0: nsCString result; michael@0: bool success = mPmp->CallGeckoGetProfile(&result); michael@0: if (success && !result.IsEmpty()) { michael@0: pse->AddSubProfile(result.get()); michael@0: } michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: PluginModuleParent::InitPluginProfiling() michael@0: { michael@0: nsCOMPtr observerService = mozilla::services::GetObserverService(); michael@0: if (observerService) { michael@0: mProfilerObserver = new PluginProfilerObserver(this); michael@0: observerService->AddObserver(mProfilerObserver, "profiler-subprocess", false); michael@0: } michael@0: } michael@0: michael@0: void michael@0: PluginModuleParent::ShutdownPluginProfiling() michael@0: { michael@0: nsCOMPtr observerService = mozilla::services::GetObserverService(); michael@0: if (observerService) { michael@0: observerService->RemoveObserver(mProfilerObserver, "profiler-subprocess"); michael@0: } michael@0: } michael@0: #endif