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