Bug 851378 - Move removing the plugin prototype from objects to content. r=bsmedberg a=bbajaj
authorJohn Schoenick <jschoenick@mozilla.com>
Thu, 14 Mar 2013 18:23:31 -0700
changeset 132481 f7975b92291a5364818a499ba950dabda13105e4
parent 132480 2b921224a702dde3f4c824cad5c145ea5dd12979
child 132482 6d8e9b790e81781601334047f9064c1eb52374ec
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, bbajaj
bugs851378
milestone21.0a2
Bug 851378 - Move removing the plugin prototype from objects to content. r=bsmedberg a=bbajaj
content/base/src/nsObjectLoadingContent.cpp
content/base/src/nsObjectLoadingContent.h
dom/plugins/base/Makefile.in
dom/plugins/base/nsJSNPRuntime.cpp
dom/plugins/base/nsJSNPRuntime.h
--- a/content/base/src/nsObjectLoadingContent.cpp
+++ b/content/base/src/nsObjectLoadingContent.cpp
@@ -19,16 +19,18 @@
 #include "nsIDOMDataContainerEvent.h"
 #include "nsIDOMDocument.h"
 #include "nsIDOMEventTarget.h"
 #include "nsIExternalProtocolHandler.h"
 #include "nsEventStates.h"
 #include "nsIObjectFrame.h"
 #include "nsIPermissionManager.h"
 #include "nsPluginHost.h"
+#include "nsJSNPRuntime.h"
+#include "nsIJSContextStack.h"
 #include "nsIPresShell.h"
 #include "nsIScriptGlobalObject.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsIStreamConverterService.h"
 #include "nsIURILoader.h"
 #include "nsIURL.h"
 #include "nsIWebNavigation.h"
 #include "nsIWebNavigationInfo.h"
@@ -2263,16 +2265,17 @@ nsObjectLoadingContent::PluginCrashed(ns
                                       const nsAString& pluginDumpID,
                                       const nsAString& browserDumpID,
                                       bool submittedCrashReport)
 {
   LOG(("OBJLC [%p]: Plugin Crashed, queuing crash event", this));
   NS_ASSERTION(mType == eType_Plugin, "PluginCrashed at non-plugin type");
 
   // Instance is dead, clean up
+  TeardownProtoChain();
   mInstanceOwner = nullptr;
   CloseChannel();
 
   // Switch to fallback/crashed state, notify
   LoadFallback(eFallbackCrashed, true);
 
   // send nsPluginCrashedEvent
   nsCOMPtr<nsIContent> thisContent =
@@ -2497,16 +2500,17 @@ nsObjectLoadingContent::DoStopPlugin(nsP
     aInstanceOwner->HidePluginWindow();
 #endif
 
     nsRefPtr<nsPluginHost> pluginHost =
       already_AddRefed<nsPluginHost>(nsPluginHost::GetInst());
     NS_ASSERTION(pluginHost, "No plugin host?");
     pluginHost->StopPluginInstance(inst);
   }
+  TeardownProtoChain();
 
   aInstanceOwner->Destroy();
   mIsStopping = false;
 }
 
 NS_IMETHODIMP
 nsObjectLoadingContent::StopPluginInstance()
 {
@@ -2740,8 +2744,66 @@ nsObjectLoadingContent::ShouldPlay(Fallb
       !mPlayPreviewCanceled && !ignoreCTP) {
     // play preview in click-to-play mode is shown instead of standard CTP UI
     aReason = eFallbackPlayPreview;
   }
 
   return allowPerm;
 }
 
