js/xpconnect/src/XPCShellImpl.cpp

changeset 0
6474c204b198
equal deleted inserted replaced
-1:000000000000 0:3617641b1018
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/. */
6
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"
35
36 #ifdef ANDROID
37 #include <android/log.h>
38 #endif
39
40 #ifdef XP_WIN
41 #include <windows.h>
42 #endif
43
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
53
54 #ifdef MOZ_CRASHREPORTER
55 #include "nsExceptionHandler.h"
56 #include "nsICrashReporter.h"
57 #endif
58
59 using namespace mozilla;
60 using namespace JS;
61
62 class XPCShellDirProvider : public nsIDirectoryServiceProvider2
63 {
64 public:
65 NS_DECL_ISUPPORTS_INHERITED
66 NS_DECL_NSIDIRECTORYSERVICEPROVIDER
67 NS_DECL_NSIDIRECTORYSERVICEPROVIDER2
68
69 XPCShellDirProvider() { }
70 ~XPCShellDirProvider() { }
71
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; }
84
85 private:
86 nsCOMPtr<nsIFile> mGREDir;
87 nsCOMPtr<nsIFile> mAppDir;
88 nsCOMPtr<nsIFile> mPluginDir;
89 nsCOMPtr<nsIFile> mAppFile;
90 };
91
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
99
100 static const char kXPConnectServiceContractID[] = "@mozilla.org/js/xpc/XPConnect;1";
101
102 #define EXITCODE_RUNTIME_ERROR 3
103 #define EXITCODE_FILE_NOT_FOUND 4
104
105 static FILE *gOutFile = nullptr;
106 static FILE *gErrFile = nullptr;
107 static FILE *gInFile = nullptr;
108
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;
114
115 static JSPrincipals *gJSPrincipals = nullptr;
116 static nsAutoString *gWorkingDirectory = nullptr;
117
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);
130
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);
142
143 // replace forward slashes with backslashes,
144 // since nsLocalFileWin chokes on them
145 char16_t* start = filenameString.BeginWriting();
146 char16_t* end = filenameString.EndWriting();
147
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
156
157 nsCOMPtr<nsIFile> location;
158 if (NS_SUCCEEDED(rv)) {
159 rv = NS_NewLocalFile(filenameString,
160 false, getter_AddRefs(location));
161 }
162
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);
168
169 rv = NS_NewLocalFile(absolutePath,
170 false, getter_AddRefs(location));
171 }
172
173 if (location) {
174 nsCOMPtr<nsIXPConnectJSObjectHolder> locationHolder;
175
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));
184
185 if (NS_SUCCEEDED(rv) &&
186 locationHolder->GetJSObject()) {
187 vp.set(OBJECT_TO_JSVAL(locationHolder->GetJSObject()));
188 }
189 }
190 }
191
192 return true;
193 #endif
194 }
195
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 }
208
209 static bool
210 ReadLine(JSContext *cx, unsigned argc, jsval *vp)
211 {
212 CallArgs args = CallArgsFromVp(argc, vp);
213
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);
218
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 }
227
228 /* Get a line from the infile */
229 JSAutoByteString strBytes(cx, str);
230 if (!strBytes || !GetLine(cx, buf, gInFile, strBytes.ptr()))
231 return false;
232
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 }
243
244 /* Turn buf into a JSString */
245 str = JS_NewStringCopyN(cx, buf, buflen);
246 if (!str)
247 return false;
248
249 args.rval().setString(str);
250 return true;
251 }
252
253 static bool
254 Print(JSContext *cx, unsigned argc, jsval *vp)
255 {
256 CallArgs args = CallArgsFromVp(argc, vp);
257 args.rval().setUndefined();
258
259 RootedString str(cx);
260 nsAutoCString utf8str;
261 size_t length;
262 const jschar *chars;
263
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;
271
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 }
283
284 static bool
285 Dump(JSContext *cx, unsigned argc, jsval *vp)
286 {
287 CallArgs args = CallArgsFromVp(argc, vp);
288 args.rval().setUndefined();
289
290 if (!args.length())
291 return true;
292
293 RootedString str(cx, ToString(cx, args[0]));
294 if (!str)
295 return false;
296
297 size_t length;
298 const jschar *chars = JS_GetStringCharsAndLength(cx, str, &length);
299 if (!chars)
300 return false;
301
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 }
316
317 static bool
318 Load(JSContext *cx, unsigned argc, jsval *vp)
319 {
320 CallArgs args = CallArgsFromVp(argc, vp);
321
322 JS::Rooted<JSObject*> obj(cx, JS_THIS_OBJECT(cx, vp));
323 if (!obj)
324 return false;
325
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;
347
348 if (!compileOnly && !JS_ExecuteScript(cx, obj, script))
349 return false;
350 }
351 args.rval().setUndefined();
352 return true;
353 }
354
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 }
365
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 }
374
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);
380
381 gQuitting = true;
382 // exit(0);
383 return false;
384 }
385
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 }
400
401 static bool
402 DumpXPC(JSContext *cx, unsigned argc, jsval *vp)
403 {
404 JS::CallArgs args = CallArgsFromVp(argc, vp);
405
406 uint16_t depth = 2;
407 if (args.length() > 0) {
408 if (!JS::ToUint16(cx, args[0], &depth))
409 return false;
410 }
411
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 }
418
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 }
431
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;
440
441 JS_SetGCZeal(cx, uint8_t(zeal), JS_DEFAULT_ZEAL_FREQ);
442 args.rval().setUndefined();
443 return true;
444 }
445 #endif
446
447 static bool
448 SendCommand(JSContext *cx, unsigned argc, Value *vp)
449 {
450 CallArgs args = CallArgsFromVp(argc, vp);
451
452 if (args.length() == 0) {
453 JS_ReportError(cx, "Function takes at least one argument!");
454 return false;
455 }
456
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 }
462
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 }
467
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 }
472
473 args.rval().setUndefined();
474 return true;
475 }
476
477 static bool
478 Options(JSContext *cx, unsigned argc, jsval *vp)
479 {
480 JS::CallArgs args = CallArgsFromVp(argc, vp);
481 ContextOptions oldOptions = ContextOptionsRef(cx);
482
483 for (unsigned i = 0; i < args.length(); ++i) {
484 JSString *str = ToString(cx, args[i]);
485 if (!str)
486 return false;
487
488 JSAutoByteString opt(cx, str);
489 if (!opt)
490 return false;
491
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 }
504
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 }
527
528 JSString *str = JS_NewStringCopyZ(cx, names);
529 free(names);
530 if (!str)
531 return false;
532
533 args.rval().setString(str);
534 return true;
535 }
536
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 }
545
546 Value v = args[0];
547 if (JSVAL_IS_PRIMITIVE(v)) {
548 JS_ReportError(cx, "Only objects have parents!");
549 return false;
550 }
551
552 args.rval().setObjectOrNull(JS_GetParent(&v.toObject()));
553 return true;
554 }
555
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;
562
563 return xpc::Base64Decode(cx, args[0], args.rval());
564 }
565
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;
572
573 return xpc::Base64Encode(cx, args[0], args.rval());
574 }
575
576 static bool
577 Blob(JSContext *cx, unsigned argc, Value *vp)
578 {
579 JS::CallArgs args = CallArgsFromVp(argc, vp);
580
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 }
587
588 nsCOMPtr<nsIJSNativeInitializer> initializer = do_QueryInterface(native);
589 MOZ_ASSERT(initializer);
590
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 }
596
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 }
602
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 }
611
612 return true;
613 }
614
615 static bool
616 File(JSContext *cx, unsigned argc, Value *vp)
617 {
618 JS::CallArgs args = CallArgsFromVp(argc, vp);
619
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 }
626
627 nsCOMPtr<nsIJSNativeInitializer> initializer = do_QueryInterface(native);
628 MOZ_ASSERT(initializer);
629
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 }
635
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 }
641
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 }
650
651 return true;
652 }
653
654 static Maybe<PersistentRootedValue> sScriptedInterruptCallback;
655
656 static bool
657 XPCShellInterruptCallback(JSContext *cx)
658 {
659 MOZ_ASSERT(!sScriptedInterruptCallback.empty());
660 RootedValue callback(cx, sScriptedInterruptCallback.ref());
661
662 // If no interrupt callback was set by script, no-op.
663 if (callback.isUndefined())
664 return true;
665
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 }
675
676 return rv.toBoolean();
677 }
678
679 static bool
680 SetInterruptCallback(JSContext *cx, unsigned argc, jsval *vp)
681 {
682 MOZ_ASSERT(!sScriptedInterruptCallback.empty());
683
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 }
690
691 // Allow callers to remove the interrupt callback by passing undefined.
692 if (args[0].isUndefined()) {
693 sScriptedInterruptCallback.ref() = UndefinedValue();
694 return true;
695 }
696
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 }
702
703 sScriptedInterruptCallback.ref() = args[0];
704
705 return true;
706 }
707
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 }
720
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 };
746
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;
755
756 RootedValue idval(cx);
757 if (!JS_IdToValue(cx, id, &idval))
758 return false;
759
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 }
800
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;
808
809 if (reflected)
810 return true;
811
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 }
823
824 reflected = true;
825 return true;
826 }
827
828 static bool
829 env_resolve(JSContext *cx, HandleObject obj, HandleId id,
830 JS::MutableHandleObject objp)
831 {
832 JSString *idstr, *valstr;
833
834 RootedValue idval(cx);
835 if (!JS_IdToValue(cx, id, &idval))
836 return false;
837
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 }
857
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 };
865
866 /***************************************************************************/
867
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;
875
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 };
882
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;
888
889 return &jsShell_ErrorFormatString[errorNumber];
890 }
891
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;
902
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);
924
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);
932
933 return;
934 }
935
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';
942
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)));
958
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;
967
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);
985
986 fprintf(gOutFile, "\n");
987 }
988
989 static void
990 Process(JSContext *cx, JS::Handle<JSObject*> obj, const char *filename, bool forceTTY)
991 {
992 FILE *file;
993
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;
1004 }
1005 }
1006
1007 ProcessFile(cx, obj, filename, file, forceTTY);
1008 if (file != stdin)
1009 fclose(file);
1010 }
1011
1012 static int
1013 usage(void)
1014 {
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;
1018 }
1019
1020 static void
1021 ProcessArgsForCompartment(JSContext *cx, char **argv, int argc)
1022 {
1023 for (int i = 0; i < argc; i++) {
1024 if (argv[i][0] != '-' || argv[i][1] == '\0')
1025 break;
1026
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;
1043 }
1044 }
1045 }
1046
1047 static int
1048 ProcessArgs(JSContext *cx, JS::Handle<JSObject*> obj, char **argv, int argc, XPCShellDirProvider* aDirProvider)
1049 {
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;
1057
1058 rcfile = fopen(rcfilename, "r");
1059 if (rcfile) {
1060 printf("[loading '%s'...]\n", rcfilename);
1061 ProcessFile(cx, obj, rcfilename, rcfile, false);
1062 fclose(rcfile);
1063 }
1064
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;
1075 }
1076 switch (argv[i][1]) {
1077 case 'v':
1078 case 'f':
1079 case 'e':
1080 ++i;
1081 break;
1082 default:;
1083 }
1084 }
1085
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;
1095
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;
1103 }
1104 }
1105
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;
1111 }
1112 switch (argv[i][1]) {
1113 case 'v':
1114 if (++i == argc) {
1115 return usage();
1116 }
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();
1134 }
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':
1147 {
1148 RootedValue rval(cx);
1149
1150 if (++i == argc) {
1151 return usage();
1152 }
1153
1154 JS_EvaluateScript(cx, obj, argv[i], strlen(argv[i]), "-e", 1, &rval);
1155
1156 isInteractive = false;
1157 break;
1158 }
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':
1170 {
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();
1177 }
1178 aDirProvider->SetPluginDir(pluginsDir);
1179 break;
1180 }
1181 default:
1182 return usage();
1183 }
1184 }
1185
1186 if (filename || isInteractive)
1187 Process(cx, obj, filename, forceTTY);
1188
1189 return gExitCode;
1190 }
1191
1192 /***************************************************************************/
1193
1194 // #define TEST_InitClassesWithNewWrappedGlobal
1195
1196 #ifdef TEST_InitClassesWithNewWrappedGlobal
1197 // XXX hacky test code...
1198 #include "xpctest.h"
1199
1200 class TestGlobal : public nsIXPCTestNoisy, public nsIXPCScriptable
1201 {
1202 public:
1203 NS_DECL_ISUPPORTS
1204 NS_DECL_NSIXPCTESTNOISY
1205 NS_DECL_NSIXPCSCRIPTABLE
1206
1207 TestGlobal(){}
1208 };
1209
1210 NS_IMPL_ISUPPORTS(TestGlobal, nsIXPCTestNoisy, nsIXPCScriptable)
1211
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 */
1219
1220 NS_IMETHODIMP TestGlobal::Squawk() {return NS_OK;}
1221
1222 #endif
1223
1224 // uncomment to install the test 'this' translator
1225 // #define TEST_TranslateThis
1226
1227 #ifdef TEST_TranslateThis
1228
1229 #include "xpctest.h"
1230
1231 class nsXPCFunctionThisTranslator : public nsIXPCFunctionThisTranslator
1232 {
1233 public:
1234 NS_DECL_ISUPPORTS
1235 NS_DECL_NSIXPCFUNCTIONTHISTRANSLATOR
1236
1237 nsXPCFunctionThisTranslator();
1238 virtual ~nsXPCFunctionThisTranslator();
1239 /* additional members */
1240 };
1241
1242 /* Implementation file */
1243 NS_IMPL_ISUPPORTS(nsXPCFunctionThisTranslator, nsIXPCFunctionThisTranslator)
1244
1245 nsXPCFunctionThisTranslator::nsXPCFunctionThisTranslator()
1246 {
1247 }
1248
1249 nsXPCFunctionThisTranslator::~nsXPCFunctionThisTranslator()
1250 {
1251 }
1252
1253 /* nsISupports TranslateThis (in nsISupports aInitialThis); */
1254 NS_IMETHODIMP
1255 nsXPCFunctionThisTranslator::TranslateThis(nsISupports *aInitialThis,
1256 nsISupports **_retval)
1257 {
1258 nsCOMPtr<nsISupports> temp = aInitialThis;
1259 temp.forget(_retval);
1260 return NS_OK;
1261 }
1262
1263 #endif
1264
1265 static void
1266 XPCShellErrorReporter(JSContext *cx, const char *message, JSErrorReport *rep)
1267 {
1268 if (gIgnoreReportedErrors)
1269 return;
1270
1271 if (!JSREPORT_IS_WARNING(rep->flags))
1272 gExitCode = EXITCODE_RUNTIME_ERROR;
1273
1274 // Delegate to the system error reporter for heavy lifting.
1275 xpc::SystemErrorReporter(cx, message, rep);
1276 }
1277
1278 static bool
1279 ContextCallback(JSContext *cx, unsigned contextOp)
1280 {
1281 if (contextOp == JSCONTEXT_NEW)
1282 JS_SetErrorReporter(cx, XPCShellErrorReporter);
1283 return true;
1284 }
1285
1286 static bool
1287 GetCurrentWorkingDirectory(nsAString& workingDirectory)
1288 {
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;
1313 }
1314 }
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;
1321 }
1322
1323 static JSSecurityCallbacks shellSecurityCallbacks;
1324
1325 int
1326 XRE_XPCShellMain(int argc, char **argv, char **envp)
1327 {
1328 JSRuntime *rt;
1329 JSContext *cx;
1330 int result;
1331 nsresult rv;
1332
1333 gErrFile = stderr;
1334 gOutFile = stdout;
1335 gInFile = stdin;
1336
1337 NS_LogInit();
1338
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;
1344 }
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;
1350 }
1351
1352 XPCShellDirProvider dirprovider;
1353
1354 dirprovider.SetAppFile(appFile);
1355
1356 nsCOMPtr<nsIFile> greDir;
1357 if (argc > 1 && !strcmp(argv[1], "-g")) {
1358 if (argc < 3)
1359 return usage();
1360
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;
1365 }
1366
1367 dirprovider.SetGREDir(greDir);
1368
1369 argc -= 2;
1370 argv += 2;
1371 } else {
1372 nsAutoString workingDir;
1373 if (!GetCurrentWorkingDirectory(workingDir)) {
1374 printf("GetCurrentWorkingDirectory failed.\n");
1375 return 1;
1376 }
1377 rv = NS_NewLocalFile(workingDir, true, getter_AddRefs(greDir));
1378 if (NS_FAILED(rv)) {
1379 printf("NS_NewLocalFile failed.\n");
1380 return 1;
1381 }
1382 }
1383
1384 if (argc > 1 && !strcmp(argv[1], "-a")) {
1385 if (argc < 3)
1386 return usage();
1387
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);
1393 }
1394 if (NS_FAILED(rv)) {
1395 printf("Couldn't use given appdir.\n");
1396 return 1;
1397 }
1398 argc -= 2;
1399 argv += 2;
1400 }
1401
1402 while (argc > 1 && !strcmp(argv[1], "-r")) {
1403 if (argc < 3)
1404 return usage();
1405
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;
1411 }
1412 XRE_AddManifestLocation(NS_COMPONENT_LOCATION, lf);
1413
1414 argc -= 2;
1415 argv += 2;
1416 }
1417
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;
1425 }
1426 MOZ_ASSERT(CrashReporter::GetEnabled());
1427 }
1428 #endif
1429
1430 {
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;
1441 }
1442
1443 XRE_InitOmnijar(greOmni, appOmni);
1444 argc-=2;
1445 argv+=2;
1446 }
1447
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;
1453 }
1454
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;
1460 }
1461
1462 if (NS_FAILED(rtsvc->GetRuntime(&rt)) || !rt) {
1463 printf("failed to get JSRuntime from nsJSRuntimeService!\n");
1464 return 1;
1465 }
1466
1467 rtsvc->RegisterContextCallback(ContextCallback);
1468
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);
1474
1475 cx = JS_NewContext(rt, 8192);
1476 if (!cx) {
1477 printf("JS_NewContext failed!\n");
1478 return 1;
1479 }
1480
1481 argc--;
1482 argv++;
1483 ProcessArgsForCompartment(cx, argv, argc);
1484
1485 nsCOMPtr<nsIXPConnect> xpc = do_GetService(nsIXPConnect::GetCID());
1486 if (!xpc) {
1487 printf("failed to get nsXPConnect service!\n");
1488 return 1;
1489 }
1490
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)
1495 {
1496
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);
1507 }
1508 } else {
1509 fprintf(gErrFile, "+++ Failed to get ScriptSecurityManager service, running without principals");
1510 }
1511 }
1512
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);
1517
1518 #ifdef TEST_TranslateThis
1519 nsCOMPtr<nsIXPCFunctionThisTranslator>
1520 translator(new nsXPCFunctionThisTranslator);
1521 xpc->SetFunctionThisTranslator(NS_GET_IID(nsITestXPCFunctionCallback), translator);
1522 #endif
1523
1524 nsCxPusher pusher;
1525 pusher.Push(cx);
1526
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;
1533 }
1534
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;
1549
1550 {
1551 JS::Rooted<JSObject*> glob(cx, holder->GetJSObject());
1552 if (!glob) {
1553 return 1;
1554 }
1555
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);
1560
1561 backstagePass->SetGlobalObject(glob);
1562
1563 JSAutoCompartment ac(cx, glob);
1564
1565 if (!JS_InitReflect(cx, glob)) {
1566 JS_EndRequest(cx);
1567 return 1;
1568 }
1569
1570 if (!JS_DefineFunctions(cx, glob, glob_functions) ||
1571 !JS_DefineProfilingFunctions(cx, glob)) {
1572 JS_EndRequest(cx);
1573 return 1;
1574 }
1575
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;
1581 }
1582
1583 JS_SetPrivate(envobj, envp);
1584
1585 nsAutoString workingDirectory;
1586 if (GetCurrentWorkingDirectory(workingDirectory))
1587 gWorkingDirectory = &workingDirectory;
1588
1589 JS_DefineProperty(cx, glob, "__LOCATION__", JS::UndefinedHandleValue, 0,
1590 GetLocationProperty, nullptr);
1591
1592 result = ProcessArgs(cx, glob, argv, argc, &dirprovider);
1593
1594 JS_DropPrincipals(rt, gJSPrincipals);
1595 JS_SetAllNonReservedSlotsToUndefined(cx, glob);
1596 JS_GC(rt);
1597 }
1598 pusher.Pop();
1599 JS_GC(rt);
1600 JS_DestroyContext(cx);
1601 } // this scopes the nsCOMPtrs
1602
1603 if (!XRE_ShutdownTestShell())
1604 NS_ERROR("problem shutting down testshell");
1605
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");
1609
1610 sScriptedInterruptCallback.destroyIfConstructed();
1611
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
1618
1619 appDir = nullptr;
1620 appFile = nullptr;
1621 dirprovider.ClearGREDir();
1622 dirprovider.ClearAppDir();
1623 dirprovider.ClearPluginDir();
1624 dirprovider.ClearAppFile();
1625
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
1631
1632 NS_LogTerm();
1633
1634 return result;
1635 }
1636
1637 void
1638 XPCShellDirProvider::SetGREDir(nsIFile* greDir)
1639 {
1640 mGREDir = greDir;
1641 }
1642
1643 void
1644 XPCShellDirProvider::SetAppFile(nsIFile* appFile)
1645 {
1646 mAppFile = appFile;
1647 }
1648
1649 void
1650 XPCShellDirProvider::SetAppDir(nsIFile* appDir)
1651 {
1652 mAppDir = appDir;
1653 }
1654
1655 void
1656 XPCShellDirProvider::SetPluginDir(nsIFile* pluginDir)
1657 {
1658 mPluginDir = pluginDir;
1659 }
1660
1661 NS_IMETHODIMP_(MozExternalRefCountType)
1662 XPCShellDirProvider::AddRef()
1663 {
1664 return 2;
1665 }
1666
1667 NS_IMETHODIMP_(MozExternalRefCountType)
1668 XPCShellDirProvider::Release()
1669 {
1670 return 1;
1671 }
1672
1673 NS_IMPL_QUERY_INTERFACE(XPCShellDirProvider,
1674 nsIDirectoryServiceProvider,
1675 nsIDirectoryServiceProvider2)
1676
1677 NS_IMETHODIMP
1678 XPCShellDirProvider::GetFile(const char *prop, bool *persistent,
1679 nsIFile* *result)
1680 {
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;
1696 }
1697
1698 return NS_ERROR_FAILURE;
1699 }
1700
1701 NS_IMETHODIMP
1702 XPCShellDirProvider::GetFiles(const char *prop, nsISimpleEnumerator* *result)
1703 {
1704 if (mGREDir && !strcmp(prop, "ChromeML")) {
1705 nsCOMArray<nsIFile> dirs;
1706
1707 nsCOMPtr<nsIFile> file;
1708 mGREDir->Clone(getter_AddRefs(file));
1709 file->AppendNative(NS_LITERAL_CSTRING("chrome"));
1710 dirs.AppendObject(file);
1711
1712 nsresult rv = NS_GetSpecialDirectory(NS_APP_CHROME_DIR,
1713 getter_AddRefs(file));
1714 if (NS_SUCCEEDED(rv))
1715 dirs.AppendObject(file);
1716
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);
1729 }
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);
1749 }
1750 }
1751 }
1752 }
1753 return NS_NewArrayEnumerator(result, dirs);
1754 }
1755 return NS_ERROR_FAILURE;
1756 }

mercurial