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