1 : /* ***** BEGIN LICENSE BLOCK *****
2 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
3 : *
4 : * The contents of this file are subject to the Mozilla Public License Version
5 : * 1.1 (the "License"); you may not use this file except in compliance with
6 : * the License. You may obtain a copy of the License at
7 : * http://www.mozilla.org/MPL/
8 : *
9 : * Software distributed under the License is distributed on an "AS IS" basis,
10 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11 : * for the specific language governing rights and limitations under the
12 : * License.
13 : *
14 : * The Original Code is Mozilla IPCShell.
15 : *
16 : * The Initial Developer of the Original Code is
17 : * Ben Turner <bent.mozilla@gmail.com>.
18 : * Portions created by the Initial Developer are Copyright (C) 2009
19 : * the Initial Developer. All Rights Reserved.
20 : *
21 : * Contributor(s):
22 : *
23 : * Alternatively, the contents of this file may be used under the terms of
24 : * either the GNU General Public License Version 2 or later (the "GPL"), or
25 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
26 : * in which case the provisions of the GPL or the LGPL are applicable instead
27 : * of those above. If you wish to allow use of your version of this file only
28 : * under the terms of either the GPL or the LGPL, and not to allow others to
29 : * use your version of this file under the terms of the MPL, indicate your
30 : * decision by deleting the provisions above and replace them with the notice
31 : * and other provisions required by the GPL or the LGPL. If you do not delete
32 : * the provisions above, a recipient may use your version of this file under
33 : * the terms of any one of the MPL, the GPL or the LGPL.
34 : *
35 : * ***** END LICENSE BLOCK ***** */
36 :
37 : #include <stdlib.h>
38 : #include <errno.h>
39 : #ifdef HAVE_IO_H
40 : #include <io.h> /* for isatty() */
41 : #endif
42 : #ifdef HAVE_UNISTD_H
43 : #include <unistd.h> /* for isatty() */
44 : #endif
45 :
46 : #include "base/basictypes.h"
47 :
48 : #include "jsapi.h"
49 : #include "jsdbgapi.h"
50 : #include "jsprf.h"
51 :
52 : #include "xpcpublic.h"
53 :
54 : #include "XPCShellEnvironment.h"
55 :
56 : #include "mozilla/XPCOM.h"
57 :
58 : #include "nsIChannel.h"
59 : #include "nsIClassInfo.h"
60 : #include "nsIDirectoryService.h"
61 : #include "nsIJSContextStack.h"
62 : #include "nsIJSRuntimeService.h"
63 : #include "nsIPrincipal.h"
64 : #include "nsIScriptSecurityManager.h"
65 : #include "nsIURI.h"
66 : #include "nsIXPConnect.h"
67 : #include "nsIXPCScriptable.h"
68 :
69 : #include "nsJSUtils.h"
70 : #include "nsJSPrincipals.h"
71 : #include "nsThreadUtils.h"
72 : #include "nsXULAppAPI.h"
73 :
74 : #include "TestShellChild.h"
75 : #include "TestShellParent.h"
76 :
77 : #define EXITCODE_RUNTIME_ERROR 3
78 : #define EXITCODE_FILE_NOT_FOUND 4
79 :
80 : using mozilla::ipc::XPCShellEnvironment;
81 : using mozilla::ipc::TestShellChild;
82 : using mozilla::ipc::TestShellParent;
83 :
84 : namespace {
85 :
86 : static const char kDefaultRuntimeScriptFilename[] = "xpcshell.js";
87 :
88 : class FullTrustSecMan : public nsIScriptSecurityManager
89 : {
90 : public:
91 : NS_DECL_ISUPPORTS
92 : NS_DECL_NSIXPCSECURITYMANAGER
93 : NS_DECL_NSISCRIPTSECURITYMANAGER
94 :
95 0 : FullTrustSecMan() { }
96 0 : virtual ~FullTrustSecMan() { }
97 :
98 0 : void SetSystemPrincipal(nsIPrincipal *aPrincipal) {
99 0 : mSystemPrincipal = aPrincipal;
100 0 : }
101 :
102 : private:
103 : nsCOMPtr<nsIPrincipal> mSystemPrincipal;
104 : };
105 :
106 : class XPCShellDirProvider : public nsIDirectoryServiceProvider
107 : {
108 : public:
109 : NS_DECL_ISUPPORTS
110 : NS_DECL_NSIDIRECTORYSERVICEPROVIDER
111 :
112 : XPCShellDirProvider() { }
113 : ~XPCShellDirProvider() { }
114 :
115 : bool SetGREDir(const char *dir);
116 : void ClearGREDir() { mGREDir = nsnull; }
117 :
118 : private:
119 : nsCOMPtr<nsILocalFile> mGREDir;
120 : };
121 :
122 : inline XPCShellEnvironment*
123 0 : Environment(JSContext* cx)
124 : {
125 : XPCShellEnvironment* env =
126 0 : static_cast<XPCShellEnvironment*>(JS_GetContextPrivate(cx));
127 0 : NS_ASSERTION(env, "Should never be null!");
128 0 : return env;
129 : }
130 :
131 : static void
132 0 : ScriptErrorReporter(JSContext *cx,
133 : const char *message,
134 : JSErrorReport *report)
135 : {
136 : int i, j, k, n;
137 0 : char *prefix = NULL, *tmp;
138 : const char *ctmp;
139 0 : JSStackFrame * fp = nsnull;
140 0 : nsCOMPtr<nsIXPConnect> xpc;
141 :
142 : // Don't report an exception from inner JS frames as the callers may intend
143 : // to handle it.
144 0 : while ((fp = JS_FrameIterator(cx, &fp))) {
145 0 : if (JS_IsScriptFrame(cx, fp)) {
146 : return;
147 : }
148 : }
149 :
150 : // In some cases cx->fp is null here so use XPConnect to tell us about inner
151 : // frames.
152 0 : if ((xpc = do_GetService(nsIXPConnect::GetCID()))) {
153 0 : nsAXPCNativeCallContext *cc = nsnull;
154 0 : xpc->GetCurrentNativeCallContext(&cc);
155 0 : if (cc) {
156 0 : nsAXPCNativeCallContext *prev = cc;
157 0 : while (NS_SUCCEEDED(prev->GetPreviousCallContext(&prev)) && prev) {
158 : PRUint16 lang;
159 0 : if (NS_SUCCEEDED(prev->GetLanguage(&lang)) &&
160 : lang == nsAXPCNativeCallContext::LANG_JS) {
161 : return;
162 : }
163 : }
164 : }
165 : }
166 :
167 0 : if (!report) {
168 0 : fprintf(stderr, "%s\n", message);
169 : return;
170 : }
171 :
172 : /* Conditionally ignore reported warnings. */
173 0 : if (JSREPORT_IS_WARNING(report->flags) &&
174 0 : !Environment(cx)->ShouldReportWarnings()) {
175 : return;
176 : }
177 :
178 0 : if (report->filename)
179 0 : prefix = JS_smprintf("%s:", report->filename);
180 0 : if (report->lineno) {
181 0 : tmp = prefix;
182 0 : prefix = JS_smprintf("%s%u: ", tmp ? tmp : "", report->lineno);
183 0 : JS_free(cx, tmp);
184 : }
185 0 : if (JSREPORT_IS_WARNING(report->flags)) {
186 0 : tmp = prefix;
187 : prefix = JS_smprintf("%s%swarning: ",
188 : tmp ? tmp : "",
189 0 : JSREPORT_IS_STRICT(report->flags) ? "strict " : "");
190 0 : JS_free(cx, tmp);
191 : }
192 :
193 : /* embedded newlines -- argh! */
194 0 : while ((ctmp = strchr(message, '\n')) != 0) {
195 0 : ctmp++;
196 0 : if (prefix) fputs(prefix, stderr);
197 0 : fwrite(message, 1, ctmp - message, stderr);
198 0 : message = ctmp;
199 : }
200 : /* If there were no filename or lineno, the prefix might be empty */
201 0 : if (prefix)
202 0 : fputs(prefix, stderr);
203 0 : fputs(message, stderr);
204 :
205 0 : if (!report->linebuf) {
206 0 : fputc('\n', stderr);
207 0 : goto out;
208 : }
209 :
210 0 : fprintf(stderr, ":\n%s%s\n%s", prefix, report->linebuf, prefix);
211 0 : n = report->tokenptr - report->linebuf;
212 0 : for (i = j = 0; i < n; i++) {
213 0 : if (report->linebuf[i] == '\t') {
214 0 : for (k = (j + 8) & ~7; j < k; j++) {
215 0 : fputc('.', stderr);
216 : }
217 0 : continue;
218 : }
219 0 : fputc('.', stderr);
220 0 : j++;
221 : }
222 0 : fputs("^\n", stderr);
223 : out:
224 0 : if (!JSREPORT_IS_WARNING(report->flags)) {
225 0 : Environment(cx)->SetExitCode(EXITCODE_RUNTIME_ERROR);
226 : }
227 0 : JS_free(cx, prefix);
228 : }
229 :
230 : JSContextCallback gOldContextCallback = NULL;
231 :
232 : static JSBool
233 0 : ContextCallback(JSContext *cx,
234 : unsigned contextOp)
235 : {
236 0 : if (gOldContextCallback && !gOldContextCallback(cx, contextOp))
237 0 : return JS_FALSE;
238 :
239 0 : if (contextOp == JSCONTEXT_NEW) {
240 0 : JS_SetErrorReporter(cx, ScriptErrorReporter);
241 0 : JS_SetVersion(cx, JSVERSION_LATEST);
242 : }
243 0 : return JS_TRUE;
244 : }
245 :
246 : static JSBool
247 0 : Print(JSContext *cx,
248 : unsigned argc,
249 : jsval *vp)
250 : {
251 : unsigned i, n;
252 : JSString *str;
253 :
254 0 : jsval *argv = JS_ARGV(cx, vp);
255 0 : for (i = n = 0; i < argc; i++) {
256 0 : str = JS_ValueToString(cx, argv[i]);
257 0 : if (!str)
258 0 : return JS_FALSE;
259 0 : JSAutoByteString bytes(cx, str);
260 0 : if (!bytes)
261 0 : return JS_FALSE;
262 0 : fprintf(stdout, "%s%s", i ? " " : "", bytes.ptr());
263 0 : fflush(stdout);
264 : }
265 0 : n++;
266 0 : if (n)
267 0 : fputc('\n', stdout);
268 0 : JS_SET_RVAL(cx, vp, JSVAL_VOID);
269 0 : return JS_TRUE;
270 : }
271 :
272 : static JSBool
273 0 : GetLine(char *bufp,
274 : FILE *file,
275 : const char *prompt)
276 : {
277 : char line[256];
278 0 : fputs(prompt, stdout);
279 0 : fflush(stdout);
280 0 : if (!fgets(line, sizeof line, file))
281 0 : return JS_FALSE;
282 0 : strcpy(bufp, line);
283 0 : return JS_TRUE;
284 : }
285 :
286 : static JSBool
287 0 : Dump(JSContext *cx,
288 : unsigned argc,
289 : jsval *vp)
290 : {
291 0 : JS_SET_RVAL(cx, vp, JSVAL_VOID);
292 :
293 : JSString *str;
294 0 : if (!argc)
295 0 : return JS_TRUE;
296 :
297 0 : str = JS_ValueToString(cx, JS_ARGV(cx, vp)[0]);
298 0 : if (!str)
299 0 : return JS_FALSE;
300 0 : JSAutoByteString bytes(cx, str);
301 0 : if (!bytes)
302 0 : return JS_FALSE;
303 :
304 0 : fputs(bytes.ptr(), stdout);
305 0 : fflush(stdout);
306 0 : return JS_TRUE;
307 : }
308 :
309 : static JSBool
310 0 : Load(JSContext *cx,
311 : unsigned argc,
312 : jsval *vp)
313 : {
314 : unsigned i;
315 : JSString *str;
316 : JSScript *script;
317 : jsval result;
318 : FILE *file;
319 :
320 0 : JSObject *obj = JS_THIS_OBJECT(cx, vp);
321 0 : if (!obj)
322 0 : return JS_FALSE;
323 :
324 0 : jsval *argv = JS_ARGV(cx, vp);
325 0 : for (i = 0; i < argc; i++) {
326 0 : str = JS_ValueToString(cx, argv[i]);
327 0 : if (!str)
328 0 : return JS_FALSE;
329 0 : argv[i] = STRING_TO_JSVAL(str);
330 0 : JSAutoByteString filename(cx, str);
331 0 : if (!filename)
332 0 : return JS_FALSE;
333 0 : file = fopen(filename.ptr(), "r");
334 0 : if (!file) {
335 0 : JS_ReportError(cx, "cannot open file '%s' for reading", filename.ptr());
336 0 : return JS_FALSE;
337 : }
338 0 : script = JS_CompileUTF8FileHandleForPrincipals(cx, obj, filename.ptr(), file,
339 0 : Environment(cx)->GetPrincipal());
340 0 : fclose(file);
341 0 : if (!script)
342 0 : return JS_FALSE;
343 :
344 0 : if (!Environment(cx)->ShouldCompileOnly() &&
345 0 : !JS_ExecuteScript(cx, obj, script, &result)) {
346 0 : return JS_FALSE;
347 : }
348 : }
349 0 : JS_SET_RVAL(cx, vp, JSVAL_VOID);
350 0 : return JS_TRUE;
351 : }
352 :
353 : static JSBool
354 0 : Version(JSContext *cx,
355 : unsigned argc,
356 : jsval *vp)
357 : {
358 0 : jsval *argv = JS_ARGV(cx, vp);
359 0 : if (argc > 0 && JSVAL_IS_INT(argv[0]))
360 0 : JS_SET_RVAL(cx, vp, INT_TO_JSVAL(JS_SetVersion(cx, JSVersion(JSVAL_TO_INT(argv[0])))));
361 : else
362 0 : JS_SET_RVAL(cx, vp, INT_TO_JSVAL(JS_GetVersion(cx)));
363 0 : return JS_TRUE;
364 : }
365 :
366 : static JSBool
367 0 : BuildDate(JSContext *cx, unsigned argc, jsval *vp)
368 : {
369 0 : fprintf(stdout, "built on %s at %s\n", __DATE__, __TIME__);
370 0 : return JS_TRUE;
371 : }
372 :
373 : static JSBool
374 0 : Quit(JSContext *cx,
375 : unsigned argc,
376 : jsval *vp)
377 : {
378 0 : int exitCode = 0;
379 0 : JS_ConvertArguments(cx, argc, JS_ARGV(cx, vp), "/ i", &exitCode);
380 :
381 0 : XPCShellEnvironment* env = Environment(cx);
382 0 : env->SetExitCode(exitCode);
383 0 : env->SetIsQuitting();
384 :
385 0 : return JS_FALSE;
386 : }
387 :
388 : static JSBool
389 0 : DumpXPC(JSContext *cx,
390 : unsigned argc,
391 : jsval *vp)
392 : {
393 0 : int32_t depth = 2;
394 :
395 0 : if (argc > 0) {
396 0 : if (!JS_ValueToInt32(cx, JS_ARGV(cx, vp)[0], &depth))
397 0 : return JS_FALSE;
398 : }
399 :
400 0 : nsCOMPtr<nsIXPConnect> xpc = do_GetService(nsIXPConnect::GetCID());
401 0 : if(xpc)
402 0 : xpc->DebugDump(int16_t(depth));
403 0 : JS_SET_RVAL(cx, vp, JSVAL_VOID);
404 0 : return JS_TRUE;
405 : }
406 :
407 : static JSBool
408 0 : GC(JSContext *cx,
409 : unsigned argc,
410 : jsval *vp)
411 : {
412 0 : JS_GC(cx);
413 : #ifdef JS_GCMETER
414 : JSRuntime *rt = JS_GetRuntime(cx);
415 : js_DumpGCStats(rt, stdout);
416 : #endif
417 0 : JS_SET_RVAL(cx, vp, JSVAL_VOID);
418 0 : return JS_TRUE;
419 : }
420 :
421 : #ifdef JS_GC_ZEAL
422 : static JSBool
423 0 : GCZeal(JSContext *cx,
424 : unsigned argc,
425 : jsval *vp)
426 : {
427 0 : jsval* argv = JS_ARGV(cx, vp);
428 :
429 : uint32_t zeal;
430 0 : if (!JS_ValueToECMAUint32(cx, argv[0], &zeal))
431 0 : return JS_FALSE;
432 :
433 0 : JS_SetGCZeal(cx, PRUint8(zeal), JS_DEFAULT_ZEAL_FREQ, JS_FALSE);
434 0 : return JS_TRUE;
435 : }
436 : #endif
437 :
438 : #ifdef DEBUG
439 :
440 : static JSBool
441 0 : DumpHeap(JSContext *cx,
442 : unsigned argc,
443 : jsval *vp)
444 : {
445 0 : JSAutoByteString fileName;
446 0 : void* startThing = NULL;
447 0 : JSGCTraceKind startTraceKind = JSTRACE_OBJECT;
448 0 : void *thingToFind = NULL;
449 0 : size_t maxDepth = (size_t)-1;
450 0 : void *thingToIgnore = NULL;
451 : FILE *dumpFile;
452 : JSBool ok;
453 :
454 0 : jsval *argv = JS_ARGV(cx, vp);
455 0 : JS_SET_RVAL(cx, vp, JSVAL_VOID);
456 :
457 0 : vp = argv + 0;
458 0 : if (argc > 0 && *vp != JSVAL_NULL && *vp != JSVAL_VOID) {
459 : JSString *str;
460 :
461 0 : str = JS_ValueToString(cx, *vp);
462 0 : if (!str)
463 0 : return JS_FALSE;
464 0 : *vp = STRING_TO_JSVAL(str);
465 0 : if (!fileName.encode(cx, str))
466 0 : return JS_FALSE;
467 : }
468 :
469 0 : vp = argv + 1;
470 0 : if (argc > 1 && *vp != JSVAL_NULL && *vp != JSVAL_VOID) {
471 0 : if (!JSVAL_IS_TRACEABLE(*vp))
472 0 : goto not_traceable_arg;
473 0 : startThing = JSVAL_TO_TRACEABLE(*vp);
474 0 : startTraceKind = JSVAL_TRACE_KIND(*vp);
475 : }
476 :
477 0 : vp = argv + 2;
478 0 : if (argc > 2 && *vp != JSVAL_NULL && *vp != JSVAL_VOID) {
479 0 : if (!JSVAL_IS_TRACEABLE(*vp))
480 0 : goto not_traceable_arg;
481 0 : thingToFind = JSVAL_TO_TRACEABLE(*vp);
482 : }
483 :
484 0 : vp = argv + 3;
485 0 : if (argc > 3 && *vp != JSVAL_NULL && *vp != JSVAL_VOID) {
486 : uint32_t depth;
487 :
488 0 : if (!JS_ValueToECMAUint32(cx, *vp, &depth))
489 0 : return JS_FALSE;
490 0 : maxDepth = depth;
491 : }
492 :
493 0 : vp = argv + 4;
494 0 : if (argc > 4 && *vp != JSVAL_NULL && *vp != JSVAL_VOID) {
495 0 : if (!JSVAL_IS_TRACEABLE(*vp))
496 0 : goto not_traceable_arg;
497 0 : thingToIgnore = JSVAL_TO_TRACEABLE(*vp);
498 : }
499 :
500 0 : if (!fileName) {
501 0 : dumpFile = stdout;
502 : } else {
503 0 : dumpFile = fopen(fileName.ptr(), "w");
504 0 : if (!dumpFile) {
505 : fprintf(stderr, "dumpHeap: can't open %s: %s\n",
506 0 : fileName.ptr(), strerror(errno));
507 0 : return JS_FALSE;
508 : }
509 : }
510 :
511 : ok = JS_DumpHeap(JS_GetRuntime(cx), dumpFile, startThing, startTraceKind, thingToFind,
512 0 : maxDepth, thingToIgnore);
513 0 : if (dumpFile != stdout)
514 0 : fclose(dumpFile);
515 0 : if (!ok)
516 0 : JS_ReportOutOfMemory(cx);
517 0 : return ok;
518 :
519 : not_traceable_arg:
520 : fprintf(stderr,
521 : "dumpHeap: argument %u is not null or a heap-allocated thing\n",
522 0 : (unsigned)(vp - argv));
523 0 : return JS_FALSE;
524 : }
525 :
526 : #endif /* DEBUG */
527 :
528 : static JSBool
529 0 : Clear(JSContext *cx,
530 : unsigned argc,
531 : jsval *vp)
532 : {
533 0 : jsval *argv = JS_ARGV(cx, vp);
534 0 : if (argc > 0 && !JSVAL_IS_PRIMITIVE(argv[0])) {
535 0 : JS_ClearScope(cx, JSVAL_TO_OBJECT(argv[0]));
536 : } else {
537 0 : JS_ReportError(cx, "'clear' requires an object");
538 0 : return JS_FALSE;
539 : }
540 0 : JS_SET_RVAL(cx, vp, JSVAL_VOID);
541 0 : return JS_TRUE;
542 : }
543 :
544 : JSFunctionSpec gGlobalFunctions[] =
545 : {
546 : {"print", Print, 0,0},
547 : {"load", Load, 1,0},
548 : {"quit", Quit, 0,0},
549 : {"version", Version, 1,0},
550 : {"build", BuildDate, 0,0},
551 : {"dumpXPC", DumpXPC, 1,0},
552 : {"dump", Dump, 1,0},
553 : {"gc", GC, 0,0},
554 : #ifdef JS_GC_ZEAL
555 : {"gczeal", GCZeal, 1,0},
556 : #endif
557 : {"clear", Clear, 1,0},
558 : #ifdef DEBUG
559 : {"dumpHeap", DumpHeap, 5,0},
560 : #endif
561 : {nsnull,nsnull,0,0}
562 : };
563 :
564 : typedef enum JSShellErrNum
565 : {
566 : #define MSG_DEF(name, number, count, exception, format) \
567 : name = number,
568 : #include "jsshell.msg"
569 : #undef MSG_DEF
570 : JSShellErr_Limit
571 : #undef MSGDEF
572 : } JSShellErrNum;
573 :
574 : static void
575 0 : ProcessFile(JSContext *cx,
576 : JSObject *obj,
577 : const char *filename,
578 : FILE *file,
579 : JSBool forceTTY)
580 : {
581 0 : XPCShellEnvironment* env = Environment(cx);
582 0 : XPCShellEnvironment::AutoContextPusher pusher(env);
583 :
584 : JSScript *script;
585 : jsval result;
586 : int lineno, startline;
587 : JSBool ok, hitEOF;
588 : char *bufp, buffer[4096];
589 : JSString *str;
590 :
591 0 : if (forceTTY) {
592 0 : file = stdin;
593 : }
594 : else
595 : #ifdef HAVE_ISATTY
596 0 : if (!isatty(fileno(file)))
597 : #endif
598 : {
599 : /*
600 : * It's not interactive - just execute it.
601 : *
602 : * Support the UNIX #! shell hack; gobble the first line if it starts
603 : * with '#'. TODO - this isn't quite compatible with sharp variables,
604 : * as a legal js program (using sharp variables) might start with '#'.
605 : * But that would require multi-character lookahead.
606 : */
607 0 : int ch = fgetc(file);
608 0 : if (ch == '#') {
609 0 : while((ch = fgetc(file)) != EOF) {
610 0 : if(ch == '\n' || ch == '\r')
611 0 : break;
612 : }
613 : }
614 0 : ungetc(ch, file);
615 :
616 0 : JSAutoRequest ar(cx);
617 :
618 0 : JSAutoEnterCompartment ac;
619 0 : if (!ac.enter(cx, obj)) {
620 0 : NS_ERROR("Failed to enter compartment!");
621 : return;
622 : }
623 :
624 : JSScript* script =
625 : JS_CompileUTF8FileHandleForPrincipals(cx, obj, filename, file,
626 0 : env->GetPrincipal());
627 0 : if (script && !env->ShouldCompileOnly())
628 0 : (void)JS_ExecuteScript(cx, obj, script, &result);
629 :
630 : return;
631 : }
632 :
633 : /* It's an interactive filehandle; drop into read-eval-print loop. */
634 0 : lineno = 1;
635 0 : hitEOF = JS_FALSE;
636 0 : do {
637 0 : bufp = buffer;
638 0 : *bufp = '\0';
639 :
640 0 : JSAutoRequest ar(cx);
641 :
642 0 : JSAutoEnterCompartment ac;
643 0 : if (!ac.enter(cx, obj)) {
644 0 : NS_ERROR("Failed to enter compartment!");
645 : return;
646 : }
647 :
648 : /*
649 : * Accumulate lines until we get a 'compilable unit' - one that either
650 : * generates an error (before running out of source) or that compiles
651 : * cleanly. This should be whenever we get a complete statement that
652 : * coincides with the end of a line.
653 : */
654 0 : startline = lineno;
655 0 : do {
656 0 : if (!GetLine(bufp, file, startline == lineno ? "js> " : "")) {
657 0 : hitEOF = JS_TRUE;
658 0 : break;
659 : }
660 0 : bufp += strlen(bufp);
661 0 : lineno++;
662 0 : } while (!JS_BufferIsCompilableUnit(cx, JS_FALSE, obj, buffer, strlen(buffer)));
663 :
664 : /* Clear any pending exception from previous failed compiles. */
665 0 : JS_ClearPendingException(cx);
666 : script =
667 : JS_CompileScriptForPrincipals(cx, obj, env->GetPrincipal(), buffer,
668 0 : strlen(buffer), "typein", startline);
669 0 : if (script) {
670 : JSErrorReporter older;
671 :
672 0 : if (!env->ShouldCompileOnly()) {
673 0 : ok = JS_ExecuteScript(cx, obj, script, &result);
674 0 : if (ok && result != JSVAL_VOID) {
675 : /* Suppress error reports from JS_ValueToString(). */
676 0 : older = JS_SetErrorReporter(cx, NULL);
677 0 : str = JS_ValueToString(cx, result);
678 0 : JSAutoByteString bytes;
679 0 : if (str)
680 0 : bytes.encode(cx, str);
681 0 : JS_SetErrorReporter(cx, older);
682 :
683 0 : if (!!bytes)
684 0 : fprintf(stdout, "%s\n", bytes.ptr());
685 : else
686 0 : ok = JS_FALSE;
687 : }
688 : }
689 : }
690 0 : } while (!hitEOF && !env->IsQuitting());
691 :
692 0 : fprintf(stdout, "\n");
693 : }
694 :
695 : } /* anonymous namespace */
696 :
697 0 : NS_INTERFACE_MAP_BEGIN(FullTrustSecMan)
698 0 : NS_INTERFACE_MAP_ENTRY(nsIXPCSecurityManager)
699 0 : NS_INTERFACE_MAP_ENTRY(nsIScriptSecurityManager)
700 0 : NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIXPCSecurityManager)
701 0 : NS_INTERFACE_MAP_END
702 :
703 0 : NS_IMPL_ADDREF(FullTrustSecMan)
704 0 : NS_IMPL_RELEASE(FullTrustSecMan)
705 :
706 : NS_IMETHODIMP
707 0 : FullTrustSecMan::CanCreateWrapper(JSContext * aJSContext,
708 : const nsIID & aIID,
709 : nsISupports *aObj,
710 : nsIClassInfo *aClassInfo,
711 : void * *aPolicy)
712 : {
713 0 : return NS_OK;
714 : }
715 :
716 : NS_IMETHODIMP
717 0 : FullTrustSecMan::CanCreateInstance(JSContext * aJSContext,
718 : const nsCID & aCID)
719 : {
720 0 : return NS_OK;
721 : }
722 :
723 : NS_IMETHODIMP
724 0 : FullTrustSecMan::CanGetService(JSContext * aJSContext,
725 : const nsCID & aCID)
726 : {
727 0 : return NS_OK;
728 : }
729 :
730 : NS_IMETHODIMP
731 0 : FullTrustSecMan::CanAccess(PRUint32 aAction,
732 : nsAXPCNativeCallContext *aCallContext,
733 : JSContext * aJSContext,
734 : JSObject * aJSObject,
735 : nsISupports *aObj,
736 : nsIClassInfo *aClassInfo,
737 : jsid aName,
738 : void * *aPolicy)
739 : {
740 0 : return NS_OK;
741 : }
742 :
743 : NS_IMETHODIMP
744 0 : FullTrustSecMan::CheckPropertyAccess(JSContext * aJSContext,
745 : JSObject * aJSObject,
746 : const char *aClassName,
747 : jsid aProperty,
748 : PRUint32 aAction)
749 : {
750 0 : return NS_OK;
751 : }
752 :
753 : NS_IMETHODIMP
754 0 : FullTrustSecMan::CheckLoadURIFromScript(JSContext * cx,
755 : nsIURI *uri)
756 : {
757 0 : return NS_OK;
758 : }
759 :
760 : NS_IMETHODIMP
761 0 : FullTrustSecMan::CheckLoadURIWithPrincipal(nsIPrincipal *aPrincipal,
762 : nsIURI *uri,
763 : PRUint32 flags)
764 : {
765 0 : return NS_OK;
766 : }
767 :
768 : NS_IMETHODIMP
769 0 : FullTrustSecMan::CheckLoadURI(nsIURI *from,
770 : nsIURI *uri,
771 : PRUint32 flags)
772 : {
773 0 : return NS_OK;
774 : }
775 :
776 : NS_IMETHODIMP
777 0 : FullTrustSecMan::CheckLoadURIStrWithPrincipal(nsIPrincipal *aPrincipal,
778 : const nsACString & uri,
779 : PRUint32 flags)
780 : {
781 0 : return NS_OK;
782 : }
783 :
784 : NS_IMETHODIMP
785 0 : FullTrustSecMan::CheckLoadURIStr(const nsACString & from,
786 : const nsACString & uri,
787 : PRUint32 flags)
788 : {
789 0 : return NS_OK;
790 : }
791 :
792 : NS_IMETHODIMP
793 0 : FullTrustSecMan::CheckFunctionAccess(JSContext * cx,
794 : void * funObj,
795 : void * targetObj)
796 : {
797 0 : return NS_OK;
798 : }
799 :
800 : NS_IMETHODIMP
801 0 : FullTrustSecMan::CanExecuteScripts(JSContext * cx,
802 : nsIPrincipal *principal,
803 : bool *_retval)
804 : {
805 0 : *_retval = true;
806 0 : return NS_OK;
807 : }
808 :
809 : NS_IMETHODIMP
810 0 : FullTrustSecMan::GetSubjectPrincipal(nsIPrincipal **_retval)
811 : {
812 0 : NS_IF_ADDREF(*_retval = mSystemPrincipal);
813 0 : return *_retval ? NS_OK : NS_ERROR_FAILURE;
814 : }
815 :
816 : NS_IMETHODIMP
817 0 : FullTrustSecMan::GetSystemPrincipal(nsIPrincipal **_retval)
818 : {
819 0 : NS_IF_ADDREF(*_retval = mSystemPrincipal);
820 0 : return *_retval ? NS_OK : NS_ERROR_FAILURE;
821 : }
822 :
823 : NS_IMETHODIMP
824 0 : FullTrustSecMan::GetCertificatePrincipal(const nsACString & aCertFingerprint,
825 : const nsACString & aSubjectName,
826 : const nsACString & aPrettyName,
827 : nsISupports *aCert,
828 : nsIURI *aURI,
829 : nsIPrincipal **_retval)
830 : {
831 0 : NS_IF_ADDREF(*_retval = mSystemPrincipal);
832 0 : return *_retval ? NS_OK : NS_ERROR_FAILURE;
833 : }
834 :
835 : NS_IMETHODIMP
836 0 : FullTrustSecMan::GetCodebasePrincipal(nsIURI *aURI,
837 : nsIPrincipal **_retval)
838 : {
839 0 : NS_IF_ADDREF(*_retval = mSystemPrincipal);
840 0 : return *_retval ? NS_OK : NS_ERROR_FAILURE;
841 : }
842 :
843 : NS_IMETHODIMP
844 0 : FullTrustSecMan::RequestCapability(nsIPrincipal *principal,
845 : const char *capability,
846 : PRInt16 *_retval)
847 : {
848 0 : *_retval = nsIPrincipal::ENABLE_GRANTED;
849 0 : return NS_OK;
850 : }
851 :
852 : NS_IMETHODIMP
853 0 : FullTrustSecMan::IsCapabilityEnabled(const char *capability,
854 : bool *_retval)
855 : {
856 0 : *_retval = true;
857 0 : return NS_OK;
858 : }
859 :
860 : NS_IMETHODIMP
861 0 : FullTrustSecMan::EnableCapability(const char *capability)
862 : {
863 0 : return NS_OK;;
864 : }
865 :
866 : NS_IMETHODIMP
867 0 : FullTrustSecMan::RevertCapability(const char *capability)
868 : {
869 0 : return NS_OK;
870 : }
871 :
872 : NS_IMETHODIMP
873 0 : FullTrustSecMan::DisableCapability(const char *capability)
874 : {
875 0 : return NS_OK;
876 : }
877 :
878 : NS_IMETHODIMP
879 0 : FullTrustSecMan::SetCanEnableCapability(const nsACString & certificateFingerprint,
880 : const char *capability,
881 : PRInt16 canEnable)
882 : {
883 0 : return NS_OK;
884 : }
885 :
886 : NS_IMETHODIMP
887 0 : FullTrustSecMan::GetObjectPrincipal(JSContext * cx,
888 : JSObject * obj,
889 : nsIPrincipal **_retval)
890 : {
891 0 : NS_IF_ADDREF(*_retval = mSystemPrincipal);
892 0 : return *_retval ? NS_OK : NS_ERROR_FAILURE;
893 : }
894 :
895 : NS_IMETHODIMP
896 0 : FullTrustSecMan::SubjectPrincipalIsSystem(bool *_retval)
897 : {
898 0 : *_retval = true;
899 0 : return NS_OK;
900 : }
901 :
902 : NS_IMETHODIMP
903 0 : FullTrustSecMan::CheckSameOrigin(JSContext * aJSContext,
904 : nsIURI *aTargetURI)
905 : {
906 0 : return NS_OK;
907 : }
908 :
909 : NS_IMETHODIMP
910 0 : FullTrustSecMan::CheckSameOriginURI(nsIURI *aSourceURI,
911 : nsIURI *aTargetURI,
912 : bool reportError)
913 : {
914 0 : return NS_OK;
915 : }
916 :
917 : NS_IMETHODIMP
918 0 : FullTrustSecMan::GetPrincipalFromContext(JSContext * cx,
919 : nsIPrincipal **_retval)
920 : {
921 0 : NS_IF_ADDREF(*_retval = mSystemPrincipal);
922 0 : return *_retval ? NS_OK : NS_ERROR_FAILURE;
923 : }
924 :
925 : NS_IMETHODIMP
926 0 : FullTrustSecMan::GetChannelPrincipal(nsIChannel *aChannel,
927 : nsIPrincipal **_retval)
928 : {
929 0 : NS_IF_ADDREF(*_retval = mSystemPrincipal);
930 0 : return *_retval ? NS_OK : NS_ERROR_FAILURE;
931 : }
932 :
933 : NS_IMETHODIMP
934 0 : FullTrustSecMan::IsSystemPrincipal(nsIPrincipal *aPrincipal,
935 : bool *_retval)
936 : {
937 0 : *_retval = aPrincipal == mSystemPrincipal;
938 0 : return NS_OK;
939 : }
940 :
941 : NS_IMETHODIMP_(nsIPrincipal *)
942 0 : FullTrustSecMan::GetCxSubjectPrincipal(JSContext *cx)
943 : {
944 0 : return mSystemPrincipal;
945 : }
946 :
947 : NS_IMETHODIMP_(nsIPrincipal *)
948 0 : FullTrustSecMan::GetCxSubjectPrincipalAndFrame(JSContext *cx,
949 : JSStackFrame **fp)
950 : {
951 0 : *fp = nsnull;
952 0 : return mSystemPrincipal;
953 : }
954 :
955 : NS_IMETHODIMP
956 0 : FullTrustSecMan::PushContextPrincipal(JSContext *cx,
957 : JSStackFrame *fp,
958 : nsIPrincipal *principal)
959 : {
960 0 : return NS_OK;
961 : }
962 :
963 : NS_IMETHODIMP
964 0 : FullTrustSecMan::PopContextPrincipal(JSContext *cx)
965 : {
966 0 : return NS_OK;
967 : }
968 :
969 : NS_IMETHODIMP_(nsrefcnt)
970 0 : XPCShellDirProvider::AddRef()
971 : {
972 0 : return 2;
973 : }
974 :
975 : NS_IMETHODIMP_(nsrefcnt)
976 0 : XPCShellDirProvider::Release()
977 : {
978 0 : return 1;
979 : }
980 :
981 0 : NS_IMPL_QUERY_INTERFACE1(XPCShellDirProvider, nsIDirectoryServiceProvider)
982 :
983 : bool
984 0 : XPCShellDirProvider::SetGREDir(const char *dir)
985 : {
986 0 : nsresult rv = XRE_GetFileFromPath(dir, getter_AddRefs(mGREDir));
987 0 : return NS_SUCCEEDED(rv);
988 : }
989 :
990 : NS_IMETHODIMP
991 0 : XPCShellDirProvider::GetFile(const char *prop,
992 : bool *persistent,
993 : nsIFile* *result)
994 : {
995 0 : if (mGREDir && !strcmp(prop, NS_GRE_DIR)) {
996 0 : *persistent = true;
997 0 : NS_ADDREF(*result = mGREDir);
998 0 : return NS_OK;
999 : }
1000 :
1001 0 : return NS_ERROR_FAILURE;
1002 : }
1003 :
1004 0 : XPCShellEnvironment::
1005 : AutoContextPusher::AutoContextPusher(XPCShellEnvironment* aEnv)
1006 : {
1007 0 : NS_ASSERTION(aEnv->mCx, "Null context?!");
1008 :
1009 0 : if (NS_SUCCEEDED(aEnv->mCxStack->Push(aEnv->mCx))) {
1010 0 : mEnv = aEnv;
1011 : }
1012 0 : }
1013 :
1014 0 : XPCShellEnvironment::
1015 : AutoContextPusher::~AutoContextPusher()
1016 : {
1017 0 : if (mEnv) {
1018 : JSContext* cx;
1019 0 : mEnv->mCxStack->Pop(&cx);
1020 0 : NS_ASSERTION(cx == mEnv->mCx, "Wrong context on the stack!");
1021 : }
1022 0 : }
1023 :
1024 : // static
1025 : XPCShellEnvironment*
1026 0 : XPCShellEnvironment::CreateEnvironment()
1027 : {
1028 0 : XPCShellEnvironment* env = new XPCShellEnvironment();
1029 0 : if (env && !env->Init()) {
1030 0 : delete env;
1031 0 : env = nsnull;
1032 : }
1033 0 : return env;
1034 : }
1035 :
1036 0 : XPCShellEnvironment::XPCShellEnvironment()
1037 : : mCx(NULL),
1038 : mJSPrincipals(NULL),
1039 : mExitCode(0),
1040 : mQuitting(JS_FALSE),
1041 : mReportWarnings(JS_TRUE),
1042 0 : mCompileOnly(JS_FALSE)
1043 : {
1044 0 : }
1045 :
1046 0 : XPCShellEnvironment::~XPCShellEnvironment()
1047 : {
1048 0 : if (mCx) {
1049 0 : JS_BeginRequest(mCx);
1050 :
1051 0 : JSObject* global = GetGlobalObject();
1052 0 : if (global) {
1053 0 : JS_ClearScope(mCx, global);
1054 : }
1055 0 : mGlobalHolder.Release();
1056 :
1057 0 : JS_GC(mCx);
1058 :
1059 0 : mCxStack = nsnull;
1060 :
1061 0 : if (mJSPrincipals) {
1062 0 : JS_DropPrincipals(JS_GetRuntime(mCx), mJSPrincipals);
1063 : }
1064 :
1065 0 : JSRuntime* rt = gOldContextCallback ? JS_GetRuntime(mCx) : NULL;
1066 :
1067 0 : JS_EndRequest(mCx);
1068 0 : JS_DestroyContext(mCx);
1069 :
1070 0 : if (gOldContextCallback) {
1071 0 : NS_ASSERTION(rt, "Should never be null!");
1072 0 : JS_SetContextCallback(rt, gOldContextCallback);
1073 0 : gOldContextCallback = NULL;
1074 : }
1075 : }
1076 0 : }
1077 :
1078 : bool
1079 0 : XPCShellEnvironment::Init()
1080 : {
1081 : nsresult rv;
1082 :
1083 : #ifdef HAVE_SETBUF
1084 : // unbuffer stdout so that output is in the correct order; note that stderr
1085 : // is unbuffered by default
1086 0 : setbuf(stdout, 0);
1087 : #endif
1088 :
1089 : nsCOMPtr<nsIJSRuntimeService> rtsvc =
1090 0 : do_GetService("@mozilla.org/js/xpc/RuntimeService;1");
1091 0 : if (!rtsvc) {
1092 0 : NS_ERROR("failed to get nsJSRuntimeService!");
1093 0 : return false;
1094 : }
1095 :
1096 : JSRuntime *rt;
1097 0 : if (NS_FAILED(rtsvc->GetRuntime(&rt)) || !rt) {
1098 0 : NS_ERROR("failed to get JSRuntime from nsJSRuntimeService!");
1099 0 : return false;
1100 : }
1101 :
1102 0 : if (!mGlobalHolder.Hold(rt)) {
1103 0 : NS_ERROR("Can't protect global object!");
1104 0 : return false;
1105 : }
1106 :
1107 0 : gOldContextCallback = JS_SetContextCallback(rt, ContextCallback);
1108 :
1109 0 : JSContext *cx = JS_NewContext(rt, 8192);
1110 0 : if (!cx) {
1111 0 : NS_ERROR("JS_NewContext failed!");
1112 :
1113 0 : JS_SetContextCallback(rt, gOldContextCallback);
1114 0 : gOldContextCallback = NULL;
1115 :
1116 0 : return false;
1117 : }
1118 0 : mCx = cx;
1119 :
1120 0 : JS_SetContextPrivate(cx, this);
1121 :
1122 : nsCOMPtr<nsIXPConnect> xpc =
1123 0 : do_GetService(nsIXPConnect::GetCID());
1124 0 : if (!xpc) {
1125 0 : NS_ERROR("failed to get nsXPConnect service!");
1126 0 : return false;
1127 : }
1128 :
1129 0 : xpc_LocalizeContext(cx);
1130 :
1131 0 : nsRefPtr<FullTrustSecMan> secman(new FullTrustSecMan());
1132 0 : xpc->SetSecurityManagerForJSContext(cx, secman, 0xFFFF);
1133 :
1134 0 : nsCOMPtr<nsIPrincipal> principal;
1135 :
1136 : nsCOMPtr<nsIScriptSecurityManager> securityManager =
1137 0 : do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
1138 0 : if (NS_SUCCEEDED(rv) && securityManager) {
1139 0 : rv = securityManager->GetSystemPrincipal(getter_AddRefs(principal));
1140 0 : if (NS_FAILED(rv)) {
1141 0 : fprintf(stderr, "+++ Failed to obtain SystemPrincipal from ScriptSecurityManager service.\n");
1142 : } else {
1143 : // fetch the JS principals and stick in a global
1144 0 : mJSPrincipals = nsJSPrincipals::get(principal);
1145 0 : JS_HoldPrincipals(mJSPrincipals);
1146 0 : secman->SetSystemPrincipal(principal);
1147 : }
1148 : } else {
1149 0 : fprintf(stderr, "+++ Failed to get ScriptSecurityManager service, running without principals");
1150 : }
1151 :
1152 : nsCOMPtr<nsIJSContextStack> cxStack =
1153 0 : do_GetService("@mozilla.org/js/xpc/ContextStack;1");
1154 0 : if (!cxStack) {
1155 0 : NS_ERROR("failed to get the nsThreadJSContextStack service!");
1156 0 : return false;
1157 : }
1158 0 : mCxStack = cxStack;
1159 :
1160 0 : AutoContextPusher pusher(this);
1161 :
1162 0 : nsCOMPtr<nsIXPCScriptable> backstagePass;
1163 0 : rv = rtsvc->GetBackstagePass(getter_AddRefs(backstagePass));
1164 0 : if (NS_FAILED(rv)) {
1165 0 : NS_ERROR("Failed to get backstage pass from rtsvc!");
1166 0 : return false;
1167 : }
1168 :
1169 0 : nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
1170 0 : rv = xpc->InitClassesWithNewWrappedGlobal(cx, backstagePass,
1171 : principal,
1172 : nsIXPConnect::
1173 : FLAG_SYSTEM_GLOBAL_OBJECT,
1174 0 : getter_AddRefs(holder));
1175 0 : if (NS_FAILED(rv)) {
1176 0 : NS_ERROR("InitClassesWithNewWrappedGlobal failed!");
1177 0 : return false;
1178 : }
1179 :
1180 : JSObject *globalObj;
1181 0 : rv = holder->GetJSObject(&globalObj);
1182 0 : if (NS_FAILED(rv)) {
1183 0 : NS_ERROR("Failed to get global JSObject!");
1184 0 : return false;
1185 : }
1186 :
1187 :
1188 : {
1189 0 : JSAutoRequest ar(cx);
1190 :
1191 0 : JSAutoEnterCompartment ac;
1192 0 : if (!ac.enter(cx, globalObj)) {
1193 0 : NS_ERROR("Failed to enter compartment!");
1194 0 : return false;
1195 : }
1196 :
1197 0 : if (!JS_DefineFunctions(cx, globalObj, gGlobalFunctions) ||
1198 0 : !JS_DefineProfilingFunctions(cx, globalObj)) {
1199 0 : NS_ERROR("JS_DefineFunctions failed!");
1200 0 : return false;
1201 : }
1202 : }
1203 :
1204 0 : mGlobalHolder = globalObj;
1205 :
1206 0 : FILE* runtimeScriptFile = fopen(kDefaultRuntimeScriptFilename, "r");
1207 0 : if (runtimeScriptFile) {
1208 0 : fprintf(stdout, "[loading '%s'...]\n", kDefaultRuntimeScriptFilename);
1209 : ProcessFile(cx, globalObj, kDefaultRuntimeScriptFilename,
1210 0 : runtimeScriptFile, JS_FALSE);
1211 0 : fclose(runtimeScriptFile);
1212 : }
1213 :
1214 0 : return true;
1215 : }
1216 :
1217 : bool
1218 0 : XPCShellEnvironment::EvaluateString(const nsString& aString,
1219 : nsString* aResult)
1220 : {
1221 0 : XPCShellEnvironment* env = Environment(mCx);
1222 0 : XPCShellEnvironment::AutoContextPusher pusher(env);
1223 :
1224 0 : JSAutoRequest ar(mCx);
1225 :
1226 0 : JS_ClearPendingException(mCx);
1227 :
1228 0 : JSObject* global = GetGlobalObject();
1229 :
1230 0 : JSAutoEnterCompartment ac;
1231 0 : if (!ac.enter(mCx, global)) {
1232 0 : NS_ERROR("Failed to enter compartment!");
1233 0 : return false;
1234 : }
1235 :
1236 : JSScript* script =
1237 : JS_CompileUCScriptForPrincipals(mCx, global, GetPrincipal(),
1238 : aString.get(), aString.Length(),
1239 0 : "typein", 0);
1240 0 : if (!script) {
1241 0 : return false;
1242 : }
1243 :
1244 0 : if (!ShouldCompileOnly()) {
1245 0 : if (aResult) {
1246 0 : aResult->Truncate();
1247 : }
1248 :
1249 : jsval result;
1250 0 : JSBool ok = JS_ExecuteScript(mCx, global, script, &result);
1251 0 : if (ok && result != JSVAL_VOID) {
1252 0 : JSErrorReporter old = JS_SetErrorReporter(mCx, NULL);
1253 0 : JSString* str = JS_ValueToString(mCx, result);
1254 0 : nsDependentJSString depStr;
1255 0 : if (str)
1256 0 : depStr.init(mCx, str);
1257 0 : JS_SetErrorReporter(mCx, old);
1258 :
1259 0 : if (!depStr.IsEmpty() && aResult) {
1260 0 : aResult->Assign(depStr);
1261 : }
1262 : }
1263 : }
1264 :
1265 0 : return true;
1266 : }
|