Testcase for bug 542263 - crash at NPObjWrapper_NewResolve or various other places
☠☠ backed out by e03e9d4315d8 ☠ ☠
authorBenjamin Smedberg <benjamin@smedbergs.us>
Tue, 02 Feb 2010 17:18:41 -0800
changeset 37843 8006ad2d0c066e0ec0f6810bfedfb1a93d487e56
parent 37842 c502a1a0900af4ae6f4db3f21ecad87f7738c79b
child 37844 27c81f40bdfdb3915565cdb77bb294c521bbb8dc
child 37845 e03e9d4315d8b99f9135ea3e971c74a6eda8ef30
push id1
push userroot
push dateTue, 26 Apr 2011 22:38:44 +0000
treeherdermozilla-beta@bfdb6e623a36 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs542263
milestone1.9.3a1pre
Testcase for bug 542263 - crash at NPObjWrapper_NewResolve or various other places
build/automation.py.in
modules/plugin/test/mochitest/Makefile.in
modules/plugin/test/mochitest/test_GCrace.html
modules/plugin/test/testplugin/README
modules/plugin/test/testplugin/nptest.cpp
--- a/build/automation.py.in
+++ b/build/automation.py.in
@@ -463,20 +463,17 @@ user_pref("camino.use_system_proxy_setti
     else:
       env['MOZ_CRASHREPORTER_DISABLE'] = '1'
 
     env['GNOME_DISABLE_CRASH_DIALOG'] = '1'
     env['XRE_NO_WINDOWS_CRASH_DIALOG'] = '1'
     return env
 
   if IS_WIN32:
-    ctypes = __import__('ctypes')
-    wintypes = __import__('ctypes.wintypes')
-    time = __import__('time')
-    msvcrt = __import__('msvcrt')
+    import ctypes, ctypes.wintypes, time, msvcrt
     PeekNamedPipe = ctypes.windll.kernel32.PeekNamedPipe
     GetLastError = ctypes.windll.kernel32.GetLastError
 
     def readWithTimeout(self, f, timeout):
       """Try to read a line of output from the file object |f|.
       |f| must be a  pipe, like the |stdout| member of a subprocess.Popen
       object created with stdout=PIPE. If no output
       is received within |timeout| seconds, return a blank line.
--- a/modules/plugin/test/mochitest/Makefile.in
+++ b/modules/plugin/test/mochitest/Makefile.in
@@ -67,16 +67,17 @@ include $(topsrcdir)/config/rules.mk
 		test_pluginstream_seek.html \
 		test_pluginstream_newstream.html \
 		test_multipleinstanceobjects.html \
 		test_streamNotify.html \
 		test_instantiation.html \
 		test_cookies.html \
 		test_npn_timers.html \
 		test_npn_asynccall.html \
+		test_GCrace.html \
 		$(NULL)
 
 #		test_npruntime_npnsetexception.html \ Disabled for e10s
 
 ifdef MOZ_IPC
 _MOCHITEST_FILES += \
 		test_crashing.html \
 		test_crashing2.html \
new file mode 100644
--- /dev/null
+++ b/modules/plugin/test/mochitest/test_GCrace.html
@@ -0,0 +1,59 @@
+<head>
+  <title>GC race with actors on the parent</title>
+
+  <script type="text/javascript"
+	  src="/MochiKit/packed.js"></script>
+  <script type="text/javascript"
+	  src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css"
+	href="/tests/SimpleTest/test.css" />
+<body onload="setTimeout(checkGCRace, 1000)">
+  <p id="display"></p>
+
+  <embed id="p" type="application/x-test" wmode="window"></embed>
+
+  <script class="testbody" type="application/javascript">
+    SimpleTest.waitForExplicitFinish();
+
+    var nested = false;
+
+    function cb(f) {
+      ok(!nested, "Callback shouldn't occur in a nested stack frame");
+      try {
+        f(35);
+        ok(true, "Callback was called, no crash");
+      }
+      catch (e) {
+        ok(false, "Exception calling callback object: " + e);
+      }
+      SimpleTest.executeSoon(removePlugin);
+    }
+
+    function removePlugin() {
+      var p = document.getElementById('p');
+      p.parentNode.removeChild(p);
+      p = null;
+      netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
+      Components.utils.forceGC();
+      SimpleTest.finish();
+    }
+
+    function checkGCRace() {
+      nested = true;
+
+      // The plugin will hand back a function and immediately sleep.
+      // We will lose our only reference to the function and force GC, followed
+      // by calling us with that function object again. We should be able to
+      // call the function and not crash.
+      var p = document.getElementById('p');
+      var f = p.checkGCRace(cb);
+      f = null;  // 'f' should be collected next GC
+
+      nested = false;
+
+      setTimeout(function() {
+        netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
+        Components.utils.forceGC();
+      }, 2000);
+    }
+  </script>
--- a/modules/plugin/test/testplugin/README
+++ b/modules/plugin/test/testplugin/README
@@ -89,16 +89,20 @@ available.
 * timerTest(callback) - initiates tests of NPN_ScheduleTimer &
 NPN_UnscheduleTimer.  When finished, calls the script callback
 with a boolean value, indicating whether the tests were successful.
 
 * asyncCallbackTest(callback) - initiates tests of
 NPN_PluginThreadAsyncCall.  When finished, calls the script callback
 with a boolean value, indicating whether the tests were successful.
 
+* checkGCRace(callback) - return a function NPObject which may be called
+  with the number '35'. Immediately after returning, sleep for 5 seconds, then
+  call "callback" with the same NPObject.
+
 == Private browsing ==
 
 The test plugin object supports the following scriptable methods:
 
 * queryPrivateModeState
 Returns the value of NPN_GetValue(NPNVprivateModeBool).
 
 * lastReportedPrivateModeState
--- a/modules/plugin/test/testplugin/nptest.cpp
+++ b/modules/plugin/test/testplugin/nptest.cpp
@@ -41,16 +41,17 @@
 #include <stdio.h>
 #include <iostream>
 #include <string>
 #include <sstream>
 
 #ifdef XP_WIN
 #include <process.h>
 #include <float.h>
+#include <windows.h>
 #define getpid _getpid
 #else
 #include <unistd.h>
 #include <pthread.h>
 #endif
 
  using namespace std;
 
@@ -139,16 +140,17 @@ static bool crashPlugin(NPObject* npobj,
 static bool crashOnDestroy(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result);
 static bool getObjectValue(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result);
 static bool checkObjectValue(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result);
 static bool enableFPExceptions(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result);
 static bool setCookie(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result);
 static bool getCookie(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result);
 static bool getAuthInfo(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result);
 static bool asyncCallbackTest(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result);
+static bool checkGCRace(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result);
 
 static const NPUTF8* sPluginMethodIdentifierNames[] = {
   "npnEvaluateTest",
   "npnInvokeTest",
   "npnInvokeDefaultTest",
   "setUndefinedValueTest",
   "identifierToStringTest",
   "timerTest",
@@ -176,16 +178,17 @@ static const NPUTF8* sPluginMethodIdenti
   "crashOnDestroy",
   "getObjectValue",
   "checkObjectValue",
   "enableFPExceptions",
   "setCookie",
   "getCookie",
   "getAuthInfo",
   "asyncCallbackTest",
+  "checkGCRace",
 };
 static NPIdentifier sPluginMethodIdentifiers[ARRAY_LENGTH(sPluginMethodIdentifierNames)];
 static const ScriptableFunction sPluginMethodFunctions[ARRAY_LENGTH(sPluginMethodIdentifierNames)] = {
   npnEvaluateTest,
   npnInvokeTest,
   npnInvokeDefaultTest,
   setUndefinedValueTest,
   identifierToStringTest,
@@ -214,16 +217,17 @@ static const ScriptableFunction sPluginM
   crashOnDestroy,
   getObjectValue,
   checkObjectValue,
   enableFPExceptions,
   setCookie,
   getCookie,
   getAuthInfo,
   asyncCallbackTest,
+  checkGCRace,
 };
 
 struct URLNotifyData
 {
   const char* cookie;
   NPObject* callback;
   uint32_t size;
   char* data;
@@ -2445,8 +2449,100 @@ asyncCallbackTest(NPObject* npobj, const
   id->asyncTestScriptCallback = argstr->UTF8Characters;
   
   id->asyncTestPhase = 0;
   id->asyncCallbackResult = true;
   NPN_PluginThreadAsyncCall(npp, asyncCallback, (void*)npobj);
   
   return true;
 }
+
+static bool
+GCRaceInvokeDefault(NPObject* o, const NPVariant* args, uint32_t argCount,
+		    NPVariant* result)
+{
+  if (1 != argCount || !NPVARIANT_IS_INT32(args[0]) ||
+      35 != NPVARIANT_TO_INT32(args[0]))
+    return false;
+
+  return true;
+}
+
+static const NPClass kGCRaceClass = {
+  NP_CLASS_STRUCT_VERSION,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  GCRaceInvokeDefault,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  NULL
+};
+
+struct GCRaceData
+{
+  GCRaceData(NPP npp, NPObject* callback, NPObject* localFunc)
+    : npp_(npp)
+    , callback_(callback)
+    , localFunc_(localFunc)
+  {
+    NPN_RetainObject(callback_);
+    NPN_RetainObject(localFunc_);
+  }
+
+  ~GCRaceData()
+  {
+    NPN_ReleaseObject(callback_);
+    NPN_ReleaseObject(localFunc_);
+  }
+
+  NPP npp_;
+  NPObject* callback_;
+  NPObject* localFunc_;
+};
+
+static void
+FinishGCRace(void* closure)
+{
+  GCRaceData* rd = static_cast<GCRaceData*>(closure);
+
+#ifdef XP_WIN
+  Sleep(5000);
+#else
+  sleep(5);
+#endif
+
+  NPVariant arg;
+  OBJECT_TO_NPVARIANT(rd->localFunc_, arg);
+
+  NPVariant result;
+  bool ok = NPN_InvokeDefault(rd->npp_, rd->callback_, &arg, 1, &result);
+  if (!ok)
+    return;
+
+  NPN_ReleaseVariantValue(&result);
+  delete rd;
+}
+
+bool
+checkGCRace(NPObject* npobj, const NPVariant* args, uint32_t argCount,
+	    NPVariant* result)
+{
+  if (1 != argCount || !NPVARIANT_IS_OBJECT(args[0]))
+    return false;
+
+  NPP npp = static_cast<TestNPObject*>(npobj)->npp;
+  
+  NPObject* localFunc =
+    NPN_CreateObject(npp, const_cast<NPClass*>(&kGCRaceClass));
+
+  GCRaceData* rd =
+    new GCRaceData(npp, NPVARIANT_TO_OBJECT(args[0]), localFunc);
+  NPN_PluginThreadAsyncCall(npp, FinishGCRace, rd);
+
+  OBJECT_TO_NPVARIANT(localFunc, *result);
+  return true;
+}