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