Bug 829557 - Part 1: When calling into plugin code, identify situations where it is safe (or unsafe) to reenter Gecko from plugin code. r=bsmedberg, a=akeybl
authorRobert O'Callahan <robert@ocallahan.org>
Thu, 28 Feb 2013 00:50:27 +1300
changeset 132282 ffaefaf5e1ea02ce12d4069349a754dcc075bb93
parent 132281 c0633e54e6ff3a8c7459e7a6a8eb298c634a0b60
child 132283 23cb9b70ca1c431c6705fc37a674d7b296d35278
push id2323
push userbbajaj@mozilla.com
push dateMon, 01 Apr 2013 19:47:02 +0000
treeherdermozilla-beta@7712be144d91 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbsmedberg, akeybl
bugs829557
milestone21.0a2
Bug 829557 - Part 1: When calling into plugin code, identify situations where it is safe (or unsafe) to reenter Gecko from plugin code. r=bsmedberg, a=akeybl When Gecko is reentered from unsafe call sites, we allow the reentry, but we suppress execution of the refresh driver to minimize the danger. In this patch, we treat all sites as unsafe.
dom/plugins/base/Makefile.in
dom/plugins/base/nsNPAPIPlugin.cpp
dom/plugins/base/nsNPAPIPluginInstance.cpp
dom/plugins/base/nsNPAPIPluginInstance.h
dom/plugins/base/nsNPAPIPluginStreamListener.cpp
dom/plugins/base/nsPluginNativeWindowOS2.cpp
dom/plugins/base/nsPluginSafety.h
layout/base/nsRefreshDriver.cpp
layout/generic/nsObjectFrame.cpp
--- a/dom/plugins/base/Makefile.in
+++ b/dom/plugins/base/Makefile.in
@@ -36,16 +36,17 @@ EXPORTS = \
   npfunctions.h \
   nptypes.h \
   npruntime.h \
   nsPluginLogging.h \
   nsPluginNativeWindow.h \
   nsPluginsCID.h \
   nsNPAPIPluginInstance.h \
   nsPluginsDir.h \
+  nsPluginSafety.h \
   nsPluginTags.h \
   nsPluginDirServiceProvider.h \
   nsPluginHost.h \
   nsPluginInstanceOwner.h \
   nsPluginPlayPreviewInfo.h \
   $(NULL)
 
 EXPORTS_mozilla = \
--- a/dom/plugins/base/nsNPAPIPlugin.cpp
+++ b/dom/plugins/base/nsNPAPIPlugin.cpp
@@ -181,29 +181,29 @@ static PRCList sPendingAsyncCalls = PR_I
 // POST/GET stream type
 enum eNPPStreamTypeInternal {
   eNPPStreamTypeInternal_Get,
   eNPPStreamTypeInternal_Post
 };
 
 static NS_DEFINE_IID(kMemoryCID, NS_MEMORY_CID);
 
-PRIntervalTime NS_NotifyBeginPluginCall()
+PRIntervalTime NS_NotifyBeginPluginCall(NSPluginCallReentry aReentryState)
 {
-  nsNPAPIPluginInstance::BeginPluginCall();
+  nsNPAPIPluginInstance::BeginPluginCall(aReentryState);
   return PR_IntervalNow();
 }
 
 // This function sends a notification using the observer service to any object
 // registered to listen to the "experimental-notify-plugin-call" subject.
 // Each "experimental-notify-plugin-call" notification carries with it the run
 // time value in milliseconds that the call took to execute.
