/* -*- 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/. */ #ifndef mozilla_plugins_PluginModuleParent_h #define mozilla_plugins_PluginModuleParent_h #include "base/process.h" #include "mozilla/FileUtils.h" #include "mozilla/HangAnnotations.h" #include "mozilla/PluginLibrary.h" #include "mozilla/plugins/PluginProcessParent.h" #include "mozilla/plugins/PPluginModuleParent.h" #include "mozilla/plugins/PluginMessageUtils.h" #include "mozilla/plugins/PluginTypes.h" #include "mozilla/ipc/TaskFactory.h" #include "mozilla/TimeStamp.h" #include "npapi.h" #include "npfunctions.h" #include "nsDataHashtable.h" #include "nsHashKeys.h" #include "nsIObserver.h" #ifdef XP_WIN #include "nsWindowsHelpers.h" #endif class nsIProfileSaveEvent; class nsPluginTag; namespace mozilla { namespace layers { class TextureClientRecycleAllocator; } // namespace layers namespace plugins { //----------------------------------------------------------------------------- class BrowserStreamParent; class PluginAsyncSurrogate; class PluginInstanceParent; #ifdef XP_WIN class PluginHangUIParent; #endif /** * PluginModuleParent * * This class implements the NPP API from the perspective of the rest * of Gecko, forwarding NPP calls along to the child process that is * actually running the plugin. * * This class /also/ implements a version of the NPN API, because the * child process needs to make these calls back into Gecko proper. * This class is responsible for "actually" making those function calls. * * If a plugin is running, there will always be one PluginModuleParent for it in * the chrome process. In addition, any content process using the plugin will * have its own PluginModuleParent. The subclasses PluginModuleChromeParent and * PluginModuleContentParent implement functionality that is specific to one * case or the other. */ class PluginModuleParent : public PPluginModuleParent , public PluginLibrary { protected: typedef mozilla::PluginLibrary PluginLibrary; PPluginInstanceParent* AllocPPluginInstanceParent(const nsCString& aMimeType, const uint16_t& aMode, const InfallibleTArray& aNames, const InfallibleTArray& aValues) override; virtual bool DeallocPPluginInstanceParent(PPluginInstanceParent* aActor) override; public: explicit PluginModuleParent(bool aIsChrome, bool aAllowAsyncInit); virtual ~PluginModuleParent(); bool RemovePendingSurrogate(const RefPtr& aSurrogate); /** @return the state of the pref that controls async plugin init */ bool IsStartingAsync() const { return mIsStartingAsync; } /** @return whether this modules NP_Initialize has successfully completed executing */ bool IsInitialized() const { return mNPInitialized; } bool IsChrome() const { return mIsChrome; } virtual void SetPlugin(nsNPAPIPlugin* plugin) override { mPlugin = plugin; } virtual void ActorDestroy(ActorDestroyReason why) override; const NPNetscapeFuncs* GetNetscapeFuncs() { return mNPNIface; } bool OkToCleanup() const { return !IsOnCxxStack(); } void ProcessRemoteNativeEventsInInterruptCall() override; virtual bool WaitForIPCConnection() { return true; } nsCString GetHistogramKey() const { return mPluginName + mPluginVersion; } virtual nsresult GetRunID(uint32_t* aRunID) override; virtual void SetHasLocalInstance() override { mHadLocalInstance = true; } int GetQuirks() { return mQuirks; } protected: virtual mozilla::ipc::RacyInterruptPolicy MediateInterruptRace(const MessageInfo& parent, const MessageInfo& child) override { return MediateRace(parent, child); } virtual bool RecvBackUpXResources(const FileDescriptor& aXSocketFd) override; virtual bool AnswerProcessSomeEvents() override; virtual bool RecvProcessNativeEventsInInterruptCall() override; virtual bool RecvPluginShowWindow(const uint32_t& aWindowId, const bool& aModal, const int32_t& aX, const int32_t& aY, const size_t& aWidth, const size_t& aHeight) override; virtual bool RecvPluginHideWindow(const uint32_t& aWindowId) override; virtual bool RecvSetCursor(const NSCursorInfo& aCursorInfo) override; virtual bool RecvShowCursor(const bool& aShow) override; virtual bool RecvPushCursor(const NSCursorInfo& aCursorInfo) override; virtual bool RecvPopCursor() override; virtual bool RecvNPN_SetException(const nsCString& aMessage) override; virtual bool RecvNPN_ReloadPlugins(const bool& aReloadPages) override; virtual bool RecvNP_InitializeResult(const NPError& aError) override; static BrowserStreamParent* StreamCast(NPP instance, NPStream* s, PluginAsyncSurrogate** aSurrogate = nullptr); virtual bool AnswerNPN_SetValue_NPPVpluginRequiresAudioDeviceChanges( const bool& shouldRegister, NPError* result) override; protected: void SetChildTimeout(const int32_t aChildTimeout); static void TimeoutChanged(const char* aPref, void* aModule); virtual void UpdatePluginTimeout() {} virtual bool RecvNotifyContentModuleDestroyed() override { return true; } virtual bool AnswerGetKeyState(const int32_t& aVirtKey, int16_t* aRet) override; virtual bool RecvReturnClearSiteData(const NPError& aRv, const uint64_t& aCallbackId) override; virtual bool RecvReturnSitesWithData(nsTArray&& aSites, const uint64_t& aCallbackId) override; void SetPluginFuncs(NPPluginFuncs* aFuncs); nsresult NPP_NewInternal(NPMIMEType pluginType, NPP instance, uint16_t mode, InfallibleTArray& names, InfallibleTArray& values, NPSavedData* saved, NPError* error); // NPP-like API that Gecko calls are trampolined into. These // messages then get forwarded along to the plugin instance, // and then eventually the child process. static NPError NPP_Destroy(NPP instance, NPSavedData** save); static NPError NPP_SetWindow(NPP instance, NPWindow* window); static NPError NPP_NewStream(NPP instance, NPMIMEType type, NPStream* stream, NPBool seekable, uint16_t* stype); static NPError NPP_DestroyStream(NPP instance, NPStream* stream, NPReason reason); static int32_t NPP_WriteReady(NPP instance, NPStream* stream); static int32_t NPP_Write(NPP instance, NPStream* stream, int32_t offset, int32_t len, void* buffer); static void NPP_StreamAsFile(NPP instance, NPStream* stream, const char* fname); static void NPP_Print(NPP instance, NPPrint* platformPrint); static int16_t NPP_HandleEvent(NPP instance, void* event); static void NPP_URLNotify(NPP instance, const char* url, NPReason reason, void* notifyData); static NPError NPP_GetValue(NPP instance, NPPVariable variable, void *ret_value); static NPError NPP_SetValue(NPP instance, NPNVariable variable, void *value); static void NPP_URLRedirectNotify(NPP instance, const char* url, int32_t status, void* notifyData); virtual bool HasRequiredFunctions() override; virtual nsresult AsyncSetWindow(NPP aInstance, NPWindow* aWindow) override; virtual nsresult GetImageContainer(NPP aInstance, mozilla::layers::ImageContainer** aContainer) override; virtual nsresult GetImageSize(NPP aInstance, nsIntSize* aSize) override; virtual void DidComposite(NPP aInstance) override; virtual bool IsOOP() override { return true; } virtual nsresult SetBackgroundUnknown(NPP instance) override; virtual nsresult BeginUpdateBackground(NPP instance, const nsIntRect& aRect, DrawTarget** aDrawTarget) override; virtual nsresult EndUpdateBackground(NPP instance, const nsIntRect& aRect) override; #if defined(XP_WIN) virtual nsresult GetScrollCaptureContainer(NPP aInstance, mozilla::layers::ImageContainer** aContainer) override; #endif virtual nsresult HandledWindowedPluginKeyEvent( NPP aInstance, const mozilla::NativeEventData& aNativeKeyData, bool aIsConsumed) override; #if defined(XP_UNIX) && !defined(XP_MACOSX) virtual nsresult NP_Initialize(NPNetscapeFuncs* bFuncs, NPPluginFuncs* pFuncs, NPError* error) override; #else virtual nsresult NP_Initialize(NPNetscapeFuncs* bFuncs, NPError* error) override; #endif virtual nsresult NP_Shutdown(NPError* error) override; virtual nsresult NP_GetMIMEDescription(const char** mimeDesc) override; virtual nsresult NP_GetValue(void *future, NPPVariable aVariable, void *aValue, NPError* error) override; #if defined(XP_WIN) || defined(XP_MACOSX) virtual nsresult NP_GetEntryPoints(NPPluginFuncs* pFuncs, NPError* error) override; #endif virtual nsresult NPP_New(NPMIMEType pluginType, NPP instance, uint16_t mode, int16_t argc, char* argn[], char* argv[], NPSavedData* saved, NPError* error) override; virtual nsresult NPP_ClearSiteData(const char* site, uint64_t flags, uint64_t maxAge, nsCOMPtr callback) override; virtual nsresult NPP_GetSitesWithData(nsCOMPtr callback) override; private: std::map> mClearSiteDataCallbacks; std::map> mSitesWithDataCallbacks; nsCString mPluginFilename; int mQuirks; void InitQuirksModes(const nsCString& aMimeType); public: #if defined(XP_MACOSX) virtual nsresult IsRemoteDrawingCoreAnimation(NPP instance, bool *aDrawing) override; #endif #if defined(XP_MACOSX) || defined(XP_WIN) virtual nsresult ContentsScaleFactorChanged(NPP instance, double aContentsScaleFactor) override; #endif void InitAsyncSurrogates(); layers::TextureClientRecycleAllocator* EnsureTextureAllocatorForDirectBitmap(); layers::TextureClientRecycleAllocator* EnsureTextureAllocatorForDXGISurface(); protected: void NotifyFlashHang(); void NotifyPluginCrashed(); void OnInitFailure(); bool MaybeRunDeferredShutdown(); bool DoShutdown(NPError* error); bool GetSetting(NPNVariable aVariable); void GetSettings(PluginSettings* aSettings); bool mIsChrome; bool mShutdown; bool mHadLocalInstance; bool mClearSiteDataSupported; bool mGetSitesWithDataSupported; NPNetscapeFuncs* mNPNIface; NPPluginFuncs* mNPPIface; nsNPAPIPlugin* mPlugin; ipc::TaskFactory mTaskFactory; nsString mPluginDumpID; nsString mBrowserDumpID; nsString mHangID; RefPtr mProfilerObserver; nsCString mPluginName; nsCString mPluginVersion; int32_t mSandboxLevel; bool mIsFlashPlugin; #ifdef MOZ_X11 // Dup of plugin's X socket, used to scope its resources to this // object instead of the plugin process's lifetime ScopedClose mPluginXSocketFdDup; #endif bool GetPluginDetails(); friend class mozilla::plugins::PluginAsyncSurrogate; bool mIsStartingAsync; bool mNPInitialized; bool mIsNPShutdownPending; nsTArray> mSurrogateInstances; nsresult mAsyncNewRv; uint32_t mRunID; RefPtr mTextureAllocatorForDirectBitmap; RefPtr mTextureAllocatorForDXGISurface; }; class PluginModuleContentParent : public PluginModuleParent { public: explicit PluginModuleContentParent(bool aAllowAsyncInit); static PluginLibrary* LoadModule(uint32_t aPluginId, nsPluginTag* aPluginTag); static PluginModuleContentParent* Initialize(mozilla::ipc::Transport* aTransport, base::ProcessId aOtherProcess); static void OnLoadPluginResult(const uint32_t& aPluginId, const bool& aResult); static void AssociatePluginId(uint32_t aPluginId, base::ProcessId aProcessId); virtual ~PluginModuleContentParent(); #if defined(XP_WIN) || defined(XP_MACOSX) nsresult NP_Initialize(NPNetscapeFuncs* bFuncs, NPError* error) override; #endif private: virtual bool ShouldContinueFromReplyTimeout() override; virtual void OnExitedSyncSend() override; static PluginModuleContentParent* sSavedModuleParent; uint32_t mPluginId; }; class PluginModuleChromeParent : public PluginModuleParent , public mozilla::HangMonitor::Annotator { public: /** * LoadModule * * This may or may not launch a plugin child process, * and may or may not be very expensive. */ static PluginLibrary* LoadModule(const char* aFilePath, uint32_t aPluginId, nsPluginTag* aPluginTag); /** * The following two functions are called by SetupBridge to determine * whether an existing plugin module was reused, or whether a new module * was instantiated by the plugin host. */ static void ClearInstantiationFlag() { sInstantiated = false; } static bool DidInstantiate() { return sInstantiated; } virtual ~PluginModuleChromeParent(); /* * Takes a full multi-process dump including the plugin process and the * content process. If aBrowserDumpId is not empty then the browser dump * associated with it will be paired to the resulting minidump. * Takes ownership of the file associated with aBrowserDumpId. * * @param aContentPid PID of the e10s content process from which a hang was * reported. May be kInvalidProcessId if not applicable. * @param aBrowserDumpId (optional) previously taken browser dump id. If * provided TakeFullMinidump will use this dump file instead of * generating a new one. If not provided a browser dump will be taken at * the time of this call. * @param aDumpId Returns the ID of the newly generated crash dump. Left * untouched upon failure. */ void TakeFullMinidump(base::ProcessId aContentPid, const nsAString& aBrowserDumpId, nsString& aDumpId); /* * Terminates the plugin process associated with this plugin module. Also * generates appropriate crash reports unless an existing one is provided. * Takes ownership of the file associated with aDumpId on success. * * @param aMsgLoop the main message pump associated with the module * protocol. * @param aContentPid PID of the e10s content process from which a hang was * reported. May be kInvalidProcessId if not applicable. * @param aMonitorDescription a string describing the hang monitor that * is making this call. This string is added to the crash reporter * annotations for the plugin process. * @param aDumpId (optional) previously taken dump id. If provided * TerminateChildProcess will use this dump file instead of generating a * multi-process crash report. If not provided a multi-process dump will * be taken at the time of this call. */ void TerminateChildProcess(MessageLoop* aMsgLoop, base::ProcessId aContentPid, const nsCString& aMonitorDescription, const nsAString& aDumpId); #ifdef XP_WIN /** * Called by Plugin Hang UI to notify that the user has clicked continue. * Used for chrome hang annotations. */ void OnHangUIContinue(); void EvaluateHangUIState(const bool aReset); #endif // XP_WIN virtual bool WaitForIPCConnection() override; virtual bool RecvNP_InitializeResult(const NPError& aError) override; void SetContentParent(dom::ContentParent* aContentParent); bool SendAssociatePluginId(); void CachedSettingChanged(); virtual bool AnswerGetKeyState(const int32_t& aVirtKey, int16_t* aRet) override; private: virtual void EnteredCxxStack() override; void ExitedCxxStack() override; mozilla::ipc::IProtocol* GetInvokingProtocol(); PluginInstanceParent* GetManagingInstance(mozilla::ipc::IProtocol* aProtocol); virtual void AnnotateHang(mozilla::HangMonitor::HangAnnotations& aAnnotations) override; virtual bool ShouldContinueFromReplyTimeout() override; PluginProcessParent* Process() const { return mSubprocess; } base::ProcessHandle ChildProcessHandle() { return mSubprocess->GetChildProcessHandle(); } #if defined(XP_UNIX) && !defined(XP_MACOSX) virtual nsresult NP_Initialize(NPNetscapeFuncs* bFuncs, NPPluginFuncs* pFuncs, NPError* error) override; #else virtual nsresult NP_Initialize(NPNetscapeFuncs* bFuncs, NPError* error) override; #endif #if defined(XP_WIN) || defined(XP_MACOSX) virtual nsresult NP_GetEntryPoints(NPPluginFuncs* pFuncs, NPError* error) override; #endif virtual void ActorDestroy(ActorDestroyReason why) override; // aFilePath is UTF8, not native! explicit PluginModuleChromeParent(const char* aFilePath, uint32_t aPluginId, int32_t aSandboxLevel, bool aAllowAsyncInit); void CleanupFromTimeout(const bool aByHangUI); virtual void UpdatePluginTimeout() override; void RegisterSettingsCallbacks(); void UnregisterSettingsCallbacks(); virtual bool RecvNotifyContentModuleDestroyed() override; static void CachedSettingChanged(const char* aPref, void* aModule); virtual bool AnswerNPN_SetValue_NPPVpluginRequiresAudioDeviceChanges( const bool& shouldRegister, NPError* result) override; PluginProcessParent* mSubprocess; uint32_t mPluginId; ipc::TaskFactory mChromeTaskFactory; enum HangAnnotationFlags { kInPluginCall = (1u << 0), kHangUIShown = (1u << 1), kHangUIContinued = (1u << 2), kHangUIDontShow = (1u << 3) }; Atomic mHangAnnotationFlags; #ifdef XP_WIN InfallibleTArray mPluginCpuUsageOnHang; PluginHangUIParent *mHangUIParent; bool mHangUIEnabled; bool mIsTimerReset; /** * Launches the Plugin Hang UI. * * @return true if plugin-hang-ui.exe has been successfully launched. * false if the Plugin Hang UI is disabled, already showing, * or the launch failed. */ bool LaunchHangUI(); /** * Finishes the Plugin Hang UI and cancels if it is being shown to the user. */ void FinishHangUI(); #endif friend class mozilla::plugins::PluginAsyncSurrogate; void OnProcessLaunched(const bool aSucceeded); class LaunchedTask : public LaunchCompleteTask { public: explicit LaunchedTask(PluginModuleChromeParent* aModule) : mModule(aModule) { MOZ_ASSERT(aModule); } NS_IMETHOD Run() override { mModule->OnProcessLaunched(mLaunchSucceeded); return NS_OK; } private: PluginModuleChromeParent* mModule; }; friend class LaunchedTask; bool mInitOnAsyncConnect; nsresult mAsyncInitRv; NPError mAsyncInitError; // mContentParent is to be used ONLY during the IPC dance that occurs // when ContentParent::RecvLoadPlugin is called under async plugin init! // In other contexts it is *unsafe*, as there might be multiple content // processes in existence! dom::ContentParent* mContentParent; nsCOMPtr mPluginOfflineObserver; nsCString mProfile; bool mIsBlocklisted; static bool sInstantiated; }; } // namespace plugins } // namespace mozilla #endif // mozilla_plugins_PluginModuleParent_h