Backed out changeset af3eb6a8e0ae (bug 1317111)
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Wed, 16 Nov 2016 14:50:42 +0100
changeset 322710 b23408de27e4103ea5fee42b2876fe78c7d0ea3b
parent 322709 4d4607c6d4de168535594c048e3c615376734aea
child 322711 5bfd79155fff3f7bef6eee90509c7167edd4b634
push id83967
push usercbook@mozilla.com
push dateWed, 16 Nov 2016 13:51:57 +0000
treeherdermozilla-inbound@b8a81bf64c4e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1317111
milestone53.0a1
backs outaf3eb6a8e0ae5d925fda8d2f9dda039ed6d0a9ac
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Backed out changeset af3eb6a8e0ae (bug 1317111)
dom/base/nsObjectLoadingContent.cpp
dom/base/nsObjectLoadingContent.h
dom/plugins/base/nsPluginHost.cpp
dom/plugins/base/nsPluginHost.h
dom/plugins/base/nsPluginNativeWindowWin.cpp
--- a/dom/base/nsObjectLoadingContent.cpp
+++ b/dom/base/nsObjectLoadingContent.cpp
@@ -367,16 +367,88 @@ nsPluginCrashedEvent::Run()
 
   event->SetTrusted(true);
   event->WidgetEventPtr()->mFlags.mOnlyChromeDispatch = true;
 
   EventDispatcher::DispatchDOMEvent(mContent, nullptr, event, nullptr, nullptr);
   return NS_OK;
 }
 