-void NS_NotifyPluginCall(PRIntervalTime startTime) 
+void NS_NotifyPluginCall(PRIntervalTime startTime, NSPluginCallReentry aReentryState)
 {
-  nsNPAPIPluginInstance::EndPluginCall();
+  nsNPAPIPluginInstance::EndPluginCall(aReentryState);
 
   PRIntervalTime endTime = PR_IntervalNow() - startTime;
   nsCOMPtr<nsIObserverService> notifyUIService =
     mozilla::services::GetObserverService();
   if (!notifyUIService)
     return;
 
   float runTimeInSeconds = float(endTime) / PR_TicksPerSecond();
@@ -784,17 +784,18 @@ nsPluginThreadRunnable::~nsPluginThreadR
 }
 
 NS_IMETHODIMP
 nsPluginThreadRunnable::Run()
 {
   if (mFunc) {
     PluginDestructionGuard guard(mInstance);
 
-    NS_TRY_SAFE_CALL_VOID(mFunc(mUserData), nullptr);
+    NS_TRY_SAFE_CALL_VOID(mFunc(mUserData), nullptr,
+                          NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO);
   }
 
   return NS_OK;
 }
 
 void
 OnPluginDestroy(NPP instance)
 {
--- a/dom/plugins/base/nsNPAPIPluginInstance.cpp
+++ b/dom/plugins/base/nsNPAPIPluginInstance.cpp
@@ -206,17 +206,17 @@ nsNPAPIPluginInstance::~nsNPAPIPluginIns
 #endif
 
   if (mMIMEType) {
     PR_Free((void *)mMIMEType);
     mMIMEType = nullptr;
   }
 }
 
-uint32_t nsNPAPIPluginInstance::gInPluginCalls = 0;
+uint32_t nsNPAPIPluginInstance::gInUnsafePluginCalls = 0;
 
 void
 nsNPAPIPluginInstance::Destroy()
 {
   Stop();
   mPlugin = nullptr;
 
 #if MOZ_WIDGET_ANDROID
@@ -310,17 +310,18 @@ nsresult nsNPAPIPluginInstance::Stop()
     return NS_ERROR_FAILURE;
 
   NPPluginFuncs* pluginFunctions = mPlugin->PluginFuncs();
 
   NPError error = NPERR_GENERIC_ERROR;
   if (pluginFunctions->destroy) {
     NPSavedData *sdata = 0;
 
-    NS_TRY_SAFE_CALL_RETURN(error, (*pluginFunctions->destroy)(&mNPP, &sdata), this);
+    NS_TRY_SAFE_CALL_RETURN(error, (*pluginFunctions->destroy)(&mNPP, &sdata), this,
+                            NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO);
 
     NPP_PLUGIN_LOG(PLUGIN_LOG_NORMAL,
                    ("NPP Destroy called: this=%p, npp=%p, return=%d\n", this, &mNPP, error));
   }
   mRunning = DESTROYED;
 
 #if MOZ_WIDGET_ANDROID
   for (uint32_t i = 0; i < mPostedEvents.Length(); i++) {
@@ -575,17 +576,18 @@ nsresult nsNPAPIPluginInstance::SetWindo
     PLUGIN_LOG(PLUGIN_LOG_NORMAL, ("nsNPAPIPluginInstance::SetWindow (about to call it) this=%p\n",this));
 
     bool oldVal = mInPluginInitCall;
     mInPluginInitCall = true;
 
     NPPAutoPusher nppPusher(&mNPP);
 
     DebugOnly<NPError> error;
-    NS_TRY_SAFE_CALL_RETURN(error, (*pluginFunctions->setwindow)(&mNPP, (NPWindow*)window), this);
+    NS_TRY_SAFE_CALL_RETURN(error, (*pluginFunctions->setwindow)(&mNPP, (NPWindow*)window), this,
+                            NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO);
 
     mInPluginInitCall = oldVal;
 
     NPP_PLUGIN_LOG(PLUGIN_LOG_NORMAL,
     ("NPP SetWindow called: this=%p, [x=%d,y=%d,w=%d,h=%d], clip[t=%d,b=%d,l=%d,r=%d], return=%d\n",
     this, window->x, window->y, window->width, window->height,
     window->clipRect.top, window->clipRect.bottom, window->clipRect.left, window->clipRect.right, (NPError)error));
   }
@@ -643,17 +645,18 @@ nsresult nsNPAPIPluginInstance::Print(NP
       void** destination = (void **)&(thePrint->print.embedPrint.window.type);
       *destination = source;
     } else {
       NS_ERROR("Incompatible OS for assignment");
     }
   }
 
   if (pluginFunctions->print)
