js/xpconnect/loader/mozJSComponentLoader.cpp

changeset 0
6474c204b198
equal deleted inserted replaced
-1:000000000000 0:1c76fcdae46a
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 "mozilla/Attributes.h"
8
9 #ifdef MOZ_LOGGING
10 #define FORCE_PR_LOG
11 #endif
12
13 #include <cstdarg>
14
15 #include "prlog.h"
16 #ifdef ANDROID
17 #include <android/log.h>
18 #endif
19 #ifdef XP_WIN
20 #include <windows.h>
21 #endif
22
23 #include "jsapi.h"
24 #include "nsCOMPtr.h"
25 #include "nsAutoPtr.h"
26 #include "nsIComponentManager.h"
27 #include "mozilla/Module.h"
28 #include "nsIFile.h"
29 #include "mozJSComponentLoader.h"
30 #include "mozJSLoaderUtils.h"
31 #include "nsIJSRuntimeService.h"
32 #include "nsIXPConnect.h"
33 #include "nsIObserverService.h"
34 #include "nsIScriptSecurityManager.h"
35 #include "nsIFileURL.h"
36 #include "nsIJARURI.h"
37 #include "nsNetUtil.h"
38 #include "nsDOMBlobBuilder.h"
39 #include "jsprf.h"
40 #include "nsJSPrincipals.h"
41 #include "xpcprivate.h"
42 #include "xpcpublic.h"
43 #include "nsContentUtils.h"
44 #include "nsCxPusher.h"
45 #include "WrapperFactory.h"
46
47 #include "mozilla/scache/StartupCache.h"
48 #include "mozilla/scache/StartupCacheUtils.h"
49 #include "mozilla/Preferences.h"
50
51 #include "js/OldDebugAPI.h"
52
53 using namespace mozilla;
54 using namespace mozilla::scache;
55 using namespace xpc;
56 using namespace JS;
57
58 // This JSClass exists to trick silly code that expects toString()ing the
59 // global in a component scope to return something with "BackstagePass" in it
60 // to continue working.
61 static const JSClass kFakeBackstagePassJSClass =
62 {
63 "FakeBackstagePass",
64 0,
65 JS_PropertyStub,
66 JS_DeletePropertyStub,
67 JS_PropertyStub,
68 JS_StrictPropertyStub,
69 JS_EnumerateStub,
70 JS_ResolveStub,
71 JS_ConvertStub
72 };
73
74 static const char kJSRuntimeServiceContractID[] = "@mozilla.org/js/xpc/RuntimeService;1";
75 static const char kXPConnectServiceContractID[] = "@mozilla.org/js/xpc/XPConnect;1";
76 static const char kObserverServiceContractID[] = "@mozilla.org/observer-service;1";
77 static const char kJSCachePrefix[] = "jsloader";
78
79 #define HAVE_PR_MEMMAP
80
81 /**
82 * Buffer sizes for serialization and deserialization of scripts.
83 * FIXME: bug #411579 (tune this macro!) Last updated: Jan 2008
84 */
85 #define XPC_SERIALIZATION_BUFFER_SIZE (64 * 1024)
86 #define XPC_DESERIALIZATION_BUFFER_SIZE (12 * 8192)
87
88 #ifdef PR_LOGGING
89 // NSPR_LOG_MODULES=JSComponentLoader:5
90 static PRLogModuleInfo *gJSCLLog;
91 #endif
92
93 #define LOG(args) PR_LOG(gJSCLLog, PR_LOG_DEBUG, args)
94
95 // Components.utils.import error messages
96 #define ERROR_SCOPE_OBJ "%s - Second argument must be an object."
97 #define ERROR_NOT_PRESENT "%s - EXPORTED_SYMBOLS is not present."
98 #define ERROR_NOT_AN_ARRAY "%s - EXPORTED_SYMBOLS is not an array."
99 #define ERROR_GETTING_ARRAY_LENGTH "%s - Error getting array length of EXPORTED_SYMBOLS."
100 #define ERROR_ARRAY_ELEMENT "%s - EXPORTED_SYMBOLS[%d] is not a string."
101 #define ERROR_GETTING_SYMBOL "%s - Could not get symbol '%s'."
102 #define ERROR_SETTING_SYMBOL "%s - Could not set symbol '%s' on target object."
103
104 static bool
105 Dump(JSContext *cx, unsigned argc, Value *vp)
106 {
107 CallArgs args = CallArgsFromVp(argc, vp);
108
109 if (args.length() == 0)
110 return true;
111
112 JSString *str = JS::ToString(cx, args[0]);
113 if (!str)
114 return false;
115
116 size_t length;
117 const jschar *chars = JS_GetStringCharsAndLength(cx, str, &length);
118 if (!chars)
119 return false;
120
121 NS_ConvertUTF16toUTF8 utf8str(reinterpret_cast<const char16_t*>(chars),
122 length);
123 #ifdef ANDROID
124 __android_log_print(ANDROID_LOG_INFO, "Gecko", "%s", utf8str.get());
125 #endif
126 #ifdef XP_WIN
127 if (IsDebuggerPresent()) {
128 OutputDebugStringW(reinterpret_cast<const wchar_t*>(chars));
129 }
130 #endif
131 fputs(utf8str.get(), stdout);
132 fflush(stdout);
133 return true;
134 }
135
136 static bool
137 Debug(JSContext *cx, unsigned argc, jsval *vp)
138 {
139 #ifdef DEBUG
140 return Dump(cx, argc, vp);
141 #else
142 return true;
143 #endif
144 }
145
146 static bool
147 File(JSContext *cx, unsigned argc, Value *vp)
148 {
149 CallArgs args = CallArgsFromVp(argc, vp);
150
151 if (args.length() == 0) {
152 XPCThrower::Throw(NS_ERROR_UNEXPECTED, cx);
153 return false;
154 }
155
156 nsCOMPtr<nsISupports> native;
157 nsresult rv = nsDOMMultipartFile::NewFile(getter_AddRefs(native));
158 if (NS_FAILED(rv)) {
159 XPCThrower::Throw(rv, cx);
160 return false;
161 }
162
163 nsCOMPtr<nsIJSNativeInitializer> initializer = do_QueryInterface(native);
164 MOZ_ASSERT(initializer);
165
166 rv = initializer->Initialize(nullptr, cx, nullptr, args);
167 if (NS_FAILED(rv)) {
168 XPCThrower::Throw(rv, cx);
169 return false;
170 }
171
172 nsXPConnect *xpc = nsXPConnect::XPConnect();
173 JSObject *glob = CurrentGlobalOrNull(cx);
174
175 nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
176 rv = xpc->WrapNativeToJSVal(cx, glob, native, nullptr,
177 &NS_GET_IID(nsISupports),
178 true, args.rval());
179 if (NS_FAILED(rv)) {
180 XPCThrower::Throw(rv, cx);
181 return false;
182 }
183 return true;
184 }
185
186 static bool
187 Blob(JSContext *cx, unsigned argc, Value *vp)
188 {
189 CallArgs args = CallArgsFromVp(argc, vp);
190
191 nsCOMPtr<nsISupports> native;
192 nsresult rv = nsDOMMultipartFile::NewBlob(getter_AddRefs(native));
193 if (NS_FAILED(rv)) {
194 XPCThrower::Throw(rv, cx);
195 return false;
196 }
197
198 nsCOMPtr<nsIJSNativeInitializer> initializer = do_QueryInterface(native);
199 MOZ_ASSERT(initializer);
200
201 rv = initializer->Initialize(nullptr, cx, nullptr, args);
202 if (NS_FAILED(rv)) {
203 XPCThrower::Throw(rv, cx);
204 return false;
205 }
206
207 nsXPConnect *xpc = nsXPConnect::XPConnect();
208 JSObject *glob = CurrentGlobalOrNull(cx);
209
210 nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
211 rv = xpc->WrapNativeToJSVal(cx, glob, native, nullptr,
212 &NS_GET_IID(nsISupports),
213 true, args.rval());
214 if (NS_FAILED(rv)) {
215 XPCThrower::Throw(rv, cx);
216 return false;
217 }
218 return true;
219 }
220
221 static const JSFunctionSpec gGlobalFun[] = {
222 JS_FS("dump", Dump, 1,0),
223 JS_FS("debug", Debug, 1,0),
224 JS_FS("atob", Atob, 1,0),
225 JS_FS("btoa", Btoa, 1,0),
226 JS_FS("File", File, 1,JSFUN_CONSTRUCTOR),
227 JS_FS("Blob", Blob, 2,JSFUN_CONSTRUCTOR),
228 JS_FS_END
229 };
230
231 class MOZ_STACK_CLASS JSCLContextHelper
232 {
233 public:
234 JSCLContextHelper(JSContext* aCx);
235 ~JSCLContextHelper();
236
237 void reportErrorAfterPop(char *buf);
238
239 operator JSContext*() const {return mContext;}
240
241 private:
242
243 JSContext* mContext;
244 nsCxPusher mPusher;
245 char* mBuf;
246
247 // prevent copying and assignment
248 JSCLContextHelper(const JSCLContextHelper &) MOZ_DELETE;
249 const JSCLContextHelper& operator=(const JSCLContextHelper &) MOZ_DELETE;
250 };
251
252
253 class JSCLAutoErrorReporterSetter
254 {
255 public:
256 JSCLAutoErrorReporterSetter(JSContext* cx, JSErrorReporter reporter)
257 {mContext = cx; mOldReporter = JS_SetErrorReporter(cx, reporter);}
258 ~JSCLAutoErrorReporterSetter()
259 {JS_SetErrorReporter(mContext, mOldReporter);}
260 private:
261 JSContext* mContext;
262 JSErrorReporter mOldReporter;
263
264 JSCLAutoErrorReporterSetter(const JSCLAutoErrorReporterSetter &) MOZ_DELETE;
265 const JSCLAutoErrorReporterSetter& operator=(const JSCLAutoErrorReporterSetter &) MOZ_DELETE;
266 };
267
268 static nsresult
269 ReportOnCaller(JSContext *callerContext,
270 const char *format, ...) {
271 if (!callerContext) {
272 return NS_ERROR_FAILURE;
273 }
274
275 va_list ap;
276 va_start(ap, format);
277
278 char *buf = JS_vsmprintf(format, ap);
279 if (!buf) {
280 return NS_ERROR_OUT_OF_MEMORY;
281 }
282
283 JS_ReportError(callerContext, buf);
284 JS_smprintf_free(buf);
285
286 return NS_OK;
287 }
288
289 static nsresult
290 ReportOnCaller(JSCLContextHelper &helper,
291 const char *format, ...)
292 {
293 va_list ap;
294 va_start(ap, format);
295
296 char *buf = JS_vsmprintf(format, ap);
297 if (!buf) {
298 return NS_ERROR_OUT_OF_MEMORY;
299 }
300
301 helper.reportErrorAfterPop(buf);
302
303 return NS_OK;
304 }
305
306 mozJSComponentLoader::mozJSComponentLoader()
307 : mRuntime(nullptr),
308 mContext(nullptr),
309 mModules(32),
310 mImports(32),
311 mInProgressImports(32),
312 mThisObjects(32),
313 mInitialized(false),
314 mReuseLoaderGlobal(false)
315 {
316 MOZ_ASSERT(!sSelf, "mozJSComponentLoader should be a singleton");
317
318 #ifdef PR_LOGGING
319 if (!gJSCLLog) {
320 gJSCLLog = PR_NewLogModule("JSComponentLoader");
321 }
322 #endif
323
324 sSelf = this;
325 }
326
327 mozJSComponentLoader::~mozJSComponentLoader()
328 {
329 if (mInitialized) {
330 NS_ERROR("'xpcom-shutdown-loaders' was not fired before cleaning up mozJSComponentLoader");
331 UnloadModules();
332 }
333
334 sSelf = nullptr;
335 }
336
337 mozJSComponentLoader*
338 mozJSComponentLoader::sSelf;
339
340 NS_IMPL_ISUPPORTS(mozJSComponentLoader,
341 mozilla::ModuleLoader,
342 xpcIJSModuleLoader,
343 nsIObserver)
344
345 nsresult
346 mozJSComponentLoader::ReallyInit()
347 {
348 nsresult rv;
349
350 mReuseLoaderGlobal = Preferences::GetBool("jsloader.reuseGlobal");
351
352 // XXXkhuey B2G child processes have some sort of preferences race that
353 // results in getting the wrong value.
354 #ifdef MOZ_B2G
355 mReuseLoaderGlobal = true;
356 #endif
357
358 /*
359 * Get the JSRuntime from the runtime svc, if possible.
360 * We keep a reference around, because it's a Bad Thing if the runtime
361 * service gets shut down before we're done. Bad!
362 */
363
364 mRuntimeService = do_GetService(kJSRuntimeServiceContractID, &rv);
365 if (NS_FAILED(rv) ||
366 NS_FAILED(rv = mRuntimeService->GetRuntime(&mRuntime)))
367 return rv;
368
369 // Create our compilation context.
370 mContext = JS_NewContext(mRuntime, 256);
371 if (!mContext)
372 return NS_ERROR_OUT_OF_MEMORY;
373
374 nsCOMPtr<nsIScriptSecurityManager> secman =
375 do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
376 if (!secman)
377 return NS_ERROR_FAILURE;
378
379 rv = secman->GetSystemPrincipal(getter_AddRefs(mSystemPrincipal));
380 if (NS_FAILED(rv) || !mSystemPrincipal)
381 return NS_ERROR_FAILURE;
382
383 nsCOMPtr<nsIObserverService> obsSvc =
384 do_GetService(kObserverServiceContractID, &rv);
385 NS_ENSURE_SUCCESS(rv, rv);
386
387 rv = obsSvc->AddObserver(this, "xpcom-shutdown-loaders", false);
388 NS_ENSURE_SUCCESS(rv, rv);
389
390 mInitialized = true;
391
392 return NS_OK;
393 }
394
395 const mozilla::Module*
396 mozJSComponentLoader::LoadModule(FileLocation &aFile)
397 {
398 nsCOMPtr<nsIFile> file = aFile.GetBaseFile();
399
400 nsCString spec;
401 aFile.GetURIString(spec);
402
403 nsCOMPtr<nsIURI> uri;
404 nsresult rv = NS_NewURI(getter_AddRefs(uri), spec);
405 if (NS_FAILED(rv))
406 return nullptr;
407
408 if (!mInitialized) {
409 rv = ReallyInit();
410 if (NS_FAILED(rv))
411 return nullptr;
412 }
413
414 ModuleEntry* mod;
415 if (mModules.Get(spec, &mod))
416 return mod;
417
418 nsAutoPtr<ModuleEntry> entry(new ModuleEntry(mContext));
419
420 JSAutoRequest ar(mContext);
421 RootedValue dummy(mContext);
422 rv = ObjectForLocation(file, uri, &entry->obj, &entry->thisObjectKey,
423 &entry->location, false, &dummy);
424 if (NS_FAILED(rv)) {
425 return nullptr;
426 }
427
428 nsCOMPtr<nsIXPConnect> xpc = do_GetService(kXPConnectServiceContractID,
429 &rv);
430 if (NS_FAILED(rv))
431 return nullptr;
432
433 nsCOMPtr<nsIComponentManager> cm;
434 rv = NS_GetComponentManager(getter_AddRefs(cm));
435 if (NS_FAILED(rv))
436 return nullptr;
437
438 JSCLContextHelper cx(mContext);
439 JSAutoCompartment ac(cx, entry->obj);
440
441 nsCOMPtr<nsIXPConnectJSObjectHolder> cm_holder;
442 rv = xpc->WrapNative(cx, entry->obj, cm,
443 NS_GET_IID(nsIComponentManager),
444 getter_AddRefs(cm_holder));
445
446 if (NS_FAILED(rv)) {
447 return nullptr;
448 }
449
450 JSObject* cm_jsobj = cm_holder->GetJSObject();
451 if (!cm_jsobj) {
452 return nullptr;
453 }
454
455 nsCOMPtr<nsIXPConnectJSObjectHolder> file_holder;
456 RootedObject entryObj(cx, entry->obj);
457 rv = xpc->WrapNative(cx, entryObj, file,
458 NS_GET_IID(nsIFile),
459 getter_AddRefs(file_holder));
460
461 if (NS_FAILED(rv)) {
462 return nullptr;
463 }
464
465 JSObject* file_jsobj = file_holder->GetJSObject();
466 if (!file_jsobj) {
467 return nullptr;
468 }
469
470 JSCLAutoErrorReporterSetter aers(cx, xpc::SystemErrorReporter);
471
472 RootedValue NSGetFactory_val(cx);
473 if (!JS_GetProperty(cx, entryObj, "NSGetFactory", &NSGetFactory_val) ||
474 JSVAL_IS_VOID(NSGetFactory_val)) {
475 return nullptr;
476 }
477
478 if (JS_TypeOfValue(cx, NSGetFactory_val) != JSTYPE_FUNCTION) {
479 nsAutoCString spec;
480 uri->GetSpec(spec);
481 JS_ReportError(cx, "%s has NSGetFactory property that is not a function",
482 spec.get());
483 return nullptr;
484 }
485
486 RootedObject jsGetFactoryObj(cx);
487 if (!JS_ValueToObject(cx, NSGetFactory_val, &jsGetFactoryObj) ||
488 !jsGetFactoryObj) {
489 /* XXX report error properly */
490 return nullptr;
491 }
492
493 rv = xpc->WrapJS(cx, jsGetFactoryObj,
494 NS_GET_IID(xpcIJSGetFactory), getter_AddRefs(entry->getfactoryobj));
495 if (NS_FAILED(rv)) {
496 /* XXX report error properly */
497 #ifdef DEBUG
498 fprintf(stderr, "mJCL: couldn't get nsIModule from jsval\n");
499 #endif
500 return nullptr;
501 }
502
503 // Cache this module for later
504 mModules.Put(spec, entry);
505
506 // Set the location information for the new global, so that tools like
507 // about:memory may use that information
508 if (!mReuseLoaderGlobal) {
509 xpc::SetLocationForGlobal(entryObj, spec);
510 }
511
512 // The hash owns the ModuleEntry now, forget about it
513 return entry.forget();
514 }
515
516 nsresult
517 mozJSComponentLoader::FindTargetObject(JSContext* aCx,
518 MutableHandleObject aTargetObject)
519 {
520 aTargetObject.set(nullptr);
521
522 RootedObject targetObject(aCx);
523 if (mReuseLoaderGlobal) {
524 JSScript* script =
525 js::GetOutermostEnclosingFunctionOfScriptedCaller(aCx);
526 if (script) {
527 targetObject = mThisObjects.Get(script);
528 }
529 }
530
531 // The above could fail, even if mReuseLoaderGlobal, if the scripted
532 // caller is not a component/JSM (it could be a DOM scope, for
533 // instance).
534 if (!targetObject) {
535 // Our targetObject is the caller's global object. Let's get it.
536 nsresult rv;
537 nsCOMPtr<nsIXPConnect> xpc =
538 do_GetService(kXPConnectServiceContractID, &rv);
539 NS_ENSURE_SUCCESS(rv, rv);
540
541 nsAXPCNativeCallContext *cc = nullptr;
542 rv = xpc->GetCurrentNativeCallContext(&cc);
543 NS_ENSURE_SUCCESS(rv, rv);
544
545 nsCOMPtr<nsIXPConnectWrappedNative> wn;
546 rv = cc->GetCalleeWrapper(getter_AddRefs(wn));
547 NS_ENSURE_SUCCESS(rv, rv);
548
549 targetObject = wn->GetJSObject();
550 if (!targetObject) {
551 NS_ERROR("null calling object");
552 return NS_ERROR_FAILURE;
553 }
554
555 targetObject = JS_GetGlobalForObject(aCx, targetObject);
556 }
557
558 aTargetObject.set(targetObject);
559 return NS_OK;
560 }
561
562 void
563 mozJSComponentLoader::NoteSubScript(HandleScript aScript, HandleObject aThisObject)
564 {
565 if (!mInitialized && NS_FAILED(ReallyInit())) {
566 MOZ_CRASH();
567 }
568
569 if (js::GetObjectJSClass(aThisObject) == &kFakeBackstagePassJSClass) {
570 mThisObjects.Put(aScript, aThisObject);
571 }
572 }
573
574 /* static */ size_t
575 mozJSComponentLoader::DataEntrySizeOfExcludingThis(const nsACString& aKey,
576 ModuleEntry* const& aData,
577 MallocSizeOf aMallocSizeOf, void*)
578 {
579 return aKey.SizeOfExcludingThisIfUnshared(aMallocSizeOf) +
580 aData->SizeOfIncludingThis(aMallocSizeOf);
581 }
582
583 /* static */ size_t
584 mozJSComponentLoader::ClassEntrySizeOfExcludingThis(const nsACString& aKey,
585 const nsAutoPtr<ModuleEntry>& aData,
586 MallocSizeOf aMallocSizeOf, void*)
587 {
588 return aKey.SizeOfExcludingThisIfUnshared(aMallocSizeOf) +
589 aData->SizeOfIncludingThis(aMallocSizeOf);
590 }
591
592 size_t
593 mozJSComponentLoader::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf)
594 {
595 size_t amount = aMallocSizeOf(this);
596
597 amount += mModules.SizeOfExcludingThis(DataEntrySizeOfExcludingThis, aMallocSizeOf);
598 amount += mImports.SizeOfExcludingThis(ClassEntrySizeOfExcludingThis, aMallocSizeOf);
599 amount += mInProgressImports.SizeOfExcludingThis(DataEntrySizeOfExcludingThis, aMallocSizeOf);
600 amount += mThisObjects.SizeOfExcludingThis(nullptr, aMallocSizeOf);
601
602 return amount;
603 }
604
605 // Some stack based classes for cleaning up on early return
606 #ifdef HAVE_PR_MEMMAP
607 class FileAutoCloser
608 {
609 public:
610 explicit FileAutoCloser(PRFileDesc *file) : mFile(file) {}
611 ~FileAutoCloser() { PR_Close(mFile); }
612 private:
613 PRFileDesc *mFile;
614 };
615
616 class FileMapAutoCloser
617 {
618 public:
619 explicit FileMapAutoCloser(PRFileMap *map) : mMap(map) {}
620 ~FileMapAutoCloser() { PR_CloseFileMap(mMap); }
621 private:
622 PRFileMap *mMap;
623 };
624 #else
625 class ANSIFileAutoCloser
626 {
627 public:
628 explicit ANSIFileAutoCloser(FILE *file) : mFile(file) {}
629 ~ANSIFileAutoCloser() { fclose(mFile); }
630 private:
631 FILE *mFile;
632 };
633 #endif
634
635 JSObject*
636 mozJSComponentLoader::PrepareObjectForLocation(JSCLContextHelper& aCx,
637 nsIFile *aComponentFile,
638 nsIURI *aURI,
639 bool aReuseLoaderGlobal,
640 bool *aRealFile)
641 {
642 nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
643 if (aReuseLoaderGlobal) {
644 holder = mLoaderGlobal;
645 }
646
647 nsresult rv = NS_OK;
648 nsCOMPtr<nsIXPConnect> xpc =
649 do_GetService(kXPConnectServiceContractID, &rv);
650 NS_ENSURE_SUCCESS(rv, nullptr);
651 bool createdNewGlobal = false;
652
653 if (!mLoaderGlobal) {
654 nsRefPtr<BackstagePass> backstagePass;
655 rv = NS_NewBackstagePass(getter_AddRefs(backstagePass));
656 NS_ENSURE_SUCCESS(rv, nullptr);
657
658 CompartmentOptions options;
659 options.setZone(SystemZone)
660 .setVersion(JSVERSION_LATEST);
661 // Defer firing OnNewGlobalObject until after the __URI__ property has
662 // been defined so the JS debugger can tell what module the global is
663 // for
664 rv = xpc->InitClassesWithNewWrappedGlobal(aCx,
665 static_cast<nsIGlobalObject *>(backstagePass),
666 mSystemPrincipal,
667 nsIXPConnect::DONT_FIRE_ONNEWGLOBALHOOK,
668 options,
669 getter_AddRefs(holder));
670 NS_ENSURE_SUCCESS(rv, nullptr);
671 createdNewGlobal = true;
672
673 RootedObject global(aCx, holder->GetJSObject());
674 NS_ENSURE_TRUE(global, nullptr);
675
676 backstagePass->SetGlobalObject(global);
677
678 JSAutoCompartment ac(aCx, global);
679 if (!JS_DefineFunctions(aCx, global, gGlobalFun) ||
680 !JS_DefineProfilingFunctions(aCx, global)) {
681 return nullptr;
682 }
683
684 if (aReuseLoaderGlobal) {
685 mLoaderGlobal = holder;
686 }
687 }
688
689 RootedObject obj(aCx, holder->GetJSObject());
690 NS_ENSURE_TRUE(obj, nullptr);
691
692 JSAutoCompartment ac(aCx, obj);
693
694 if (aReuseLoaderGlobal) {
695 // If we're reusing the loader global, we don't actually use the
696 // global, but rather we use a different object as the 'this' object.
697 obj = JS_NewObject(aCx, &kFakeBackstagePassJSClass, NullPtr(), NullPtr());
698 NS_ENSURE_TRUE(obj, nullptr);
699 }
700
701 *aRealFile = false;
702
703 // need to be extra careful checking for URIs pointing to files
704 // EnsureFile may not always get called, especially on resource URIs
705 // so we need to call GetFile to make sure this is a valid file
706 nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(aURI, &rv);
707 nsCOMPtr<nsIFile> testFile;
708 if (NS_SUCCEEDED(rv)) {
709 fileURL->GetFile(getter_AddRefs(testFile));
710 }
711
712 if (testFile) {
713 *aRealFile = true;
714
715 nsCOMPtr<nsIXPConnectJSObjectHolder> locationHolder;
716 rv = xpc->WrapNative(aCx, obj, aComponentFile,
717 NS_GET_IID(nsIFile),
718 getter_AddRefs(locationHolder));
719 NS_ENSURE_SUCCESS(rv, nullptr);
720
721 RootedObject locationObj(aCx, locationHolder->GetJSObject());
722 NS_ENSURE_TRUE(locationObj, nullptr);
723
724 if (!JS_DefineProperty(aCx, obj, "__LOCATION__", locationObj, 0))
725 return nullptr;
726 }
727
728 nsAutoCString nativePath;
729 rv = aURI->GetSpec(nativePath);
730 NS_ENSURE_SUCCESS(rv, nullptr);
731
732 // Expose the URI from which the script was imported through a special
733 // variable that we insert into the JSM.
734 RootedString exposedUri(aCx, JS_NewStringCopyN(aCx, nativePath.get(), nativePath.Length()));
735 NS_ENSURE_TRUE(exposedUri, nullptr);
736
737 if (!JS_DefineProperty(aCx, obj, "__URI__", exposedUri, 0))
738 return nullptr;
739
740 if (createdNewGlobal) {
741 RootedObject global(aCx, holder->GetJSObject());
742 JS_FireOnNewGlobalObject(aCx, global);
743 }
744
745 return obj;
746 }
747
748 nsresult
749 mozJSComponentLoader::ObjectForLocation(nsIFile *aComponentFile,
750 nsIURI *aURI,
751 MutableHandleObject aObject,
752 MutableHandleScript aTableScript,
753 char **aLocation,
754 bool aPropagateExceptions,
755 MutableHandleValue aException)
756 {
757 JSCLContextHelper cx(mContext);
758
759 JS_AbortIfWrongThread(JS_GetRuntime(cx));
760
761 JSCLAutoErrorReporterSetter aers(cx, xpc::SystemErrorReporter);
762
763 bool realFile = false;
764 RootedObject obj(cx, PrepareObjectForLocation(cx, aComponentFile, aURI,
765 mReuseLoaderGlobal, &realFile));
766 NS_ENSURE_TRUE(obj, NS_ERROR_FAILURE);
767
768 JSAutoCompartment ac(cx, obj);
769
770 RootedScript script(cx);
771 RootedFunction function(cx);
772
773 nsAutoCString nativePath;
774 nsresult rv = aURI->GetSpec(nativePath);
775 NS_ENSURE_SUCCESS(rv, rv);
776
777 // Before compiling the script, first check to see if we have it in
778 // the startupcache. Note: as a rule, startupcache errors are not fatal
779 // to loading the script, since we can always slow-load.
780
781 bool writeToCache = false;
782 StartupCache* cache = StartupCache::GetSingleton();
783
784 nsAutoCString cachePath(kJSCachePrefix);
785 rv = PathifyURI(aURI, cachePath);
786 NS_ENSURE_SUCCESS(rv, rv);
787
788 if (cache) {
789 if (!mReuseLoaderGlobal) {
790 rv = ReadCachedScript(cache, cachePath, cx, mSystemPrincipal, &script);
791 } else {
792 rv = ReadCachedFunction(cache, cachePath, cx, mSystemPrincipal,
793 function.address());
794 }
795
796 if (NS_SUCCEEDED(rv)) {
797 LOG(("Successfully loaded %s from startupcache\n", nativePath.get()));
798 } else {
799 // This is ok, it just means the script is not yet in the
800 // cache. Could mean that the cache was corrupted and got removed,
801 // but either way we're going to write this out.
802 writeToCache = true;
803 }
804 }
805
806 if (!script && !function) {
807 // The script wasn't in the cache , so compile it now.
808 LOG(("Slow loading %s\n", nativePath.get()));
809
810 // If aPropagateExceptions is true, then our caller wants us to propagate
811 // any exceptions out to our caller. Ensure that the engine doesn't
812 // eagerly report the exception.
813 AutoSaveContextOptions asco(cx);
814 if (aPropagateExceptions)
815 ContextOptionsRef(cx).setDontReportUncaught(true);
816
817 // Note - if mReuseLoaderGlobal is true, then we can't do lazy source,
818 // because we compile things as functions (rather than script), and lazy
819 // source isn't supported in that configuration. That's ok though,
820 // because we only do mReuseLoaderGlobal on b2g, where we invoke
821 // setDiscardSource(true) on the entire global.
822 CompileOptions options(cx);
823 options.setNoScriptRval(mReuseLoaderGlobal ? false : true)
824 .setVersion(JSVERSION_LATEST)
825 .setFileAndLine(nativePath.get(), 1)
826 .setSourceIsLazy(!mReuseLoaderGlobal);
827
828 if (realFile) {
829 #ifdef HAVE_PR_MEMMAP
830 int64_t fileSize;
831 rv = aComponentFile->GetFileSize(&fileSize);
832 if (NS_FAILED(rv)) {
833 return rv;
834 }
835
836 int64_t maxSize = UINT32_MAX;
837 if (fileSize > maxSize) {
838 NS_ERROR("file too large");
839 return NS_ERROR_FAILURE;
840 }
841
842 PRFileDesc *fileHandle;
843 rv = aComponentFile->OpenNSPRFileDesc(PR_RDONLY, 0, &fileHandle);
844 if (NS_FAILED(rv)) {
845 return NS_ERROR_FILE_NOT_FOUND;
846 }
847
848 // Make sure the file is closed, no matter how we return.
849 FileAutoCloser fileCloser(fileHandle);
850
851 // We don't provide the file size here. If we did, PR_CreateFileMap
852 // would simply stat() the file to verify that the size we provided
853 // didn't require extending the file. We know that the file doesn't
854 // need to be extended, so skip the extra work by not providing the
855 // size.
856 PRFileMap *map = PR_CreateFileMap(fileHandle, 0, PR_PROT_READONLY);
857 if (!map) {
858 NS_ERROR("Failed to create file map");
859 return NS_ERROR_FAILURE;
860 }
861
862 // Make sure the file map is closed, no matter how we return.
863 FileMapAutoCloser mapCloser(map);
864
865 uint32_t fileSize32 = fileSize;
866
867 char *buf = static_cast<char*>(PR_MemMap(map, 0, fileSize32));
868 if (!buf) {
869 NS_WARNING("Failed to map file");
870 return NS_ERROR_FAILURE;
871 }
872
873 if (!mReuseLoaderGlobal) {
874 script = Compile(cx, obj, options, buf,
875 fileSize32);
876 } else {
877 function = CompileFunction(cx, obj, options,
878 nullptr, 0, nullptr,
879 buf, fileSize32);
880 }
881
882 PR_MemUnmap(buf, fileSize32);
883
884 #else /* HAVE_PR_MEMMAP */
885
886 /**
887 * No memmap implementation, so fall back to
888 * reading in the file
889 */
890
891 FILE *fileHandle;
892 rv = aComponentFile->OpenANSIFileDesc("r", &fileHandle);
893 if (NS_FAILED(rv)) {
894 return NS_ERROR_FILE_NOT_FOUND;
895 }
896
897 // Ensure file fclose
898 ANSIFileAutoCloser fileCloser(fileHandle);
899
900 int64_t len;
901 rv = aComponentFile->GetFileSize(&len);
902 if (NS_FAILED(rv) || len < 0) {
903 NS_WARNING("Failed to get file size");
904 return NS_ERROR_FAILURE;
905 }
906
907 char *buf = (char *) malloc(len * sizeof(char));
908 if (!buf) {
909 return NS_ERROR_FAILURE;
910 }
911
912 size_t rlen = fread(buf, 1, len, fileHandle);
913 if (rlen != (uint64_t)len) {
914 free(buf);
915 NS_WARNING("Failed to read file");
916 return NS_ERROR_FAILURE;
917 }
918
919 if (!mReuseLoaderGlobal) {
920 script = Compile(cx, obj, options, buf,
921 fileSize32);
922 } else {
923 function = CompileFunction(cx, obj, options,
924 nullptr, 0, nullptr,
925 buf, fileSize32);
926 }
927
928 free(buf);
929
930 #endif /* HAVE_PR_MEMMAP */
931 } else {
932 nsCOMPtr<nsIIOService> ioService = do_GetIOService(&rv);
933 NS_ENSURE_SUCCESS(rv, rv);
934
935 nsCOMPtr<nsIChannel> scriptChannel;
936 rv = ioService->NewChannelFromURI(aURI, getter_AddRefs(scriptChannel));
937 NS_ENSURE_SUCCESS(rv, rv);
938
939 nsCOMPtr<nsIInputStream> scriptStream;
940 rv = scriptChannel->Open(getter_AddRefs(scriptStream));
941 NS_ENSURE_SUCCESS(rv, rv);
942
943 uint64_t len64;
944 uint32_t bytesRead;
945
946 rv = scriptStream->Available(&len64);
947 NS_ENSURE_SUCCESS(rv, rv);
948 NS_ENSURE_TRUE(len64 < UINT32_MAX, NS_ERROR_FILE_TOO_BIG);
949 if (!len64)
950 return NS_ERROR_FAILURE;
951 uint32_t len = (uint32_t)len64;
952
953 /* malloc an internal buf the size of the file */
954 nsAutoArrayPtr<char> buf(new char[len + 1]);
955 if (!buf)
956 return NS_ERROR_OUT_OF_MEMORY;
957
958 /* read the file in one swoop */
959 rv = scriptStream->Read(buf, len, &bytesRead);
960 if (bytesRead != len)
961 return NS_BASE_STREAM_OSERROR;
962
963 buf[len] = '\0';
964
965 if (!mReuseLoaderGlobal) {
966 script = Compile(cx, obj, options, buf, bytesRead);
967 } else {
968 function = CompileFunction(cx, obj, options,
969 nullptr, 0, nullptr,
970 buf, bytesRead);
971 }
972 }
973 // Propagate the exception, if one exists. Also, don't leave the stale
974 // exception on this context.
975 if (!script && !function && aPropagateExceptions) {
976 JS_GetPendingException(cx, aException);
977 JS_ClearPendingException(cx);
978 }
979 }
980
981 if (!script && !function) {
982 return NS_ERROR_FAILURE;
983 }
984
985 if (writeToCache) {
986 // We successfully compiled the script, so cache it.
987 if (script) {
988 rv = WriteCachedScript(cache, cachePath, cx, mSystemPrincipal,
989 script);
990 } else {
991 rv = WriteCachedFunction(cache, cachePath, cx, mSystemPrincipal,
992 function);
993 }
994
995 // Don't treat failure to write as fatal, since we might be working
996 // with a read-only cache.
997 if (NS_SUCCEEDED(rv)) {
998 LOG(("Successfully wrote to cache\n"));
999 } else {
1000 LOG(("Failed to write to cache\n"));
1001 }
1002 }
1003
1004 // Assign aObject here so that it's available to recursive imports.
1005 // See bug 384168.
1006 aObject.set(obj);
1007
1008 RootedScript tableScript(cx, script);
1009 if (!tableScript) {
1010 tableScript = JS_GetFunctionScript(cx, function);
1011 MOZ_ASSERT(tableScript);
1012 }
1013
1014 aTableScript.set(tableScript);
1015
1016 if (js::GetObjectJSClass(obj) == &kFakeBackstagePassJSClass) {
1017 MOZ_ASSERT(mReuseLoaderGlobal);
1018 // tableScript stays in the table until shutdown. It is rooted by
1019 // virtue of the fact that aTableScript is a handle to
1020 // ModuleEntry::thisObjectKey, which is a PersistentRootedScript. Since
1021 // ModuleEntries are never dynamically unloaded when mReuseLoaderGlobal
1022 // is true, this prevents it from being collected and another script
1023 // getting the same address.
1024 mThisObjects.Put(tableScript, obj);
1025 }
1026 bool ok = false;
1027
1028 {
1029 AutoSaveContextOptions asco(cx);
1030 if (aPropagateExceptions)
1031 ContextOptionsRef(cx).setDontReportUncaught(true);
1032 if (script) {
1033 ok = JS_ExecuteScriptVersion(cx, obj, script, JSVERSION_LATEST);
1034 } else {
1035 RootedValue rval(cx);
1036 ok = JS_CallFunction(cx, obj, function, JS::HandleValueArray::empty(), &rval);
1037 }
1038 }
1039
1040 if (!ok) {
1041 if (aPropagateExceptions) {
1042 JS_GetPendingException(cx, aException);
1043 JS_ClearPendingException(cx);
1044 }
1045 aObject.set(nullptr);
1046 aTableScript.set(nullptr);
1047 mThisObjects.Remove(tableScript);
1048 return NS_ERROR_FAILURE;
1049 }
1050
1051 /* Freed when we remove from the table. */
1052 *aLocation = ToNewCString(nativePath);
1053 if (!*aLocation) {
1054 aObject.set(nullptr);
1055 aTableScript.set(nullptr);
1056 mThisObjects.Remove(tableScript);
1057 return NS_ERROR_OUT_OF_MEMORY;
1058 }
1059
1060 return NS_OK;
1061 }
1062
1063 /* static */ PLDHashOperator
1064 mozJSComponentLoader::ClearModules(const nsACString& key, ModuleEntry*& entry, void* cx)
1065 {
1066 entry->Clear();
1067 return PL_DHASH_REMOVE;
1068 }
1069
1070 void
1071 mozJSComponentLoader::UnloadModules()
1072 {
1073 mInitialized = false;
1074
1075 if (mLoaderGlobal) {
1076 MOZ_ASSERT(mReuseLoaderGlobal, "How did this happen?");
1077
1078 JSAutoRequest ar(mContext);
1079 RootedObject global(mContext, mLoaderGlobal->GetJSObject());
1080 if (global) {
1081 JSAutoCompartment ac(mContext, global);
1082 JS_SetAllNonReservedSlotsToUndefined(mContext, global);
1083 } else {
1084 NS_WARNING("Going to leak!");
1085 }
1086
1087 mLoaderGlobal = nullptr;
1088 }
1089
1090 mInProgressImports.Clear();
1091 mImports.Clear();
1092 mThisObjects.Clear();
1093
1094 mModules.Enumerate(ClearModules, nullptr);
1095
1096 JS_DestroyContextNoGC(mContext);
1097 mContext = nullptr;
1098
1099 mRuntimeService = nullptr;
1100 }
1101
1102 NS_IMETHODIMP
1103 mozJSComponentLoader::Import(const nsACString& registryLocation,
1104 HandleValue targetValArg,
1105 JSContext *cx,
1106 uint8_t optionalArgc,
1107 MutableHandleValue retval)
1108 {
1109 MOZ_ASSERT(nsContentUtils::IsCallerChrome());
1110
1111 RootedValue targetVal(cx, targetValArg);
1112 RootedObject targetObject(cx, nullptr);
1113 if (optionalArgc) {
1114 // The caller passed in the optional second argument. Get it.
1115 if (targetVal.isObject()) {
1116 // If we're passing in something like a content DOM window, chances
1117 // are the caller expects the properties to end up on the object
1118 // proper and not on the Xray holder. This is dubious, but can be used
1119 // during testing. Given that dumb callers can already leak JSMs into
1120 // content by passing a raw content JS object (where Xrays aren't
1121 // possible), we aim for consistency here. Waive xray.
1122 if (WrapperFactory::IsXrayWrapper(&targetVal.toObject()) &&
1123 !WrapperFactory::WaiveXrayAndWrap(cx, &targetVal))
1124 {
1125 return NS_ERROR_FAILURE;
1126 }
1127 targetObject = &targetVal.toObject();
1128 } else if (!targetVal.isNull()) {
1129 // If targetVal isNull(), we actually want to leave targetObject null.
1130 // Not doing so breaks |make package|.
1131 return ReportOnCaller(cx, ERROR_SCOPE_OBJ,
1132 PromiseFlatCString(registryLocation).get());
1133 }
1134 } else {
1135 nsresult rv = FindTargetObject(cx, &targetObject);
1136 NS_ENSURE_SUCCESS(rv, rv);
1137 }
1138
1139 Maybe<JSAutoCompartment> ac;
1140 if (targetObject) {
1141 ac.construct(cx, targetObject);
1142 }
1143
1144 RootedObject global(cx);
1145 nsresult rv = ImportInto(registryLocation, targetObject, cx, &global);
1146
1147 if (global) {
1148 if (!JS_WrapObject(cx, &global)) {
1149 NS_ERROR("can't wrap return value");
1150 return NS_ERROR_FAILURE;
1151 }
1152
1153 retval.setObject(*global);
1154 }
1155 return rv;
1156 }
1157
1158 /* [noscript] JSObjectPtr importInto(in AUTF8String registryLocation,
1159 in JSObjectPtr targetObj); */
1160 NS_IMETHODIMP
1161 mozJSComponentLoader::ImportInto(const nsACString &aLocation,
1162 JSObject *aTargetObj,
1163 nsAXPCNativeCallContext *cc,
1164 JSObject **_retval)
1165 {
1166 JSContext *callercx;
1167 nsresult rv = cc->GetJSContext(&callercx);
1168 NS_ENSURE_SUCCESS(rv, rv);
1169
1170 RootedObject targetObject(callercx, aTargetObj);
1171 RootedObject global(callercx);
1172 rv = ImportInto(aLocation, targetObject, callercx, &global);
1173 NS_ENSURE_SUCCESS(rv, rv);
1174 *_retval = global;
1175 return NS_OK;
1176 }
1177
1178 nsresult
1179 mozJSComponentLoader::ImportInto(const nsACString &aLocation,
1180 HandleObject targetObj,
1181 JSContext *callercx,
1182 MutableHandleObject vp)
1183 {
1184 vp.set(nullptr);
1185
1186 nsresult rv;
1187 if (!mInitialized) {
1188 rv = ReallyInit();
1189 NS_ENSURE_SUCCESS(rv, rv);
1190 }
1191
1192 nsCOMPtr<nsIIOService> ioService = do_GetIOService(&rv);
1193 NS_ENSURE_SUCCESS(rv, rv);
1194
1195 // Get the URI.
1196 nsCOMPtr<nsIURI> resURI;
1197 rv = ioService->NewURI(aLocation, nullptr, nullptr, getter_AddRefs(resURI));
1198 NS_ENSURE_SUCCESS(rv, rv);
1199
1200 // figure out the resolved URI
1201 nsCOMPtr<nsIChannel> scriptChannel;
1202 rv = ioService->NewChannelFromURI(resURI, getter_AddRefs(scriptChannel));
1203 NS_ENSURE_SUCCESS(rv, NS_ERROR_INVALID_ARG);
1204
1205 nsCOMPtr<nsIURI> resolvedURI;
1206 rv = scriptChannel->GetURI(getter_AddRefs(resolvedURI));
1207 NS_ENSURE_SUCCESS(rv, rv);
1208
1209 // get the JAR if there is one
1210 nsCOMPtr<nsIJARURI> jarURI;
1211 jarURI = do_QueryInterface(resolvedURI, &rv);
1212 nsCOMPtr<nsIFileURL> baseFileURL;
1213 if (NS_SUCCEEDED(rv)) {
1214 nsCOMPtr<nsIURI> baseURI;
1215 while (jarURI) {
1216 jarURI->GetJARFile(getter_AddRefs(baseURI));
1217 jarURI = do_QueryInterface(baseURI, &rv);
1218 }
1219 baseFileURL = do_QueryInterface(baseURI, &rv);
1220 NS_ENSURE_SUCCESS(rv, rv);
1221 } else {
1222 baseFileURL = do_QueryInterface(resolvedURI, &rv);
1223 NS_ENSURE_SUCCESS(rv, rv);
1224 }
1225
1226 nsCOMPtr<nsIFile> sourceFile;
1227 rv = baseFileURL->GetFile(getter_AddRefs(sourceFile));
1228 NS_ENSURE_SUCCESS(rv, rv);
1229
1230 nsCOMPtr<nsIFile> sourceLocalFile;
1231 sourceLocalFile = do_QueryInterface(sourceFile, &rv);
1232 NS_ENSURE_SUCCESS(rv, rv);
1233
1234 nsAutoCString key;
1235 rv = resolvedURI->GetSpec(key);
1236 NS_ENSURE_SUCCESS(rv, rv);
1237
1238 ModuleEntry* mod;
1239 nsAutoPtr<ModuleEntry> newEntry;
1240 if (!mImports.Get(key, &mod) && !mInProgressImports.Get(key, &mod)) {
1241 newEntry = new ModuleEntry(callercx);
1242 if (!newEntry)
1243 return NS_ERROR_OUT_OF_MEMORY;
1244 mInProgressImports.Put(key, newEntry);
1245
1246 RootedValue exception(callercx);
1247 rv = ObjectForLocation(sourceLocalFile, resURI, &newEntry->obj,
1248 &newEntry->thisObjectKey,
1249 &newEntry->location, true, &exception);
1250
1251 mInProgressImports.Remove(key);
1252
1253 if (NS_FAILED(rv)) {
1254 if (!exception.isUndefined()) {
1255 // An exception was thrown during compilation. Propagate it
1256 // out to our caller so they can report it.
1257 if (!JS_WrapValue(callercx, &exception))
1258 return NS_ERROR_OUT_OF_MEMORY;
1259 JS_SetPendingException(callercx, exception);
1260 return NS_OK;
1261 }
1262
1263 // Something failed, but we don't know what it is, guess.
1264 return NS_ERROR_FILE_NOT_FOUND;
1265 }
1266
1267 // Set the location information for the new global, so that tools like
1268 // about:memory may use that information
1269 if (!mReuseLoaderGlobal) {
1270 xpc::SetLocationForGlobal(newEntry->obj, aLocation);
1271 }
1272
1273 mod = newEntry;
1274 }
1275
1276 MOZ_ASSERT(mod->obj, "Import table contains entry with no object");
1277 vp.set(mod->obj);
1278
1279 if (targetObj) {
1280 JSCLContextHelper cxhelper(mContext);
1281 JSAutoCompartment ac(mContext, mod->obj);
1282
1283 RootedValue symbols(mContext);
1284 RootedObject modObj(mContext, mod->obj);
1285 if (!JS_GetProperty(mContext, modObj,
1286 "EXPORTED_SYMBOLS", &symbols)) {
1287 return ReportOnCaller(cxhelper, ERROR_NOT_PRESENT,
1288 PromiseFlatCString(aLocation).get());
1289 }
1290
1291 if (!JS_IsArrayObject(mContext, symbols)) {
1292 return ReportOnCaller(cxhelper, ERROR_NOT_AN_ARRAY,
1293 PromiseFlatCString(aLocation).get());
1294 }
1295
1296 RootedObject symbolsObj(mContext, &symbols.toObject());
1297
1298 // Iterate over symbols array, installing symbols on targetObj:
1299
1300 uint32_t symbolCount = 0;
1301 if (!JS_GetArrayLength(mContext, symbolsObj, &symbolCount)) {
1302 return ReportOnCaller(cxhelper, ERROR_GETTING_ARRAY_LENGTH,
1303 PromiseFlatCString(aLocation).get());
1304 }
1305
1306 #ifdef DEBUG
1307 nsAutoCString logBuffer;
1308 #endif
1309
1310 RootedValue value(mContext);
1311 RootedId symbolId(mContext);
1312 for (uint32_t i = 0; i < symbolCount; ++i) {
1313 if (!JS_GetElement(mContext, symbolsObj, i, &value) ||
1314 !value.isString() ||
1315 !JS_ValueToId(mContext, value, &symbolId)) {
1316 return ReportOnCaller(cxhelper, ERROR_ARRAY_ELEMENT,
1317 PromiseFlatCString(aLocation).get(), i);
1318 }
1319
1320 RootedObject modObj(mContext, mod->obj);
1321 if (!JS_GetPropertyById(mContext, modObj, symbolId, &value)) {
1322 JSAutoByteString bytes(mContext, JSID_TO_STRING(symbolId));
1323 if (!bytes)
1324 return NS_ERROR_FAILURE;
1325 return ReportOnCaller(cxhelper, ERROR_GETTING_SYMBOL,
1326 PromiseFlatCString(aLocation).get(),
1327 bytes.ptr());
1328 }
1329
1330 JSAutoCompartment target_ac(mContext, targetObj);
1331
1332 if (!JS_WrapValue(mContext, &value) ||
1333 !JS_SetPropertyById(mContext, targetObj, symbolId, value)) {
1334 JSAutoByteString bytes(mContext, JSID_TO_STRING(symbolId));
1335 if (!bytes)
1336 return NS_ERROR_FAILURE;
1337 return ReportOnCaller(cxhelper, ERROR_SETTING_SYMBOL,
1338 PromiseFlatCString(aLocation).get(),
1339 bytes.ptr());
1340 }
1341 #ifdef DEBUG
1342 if (i == 0) {
1343 logBuffer.AssignLiteral("Installing symbols [ ");
1344 }
1345 JSAutoByteString bytes(mContext, JSID_TO_STRING(symbolId));
1346 if (!!bytes)
1347 logBuffer.Append(bytes.ptr());
1348 logBuffer.AppendLiteral(" ");
1349 if (i == symbolCount - 1) {
1350 LOG(("%s] from %s\n", logBuffer.get(),
1351 PromiseFlatCString(aLocation).get()));
1352 }
1353 #endif
1354 }
1355 }
1356
1357 // Cache this module for later
1358 if (newEntry) {
1359 mImports.Put(key, newEntry);
1360 newEntry.forget();
1361 }
1362
1363 return NS_OK;
1364 }
1365
1366 NS_IMETHODIMP
1367 mozJSComponentLoader::Unload(const nsACString & aLocation)
1368 {
1369 nsresult rv;
1370
1371 if (!mInitialized) {
1372 return NS_OK;
1373 }
1374
1375 nsCOMPtr<nsIIOService> ioService = do_GetIOService(&rv);
1376 NS_ENSURE_SUCCESS(rv, rv);
1377
1378 // Get the URI.
1379 nsCOMPtr<nsIURI> resURI;
1380 rv = ioService->NewURI(aLocation, nullptr, nullptr, getter_AddRefs(resURI));
1381 NS_ENSURE_SUCCESS(rv, rv);
1382
1383 // figure out the resolved URI
1384 nsCOMPtr<nsIChannel> scriptChannel;
1385 rv = ioService->NewChannelFromURI(resURI, getter_AddRefs(scriptChannel));
1386 NS_ENSURE_SUCCESS(rv, NS_ERROR_INVALID_ARG);
1387
1388 nsCOMPtr<nsIURI> resolvedURI;
1389 rv = scriptChannel->GetURI(getter_AddRefs(resolvedURI));
1390 NS_ENSURE_SUCCESS(rv, rv);
1391
1392 nsAutoCString key;
1393 rv = resolvedURI->GetSpec(key);
1394 NS_ENSURE_SUCCESS(rv, rv);
1395
1396 ModuleEntry* mod;
1397 if (mImports.Get(key, &mod)) {
1398 mImports.Remove(key);
1399 }
1400
1401 return NS_OK;
1402 }
1403
1404 NS_IMETHODIMP
1405 mozJSComponentLoader::Observe(nsISupports *subject, const char *topic,
1406 const char16_t *data)
1407 {
1408 if (!strcmp(topic, "xpcom-shutdown-loaders")) {
1409 UnloadModules();
1410 } else {
1411 NS_ERROR("Unexpected observer topic.");
1412 }
1413
1414 return NS_OK;
1415 }
1416
1417 size_t
1418 mozJSComponentLoader::ModuleEntry::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
1419 {
1420 size_t n = aMallocSizeOf(this);
1421 n += aMallocSizeOf(location);
1422
1423 return n;
1424 }
1425
1426 /* static */ already_AddRefed<nsIFactory>
1427 mozJSComponentLoader::ModuleEntry::GetFactory(const mozilla::Module& module,
1428 const mozilla::Module::CIDEntry& entry)
1429 {
1430 const ModuleEntry& self = static_cast<const ModuleEntry&>(module);
1431 MOZ_ASSERT(self.getfactoryobj, "Handing out an uninitialized module?");
1432
1433 nsCOMPtr<nsIFactory> f;
1434 nsresult rv = self.getfactoryobj->Get(*entry.cid, getter_AddRefs(f));
1435 if (NS_FAILED(rv))
1436 return nullptr;
1437
1438 return f.forget();
1439 }
1440
1441 //----------------------------------------------------------------------
1442
1443 JSCLContextHelper::JSCLContextHelper(JSContext* aCx)
1444 : mContext(aCx)
1445 , mBuf(nullptr)
1446 {
1447 mPusher.Push(mContext);
1448 JS_BeginRequest(mContext);
1449 }
1450
1451 JSCLContextHelper::~JSCLContextHelper()
1452 {
1453 JS_EndRequest(mContext);
1454 mPusher.Pop();
1455 JSContext *restoredCx = nsContentUtils::GetCurrentJSContext();
1456 if (restoredCx && mBuf) {
1457 JS_ReportError(restoredCx, mBuf);
1458 }
1459
1460 if (mBuf) {
1461 JS_smprintf_free(mBuf);
1462 }
1463 }
1464
1465 void
1466 JSCLContextHelper::reportErrorAfterPop(char *buf)
1467 {
1468 MOZ_ASSERT(!mBuf, "Already called reportErrorAfterPop");
1469 mBuf = buf;
1470 }

mercurial