+void
+nsObjectLoadingContent::TeardownProtoChain()
+{
+  nsCOMPtr<nsIContent> thisContent =
+    do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
+  nsCOMPtr<nsIXPConnect> xpc(do_GetService(nsIXPConnect::GetCID()));
+  NS_ENSURE_TRUE(xpc, /* void */);
+
+  // Use the safe JSContext here as we're not always able to find the
+  // JSContext associated with the NPP any more.
+  JSContext *cx = nsContentUtils::GetSafeJSContext();
+  nsIDocument* doc = thisContent->OwnerDoc();
+  NS_ENSURE_TRUE(doc, /* void */);
+  nsIScriptGlobalObject* sgo = doc->GetScriptGlobalObject();
+  NS_ENSURE_TRUE(sgo, /* void */);
+
+  nsCOMPtr<nsIXPConnectWrappedNative> holder;
+  xpc->GetWrappedNativeOfNativeObject(cx, sgo->GetGlobalJSObject(), thisContent,
+                                      NS_GET_IID(nsISupports),
+                                      getter_AddRefs(holder));
+  NS_ENSURE_TRUE(holder, /* void */);
+
+  JSObject *obj;
+  holder->GetJSObject(&obj);
+  NS_ENSURE_TRUE(obj, /* void */);
+
+  JSObject *proto;
+  JSAutoRequest ar(cx);
+  JSAutoCompartment ac(cx, obj);
+
+  // Loop over the DOM element's JS object prototype chain and remove
+  // all JS objects of the class sNPObjectJSWrapperClass
+  bool removed = false;
+  while (obj) {
+    if (!::JS_GetPrototype(cx, obj, &proto)) {
+      return;
+    }
+    if (!proto) {
+      break;
+    }
+    // Unwrap while checking the jsclass - if the prototype is a wrapper for
+    // an NP object, that counts too.
+    if (JS_GetClass(js::UnwrapObject(proto)) == &sNPObjectJSWrapperClass) {
+      // We found an NPObject on the proto chain, get its prototype...
+      if (!::JS_GetPrototype(cx, proto, &proto)) {
+        return;
+      }
+
+      MOZ_ASSERT(!removed, "more than one NPObject in prototype chain");
+      removed = true;
+
+      // ... and pull it out of the chain.
+      ::JS_SetPrototype(cx, obj, proto);
+    }
+
+    obj = proto;
+  }
+}
--- a/content/base/src/nsObjectLoadingContent.h
+++ b/content/base/src/nsObjectLoadingContent.h
@@ -132,16 +132,19 @@ class nsObjectLoadingContent : public ns
     void NotifyOwnerDocumentActivityChanged();
 
     /**
      * Used by pluginHost to know if we're loading with a channel, so it
      * will not open its own.
      */
     bool SrcStreamLoading() { return mSrcStreamLoading; }
 