-      NS_TRY_SAFE_CALL_VOID((*pluginFunctions->print)(&mNPP, thePrint), this);
+      NS_TRY_SAFE_CALL_VOID((*pluginFunctions->print)(&mNPP, thePrint), this,
+                            NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO);
 
   NPP_PLUGIN_LOG(PLUGIN_LOG_NORMAL,
   ("NPP PrintProc called: this=%p, pDC=%p, [x=%d,y=%d,w=%d,h=%d], clip[t=%d,b=%d,l=%d,r=%d]\n",
   this,
   platformPrint->print.embedPrint.platformPrint,
   platformPrint->print.embedPrint.window.x,
   platformPrint->print.embedPrint.window.y,
   platformPrint->print.embedPrint.window.width,
@@ -681,17 +684,18 @@ nsresult nsNPAPIPluginInstance::HandleEv
 
   NPPluginFuncs* pluginFunctions = mPlugin->PluginFuncs();
 
   int16_t tmpResult = kNPEventNotHandled;
 
   if (pluginFunctions->event) {
     mCurrentPluginEvent = event;
 #if defined(XP_WIN) || defined(XP_OS2)
-    NS_TRY_SAFE_CALL_RETURN(tmpResult, (*pluginFunctions->event)(&mNPP, event), this);
+    NS_TRY_SAFE_CALL_RETURN(tmpResult, (*pluginFunctions->event)(&mNPP, event), this,
+                            NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO);
 #else
     MAIN_THREAD_JNI_REF_GUARD;
     tmpResult = (*pluginFunctions->event)(&mNPP, event);
 #endif
     NPP_PLUGIN_LOG(PLUGIN_LOG_NOISY,
       ("NPP HandleEvent called: this=%p, npp=%p, event=%p, return=%d\n", 
       this, &mNPP, event, tmpResult));
 
@@ -711,17 +715,18 @@ nsresult nsNPAPIPluginInstance::GetValue
   NPPluginFuncs* pluginFunctions = mPlugin->PluginFuncs();
 
   nsresult rv = NS_ERROR_FAILURE;
 
   if (pluginFunctions->getvalue && RUNNING == mRunning) {
     PluginDestructionGuard guard(this);
 
     NPError pluginError = NPERR_GENERIC_ERROR;
-    NS_TRY_SAFE_CALL_RETURN(pluginError, (*pluginFunctions->getvalue)(&mNPP, variable, value), this);
+    NS_TRY_SAFE_CALL_RETURN(pluginError, (*pluginFunctions->getvalue)(&mNPP, variable, value), this,
+                            NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO);
     NPP_PLUGIN_LOG(PLUGIN_LOG_NORMAL,
     ("NPP GetValue called: this=%p, npp=%p, var=%d, value=%d, return=%d\n", 
     this, &mNPP, variable, value, pluginError));
 
     if (pluginError == NPERR_NO_ERROR) {
       rv = NS_OK;
     }
   }
@@ -1397,17 +1402,18 @@ nsNPAPIPluginInstance::PrivateModeStateC
 
   if (!pluginFunctions->setvalue)
     return NS_ERROR_FAILURE;
 
   PluginDestructionGuard guard(this);
 
   NPError error;
   NPBool value = static_cast<NPBool>(enabled);
-  NS_TRY_SAFE_CALL_RETURN(error, (*pluginFunctions->setvalue)(&mNPP, NPNVprivateModeBool, &value), this);
+  NS_TRY_SAFE_CALL_RETURN(error, (*pluginFunctions->setvalue)(&mNPP, NPNVprivateModeBool, &value), this,
+                          NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO);
   return (error == NPERR_NO_ERROR) ? NS_OK : NS_ERROR_FAILURE;
 }
 
 static void
 PluginTimerCallback(nsITimer *aTimer, void *aClosure)
 {
   nsNPAPITimer* t = (nsNPAPITimer*)aClosure;
   NPP npp = t->npp;
--- a/dom/plugins/base/nsNPAPIPluginInstance.h
+++ b/dom/plugins/base/nsNPAPIPluginInstance.h
@@ -45,16 +45,28 @@ const NPDrawingModel kDefaultDrawingMode
 const NPDrawingModel kDefaultDrawingModel = NPDrawingModelQuickDraw; // Not supported
 #else
 const NPDrawingModel kDefaultDrawingModel = NPDrawingModelCoreGraphics;
 #endif
 #else
 const NPDrawingModel kDefaultDrawingModel = static_cast<NPDrawingModel>(0);
 #endif
 
+/**
+ * Used to indicate whether it's OK to reenter Gecko and repaint, flush frames,
+ * run scripts, etc, during this plugin call.
+ * When NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO is set, we try to avoid dangerous
+ * Gecko activities when the plugin spins a nested event loop, on a best-effort
+ * basis.
+ */
+enum NSPluginCallReentry {
+  NS_PLUGIN_CALL_SAFE_TO_REENTER_GECKO,
+  NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO
+};
+
 class nsNPAPITimer
 {
 public:
   NPP npp;
   uint32_t id;
   nsCOMPtr<nsITimer> timer;
   void (*callback)(NPP npp, uint32_t timerID);
   bool inCallback;
@@ -268,22 +280,29 @@ public:
 
   // Called when the instance fails to instantiate beceause the Carbon
   // event model is not supported.
   void CarbonNPAPIFailure();
 
   // Returns the contents scale factor of the screen the plugin is drawn on.
   double GetContentsScaleFactor();
 
-  static bool InPluginCall() { return gInPluginCalls > 0; }
-  static void BeginPluginCall() { ++gInPluginCalls; }
-  static void EndPluginCall()
+  static bool InPluginCallUnsafeForReentry() { return gInUnsafePluginCalls > 0; }
+  static void BeginPluginCall(NSPluginCallReentry aReentryState)
   {
-    NS_ASSERTION(InPluginCall(), "Must be in plugin call");
-    --gInPluginCalls;
+    if (aReentryState == NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO) {
+      ++gInUnsafePluginCalls;
+    }
+  }
+  static void EndPluginCall(NSPluginCallReentry aReentryState)
+  {
+    if (aReentryState == NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO) {
+      NS_ASSERTION(gInUnsafePluginCalls > 0, "Must be in plugin call");
+      --gInUnsafePluginCalls;
+    }
   }
 
 protected:
 
   nsresult GetTagType(nsPluginTagType *result);
   nsresult GetAttributes(uint16_t& n, const char*const*& names,
                          const char*const*& values);
   nsresult GetParameters(uint16_t& n, const char*const*& names,
@@ -371,12 +390,12 @@ private:
   bool mOnScreen;
 
   nsIntSize mCurrentSize;
 #endif
 
   // is this instance Java and affected by bug 750480?
   bool mHaveJavaC2PJSObjectQuirk;
 
-  static uint32_t gInPluginCalls;
+  static uint32_t gInUnsafePluginCalls;
 };
 
 #endif // nsNPAPIPluginInstance_h_
--- a/dom/plugins/base/nsNPAPIPluginStreamListener.cpp
+++ b/dom/plugins/base/nsNPAPIPluginStreamListener.cpp
@@ -230,17 +230,18 @@ nsNPAPIPluginStreamListener::CleanUpStre
 
   NPP npp;
   mInst->GetNPP(&npp);
 
   if (mStreamStarted && pluginFunctions->destroystream) {
     NPPAutoPusher nppPusher(npp);
 
     NPError error;
-    NS_TRY_SAFE_CALL_RETURN(error, (*pluginFunctions->destroystream)(npp, &mNPStreamWrapper->mNPStream, reason), mInst);
+    NS_TRY_SAFE_CALL_RETURN(error, (*pluginFunctions->destroystream)(npp, &mNPStreamWrapper->mNPStream, reason), mInst,
+                            NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO);
     
     NPP_PLUGIN_LOG(PLUGIN_LOG_NORMAL,
                    ("NPP DestroyStream called: this=%p, npp=%p, reason=%d, return=%d, url=%s\n",
                     this, npp, reason, error, mNPStreamWrapper->mNPStream.url));
     
     if (error == NPERR_NO_ERROR)
       rv = NS_OK;
   }
@@ -268,17 +269,18 @@ nsNPAPIPluginStreamListener::CallURLNoti
     return;
 
   NPPluginFuncs* pluginFunctions = plugin->PluginFuncs();
 
   if (pluginFunctions->urlnotify) {
     NPP npp;
     mInst->GetNPP(&npp);
     
-    NS_TRY_SAFE_CALL_VOID((*pluginFunctions->urlnotify)(npp, mNotifyURL, reason, mNPStreamWrapper->mNPStream.notifyData), mInst);
+    NS_TRY_SAFE_CALL_VOID((*pluginFunctions->urlnotify)(npp, mNotifyURL, reason, mNPStreamWrapper->mNPStream.notifyData), mInst,
+                          NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO);
     
     NPP_PLUGIN_LOG(PLUGIN_LOG_NORMAL,
                    ("NPP URLNotify called: this=%p, npp=%p, notify=%p, reason=%d, url=%s\n",
                     this, npp, mNPStreamWrapper->mNPStream.notifyData, reason, mNotifyURL));
   }
 }
 
 nsresult
@@ -316,17 +318,18 @@ nsNPAPIPluginStreamListener::OnStartBind
     mResponseHeaderBuf = PL_strdup(mResponseHeaders.get());
     mNPStreamWrapper->mNPStream.headers = mResponseHeaderBuf;
   }
   
   mStreamListenerPeer = streamPeer;
   
   NPPAutoPusher nppPusher(npp);
   
-  NS_TRY_SAFE_CALL_RETURN(error, (*pluginFunctions->newstream)(npp, (char*)contentType, &mNPStreamWrapper->mNPStream, seekable, &streamType), mInst);
+  NS_TRY_SAFE_CALL_RETURN(error, (*pluginFunctions->newstream)(npp, (char*)contentType, &mNPStreamWrapper->mNPStream, seekable, &streamType), mInst,
+                          NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO);
   
   NPP_PLUGIN_LOG(PLUGIN_LOG_NORMAL,
                  ("NPP NewStream called: this=%p, npp=%p, mime=%s, seek=%d, type=%d, return=%d, url=%s\n",
                   this, npp, (char *)contentType, seekable, streamType, error, mNPStreamWrapper->mNPStream.url));
   
   if (error != NPERR_NO_ERROR)
     return NS_ERROR_FAILURE;
   
@@ -558,17 +561,18 @@ nsNPAPIPluginStreamListener::OnDataAvail
     // mStreamBufferByteCount tells us how many bytes there are in the
     // buffer. WriteReady returns to us how many bytes the plugin is
     // ready to handle.
     while (mStreamBufferByteCount > 0) {
       int32_t numtowrite;
       if (pluginFunctions->writeready) {
         NPPAutoPusher nppPusher(npp);
         
-        NS_TRY_SAFE_CALL_RETURN(numtowrite, (*pluginFunctions->writeready)(npp, &mNPStreamWrapper->mNPStream), mInst);
+        NS_TRY_SAFE_CALL_RETURN(numtowrite, (*pluginFunctions->writeready)(npp, &mNPStreamWrapper->mNPStream), mInst,
+                                NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO);
         NPP_PLUGIN_LOG(PLUGIN_LOG_NOISY,
                        ("NPP WriteReady called: this=%p, npp=%p, "
                         "return(towrite)=%d, url=%s\n",
                         this, npp, numtowrite, mNPStreamWrapper->mNPStream.url));
         
         if (!mStreamStarted) {
           // The plugin called NPN_DestroyStream() from within
           // NPP_WriteReady(), kill the stream.
@@ -607,17 +611,18 @@ nsNPAPIPluginStreamListener::OnDataAvail
         // if WriteReady is not supported by the plugin, just write
         // the whole buffer
         numtowrite = mStreamBufferByteCount;
       }
       
       NPPAutoPusher nppPusher(npp);
       
       int32_t writeCount = 0; // bytes consumed by plugin instance
-      NS_TRY_SAFE_CALL_RETURN(writeCount, (*pluginFunctions->write)(npp, &mNPStreamWrapper->mNPStream, streamPosition, numtowrite, ptrStreamBuffer), mInst);
+      NS_TRY_SAFE_CALL_RETURN(writeCount, (*pluginFunctions->write)(npp, &mNPStreamWrapper->mNPStream, streamPosition, numtowrite, ptrStreamBuffer), mInst,
+                              NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO);
       
       NPP_PLUGIN_LOG(PLUGIN_LOG_NOISY,
                      ("NPP Write called: this=%p, npp=%p, pos=%d, len=%d, "
                       "buf=%s, return(written)=%d,  url=%s\n",
                       this, npp, streamPosition, numtowrite,
                       ptrStreamBuffer, writeCount, mNPStreamWrapper->mNPStream.url));
       
       if (!mStreamStarted) {
@@ -715,17 +720,18 @@ nsNPAPIPluginStreamListener::OnFileAvail
   NPPluginFuncs* pluginFunctions = plugin->PluginFuncs();
 
   if (!pluginFunctions->asfile)
     return NS_ERROR_FAILURE;
 
   NPP npp;
   mInst->GetNPP(&npp);
   
-  NS_TRY_SAFE_CALL_VOID((*pluginFunctions->asfile)(npp, &mNPStreamWrapper->mNPStream, fileName), mInst);
+  NS_TRY_SAFE_CALL_VOID((*pluginFunctions->asfile)(npp, &mNPStreamWrapper->mNPStream, fileName), mInst,
+                        NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO);
   
   NPP_PLUGIN_LOG(PLUGIN_LOG_NORMAL,
                  ("NPP StreamAsFile called: this=%p, npp=%p, url=%s, file=%s\n",
                   this, npp, mNPStreamWrapper->mNPStream.url, fileName));
   
   return NS_OK;
 }
 
@@ -857,17 +863,18 @@ nsNPAPIPluginStreamListener::HandleRedir
         if (NS_SUCCEEDED(uri->GetAsciiSpec(spec))) {
           // At this point the plugin will be responsible for making the callback
           // so save the callback object.
           mHTTPRedirectCallback = callback;
 
           NPP npp;
           mInst->GetNPP(&npp);
 #if defined(XP_WIN) || defined(XP_OS2)
-          NS_TRY_SAFE_CALL_VOID((*pluginFunctions->urlredirectnotify)(npp, spec.get(), static_cast<int32_t>(status), mNPStreamWrapper->mNPStream.notifyData), mInst);
+          NS_TRY_SAFE_CALL_VOID((*pluginFunctions->urlredirectnotify)(npp, spec.get(), static_cast<int32_t>(status), mNPStreamWrapper->mNPStream.notifyData), mInst,
+                                NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO);
 #else
           MAIN_THREAD_JNI_REF_GUARD;
           (*pluginFunctions->urlredirectnotify)(npp, spec.get(), static_cast<int32_t>(status), mNPStreamWrapper->mNPStream.notifyData);
 #endif
           return true;
         }
       }
     }
--- a/dom/plugins/base/nsPluginNativeWindowOS2.cpp
+++ b/dom/plugins/base/nsPluginNativeWindowOS2.cpp
@@ -168,17 +168,18 @@ static bool ProcessFlashMessageDelayed(n
                                          MPARAM mp1, MPARAM mp2)
 {
   NS_ENSURE_TRUE(aWin, NS_ERROR_NULL_POINTER);
   NS_ENSURE_TRUE(aInst, NS_ERROR_NULL_POINTER);
 
   if (msg == sWM_FLASHBOUNCEMSG) {
     // See PluginWindowEvent::Run() below.
     NS_TRY_SAFE_CALL_VOID((aWin->GetWindowProc())(hWnd, WM_USER_FLASH, mp1, mp2),
-                           inst);
+                           inst,
+                           NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO);
     return TRUE;
   }
 
   if (msg != WM_USER_FLASH)
     return false; // no need to delay
 
   // do stuff
   nsCOMPtr<nsIRunnable> pwe = aWin->GetPluginWindowEvent(hWnd, msg, mp1, mp2);
@@ -304,19 +305,21 @@ static MRESULT EXPENTRY PluginWndProc(HW
     uint16_t apiVersion;
     if (NS_SUCCEEDED(inst->GetPluginAPIVersion(&apiVersion)) &&
         !versionOK(apiVersion, NP_POPUP_API_VERSION))
       inst->PushPopupsEnabledState(true);
   }
 
   MRESULT res = (MRESULT)TRUE;
   if (win->mPluginType == nsPluginType_Java_vm)
-    NS_TRY_SAFE_CALL_RETURN(res, ::WinDefWindowProc(hWnd, msg, mp1, mp2), inst);
+    NS_TRY_SAFE_CALL_RETURN(res, ::WinDefWindowProc(hWnd, msg, mp1, mp2), inst,
+                            NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO);
   else
-    NS_TRY_SAFE_CALL_RETURN(res, (win->GetWindowProc())(hWnd, msg, mp1, mp2), inst);
+    NS_TRY_SAFE_CALL_RETURN(res, (win->GetWindowProc())(hWnd, msg, mp1, mp2), inst,
+                            NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO);
 
   if (inst) {
     // Popups are enabled (were enabled before the call to
     // CallWindowProc()). Some plugins (at least the flash player)
     // post messages from their key handlers etc that delay the actual
     // processing, so we need to delay the disabling of popups so that
     // popups remain enabled when the flash player ends up processing
     // the actual key handlers. We do this by posting an event that
@@ -392,17 +395,18 @@ NS_IMETHODIMP PluginWindowEvent::Run()
     // XXX Unwind issues related to runnable event callback depth for this
     // event and destruction of the plugin. (Bug 493601)
     ::WinPostMsg(hWnd, sWM_FLASHBOUNCEMSG, GetWParam(), GetLParam());
   else
     // Currently not used, but added so that processing events here
     // is more generic.
     NS_TRY_SAFE_CALL_VOID((win->GetWindowProc()) 
                           (hWnd, GetMsg(), GetWParam(), GetLParam()),
-                          inst);
+                          inst,
+                          NS_PLUGIN_CALL_UNSAFE_TO_REENTER_GECKO);
 
   Clear();
   return NS_OK;
 }
 
 PluginWindowEvent*
 nsPluginNativeWindowOS2::GetPluginWindowEvent(HWND aWnd, ULONG aMsg, MPARAM aMp1, MPARAM aMp2)
 {
--- a/dom/plugins/base/nsPluginSafety.h
+++ b/dom/plugins/base/nsPluginSafety.h
@@ -3,38 +3,40 @@
  * 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 nsPluginSafety_h_
 #define nsPluginSafety_h_
 
 #include <prinrval.h>
 
+enum NSPluginCallReentry;
+
 // On Android, we need to guard against plugin code leaking entries in the local
 // JNI ref table. See https://bugzilla.mozilla.org/show_bug.cgi?id=780831#c21
 #ifdef MOZ_WIDGET_ANDROID
   #include "AndroidBridge.h"
 
   #define MAIN_THREAD_JNI_REF_GUARD mozilla::AutoLocalJNIFrame jniFrame
 #else
   #define MAIN_THREAD_JNI_REF_GUARD
 #endif
 
-PRIntervalTime NS_NotifyBeginPluginCall();
-void NS_NotifyPluginCall(PRIntervalTime);
+PRIntervalTime NS_NotifyBeginPluginCall(NSPluginCallReentry aReentryState);
+void NS_NotifyPluginCall(PRIntervalTime aTime, NSPluginCallReentry aReentryState);
 
-#define NS_TRY_SAFE_CALL_RETURN(ret, fun, pluginInst) \
+#define NS_TRY_SAFE_CALL_RETURN(ret, fun, pluginInst, pluginCallReentry) \
 PR_BEGIN_MACRO                                     \
   MAIN_THREAD_JNI_REF_GUARD;                       \
-  PRIntervalTime startTime = NS_NotifyBeginPluginCall(); \
+  PRIntervalTime startTime = NS_NotifyBeginPluginCall(pluginCallReentry); \
   ret = fun;                                       \
-  NS_NotifyPluginCall(startTime);		               \
+  NS_NotifyPluginCall(startTime, pluginCallReentry); \
 PR_END_MACRO
 
-#define NS_TRY_SAFE_CALL_VOID(fun, pluginInst)     \
+#define NS_TRY_SAFE_CALL_VOID(fun, pluginInst, pluginCallReentry) \
 PR_BEGIN_MACRO                                     \
   MAIN_THREAD_JNI_REF_GUARD;                       \
-  PRIntervalTime startTime = NS_NotifyBeginPluginCall(); \
+  PRIntervalTime startTime = NS_NotifyBeginPluginCall(pluginCallReentry); \
   fun;                                             \
-  NS_NotifyPluginCall(startTime);		               \
+  NS_NotifyPluginCall(startTime, pluginCallReentry); \
 PR_END_MACRO
 
 #endif //nsPluginSafety_h_
--- a/layout/base/nsRefreshDriver.cpp
+++ b/layout/base/nsRefreshDriver.cpp
@@ -810,17 +810,17 @@ nsRefreshDriver::DoTick()
 }
 
 void
 nsRefreshDriver::Tick(int64_t aNowEpoch, TimeStamp aNowTime)
 {
   NS_PRECONDITION(!nsContentUtils::GetCurrentJSContext(),
                   "Shouldn't have a JSContext on the stack");
 
-  if (nsNPAPIPluginInstance::InPluginCall()) {
+  if (nsNPAPIPluginInstance::InPluginCallUnsafeForReentry()) {
     NS_ERROR("Refresh driver should not run during plugin call!");
     // Try to survive this by just ignoring the refresh tick.
     return;
   }
 
   SAMPLE_LABEL("nsRefreshDriver", "Tick");
 
   // We're either frozen or we were disconnected (likely in the middle
--- a/layout/generic/nsObjectFrame.cpp
+++ b/layout/generic/nsObjectFrame.cpp
@@ -2066,30 +2066,34 @@ nsObjectFrame::HandleEvent(nsPresContext
     mInstanceOwner->ContentsScaleFactorChanged(scaleFactor);
     return NS_OK;
   }
 #endif
 
   if (mInstanceOwner->SendNativeEvents() &&
       NS_IS_PLUGIN_EVENT(anEvent)) {
     *anEventStatus = mInstanceOwner->ProcessEvent(*anEvent);
+    // Due to plugin code reentering Gecko, this frame may be dead at this
+    // point.
     return rv;
   }
 
 #ifdef XP_WIN
   rv = nsObjectFrameSuper::HandleEvent(aPresContext, anEvent, anEventStatus);
   return rv;
 #endif
 
 #ifdef XP_MACOSX
   // we want to process some native mouse events in the cocoa event model
   if ((anEvent->message == NS_MOUSE_ENTER ||
        anEvent->message == NS_WHEEL_WHEEL) &&
       mInstanceOwner->GetEventModel() == NPEventModelCocoa) {
     *anEventStatus = mInstanceOwner->ProcessEvent(*anEvent);
+    // Due to plugin code reentering Gecko, this frame may be dead at this
+    // point.
     return rv;
   }
 #endif
 
   return nsObjectFrameSuper::HandleEvent(aPresContext, anEvent, anEventStatus);
 }
 
 #ifdef XP_MACOSX