Coverage Report

Created: 2018-09-25 14:53

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