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: #ifndef dom_plugins_PluginModuleChild_h michael@0: #define dom_plugins_PluginModuleChild_h 1 michael@0: michael@0: #include "mozilla/Attributes.h" michael@0: michael@0: #include michael@0: #include michael@0: michael@0: #include "base/basictypes.h" michael@0: michael@0: #include "prlink.h" michael@0: michael@0: #include "npapi.h" michael@0: #include "npfunctions.h" michael@0: michael@0: #include "nsAutoPtr.h" michael@0: #include "nsDataHashtable.h" michael@0: #include "nsTHashtable.h" michael@0: #include "nsHashKeys.h" michael@0: michael@0: #ifdef MOZ_WIDGET_COCOA michael@0: #include "PluginInterposeOSX.h" michael@0: #endif michael@0: michael@0: #include "mozilla/plugins/PPluginModuleChild.h" michael@0: #include "mozilla/plugins/PluginInstanceChild.h" michael@0: #include "mozilla/plugins/PluginIdentifierChild.h" michael@0: #include "mozilla/plugins/PluginMessageUtils.h" michael@0: michael@0: // NOTE: stolen from nsNPAPIPlugin.h michael@0: michael@0: #if defined(XP_WIN) michael@0: #define NS_NPAPIPLUGIN_CALLBACK(_type, _name) _type (__stdcall * _name) michael@0: #else michael@0: #define NS_NPAPIPLUGIN_CALLBACK(_type, _name) _type (* _name) michael@0: #endif michael@0: michael@0: typedef NS_NPAPIPLUGIN_CALLBACK(NPError, NP_GETENTRYPOINTS) (NPPluginFuncs* pCallbacks); michael@0: typedef NS_NPAPIPLUGIN_CALLBACK(NPError, NP_PLUGININIT) (const NPNetscapeFuncs* pCallbacks); michael@0: typedef NS_NPAPIPLUGIN_CALLBACK(NPError, NP_PLUGINUNIXINIT) (const NPNetscapeFuncs* pCallbacks, NPPluginFuncs* fCallbacks); michael@0: typedef NS_NPAPIPLUGIN_CALLBACK(NPError, NP_PLUGINSHUTDOWN) (void); michael@0: michael@0: namespace mozilla { michael@0: namespace dom { michael@0: class PCrashReporterChild; michael@0: } michael@0: michael@0: namespace plugins { michael@0: michael@0: #ifdef MOZ_WIDGET_QT michael@0: class NestedLoopTimer; michael@0: static const int kNestedLoopDetectorIntervalMs = 90; michael@0: #endif michael@0: michael@0: class PluginScriptableObjectChild; michael@0: class PluginInstanceChild; michael@0: michael@0: class PluginModuleChild : public PPluginModuleChild michael@0: { michael@0: typedef mozilla::dom::PCrashReporterChild PCrashReporterChild; michael@0: protected: michael@0: virtual mozilla::ipc::RacyInterruptPolicy michael@0: MediateInterruptRace(const Message& parent, const Message& child) MOZ_OVERRIDE michael@0: { michael@0: return MediateRace(parent, child); michael@0: } michael@0: michael@0: virtual bool ShouldContinueFromReplyTimeout() MOZ_OVERRIDE; michael@0: michael@0: // Implement the PPluginModuleChild interface michael@0: virtual bool AnswerNP_GetEntryPoints(NPError* rv) MOZ_OVERRIDE; michael@0: virtual bool AnswerNP_Initialize(const uint32_t& aFlags, NPError* rv) MOZ_OVERRIDE; michael@0: michael@0: virtual PPluginIdentifierChild* michael@0: AllocPPluginIdentifierChild(const nsCString& aString, michael@0: const int32_t& aInt, michael@0: const bool& aTemporary) MOZ_OVERRIDE; michael@0: michael@0: virtual bool michael@0: RecvPPluginIdentifierConstructor(PPluginIdentifierChild* actor, michael@0: const nsCString& aString, michael@0: const int32_t& aInt, michael@0: const bool& aTemporary) MOZ_OVERRIDE; michael@0: michael@0: virtual bool michael@0: DeallocPPluginIdentifierChild(PPluginIdentifierChild* aActor) MOZ_OVERRIDE; michael@0: michael@0: virtual PPluginInstanceChild* michael@0: AllocPPluginInstanceChild(const nsCString& aMimeType, michael@0: const uint16_t& aMode, michael@0: const InfallibleTArray& aNames, michael@0: const InfallibleTArray& aValues, michael@0: NPError* rv) MOZ_OVERRIDE; michael@0: michael@0: virtual bool michael@0: DeallocPPluginInstanceChild(PPluginInstanceChild* aActor) MOZ_OVERRIDE; michael@0: michael@0: virtual bool michael@0: AnswerPPluginInstanceConstructor(PPluginInstanceChild* aActor, michael@0: const nsCString& aMimeType, michael@0: const uint16_t& aMode, michael@0: const InfallibleTArray& aNames, michael@0: const InfallibleTArray& aValues, michael@0: NPError* rv) MOZ_OVERRIDE; michael@0: virtual bool michael@0: AnswerNP_Shutdown(NPError *rv) MOZ_OVERRIDE; michael@0: michael@0: virtual bool michael@0: AnswerOptionalFunctionsSupported(bool *aURLRedirectNotify, michael@0: bool *aClearSiteData, michael@0: bool *aGetSitesWithData) MOZ_OVERRIDE; michael@0: michael@0: virtual bool michael@0: AnswerNPP_ClearSiteData(const nsCString& aSite, michael@0: const uint64_t& aFlags, michael@0: const uint64_t& aMaxAge, michael@0: NPError* aResult) MOZ_OVERRIDE; michael@0: michael@0: virtual bool michael@0: AnswerNPP_GetSitesWithData(InfallibleTArray* aResult) MOZ_OVERRIDE; michael@0: michael@0: virtual bool michael@0: RecvSetAudioSessionData(const nsID& aId, michael@0: const nsString& aDisplayName, michael@0: const nsString& aIconPath) MOZ_OVERRIDE; michael@0: michael@0: virtual bool michael@0: RecvSetParentHangTimeout(const uint32_t& aSeconds) MOZ_OVERRIDE; michael@0: michael@0: virtual PCrashReporterChild* michael@0: AllocPCrashReporterChild(mozilla::dom::NativeThreadId* id, michael@0: uint32_t* processType) MOZ_OVERRIDE; michael@0: virtual bool michael@0: DeallocPCrashReporterChild(PCrashReporterChild* actor) MOZ_OVERRIDE; michael@0: virtual bool michael@0: AnswerPCrashReporterConstructor(PCrashReporterChild* actor, michael@0: mozilla::dom::NativeThreadId* id, michael@0: uint32_t* processType) MOZ_OVERRIDE; michael@0: michael@0: virtual void michael@0: ActorDestroy(ActorDestroyReason why) MOZ_OVERRIDE; michael@0: michael@0: MOZ_NORETURN void QuickExit(); michael@0: michael@0: virtual bool michael@0: RecvProcessNativeEventsInInterruptCall() MOZ_OVERRIDE; michael@0: michael@0: virtual bool michael@0: AnswerGeckoGetProfile(nsCString* aProfile) MOZ_OVERRIDE; michael@0: michael@0: public: michael@0: PluginModuleChild(); michael@0: virtual ~PluginModuleChild(); michael@0: michael@0: // aPluginFilename is UTF8, not native-charset! michael@0: bool Init(const std::string& aPluginFilename, michael@0: base::ProcessHandle aParentProcessHandle, michael@0: MessageLoop* aIOLoop, michael@0: IPC::Channel* aChannel); michael@0: michael@0: void CleanUp(); michael@0: michael@0: const char* GetUserAgent(); michael@0: michael@0: static const NPNetscapeFuncs sBrowserFuncs; michael@0: michael@0: static PluginModuleChild* current(); michael@0: michael@0: bool RegisterActorForNPObject(NPObject* aObject, michael@0: PluginScriptableObjectChild* aActor); michael@0: michael@0: void UnregisterActorForNPObject(NPObject* aObject); michael@0: michael@0: PluginScriptableObjectChild* GetActorForNPObject(NPObject* aObject); michael@0: michael@0: #ifdef DEBUG michael@0: bool NPObjectIsRegistered(NPObject* aObject); michael@0: #endif michael@0: michael@0: bool AsyncDrawingAllowed() { return mAsyncDrawingAllowed; } michael@0: michael@0: /** michael@0: * The child implementation of NPN_CreateObject. michael@0: */ michael@0: static NPObject* NPN_CreateObject(NPP aNPP, NPClass* aClass); michael@0: /** michael@0: * The child implementation of NPN_RetainObject. michael@0: */ michael@0: static NPObject* NPN_RetainObject(NPObject* aNPObj); michael@0: /** michael@0: * The child implementation of NPN_ReleaseObject. michael@0: */ michael@0: static void NPN_ReleaseObject(NPObject* aNPObj); michael@0: michael@0: /** michael@0: * The child implementations of NPIdentifier-related functions. michael@0: */ michael@0: static NPIdentifier NPN_GetStringIdentifier(const NPUTF8* aName); michael@0: static void NPN_GetStringIdentifiers(const NPUTF8** aNames, michael@0: int32_t aNameCount, michael@0: NPIdentifier* aIdentifiers); michael@0: static NPIdentifier NPN_GetIntIdentifier(int32_t aIntId); michael@0: static bool NPN_IdentifierIsString(NPIdentifier aIdentifier); michael@0: static NPUTF8* NPN_UTF8FromIdentifier(NPIdentifier aIdentifier); michael@0: static int32_t NPN_IntFromIdentifier(NPIdentifier aIdentifier); michael@0: michael@0: #ifdef MOZ_WIDGET_COCOA michael@0: void ProcessNativeEvents(); michael@0: michael@0: void PluginShowWindow(uint32_t window_id, bool modal, CGRect r) { michael@0: SendPluginShowWindow(window_id, modal, r.origin.x, r.origin.y, r.size.width, r.size.height); michael@0: } michael@0: michael@0: void PluginHideWindow(uint32_t window_id) { michael@0: SendPluginHideWindow(window_id); michael@0: } michael@0: michael@0: void SetCursor(NSCursorInfo& cursorInfo) { michael@0: SendSetCursor(cursorInfo); michael@0: } michael@0: michael@0: void ShowCursor(bool show) { michael@0: SendShowCursor(show); michael@0: } michael@0: michael@0: void PushCursor(NSCursorInfo& cursorInfo) { michael@0: SendPushCursor(cursorInfo); michael@0: } michael@0: michael@0: void PopCursor() { michael@0: SendPopCursor(); michael@0: } michael@0: michael@0: bool GetNativeCursorsSupported() { michael@0: bool supported = false; michael@0: SendGetNativeCursorsSupported(&supported); michael@0: return supported; michael@0: } michael@0: #endif michael@0: michael@0: // Quirks mode support for various plugin mime types michael@0: enum PluginQuirks { michael@0: QUIRKS_NOT_INITIALIZED = 0, michael@0: // Silverlight assumes it is transparent in windowless mode. This quirk michael@0: // matches the logic in nsNPAPIPluginInstance::SetWindowless. michael@0: QUIRK_SILVERLIGHT_DEFAULT_TRANSPARENT = 1 << 0, michael@0: // Win32: Hook TrackPopupMenu api so that we can swap out parent michael@0: // hwnds. The api will fail with parents not associated with our michael@0: // child ui thread. See WinlessHandleEvent for details. michael@0: QUIRK_WINLESS_TRACKPOPUP_HOOK = 1 << 1, michael@0: // Win32: Throttle flash WM_USER+1 heart beat messages to prevent michael@0: // flooding chromium's dispatch loop, which can cause ipc traffic michael@0: // processing lag. michael@0: QUIRK_FLASH_THROTTLE_WMUSER_EVENTS = 1 << 2, michael@0: // Win32: Catch resets on our subclass by hooking SetWindowLong. michael@0: QUIRK_FLASH_HOOK_SETLONGPTR = 1 << 3, michael@0: // X11: Work around a bug in Flash up to 10.1 d51 at least, where michael@0: // expose event top left coordinates within the plugin-rect and michael@0: // not at the drawable origin are misinterpreted. michael@0: QUIRK_FLASH_EXPOSE_COORD_TRANSLATION = 1 << 4, michael@0: // Win32: Catch get window info calls on the browser and tweak the michael@0: // results so mouse input works when flash is displaying it's settings michael@0: // window. michael@0: QUIRK_FLASH_HOOK_GETWINDOWINFO = 1 << 5, michael@0: // Win: Addresses a flash bug with mouse capture and full screen michael@0: // windows. michael@0: QUIRK_FLASH_FIXUP_MOUSE_CAPTURE = 1 << 6, michael@0: // Win: QuickTime steals focus on SetWindow calls even if it's hidden. michael@0: // Avoid calling SetWindow in that case. michael@0: QUIRK_QUICKTIME_AVOID_SETWINDOW = 1 << 7, michael@0: // Win: Check to make sure the parent window has focus before calling michael@0: // set focus on the child. Addresses a full screen dialog prompt michael@0: // problem in Silverlight. michael@0: QUIRK_SILVERLIGHT_FOCUS_CHECK_PARENT = 1 << 8, michael@0: // Mac: Allow the plugin to use offline renderer mode. michael@0: // Use this only if the plugin is certified the support the offline renderer. michael@0: QUIRK_ALLOW_OFFLINE_RENDERER = 1 << 9, michael@0: // Mac: Work around a Flash bug that can cause plugin process crashes michael@0: // in CoreGraphics mode: The Flash plugin sometimes accesses the michael@0: // CGContextRef we pass to it in NPP_HandleEvent(NPCocoaEventDrawRect) michael@0: // outside of that call. See bug 804606. michael@0: QUIRK_FLASH_AVOID_CGMODE_CRASHES = 1 << 10, michael@0: }; michael@0: michael@0: int GetQuirks() { return mQuirks; } michael@0: michael@0: private: michael@0: void AddQuirk(PluginQuirks quirk) { michael@0: if (mQuirks == QUIRKS_NOT_INITIALIZED) michael@0: mQuirks = 0; michael@0: mQuirks |= quirk; michael@0: } michael@0: void InitQuirksModes(const nsCString& aMimeType); michael@0: bool InitGraphics(); michael@0: void DeinitGraphics(); michael@0: #if defined(MOZ_WIDGET_GTK) michael@0: static gboolean DetectNestedEventLoop(gpointer data); michael@0: static gboolean ProcessBrowserEvents(gpointer data); michael@0: michael@0: virtual void EnteredCxxStack() MOZ_OVERRIDE; michael@0: virtual void ExitedCxxStack() MOZ_OVERRIDE; michael@0: #elif defined(MOZ_WIDGET_QT) michael@0: michael@0: virtual void EnteredCxxStack() MOZ_OVERRIDE; michael@0: virtual void ExitedCxxStack() MOZ_OVERRIDE; michael@0: #endif michael@0: michael@0: PRLibrary* mLibrary; michael@0: nsCString mPluginFilename; // UTF8 michael@0: nsCString mUserAgent; michael@0: int mQuirks; michael@0: bool mAsyncDrawingAllowed; michael@0: michael@0: // we get this from the plugin michael@0: NP_PLUGINSHUTDOWN mShutdownFunc; michael@0: #if defined(OS_LINUX) || defined(OS_BSD) michael@0: NP_PLUGINUNIXINIT mInitializeFunc; michael@0: #elif defined(OS_WIN) || defined(OS_MACOSX) michael@0: NP_PLUGININIT mInitializeFunc; michael@0: NP_GETENTRYPOINTS mGetEntryPointsFunc; michael@0: #endif michael@0: michael@0: NPPluginFuncs mFunctions; michael@0: NPSavedData mSavedData; michael@0: michael@0: #if defined(MOZ_WIDGET_GTK) michael@0: // If a plugin spins a nested glib event loop in response to a michael@0: // synchronous IPC message from the browser, the loop might break michael@0: // only after the browser responds to a request sent by the michael@0: // plugin. This can happen if a plugin uses gtk's synchronous michael@0: // copy/paste, for example. But because the browser is blocked on michael@0: // a condvar, it can't respond to the request. This situation michael@0: // isn't technically a deadlock, but the symptoms are basically michael@0: // the same from the user's perspective. michael@0: // michael@0: // We take two steps to prevent this michael@0: // michael@0: // (1) Detect nested event loops spun by the plugin. This is michael@0: // done by scheduling a glib timer event in the plugin michael@0: // process whenever the browser might block on the plugin. michael@0: // If the plugin indeed spins a nested loop, this timer event michael@0: // will fire "soon" thereafter. michael@0: // michael@0: // (2) When a nested loop is detected, deschedule the michael@0: // nested-loop-detection timer and in its place, schedule michael@0: // another timer that periodically calls back into the michael@0: // browser and spins a mini event loop. This mini event loop michael@0: // processes a handful of pending native events. michael@0: // michael@0: // Because only timer (1) or (2) (or neither) may be active at any michael@0: // point in time, we use the same member variable michael@0: // |mNestedLoopTimerId| to refer to both. michael@0: // michael@0: // When the browser no longer might be blocked on a plugin's IPC michael@0: // response, we deschedule whichever of (1) or (2) is active. michael@0: guint mNestedLoopTimerId; michael@0: # ifdef DEBUG michael@0: // Depth of the stack of calls to g_main_context_dispatch before any michael@0: // nested loops are run. This is 1 when IPC calls are dispatched from michael@0: // g_main_context_iteration, or 0 when dispatched directly from michael@0: // MessagePumpForUI. michael@0: int mTopLoopDepth; michael@0: # endif michael@0: #elif defined (MOZ_WIDGET_QT) michael@0: NestedLoopTimer *mNestedLoopTimerObject; michael@0: #endif michael@0: michael@0: struct NPObjectData : public nsPtrHashKey michael@0: { michael@0: NPObjectData(const NPObject* key) michael@0: : nsPtrHashKey(key) michael@0: , instance(nullptr) michael@0: , actor(nullptr) michael@0: { } michael@0: michael@0: // never nullptr michael@0: PluginInstanceChild* instance; michael@0: michael@0: // sometimes nullptr (no actor associated with an NPObject) michael@0: PluginScriptableObjectChild* actor; michael@0: }; michael@0: /** michael@0: * mObjectMap contains all the currently active NPObjects (from NPN_CreateObject until the michael@0: * final release/dealloc, whether or not an actor is currently associated with the object. michael@0: */ michael@0: nsTHashtable mObjectMap; michael@0: michael@0: friend class PluginIdentifierChild; michael@0: friend class PluginIdentifierChildString; michael@0: friend class PluginIdentifierChildInt; michael@0: nsDataHashtable mStringIdentifiers; michael@0: nsDataHashtable mIntIdentifiers; michael@0: michael@0: public: // called by PluginInstanceChild michael@0: /** michael@0: * Dealloc an NPObject after last-release or when the associated instance michael@0: * is destroyed. This function will remove the object from mObjectMap. michael@0: */ michael@0: static void DeallocNPObject(NPObject* o); michael@0: michael@0: NPError NPP_Destroy(PluginInstanceChild* instance) { michael@0: return mFunctions.destroy(instance->GetNPP(), 0); michael@0: } michael@0: michael@0: /** michael@0: * Fill PluginInstanceChild.mDeletingHash with all the remaining NPObjects michael@0: * associated with that instance. michael@0: */ michael@0: void FindNPObjectsForInstance(PluginInstanceChild* instance); michael@0: michael@0: private: michael@0: static PLDHashOperator CollectForInstance(NPObjectData* d, void* userArg); michael@0: michael@0: #if defined(OS_WIN) michael@0: virtual void EnteredCall() MOZ_OVERRIDE; michael@0: virtual void ExitedCall() MOZ_OVERRIDE; michael@0: michael@0: // Entered/ExitedCall notifications keep track of whether the plugin has michael@0: // entered a nested event loop within this interrupt call. michael@0: struct IncallFrame michael@0: { michael@0: IncallFrame() michael@0: : _spinning(false) michael@0: , _savedNestableTasksAllowed(false) michael@0: { } michael@0: michael@0: bool _spinning; michael@0: bool _savedNestableTasksAllowed; michael@0: }; michael@0: michael@0: nsAutoTArray mIncallPumpingStack; michael@0: michael@0: static LRESULT CALLBACK NestedInputEventHook(int code, michael@0: WPARAM wParam, michael@0: LPARAM lParam); michael@0: static LRESULT CALLBACK CallWindowProcHook(int code, michael@0: WPARAM wParam, michael@0: LPARAM lParam); michael@0: void SetEventHooks(); michael@0: void ResetEventHooks(); michael@0: HHOOK mNestedEventHook; michael@0: HHOOK mGlobalCallWndProcHook; michael@0: #endif michael@0: }; michael@0: michael@0: } /* namespace plugins */ michael@0: } /* namespace mozilla */ michael@0: michael@0: #endif // ifndef dom_plugins_PluginModuleChild_h