/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 | } |