js/xpconnect/src/XPCShellImpl.cpp

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

     1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
     2 /* vim: set ts=8 sts=4 et sw=4 tw=99: */
     3 /* This Source Code Form is subject to the terms of the Mozilla Public
     4  * License, v. 2.0. If a copy of the MPL was not distributed with this
     5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     7 #include "nsXULAppAPI.h"
     8 #include "jsapi.h"
     9 #include "jsfriendapi.h"
    10 #include "jsprf.h"
    11 #include "js/OldDebugAPI.h"
    12 #include "nsServiceManagerUtils.h"
    13 #include "nsComponentManagerUtils.h"
    14 #include "nsIXPConnect.h"
    15 #include "nsIJSNativeInitializer.h"
    16 #include "nsIServiceManager.h"
    17 #include "nsIFile.h"
    18 #include "nsString.h"
    19 #include "nsIDirectoryService.h"
    20 #include "nsDirectoryServiceDefs.h"
    21 #include "nsAppDirectoryServiceDefs.h"
    22 #include "nscore.h"
    23 #include "nsArrayEnumerator.h"
    24 #include "nsCOMArray.h"
    25 #include "nsDirectoryServiceUtils.h"
    26 #include "nsIJSRuntimeService.h"
    27 #include "nsCOMPtr.h"
    28 #include "nsAutoPtr.h"
    29 #include "nsJSPrincipals.h"
    30 #include "xpcpublic.h"
    31 #include "BackstagePass.h"
    32 #include "nsCxPusher.h"
    33 #include "nsIScriptSecurityManager.h"
    34 #include "nsIPrincipal.h"
    36 #ifdef ANDROID
    37 #include <android/log.h>
    38 #endif
    40 #ifdef XP_WIN
    41 #include <windows.h>
    42 #endif
    44 // all this crap is needed to do the interactive shell stuff
    45 #include <stdlib.h>
    46 #include <errno.h>
    47 #ifdef HAVE_IO_H
    48 #include <io.h>     /* for isatty() */
    49 #endif
    50 #ifdef HAVE_UNISTD_H
    51 #include <unistd.h>     /* for isatty() */
    52 #endif
    54 #ifdef MOZ_CRASHREPORTER
    55 #include "nsExceptionHandler.h"
    56 #include "nsICrashReporter.h"
    57 #endif
    59 using namespace mozilla;
    60 using namespace JS;
    62 class XPCShellDirProvider : public nsIDirectoryServiceProvider2
    63 {
    64 public:
    65     NS_DECL_ISUPPORTS_INHERITED
    66     NS_DECL_NSIDIRECTORYSERVICEPROVIDER
    67     NS_DECL_NSIDIRECTORYSERVICEPROVIDER2
    69     XPCShellDirProvider() { }
    70     ~XPCShellDirProvider() { }
    72     // The platform resource folder
    73     void SetGREDir(nsIFile *greDir);
    74     void ClearGREDir() { mGREDir = nullptr; }
    75     // The application resource folder
    76     void SetAppDir(nsIFile *appFile);
    77     void ClearAppDir() { mAppDir = nullptr; }
    78     // The app executable
    79     void SetAppFile(nsIFile *appFile);
    80     void ClearAppFile() { mAppFile = nullptr; }
    81     // An additional custom plugin dir if specified
    82     void SetPluginDir(nsIFile* pluginDir);
    83     void ClearPluginDir() { mPluginDir = nullptr; }
    85 private:
    86     nsCOMPtr<nsIFile> mGREDir;
    87     nsCOMPtr<nsIFile> mAppDir;
    88     nsCOMPtr<nsIFile> mPluginDir;
    89     nsCOMPtr<nsIFile> mAppFile;
    90 };
    92 #ifdef JS_THREADSAFE
    93 #define DoBeginRequest(cx) JS_BeginRequest((cx))
    94 #define DoEndRequest(cx)   JS_EndRequest((cx))
    95 #else
    96 #define DoBeginRequest(cx) ((void)0)
    97 #define DoEndRequest(cx)   ((void)0)
    98 #endif
   100 static const char kXPConnectServiceContractID[] = "@mozilla.org/js/xpc/XPConnect;1";
   102 #define EXITCODE_RUNTIME_ERROR 3
   103 #define EXITCODE_FILE_NOT_FOUND 4
   105 static FILE *gOutFile = nullptr;
   106 static FILE *gErrFile = nullptr;
   107 static FILE *gInFile = nullptr;
   109 static int gExitCode = 0;
   110 static bool gIgnoreReportedErrors = false;
   111 static bool gQuitting = false;
   112 static bool reportWarnings = true;
   113 static bool compileOnly = false;
   115 static JSPrincipals *gJSPrincipals = nullptr;
   116 static nsAutoString *gWorkingDirectory = nullptr;
   118 static bool
   119 GetLocationProperty(JSContext *cx, HandleObject obj, HandleId id, MutableHandleValue vp)
   120 {
   121 #if !defined(XP_WIN) && !defined(XP_UNIX)
   122     //XXX: your platform should really implement this
   123     return false;
   124 #else
   125     JS::AutoFilename filename;
   126     if (JS::DescribeScriptedCaller(cx, &filename) && filename.get()) {
   127         nsresult rv;
   128         nsCOMPtr<nsIXPConnect> xpc =
   129             do_GetService(kXPConnectServiceContractID, &rv);
   131 #if defined(XP_WIN)
   132         // convert from the system codepage to UTF-16
   133         int bufferSize = MultiByteToWideChar(CP_ACP, 0, filename.get(),
   134                                              -1, nullptr, 0);
   135         nsAutoString filenameString;
   136         filenameString.SetLength(bufferSize);
   137         MultiByteToWideChar(CP_ACP, 0, filename.get(),
   138                             -1, (LPWSTR)filenameString.BeginWriting(),
   139                             filenameString.Length());
   140         // remove the null terminator
   141         filenameString.SetLength(bufferSize - 1);
   143         // replace forward slashes with backslashes,
   144         // since nsLocalFileWin chokes on them
   145         char16_t* start = filenameString.BeginWriting();
   146         char16_t* end = filenameString.EndWriting();
   148         while (start != end) {
   149             if (*start == L'/')
   150                 *start = L'\\';
   151             start++;
   152         }
   153 #elif defined(XP_UNIX)
   154         NS_ConvertUTF8toUTF16 filenameString(filename.get());
   155 #endif
   157         nsCOMPtr<nsIFile> location;
   158         if (NS_SUCCEEDED(rv)) {
   159             rv = NS_NewLocalFile(filenameString,
   160                                  false, getter_AddRefs(location));
   161         }
   163         if (!location && gWorkingDirectory) {
   164             // could be a relative path, try appending it to the cwd
   165             // and then normalize
   166             nsAutoString absolutePath(*gWorkingDirectory);
   167             absolutePath.Append(filenameString);
   169             rv = NS_NewLocalFile(absolutePath,
   170                                  false, getter_AddRefs(location));
   171         }
   173         if (location) {
   174             nsCOMPtr<nsIXPConnectJSObjectHolder> locationHolder;
   176             bool symlink;
   177             // don't normalize symlinks, because that's kind of confusing
   178             if (NS_SUCCEEDED(location->IsSymlink(&symlink)) &&
   179                 !symlink)
   180                 location->Normalize();
   181             rv = xpc->WrapNative(cx, obj, location,
   182                                  NS_GET_IID(nsIFile),
   183                                  getter_AddRefs(locationHolder));
   185             if (NS_SUCCEEDED(rv) &&
   186                 locationHolder->GetJSObject()) {
   187                 vp.set(OBJECT_TO_JSVAL(locationHolder->GetJSObject()));
   188             }
   189         }
   190     }
   192     return true;
   193 #endif
   194 }
   196 static bool
   197 GetLine(JSContext *cx, char *bufp, FILE *file, const char *prompt) {
   198     {
   199         char line[256] = { '\0' };
   200         fputs(prompt, gOutFile);
   201         fflush(gOutFile);
   202         if ((!fgets(line, sizeof line, file) && errno != EINTR) || feof(file))
   203             return false;
   204         strcpy(bufp, line);
   205     }
   206     return true;
   207 }
   209 static bool
   210 ReadLine(JSContext *cx, unsigned argc, jsval *vp)
   211 {
   212     CallArgs args = CallArgsFromVp(argc, vp);
   214     // While 4096 might be quite arbitrary, this is something to be fixed in
   215     // bug 105707. It is also the same limit as in ProcessFile.
   216     char buf[4096];
   217     RootedString str(cx);
   219     /* If a prompt was specified, construct the string */
   220     if (args.length() > 0) {
   221         str = JS::ToString(cx, args[0]);
   222         if (!str)
   223             return false;
   224     } else {
   225         str = JS_GetEmptyString(JS_GetRuntime(cx));
   226     }
   228     /* Get a line from the infile */
   229     JSAutoByteString strBytes(cx, str);
   230     if (!strBytes || !GetLine(cx, buf, gInFile, strBytes.ptr()))
   231         return false;
   233     /* Strip newline character added by GetLine() */
   234     unsigned int buflen = strlen(buf);
   235     if (buflen == 0) {
   236         if (feof(gInFile)) {
   237             args.rval().setNull();
   238             return true;
   239         }
   240     } else if (buf[buflen - 1] == '\n') {
   241         --buflen;
   242     }
   244     /* Turn buf into a JSString */
   245     str = JS_NewStringCopyN(cx, buf, buflen);
   246     if (!str)
   247         return false;
   249     args.rval().setString(str);
   250     return true;
   251 }
   253 static bool
   254 Print(JSContext *cx, unsigned argc, jsval *vp)
   255 {
   256     CallArgs args = CallArgsFromVp(argc, vp);
   257     args.rval().setUndefined();
   259     RootedString str(cx);
   260     nsAutoCString utf8str;
   261     size_t length;
   262     const jschar *chars;
   264     for (unsigned i = 0; i < args.length(); i++) {
   265         str = ToString(cx, args[i]);
   266         if (!str)
   267             return false;
   268         chars = JS_GetStringCharsAndLength(cx, str, &length);
   269         if (!chars)
   270             return false;
   272         if (i)
   273             utf8str.Append(' ');
   274         AppendUTF16toUTF8(Substring(reinterpret_cast<const char16_t*>(chars),
   275                                     length),
   276                           utf8str);
   277     }
   278     utf8str.Append('\n');
   279     fputs(utf8str.get(), gOutFile);
   280     fflush(gOutFile);
   281     return true;
   282 }
   284 static bool
   285 Dump(JSContext *cx, unsigned argc, jsval *vp)
   286 {
   287     CallArgs args = CallArgsFromVp(argc, vp);
   288     args.rval().setUndefined();
   290     if (!args.length())
   291          return true;
   293     RootedString str(cx, ToString(cx, args[0]));
   294     if (!str)
   295         return false;
   297     size_t length;
   298     const jschar *chars = JS_GetStringCharsAndLength(cx, str, &length);
   299     if (!chars)
   300         return false;
   302     NS_ConvertUTF16toUTF8 utf8str(reinterpret_cast<const char16_t*>(chars),
   303                                   length);
   304 #ifdef ANDROID
   305     __android_log_print(ANDROID_LOG_INFO, "Gecko", "%s", utf8str.get());
   306 #endif
   307 #ifdef XP_WIN
   308     if (IsDebuggerPresent()) {
   309       OutputDebugStringW(reinterpret_cast<const wchar_t*>(chars));
   310     }
   311 #endif
   312     fputs(utf8str.get(), gOutFile);
   313     fflush(gOutFile);
   314     return true;
   315 }
   317 static bool
   318 Load(JSContext *cx, unsigned argc, jsval *vp)
   319 {
   320     CallArgs args = CallArgsFromVp(argc, vp);
   322     JS::Rooted<JSObject*> obj(cx, JS_THIS_OBJECT(cx, vp));
   323     if (!obj)
   324         return false;
   326     RootedString str(cx);
   327     for (unsigned i = 0; i < args.length(); i++) {
   328         str = ToString(cx, args[i]);
   329         if (!str)
   330             return false;
   331         JSAutoByteString filename(cx, str);
   332         if (!filename)
   333             return false;
   334         FILE *file = fopen(filename.ptr(), "r");
   335         if (!file) {
   336             JS_ReportError(cx, "cannot open file '%s' for reading",
   337                            filename.ptr());
   338             return false;
   339         }
   340         JS::CompileOptions options(cx);
   341         options.setUTF8(true)
   342                .setFileAndLine(filename.ptr(), 1);
   343         JS::Rooted<JSScript*> script(cx, JS::Compile(cx, obj, options, file));
   344         fclose(file);
   345         if (!script)
   346             return false;
   348         if (!compileOnly && !JS_ExecuteScript(cx, obj, script))
   349             return false;
   350     }
   351     args.rval().setUndefined();
   352     return true;
   353 }
   355 static bool
   356 Version(JSContext *cx, unsigned argc, jsval *vp)
   357 {
   358     CallArgs args = CallArgsFromVp(argc, vp);
   359     args.rval().setInt32(JS_GetVersion(cx));
   360     if (args.get(0).isInt32())
   361         JS_SetVersionForCompartment(js::GetContextCompartment(cx),
   362                                     JSVersion(args[0].toInt32()));
   363     return true;
   364 }
   366 static bool
   367 BuildDate(JSContext *cx, unsigned argc, jsval *vp)
   368 {
   369     CallArgs args = CallArgsFromVp(argc, vp);
   370     fprintf(gOutFile, "built on %s at %s\n", __DATE__, __TIME__);
   371     args.rval().setUndefined();
   372     return true;
   373 }
   375 static bool
   376 Quit(JSContext *cx, unsigned argc, jsval *vp)
   377 {
   378     gExitCode = 0;
   379     JS_ConvertArguments(cx, JS::CallArgsFromVp(argc, vp),"/ i", &gExitCode);
   381     gQuitting = true;
   382 //    exit(0);
   383     return false;
   384 }
   386 // Provide script a way to disable the xpcshell error reporter, preventing
   387 // reported errors from being logged to the console and also from affecting the
   388 // exit code returned by the xpcshell binary.
   389 static bool
   390 IgnoreReportedErrors(JSContext *cx, unsigned argc, jsval *vp)
   391 {
   392     CallArgs args = CallArgsFromVp(argc, vp);
   393     if (args.length() != 1 || !args[0].isBoolean()) {
   394         JS_ReportError(cx, "Bad arguments");
   395         return false;
   396     }
   397     gIgnoreReportedErrors = args[0].toBoolean();
   398     return true;
   399 }
   401 static bool
   402 DumpXPC(JSContext *cx, unsigned argc, jsval *vp)
   403 {
   404     JS::CallArgs args = CallArgsFromVp(argc, vp);
   406     uint16_t depth = 2;
   407     if (args.length() > 0) {
   408         if (!JS::ToUint16(cx, args[0], &depth))
   409             return false;
   410     }
   412     nsCOMPtr<nsIXPConnect> xpc = do_GetService(nsIXPConnect::GetCID());
   413     if (xpc)
   414         xpc->DebugDump(int16_t(depth));
   415     args.rval().setUndefined();
   416     return true;
   417 }
   419 static bool
   420 GC(JSContext *cx, unsigned argc, jsval *vp)
   421 {
   422     CallArgs args = CallArgsFromVp(argc, vp);
   423     JSRuntime *rt = JS_GetRuntime(cx);
   424     JS_GC(rt);
   425 #ifdef JS_GCMETER
   426     js_DumpGCStats(rt, stdout);
   427 #endif
   428     args.rval().setUndefined();
   429     return true;
   430 }
   432 #ifdef JS_GC_ZEAL
   433 static bool
   434 GCZeal(JSContext *cx, unsigned argc, jsval *vp)
   435 {
   436     CallArgs args = CallArgsFromVp(argc, vp);
   437     uint32_t zeal;
   438     if (!ToUint32(cx, args.get(0), &zeal))
   439         return false;
   441     JS_SetGCZeal(cx, uint8_t(zeal), JS_DEFAULT_ZEAL_FREQ);
   442     args.rval().setUndefined();
   443     return true;
   444 }
   445 #endif
   447 static bool
   448 SendCommand(JSContext *cx, unsigned argc, Value *vp)
   449 {
   450     CallArgs args = CallArgsFromVp(argc, vp);
   452     if (args.length() == 0) {
   453         JS_ReportError(cx, "Function takes at least one argument!");
   454         return false;
   455     }
   457     JSString* str = ToString(cx, args[0]);
   458     if (!str) {
   459         JS_ReportError(cx, "Could not convert argument 1 to string!");
   460         return false;
   461     }
   463     if (args.length() > 1 && JS_TypeOfValue(cx, args[1]) != JSTYPE_FUNCTION) {
   464         JS_ReportError(cx, "Could not convert argument 2 to function!");
   465         return false;
   466     }
   468     if (!XRE_SendTestShellCommand(cx, str, args.length() > 1 ? args[1].address() : nullptr)) {
   469         JS_ReportError(cx, "Couldn't send command!");
   470         return false;
   471     }
   473     args.rval().setUndefined();
   474     return true;
   475 }
   477 static bool
   478 Options(JSContext *cx, unsigned argc, jsval *vp)
   479 {
   480     JS::CallArgs args = CallArgsFromVp(argc, vp);
   481     ContextOptions oldOptions = ContextOptionsRef(cx);
   483     for (unsigned i = 0; i < args.length(); ++i) {
   484         JSString *str = ToString(cx, args[i]);
   485         if (!str)
   486             return false;
   488         JSAutoByteString opt(cx, str);
   489         if (!opt)
   490             return false;
   492         if (strcmp(opt.ptr(), "strict") == 0)
   493             ContextOptionsRef(cx).toggleExtraWarnings();
   494         else if (strcmp(opt.ptr(), "werror") == 0)
   495             ContextOptionsRef(cx).toggleWerror();
   496         else if (strcmp(opt.ptr(), "strict_mode") == 0)
   497             ContextOptionsRef(cx).toggleStrictMode();
   498         else {
   499             JS_ReportError(cx, "unknown option name '%s'. The valid names are "
   500                            "strict, werror, and strict_mode.", opt.ptr());
   501             return false;
   502         }
   503     }
   505     char *names = nullptr;
   506     if (oldOptions.extraWarnings()) {
   507         names = JS_sprintf_append(names, "%s", "strict");
   508         if (!names) {
   509             JS_ReportOutOfMemory(cx);
   510             return false;
   511         }
   512     }
   513     if (oldOptions.werror()) {
   514         names = JS_sprintf_append(names, "%s%s", names ? "," : "", "werror");
   515         if (!names) {
   516             JS_ReportOutOfMemory(cx);
   517             return false;
   518         }
   519     }
   520     if (names && oldOptions.strictMode()) {
   521         names = JS_sprintf_append(names, "%s%s", names ? "," : "", "strict_mode");
   522         if (!names) {
   523             JS_ReportOutOfMemory(cx);
   524             return false;
   525         }
   526     }
   528     JSString *str = JS_NewStringCopyZ(cx, names);
   529     free(names);
   530     if (!str)
   531         return false;
   533     args.rval().setString(str);
   534     return true;
   535 }
   537 static bool
   538 Parent(JSContext *cx, unsigned argc, jsval *vp)
   539 {
   540     CallArgs args = CallArgsFromVp(argc, vp);
   541     if (args.length() != 1) {
   542         JS_ReportError(cx, "Wrong number of arguments");
   543         return false;
   544     }
   546     Value v = args[0];
   547     if (JSVAL_IS_PRIMITIVE(v)) {
   548         JS_ReportError(cx, "Only objects have parents!");
   549         return false;
   550     }
   552     args.rval().setObjectOrNull(JS_GetParent(&v.toObject()));
   553     return true;
   554 }
   556 static bool
   557 Atob(JSContext *cx, unsigned argc, Value *vp)
   558 {
   559     CallArgs args = CallArgsFromVp(argc, vp);
   560     if (!args.length())
   561         return true;
   563     return xpc::Base64Decode(cx, args[0], args.rval());
   564 }
   566 static bool
   567 Btoa(JSContext *cx, unsigned argc, Value *vp)
   568 {
   569     CallArgs args = CallArgsFromVp(argc, vp);
   570     if (!args.length())
   571         return true;
   573   return xpc::Base64Encode(cx, args[0], args.rval());
   574 }
   576 static bool
   577 Blob(JSContext *cx, unsigned argc, Value *vp)
   578 {
   579   JS::CallArgs args = CallArgsFromVp(argc, vp);
   581   nsCOMPtr<nsISupports> native =
   582     do_CreateInstance("@mozilla.org/dom/multipart-blob;1");
   583   if (!native) {
   584     JS_ReportError(cx, "Could not create native object!");
   585     return false;
   586   }
   588   nsCOMPtr<nsIJSNativeInitializer> initializer = do_QueryInterface(native);
   589   MOZ_ASSERT(initializer);
   591   nsresult rv = initializer->Initialize(nullptr, cx, nullptr, args);
   592   if (NS_FAILED(rv)) {
   593     JS_ReportError(cx, "Could not initialize native object!");
   594     return false;
   595   }
   597   nsCOMPtr<nsIXPConnect> xpc = do_GetService(kXPConnectServiceContractID, &rv);
   598   if (NS_FAILED(rv)) {
   599     JS_ReportError(cx, "Could not get XPConnent service!");
   600     return false;
   601   }
   603   JSObject *global = JS::CurrentGlobalOrNull(cx);
   604   rv = xpc->WrapNativeToJSVal(cx, global, native, nullptr,
   605                               &NS_GET_IID(nsISupports), true,
   606                               args.rval());
   607   if (NS_FAILED(rv)) {
   608     JS_ReportError(cx, "Could not wrap native object!");
   609     return false;
   610   }
   612   return true;
   613 }
   615 static bool
   616 File(JSContext *cx, unsigned argc, Value *vp)
   617 {
   618   JS::CallArgs args = CallArgsFromVp(argc, vp);
   620   nsCOMPtr<nsISupports> native =
   621     do_CreateInstance("@mozilla.org/dom/multipart-file;1");
   622   if (!native) {
   623     JS_ReportError(cx, "Could not create native object!");
   624     return false;
   625   }
   627   nsCOMPtr<nsIJSNativeInitializer> initializer = do_QueryInterface(native);
   628   MOZ_ASSERT(initializer);
   630   nsresult rv = initializer->Initialize(nullptr, cx, nullptr, args);
   631   if (NS_FAILED(rv)) {
   632     JS_ReportError(cx, "Could not initialize native object!");
   633     return false;
   634   }
   636   nsCOMPtr<nsIXPConnect> xpc = do_GetService(kXPConnectServiceContractID, &rv);
   637   if (NS_FAILED(rv)) {
   638     JS_ReportError(cx, "Could not get XPConnent service!");
   639     return false;
   640   }
   642   JSObject *global = JS::CurrentGlobalOrNull(cx);
   643   rv = xpc->WrapNativeToJSVal(cx, global, native, nullptr,
   644                               &NS_GET_IID(nsISupports), true,
   645                               args.rval());
   646   if (NS_FAILED(rv)) {
   647     JS_ReportError(cx, "Could not wrap native object!");
   648     return false;
   649   }
   651   return true;
   652 }
   654 static Maybe<PersistentRootedValue> sScriptedInterruptCallback;
   656 static bool
   657 XPCShellInterruptCallback(JSContext *cx)
   658 {
   659     MOZ_ASSERT(!sScriptedInterruptCallback.empty());
   660     RootedValue callback(cx, sScriptedInterruptCallback.ref());
   662     // If no interrupt callback was set by script, no-op.
   663     if (callback.isUndefined())
   664         return true;
   666     JSAutoCompartment ac(cx, &callback.toObject());
   667     RootedValue rv(cx);
   668     if (!JS_CallFunctionValue(cx, JS::NullPtr(), callback, JS::HandleValueArray::empty(), &rv) ||
   669         !rv.isBoolean())
   670     {
   671         NS_WARNING("Scripted interrupt callback failed! Terminating script.");
   672         JS_ClearPendingException(cx);
   673         return false;
   674     }
   676     return rv.toBoolean();
   677 }
   679 static bool
   680 SetInterruptCallback(JSContext *cx, unsigned argc, jsval *vp)
   681 {
   682     MOZ_ASSERT(!sScriptedInterruptCallback.empty());
   684     // Sanity-check args.
   685     JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
   686     if (args.length() != 1) {
   687         JS_ReportError(cx, "Wrong number of arguments");
   688         return false;
   689     }
   691     // Allow callers to remove the interrupt callback by passing undefined.
   692     if (args[0].isUndefined()) {
   693         sScriptedInterruptCallback.ref() = UndefinedValue();
   694         return true;
   695     }
   697     // Otherwise, we should have a callable object.
   698     if (!args[0].isObject() || !JS_ObjectIsCallable(cx, &args[0].toObject())) {
   699         JS_ReportError(cx, "Argument must be callable");
   700         return false;
   701     }
   703     sScriptedInterruptCallback.ref() = args[0];
   705     return true;
   706 }
   708 static bool
   709 SimulateActivityCallback(JSContext *cx, unsigned argc, jsval *vp)
   710 {
   711     // Sanity-check args.
   712     JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
   713     if (args.length() != 1 || !args[0].isBoolean()) {
   714         JS_ReportError(cx, "Wrong number of arguments");
   715         return false;
   716     }
   717     xpc::SimulateActivityCallback(args[0].toBoolean());
   718     return true;
   719 }
   721 static const JSFunctionSpec glob_functions[] = {
   722     JS_FS("print",           Print,          0,0),
   723     JS_FS("readline",        ReadLine,       1,0),
   724     JS_FS("load",            Load,           1,0),
   725     JS_FS("quit",            Quit,           0,0),
   726     JS_FS("ignoreReportedErrors", IgnoreReportedErrors, 1,0),
   727     JS_FS("version",         Version,        1,0),
   728     JS_FS("build",           BuildDate,      0,0),
   729     JS_FS("dumpXPC",         DumpXPC,        1,0),
   730     JS_FS("dump",            Dump,           1,0),
   731     JS_FS("gc",              GC,             0,0),
   732 #ifdef JS_GC_ZEAL
   733     JS_FS("gczeal",          GCZeal,         1,0),
   734 #endif
   735     JS_FS("options",         Options,        0,0),
   736     JS_FN("parent",          Parent,         1,0),
   737     JS_FS("sendCommand",     SendCommand,    1,0),
   738     JS_FS("atob",            Atob,           1,0),
   739     JS_FS("btoa",            Btoa,           1,0),
   740     JS_FS("Blob",            Blob,           2,JSFUN_CONSTRUCTOR),
   741     JS_FS("File",            File,           2,JSFUN_CONSTRUCTOR),
   742     JS_FS("setInterruptCallback", SetInterruptCallback, 1,0),
   743     JS_FS("simulateActivityCallback", SimulateActivityCallback, 1,0),
   744     JS_FS_END
   745 };
   747 static bool
   748 env_setProperty(JSContext *cx, HandleObject obj, HandleId id, bool strict, MutableHandleValue vp)
   749 {
   750 /* XXX porting may be easy, but these don't seem to supply setenv by default */
   751 #if !defined SOLARIS
   752     JSString *valstr;
   753     JS::Rooted<JSString*> idstr(cx);
   754     int rv;
   756     RootedValue idval(cx);
   757     if (!JS_IdToValue(cx, id, &idval))
   758         return false;
   760     idstr = ToString(cx, idval);
   761     valstr = ToString(cx, vp);
   762     if (!idstr || !valstr)
   763         return false;
   764     JSAutoByteString name(cx, idstr);
   765     if (!name)
   766         return false;
   767     JSAutoByteString value(cx, valstr);
   768     if (!value)
   769         return false;
   770 #if defined XP_WIN || defined HPUX || defined OSF1 || defined SCO
   771     {
   772         char *waste = JS_smprintf("%s=%s", name.ptr(), value.ptr());
   773         if (!waste) {
   774             JS_ReportOutOfMemory(cx);
   775             return false;
   776         }
   777         rv = putenv(waste);
   778 #ifdef XP_WIN
   779         /*
   780          * HPUX9 at least still has the bad old non-copying putenv.
   781          *
   782          * Per mail from <s.shanmuganathan@digital.com>, OSF1 also has a putenv
   783          * that will crash if you pass it an auto char array (so it must place
   784          * its argument directly in the char *environ[] array).
   785          */
   786         free(waste);
   787 #endif
   788     }
   789 #else
   790     rv = setenv(name.ptr(), value.ptr(), 1);
   791 #endif
   792     if (rv < 0) {
   793         JS_ReportError(cx, "can't set envariable %s to %s", name.ptr(), value.ptr());
   794         return false;
   795     }
   796     vp.set(STRING_TO_JSVAL(valstr));
   797 #endif /* !defined SOLARIS */
   798     return true;
   799 }
   801 static bool
   802 env_enumerate(JSContext *cx, HandleObject obj)
   803 {
   804     static bool reflected;
   805     char **evp, *name, *value;
   806     RootedString valstr(cx);
   807     bool ok;
   809     if (reflected)
   810         return true;
   812     for (evp = (char **)JS_GetPrivate(obj); (name = *evp) != nullptr; evp++) {
   813         value = strchr(name, '=');
   814         if (!value)
   815             continue;
   816         *value++ = '\0';
   817         valstr = JS_NewStringCopyZ(cx, value);
   818         ok = valstr ? JS_DefineProperty(cx, obj, name, valstr, JSPROP_ENUMERATE) : false;
   819         value[-1] = '=';
   820         if (!ok)
   821             return false;
   822     }
   824     reflected = true;
   825     return true;
   826 }
   828 static bool
   829 env_resolve(JSContext *cx, HandleObject obj, HandleId id,
   830             JS::MutableHandleObject objp)
   831 {
   832     JSString *idstr, *valstr;
   834     RootedValue idval(cx);
   835     if (!JS_IdToValue(cx, id, &idval))
   836         return false;
   838     idstr = ToString(cx, idval);
   839     if (!idstr)
   840         return false;
   841     JSAutoByteString name(cx, idstr);
   842     if (!name)
   843         return false;
   844     const char *value = getenv(name.ptr());
   845     if (value) {
   846         valstr = JS_NewStringCopyZ(cx, value);
   847         if (!valstr)
   848             return false;
   849         if (!JS_DefinePropertyById(cx, obj, id, STRING_TO_JSVAL(valstr),
   850                                    nullptr, nullptr, JSPROP_ENUMERATE)) {
   851             return false;
   852         }
   853         objp.set(obj);
   854     }
   855     return true;
   856 }
   858 static const JSClass env_class = {
   859     "environment", JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE,
   860     JS_PropertyStub,  JS_DeletePropertyStub,
   861     JS_PropertyStub,  env_setProperty,
   862     env_enumerate, (JSResolveOp) env_resolve,
   863     JS_ConvertStub,   nullptr
   864 };
   866 /***************************************************************************/
   868 typedef enum JSShellErrNum {
   869 #define MSG_DEF(name, number, count, exception, format) \
   870     name = number,
   871 #include "jsshell.msg"
   872 #undef MSG_DEF
   873     JSShellErr_Limit
   874 } JSShellErrNum;
   876 static const JSErrorFormatString jsShell_ErrorFormatString[JSShellErr_Limit] = {
   877 #define MSG_DEF(name, number, count, exception, format) \
   878     { format, count } ,
   879 #include "jsshell.msg"
   880 #undef MSG_DEF
   881 };
   883 static const JSErrorFormatString *
   884 my_GetErrorMessage(void *userRef, const char *locale, const unsigned errorNumber)
   885 {
   886     if (errorNumber == 0 || errorNumber >= JSShellErr_Limit)
   887         return nullptr;
   889     return &jsShell_ErrorFormatString[errorNumber];
   890 }
   892 static void
   893 ProcessFile(JSContext *cx, JS::Handle<JSObject*> obj, const char *filename, FILE *file,
   894             bool forceTTY)
   895 {
   896     JS::RootedScript script(cx);
   897     JS::RootedValue result(cx);
   898     int lineno, startline;
   899     bool ok, hitEOF;
   900     char *bufp, buffer[4096];
   901     JSString *str;
   903     if (forceTTY) {
   904         file = stdin;
   905     } else if (!isatty(fileno(file)))
   906     {
   907         /*
   908          * It's not interactive - just execute it.
   909          *
   910          * Support the UNIX #! shell hack; gobble the first line if it starts
   911          * with '#'.  TODO - this isn't quite compatible with sharp variables,
   912          * as a legal js program (using sharp variables) might start with '#'.
   913          * But that would require multi-character lookahead.
   914          */
   915         int ch = fgetc(file);
   916         if (ch == '#') {
   917             while ((ch = fgetc(file)) != EOF) {
   918                 if (ch == '\n' || ch == '\r')
   919                     break;
   920             }
   921         }
   922         ungetc(ch, file);
   923         DoBeginRequest(cx);
   925         JS::CompileOptions options(cx);
   926         options.setUTF8(true)
   927                .setFileAndLine(filename, 1);
   928         script = JS::Compile(cx, obj, options, file);
   929         if (script && !compileOnly)
   930             (void)JS_ExecuteScript(cx, obj, script, &result);
   931         DoEndRequest(cx);
   933         return;
   934     }
   936     /* It's an interactive filehandle; drop into read-eval-print loop. */
   937     lineno = 1;
   938     hitEOF = false;
   939     do {
   940         bufp = buffer;
   941         *bufp = '\0';
   943         /*
   944          * Accumulate lines until we get a 'compilable unit' - one that either
   945          * generates an error (before running out of source) or that compiles
   946          * cleanly.  This should be whenever we get a complete statement that
   947          * coincides with the end of a line.
   948          */
   949         startline = lineno;
   950         do {
   951             if (!GetLine(cx, bufp, file, startline == lineno ? "js> " : "")) {
   952                 hitEOF = true;
   953                 break;
   954             }
   955             bufp += strlen(bufp);
   956             lineno++;
   957         } while (!JS_BufferIsCompilableUnit(cx, obj, buffer, strlen(buffer)));
   959         DoBeginRequest(cx);
   960         /* Clear any pending exception from previous failed compiles.  */
   961         JS_ClearPendingException(cx);
   962         JS::CompileOptions options(cx);
   963         options.setFileAndLine("typein", startline);
   964         script = JS_CompileScript(cx, obj, buffer, strlen(buffer), options);
   965         if (script) {
   966             JSErrorReporter older;
   968             if (!compileOnly) {
   969                 ok = JS_ExecuteScript(cx, obj, script, &result);
   970                 if (ok && result != JSVAL_VOID) {
   971                     /* Suppress error reports from JS::ToString(). */
   972                     older = JS_SetErrorReporter(cx, nullptr);
   973                     str = ToString(cx, result);
   974                     JS_SetErrorReporter(cx, older);
   975                     JSAutoByteString bytes;
   976                     if (str && bytes.encodeLatin1(cx, str))
   977                         fprintf(gOutFile, "%s\n", bytes.ptr());
   978                     else
   979                         ok = false;
   980                 }
   981             }
   982         }
   983         DoEndRequest(cx);
   984     } while (!hitEOF && !gQuitting);
   986     fprintf(gOutFile, "\n");
   987 }
   989 static void
   990 Process(JSContext *cx, JS::Handle<JSObject*> obj, const char *filename, bool forceTTY)
   991 {
   992     FILE *file;
   994     if (forceTTY || !filename || strcmp(filename, "-") == 0) {
   995         file = stdin;
   996     } else {
   997         file = fopen(filename, "r");
   998         if (!file) {
   999             JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr,
  1000                                  JSSMSG_CANT_OPEN,
  1001                                  filename, strerror(errno));
  1002             gExitCode = EXITCODE_FILE_NOT_FOUND;
  1003             return;
  1007     ProcessFile(cx, obj, filename, file, forceTTY);
  1008     if (file != stdin)
  1009         fclose(file);
  1012 static int
  1013 usage(void)
  1015     fprintf(gErrFile, "%s\n", JS_GetImplementationVersion());
  1016     fprintf(gErrFile, "usage: xpcshell [-g gredir] [-a appdir] [-r manifest]... [-PsSwWCijmIn] [-v version] [-f scriptfile] [-e script] [scriptfile] [scriptarg...]\n");
  1017     return 2;
  1020 static void
  1021 ProcessArgsForCompartment(JSContext *cx, char **argv, int argc)
  1023     for (int i = 0; i < argc; i++) {
  1024         if (argv[i][0] != '-' || argv[i][1] == '\0')
  1025             break;
  1027         switch (argv[i][1]) {
  1028           case 'v':
  1029           case 'f':
  1030           case 'e':
  1031             if (++i == argc)
  1032                 return;
  1033             break;
  1034         case 'S':
  1035             ContextOptionsRef(cx).toggleWerror();
  1036         case 's':
  1037             ContextOptionsRef(cx).toggleExtraWarnings();
  1038             break;
  1039         case 'I':
  1040             RuntimeOptionsRef(cx).toggleIon()
  1041                                  .toggleAsmJS();
  1042             break;
  1047 static int
  1048 ProcessArgs(JSContext *cx, JS::Handle<JSObject*> obj, char **argv, int argc, XPCShellDirProvider* aDirProvider)
  1050     const char rcfilename[] = "xpcshell.js";
  1051     FILE *rcfile;
  1052     int i;
  1053     JS::Rooted<JSObject*> argsObj(cx);
  1054     char *filename = nullptr;
  1055     bool isInteractive = true;
  1056     bool forceTTY = false;
  1058     rcfile = fopen(rcfilename, "r");
  1059     if (rcfile) {
  1060         printf("[loading '%s'...]\n", rcfilename);
  1061         ProcessFile(cx, obj, rcfilename, rcfile, false);
  1062         fclose(rcfile);
  1065     /*
  1066      * Scan past all optional arguments so we can create the arguments object
  1067      * before processing any -f options, which must interleave properly with
  1068      * -v and -w options.  This requires two passes, and without getopt, we'll
  1069      * have to keep the option logic here and in the second for loop in sync.
  1070      */
  1071     for (i = 0; i < argc; i++) {
  1072         if (argv[i][0] != '-' || argv[i][1] == '\0') {
  1073             ++i;
  1074             break;
  1076         switch (argv[i][1]) {
  1077           case 'v':
  1078           case 'f':
  1079           case 'e':
  1080             ++i;
  1081             break;
  1082           default:;
  1086     /*
  1087      * Create arguments early and define it to root it, so it's safe from any
  1088      * GC calls nested below, and so it is available to -f <file> arguments.
  1089      */
  1090     argsObj = JS_NewArrayObject(cx, 0);
  1091     if (!argsObj)
  1092         return 1;
  1093     if (!JS_DefineProperty(cx, obj, "arguments", argsObj, 0))
  1094         return 1;
  1096     for (size_t j = 0, length = argc - i; j < length; j++) {
  1097         JSString *str = JS_NewStringCopyZ(cx, argv[i++]);
  1098         if (!str)
  1099             return 1;
  1100         if (!JS_DefineElement(cx, argsObj, j, STRING_TO_JSVAL(str),
  1101                               nullptr, nullptr, JSPROP_ENUMERATE)) {
  1102             return 1;
  1106     for (i = 0; i < argc; i++) {
  1107         if (argv[i][0] != '-' || argv[i][1] == '\0') {
  1108             filename = argv[i++];
  1109             isInteractive = false;
  1110             break;
  1112         switch (argv[i][1]) {
  1113         case 'v':
  1114             if (++i == argc) {
  1115                 return usage();
  1117             JS_SetVersionForCompartment(js::GetContextCompartment(cx),
  1118                                         JSVersion(atoi(argv[i])));
  1119             break;
  1120         case 'W':
  1121             reportWarnings = false;
  1122             break;
  1123         case 'w':
  1124             reportWarnings = true;
  1125             break;
  1126         case 'x':
  1127             break;
  1128         case 'd':
  1129             xpc_ActivateDebugMode();
  1130             break;
  1131         case 'f':
  1132             if (++i == argc) {
  1133                 return usage();
  1135             Process(cx, obj, argv[i], false);
  1136             /*
  1137              * XXX: js -f foo.js should interpret foo.js and then
  1138              * drop into interactive mode, but that breaks test
  1139              * harness. Just execute foo.js for now.
  1140              */
  1141             isInteractive = false;
  1142             break;
  1143         case 'i':
  1144             isInteractive = forceTTY = true;
  1145             break;
  1146         case 'e':
  1148             RootedValue rval(cx);
  1150             if (++i == argc) {
  1151                 return usage();
  1154             JS_EvaluateScript(cx, obj, argv[i], strlen(argv[i]), "-e", 1, &rval);
  1156             isInteractive = false;
  1157             break;
  1159         case 'C':
  1160             compileOnly = true;
  1161             isInteractive = false;
  1162             break;
  1163         case 'S':
  1164         case 's':
  1165         case 'm':
  1166         case 'I':
  1167             // These options are processed in ProcessArgsForCompartment.
  1168             break;
  1169         case 'p':
  1171           // plugins path
  1172           char *pluginPath = argv[++i];
  1173           nsCOMPtr<nsIFile> pluginsDir;
  1174           if (NS_FAILED(XRE_GetFileFromPath(pluginPath, getter_AddRefs(pluginsDir)))) {
  1175               fprintf(gErrFile, "Couldn't use given plugins dir.\n");
  1176               return usage();
  1178           aDirProvider->SetPluginDir(pluginsDir);
  1179           break;
  1181         default:
  1182             return usage();
  1186     if (filename || isInteractive)
  1187         Process(cx, obj, filename, forceTTY);
  1189     return gExitCode;
  1192 /***************************************************************************/
  1194 // #define TEST_InitClassesWithNewWrappedGlobal
  1196 #ifdef TEST_InitClassesWithNewWrappedGlobal
  1197 // XXX hacky test code...
  1198 #include "xpctest.h"
  1200 class TestGlobal : public nsIXPCTestNoisy, public nsIXPCScriptable
  1202 public:
  1203     NS_DECL_ISUPPORTS
  1204     NS_DECL_NSIXPCTESTNOISY
  1205     NS_DECL_NSIXPCSCRIPTABLE
  1207     TestGlobal(){}
  1208 };
  1210 NS_IMPL_ISUPPORTS(TestGlobal, nsIXPCTestNoisy, nsIXPCScriptable)
  1212 // The nsIXPCScriptable map declaration that will generate stubs for us...
  1213 #define XPC_MAP_CLASSNAME           TestGlobal
  1214 #define XPC_MAP_QUOTED_CLASSNAME   "TestGlobal"
  1215 #define XPC_MAP_FLAGS               nsIXPCScriptable::USE_JSSTUB_FOR_ADDPROPERTY |\
  1216                                     nsIXPCScriptable::USE_JSSTUB_FOR_DELPROPERTY |\
  1217                                     nsIXPCScriptable::USE_JSSTUB_FOR_SETPROPERTY
  1218 #include "xpc_map_end.h" /* This will #undef the above */
  1220 NS_IMETHODIMP TestGlobal::Squawk() {return NS_OK;}
  1222 #endif
  1224 // uncomment to install the test 'this' translator
  1225 // #define TEST_TranslateThis
  1227 #ifdef TEST_TranslateThis
  1229 #include "xpctest.h"
  1231 class nsXPCFunctionThisTranslator : public nsIXPCFunctionThisTranslator
  1233 public:
  1234   NS_DECL_ISUPPORTS
  1235   NS_DECL_NSIXPCFUNCTIONTHISTRANSLATOR
  1237   nsXPCFunctionThisTranslator();
  1238   virtual ~nsXPCFunctionThisTranslator();
  1239   /* additional members */
  1240 };
  1242 /* Implementation file */
  1243 NS_IMPL_ISUPPORTS(nsXPCFunctionThisTranslator, nsIXPCFunctionThisTranslator)
  1245 nsXPCFunctionThisTranslator::nsXPCFunctionThisTranslator()
  1249 nsXPCFunctionThisTranslator::~nsXPCFunctionThisTranslator()
  1253 /* nsISupports TranslateThis (in nsISupports aInitialThis); */
  1254 NS_IMETHODIMP
  1255 nsXPCFunctionThisTranslator::TranslateThis(nsISupports *aInitialThis,
  1256                                            nsISupports **_retval)
  1258     nsCOMPtr<nsISupports> temp = aInitialThis;
  1259     temp.forget(_retval);
  1260     return NS_OK;
  1263 #endif
  1265 static void
  1266 XPCShellErrorReporter(JSContext *cx, const char *message, JSErrorReport *rep)
  1268     if (gIgnoreReportedErrors)
  1269         return;
  1271     if (!JSREPORT_IS_WARNING(rep->flags))
  1272         gExitCode = EXITCODE_RUNTIME_ERROR;
  1274     // Delegate to the system error reporter for heavy lifting.
  1275     xpc::SystemErrorReporter(cx, message, rep);
  1278 static bool
  1279 ContextCallback(JSContext *cx, unsigned contextOp)
  1281     if (contextOp == JSCONTEXT_NEW)
  1282         JS_SetErrorReporter(cx, XPCShellErrorReporter);
  1283     return true;
  1286 static bool
  1287 GetCurrentWorkingDirectory(nsAString& workingDirectory)
  1289 #if !defined(XP_WIN) && !defined(XP_UNIX)
  1290     //XXX: your platform should really implement this
  1291     return false;
  1292 #elif XP_WIN
  1293     DWORD requiredLength = GetCurrentDirectoryW(0, nullptr);
  1294     workingDirectory.SetLength(requiredLength);
  1295     GetCurrentDirectoryW(workingDirectory.Length(),
  1296                          (LPWSTR)workingDirectory.BeginWriting());
  1297     // we got a trailing null there
  1298     workingDirectory.SetLength(requiredLength);
  1299     workingDirectory.Replace(workingDirectory.Length() - 1, 1, L'\\');
  1300 #elif defined(XP_UNIX)
  1301     nsAutoCString cwd;
  1302     // 1024 is just a guess at a sane starting value
  1303     size_t bufsize = 1024;
  1304     char* result = nullptr;
  1305     while (result == nullptr) {
  1306         cwd.SetLength(bufsize);
  1307         result = getcwd(cwd.BeginWriting(), cwd.Length());
  1308         if (!result) {
  1309             if (errno != ERANGE)
  1310                 return false;
  1311             // need to make the buffer bigger
  1312             bufsize *= 2;
  1315     // size back down to the actual string length
  1316     cwd.SetLength(strlen(result) + 1);
  1317     cwd.Replace(cwd.Length() - 1, 1, '/');
  1318     workingDirectory = NS_ConvertUTF8toUTF16(cwd);
  1319 #endif
  1320     return true;
  1323 static JSSecurityCallbacks shellSecurityCallbacks;
  1325 int
  1326 XRE_XPCShellMain(int argc, char **argv, char **envp)
  1328     JSRuntime *rt;
  1329     JSContext *cx;
  1330     int result;
  1331     nsresult rv;
  1333     gErrFile = stderr;
  1334     gOutFile = stdout;
  1335     gInFile = stdin;
  1337     NS_LogInit();
  1339     nsCOMPtr<nsIFile> appFile;
  1340     rv = XRE_GetBinaryPath(argv[0], getter_AddRefs(appFile));
  1341     if (NS_FAILED(rv)) {
  1342         printf("Couldn't find application file.\n");
  1343         return 1;
  1345     nsCOMPtr<nsIFile> appDir;
  1346     rv = appFile->GetParent(getter_AddRefs(appDir));
  1347     if (NS_FAILED(rv)) {
  1348         printf("Couldn't get application directory.\n");
  1349         return 1;
  1352     XPCShellDirProvider dirprovider;
  1354     dirprovider.SetAppFile(appFile);
  1356     nsCOMPtr<nsIFile> greDir;
  1357     if (argc > 1 && !strcmp(argv[1], "-g")) {
  1358         if (argc < 3)
  1359             return usage();
  1361         rv = XRE_GetFileFromPath(argv[2], getter_AddRefs(greDir));
  1362         if (NS_FAILED(rv)) {
  1363             printf("Couldn't use given GRE dir.\n");
  1364             return 1;
  1367         dirprovider.SetGREDir(greDir);
  1369         argc -= 2;
  1370         argv += 2;
  1371     } else {
  1372         nsAutoString workingDir;
  1373         if (!GetCurrentWorkingDirectory(workingDir)) {
  1374             printf("GetCurrentWorkingDirectory failed.\n");
  1375             return 1;
  1377         rv = NS_NewLocalFile(workingDir, true, getter_AddRefs(greDir));
  1378         if (NS_FAILED(rv)) {
  1379             printf("NS_NewLocalFile failed.\n");
  1380             return 1;
  1384     if (argc > 1 && !strcmp(argv[1], "-a")) {
  1385         if (argc < 3)
  1386             return usage();
  1388         nsCOMPtr<nsIFile> dir;
  1389         rv = XRE_GetFileFromPath(argv[2], getter_AddRefs(dir));
  1390         if (NS_SUCCEEDED(rv)) {
  1391             appDir = do_QueryInterface(dir, &rv);
  1392             dirprovider.SetAppDir(appDir);
  1394         if (NS_FAILED(rv)) {
  1395             printf("Couldn't use given appdir.\n");
  1396             return 1;
  1398         argc -= 2;
  1399         argv += 2;
  1402     while (argc > 1 && !strcmp(argv[1], "-r")) {
  1403         if (argc < 3)
  1404             return usage();
  1406         nsCOMPtr<nsIFile> lf;
  1407         rv = XRE_GetFileFromPath(argv[2], getter_AddRefs(lf));
  1408         if (NS_FAILED(rv)) {
  1409             printf("Couldn't get manifest file.\n");
  1410             return 1;
  1412         XRE_AddManifestLocation(NS_COMPONENT_LOCATION, lf);
  1414         argc -= 2;
  1415         argv += 2;
  1418 #ifdef MOZ_CRASHREPORTER
  1419     const char *val = getenv("MOZ_CRASHREPORTER");
  1420     if (val && *val) {
  1421         rv = CrashReporter::SetExceptionHandler(greDir, true);
  1422         if (NS_FAILED(rv)) {
  1423             printf("CrashReporter::SetExceptionHandler failed!\n");
  1424             return 1;
  1426         MOZ_ASSERT(CrashReporter::GetEnabled());
  1428 #endif
  1431         if (argc > 1 && !strcmp(argv[1], "--greomni")) {
  1432             nsCOMPtr<nsIFile> greOmni;
  1433             nsCOMPtr<nsIFile> appOmni;
  1434             XRE_GetFileFromPath(argv[2], getter_AddRefs(greOmni));
  1435             if (argc > 3 && !strcmp(argv[3], "--appomni")) {
  1436                 XRE_GetFileFromPath(argv[4], getter_AddRefs(appOmni));
  1437                 argc-=2;
  1438                 argv+=2;
  1439             } else {
  1440                 appOmni = greOmni;
  1443             XRE_InitOmnijar(greOmni, appOmni);
  1444             argc-=2;
  1445             argv+=2;
  1448         nsCOMPtr<nsIServiceManager> servMan;
  1449         rv = NS_InitXPCOM2(getter_AddRefs(servMan), appDir, &dirprovider);
  1450         if (NS_FAILED(rv)) {
  1451             printf("NS_InitXPCOM2 failed!\n");
  1452             return 1;
  1455         nsCOMPtr<nsIJSRuntimeService> rtsvc = do_GetService("@mozilla.org/js/xpc/RuntimeService;1");
  1456         // get the JSRuntime from the runtime svc
  1457         if (!rtsvc) {
  1458             printf("failed to get nsJSRuntimeService!\n");
  1459             return 1;
  1462         if (NS_FAILED(rtsvc->GetRuntime(&rt)) || !rt) {
  1463             printf("failed to get JSRuntime from nsJSRuntimeService!\n");
  1464             return 1;
  1467         rtsvc->RegisterContextCallback(ContextCallback);
  1469         // Override the default XPConnect interrupt callback. We could store the
  1470         // old one and restore it before shutting down, but there's not really a
  1471         // reason to bother.
  1472         sScriptedInterruptCallback.construct(rt, UndefinedValue());
  1473         JS_SetInterruptCallback(rt, XPCShellInterruptCallback);
  1475         cx = JS_NewContext(rt, 8192);
  1476         if (!cx) {
  1477             printf("JS_NewContext failed!\n");
  1478             return 1;
  1481         argc--;
  1482         argv++;
  1483         ProcessArgsForCompartment(cx, argv, argc);
  1485         nsCOMPtr<nsIXPConnect> xpc = do_GetService(nsIXPConnect::GetCID());
  1486         if (!xpc) {
  1487             printf("failed to get nsXPConnect service!\n");
  1488             return 1;
  1491         nsCOMPtr<nsIPrincipal> systemprincipal;
  1492         // Fetch the system principal and store it away in a global, to use for
  1493         // script compilation in Load() and ProcessFile() (including interactive
  1494         // eval loop)
  1497             nsCOMPtr<nsIScriptSecurityManager> securityManager =
  1498                 do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
  1499             if (NS_SUCCEEDED(rv) && securityManager) {
  1500                 rv = securityManager->GetSystemPrincipal(getter_AddRefs(systemprincipal));
  1501                 if (NS_FAILED(rv)) {
  1502                     fprintf(gErrFile, "+++ Failed to obtain SystemPrincipal from ScriptSecurityManager service.\n");
  1503                 } else {
  1504                     // fetch the JS principals and stick in a global
  1505                     gJSPrincipals = nsJSPrincipals::get(systemprincipal);
  1506                     JS_HoldPrincipals(gJSPrincipals);
  1508             } else {
  1509                 fprintf(gErrFile, "+++ Failed to get ScriptSecurityManager service, running without principals");
  1513         const JSSecurityCallbacks *scb = JS_GetSecurityCallbacks(rt);
  1514         MOZ_ASSERT(scb, "We are assuming that nsScriptSecurityManager::Init() has been run");
  1515         shellSecurityCallbacks = *scb;
  1516         JS_SetSecurityCallbacks(rt, &shellSecurityCallbacks);
  1518 #ifdef TEST_TranslateThis
  1519         nsCOMPtr<nsIXPCFunctionThisTranslator>
  1520             translator(new nsXPCFunctionThisTranslator);
  1521         xpc->SetFunctionThisTranslator(NS_GET_IID(nsITestXPCFunctionCallback), translator);
  1522 #endif
  1524         nsCxPusher pusher;
  1525         pusher.Push(cx);
  1527         nsRefPtr<BackstagePass> backstagePass;
  1528         rv = NS_NewBackstagePass(getter_AddRefs(backstagePass));
  1529         if (NS_FAILED(rv)) {
  1530             fprintf(gErrFile, "+++ Failed to create BackstagePass: %8x\n",
  1531                     static_cast<uint32_t>(rv));
  1532             return 1;
  1535         // Make the default XPCShell global use a fresh zone (rather than the
  1536         // System Zone) to improve cross-zone test coverage.
  1537         JS::CompartmentOptions options;
  1538         options.setZone(JS::FreshZone)
  1539                .setVersion(JSVERSION_LATEST);
  1540         nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
  1541         rv = xpc->InitClassesWithNewWrappedGlobal(cx,
  1542                                                   static_cast<nsIGlobalObject *>(backstagePass),
  1543                                                   systemprincipal,
  1544                                                   0,
  1545                                                   options,
  1546                                                   getter_AddRefs(holder));
  1547         if (NS_FAILED(rv))
  1548             return 1;
  1551             JS::Rooted<JSObject*> glob(cx, holder->GetJSObject());
  1552             if (!glob) {
  1553                 return 1;
  1556             // Even if we're building in a configuration where source is
  1557             // discarded, there's no reason to do that on XPCShell, and doing so
  1558             // might break various automation scripts.
  1559             JS::CompartmentOptionsRef(glob).setDiscardSource(false);
  1561             backstagePass->SetGlobalObject(glob);
  1563             JSAutoCompartment ac(cx, glob);
  1565             if (!JS_InitReflect(cx, glob)) {
  1566                 JS_EndRequest(cx);
  1567                 return 1;
  1570             if (!JS_DefineFunctions(cx, glob, glob_functions) ||
  1571                 !JS_DefineProfilingFunctions(cx, glob)) {
  1572                 JS_EndRequest(cx);
  1573                 return 1;
  1576             JS::Rooted<JSObject*> envobj(cx);
  1577             envobj = JS_DefineObject(cx, glob, "environment", &env_class, nullptr, 0);
  1578             if (!envobj) {
  1579                 JS_EndRequest(cx);
  1580                 return 1;
  1583             JS_SetPrivate(envobj, envp);
  1585             nsAutoString workingDirectory;
  1586             if (GetCurrentWorkingDirectory(workingDirectory))
  1587                 gWorkingDirectory = &workingDirectory;
  1589             JS_DefineProperty(cx, glob, "__LOCATION__", JS::UndefinedHandleValue, 0,
  1590                               GetLocationProperty, nullptr);
  1592             result = ProcessArgs(cx, glob, argv, argc, &dirprovider);
  1594             JS_DropPrincipals(rt, gJSPrincipals);
  1595             JS_SetAllNonReservedSlotsToUndefined(cx, glob);
  1596             JS_GC(rt);
  1598         pusher.Pop();
  1599         JS_GC(rt);
  1600         JS_DestroyContext(cx);
  1601     } // this scopes the nsCOMPtrs
  1603     if (!XRE_ShutdownTestShell())
  1604         NS_ERROR("problem shutting down testshell");
  1606     // no nsCOMPtrs are allowed to be alive when you call NS_ShutdownXPCOM
  1607     rv = NS_ShutdownXPCOM( nullptr );
  1608     MOZ_ASSERT(NS_SUCCEEDED(rv), "NS_ShutdownXPCOM failed");
  1610     sScriptedInterruptCallback.destroyIfConstructed();
  1612 #ifdef TEST_CALL_ON_WRAPPED_JS_AFTER_SHUTDOWN
  1613     // test of late call and release (see above)
  1614     JSContext* bogusCX;
  1615     bogus->Peek(&bogusCX);
  1616     bogus = nullptr;
  1617 #endif
  1619     appDir = nullptr;
  1620     appFile = nullptr;
  1621     dirprovider.ClearGREDir();
  1622     dirprovider.ClearAppDir();
  1623     dirprovider.ClearPluginDir();
  1624     dirprovider.ClearAppFile();
  1626 #ifdef MOZ_CRASHREPORTER
  1627     // Shut down the crashreporter service to prevent leaking some strings it holds.
  1628     if (CrashReporter::GetEnabled())
  1629         CrashReporter::UnsetExceptionHandler();
  1630 #endif
  1632     NS_LogTerm();
  1634     return result;
  1637 void
  1638 XPCShellDirProvider::SetGREDir(nsIFile* greDir)
  1640     mGREDir = greDir;
  1643 void
  1644 XPCShellDirProvider::SetAppFile(nsIFile* appFile)
  1646     mAppFile = appFile;
  1649 void
  1650 XPCShellDirProvider::SetAppDir(nsIFile* appDir)
  1652     mAppDir = appDir;
  1655 void
  1656 XPCShellDirProvider::SetPluginDir(nsIFile* pluginDir)
  1658     mPluginDir = pluginDir;
  1661 NS_IMETHODIMP_(MozExternalRefCountType)
  1662 XPCShellDirProvider::AddRef()
  1664     return 2;
  1667 NS_IMETHODIMP_(MozExternalRefCountType)
  1668 XPCShellDirProvider::Release()
  1670     return 1;
  1673 NS_IMPL_QUERY_INTERFACE(XPCShellDirProvider,
  1674                         nsIDirectoryServiceProvider,
  1675                         nsIDirectoryServiceProvider2)
  1677 NS_IMETHODIMP
  1678 XPCShellDirProvider::GetFile(const char *prop, bool *persistent,
  1679                              nsIFile* *result)
  1681     if (mGREDir && !strcmp(prop, NS_GRE_DIR)) {
  1682         *persistent = true;
  1683         return mGREDir->Clone(result);
  1684     } else if (mAppFile && !strcmp(prop, XRE_EXECUTABLE_FILE)) {
  1685         *persistent = true;
  1686         return mAppFile->Clone(result);
  1687     } else if (mGREDir && !strcmp(prop, NS_APP_PREF_DEFAULTS_50_DIR)) {
  1688         nsCOMPtr<nsIFile> file;
  1689         *persistent = true;
  1690         if (NS_FAILED(mGREDir->Clone(getter_AddRefs(file))) ||
  1691             NS_FAILED(file->AppendNative(NS_LITERAL_CSTRING("defaults"))) ||
  1692             NS_FAILED(file->AppendNative(NS_LITERAL_CSTRING("pref"))))
  1693             return NS_ERROR_FAILURE;
  1694         file.forget(result);
  1695         return NS_OK;
  1698     return NS_ERROR_FAILURE;
  1701 NS_IMETHODIMP
  1702 XPCShellDirProvider::GetFiles(const char *prop, nsISimpleEnumerator* *result)
  1704     if (mGREDir && !strcmp(prop, "ChromeML")) {
  1705         nsCOMArray<nsIFile> dirs;
  1707         nsCOMPtr<nsIFile> file;
  1708         mGREDir->Clone(getter_AddRefs(file));
  1709         file->AppendNative(NS_LITERAL_CSTRING("chrome"));
  1710         dirs.AppendObject(file);
  1712         nsresult rv = NS_GetSpecialDirectory(NS_APP_CHROME_DIR,
  1713                                              getter_AddRefs(file));
  1714         if (NS_SUCCEEDED(rv))
  1715             dirs.AppendObject(file);
  1717         return NS_NewArrayEnumerator(result, dirs);
  1718     } else if (!strcmp(prop, NS_APP_PREFS_DEFAULTS_DIR_LIST)) {
  1719         nsCOMArray<nsIFile> dirs;
  1720         nsCOMPtr<nsIFile> appDir;
  1721         bool exists;
  1722         if (mAppDir &&
  1723             NS_SUCCEEDED(mAppDir->Clone(getter_AddRefs(appDir))) &&
  1724             NS_SUCCEEDED(appDir->AppendNative(NS_LITERAL_CSTRING("defaults"))) &&
  1725             NS_SUCCEEDED(appDir->AppendNative(NS_LITERAL_CSTRING("preferences"))) &&
  1726             NS_SUCCEEDED(appDir->Exists(&exists)) && exists) {
  1727             dirs.AppendObject(appDir);
  1728             return NS_NewArrayEnumerator(result, dirs);
  1730         return NS_ERROR_FAILURE;
  1731     } else if (!strcmp(prop, NS_APP_PLUGINS_DIR_LIST)) {
  1732         nsCOMArray<nsIFile> dirs;
  1733         // Add the test plugin location passed in by the caller or through
  1734         // runxpcshelltests.
  1735         if (mPluginDir) {
  1736             dirs.AppendObject(mPluginDir);
  1737         // If there was no path specified, default to the one set up by automation
  1738         } else {
  1739             nsCOMPtr<nsIFile> file;
  1740             bool exists;
  1741             // We have to add this path, buildbot copies the test plugin directory
  1742             // to (app)/bin when unpacking test zips.
  1743             if (mGREDir) {
  1744                 mGREDir->Clone(getter_AddRefs(file));
  1745                 if (NS_SUCCEEDED(mGREDir->Clone(getter_AddRefs(file)))) {
  1746                     file->AppendNative(NS_LITERAL_CSTRING("plugins"));
  1747                     if (NS_SUCCEEDED(file->Exists(&exists)) && exists) {
  1748                         dirs.AppendObject(file);
  1753         return NS_NewArrayEnumerator(result, dirs);
  1755     return NS_ERROR_FAILURE;

mercurial