Bug 870043 - Add support for scripted operation callbacks in XPCShell. r=mrbkap
authorBobby Holley <bobbyholley@gmail.com>
Wed, 24 Jul 2013 15:33:31 -0700
changeset 152173 be80ef1cacc25cca2cc92d7038d1aef5bacc6e70
parent 152172 6064e8e0439bf0e598c346f656dfdc8545389103
child 152174 c53f4a66b05921dfcad3863de10d19ac95aed9ee
push id2859
push userakeybl@mozilla.com
push dateMon, 16 Sep 2013 19:14:59 +0000
treeherdermozilla-beta@87d3c51cd2bf [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmrbkap
bugs870043
milestone25.0a1
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
Bug 870043 - Add support for scripted operation callbacks in XPCShell. r=mrbkap
js/xpconnect/shell/xpcshell.cpp
--- a/js/xpconnect/shell/xpcshell.cpp
+++ b/js/xpconnect/shell/xpcshell.cpp
@@ -807,16 +807,65 @@ File(JSContext *cx, unsigned argc, jsval
   if (NS_FAILED(rv)) {
     JS_ReportError(cx, "Could not wrap native object!");
     return false;
   }
 
   return true;
 }
 
+Value sScriptedOperationCallback = UndefinedValue();
+
+static JSBool
+XPCShellOperationCallback(JSContext *cx)
+{
+    // If no operation callback was set by script, no-op.
+    if (sScriptedOperationCallback.isUndefined())
+        return true;
+
+    JSAutoCompartment ac(cx, &sScriptedOperationCallback.toObject());
+    RootedValue rv(cx);
+    if (!JS_CallFunctionValue(cx, nullptr, sScriptedOperationCallback,
+                              0, nullptr, rv.address()) || !rv.isBoolean())
+    {
+        NS_WARNING("Scripted operation callback failed! Terminating script.");
+        JS_ClearPendingException(cx);
+        return false;
+    }
+
+    return rv.toBoolean();
+}
+
+static JSBool
+SetOperationCallback(JSContext *cx, unsigned argc, jsval *vp)
+{
+    // Sanity-check args.
+    JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+    if (args.length() != 1) {
+        JS_ReportError(cx, "Wrong number of arguments");
+        return false;
+    }
+
+    // Allow callers to remove the operation callback by passing undefined.
+    if (args[0].isUndefined()) {
+        sScriptedOperationCallback = UndefinedValue();
+        return true;
+    }
+
+    // Otherwise, we should have a callable object.
+    if (!args[0].isObject() || !JS_ObjectIsCallable(cx, &args[0].toObject())) {
+        JS_ReportError(cx, "Argument must be callable");
+        return false;
+    }
+
+    sScriptedOperationCallback = args[0];
+
+    return true;
+}
+
 static const JSFunctionSpec glob_functions[] = {
     JS_FS("print",           Print,          0,0),
     JS_FS("readline",        ReadLine,       1,0),
     JS_FS("load",            Load,           1,0),
     JS_FS("quit",            Quit,           0,0),
     JS_FS("ignoreReportedErrors", IgnoreReportedErrors, 1,0),
     JS_FS("version",         Version,        1,0),
     JS_FS("build",           BuildDate,      0,0),
@@ -831,16 +880,17 @@ static const JSFunctionSpec glob_functio
 #ifdef DEBUG
     JS_FS("dumpHeap",        DumpHeap,       5,0),
 #endif
     JS_FS("sendCommand",     SendCommand,    1,0),
     JS_FS("atob",            Atob,           1,0),
     JS_FS("btoa",            Btoa,           1,0),
     JS_FS("Blob",            Blob,           2,JSFUN_CONSTRUCTOR),
     JS_FS("File",            File,           2,JSFUN_CONSTRUCTOR),
+    JS_FS("setOperationCallback", SetOperationCallback, 1,0),
     JS_FS_END
 };
 
 JSClass global_class = {
     "global", 0,
     JS_PropertyStub,  JS_DeletePropertyStub,  JS_PropertyStub,  JS_StrictPropertyStub,
     JS_EnumerateStub, JS_ResolveStub,   JS_ConvertStub,   nullptr
 };
@@ -1405,16 +1455,17 @@ XPCShellErrorReporter(JSContext *cx, con
 static JSBool
 ContextCallback(JSContext *cx, unsigned contextOp)
 {
     if (gOldJSContextCallback && !gOldJSContextCallback(cx, contextOp))
         return false;
 
     if (contextOp == JSCONTEXT_NEW) {
         JS_SetErrorReporter(cx, XPCShellErrorReporter);
+        JS_SetOperationCallback(cx, XPCShellOperationCallback);
     }
     return true;
 }
 
 static bool
 GetCurrentWorkingDirectory(nsAString& workingDirectory)
 {
 #if !defined(XP_WIN) && !defined(XP_UNIX)
@@ -1688,17 +1739,19 @@ main(int argc, char **argv, char **envp)
 
             nsAutoString workingDirectory;
             if (GetCurrentWorkingDirectory(workingDirectory))
                 gWorkingDirectory = &workingDirectory;
 
             JS_DefineProperty(cx, glob, "__LOCATION__", JSVAL_VOID,
                               GetLocationProperty, NULL, 0);
 
+            JS_AddValueRoot(cx, &sScriptedOperationCallback);
             result = ProcessArgs(cx, glob, argv, argc, &dirprovider);
+            JS_RemoveValueRoot(cx, &sScriptedOperationCallback);
 
             JS_DropPrincipals(rt, gJSPrincipals);
             JS_SetAllNonReservedSlotsToUndefined(cx, glob);
             JS_GC(rt);
         }
         pusher.Pop();
         JS_GC(rt);
         JS_DestroyContext(cx);