+class nsStopPluginRunnable : public Runnable, public nsITimerCallback
+{
+public:
+  NS_DECL_ISUPPORTS_INHERITED
+
+  nsStopPluginRunnable(nsPluginInstanceOwner* aInstanceOwner,
+                       nsObjectLoadingContent* aContent)
+    : mInstanceOwner(aInstanceOwner)
+    , mContent(aContent)
+  {
+    NS_ASSERTION(aInstanceOwner, "need an owner");
+    NS_ASSERTION(aContent, "need a nsObjectLoadingContent");
+  }
+
+  // Runnable
+  NS_IMETHOD Run() override;
+
+  // nsITimerCallback
+  NS_IMETHOD Notify(nsITimer* timer) override;
+
+protected:
+  virtual ~nsStopPluginRunnable() {}
+
+private:
+  nsCOMPtr<nsITimer> mTimer;
+  RefPtr<nsPluginInstanceOwner> mInstanceOwner;
+  nsCOMPtr<nsIObjectLoadingContent> mContent;
+};
+
+NS_IMPL_ISUPPORTS_INHERITED(nsStopPluginRunnable, Runnable, nsITimerCallback)
+
+NS_IMETHODIMP
+nsStopPluginRunnable::Notify(nsITimer *aTimer)
+{
+  return Run();
+}
+
+NS_IMETHODIMP
+nsStopPluginRunnable::Run()
+{
+  // InitWithCallback calls Release before AddRef so we need to hold a
+  // strong ref on 'this' since we fall through to this scope if it fails.
+  nsCOMPtr<nsITimerCallback> kungFuDeathGrip = this;
+  nsCOMPtr<nsIAppShell> appShell = do_GetService(kAppShellCID);
+  if (appShell) {
+    uint32_t currentLevel = 0;
+    appShell->GetEventloopNestingLevel(&currentLevel);
+    if (currentLevel > mInstanceOwner->GetLastEventloopNestingLevel()) {
+      if (!mTimer)
+        mTimer = do_CreateInstance("@mozilla.org/timer;1");
+      if (mTimer) {
+        // Fire 100ms timer to try to tear down this plugin as quickly as
+        // possible once the nesting level comes back down.
+        nsresult rv = mTimer->InitWithCallback(this, 100,
+                                               nsITimer::TYPE_ONE_SHOT);
+        if (NS_SUCCEEDED(rv)) {
+          return rv;
+        }
+      }
+      NS_ERROR("Failed to setup a timer to stop the plugin later (at a safe "
+               "time). Stopping the plugin now, this might crash.");
+    }
+  }
+
+  mTimer = nullptr;
+
+  static_cast<nsObjectLoadingContent*>(mContent.get())->
+    DoStopPlugin(mInstanceOwner, false, true);
+
+  return NS_OK;
+}
+
 // You can't take the address of bitfield members, so we have two separate
 // classes for these :-/
 
 // Sets a object's mInstantiating bit to false when destroyed
 class AutoSetInstantiatingToFalse {
 public:
   explicit AutoSetInstantiatingToFalse(nsObjectLoadingContent* aContent)
     : mContent(aContent) {}
@@ -2993,16 +3065,39 @@ nsObjectLoadingContent::AsyncStartPlugin
 
 NS_IMETHODIMP
 nsObjectLoadingContent::GetSrcURI(nsIURI** aURI)
 {
   NS_IF_ADDREF(*aURI = GetSrcURI());
   return NS_OK;
 }
 
+static bool
+DoDelayedStop(nsPluginInstanceOwner* aInstanceOwner,
+              nsObjectLoadingContent* aContent,
+              bool aDelayedStop)
+{
+  // Don't delay stopping QuickTime (bug 425157), Flip4Mac (bug 426524),
+  // XStandard (bug 430219), CMISS Zinc (bug 429604).
+  if (aDelayedStop
+#if !(defined XP_WIN || defined MOZ_X11)
+      && !aInstanceOwner->MatchPluginName("QuickTime")
+      && !aInstanceOwner->MatchPluginName("Flip4Mac")
+      && !aInstanceOwner->MatchPluginName("XStandard plugin")
+      && !aInstanceOwner->MatchPluginName("CMISS Zinc Plugin")
+#endif
+      ) {
+    nsCOMPtr<nsIRunnable> evt =
+      new nsStopPluginRunnable(aInstanceOwner, aContent);
+    NS_DispatchToCurrentThread(evt);
+    return true;
+  }
+  return false;
+}
+
 void
 nsObjectLoadingContent::LoadFallback(FallbackType aType, bool aNotify) {
   EventStates oldState = ObjectState();
   ObjectType oldType = mType;
 
   NS_ASSERTION(!mInstanceOwner && !mFrameLoader && !mChannel,
                "LoadFallback called with loaded content");
 
@@ -3056,31 +3151,38 @@ nsObjectLoadingContent::LoadFallback(Fal
   if (!aNotify) {
     return; // done
   }
 
   NotifyStateChanged(oldType, oldState, false, true);
 }
 
 void
-nsObjectLoadingContent::DoStopPlugin(nsPluginInstanceOwner* aInstanceOwner)
+nsObjectLoadingContent::DoStopPlugin(nsPluginInstanceOwner* aInstanceOwner,
+                                     bool aDelayedStop,
+                                     bool aForcedReentry)
 {
   // DoStopPlugin can process events -- There may be pending
   // CheckPluginStopEvent events which can drop in underneath us and destroy the
-  // instance we are about to destroy. We prevent that with the mIsStopping
-  // flag.
-  if (mIsStopping) {
+  // instance we are about to destroy. We prevent that with the mPluginStopping
+  // flag.  (aForcedReentry is only true from the callback of an earlier delayed
+  // stop)
+  if (mIsStopping && !aForcedReentry) {
     return;
   }
   mIsStopping = true;
 
   RefPtr<nsPluginInstanceOwner> kungFuDeathGrip(aInstanceOwner);
   RefPtr<nsNPAPIPluginInstance> inst;
   aInstanceOwner->GetInstance(getter_AddRefs(inst));
   if (inst) {
+    if (DoDelayedStop(aInstanceOwner, this, aDelayedStop)) {
+      return;
+    }
+
 #if defined(XP_MACOSX)
     aInstanceOwner->HidePluginWindow();
 #endif
 
     RefPtr<nsPluginHost> pluginHost = nsPluginHost::GetInst();
     NS_ASSERTION(pluginHost, "No plugin host?");
     pluginHost->StopPluginInstance(inst);
   }
@@ -3123,21 +3225,37 @@ nsObjectLoadingContent::StopPluginInstan
     LOG(("OBJLC [%p]: StopPluginInstance - Closing used channel", this));
     CloseChannel();
   }
 
   // We detach the instance owner's frame before destruction, but don't destroy
   // the instance owner until the plugin is stopped.
   mInstanceOwner->SetFrame(nullptr);
 
+  bool delayedStop = false;
+#ifdef XP_WIN
+  // Force delayed stop for Real plugin only; see bug 420886, 426852.
+  RefPtr<nsNPAPIPluginInstance> inst;
+  mInstanceOwner->GetInstance(getter_AddRefs(inst));
+  if (inst) {
+    const char* mime = nullptr;
+    if (NS_SUCCEEDED(inst->GetMIMEType(&mime)) && mime) {
+      if (nsPluginHost::GetSpecialType(nsDependentCString(mime)) ==
+          nsPluginHost::eSpecialType_RealPlayer) {
+        delayedStop = true;
+      }
+    }
+  }
+#endif
+
   RefPtr<nsPluginInstanceOwner> ownerGrip(mInstanceOwner);
   mInstanceOwner = nullptr;
 
   // This can/will re-enter
-  DoStopPlugin(ownerGrip);
+  DoStopPlugin(ownerGrip, delayedStop);
 
   return NS_OK;
 }
 
 void
 nsObjectLoadingContent::NotifyContentObjectWrapper()
 {
   nsCOMPtr<nsIContent> thisContent =
--- a/dom/base/nsObjectLoadingContent.h
+++ b/dom/base/nsObjectLoadingContent.h
@@ -326,17 +326,18 @@ class nsObjectLoadingContent : public ns
      */
     void DestroyContent();
 
     static void Traverse(nsObjectLoadingContent *tmp,
                          nsCycleCollectionTraversalCallback &cb);
 
     void CreateStaticClone(nsObjectLoadingContent* aDest) const;
 
-    void DoStopPlugin(nsPluginInstanceOwner* aInstanceOwner);
+    void DoStopPlugin(nsPluginInstanceOwner* aInstanceOwner, bool aDelayedStop,
+                      bool aForcedReentry = false);
 
     nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent,
                         nsIContent* aBindingParent,
                         bool aCompileEventHandler);
     void UnbindFromTree(bool aDeep = true,
                         bool aNullParent = true);
 
     /**
--- a/dom/plugins/base/nsPluginHost.cpp
+++ b/dom/plugins/base/nsPluginHost.cpp
@@ -1846,16 +1846,21 @@ nsPluginHost::GetSpecialType(const nsACS
   }
 
   if (aMIMEType.LowerCaseEqualsASCII("application/x-silverlight") ||
       aMIMEType.LowerCaseEqualsASCII("application/x-silverlight-2") ||
       aMIMEType.LowerCaseEqualsASCII("application/x-silverlight-test")) {
     return eSpecialType_Silverlight;
   }
 
+  if (aMIMEType.LowerCaseEqualsASCII("audio/x-pn-realaudio-plugin")) {
+    NS_WARNING("You are loading RealPlayer");
+    return eSpecialType_RealPlayer;
+  }
+
   if (aMIMEType.LowerCaseEqualsASCII("application/vnd.unity")) {
     return eSpecialType_Unity;
   }
 
   // Java registers variants of its MIME with parameters, e.g.
   // application/x-java-vm;version=1.3
   const nsACString &noParam = Substring(aMIMEType, 0, aMIMEType.FindChar(';'));
 
--- a/dom/plugins/base/nsPluginHost.h
+++ b/dom/plugins/base/nsPluginHost.h
@@ -200,16 +200,18 @@ public:
                      // Informs some decisions about OOP and quirks
                      eSpecialType_Flash,
                      // Binds to the <applet> tag, has various special
                      // rules around opening channels, codebase, ...
                      eSpecialType_Java,
                      // Some IPC quirks
                      eSpecialType_Silverlight,
                      // Native widget quirks
+                     eSpecialType_RealPlayer,
+                     // Native widget quirks
                      eSpecialType_Unity };
   static SpecialType GetSpecialType(const nsACString & aMIMEType);
 
   static nsresult PostPluginUnloadEvent(PRLibrary* aLibrary);
 
   void PluginCrashed(nsNPAPIPlugin* plugin,
                      const nsAString& pluginDumpID,
                      const nsAString& browserDumpID);
--- a/dom/plugins/base/nsPluginNativeWindowWin.cpp
+++ b/dom/plugins/base/nsPluginNativeWindowWin.cpp
@@ -124,17 +124,19 @@ private:
   RefPtr<PluginWindowEvent> mCachedPluginWindowEvent;
 
   HWND mParentWnd;
   LONG_PTR mParentProc;
 public:
   nsPluginHost::SpecialType mPluginType;
 };
 
+static bool sInMessageDispatch = false;
 static bool sInPreviousMessageDispatch = false;
+static UINT sLastMsg = 0;
 
 static bool ProcessFlashMessageDelayed(nsPluginNativeWindowWin * aWin, nsNPAPIPluginInstance * aInst,
                                          HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
 {
   NS_ENSURE_TRUE(aWin, false);
   NS_ENSURE_TRUE(aInst, false);
 
   if (msg == sWM_FLASHBOUNCEMSG) {
@@ -194,16 +196,26 @@ static LRESULT CALLBACK PluginWndProcInt
     return TRUE;
 
   // The DispatchEvent(ePluginActivate) below can trigger a reentrant focus
   // event which might destroy us.  Hold a strong ref on the plugin instance
   // to prevent that, bug 374229.
   RefPtr<nsNPAPIPluginInstance> inst;
   win->GetPluginInstance(inst);
 
+  // Real may go into a state where it recursivly dispatches the same event
+  // when subclassed. If this is Real, lets examine the event and drop it
+  // on the floor if we get into this recursive situation. See bug 192914.
+  if (win->mPluginType == nsPluginHost::eSpecialType_RealPlayer) {
+    if (sInMessageDispatch && msg == sLastMsg)
+      return true;
+    // Cache the last message sent
+    sLastMsg = msg;
+  }
+
   bool enablePopups = false;
 
   // Activate/deactivate mouse capture on the plugin widget
   // here, before we pass the Windows event to the plugin
   // because its possible our widget won't get paired events
   // (see bug 131007) and we'll look frozen. Note that this
   // is also done in ChildWindow::DispatchMouseEvent.
   switch (msg) {
@@ -263,16 +275,20 @@ static LRESULT CALLBACK PluginWndProcInt
           widget->DispatchEvent(&event, status);
         }
       }
     }
     break;
 
     case WM_SETFOCUS:
     case WM_KILLFOCUS: {
+      // RealPlayer can crash, don't process the message for those,
+      // see bug 328675.
+      if (win->mPluginType == nsPluginHost::eSpecialType_RealPlayer && msg == sLastMsg)
+        return TRUE;
       // Make sure setfocus and killfocus get through to the widget procedure
       // even if they are eaten by the plugin. Also make sure we aren't calling
       // recursively.
       WNDPROC prevWndProc = win->GetPrevWindowProc();
       if (prevWndProc && !sInPreviousMessageDispatch) {
         sInPreviousMessageDispatch = true;
         ::CallWindowProc(prevWndProc, hWnd, msg, wParam, lParam);
         sInPreviousMessageDispatch = false;
@@ -292,25 +308,27 @@ static LRESULT CALLBACK PluginWndProcInt
   if (enablePopups && inst) {
     uint16_t apiVersion;
     if (NS_SUCCEEDED(inst->GetPluginAPIVersion(&apiVersion)) &&
         !versionOK(apiVersion, NP_POPUP_API_VERSION)) {
       inst->PushPopupsEnabledState(true);
     }
   }
 
+  sInMessageDispatch = true;
   LRESULT res;
   WNDPROC proc = (WNDPROC)win->GetWindowProc();
   if (PluginWndProc == proc) {
     NS_WARNING("Previous plugin window procedure references PluginWndProc! "
                "Report this bug!");
     res = CallWindowProc(DefWindowProc, hWnd, msg, wParam, lParam);
   } else {
     res = CallWindowProc(proc, hWnd, msg, wParam, lParam);
   }
+  sInMessageDispatch = false;
 
   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