From 1e5759435b205d5ce976fecb8d08d7523810efc5 Mon Sep 17 00:00:00 2001 From: Josh Aas Date: Tue, 15 Dec 2009 15:44:52 -0500 Subject: [PATCH] Greatly reduce the number of timers firing when multiple Carbon plugin instances are active on Mac OS X. Should reduce CPU usage. Make all instances use the same (at most two) timers for idle events. b=519598 r=roc --- dom/base/nsGlobalWindow.cpp | 6 ++ layout/generic/nsObjectFrame.cpp | 83 ++++++++----------- modules/plugin/base/public/nsIPluginHost.idl | 7 +- .../base/public/nsIPluginInstanceOwner.idl | 6 +- modules/plugin/base/src/nsPluginHost.cpp | 77 ++++++++++++++++- modules/plugin/base/src/nsPluginHost.h | 18 ++++ 6 files changed, 145 insertions(+), 52 deletions(-) diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp index e3d3f282200e4..7c225af53d6b3 100644 --- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -419,6 +419,7 @@ class nsDummyJavaPluginOwner : public nsIPluginInstanceOwner NPError ShowNativeContextMenu(NPMenu* menu, void* event); NPBool ConvertPoint(double sourceX, double sourceY, NPCoordinateSpace sourceSpace, double *destX, double *destY, NPCoordinateSpace destSpace); + void SendIdleEvent(); NS_DECL_CYCLE_COLLECTION_CLASS(nsDummyJavaPluginOwner) @@ -563,6 +564,11 @@ nsDummyJavaPluginOwner::SetEventModel(PRInt32 eventModel) return NS_ERROR_NOT_IMPLEMENTED; } +void +nsDummyJavaPluginOwner::SendIdleEvent() +{ +} + /** * An indirect observer object that means we don't have to implement nsIObserver * on nsGlobalWindow, where any script could see it. diff --git a/layout/generic/nsObjectFrame.cpp b/layout/generic/nsObjectFrame.cpp index 0891f6a682997..fbba3bfd22415 100644 --- a/layout/generic/nsObjectFrame.cpp +++ b/layout/generic/nsObjectFrame.cpp @@ -224,9 +224,9 @@ enum { XKeyPress = KeyPress }; static PRLogModuleInfo *nsObjectFrameLM = PR_NewLogModule("nsObjectFrame"); #endif /* PR_LOGGING */ -#define NORMAL_PLUGIN_DELAY 20 -// must avoid audio skipping/delays -#define HIDDEN_PLUGIN_DELAY 125 +#if defined(XP_MACOSX) && !defined(NP_NO_CARBON) +#define MAC_CARBON_PLUGINS +#endif // special class for handeling DOM context menu events because for // some reason it starves other mouse events if implemented on the @@ -257,7 +257,6 @@ class nsPluginDOMContextMenuListener : public nsIDOMContextMenuListener class nsPluginInstanceOwner : public nsIPluginInstanceOwner, public nsIPluginTagInfo, - public nsITimerCallback, public nsIDOMMouseListener, public nsIDOMMouseMotionListener, public nsIDOMKeyListener, @@ -325,11 +324,11 @@ class nsPluginInstanceOwner : public nsIPluginInstanceOwner, void Paint(const nsRect& aDirtyRect, HPS aHPS); #endif - // nsITimerCallback interface - NS_DECL_NSITIMERCALLBACK - +#ifdef MAC_CARBON_PLUGINS void CancelTimer(); - void StartTimer(unsigned int aDelay); + void StartTimer(PRBool isVisible); +#endif + void SendIdleEvent(); // nsIScrollPositionListener interface NS_IMETHOD ScrollPositionWillChange(nsIScrollableView* aScrollable, nscoord aX, nscoord aY); @@ -432,7 +431,6 @@ class nsPluginInstanceOwner : public nsIPluginInstanceOwner, nsCString mDocumentBase; char *mTagText; nsCOMPtr mWidget; - nsCOMPtr mPluginTimer; nsCOMPtr mPluginHost; #ifdef XP_MACOSX @@ -452,7 +450,6 @@ class nsPluginInstanceOwner : public nsIPluginInstanceOwner, // If true, destroy the widget on destruction. Used when plugin stop // is being delayed to a safer point in time. PRPackedBool mDestroyWidget; - PRPackedBool mTimerCanceled; PRUint16 mNumCachedAttrs; PRUint16 mNumCachedParams; char **mCachedAttrParamNames; @@ -1368,7 +1365,7 @@ nsObjectFrame::PrintPlugin(nsIRenderingContext& aRenderingContext, window.clipRect.left = 0; window.clipRect.right = 0; // platform specific printing code -#if defined(XP_MACOSX) && !defined(NP_NO_CARBON) +#ifdef MAC_CARBON_PLUGINS nsSize contentSize = GetContentRect().Size(); window.x = 0; window.y = 0; @@ -1932,7 +1929,9 @@ nsObjectFrame::HandleEvent(nsPresContext* aPresContext, #endif if (anEvent->message == NS_DESTROY) { +#ifdef MAC_CARBON_PLUGINS mInstanceOwner->CancelTimer(); +#endif return rv; } @@ -2432,7 +2431,6 @@ nsPluginInstanceOwner::nsPluginInstanceOwner() mCachedAttrParamNames = nsnull; mCachedAttrParamValues = nsnull; mDestroyWidget = PR_FALSE; - mTimerCanceled = PR_TRUE; #ifdef MOZ_COMPOSITED_PLUGINS mLastPoint = nsIntPoint(0,0); @@ -2463,8 +2461,9 @@ nsPluginInstanceOwner::~nsPluginInstanceOwner() PR_LOG(nsObjectFrameLM, PR_LOG_DEBUG, ("nsPluginInstanceOwner %p deleted\n", this)); - // shut off the timer. +#ifdef MAC_CARBON_PLUGINS CancelTimer(); +#endif mObjectFrame = nsnull; @@ -2521,7 +2520,6 @@ NS_IMPL_RELEASE(nsPluginInstanceOwner) NS_INTERFACE_MAP_BEGIN(nsPluginInstanceOwner) NS_INTERFACE_MAP_ENTRY(nsIPluginInstanceOwner) NS_INTERFACE_MAP_ENTRY(nsIPluginTagInfo) - NS_INTERFACE_MAP_ENTRY(nsITimerCallback) NS_INTERFACE_MAP_ENTRY(nsIDOMMouseListener) NS_INTERFACE_MAP_ENTRY(nsIDOMMouseMotionListener) NS_INTERFACE_MAP_ENTRY(nsIDOMKeyListener) @@ -3571,7 +3569,7 @@ nsPluginInstanceOwner::GetEventloopNestingLevel() nsresult nsPluginInstanceOwner::ScrollPositionWillChange(nsIScrollableView* aScrollable, nscoord aX, nscoord aY) { -#if defined(XP_MACOSX) && !defined(NP_NO_CARBON) +#ifdef MAC_CARBON_PLUGINS if (GetEventModel() != NPEventModelCarbon) return NS_OK; @@ -3598,7 +3596,7 @@ nsresult nsPluginInstanceOwner::ScrollPositionWillChange(nsIScrollableView* aScr nsresult nsPluginInstanceOwner::ScrollPositionDidChange(nsIScrollableView* aScrollable, nscoord aX, nscoord aY) { -#if defined(XP_MACOSX) && !defined(NP_NO_CARBON) +#ifdef MAC_CARBON_PLUGINS if (GetEventModel() != NPEventModelCarbon) return NS_OK; @@ -3617,9 +3615,9 @@ nsresult nsPluginInstanceOwner::ScrollPositionDidChange(nsIScrollableView* aScro pluginWidget->EndDrawPlugin(); } } -#endif - StartTimer(NORMAL_PLUGIN_DELAY); + StartTimer(PR_TRUE); +#endif return NS_OK; } @@ -3679,7 +3677,7 @@ nsresult nsPluginInstanceOwner::KeyUp(nsIDOMEvent* aKeyEvent) nsresult nsPluginInstanceOwner::KeyPress(nsIDOMEvent* aKeyEvent) { -#if defined(XP_MACOSX) && !defined(NP_NO_CARBON) +#ifdef MAC_CARBON_PLUGINS // send KeyPress events only for Mac OS X Carbon event model if (GetEventModel() != NPEventModelCarbon) return aKeyEvent->PreventDefault(); @@ -4634,8 +4632,10 @@ nsEventStatus nsPluginInstanceOwner::ProcessEvent(const nsGUIEvent& anEvent) nsresult nsPluginInstanceOwner::Destroy() { +#ifdef MAC_CARBON_PLUGINS // stop the timer explicitly to reduce reference count. CancelTimer(); +#endif // unregister context menu listener if (mCXMenuListener) { @@ -5361,14 +5361,9 @@ nsPluginInstanceOwner::Renderer::NativeDraw(QWidget * drawable, } #endif -// Here's how we give idle time to plugins. - -NS_IMETHODIMP nsPluginInstanceOwner::Notify(nsITimer* timer) +void nsPluginInstanceOwner::SendIdleEvent() { -#if defined(XP_MACOSX) && !defined(NP_NO_CARBON) - if (GetEventModel() != NPEventModelCarbon) - return NS_OK; - +#ifdef MAC_CARBON_PLUGINS // validate the plugin clipping information by syncing the plugin window info to // reflect the current widget location. This makes sure that everything is updated // correctly in the event of scrolling in the window. @@ -5394,36 +5389,22 @@ NS_IMETHODIMP nsPluginInstanceOwner::Notify(nsITimer* timer) } } #endif - return NS_OK; } -void nsPluginInstanceOwner::StartTimer(unsigned int aDelay) +#ifdef MAC_CARBON_PLUGINS +void nsPluginInstanceOwner::StartTimer(PRBool isVisible) { -#if defined(XP_MACOSX) && !defined(NP_NO_CARBON) if (GetEventModel() != NPEventModelCarbon) return; - if (!mTimerCanceled) - return; - - // start a periodic timer to provide null events to the plugin instance. - if (!mPluginTimer) { - mPluginTimer = do_CreateInstance("@mozilla.org/timer;1"); - } - if (mPluginTimer) { - mTimerCanceled = PR_FALSE; - mPluginTimer->InitWithCallback(this, aDelay, nsITimer::TYPE_REPEATING_SLACK); - } -#endif + mPluginHost->AddIdleTimeTarget(this, isVisible); } void nsPluginInstanceOwner::CancelTimer() { - if (mPluginTimer) { - mPluginTimer->Cancel(); - } - mTimerCanceled = PR_TRUE; + mPluginHost->RemoveIdleTimeTarget(this); } +#endif nsresult nsPluginInstanceOwner::Init(nsPresContext* aPresContext, nsObjectFrame* aFrame, @@ -5596,8 +5577,10 @@ NS_IMETHODIMP nsPluginInstanceOwner::CreateWidget(void) mPluginWindow->type = NPWindowTypeWindow; mPluginWindow->window = GetPluginPort(); +#ifdef MAC_CARBON_PLUGINS // start the idle timer. - StartTimer(NORMAL_PLUGIN_DELAY); + StartTimer(PR_TRUE); +#endif // tell the plugin window about the widget mPluginWindow->SetPluginWidget(mWidget); @@ -5638,7 +5621,7 @@ PRBool nsPluginInstanceOwner::UpdateVisibility() } #endif - // Mac specific code to fix up the port location and clipping region +// Mac specific code to fix up the port location and clipping region #ifdef XP_MACOSX void* nsPluginInstanceOwner::FixUpPluginWindow(PRInt32 inPaintState) @@ -5734,15 +5717,17 @@ void* nsPluginInstanceOwner::FixUpPluginWindow(PRInt32 inPaintState) { mInstance->SetWindow(mPluginWindow); mPluginPortChanged = PR_FALSE; +#ifdef MAC_CARBON_PLUGINS // if the clipRect is of size 0, make the null timer fire less often CancelTimer(); if (mPluginWindow->clipRect.left == mPluginWindow->clipRect.right || mPluginWindow->clipRect.top == mPluginWindow->clipRect.bottom) { - StartTimer(HIDDEN_PLUGIN_DELAY); + StartTimer(PR_FALSE); } else { - StartTimer(NORMAL_PLUGIN_DELAY); + StartTimer(PR_TRUE); } +#endif } else if (mPluginPortChanged) { mInstance->SetWindow(mPluginWindow); mPluginPortChanged = PR_FALSE; diff --git a/modules/plugin/base/public/nsIPluginHost.idl b/modules/plugin/base/public/nsIPluginHost.idl index a4c01b0e85750..cd48f7de373b8 100644 --- a/modules/plugin/base/public/nsIPluginHost.idl +++ b/modules/plugin/base/public/nsIPluginHost.idl @@ -64,7 +64,7 @@ interface nsIPluginStreamListener; [ref] native nsIStreamListenerRef(nsIStreamListener *); [ptr] native nsPluginNativeWindowPtr(nsPluginNativeWindow); -[scriptable, uuid(30C7C529-B05C-4950-B5B8-9AF673E46521)] +[scriptable, uuid(AA13B116-2AFC-4F23-8395-913C0475D173)] interface nsIPluginHost : nsISupports { [noscript] void init(); @@ -285,6 +285,11 @@ interface nsIPluginHost : nsISupports * @return plugin tag object */ [noscript] nsIPluginTag getPluginTagForInstance(in nsIPluginInstance aInstance); + +%{C++ + virtual void AddIdleTimeTarget(nsIPluginInstanceOwner* objectFrame, PRBool isVisible) = 0; + virtual void RemoveIdleTimeTarget(nsIPluginInstanceOwner* objectFrame) = 0; +%} }; %{C++ diff --git a/modules/plugin/base/public/nsIPluginInstanceOwner.idl b/modules/plugin/base/public/nsIPluginInstanceOwner.idl index ad07178bde8be..0f2cb23cf7cf0 100644 --- a/modules/plugin/base/public/nsIPluginInstanceOwner.idl +++ b/modules/plugin/base/public/nsIPluginInstanceOwner.idl @@ -49,7 +49,7 @@ class nsPluginEvent; [ref] native nsIPluginInstanceRef(nsIPluginInstance*); -[uuid(8080E717-7261-4707-B8B4-48250F47055F)] +[uuid(D8776CDC-00DF-4395-A432-2E78EBCC12B6)] interface nsIPluginInstanceOwner : nsISupports { /** @@ -137,4 +137,8 @@ interface nsIPluginInstanceOwner : nsISupports %} void setEventModel(in PRInt32 eventModel); + +%{C++ + virtual void SendIdleEvent() = 0; +%} }; diff --git a/modules/plugin/base/src/nsPluginHost.cpp b/modules/plugin/base/src/nsPluginHost.cpp index 5ec1cd3617e7d..e7ee14d579c99 100644 --- a/modules/plugin/base/src/nsPluginHost.cpp +++ b/modules/plugin/base/src/nsPluginHost.cpp @@ -2448,6 +2448,11 @@ nsPluginHost::nsPluginHost() PLUGIN_LOG(PLUGIN_LOG_ALWAYS,("nsPluginHost::ctor\n")); PR_LogFlush(); #endif + +#ifdef MAC_CARBON_PLUGINS + mVisiblePluginTimer = do_CreateInstance("@mozilla.org/timer;1"); + mHiddenPluginTimer = do_CreateInstance("@mozilla.org/timer;1"); +#endif } nsPluginHost::~nsPluginHost() @@ -2997,7 +3002,7 @@ NS_IMETHODIMP nsPluginHost::InstantiatePluginForChannel(nsIChannel* aChannel, return NewEmbeddedPluginStreamListener(uri, aOwner, nsnull, aListener); } -// Called by nsPluginInstanceOwner (nsObjectFrame.cpp - embedded case) +// Called by nsPluginInstanceOwner NS_IMETHODIMP nsPluginHost::InstantiateEmbeddedPlugin(const char *aMimeType, nsIURI* aURL, nsIPluginInstanceOwner *aOwner) @@ -5888,6 +5893,76 @@ nsresult nsPluginHost::AddUnusedLibrary(PRLibrary * aLibrary) return NS_OK; } +#ifdef MAC_CARBON_PLUGINS +// Flash requires a minimum of 8 events per second to avoid audio skipping. +// Since WebKit uses a hidden plugin event rate of 4 events per second Flash +// uses a Carbon timer for WebKit which fires at 8 events per second. +#define HIDDEN_PLUGIN_DELAY 125 +#define VISIBLE_PLUGIN_DELAY 20 +#endif + +void nsPluginHost::AddIdleTimeTarget(nsIPluginInstanceOwner* objectFrame, PRBool isVisible) +{ +#ifdef MAC_CARBON_PLUGINS + nsTObserverArray *targetArray; + if (isVisible) { + targetArray = &mVisibleTimerTargets; + } else { + targetArray = &mHiddenTimerTargets; + } + + if (targetArray->Contains(objectFrame)) { + return; + } + + targetArray->AppendElement(objectFrame); + if (targetArray->Length() == 1) { + if (isVisible) { + mVisiblePluginTimer->InitWithCallback(this, VISIBLE_PLUGIN_DELAY, nsITimer::TYPE_REPEATING_SLACK); + } else { + mHiddenPluginTimer->InitWithCallback(this, HIDDEN_PLUGIN_DELAY, nsITimer::TYPE_REPEATING_SLACK); + } + } +#endif +} + +void nsPluginHost::RemoveIdleTimeTarget(nsIPluginInstanceOwner* objectFrame) +{ +#ifdef MAC_CARBON_PLUGINS + PRBool visibleRemoved = mVisibleTimerTargets.RemoveElement(objectFrame); + if (visibleRemoved && mVisibleTimerTargets.IsEmpty()) { + mVisiblePluginTimer->Cancel(); + } + + PRBool hiddenRemoved = mHiddenTimerTargets.RemoveElement(objectFrame); + if (hiddenRemoved && mHiddenTimerTargets.IsEmpty()) { + mHiddenPluginTimer->Cancel(); + } + + NS_ASSERTION(!(hiddenRemoved && visibleRemoved), "Plugin instance received visible and hidden idle event notifications"); +#endif +} + +NS_IMETHODIMP nsPluginHost::Notify(nsITimer* timer) +{ +#ifdef MAC_CARBON_PLUGINS + if (timer == mVisiblePluginTimer) { + nsTObserverArray::ForwardIterator iter(mVisibleTimerTargets); + while (iter.HasMore()) { + iter.GetNext()->SendIdleEvent(); + } + return NS_OK; + } else if (timer == mHiddenPluginTimer) { + nsTObserverArray::ForwardIterator iter(mHiddenTimerTargets); + while (iter.HasMore()) { + iter.GetNext()->SendIdleEvent(); + } + return NS_OK; + } +#endif + return NS_ERROR_FAILURE; +} + nsresult nsPluginStreamListenerPeer::ServeStreamAsFile(nsIRequest *request, nsISupports* aContext) { diff --git a/modules/plugin/base/src/nsPluginHost.h b/modules/plugin/base/src/nsPluginHost.h index ca832eb39dbc2..2d3660a12759b 100644 --- a/modules/plugin/base/src/nsPluginHost.h +++ b/modules/plugin/base/src/nsPluginHost.h @@ -58,6 +58,8 @@ #include "nsWeakReference.h" #include "nsThreadUtils.h" #include "nsTArray.h" +#include "nsTObserverArray.h" +#include "nsITimer.h" class nsNPAPIPlugin; class nsIComponentManager; @@ -73,6 +75,10 @@ class nsPluginHost; #define NS_PLUGIN_FLAG_UNWANTED 0x0008 // this is an unwanted plugin #define NS_PLUGIN_FLAG_BLOCKLISTED 0x0010 // this is a blocklisted plugin +#if defined(XP_MACOSX) && !defined(NP_NO_CARBON) +#define MAC_CARBON_PLUGINS +#endif + // A linked-list of plugin information that is used for instantiating plugins // and reflecting plugin information into JavaScript. class nsPluginTag : public nsIPluginTag @@ -184,6 +190,7 @@ class nsPluginInstanceTagList class nsPluginHost : public nsIPluginHost, public nsIObserver, + public nsITimerCallback, public nsSupportsWeakReference { public: @@ -198,6 +205,7 @@ class nsPluginHost : public nsIPluginHost, NS_DECL_ISUPPORTS NS_DECL_NSIPLUGINHOST NS_DECL_NSIOBSERVER + NS_DECL_NSITIMERCALLBACK NS_IMETHOD GetURL(nsISupports* pluginInst, @@ -266,6 +274,9 @@ class nsPluginHost : public nsIPluginHost, static nsresult GetPrompt(nsIPluginInstanceOwner *aOwner, nsIPrompt **aPrompt); + void AddIdleTimeTarget(nsIPluginInstanceOwner* objectFrame, PRBool isVisible); + void RemoveIdleTimeTarget(nsIPluginInstanceOwner* objectFrame); + private: nsresult TrySetUpPluginInstance(const char *aMimeType, nsIURI *aURL, nsIPluginInstanceOwner *aOwner); @@ -373,6 +384,13 @@ class nsPluginHost : public nsIPluginHost, // We need to hold a global ptr to ourselves because we register for // two different CIDs for some reason... static nsPluginHost* sInst; + +#ifdef MAC_CARBON_PLUGINS + nsCOMPtr mVisiblePluginTimer; + nsTObserverArray mVisibleTimerTargets; + nsCOMPtr mHiddenPluginTimer; + nsTObserverArray mHiddenTimerTargets; +#endif }; class NS_STACK_CLASS PluginDestructionGuard : protected PRCList