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.

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

mercurial