1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
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/ArrayUtils.h"
8 #include "mozilla/Printf.h"
9 #include "mozilla/UniquePtr.h"
10
11 #include "ManifestParser.h"
12
13 #include <string.h>
14
15 #include "prio.h"
16 #if defined(XP_WIN)
17 # include <windows.h>
18 #elif defined(MOZ_WIDGET_COCOA)
19 # include <CoreServices/CoreServices.h>
20 # include "nsCocoaFeatures.h"
21 #elif defined(MOZ_WIDGET_GTK)
22 # include <gtk/gtk.h>
23 #endif
24
25 #ifdef MOZ_WIDGET_ANDROID
26 # include "AndroidBridge.h"
27 # include "mozilla/java/GeckoAppShellWrappers.h"
28 #endif
29
30 #ifdef MOZ_BACKGROUNDTASKS
31 # include "mozilla/BackgroundTasks.h"
32 #endif
33
34 #include "mozilla/Services.h"
35
36 #include "nsCRT.h"
37 #include "nsConsoleMessage.h"
38 #include "nsTextFormatter.h"
39 #include "nsVersionComparator.h"
40 #include "nsXPCOMCIDInternal.h"
41
42 #include "nsIConsoleService.h"
43 #include "nsIScriptError.h"
44 #include "nsIXULAppInfo.h"
45 #include "nsIXULRuntime.h"
46
47 using namespace mozilla;
48
49 struct ManifestDirective {
50 const char* directive;
51 int argc;
52
53 bool ischrome;
54
55 // The contentaccessible flags only apply to content/resource directives.
56 bool contentflags;
57
58 // Function to handle this directive. This isn't a union because C++ still
59 // hasn't learned how to initialize unions in a sane way.
60 void (nsComponentManagerImpl::*mgrfunc)(
61 nsComponentManagerImpl::ManifestProcessingContext& aCx, int aLineNo,
62 char* const* aArgv);
63 void (nsChromeRegistry::*regfunc)(
64 nsChromeRegistry::ManifestProcessingContext& aCx, int aLineNo,
65 char* const* aArgv, int aFlags);
66 };
67 static const ManifestDirective kParsingTable[] = {
68 // clang-format off
69 {
70 "manifest", 1, true, false,
71 &nsComponentManagerImpl::ManifestManifest, nullptr,
72 },
73 {
74 "component", 2, false, false,
75 &nsComponentManagerImpl::ManifestComponent, nullptr,
76 },
77 {
78 "contract", 2, false, false,
79 &nsComponentManagerImpl::ManifestContract, nullptr,
80 },
81 {
82 "category", 3, false, false,
83 &nsComponentManagerImpl::ManifestCategory, nullptr,
84 },
85 {
86 "content", 2, true, true,
87 nullptr, &nsChromeRegistry::ManifestContent,
88 },
89 {
90 "locale", 3, true, false,
91 nullptr, &nsChromeRegistry::ManifestLocale,
92 },
93 {
94 "skin", 3, true, false,
95 nullptr, &nsChromeRegistry::ManifestSkin,
96 },
97 {
98 // NB: note that while skin manifests can use this, they are only allowed
99 // to use it for chrome://../skin/ URLs
100 "override", 2, true, false,
101 nullptr, &nsChromeRegistry::ManifestOverride,
102 },
103 {
104 "resource", 2, false, true,
105 nullptr, &nsChromeRegistry::ManifestResource,
106 }
107 // clang-format on
108 };
109
110 static const char kWhitespace[] = "\t ";
111
IsNewline(char aChar)112 static bool IsNewline(char aChar) { return aChar == '\n' || aChar == '\r'; }
113
LogMessage(const char * aMsg,...)114 void LogMessage(const char* aMsg, ...) {
115 MOZ_ASSERT(nsComponentManagerImpl::gComponentManager);
116
117 nsCOMPtr<nsIConsoleService> console =
118 do_GetService(NS_CONSOLESERVICE_CONTRACTID);
119 if (!console) {
120 return;
121 }
122
123 va_list args;
124 va_start(args, aMsg);
125 SmprintfPointer formatted(mozilla::Vsmprintf(aMsg, args));
126 va_end(args);
127
128 nsCOMPtr<nsIConsoleMessage> error =
129 new nsConsoleMessage(NS_ConvertUTF8toUTF16(formatted.get()).get());
130 console->LogMessage(error);
131 }
132
LogMessageWithContext(FileLocation & aFile,uint32_t aLineNumber,const char * aMsg,...)133 void LogMessageWithContext(FileLocation& aFile, uint32_t aLineNumber,
134 const char* aMsg, ...) {
135 va_list args;
136 va_start(args, aMsg);
137 SmprintfPointer formatted(mozilla::Vsmprintf(aMsg, args));
138 va_end(args);
139 if (!formatted) {
140 return;
141 }
142
143 MOZ_ASSERT(nsComponentManagerImpl::gComponentManager);
144
145 nsCString file;
146 aFile.GetURIString(file);
147
148 nsCOMPtr<nsIScriptError> error = do_CreateInstance(NS_SCRIPTERROR_CONTRACTID);
149 if (!error) {
150 // This can happen early in component registration. Fall back to a
151 // generic console message.
152 LogMessage("Warning: in '%s', line %i: %s", file.get(), aLineNumber,
153 formatted.get());
154 return;
155 }
156
157 nsCOMPtr<nsIConsoleService> console =
158 do_GetService(NS_CONSOLESERVICE_CONTRACTID);
159 if (!console) {
160 return;
161 }
162
163 nsresult rv = error->Init(NS_ConvertUTF8toUTF16(formatted.get()),
164 NS_ConvertUTF8toUTF16(file), u""_ns, aLineNumber, 0,
165 nsIScriptError::warningFlag, "chrome registration",
166 false /* from private window */,
167 true /* from chrome context */);
168 if (NS_FAILED(rv)) {
169 return;
170 }
171
172 console->LogMessage(error);
173 }
174
175 /**
176 * Check for a modifier flag of the following forms:
177 * "flag" (same as "true")
178 * "flag=yes|true|1"
179 * "flag="no|false|0"
180 * @param aFlag The flag to compare.
181 * @param aData The tokenized data to check; this is lowercased
182 * before being passed in.
183 * @param aResult If the flag is found, the value is assigned here.
184 * @return Whether the flag was handled.
185 */
CheckFlag(const nsAString & aFlag,const nsAString & aData,bool & aResult)186 static bool CheckFlag(const nsAString& aFlag, const nsAString& aData,
187 bool& aResult) {
188 if (!StringBeginsWith(aData, aFlag)) {
189 return false;
190 }
191
192 if (aFlag.Length() == aData.Length()) {
193 // the data is simply "flag", which is the same as "flag=yes"
194 aResult = true;
195 return true;
196 }
197
198 if (aData.CharAt(aFlag.Length()) != '=') {
199 // the data is "flag2=", which is not anything we care about
200 return false;
201 }
202
203 if (aData.Length() == aFlag.Length() + 1) {
204 aResult = false;
205 return true;
206 }
207
208 switch (aData.CharAt(aFlag.Length() + 1)) {
209 case '1':
210 case 't': // true
211 case 'y': // yes
212 aResult = true;
213 return true;
214
215 case '0':
216 case 'f': // false
217 case 'n': // no
218 aResult = false;
219 return true;
220 }
221
222 return false;
223 }
224
225 enum TriState { eUnspecified, eBad, eOK };
226
227 /**
228 * Check for a modifier flag of the following form:
229 * "flag=string"
230 * "flag!=string"
231 * @param aFlag The flag to compare.
232 * @param aData The tokenized data to check; this is lowercased
233 * before being passed in.
234 * @param aValue The value that is expected.
235 * @param aResult If this is "ok" when passed in, this is left alone.
236 * Otherwise if the flag is found it is set to eBad or eOK.
237 * @return Whether the flag was handled.
238 */
CheckStringFlag(const nsAString & aFlag,const nsAString & aData,const nsAString & aValue,TriState & aResult)239 static bool CheckStringFlag(const nsAString& aFlag, const nsAString& aData,
240 const nsAString& aValue, TriState& aResult) {
241 if (aData.Length() < aFlag.Length() + 1) {
242 return false;
243 }
244
245 if (!StringBeginsWith(aData, aFlag)) {
246 return false;
247 }
248
249 bool comparison = true;
250 if (aData[aFlag.Length()] != '=') {
251 if (aData[aFlag.Length()] == '!' && aData.Length() >= aFlag.Length() + 2 &&
252 aData[aFlag.Length() + 1] == '=') {
253 comparison = false;
254 } else {
255 return false;
256 }
257 }
258
259 if (aResult != eOK) {
260 nsDependentSubstring testdata =
261 Substring(aData, aFlag.Length() + (comparison ? 1 : 2));
262 if (testdata.Equals(aValue)) {
263 aResult = comparison ? eOK : eBad;
264 } else {
265 aResult = comparison ? eBad : eOK;
266 }
267 }
268
269 return true;
270 }
271
CheckOsFlag(const nsAString & aFlag,const nsAString & aData,const nsAString & aValue,TriState & aResult)272 static bool CheckOsFlag(const nsAString& aFlag, const nsAString& aData,
273 const nsAString& aValue, TriState& aResult) {
274 bool result = CheckStringFlag(aFlag, aData, aValue, aResult);
275 #if defined(XP_UNIX) && !defined(XP_DARWIN) && !defined(ANDROID)
276 if (result && aResult == eBad) {
277 result = CheckStringFlag(aFlag, aData, u"likeunix"_ns, aResult);
278 }
279 #endif
280 return result;
281 }
282
283 /**
284 * Check for a modifier flag of the following form:
285 * "flag=version"
286 * "flag<=version"
287 * "flag<version"
288 * "flag>=version"
289 * "flag>version"
290 * @param aFlag The flag to compare.
291 * @param aData The tokenized data to check; this is lowercased
292 * before being passed in.
293 * @param aValue The value that is expected. If this is empty then no
294 * comparison will match.
295 * @param aResult If this is eOK when passed in, this is left alone.
296 * Otherwise if the flag is found it is set to eBad or eOK.
297 * @return Whether the flag was handled.
298 */
299
300 #define COMPARE_EQ 1 << 0
301 #define COMPARE_LT 1 << 1
302 #define COMPARE_GT 1 << 2
303
CheckVersionFlag(const nsString & aFlag,const nsString & aData,const nsString & aValue,TriState & aResult)304 static bool CheckVersionFlag(const nsString& aFlag, const nsString& aData,
305 const nsString& aValue, TriState& aResult) {
306 if (aData.Length() < aFlag.Length() + 2) {
307 return false;
308 }
309
310 if (!StringBeginsWith(aData, aFlag)) {
311 return false;
312 }
313
314 if (aValue.Length() == 0) {
315 if (aResult != eOK) {
316 aResult = eBad;
317 }
318 return true;
319 }
320
321 uint32_t comparison;
322 nsAutoString testdata;
323
324 switch (aData[aFlag.Length()]) {
325 case '=':
326 comparison = COMPARE_EQ;
327 testdata = Substring(aData, aFlag.Length() + 1);
328 break;
329
330 case '<':
331 if (aData[aFlag.Length() + 1] == '=') {
332 comparison = COMPARE_EQ | COMPARE_LT;
333 testdata = Substring(aData, aFlag.Length() + 2);
334 } else {
335 comparison = COMPARE_LT;
336 testdata = Substring(aData, aFlag.Length() + 1);
337 }
338 break;
339
340 case '>':
341 if (aData[aFlag.Length() + 1] == '=') {
342 comparison = COMPARE_EQ | COMPARE_GT;
343 testdata = Substring(aData, aFlag.Length() + 2);
344 } else {
345 comparison = COMPARE_GT;
346 testdata = Substring(aData, aFlag.Length() + 1);
347 }
348 break;
349
350 default:
351 return false;
352 }
353
354 if (testdata.Length() == 0) {
355 return false;
356 }
357
358 if (aResult != eOK) {
359 int32_t c = mozilla::CompareVersions(NS_ConvertUTF16toUTF8(aValue).get(),
360 NS_ConvertUTF16toUTF8(testdata).get());
361 if ((c == 0 && comparison & COMPARE_EQ) ||
362 (c < 0 && comparison & COMPARE_LT) ||
363 (c > 0 && comparison & COMPARE_GT)) {
364 aResult = eOK;
365 } else {
366 aResult = eBad;
367 }
368 }
369
370 return true;
371 }
372
373 // In-place conversion of ascii characters to lower case
ToLowerCase(char * aToken)374 static void ToLowerCase(char* aToken) {
375 for (; *aToken; ++aToken) {
376 *aToken = NS_ToLower(*aToken);
377 }
378 }
379
380 namespace {
381
382 struct CachedDirective {
383 int lineno;
384 char* argv[4];
385 };
386
387 } // namespace
388
ParseManifest(NSLocationType aType,FileLocation & aFile,char * aBuf,bool aChromeOnly)389 void ParseManifest(NSLocationType aType, FileLocation& aFile, char* aBuf,
390 bool aChromeOnly) {
391 nsComponentManagerImpl::ManifestProcessingContext mgrcx(aType, aFile,
392 aChromeOnly);
393 nsChromeRegistry::ManifestProcessingContext chromecx(aType, aFile);
394 nsresult rv;
395
396 constexpr auto kContentAccessible = u"contentaccessible"_ns;
397 constexpr auto kRemoteEnabled = u"remoteenabled"_ns;
398 constexpr auto kRemoteRequired = u"remoterequired"_ns;
399 constexpr auto kApplication = u"application"_ns;
400 constexpr auto kAppVersion = u"appversion"_ns;
401 constexpr auto kGeckoVersion = u"platformversion"_ns;
402 constexpr auto kOs = u"os"_ns;
403 constexpr auto kOsVersion = u"osversion"_ns;
404 constexpr auto kABI = u"abi"_ns;
405 constexpr auto kProcess = u"process"_ns;
406 #if defined(MOZ_WIDGET_ANDROID)
407 constexpr auto kTablet = u"tablet"_ns;
408 #endif
409 // You might expect this to be guarded by MOZ_BACKGROUNDTASKS, but it's not
410 // possible to have conditional manifest contents, so we need to recognize and
411 // discard these tokens even when MOZ_BACKGROUNDTASKS is not set.
412 constexpr auto kBackgroundTask = u"backgroundtask"_ns;
413
414 constexpr auto kMain = u"main"_ns;
415 constexpr auto kContent = u"content"_ns;
416
417 // Obsolete
418 constexpr auto kXPCNativeWrappers = u"xpcnativewrappers"_ns;
419
420 nsAutoString appID;
421 nsAutoString appVersion;
422 nsAutoString geckoVersion;
423 nsAutoString osTarget;
424 nsAutoString abi;
425 nsAutoString process;
426
427 nsCOMPtr<nsIXULAppInfo> xapp(do_GetService(XULAPPINFO_SERVICE_CONTRACTID));
428 if (xapp) {
429 nsAutoCString s;
430 rv = xapp->GetID(s);
431 if (NS_SUCCEEDED(rv)) {
432 CopyUTF8toUTF16(s, appID);
433 }
434
435 rv = xapp->GetVersion(s);
436 if (NS_SUCCEEDED(rv)) {
437 CopyUTF8toUTF16(s, appVersion);
438 }
439
440 rv = xapp->GetPlatformVersion(s);
441 if (NS_SUCCEEDED(rv)) {
442 CopyUTF8toUTF16(s, geckoVersion);
443 }
444
445 nsCOMPtr<nsIXULRuntime> xruntime(do_QueryInterface(xapp));
446 if (xruntime) {
447 rv = xruntime->GetOS(s);
448 if (NS_SUCCEEDED(rv)) {
449 ToLowerCase(s);
450 CopyUTF8toUTF16(s, osTarget);
451 }
452
453 rv = xruntime->GetXPCOMABI(s);
454 if (NS_SUCCEEDED(rv) && osTarget.Length()) {
455 ToLowerCase(s);
456 CopyUTF8toUTF16(s, abi);
457 abi.Insert(char16_t('_'), 0);
458 abi.Insert(osTarget, 0);
459 }
460 }
461 }
462
463 nsAutoString osVersion;
464 #if defined(XP_WIN)
465 # pragma warning(push)
466 # pragma warning(disable : 4996) // VC12+ deprecates GetVersionEx
467 OSVERSIONINFO info = {sizeof(OSVERSIONINFO)};
468 if (GetVersionEx(&info)) {
469 nsTextFormatter::ssprintf(osVersion, u"%ld.%ld", info.dwMajorVersion,
470 info.dwMinorVersion);
471 }
472 # pragma warning(pop)
473 #elif defined(MOZ_WIDGET_COCOA)
474 SInt32 majorVersion = nsCocoaFeatures::macOSVersionMajor();
475 SInt32 minorVersion = nsCocoaFeatures::macOSVersionMinor();
476 nsTextFormatter::ssprintf(osVersion, u"%ld.%ld", majorVersion, minorVersion);
477 #elif defined(MOZ_WIDGET_GTK)
478 nsTextFormatter::ssprintf(osVersion, u"%ld.%ld", gtk_major_version,
479 gtk_minor_version);
480 #elif defined(MOZ_WIDGET_ANDROID)
481 bool isTablet = false;
482 if (mozilla::AndroidBridge::Bridge()) {
483 mozilla::AndroidBridge::Bridge()->GetStaticStringField(
484 "android/os/Build$VERSION", "RELEASE", osVersion);
485 isTablet = java::GeckoAppShell::IsTablet();
486 }
487 #endif
488
489 if (XRE_IsContentProcess()) {
490 process = kContent;
491 } else {
492 process = kMain;
493 }
494
495 // Because contracts must be registered after CIDs, we save and process them
496 // at the end.
497 nsTArray<CachedDirective> contracts;
498
499 char* token;
500 char* newline = aBuf;
501 uint32_t line = 0;
502
503 // outer loop tokenizes by newline
504 while (*newline) {
505 while (*newline && IsNewline(*newline)) {
506 ++newline;
507 ++line;
508 }
509 if (!*newline) {
510 break;
511 }
512
513 token = newline;
514 while (*newline && !IsNewline(*newline)) {
515 ++newline;
516 }
517
518 if (*newline) {
519 *newline = '\0';
520 ++newline;
521 }
522 ++line;
523
524 if (*token == '#') { // ignore lines that begin with # as comments
525 continue;
526 }
527
528 char* whitespace = token;
529 token = nsCRT::strtok(whitespace, kWhitespace, &whitespace);
530 if (!token) {
531 continue;
532 }
533
534 const ManifestDirective* directive = nullptr;
535 for (const ManifestDirective* d = kParsingTable;
536 d < ArrayEnd(kParsingTable); ++d) {
537 if (!strcmp(d->directive, token)) {
538 directive = d;
539 break;
540 }
541 }
542
543 if (!directive) {
544 LogMessageWithContext(
545 aFile, line, "Ignoring unrecognized chrome manifest directive '%s'.",
546 token);
547 continue;
548 }
549
550 if (!directive->ischrome && NS_BOOTSTRAPPED_LOCATION == aType) {
551 LogMessageWithContext(
552 aFile, line,
553 "Bootstrapped manifest not allowed to use '%s' directive.", token);
554 continue;
555 }
556
557 NS_ASSERTION(directive->argc < 4, "Need to reset argv array length");
558 char* argv[4];
559 for (int i = 0; i < directive->argc; ++i) {
560 argv[i] = nsCRT::strtok(whitespace, kWhitespace, &whitespace);
561 }
562
563 if (!argv[directive->argc - 1]) {
564 LogMessageWithContext(aFile, line,
565 "Not enough arguments for chrome manifest "
566 "directive '%s', expected %i.",
567 token, directive->argc);
568 continue;
569 }
570
571 bool ok = true;
572 TriState stAppVersion = eUnspecified;
573 TriState stGeckoVersion = eUnspecified;
574 TriState stApp = eUnspecified;
575 TriState stOsVersion = eUnspecified;
576 TriState stOs = eUnspecified;
577 TriState stABI = eUnspecified;
578 TriState stProcess = eUnspecified;
579 #if defined(MOZ_WIDGET_ANDROID)
580 TriState stTablet = eUnspecified;
581 #endif
582 #ifdef MOZ_BACKGROUNDTASKS
583 // When in background task mode, default to not registering
584 // category directivies unless backgroundtask=1 is specified.
585 TriState stBackgroundTask = (BackgroundTasks::IsBackgroundTaskMode() &&
586 strcmp("category", directive->directive) == 0)
587 ? eBad
588 : eUnspecified;
589 #endif
590 int flags = 0;
591
592 while ((token = nsCRT::strtok(whitespace, kWhitespace, &whitespace)) &&
593 ok) {
594 ToLowerCase(token);
595 NS_ConvertASCIItoUTF16 wtoken(token);
596
597 if (CheckStringFlag(kApplication, wtoken, appID, stApp) ||
598 CheckOsFlag(kOs, wtoken, osTarget, stOs) ||
599 CheckStringFlag(kABI, wtoken, abi, stABI) ||
600 CheckStringFlag(kProcess, wtoken, process, stProcess) ||
601 CheckVersionFlag(kOsVersion, wtoken, osVersion, stOsVersion) ||
602 CheckVersionFlag(kAppVersion, wtoken, appVersion, stAppVersion) ||
603 CheckVersionFlag(kGeckoVersion, wtoken, geckoVersion,
604 stGeckoVersion)) {
605 continue;
606 }
607
608 #if defined(MOZ_WIDGET_ANDROID)
609 bool tablet = false;
610 if (CheckFlag(kTablet, wtoken, tablet)) {
611 stTablet = (tablet == isTablet) ? eOK : eBad;
612 continue;
613 }
614 #endif
615
616 // You might expect this to be guarded by MOZ_BACKGROUNDTASKS, it's not
617 // possible to have conditional manifest contents.
618 bool flag;
619 if (CheckFlag(kBackgroundTask, wtoken, flag)) {
620 #if defined(MOZ_BACKGROUNDTASKS)
621 // Background task mode is active: filter.
622 stBackgroundTask =
623 (flag == BackgroundTasks::IsBackgroundTaskMode()) ? eOK : eBad;
624 #endif /* defined(MOZ_BACKGROUNDTASKS) */
625 continue;
626 }
627
628 if (directive->contentflags) {
629 bool flag;
630 if (CheckFlag(kContentAccessible, wtoken, flag)) {
631 if (flag) flags |= nsChromeRegistry::CONTENT_ACCESSIBLE;
632 continue;
633 }
634 if (CheckFlag(kRemoteEnabled, wtoken, flag)) {
635 if (flag) flags |= nsChromeRegistry::REMOTE_ALLOWED;
636 continue;
637 }
638 if (CheckFlag(kRemoteRequired, wtoken, flag)) {
639 if (flag) flags |= nsChromeRegistry::REMOTE_REQUIRED;
640 continue;
641 }
642 }
643
644 bool xpcNativeWrappers = true; // Dummy for CheckFlag.
645 if (CheckFlag(kXPCNativeWrappers, wtoken, xpcNativeWrappers)) {
646 LogMessageWithContext(
647 aFile, line, "Ignoring obsolete chrome registration modifier '%s'.",
648 token);
649 continue;
650 }
651
652 LogMessageWithContext(
653 aFile, line, "Unrecognized chrome manifest modifier '%s'.", token);
654 ok = false;
655 }
656
657 if (!ok || stApp == eBad || stAppVersion == eBad ||
658 stGeckoVersion == eBad || stOs == eBad || stOsVersion == eBad ||
659 #ifdef MOZ_WIDGET_ANDROID
660 stTablet == eBad ||
661 #endif
662 #ifdef MOZ_BACKGROUNDTASKS
663 stBackgroundTask == eBad ||
664 #endif
665 stABI == eBad || stProcess == eBad) {
666 continue;
667 }
668
669 if (directive->regfunc) {
670 if (GeckoProcessType_Default != XRE_GetProcessType()) {
671 continue;
672 }
673
674 if (!nsChromeRegistry::gChromeRegistry) {
675 nsCOMPtr<nsIChromeRegistry> cr = mozilla::services::GetChromeRegistry();
676 if (!nsChromeRegistry::gChromeRegistry) {
677 LogMessageWithContext(aFile, line,
678 "Chrome registry isn't available yet.");
679 continue;
680 }
681 }
682
683 (nsChromeRegistry::gChromeRegistry->*(directive->regfunc))(chromecx, line,
684 argv, flags);
685 } else if (directive->ischrome || !aChromeOnly) {
686 (nsComponentManagerImpl::gComponentManager->*(directive->mgrfunc))(
687 mgrcx, line, argv);
688 }
689 }
690
691 for (uint32_t i = 0; i < contracts.Length(); ++i) {
692 CachedDirective& d = contracts[i];
693 nsComponentManagerImpl::gComponentManager->ManifestContract(mgrcx, d.lineno,
694 d.argv);
695 }
696 }
697