/src/mozilla-central/js/xpconnect/loader/mozJSComponentLoader.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 "mozilla/Attributes.h" |
8 | | |
9 | | #include <cstdarg> |
10 | | |
11 | | #include "mozilla/Logging.h" |
12 | | #ifdef ANDROID |
13 | | #include <android/log.h> |
14 | | #endif |
15 | | #ifdef XP_WIN |
16 | | #include <windows.h> |
17 | | #endif |
18 | | |
19 | | #include "jsapi.h" |
20 | | #include "js/CharacterEncoding.h" |
21 | | #include "js/CompilationAndEvaluation.h" |
22 | | #include "js/Printf.h" |
23 | | #include "nsCOMPtr.h" |
24 | | #include "nsAutoPtr.h" |
25 | | #include "nsExceptionHandler.h" |
26 | | #include "nsIComponentManager.h" |
27 | | #include "mozilla/Module.h" |
28 | | #include "nsIFile.h" |
29 | | #include "mozJSComponentLoader.h" |
30 | | #include "mozJSLoaderUtils.h" |
31 | | #include "nsIXPConnect.h" |
32 | | #include "nsIObserverService.h" |
33 | | #include "nsIScriptSecurityManager.h" |
34 | | #include "nsIFileURL.h" |
35 | | #include "nsIJARURI.h" |
36 | | #include "nsNetUtil.h" |
37 | | #include "nsJSPrincipals.h" |
38 | | #include "nsJSUtils.h" |
39 | | #include "xpcprivate.h" |
40 | | #include "xpcpublic.h" |
41 | | #include "nsContentUtils.h" |
42 | | #include "nsReadableUtils.h" |
43 | | #include "nsXULAppAPI.h" |
44 | | #include "GeckoProfiler.h" |
45 | | #include "WrapperFactory.h" |
46 | | |
47 | | #include "AutoMemMap.h" |
48 | | #include "ScriptPreloader-inl.h" |
49 | | |
50 | | #include "mozilla/scache/StartupCache.h" |
51 | | #include "mozilla/scache/StartupCacheUtils.h" |
52 | | #include "mozilla/MacroForEach.h" |
53 | | #include "mozilla/Preferences.h" |
54 | | #include "mozilla/ResultExtensions.h" |
55 | | #include "mozilla/ScriptPreloader.h" |
56 | | #include "mozilla/dom/DOMPrefs.h" |
57 | | #include "mozilla/dom/ScriptSettings.h" |
58 | | #include "mozilla/ResultExtensions.h" |
59 | | #include "mozilla/UniquePtrExtensions.h" |
60 | | #include "mozilla/Unused.h" |
61 | | |
62 | | using namespace mozilla; |
63 | | using namespace mozilla::scache; |
64 | | using namespace mozilla::loader; |
65 | | using namespace xpc; |
66 | | using namespace JS; |
67 | | |
68 | | static const char kObserverServiceContractID[] = "@mozilla.org/observer-service;1"; |
69 | | |
70 | 5 | #define JS_CACHE_PREFIX(aType) "jsloader/" aType |
71 | | |
72 | | /** |
73 | | * Buffer sizes for serialization and deserialization of scripts. |
74 | | * FIXME: bug #411579 (tune this macro!) Last updated: Jan 2008 |
75 | | */ |
76 | | #define XPC_SERIALIZATION_BUFFER_SIZE (64 * 1024) |
77 | | #define XPC_DESERIALIZATION_BUFFER_SIZE (12 * 8192) |
78 | | |
79 | | // MOZ_LOG=JSComponentLoader:5 |
80 | | static LazyLogModule gJSCLLog("JSComponentLoader"); |
81 | | |
82 | 10 | #define LOG(args) MOZ_LOG(gJSCLLog, mozilla::LogLevel::Debug, args) |
83 | | |
84 | | // Components.utils.import error messages |
85 | 0 | #define ERROR_SCOPE_OBJ "%s - Second argument must be an object." |
86 | 0 | #define ERROR_NOT_PRESENT "%s - EXPORTED_SYMBOLS is not present." |
87 | 0 | #define ERROR_NOT_AN_ARRAY "%s - EXPORTED_SYMBOLS is not an array." |
88 | 0 | #define ERROR_GETTING_ARRAY_LENGTH "%s - Error getting array length of EXPORTED_SYMBOLS." |
89 | 0 | #define ERROR_ARRAY_ELEMENT "%s - EXPORTED_SYMBOLS[%d] is not a string." |
90 | 0 | #define ERROR_GETTING_SYMBOL "%s - Could not get symbol '%s'." |
91 | | #define ERROR_SETTING_SYMBOL "%s - Could not set symbol '%s' on target object." |
92 | | |
93 | | static bool |
94 | | Dump(JSContext* cx, unsigned argc, Value* vp) |
95 | 0 | { |
96 | 0 | if (!mozilla::dom::DOMPrefs::DumpEnabled()) { |
97 | 0 | return true; |
98 | 0 | } |
99 | 0 | |
100 | 0 | CallArgs args = CallArgsFromVp(argc, vp); |
101 | 0 |
|
102 | 0 | if (args.length() == 0) { |
103 | 0 | return true; |
104 | 0 | } |
105 | 0 | |
106 | 0 | RootedString str(cx, JS::ToString(cx, args[0])); |
107 | 0 | if (!str) { |
108 | 0 | return false; |
109 | 0 | } |
110 | 0 | |
111 | 0 | JS::UniqueChars utf8str = JS_EncodeStringToUTF8(cx, str); |
112 | 0 | if (!utf8str) { |
113 | 0 | return false; |
114 | 0 | } |
115 | 0 | |
116 | | #ifdef ANDROID |
117 | | __android_log_print(ANDROID_LOG_INFO, "Gecko", "%s", utf8str.get()); |
118 | | #endif |
119 | | #ifdef XP_WIN |
120 | | if (IsDebuggerPresent()) { |
121 | | nsAutoJSString wstr; |
122 | | if (!wstr.init(cx, str)) { |
123 | | return false; |
124 | | } |
125 | | OutputDebugStringW(wstr.get()); |
126 | | } |
127 | | #endif |
128 | 0 | fputs(utf8str.get(), stdout); |
129 | 0 | fflush(stdout); |
130 | 0 | return true; |
131 | 0 | } |
132 | | |
133 | | static bool |
134 | | Debug(JSContext* cx, unsigned argc, Value* vp) |
135 | 0 | { |
136 | | #ifdef DEBUG |
137 | | return Dump(cx, argc, vp); |
138 | | #else |
139 | | return true; |
140 | 0 | #endif |
141 | 0 | } |
142 | | |
143 | | static const JSFunctionSpec gGlobalFun[] = { |
144 | | JS_FN("dump", Dump, 1,0), |
145 | | JS_FN("debug", Debug, 1,0), |
146 | | JS_FN("atob", Atob, 1,0), |
147 | | JS_FN("btoa", Btoa, 1,0), |
148 | | JS_FS_END |
149 | | }; |
150 | | |
151 | | class MOZ_STACK_CLASS JSCLContextHelper |
152 | | { |
153 | | public: |
154 | | explicit JSCLContextHelper(JSContext* aCx); |
155 | | ~JSCLContextHelper(); |
156 | | |
157 | | void reportErrorAfterPop(UniqueChars&& buf); |
158 | | |
159 | | private: |
160 | | JSContext* mContext; |
161 | | UniqueChars mBuf; |
162 | | |
163 | | // prevent copying and assignment |
164 | | JSCLContextHelper(const JSCLContextHelper&) = delete; |
165 | | const JSCLContextHelper& operator=(const JSCLContextHelper&) = delete; |
166 | | }; |
167 | | |
168 | | static nsresult |
169 | | MOZ_FORMAT_PRINTF(2, 3) |
170 | | ReportOnCallerUTF8(JSContext* callerContext, |
171 | 0 | const char* format, ...) { |
172 | 0 | if (!callerContext) { |
173 | 0 | return NS_ERROR_FAILURE; |
174 | 0 | } |
175 | 0 | |
176 | 0 | va_list ap; |
177 | 0 | va_start(ap, format); |
178 | 0 |
|
179 | 0 | UniqueChars buf = JS_vsmprintf(format, ap); |
180 | 0 | if (!buf) { |
181 | 0 | va_end(ap); |
182 | 0 | return NS_ERROR_OUT_OF_MEMORY; |
183 | 0 | } |
184 | 0 | |
185 | 0 | JS_ReportErrorUTF8(callerContext, "%s", buf.get()); |
186 | 0 |
|
187 | 0 | va_end(ap); |
188 | 0 | return NS_OK; |
189 | 0 | } |
190 | | |
191 | | mozJSComponentLoader::mozJSComponentLoader() |
192 | | : mModules(16), |
193 | | mImports(16), |
194 | | mInProgressImports(16), |
195 | | mLocations(16), |
196 | | mInitialized(false), |
197 | | mShareLoaderGlobal(false), |
198 | | mLoaderGlobal(dom::RootingCx()) |
199 | 3 | { |
200 | 3 | MOZ_ASSERT(!sSelf, "mozJSComponentLoader should be a singleton"); |
201 | 3 | } |
202 | | |
203 | | // static |
204 | | already_AddRefed<mozJSComponentLoader> |
205 | | mozJSComponentLoader::GetOrCreate() |
206 | 6 | { |
207 | 6 | if (!sSelf) { |
208 | 3 | sSelf = new mozJSComponentLoader(); |
209 | 3 | } |
210 | 6 | return do_AddRef(sSelf); |
211 | 6 | } |
212 | | |
213 | 20 | #define ENSURE_DEP(name) { nsresult rv = Ensure##name(); NS_ENSURE_SUCCESS(rv, rv); } |
214 | 20 | #define ENSURE_DEPS(...) MOZ_FOR_EACH(ENSURE_DEP, (), (__VA_ARGS__)); |
215 | 30 | #define BEGIN_ENSURE(self, ...) { \ |
216 | 30 | if (m##self) \ |
217 | 30 | return NS_OK; \ |
218 | 30 | ENSURE_DEPS(__VA_ARGS__); \ |
219 | 15 | } |
220 | | |
221 | | class MOZ_STACK_CLASS ComponentLoaderInfo { |
222 | | public: |
223 | 9 | explicit ComponentLoaderInfo(const nsACString& aLocation) : mLocation(aLocation) {} |
224 | | |
225 | 0 | nsIIOService* IOService() { MOZ_ASSERT(mIOService); return mIOService; } |
226 | 10 | nsresult EnsureIOService() { |
227 | 10 | if (mIOService) { |
228 | 5 | return NS_OK; |
229 | 5 | } |
230 | 5 | nsresult rv; |
231 | 5 | mIOService = do_GetIOService(&rv); |
232 | 5 | return rv; |
233 | 5 | } |
234 | | |
235 | 10 | nsIURI* URI() { MOZ_ASSERT(mURI); return mURI; } |
236 | 16 | nsresult EnsureURI() { |
237 | 16 | BEGIN_ENSURE(URI, IOService); |
238 | 5 | return mIOService->NewURI(mLocation, nullptr, nullptr, getter_AddRefs(mURI)); |
239 | 5 | } |
240 | | |
241 | 5 | nsIChannel* ScriptChannel() { MOZ_ASSERT(mScriptChannel); return mScriptChannel; } |
242 | 5 | nsresult EnsureScriptChannel() { |
243 | 5 | BEGIN_ENSURE(ScriptChannel, IOService, URI); |
244 | 5 | return NS_NewChannel(getter_AddRefs(mScriptChannel), |
245 | 5 | mURI, |
246 | 5 | nsContentUtils::GetSystemPrincipal(), |
247 | 5 | nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL, |
248 | 5 | nsIContentPolicy::TYPE_SCRIPT, |
249 | 5 | nullptr, // aPerformanceStorage |
250 | 5 | nullptr, // aLoadGroup |
251 | 5 | nullptr, // aCallbacks |
252 | 5 | nsIRequest::LOAD_NORMAL, |
253 | 5 | mIOService); |
254 | 10 | } |
255 | | |
256 | 13 | nsIURI* ResolvedURI() { MOZ_ASSERT(mResolvedURI); return mResolvedURI; } |
257 | 9 | nsresult EnsureResolvedURI() { |
258 | 9 | BEGIN_ENSURE(ResolvedURI, URI); |
259 | 5 | return ResolveURI(mURI, getter_AddRefs(mResolvedURI)); |
260 | 5 | } |
261 | | |
262 | 28 | const nsACString& Key() { return mLocation; } |
263 | 8 | nsresult EnsureKey() { |
264 | 8 | return NS_OK; |
265 | 8 | } |
266 | | |
267 | 0 | MOZ_MUST_USE nsresult GetLocation(nsCString& aLocation) { |
268 | 0 | nsresult rv = EnsureURI(); |
269 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
270 | 0 | return mURI->GetSpec(aLocation); |
271 | 0 | } |
272 | | |
273 | | private: |
274 | | const nsACString& mLocation; |
275 | | nsCOMPtr<nsIIOService> mIOService; |
276 | | nsCOMPtr<nsIURI> mURI; |
277 | | nsCOMPtr<nsIChannel> mScriptChannel; |
278 | | nsCOMPtr<nsIURI> mResolvedURI; |
279 | | }; |
280 | | |
281 | | template <typename ...Args> |
282 | | static nsresult |
283 | | ReportOnCallerUTF8(JSCLContextHelper& helper, |
284 | | const char* format, |
285 | | ComponentLoaderInfo& info, |
286 | | Args... args) |
287 | 0 | { |
288 | 0 | nsCString location; |
289 | 0 | MOZ_TRY(info.GetLocation(location)); |
290 | 0 |
|
291 | 0 | UniqueChars buf = JS_smprintf(format, location.get(), args...); |
292 | 0 | if (!buf) { |
293 | 0 | return NS_ERROR_OUT_OF_MEMORY; |
294 | 0 | } |
295 | 0 | |
296 | 0 | helper.reportErrorAfterPop(std::move(buf)); |
297 | 0 | return NS_ERROR_FAILURE; |
298 | 0 | } Unexecuted instantiation: mozJSComponentLoader.cpp:nsresult ReportOnCallerUTF8<>(JSCLContextHelper&, char const*, ComponentLoaderInfo&) Unexecuted instantiation: mozJSComponentLoader.cpp:nsresult ReportOnCallerUTF8<unsigned int>(JSCLContextHelper&, char const*, ComponentLoaderInfo&, unsigned int) Unexecuted instantiation: mozJSComponentLoader.cpp:nsresult ReportOnCallerUTF8<char*>(JSCLContextHelper&, char const*, ComponentLoaderInfo&, char*) |
299 | | |
300 | | #undef BEGIN_ENSURE |
301 | | #undef ENSURE_DEPS |
302 | | #undef ENSURE_DEP |
303 | | |
304 | | mozJSComponentLoader::~mozJSComponentLoader() |
305 | 0 | { |
306 | 0 | if (mInitialized) { |
307 | 0 | NS_ERROR("'xpcom-shutdown-loaders' was not fired before cleaning up mozJSComponentLoader"); |
308 | 0 | UnloadModules(); |
309 | 0 | } |
310 | 0 |
|
311 | 0 | sSelf = nullptr; |
312 | 0 | } |
313 | | |
314 | | mozJSComponentLoader* |
315 | | mozJSComponentLoader::sSelf; |
316 | | |
317 | | NS_IMPL_ISUPPORTS(mozJSComponentLoader, |
318 | | mozilla::ModuleLoader, |
319 | | xpcIJSModuleLoader, |
320 | | nsIObserver) |
321 | | |
322 | | nsresult |
323 | | mozJSComponentLoader::ReallyInit() |
324 | 1 | { |
325 | 1 | MOZ_ASSERT(!mInitialized); |
326 | 1 | |
327 | 1 | const char* shareGlobal = PR_GetEnv("MOZ_LOADER_SHARE_GLOBAL"); |
328 | 1 | if (shareGlobal && *shareGlobal) { |
329 | 0 | nsDependentCString val(shareGlobal); |
330 | 0 | mShareLoaderGlobal = !(val.EqualsLiteral("0") || |
331 | 0 | val.LowerCaseEqualsLiteral("no") || |
332 | 0 | val.LowerCaseEqualsLiteral("false") || |
333 | 0 | val.LowerCaseEqualsLiteral("off")); |
334 | 1 | } else { |
335 | 1 | mShareLoaderGlobal = Preferences::GetBool("jsloader.shareGlobal"); |
336 | 1 | } |
337 | 1 | |
338 | 1 | nsresult rv; |
339 | 1 | nsCOMPtr<nsIObserverService> obsSvc = |
340 | 1 | do_GetService(kObserverServiceContractID, &rv); |
341 | 1 | NS_ENSURE_SUCCESS(rv, rv); |
342 | 1 | |
343 | 1 | rv = obsSvc->AddObserver(this, "xpcom-shutdown-loaders", false); |
344 | 1 | NS_ENSURE_SUCCESS(rv, rv); |
345 | 1 | |
346 | 1 | mInitialized = true; |
347 | 1 | |
348 | 1 | return NS_OK; |
349 | 1 | } |
350 | | |
351 | | // For terrible compatibility reasons, we need to consider both the global |
352 | | // lexical environment and the global of modules when searching for exported |
353 | | // symbols. |
354 | | static JSObject* |
355 | | ResolveModuleObjectProperty(JSContext* aCx, HandleObject aModObj, const char* name) |
356 | 5 | { |
357 | 5 | if (JS_HasExtensibleLexicalEnvironment(aModObj)) { |
358 | 5 | RootedObject lexical(aCx, JS_ExtensibleLexicalEnvironment(aModObj)); |
359 | 5 | bool found; |
360 | 5 | if (!JS_HasOwnProperty(aCx, lexical, name, &found)) { |
361 | 0 | return nullptr; |
362 | 0 | } |
363 | 5 | if (found) { |
364 | 0 | return lexical; |
365 | 0 | } |
366 | 5 | } |
367 | 5 | return aModObj; |
368 | 5 | } |
369 | | |
370 | | static mozilla::Result<nsCString, nsresult> ReadScript(ComponentLoaderInfo& aInfo); |
371 | | |
372 | | static nsresult |
373 | | AnnotateScriptContents(CrashReporter::Annotation aName, const nsACString& aURI) |
374 | 0 | { |
375 | 0 | ComponentLoaderInfo info(aURI); |
376 | 0 |
|
377 | 0 | nsCString str; |
378 | 0 | MOZ_TRY_VAR(str, ReadScript(info)); |
379 | 0 |
|
380 | 0 | // The crash reporter won't accept any strings with embedded nuls. We |
381 | 0 | // shouldn't have any here, but if we do because of data corruption, we |
382 | 0 | // still want the annotation. So replace any embedded nuls before |
383 | 0 | // annotating. |
384 | 0 | str.ReplaceSubstring(NS_LITERAL_CSTRING("\0"), NS_LITERAL_CSTRING("\\0")); |
385 | 0 |
|
386 | 0 | CrashReporter::AnnotateCrashReport(aName, str); |
387 | 0 |
|
388 | 0 | return NS_OK; |
389 | 0 | } |
390 | | |
391 | | nsresult |
392 | | mozJSComponentLoader::AnnotateCrashReport() |
393 | 0 | { |
394 | 0 | Unused << AnnotateScriptContents( |
395 | 0 | CrashReporter::Annotation::nsAsyncShutdownComponent, |
396 | 0 | NS_LITERAL_CSTRING("resource://gre/components/nsAsyncShutdown.js")); |
397 | 0 |
|
398 | 0 | Unused << AnnotateScriptContents( |
399 | 0 | CrashReporter::Annotation::AsyncShutdownModule, |
400 | 0 | NS_LITERAL_CSTRING("resource://gre/modules/AsyncShutdown.jsm")); |
401 | 0 |
|
402 | 0 | return NS_OK; |
403 | 0 | } |
404 | | |
405 | | const mozilla::Module* |
406 | | mozJSComponentLoader::LoadModule(FileLocation& aFile) |
407 | 1 | { |
408 | 1 | if (!NS_IsMainThread()) { |
409 | 0 | MOZ_ASSERT(false, "Don't use JS components off the main thread"); |
410 | 0 | return nullptr; |
411 | 0 | } |
412 | 1 | |
413 | 1 | nsCOMPtr<nsIFile> file = aFile.GetBaseFile(); |
414 | 1 | |
415 | 1 | nsCString spec; |
416 | 1 | aFile.GetURIString(spec); |
417 | 1 | ComponentLoaderInfo info(spec); |
418 | 1 | nsresult rv = info.EnsureURI(); |
419 | 1 | NS_ENSURE_SUCCESS(rv, nullptr); |
420 | 1 | |
421 | 1 | if (!mInitialized) { |
422 | 1 | rv = ReallyInit(); |
423 | 1 | if (NS_FAILED(rv)) { |
424 | 0 | return nullptr; |
425 | 0 | } |
426 | 1 | } |
427 | 1 | |
428 | 1 | AUTO_PROFILER_LABEL_DYNAMIC_NSCSTRING( |
429 | 1 | "mozJSComponentLoader::LoadModule", OTHER, spec); |
430 | 1 | |
431 | 1 | ModuleEntry* mod; |
432 | 1 | if (mModules.Get(spec, &mod)) { |
433 | 0 | return mod; |
434 | 0 | } |
435 | 1 | |
436 | 1 | dom::AutoJSAPI jsapi; |
437 | 1 | jsapi.Init(); |
438 | 1 | JSContext* cx = jsapi.cx(); |
439 | 1 | |
440 | 1 | bool isCriticalModule = StringEndsWith(spec, NS_LITERAL_CSTRING("/nsAsyncShutdown.js")); |
441 | 1 | |
442 | 1 | nsAutoPtr<ModuleEntry> entry(new ModuleEntry(RootingContext::get(cx))); |
443 | 1 | RootedValue exn(cx); |
444 | 1 | rv = ObjectForLocation(info, file, &entry->obj, &entry->thisObjectKey, |
445 | 1 | &entry->location, isCriticalModule, &exn); |
446 | 1 | if (NS_FAILED(rv)) { |
447 | 0 | // Temporary debugging assertion for bug 1403348: |
448 | 0 | if (isCriticalModule && !exn.isUndefined()) { |
449 | 0 | AnnotateCrashReport(); |
450 | 0 |
|
451 | 0 | JSAutoRealm ar(cx, xpc::PrivilegedJunkScope()); |
452 | 0 | JS_WrapValue(cx, &exn); |
453 | 0 |
|
454 | 0 | nsAutoCString file; |
455 | 0 | uint32_t line; |
456 | 0 | uint32_t column; |
457 | 0 | nsAutoString msg; |
458 | 0 | nsContentUtils::ExtractErrorValues(cx, exn, file, &line, &column, msg); |
459 | 0 |
|
460 | 0 | NS_ConvertUTF16toUTF8 cMsg(msg); |
461 | 0 | MOZ_CRASH_UNSAFE_PRINTF("Failed to load module \"%s\": " |
462 | 0 | "[\"%s\" {file: \"%s\", line: %u}]", |
463 | 0 | spec.get(), cMsg.get(), file.get(), line); |
464 | 0 | } |
465 | 0 | return nullptr; |
466 | 1 | } |
467 | 1 | |
468 | 1 | nsCOMPtr<nsIComponentManager> cm; |
469 | 1 | rv = NS_GetComponentManager(getter_AddRefs(cm)); |
470 | 1 | if (NS_FAILED(rv)) { |
471 | 0 | return nullptr; |
472 | 0 | } |
473 | 1 | |
474 | 1 | JSAutoRealm ar(cx, entry->obj); |
475 | 1 | RootedObject entryObj(cx, entry->obj); |
476 | 1 | |
477 | 1 | RootedObject NSGetFactoryHolder(cx, ResolveModuleObjectProperty(cx, entryObj, "NSGetFactory")); |
478 | 1 | RootedValue NSGetFactory_val(cx); |
479 | 1 | if (!NSGetFactoryHolder || |
480 | 1 | !JS_GetProperty(cx, NSGetFactoryHolder, "NSGetFactory", &NSGetFactory_val) || |
481 | 1 | NSGetFactory_val.isUndefined()) |
482 | 0 | { |
483 | 0 | return nullptr; |
484 | 0 | } |
485 | 1 | |
486 | 1 | if (JS_TypeOfValue(cx, NSGetFactory_val) != JSTYPE_FUNCTION) { |
487 | 0 | /* |
488 | 0 | * spec's encoding is ASCII unless it's zip file, otherwise it's |
489 | 0 | * random encoding. Latin1 variant is safe for random encoding. |
490 | 0 | */ |
491 | 0 | JS_ReportErrorLatin1(cx, "%s has NSGetFactory property that is not a function", |
492 | 0 | spec.get()); |
493 | 0 | return nullptr; |
494 | 0 | } |
495 | 1 | |
496 | 1 | RootedObject jsGetFactoryObj(cx); |
497 | 1 | if (!JS_ValueToObject(cx, NSGetFactory_val, &jsGetFactoryObj) || |
498 | 1 | !jsGetFactoryObj) { |
499 | 0 | /* XXX report error properly */ |
500 | 0 | return nullptr; |
501 | 0 | } |
502 | 1 | |
503 | 1 | rv = nsXPConnect::XPConnect()->WrapJS(cx, jsGetFactoryObj, |
504 | 1 | NS_GET_IID(xpcIJSGetFactory), |
505 | 1 | getter_AddRefs(entry->getfactoryobj)); |
506 | 1 | if (NS_FAILED(rv)) { |
507 | 0 | /* XXX report error properly */ |
508 | | #ifdef DEBUG |
509 | | fprintf(stderr, "mJCL: couldn't get nsIModule from jsval\n"); |
510 | | #endif |
511 | | return nullptr; |
512 | 0 | } |
513 | 1 | |
514 | 1 | #if defined(NIGHTLY_BUILD) || defined(DEBUG) |
515 | 1 | if (Preferences::GetBool("browser.startup.record", false)) { |
516 | 0 | entry->importStack = xpc_PrintJSStack(cx, false, false, false).get(); |
517 | 0 | } |
518 | 1 | #endif |
519 | 1 | |
520 | 1 | // Cache this module for later |
521 | 1 | mModules.Put(spec, entry); |
522 | 1 | |
523 | 1 | // The hash owns the ModuleEntry now, forget about it |
524 | 1 | return entry.forget(); |
525 | 1 | } |
526 | | |
527 | | void |
528 | | mozJSComponentLoader::FindTargetObject(JSContext* aCx, |
529 | | MutableHandleObject aTargetObject) |
530 | 8 | { |
531 | 8 | aTargetObject.set(js::GetJSMEnvironmentOfScriptedCaller(aCx)); |
532 | 8 | |
533 | 8 | // The above could fail if the scripted caller is not a component/JSM (it |
534 | 8 | // could be a DOM scope, for instance). |
535 | 8 | // |
536 | 8 | // If the target object was not in the JSM shared global, return the global |
537 | 8 | // instead. This is needed when calling the subscript loader within a frame |
538 | 8 | // script, since it the FrameScript NSVO will have been found. |
539 | 8 | if (!aTargetObject || |
540 | 8 | !IsLoaderGlobal(JS::GetNonCCWObjectGlobal(aTargetObject))) { |
541 | 0 | aTargetObject.set(CurrentGlobalOrNull(aCx)); |
542 | 0 | } |
543 | 8 | } |
544 | | |
545 | | // This requires that the keys be strings and the values be pointers. |
546 | | template <class Key, class Data, class UserData> |
547 | | static size_t |
548 | | SizeOfTableExcludingThis(const nsBaseHashtable<Key, Data, UserData>& aTable, |
549 | | MallocSizeOf aMallocSizeOf) |
550 | 0 | { |
551 | 0 | size_t n = aTable.ShallowSizeOfExcludingThis(aMallocSizeOf); |
552 | 0 | for (auto iter = aTable.ConstIter(); !iter.Done(); iter.Next()) { |
553 | 0 | n += iter.Key().SizeOfExcludingThisIfUnshared(aMallocSizeOf); |
554 | 0 | n += iter.Data()->SizeOfIncludingThis(aMallocSizeOf); |
555 | 0 | } |
556 | 0 | return n; |
557 | 0 | } Unexecuted instantiation: mozJSComponentLoader.cpp:unsigned long SizeOfTableExcludingThis<nsCStringHashKey, mozJSComponentLoader::ModuleEntry*, mozJSComponentLoader::ModuleEntry*>(nsBaseHashtable<nsCStringHashKey, mozJSComponentLoader::ModuleEntry*, mozJSComponentLoader::ModuleEntry*> const&, unsigned long (*)(void const*)) Unexecuted instantiation: mozJSComponentLoader.cpp:unsigned long SizeOfTableExcludingThis<nsCStringHashKey, nsAutoPtr<mozJSComponentLoader::ModuleEntry>, mozJSComponentLoader::ModuleEntry*>(nsBaseHashtable<nsCStringHashKey, nsAutoPtr<mozJSComponentLoader::ModuleEntry>, mozJSComponentLoader::ModuleEntry*> const&, unsigned long (*)(void const*)) |
558 | | |
559 | | size_t |
560 | | mozJSComponentLoader::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) |
561 | 0 | { |
562 | 0 | size_t n = aMallocSizeOf(this); |
563 | 0 | n += SizeOfTableExcludingThis(mModules, aMallocSizeOf); |
564 | 0 | n += SizeOfTableExcludingThis(mImports, aMallocSizeOf); |
565 | 0 | n += mLocations.ShallowSizeOfExcludingThis(aMallocSizeOf); |
566 | 0 | n += SizeOfTableExcludingThis(mInProgressImports, aMallocSizeOf); |
567 | 0 | return n; |
568 | 0 | } |
569 | | |
570 | | void |
571 | | mozJSComponentLoader::CreateLoaderGlobal(JSContext* aCx, |
572 | | const nsACString& aLocation, |
573 | | MutableHandleObject aGlobal) |
574 | 3 | { |
575 | 3 | RefPtr<BackstagePass> backstagePass; |
576 | 3 | nsresult rv = NS_NewBackstagePass(getter_AddRefs(backstagePass)); |
577 | 3 | NS_ENSURE_SUCCESS_VOID(rv); |
578 | 3 | |
579 | 3 | RealmOptions options; |
580 | 3 | |
581 | 3 | options.creationOptions() |
582 | 3 | .setNewCompartmentInSystemZone(); |
583 | 3 | |
584 | 3 | if (xpc::SharedMemoryEnabled()) { |
585 | 0 | options.creationOptions().setSharedMemoryAndAtomicsEnabled(true); |
586 | 0 | } |
587 | 3 | |
588 | 3 | // Defer firing OnNewGlobalObject until after the __URI__ property has |
589 | 3 | // been defined so the JS debugger can tell what module the global is |
590 | 3 | // for |
591 | 3 | RootedObject global(aCx); |
592 | 3 | rv = xpc::InitClassesWithNewWrappedGlobal(aCx, |
593 | 3 | static_cast<nsIGlobalObject*>(backstagePass), |
594 | 3 | nsContentUtils::GetSystemPrincipal(), |
595 | 3 | xpc::DONT_FIRE_ONNEWGLOBALHOOK, |
596 | 3 | options, |
597 | 3 | &global); |
598 | 3 | NS_ENSURE_SUCCESS_VOID(rv); |
599 | 3 | |
600 | 3 | NS_ENSURE_TRUE_VOID(global); |
601 | 3 | |
602 | 3 | backstagePass->SetGlobalObject(global); |
603 | 3 | |
604 | 3 | JSAutoRealm ar(aCx, global); |
605 | 3 | if (!JS_DefineFunctions(aCx, global, gGlobalFun) || |
606 | 3 | !JS_DefineProfilingFunctions(aCx, global)) { |
607 | 0 | return; |
608 | 0 | } |
609 | 3 | |
610 | 3 | // Set the location information for the new global, so that tools like |
611 | 3 | // about:memory may use that information |
612 | 3 | xpc::SetLocationForGlobal(global, aLocation); |
613 | 3 | |
614 | 3 | aGlobal.set(global); |
615 | 3 | } |
616 | | |
617 | | bool |
618 | | mozJSComponentLoader::ReuseGlobal(nsIURI* aURI) |
619 | 5 | { |
620 | 5 | if (!mShareLoaderGlobal) { |
621 | 0 | return false; |
622 | 0 | } |
623 | 5 | |
624 | 5 | nsCString spec; |
625 | 5 | NS_ENSURE_SUCCESS(aURI->GetSpec(spec), false); |
626 | 5 | |
627 | 5 | // The loader calls Object.freeze on global properties, which |
628 | 5 | // causes problems if the global is shared with other code. |
629 | 5 | if (spec.EqualsASCII("resource://gre/modules/commonjs/toolkit/loader.js")) { |
630 | 0 | return false; |
631 | 0 | } |
632 | 5 | |
633 | 5 | // Various tests call addDebuggerToGlobal on the result of |
634 | 5 | // importing this JSM, which would be annoying to fix. |
635 | 5 | if (spec.EqualsASCII("resource://gre/modules/jsdebugger.jsm")) { |
636 | 0 | return false; |
637 | 0 | } |
638 | 5 | |
639 | 5 | // Some SpecialPowers jsms call Cu.forcePermissiveCOWs(), |
640 | 5 | // which sets a per-compartment flag that disables certain |
641 | 5 | // security wrappers, so don't use the shared global for them |
642 | 5 | // to avoid breaking tests. |
643 | 5 | if (FindInReadable(NS_LITERAL_CSTRING("resource://specialpowers/"), spec)) { |
644 | 0 | return false; |
645 | 0 | } |
646 | 5 | |
647 | 5 | return true; |
648 | 5 | } |
649 | | |
650 | | JSObject* |
651 | | mozJSComponentLoader::GetSharedGlobal(JSContext* aCx) |
652 | 8 | { |
653 | 8 | if (!mLoaderGlobal) { |
654 | 3 | JS::RootedObject globalObj(aCx); |
655 | 3 | CreateLoaderGlobal(aCx, NS_LITERAL_CSTRING("shared JSM global"), |
656 | 3 | &globalObj); |
657 | 3 | |
658 | 3 | // If we fail to create a module global this early, we're not going to |
659 | 3 | // get very far, so just bail out now. |
660 | 3 | MOZ_RELEASE_ASSERT(globalObj); |
661 | 3 | mLoaderGlobal = globalObj; |
662 | 3 | |
663 | 3 | // AutoEntryScript required to invoke debugger hook, which is a |
664 | 3 | // Gecko-specific concept at present. |
665 | 3 | dom::AutoEntryScript aes(globalObj, |
666 | 3 | "component loader report global"); |
667 | 3 | JS_FireOnNewGlobalObject(aes.cx(), globalObj); |
668 | 3 | } |
669 | 8 | |
670 | 8 | return mLoaderGlobal; |
671 | 8 | } |
672 | | |
673 | | JSObject* |
674 | | mozJSComponentLoader::PrepareObjectForLocation(JSContext* aCx, |
675 | | nsIFile* aComponentFile, |
676 | | nsIURI* aURI, |
677 | | bool* aReuseGlobal, |
678 | | bool* aRealFile) |
679 | 5 | { |
680 | 5 | nsAutoCString nativePath; |
681 | 5 | NS_ENSURE_SUCCESS(aURI->GetSpec(nativePath), nullptr); |
682 | 5 | |
683 | 5 | bool reuseGlobal = ReuseGlobal(aURI); |
684 | 5 | |
685 | 5 | *aReuseGlobal = reuseGlobal; |
686 | 5 | |
687 | 5 | bool createdNewGlobal = false; |
688 | 5 | RootedObject globalObj(aCx); |
689 | 5 | if (reuseGlobal) { |
690 | 5 | globalObj = GetSharedGlobal(aCx); |
691 | 5 | } else if (!globalObj) { |
692 | 0 | CreateLoaderGlobal(aCx, nativePath, &globalObj); |
693 | 0 | createdNewGlobal = true; |
694 | 0 | } |
695 | 5 | |
696 | 5 | // |thisObj| is the object we set properties on for a particular .jsm. |
697 | 5 | RootedObject thisObj(aCx, globalObj); |
698 | 5 | NS_ENSURE_TRUE(thisObj, nullptr); |
699 | 5 | |
700 | 5 | JSAutoRealm ar(aCx, thisObj); |
701 | 5 | |
702 | 5 | if (reuseGlobal) { |
703 | 5 | thisObj = js::NewJSMEnvironment(aCx); |
704 | 5 | NS_ENSURE_TRUE(thisObj, nullptr); |
705 | 5 | } |
706 | 5 | |
707 | 5 | *aRealFile = false; |
708 | 5 | |
709 | 5 | // need to be extra careful checking for URIs pointing to files |
710 | 5 | // EnsureFile may not always get called, especially on resource URIs |
711 | 5 | // so we need to call GetFile to make sure this is a valid file |
712 | 5 | nsresult rv = NS_OK; |
713 | 5 | nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(aURI, &rv); |
714 | 5 | nsCOMPtr<nsIFile> testFile; |
715 | 5 | if (NS_SUCCEEDED(rv)) { |
716 | 4 | fileURL->GetFile(getter_AddRefs(testFile)); |
717 | 4 | } |
718 | 5 | |
719 | 5 | if (testFile) { |
720 | 0 | *aRealFile = true; |
721 | 0 |
|
722 | 0 | if (XRE_IsParentProcess()) { |
723 | 0 | RootedObject locationObj(aCx); |
724 | 0 |
|
725 | 0 | rv = nsXPConnect::XPConnect()->WrapNative(aCx, thisObj, aComponentFile, |
726 | 0 | NS_GET_IID(nsIFile), |
727 | 0 | locationObj.address()); |
728 | 0 | NS_ENSURE_SUCCESS(rv, nullptr); |
729 | 0 | NS_ENSURE_TRUE(locationObj, nullptr); |
730 | 0 |
|
731 | 0 | if (!JS_DefineProperty(aCx, thisObj, "__LOCATION__", locationObj, 0)) { |
732 | 0 | return nullptr; |
733 | 0 | } |
734 | 5 | } |
735 | 0 | } |
736 | 5 | |
737 | 5 | // Expose the URI from which the script was imported through a special |
738 | 5 | // variable that we insert into the JSM. |
739 | 5 | RootedString exposedUri(aCx, JS_NewStringCopyN(aCx, nativePath.get(), nativePath.Length())); |
740 | 5 | NS_ENSURE_TRUE(exposedUri, nullptr); |
741 | 5 | |
742 | 5 | if (!JS_DefineProperty(aCx, thisObj, "__URI__", exposedUri, 0)) { |
743 | 0 | return nullptr; |
744 | 0 | } |
745 | 5 | |
746 | 5 | if (createdNewGlobal) { |
747 | 0 | // AutoEntryScript required to invoke debugger hook, which is a |
748 | 0 | // Gecko-specific concept at present. |
749 | 0 | dom::AutoEntryScript aes(globalObj, |
750 | 0 | "component loader report global"); |
751 | 0 | JS_FireOnNewGlobalObject(aes.cx(), globalObj); |
752 | 0 | } |
753 | 5 | |
754 | 5 | return thisObj; |
755 | 5 | } |
756 | | |
757 | | static mozilla::Result<nsCString, nsresult> |
758 | | ReadScript(ComponentLoaderInfo& aInfo) |
759 | 5 | { |
760 | 5 | MOZ_TRY(aInfo.EnsureScriptChannel()); |
761 | 5 | |
762 | 5 | nsCOMPtr<nsIInputStream> scriptStream; |
763 | 5 | MOZ_TRY(NS_MaybeOpenChannelUsingOpen2(aInfo.ScriptChannel(), |
764 | 5 | getter_AddRefs(scriptStream))); |
765 | 5 | |
766 | 5 | uint64_t len64; |
767 | 5 | uint32_t bytesRead; |
768 | 5 | |
769 | 5 | MOZ_TRY(scriptStream->Available(&len64)); |
770 | 5 | NS_ENSURE_TRUE(len64 < UINT32_MAX, Err(NS_ERROR_FILE_TOO_BIG)); |
771 | 5 | NS_ENSURE_TRUE(len64, Err(NS_ERROR_FAILURE)); |
772 | 5 | uint32_t len = (uint32_t)len64; |
773 | 5 | |
774 | 5 | /* malloc an internal buf the size of the file */ |
775 | 5 | nsCString str; |
776 | 5 | if (!str.SetLength(len, fallible)) { |
777 | 0 | return Err(NS_ERROR_OUT_OF_MEMORY); |
778 | 0 | } |
779 | 5 | |
780 | 5 | /* read the file in one swoop */ |
781 | 5 | MOZ_TRY(scriptStream->Read(str.BeginWriting(), len, &bytesRead)); |
782 | 5 | if (bytesRead != len) { |
783 | 0 | return Err(NS_BASE_STREAM_OSERROR); |
784 | 0 | } |
785 | 5 | |
786 | 5 | return std::move(str); |
787 | 5 | } |
788 | | |
789 | | nsresult |
790 | | mozJSComponentLoader::ObjectForLocation(ComponentLoaderInfo& aInfo, |
791 | | nsIFile* aComponentFile, |
792 | | MutableHandleObject aObject, |
793 | | MutableHandleScript aTableScript, |
794 | | char** aLocation, |
795 | | bool aPropagateExceptions, |
796 | | MutableHandleValue aException) |
797 | 5 | { |
798 | 5 | MOZ_ASSERT(NS_IsMainThread(), "Must be on main thread."); |
799 | 5 | |
800 | 5 | dom::AutoJSAPI jsapi; |
801 | 5 | jsapi.Init(); |
802 | 5 | JSContext* cx = jsapi.cx(); |
803 | 5 | |
804 | 5 | bool realFile = false; |
805 | 5 | nsresult rv = aInfo.EnsureURI(); |
806 | 5 | NS_ENSURE_SUCCESS(rv, rv); |
807 | 5 | bool reuseGlobal = false; |
808 | 5 | RootedObject obj(cx, PrepareObjectForLocation(cx, aComponentFile, aInfo.URI(), |
809 | 5 | &reuseGlobal, &realFile)); |
810 | 5 | NS_ENSURE_TRUE(obj, NS_ERROR_FAILURE); |
811 | 5 | MOZ_ASSERT(JS_IsGlobalObject(obj) == !reuseGlobal); |
812 | 5 | |
813 | 5 | JSAutoRealm ar(cx, obj); |
814 | 5 | |
815 | 5 | RootedScript script(cx); |
816 | 5 | |
817 | 5 | nsAutoCString nativePath; |
818 | 5 | rv = aInfo.URI()->GetSpec(nativePath); |
819 | 5 | NS_ENSURE_SUCCESS(rv, rv); |
820 | 5 | |
821 | 5 | // Before compiling the script, first check to see if we have it in |
822 | 5 | // the startupcache. Note: as a rule, startupcache errors are not fatal |
823 | 5 | // to loading the script, since we can always slow-load. |
824 | 5 | |
825 | 5 | bool writeToCache = false; |
826 | 5 | StartupCache* cache = StartupCache::GetSingleton(); |
827 | 5 | |
828 | 5 | aInfo.EnsureResolvedURI(); |
829 | 5 | |
830 | 5 | nsAutoCString cachePath(reuseGlobal ? JS_CACHE_PREFIX("non-syntactic") |
831 | 5 | : JS_CACHE_PREFIX("global")); |
832 | 5 | rv = PathifyURI(aInfo.ResolvedURI(), cachePath); |
833 | 5 | NS_ENSURE_SUCCESS(rv, rv); |
834 | 5 | |
835 | 5 | script = ScriptPreloader::GetSingleton().GetCachedScript(cx, cachePath); |
836 | 5 | if (!script && cache) { |
837 | 5 | ReadCachedScript(cache, cachePath, cx, &script); |
838 | 5 | } |
839 | 5 | |
840 | 5 | if (script) { |
841 | 0 | LOG(("Successfully loaded %s from startupcache\n", nativePath.get())); |
842 | 5 | } else if (cache) { |
843 | 5 | // This is ok, it just means the script is not yet in the |
844 | 5 | // cache. Could mean that the cache was corrupted and got removed, |
845 | 5 | // but either way we're going to write this out. |
846 | 5 | writeToCache = true; |
847 | 5 | // ReadCachedScript may have set a pending exception. |
848 | 5 | JS_ClearPendingException(cx); |
849 | 5 | } |
850 | 5 | |
851 | 5 | if (!script) { |
852 | 5 | // The script wasn't in the cache , so compile it now. |
853 | 5 | LOG(("Slow loading %s\n", nativePath.get())); |
854 | 5 | |
855 | 5 | // If we are debugging a replaying process and have diverged from the |
856 | 5 | // recording, trying to load and compile new code will cause the |
857 | 5 | // debugger operation to fail, so just abort now. |
858 | 5 | if (recordreplay::HasDivergedFromRecording()) { |
859 | 0 | return NS_ERROR_FAILURE; |
860 | 0 | } |
861 | 5 | |
862 | 5 | // Use lazy source if we're using the startup cache. Non-lazy source + |
863 | 5 | // startup cache regresses installer size (due to source code stored in |
864 | 5 | // XDR encoded modules in omni.ja). Also, XDR decoding is relatively |
865 | 5 | // fast. When we're not using the startup cache, we want to use non-lazy |
866 | 5 | // source code so that we can use lazy parsing. |
867 | 5 | // See bug 1303754. |
868 | 5 | CompileOptions options(cx); |
869 | 5 | options.setNoScriptRval(true) |
870 | 5 | .maybeMakeStrictMode(true) |
871 | 5 | .setFileAndLine(nativePath.get(), 1) |
872 | 5 | .setSourceIsLazy(cache || ScriptPreloader::GetSingleton().Active()); |
873 | 5 | |
874 | 5 | if (realFile) { |
875 | 0 | AutoMemMap map; |
876 | 0 | MOZ_TRY(map.init(aComponentFile)); |
877 | 0 |
|
878 | 0 | // Note: exceptions will get handled further down; |
879 | 0 | // don't early return for them here. |
880 | 0 | auto buf = map.get<char>(); |
881 | 0 | if (reuseGlobal) { |
882 | 0 | CompileLatin1ForNonSyntacticScope(cx, options, buf.get(), map.size(), &script); |
883 | 0 | } else { |
884 | 0 | CompileLatin1(cx, options, buf.get(), map.size(), &script); |
885 | 0 | } |
886 | 5 | } else { |
887 | 5 | nsCString str; |
888 | 5 | MOZ_TRY_VAR(str, ReadScript(aInfo)); |
889 | 5 | |
890 | 5 | if (reuseGlobal) { |
891 | 5 | CompileLatin1ForNonSyntacticScope(cx, options, str.get(), str.Length(), &script); |
892 | 5 | } else { |
893 | 0 | CompileLatin1(cx, options, str.get(), str.Length(), &script); |
894 | 0 | } |
895 | 5 | } |
896 | 5 | // Propagate the exception, if one exists. Also, don't leave the stale |
897 | 5 | // exception on this context. |
898 | 5 | if (!script && aPropagateExceptions && jsapi.HasException()) { |
899 | 0 | if (!jsapi.StealException(aException)) { |
900 | 0 | return NS_ERROR_OUT_OF_MEMORY; |
901 | 0 | } |
902 | 5 | } |
903 | 5 | } |
904 | 5 | |
905 | 5 | if (!script) { |
906 | 0 | return NS_ERROR_FAILURE; |
907 | 0 | } |
908 | 5 | |
909 | 5 | ScriptPreloader::GetSingleton().NoteScript(nativePath, cachePath, script); |
910 | 5 | |
911 | 5 | if (writeToCache) { |
912 | 5 | // We successfully compiled the script, so cache it. |
913 | 5 | rv = WriteCachedScript(cache, cachePath, cx, script); |
914 | 5 | |
915 | 5 | // Don't treat failure to write as fatal, since we might be working |
916 | 5 | // with a read-only cache. |
917 | 5 | if (NS_SUCCEEDED(rv)) { |
918 | 5 | LOG(("Successfully wrote to cache\n")); |
919 | 5 | } else { |
920 | 0 | LOG(("Failed to write to cache\n")); |
921 | 0 | } |
922 | 5 | } |
923 | 5 | |
924 | 5 | // Assign aObject here so that it's available to recursive imports. |
925 | 5 | // See bug 384168. |
926 | 5 | aObject.set(obj); |
927 | 5 | |
928 | 5 | aTableScript.set(script); |
929 | 5 | |
930 | 5 | |
931 | 5 | { // Scope for AutoEntryScript |
932 | 5 | |
933 | 5 | // We're going to run script via JS_ExecuteScript, so we need an |
934 | 5 | // AutoEntryScript. This is Gecko-specific and not in any spec. |
935 | 5 | dom::AutoEntryScript aes(CurrentGlobalOrNull(cx), |
936 | 5 | "component loader load module"); |
937 | 5 | JSContext* aescx = aes.cx(); |
938 | 5 | |
939 | 5 | bool executeOk = false; |
940 | 5 | if (JS_IsGlobalObject(obj)) { |
941 | 0 | JS::RootedValue rval(cx); |
942 | 0 | executeOk = JS::CloneAndExecuteScript(aescx, script, &rval); |
943 | 5 | } else { |
944 | 5 | executeOk = js::ExecuteInJSMEnvironment(aescx, script, obj); |
945 | 5 | } |
946 | 5 | |
947 | 5 | if (!executeOk) { |
948 | 0 | if (aPropagateExceptions && aes.HasException()) { |
949 | 0 | // Ignore return value because we're returning an error code |
950 | 0 | // anyway. |
951 | 0 | Unused << aes.StealException(aException); |
952 | 0 | } |
953 | 0 | aObject.set(nullptr); |
954 | 0 | aTableScript.set(nullptr); |
955 | 0 | return NS_ERROR_FAILURE; |
956 | 0 | } |
957 | 5 | } |
958 | 5 | |
959 | 5 | /* Freed when we remove from the table. */ |
960 | 5 | *aLocation = ToNewCString(nativePath); |
961 | 5 | if (!*aLocation) { |
962 | 0 | aObject.set(nullptr); |
963 | 0 | aTableScript.set(nullptr); |
964 | 0 | return NS_ERROR_OUT_OF_MEMORY; |
965 | 0 | } |
966 | 5 | |
967 | 5 | return NS_OK; |
968 | 5 | } |
969 | | |
970 | | void |
971 | | mozJSComponentLoader::UnloadModules() |
972 | 0 | { |
973 | 0 | mInitialized = false; |
974 | 0 |
|
975 | 0 | if (mLoaderGlobal) { |
976 | 0 | dom::AutoJSAPI jsapi; |
977 | 0 | jsapi.Init(); |
978 | 0 | JSContext* cx = jsapi.cx(); |
979 | 0 | RootedObject global(cx, mLoaderGlobal); |
980 | 0 | JSAutoRealm ar(cx, global); |
981 | 0 | MOZ_ASSERT(JS_HasExtensibleLexicalEnvironment(global)); |
982 | 0 | JS_SetAllNonReservedSlotsToUndefined(cx, JS_ExtensibleLexicalEnvironment(global)); |
983 | 0 | JS_SetAllNonReservedSlotsToUndefined(cx, global); |
984 | 0 | mLoaderGlobal = nullptr; |
985 | 0 | } |
986 | 0 |
|
987 | 0 | mInProgressImports.Clear(); |
988 | 0 | mImports.Clear(); |
989 | 0 | mLocations.Clear(); |
990 | 0 |
|
991 | 0 | for (auto iter = mModules.Iter(); !iter.Done(); iter.Next()) { |
992 | 0 | iter.Data()->Clear(); |
993 | 0 | iter.Remove(); |
994 | 0 | } |
995 | 0 | } |
996 | | |
997 | | nsresult |
998 | | mozJSComponentLoader::ImportInto(const nsACString& registryLocation, |
999 | | HandleValue targetValArg, |
1000 | | JSContext* cx, |
1001 | | uint8_t optionalArgc, |
1002 | | MutableHandleValue retval) |
1003 | 8 | { |
1004 | 8 | MOZ_ASSERT(nsContentUtils::IsCallerChrome()); |
1005 | 8 | |
1006 | 8 | RootedValue targetVal(cx, targetValArg); |
1007 | 8 | RootedObject targetObject(cx, nullptr); |
1008 | 8 | |
1009 | 8 | if (optionalArgc) { |
1010 | 0 | // The caller passed in the optional second argument. Get it. |
1011 | 0 | if (targetVal.isObject()) { |
1012 | 0 | // If we're passing in something like a content DOM window, chances |
1013 | 0 | // are the caller expects the properties to end up on the object |
1014 | 0 | // proper and not on the Xray holder. This is dubious, but can be used |
1015 | 0 | // during testing. Given that dumb callers can already leak JSMs into |
1016 | 0 | // content by passing a raw content JS object (where Xrays aren't |
1017 | 0 | // possible), we aim for consistency here. Waive xray. |
1018 | 0 | if (WrapperFactory::IsXrayWrapper(&targetVal.toObject()) && |
1019 | 0 | !WrapperFactory::WaiveXrayAndWrap(cx, &targetVal)) |
1020 | 0 | { |
1021 | 0 | return NS_ERROR_FAILURE; |
1022 | 0 | } |
1023 | 0 | targetObject = &targetVal.toObject(); |
1024 | 0 | } else if (!targetVal.isNull()) { |
1025 | 0 | // If targetVal isNull(), we actually want to leave targetObject null. |
1026 | 0 | // Not doing so breaks |make package|. |
1027 | 0 | return ReportOnCallerUTF8(cx, ERROR_SCOPE_OBJ, |
1028 | 0 | PromiseFlatCString(registryLocation).get()); |
1029 | 0 | } |
1030 | 8 | } else { |
1031 | 8 | FindTargetObject(cx, &targetObject); |
1032 | 8 | } |
1033 | 8 | |
1034 | 8 | js::AssertSameCompartment(cx, targetObject); |
1035 | 8 | |
1036 | 8 | RootedObject global(cx); |
1037 | 8 | nsresult rv = ImportInto(registryLocation, targetObject, cx, &global); |
1038 | 8 | |
1039 | 8 | if (global) { |
1040 | 8 | if (!JS_WrapObject(cx, &global)) { |
1041 | 0 | NS_ERROR("can't wrap return value"); |
1042 | 0 | return NS_ERROR_FAILURE; |
1043 | 0 | } |
1044 | 8 | |
1045 | 8 | retval.setObject(*global); |
1046 | 8 | } |
1047 | 8 | return rv; |
1048 | 8 | } |
1049 | | |
1050 | | nsresult |
1051 | | mozJSComponentLoader::IsModuleLoaded(const nsACString& aLocation, |
1052 | | bool* retval) |
1053 | 0 | { |
1054 | 0 | MOZ_ASSERT(nsContentUtils::IsCallerChrome()); |
1055 | 0 |
|
1056 | 0 | nsresult rv; |
1057 | 0 | if (!mInitialized) { |
1058 | 0 | rv = ReallyInit(); |
1059 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1060 | 0 | } |
1061 | 0 |
|
1062 | 0 | ComponentLoaderInfo info(aLocation); |
1063 | 0 | rv = info.EnsureKey(); |
1064 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1065 | 0 |
|
1066 | 0 | *retval = !!mImports.Get(info.Key()); |
1067 | 0 | return NS_OK; |
1068 | 0 | } |
1069 | | |
1070 | | NS_IMETHODIMP mozJSComponentLoader::LoadedModules(uint32_t* length, |
1071 | | char*** aModules) |
1072 | 0 | { |
1073 | 0 | char** modules = new char*[mImports.Count()]; |
1074 | 0 | *length = mImports.Count(); |
1075 | 0 | *aModules = modules; |
1076 | 0 |
|
1077 | 0 | for (auto iter = mImports.Iter(); !iter.Done(); iter.Next()) { |
1078 | 0 | *modules = NS_xstrdup(iter.Data()->location); |
1079 | 0 | modules++; |
1080 | 0 | } |
1081 | 0 |
|
1082 | 0 | return NS_OK; |
1083 | 0 | } |
1084 | | |
1085 | | NS_IMETHODIMP mozJSComponentLoader::LoadedComponents(uint32_t* length, |
1086 | | char*** aComponents) |
1087 | 0 | { |
1088 | 0 | char** comp = new char*[mModules.Count()]; |
1089 | 0 | *length = mModules.Count(); |
1090 | 0 | *aComponents = comp; |
1091 | 0 |
|
1092 | 0 | for (auto iter = mModules.Iter(); !iter.Done(); iter.Next()) { |
1093 | 0 | *comp = NS_xstrdup(iter.Data()->location); |
1094 | 0 | comp++; |
1095 | 0 | } |
1096 | 0 |
|
1097 | 0 | return NS_OK; |
1098 | 0 | } |
1099 | | |
1100 | | NS_IMETHODIMP |
1101 | | mozJSComponentLoader::GetModuleImportStack(const nsACString& aLocation, |
1102 | | nsACString& retval) |
1103 | 0 | { |
1104 | 0 | #ifdef STARTUP_RECORDER_ENABLED |
1105 | 0 | MOZ_ASSERT(nsContentUtils::IsCallerChrome()); |
1106 | 0 | MOZ_ASSERT(mInitialized); |
1107 | 0 |
|
1108 | 0 | ComponentLoaderInfo info(aLocation); |
1109 | 0 | nsresult rv = info.EnsureKey(); |
1110 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1111 | 0 |
|
1112 | 0 | ModuleEntry* mod; |
1113 | 0 | if (!mImports.Get(info.Key(), &mod)) { |
1114 | 0 | return NS_ERROR_FAILURE; |
1115 | 0 | } |
1116 | 0 | |
1117 | 0 | retval = mod->importStack; |
1118 | 0 | return NS_OK; |
1119 | | #else |
1120 | | return NS_ERROR_NOT_IMPLEMENTED; |
1121 | | #endif |
1122 | | } |
1123 | | |
1124 | | NS_IMETHODIMP |
1125 | | mozJSComponentLoader::GetComponentLoadStack(const nsACString& aLocation, |
1126 | | nsACString& retval) |
1127 | 0 | { |
1128 | 0 | #ifdef STARTUP_RECORDER_ENABLED |
1129 | 0 | MOZ_ASSERT(nsContentUtils::IsCallerChrome()); |
1130 | 0 | MOZ_ASSERT(mInitialized); |
1131 | 0 |
|
1132 | 0 | ComponentLoaderInfo info(aLocation); |
1133 | 0 | nsresult rv = info.EnsureURI(); |
1134 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1135 | 0 |
|
1136 | 0 | ModuleEntry* mod; |
1137 | 0 | if (!mModules.Get(info.Key(), &mod)) { |
1138 | 0 | return NS_ERROR_FAILURE; |
1139 | 0 | } |
1140 | 0 | |
1141 | 0 | retval = mod->importStack; |
1142 | 0 | return NS_OK; |
1143 | | #else |
1144 | | return NS_ERROR_NOT_IMPLEMENTED; |
1145 | | #endif |
1146 | | } |
1147 | | |
1148 | | static JSObject* |
1149 | | ResolveModuleObjectPropertyById(JSContext* aCx, HandleObject aModObj, HandleId id) |
1150 | 4 | { |
1151 | 4 | if (JS_HasExtensibleLexicalEnvironment(aModObj)) { |
1152 | 4 | RootedObject lexical(aCx, JS_ExtensibleLexicalEnvironment(aModObj)); |
1153 | 4 | bool found; |
1154 | 4 | if (!JS_HasOwnPropertyById(aCx, lexical, id, &found)) { |
1155 | 0 | return nullptr; |
1156 | 0 | } |
1157 | 4 | if (found) { |
1158 | 0 | return lexical; |
1159 | 0 | } |
1160 | 4 | } |
1161 | 4 | return aModObj; |
1162 | 4 | } |
1163 | | |
1164 | | nsresult |
1165 | | mozJSComponentLoader::ImportInto(const nsACString& aLocation, |
1166 | | HandleObject targetObj, |
1167 | | JSContext* cx, |
1168 | | MutableHandleObject vp) |
1169 | 8 | { |
1170 | 8 | vp.set(nullptr); |
1171 | 8 | |
1172 | 8 | JS::RootedObject exports(cx); |
1173 | 8 | MOZ_TRY(Import(cx, aLocation, vp, &exports, !targetObj)); |
1174 | 8 | |
1175 | 8 | if (targetObj) { |
1176 | 8 | JS::Rooted<JS::IdVector> ids(cx, JS::IdVector(cx)); |
1177 | 8 | if (!JS_Enumerate(cx, exports, &ids)) { |
1178 | 0 | return NS_ERROR_OUT_OF_MEMORY; |
1179 | 0 | } |
1180 | 8 | |
1181 | 8 | JS::RootedValue value(cx); |
1182 | 8 | JS::RootedId id(cx); |
1183 | 8 | for (jsid idVal : ids) { |
1184 | 8 | id = idVal; |
1185 | 8 | if (!JS_GetPropertyById(cx, exports, id, &value) || |
1186 | 8 | !JS_SetPropertyById(cx, targetObj, id, value)) { |
1187 | 0 | return NS_ERROR_FAILURE; |
1188 | 0 | } |
1189 | 8 | } |
1190 | 8 | } |
1191 | 8 | |
1192 | 8 | return NS_OK; |
1193 | 8 | } |
1194 | | |
1195 | | nsresult |
1196 | | mozJSComponentLoader::ExtractExports(JSContext* aCx, ComponentLoaderInfo& aInfo, |
1197 | | ModuleEntry* aMod, |
1198 | | JS::MutableHandleObject aExports) |
1199 | 4 | { |
1200 | 4 | // cxhelper must be created before jsapi, so that jsapi is destroyed and |
1201 | 4 | // pops any context it has pushed before we report to the caller context. |
1202 | 4 | JSCLContextHelper cxhelper(aCx); |
1203 | 4 | |
1204 | 4 | // Even though we are calling JS_SetPropertyById on targetObj, we want |
1205 | 4 | // to ensure that we never run script here, so we use an AutoJSAPI and |
1206 | 4 | // not an AutoEntryScript. |
1207 | 4 | dom::AutoJSAPI jsapi; |
1208 | 4 | jsapi.Init(); |
1209 | 4 | JSContext* cx = jsapi.cx(); |
1210 | 4 | JSAutoRealm ar(cx, aMod->obj); |
1211 | 4 | |
1212 | 4 | RootedValue symbols(cx); |
1213 | 4 | { |
1214 | 4 | RootedObject obj(cx, ResolveModuleObjectProperty(cx, aMod->obj, |
1215 | 4 | "EXPORTED_SYMBOLS")); |
1216 | 4 | if (!obj || !JS_GetProperty(cx, obj, "EXPORTED_SYMBOLS", &symbols)) { |
1217 | 0 | return ReportOnCallerUTF8(cxhelper, ERROR_NOT_PRESENT, aInfo); |
1218 | 0 | } |
1219 | 4 | } |
1220 | 4 | |
1221 | 4 | bool isArray; |
1222 | 4 | if (!JS_IsArrayObject(cx, symbols, &isArray)) { |
1223 | 0 | return NS_ERROR_FAILURE; |
1224 | 0 | } |
1225 | 4 | if (!isArray) { |
1226 | 0 | return ReportOnCallerUTF8(cxhelper, ERROR_NOT_AN_ARRAY, aInfo); |
1227 | 0 | } |
1228 | 4 | |
1229 | 4 | RootedObject symbolsObj(cx, &symbols.toObject()); |
1230 | 4 | |
1231 | 4 | // Iterate over symbols array, installing symbols on targetObj: |
1232 | 4 | |
1233 | 4 | uint32_t symbolCount = 0; |
1234 | 4 | if (!JS_GetArrayLength(cx, symbolsObj, &symbolCount)) { |
1235 | 0 | return ReportOnCallerUTF8(cxhelper, ERROR_GETTING_ARRAY_LENGTH, |
1236 | 0 | aInfo); |
1237 | 0 | } |
1238 | 4 | |
1239 | | #ifdef DEBUG |
1240 | | nsAutoCString logBuffer; |
1241 | | #endif |
1242 | | |
1243 | 4 | aExports.set(JS_NewPlainObject(cx)); |
1244 | 4 | if (!aExports) { |
1245 | 0 | return NS_ERROR_OUT_OF_MEMORY; |
1246 | 0 | } |
1247 | 4 | |
1248 | 4 | bool missing = false; |
1249 | 4 | |
1250 | 4 | RootedValue value(cx); |
1251 | 4 | RootedId symbolId(cx); |
1252 | 4 | RootedObject symbolHolder(cx); |
1253 | 8 | for (uint32_t i = 0; i < symbolCount; ++i) { |
1254 | 4 | if (!JS_GetElement(cx, symbolsObj, i, &value) || |
1255 | 4 | !value.isString() || |
1256 | 4 | !JS_ValueToId(cx, value, &symbolId)) { |
1257 | 0 | return ReportOnCallerUTF8(cxhelper, ERROR_ARRAY_ELEMENT, aInfo, i); |
1258 | 0 | } |
1259 | 4 | |
1260 | 4 | symbolHolder = ResolveModuleObjectPropertyById(cx, aMod->obj, symbolId); |
1261 | 4 | if (!symbolHolder || |
1262 | 4 | !JS_GetPropertyById(cx, symbolHolder, symbolId, &value)) { |
1263 | 0 | RootedString symbolStr(cx, JSID_TO_STRING(symbolId)); |
1264 | 0 | JS::UniqueChars bytes = JS_EncodeStringToUTF8(cx, symbolStr); |
1265 | 0 | if (!bytes) { |
1266 | 0 | return NS_ERROR_FAILURE; |
1267 | 0 | } |
1268 | 0 | return ReportOnCallerUTF8(cxhelper, ERROR_GETTING_SYMBOL, |
1269 | 0 | aInfo, bytes.get()); |
1270 | 0 | } |
1271 | 4 | |
1272 | 4 | if (value.isUndefined()) { |
1273 | 0 | missing = true; |
1274 | 0 | } |
1275 | 4 | |
1276 | 4 | if (!JS_SetPropertyById(cx, aExports, symbolId, value)) { |
1277 | 0 | RootedString symbolStr(cx, JSID_TO_STRING(symbolId)); |
1278 | 0 | JS::UniqueChars bytes = JS_EncodeStringToUTF8(cx, symbolStr); |
1279 | 0 | if (!bytes) { |
1280 | 0 | return NS_ERROR_FAILURE; |
1281 | 0 | } |
1282 | 0 | return ReportOnCallerUTF8(cxhelper, ERROR_GETTING_SYMBOL, |
1283 | 0 | aInfo, bytes.get()); |
1284 | 0 | } |
1285 | | #ifdef DEBUG |
1286 | | if (i == 0) { |
1287 | | logBuffer.AssignLiteral("Installing symbols [ "); |
1288 | | } |
1289 | | JS::UniqueChars bytes = JS_EncodeStringToLatin1(cx, JSID_TO_STRING(symbolId)); |
1290 | | if (!!bytes) { |
1291 | | logBuffer.Append(bytes.get()); |
1292 | | } |
1293 | | logBuffer.Append(' '); |
1294 | | if (i == symbolCount - 1) { |
1295 | | nsCString location; |
1296 | | MOZ_TRY(aInfo.GetLocation(location)); |
1297 | | LOG(("%s] from %s\n", logBuffer.get(), location.get())); |
1298 | | } |
1299 | | #endif |
1300 | | } |
1301 | 4 | |
1302 | 4 | // Don't cache the exports object if any of its exported symbols are |
1303 | 4 | // missing. If the module hasn't finished loading yet, they may be |
1304 | 4 | // defined the next time we try to import it. |
1305 | 4 | if (!missing) { |
1306 | 4 | aMod->exports = aExports; |
1307 | 4 | } |
1308 | 4 | return NS_OK; |
1309 | 4 | } |
1310 | | |
1311 | | nsresult |
1312 | | mozJSComponentLoader::Import(JSContext* aCx, const nsACString& aLocation, |
1313 | | JS::MutableHandleObject aModuleGlobal, |
1314 | | JS::MutableHandleObject aModuleExports, |
1315 | | bool aIgnoreExports) |
1316 | 8 | { |
1317 | 8 | nsresult rv; |
1318 | 8 | if (!mInitialized) { |
1319 | 0 | rv = ReallyInit(); |
1320 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1321 | 0 | } |
1322 | 8 | |
1323 | 8 | AUTO_PROFILER_LABEL_DYNAMIC_NSCSTRING( |
1324 | 8 | "mozJSComponentLoader::Import", JS, aLocation); |
1325 | 8 | ComponentLoaderInfo info(aLocation); |
1326 | 8 | |
1327 | 8 | rv = info.EnsureKey(); |
1328 | 8 | NS_ENSURE_SUCCESS(rv, rv); |
1329 | 8 | |
1330 | 8 | ModuleEntry* mod; |
1331 | 8 | nsAutoPtr<ModuleEntry> newEntry; |
1332 | 8 | if (!mImports.Get(info.Key(), &mod) && !mInProgressImports.Get(info.Key(), &mod)) { |
1333 | 4 | newEntry = new ModuleEntry(RootingContext::get(aCx)); |
1334 | 4 | if (!newEntry) { |
1335 | 0 | return NS_ERROR_OUT_OF_MEMORY; |
1336 | 0 | } |
1337 | 4 | |
1338 | 4 | // Note: This implies EnsureURI(). |
1339 | 4 | MOZ_TRY(info.EnsureResolvedURI()); |
1340 | 4 | |
1341 | 4 | // get the JAR if there is one |
1342 | 4 | nsCOMPtr<nsIJARURI> jarURI; |
1343 | 4 | jarURI = do_QueryInterface(info.ResolvedURI(), &rv); |
1344 | 4 | nsCOMPtr<nsIFileURL> baseFileURL; |
1345 | 4 | if (NS_SUCCEEDED(rv)) { |
1346 | 4 | nsCOMPtr<nsIURI> baseURI; |
1347 | 8 | while (jarURI) { |
1348 | 4 | jarURI->GetJARFile(getter_AddRefs(baseURI)); |
1349 | 4 | jarURI = do_QueryInterface(baseURI, &rv); |
1350 | 4 | } |
1351 | 4 | baseFileURL = do_QueryInterface(baseURI, &rv); |
1352 | 4 | NS_ENSURE_SUCCESS(rv, rv); |
1353 | 4 | } else { |
1354 | 0 | baseFileURL = do_QueryInterface(info.ResolvedURI(), &rv); |
1355 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1356 | 0 | } |
1357 | 4 | |
1358 | 4 | nsCOMPtr<nsIFile> sourceFile; |
1359 | 4 | rv = baseFileURL->GetFile(getter_AddRefs(sourceFile)); |
1360 | 4 | NS_ENSURE_SUCCESS(rv, rv); |
1361 | 4 | |
1362 | 4 | rv = info.ResolvedURI()->GetSpec(newEntry->resolvedURL); |
1363 | 4 | NS_ENSURE_SUCCESS(rv, rv); |
1364 | 4 | |
1365 | 4 | nsCString* existingPath; |
1366 | 4 | if (mLocations.Get(newEntry->resolvedURL, &existingPath) && *existingPath != info.Key()) { |
1367 | 0 | return NS_ERROR_UNEXPECTED; |
1368 | 0 | } |
1369 | 4 | |
1370 | 4 | mLocations.Put(newEntry->resolvedURL, new nsCString(info.Key())); |
1371 | 4 | |
1372 | 4 | RootedValue exception(aCx); |
1373 | 4 | { |
1374 | 4 | mInProgressImports.Put(info.Key(), newEntry); |
1375 | 4 | auto cleanup = MakeScopeExit([&] () { |
1376 | 4 | mInProgressImports.Remove(info.Key()); |
1377 | 4 | }); |
1378 | 4 | |
1379 | 4 | rv = ObjectForLocation(info, sourceFile, &newEntry->obj, |
1380 | 4 | &newEntry->thisObjectKey, |
1381 | 4 | &newEntry->location, true, &exception); |
1382 | 4 | } |
1383 | 4 | |
1384 | 4 | if (NS_FAILED(rv)) { |
1385 | 0 | if (!exception.isUndefined()) { |
1386 | 0 | // An exception was thrown during compilation. Propagate it |
1387 | 0 | // out to our caller so they can report it. |
1388 | 0 | if (!JS_WrapValue(aCx, &exception)) { |
1389 | 0 | return NS_ERROR_OUT_OF_MEMORY; |
1390 | 0 | } |
1391 | 0 | JS_SetPendingException(aCx, exception); |
1392 | 0 | return NS_ERROR_FAILURE; |
1393 | 0 | } |
1394 | 0 | |
1395 | 0 | // Something failed, but we don't know what it is, guess. |
1396 | 0 | return NS_ERROR_FILE_NOT_FOUND; |
1397 | 0 | } |
1398 | 4 | |
1399 | 4 | #ifdef STARTUP_RECORDER_ENABLED |
1400 | 4 | if (Preferences::GetBool("browser.startup.record", false)) { |
1401 | 0 | newEntry->importStack = |
1402 | 0 | xpc_PrintJSStack(aCx, false, false, false).get(); |
1403 | 0 | } |
1404 | 4 | #endif |
1405 | 4 | |
1406 | 4 | mod = newEntry; |
1407 | 4 | } |
1408 | 8 | |
1409 | 8 | MOZ_ASSERT(mod->obj, "Import table contains entry with no object"); |
1410 | 8 | aModuleGlobal.set(mod->obj); |
1411 | 8 | |
1412 | 8 | JS::RootedObject exports(aCx, mod->exports); |
1413 | 8 | if (!exports && !aIgnoreExports) { |
1414 | 4 | MOZ_TRY(ExtractExports(aCx, info, mod, &exports)); |
1415 | 4 | } |
1416 | 8 | |
1417 | 8 | if (exports && !JS_WrapObject(aCx, &exports)) { |
1418 | 0 | return NS_ERROR_FAILURE; |
1419 | 0 | } |
1420 | 8 | aModuleExports.set(exports); |
1421 | 8 | |
1422 | 8 | // Cache this module for later |
1423 | 8 | if (newEntry) { |
1424 | 4 | mImports.Put(info.Key(), newEntry); |
1425 | 4 | newEntry.forget(); |
1426 | 4 | } |
1427 | 8 | |
1428 | 8 | return NS_OK; |
1429 | 8 | } |
1430 | | |
1431 | | nsresult |
1432 | | mozJSComponentLoader::Unload(const nsACString & aLocation) |
1433 | 0 | { |
1434 | 0 | nsresult rv; |
1435 | 0 |
|
1436 | 0 | if (!mInitialized) { |
1437 | 0 | return NS_OK; |
1438 | 0 | } |
1439 | 0 | |
1440 | 0 | ComponentLoaderInfo info(aLocation); |
1441 | 0 | rv = info.EnsureKey(); |
1442 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
1443 | 0 | ModuleEntry* mod; |
1444 | 0 | if (mImports.Get(info.Key(), &mod)) { |
1445 | 0 | mLocations.Remove(mod->resolvedURL); |
1446 | 0 | mImports.Remove(info.Key()); |
1447 | 0 | } |
1448 | 0 |
|
1449 | 0 | // If this is the last module to be unloaded, we will leak mLoaderGlobal |
1450 | 0 | // until UnloadModules is called. So be it. |
1451 | 0 |
|
1452 | 0 | return NS_OK; |
1453 | 0 | } |
1454 | | |
1455 | | NS_IMETHODIMP |
1456 | | mozJSComponentLoader::Observe(nsISupports* subject, const char* topic, |
1457 | | const char16_t* data) |
1458 | 0 | { |
1459 | 0 | if (!strcmp(topic, "xpcom-shutdown-loaders")) { |
1460 | 0 | UnloadModules(); |
1461 | 0 | } else { |
1462 | 0 | NS_ERROR("Unexpected observer topic."); |
1463 | 0 | } |
1464 | 0 |
|
1465 | 0 | return NS_OK; |
1466 | 0 | } |
1467 | | |
1468 | | size_t |
1469 | | mozJSComponentLoader::ModuleEntry::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const |
1470 | 0 | { |
1471 | 0 | size_t n = aMallocSizeOf(this); |
1472 | 0 | n += aMallocSizeOf(location); |
1473 | 0 |
|
1474 | 0 | return n; |
1475 | 0 | } |
1476 | | |
1477 | | /* static */ already_AddRefed<nsIFactory> |
1478 | | mozJSComponentLoader::ModuleEntry::GetFactory(const mozilla::Module& module, |
1479 | | const mozilla::Module::CIDEntry& entry) |
1480 | 1 | { |
1481 | 1 | const ModuleEntry& self = static_cast<const ModuleEntry&>(module); |
1482 | 1 | MOZ_ASSERT(self.getfactoryobj, "Handing out an uninitialized module?"); |
1483 | 1 | |
1484 | 1 | nsCOMPtr<nsIFactory> f; |
1485 | 1 | nsresult rv = self.getfactoryobj->Get(*entry.cid, getter_AddRefs(f)); |
1486 | 1 | if (NS_FAILED(rv)) { |
1487 | 0 | return nullptr; |
1488 | 0 | } |
1489 | 1 | |
1490 | 1 | return f.forget(); |
1491 | 1 | } |
1492 | | |
1493 | | //---------------------------------------------------------------------- |
1494 | | |
1495 | | JSCLContextHelper::JSCLContextHelper(JSContext* aCx) |
1496 | | : mContext(aCx) |
1497 | | , mBuf(nullptr) |
1498 | 4 | { |
1499 | 4 | } |
1500 | | |
1501 | | JSCLContextHelper::~JSCLContextHelper() |
1502 | 4 | { |
1503 | 4 | if (mBuf) { |
1504 | 0 | JS_ReportErrorUTF8(mContext, "%s", mBuf.get()); |
1505 | 0 | } |
1506 | 4 | } |
1507 | | |
1508 | | void |
1509 | | JSCLContextHelper::reportErrorAfterPop(UniqueChars&& buf) |
1510 | 0 | { |
1511 | 0 | MOZ_ASSERT(!mBuf, "Already called reportErrorAfterPop"); |
1512 | 0 | mBuf = std::move(buf); |
1513 | 0 | } |