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