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