+    // Remove plugin from protochain
+    void TeardownProtoChain();
+
   protected:
     /**
      * Begins loading the object when called
      *
      * Attributes of |this| QI'd to nsIContent will be inspected, depending on
      * the node type. This function currently assumes it is a <applet>,
      * <object>, or <embed> tag.
      *
--- a/dom/plugins/base/Makefile.in
+++ b/dom/plugins/base/Makefile.in
@@ -40,16 +40,17 @@ EXPORTS = \
   nsPluginNativeWindow.h \
   nsPluginsCID.h \
   nsNPAPIPluginInstance.h \
   nsPluginsDir.h \
   nsPluginSafety.h \
   nsPluginTags.h \
   nsPluginDirServiceProvider.h \
   nsPluginHost.h \
+  nsJSNPRuntime.h \
   nsPluginInstanceOwner.h \
   nsPluginPlayPreviewInfo.h \
   $(NULL)
 
 EXPORTS_mozilla = \
   PluginPRLibrary.h \
   $(NULL)
 
--- a/dom/plugins/base/nsJSNPRuntime.cpp
+++ b/dom/plugins/base/nsJSNPRuntime.cpp
@@ -142,17 +142,17 @@ NPObjWrapper_Call(JSContext *cx, unsigne
 
 static JSBool
 NPObjWrapper_Construct(JSContext *cx, unsigned argc, jsval *vp);
 
 static JSBool
 CreateNPObjectMember(NPP npp, JSContext *cx, JSObject *obj, NPObject *npobj,
                      jsid id, NPVariant* getPropertyResult, jsval *vp);
 
-static JSClass sNPObjectJSWrapperClass =
+JSClass sNPObjectJSWrapperClass =
   {
     NPRUNTIME_JSCLASS_NAME,
     JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS | JSCLASS_NEW_RESOLVE | JSCLASS_NEW_ENUMERATE,
     NPObjWrapper_AddProperty,
     NPObjWrapper_DelProperty,
     NPObjWrapper_GetProperty,
     NPObjWrapper_SetProperty,
     (JSEnumerateOp)NPObjWrapper_newEnumerate,
@@ -1982,101 +1982,16 @@ nsJSNPRuntime::OnPluginDestroy(NPP npp)
 
   JSAutoRequest ar(cx);
 
   if (sNPObjWrappers.ops) {
     NppAndCx nppcx = { npp, cx };
     PL_DHashTableEnumerate(&sNPObjWrappers,
                            NPObjWrapperPluginDestroyedCallback, &nppcx);
   }
-
-  // If this plugin was scripted from a webpage, the plugin's
-  // scriptable object will be on the DOM element's prototype
-  // chain. Now that the plugin is being destroyed we need to pull the
-  // plugin's scriptable object out of that prototype chain.
-  if (!npp) {
-    return;
-  }
-
-  // Find the plugin instance so that we can (eventually) get to the
-  // DOM element
-  nsNPAPIPluginInstance *inst = (nsNPAPIPluginInstance *)npp->ndata;
-  if (!inst)
-    return;
-
-  nsCOMPtr<nsIDOMElement> element;
-  inst->GetDOMElement(getter_AddRefs(element));
-  if (!element)
-    return;
-
-  // Get the DOM element's JS object.
-  nsCOMPtr<nsIXPConnect> xpc(do_GetService(nsIXPConnect::GetCID()));
-  if (!xpc)
-    return;
-
-  // OK.  Now we have to get our hands on the right scope object, since
-  // GetWrappedNativeOfNativeObject doesn't call PreCreate and hence won't get
-  // the right scope if we pass in something bogus.  The right scope lives on
-  // the script global of the element's document.
-  // XXXbz we MUST have a better way of doing this... perhaps
-  // GetWrappedNativeOfNativeObject _should_ call preCreate?
-  nsCOMPtr<nsIContent> content(do_QueryInterface(element));
-  if (!content) {
-    return;
-  }
-
-  nsIDocument* doc = content->OwnerDoc();
-
-  nsIScriptGlobalObject* sgo = doc->GetScriptGlobalObject();
-  if (!sgo) {
-    return;
-  }
-
-  nsCOMPtr<nsIXPConnectWrappedNative> holder;
-  xpc->GetWrappedNativeOfNativeObject(cx, sgo->GetGlobalJSObject(), content,
-                                      NS_GET_IID(nsISupports),
-                                      getter_AddRefs(holder));
-  if (!holder) {
-    return;
-  }
-
-  JSObject *obj, *proto;
-  holder->GetJSObject(&obj);
-
-  Maybe<JSAutoCompartment> ac;
-  if (obj) {
-    ac.construct(cx, obj);
-  }
-
-  // Loop over the DOM element's JS object prototype chain and remove
-  // all JS objects of the class sNPObjectJSWrapperClass (there should
-  // be only one, but remove all instances found in case the page put
-  // more than one of the plugin's scriptable objects on the prototype
-  // chain).
-  while (obj) {
-    if (!::JS_GetPrototype(cx, obj, &proto)) {
-      return;
-    }
-    if (!proto) {
-      break;
-    }
-    // Unwrap while checking the jsclass - if the prototype is a wrapper for
-    // an NP object, that counts too.
-    if (JS_GetClass(js::UnwrapObject(proto)) == &sNPObjectJSWrapperClass) {
-      // We found an NPObject on the proto chain, get its prototype...
-      if (!::JS_GetPrototype(cx, proto, &proto)) {
-        return;
-      }
-
-      // ... and pull it out of the chain.
-      ::JS_SetPrototype(cx, obj, proto);
-    }
-
-    obj = proto;
-  }
 }
 
 
 // Find the NPP for a NPObject.
 static NPP
 LookupNPP(NPObject *npobj)
 {
   if (npobj->_class == &nsJSObjWrapper::sJSObjWrapperNPClass) {
--- a/dom/plugins/base/nsJSNPRuntime.h
+++ b/dom/plugins/base/nsJSNPRuntime.h
@@ -26,16 +26,18 @@ public:
   {
   }
 
   JSObject *mJSObj;
 
   const NPP mNpp;
 };
 
+extern JSClass sNPObjectJSWrapperClass;
+
 class nsJSObjWrapper : public NPObject,
                        public nsJSObjWrapperKey
 {
 public:
   static NPObject *GetNewOrUsed(NPP npp, JSContext *cx, JSObject *obj);
 
 protected:
   nsJSObjWrapper(NPP npp);