Bug 1603309 - Make sure the Screenshots WebExtension is enabled during performance...
[gecko.git] / chrome / nsChromeRegistryChrome.cpp
blob41c559152f226a74598eadac653b3ee549f4ada2
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=2 sts=2 sw=2 et 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/. */
7 #include "mozilla/dom/ContentParent.h"
8 #include "RegistryMessageUtils.h"
9 #include "nsResProtocolHandler.h"
11 #include "nsChromeRegistryChrome.h"
13 #if defined(XP_WIN)
14 # include <windows.h>
15 #elif defined(XP_MACOSX)
16 # include <CoreServices/CoreServices.h>
17 #endif
19 #include "nsArrayEnumerator.h"
20 #include "nsComponentManager.h"
21 #include "nsEnumeratorUtils.h"
22 #include "nsNetUtil.h"
23 #include "nsStringEnumerator.h"
24 #include "nsTextFormatter.h"
25 #include "nsXPCOMCIDInternal.h"
27 #include "mozilla/LookAndFeel.h"
28 #include "mozilla/Unused.h"
30 #include "nsIAppStartup.h"
31 #include "nsIObserverService.h"
32 #include "mozilla/Preferences.h"
33 #include "nsIResProtocolHandler.h"
34 #include "nsIScriptError.h"
35 #include "nsIXULRuntime.h"
37 #define PACKAGE_OVERRIDE_BRANCH "chrome.override_package."
38 #define SKIN NS_LITERAL_CSTRING("classic/1.0")
40 using namespace mozilla;
41 using mozilla::dom::ContentParent;
42 using mozilla::dom::PContentParent;
43 using mozilla::intl::LocaleService;
45 // We use a "best-fit" algorithm for matching locales and themes.
46 // 1) the exact selected locale/theme
47 // 2) (locales only) same language, different country
48 // e.g. en-GB is the selected locale, only en-US is available
49 // 3) any available locale/theme
51 /**
52 * Match the language-part of two lang-COUNTRY codes, hopefully but
53 * not guaranteed to be in the form ab-CD or abz-CD. "ab" should also
54 * work, any other garbage-in will produce undefined results as long
55 * as it does not crash.
57 static bool LanguagesMatch(const nsACString& a, const nsACString& b) {
58 if (a.Length() < 2 || b.Length() < 2) return false;
60 nsACString::const_iterator as, ae, bs, be;
61 a.BeginReading(as);
62 a.EndReading(ae);
63 b.BeginReading(bs);
64 b.EndReading(be);
66 while (*as == *bs) {
67 if (*as == '-') return true;
69 ++as;
70 ++bs;
72 // reached the end
73 if (as == ae && bs == be) return true;
75 // "a" is short
76 if (as == ae) return (*bs == '-');
78 // "b" is short
79 if (bs == be) return (*as == '-');
82 return false;
85 nsChromeRegistryChrome::nsChromeRegistryChrome()
86 : mProfileLoaded(false), mDynamicRegistration(true) {}
88 nsChromeRegistryChrome::~nsChromeRegistryChrome() {}
90 nsresult nsChromeRegistryChrome::Init() {
91 nsresult rv = nsChromeRegistry::Init();
92 if (NS_FAILED(rv)) return rv;
94 bool safeMode = false;
95 nsCOMPtr<nsIXULRuntime> xulrun(do_GetService(XULAPPINFO_SERVICE_CONTRACTID));
96 if (xulrun) xulrun->GetInSafeMode(&safeMode);
98 nsCOMPtr<nsIObserverService> obsService =
99 mozilla::services::GetObserverService();
100 if (obsService) {
101 obsService->AddObserver(this, "profile-initial-state", true);
102 obsService->AddObserver(this, "intl:app-locales-changed", true);
105 return NS_OK;
108 NS_IMETHODIMP
109 nsChromeRegistryChrome::GetLocalesForPackage(
110 const nsACString& aPackage, nsIUTF8StringEnumerator** aResult) {
111 nsCString realpackage;
112 nsresult rv = OverrideLocalePackage(aPackage, realpackage);
113 if (NS_FAILED(rv)) return rv;
115 nsTArray<nsCString>* a = new nsTArray<nsCString>;
116 if (!a) return NS_ERROR_OUT_OF_MEMORY;
118 PackageEntry* entry;
119 if (mPackagesHash.Get(realpackage, &entry)) {
120 entry->locales.EnumerateToArray(a);
123 rv = NS_NewAdoptingUTF8StringEnumerator(aResult, a);
124 if (NS_FAILED(rv)) delete a;
126 return rv;
129 NS_IMETHODIMP
130 nsChromeRegistryChrome::IsLocaleRTL(const nsACString& package, bool* aResult) {
131 *aResult = false;
133 nsAutoCString locale;
134 GetSelectedLocale(package, false, locale);
135 if (locale.Length() < 2) return NS_OK;
137 *aResult = LocaleService::IsLocaleRTL(locale);
138 return NS_OK;
142 * This method negotiates only between the app locale and the available
143 * chrome packages.
145 * If you want to get the current application's UI locale, please use
146 * LocaleService::GetAppLocaleAsLangTag.
148 nsresult nsChromeRegistryChrome::GetSelectedLocale(const nsACString& aPackage,
149 bool aAsBCP47,
150 nsACString& aLocale) {
151 nsAutoCString reqLocale;
152 if (aPackage.EqualsLiteral("global")) {
153 LocaleService::GetInstance()->GetAppLocaleAsLangTag(reqLocale);
154 } else {
155 AutoTArray<nsCString, 10> requestedLocales;
156 LocaleService::GetInstance()->GetRequestedLocales(requestedLocales);
157 reqLocale.Assign(requestedLocales[0]);
160 nsCString realpackage;
161 nsresult rv = OverrideLocalePackage(aPackage, realpackage);
162 if (NS_FAILED(rv)) return rv;
163 PackageEntry* entry;
164 if (!mPackagesHash.Get(realpackage, &entry)) return NS_ERROR_FILE_NOT_FOUND;
166 aLocale = entry->locales.GetSelected(reqLocale, nsProviderArray::LOCALE);
167 if (aLocale.IsEmpty()) return NS_ERROR_FAILURE;
169 if (aAsBCP47) {
170 SanitizeForBCP47(aLocale);
173 return NS_OK;
176 nsresult nsChromeRegistryChrome::OverrideLocalePackage(
177 const nsACString& aPackage, nsACString& aOverride) {
178 const nsACString& pref =
179 NS_LITERAL_CSTRING(PACKAGE_OVERRIDE_BRANCH) + aPackage;
180 nsAutoCString override;
181 nsresult rv = mozilla::Preferences::GetCString(PromiseFlatCString(pref).get(),
182 override);
183 if (NS_SUCCEEDED(rv)) {
184 aOverride = override;
185 } else {
186 aOverride = aPackage;
188 return NS_OK;
191 NS_IMETHODIMP
192 nsChromeRegistryChrome::Observe(nsISupports* aSubject, const char* aTopic,
193 const char16_t* someData) {
194 nsresult rv = NS_OK;
196 if (!strcmp("profile-initial-state", aTopic)) {
197 mProfileLoaded = true;
198 } else if (!strcmp("intl:app-locales-changed", aTopic)) {
199 if (mProfileLoaded) {
200 FlushAllCaches();
202 } else {
203 NS_ERROR("Unexpected observer topic!");
206 return rv;
209 NS_IMETHODIMP
210 nsChromeRegistryChrome::CheckForNewChrome() {
211 nsCOMPtr<nsIAppStartup> appStartup = components::AppStartup::Service();
212 if (appStartup->GetShuttingDown()) {
213 MOZ_ASSERT(false, "checking for new chrome during shutdown");
214 return NS_ERROR_UNEXPECTED;
217 mPackagesHash.Clear();
218 mOverrideTable.Clear();
220 mDynamicRegistration = false;
222 nsComponentManagerImpl::gComponentManager->RereadChromeManifests();
224 mDynamicRegistration = true;
226 SendRegisteredChrome(nullptr);
227 return NS_OK;
230 static void SerializeURI(nsIURI* aURI, SerializedURI& aSerializedURI) {
231 if (!aURI) return;
233 aURI->GetSpec(aSerializedURI.spec);
236 void nsChromeRegistryChrome::SendRegisteredChrome(
237 mozilla::dom::PContentParent* aParent) {
238 nsTArray<ChromePackage> packages;
239 nsTArray<SubstitutionMapping> resources;
240 nsTArray<OverrideMapping> overrides;
242 for (auto iter = mPackagesHash.Iter(); !iter.Done(); iter.Next()) {
243 ChromePackage chromePackage;
244 ChromePackageFromPackageEntry(iter.Key(), iter.UserData(), &chromePackage,
245 SKIN);
246 packages.AppendElement(chromePackage);
249 // If we were passed a parent then a new child process has been created and
250 // has requested all of the chrome so send it the resources too. Otherwise
251 // resource mappings are sent by the resource protocol handler dynamically.
252 if (aParent) {
253 nsCOMPtr<nsIIOService> io(do_GetIOService());
254 NS_ENSURE_TRUE_VOID(io);
256 nsCOMPtr<nsIProtocolHandler> ph;
257 nsresult rv = io->GetProtocolHandler("resource", getter_AddRefs(ph));
258 NS_ENSURE_SUCCESS_VOID(rv);
260 nsCOMPtr<nsIResProtocolHandler> irph(do_QueryInterface(ph));
261 nsResProtocolHandler* rph = static_cast<nsResProtocolHandler*>(irph.get());
262 rv = rph->CollectSubstitutions(resources);
263 NS_ENSURE_SUCCESS_VOID(rv);
266 for (auto iter = mOverrideTable.Iter(); !iter.Done(); iter.Next()) {
267 SerializedURI chromeURI, overrideURI;
269 SerializeURI(iter.Key(), chromeURI);
270 SerializeURI(iter.UserData(), overrideURI);
272 OverrideMapping override = {chromeURI, overrideURI};
273 overrides.AppendElement(override);
276 nsAutoCString appLocale;
277 LocaleService::GetInstance()->GetAppLocaleAsLangTag(appLocale);
279 if (aParent) {
280 bool success = aParent->SendRegisterChrome(packages, resources, overrides,
281 appLocale, false);
282 NS_ENSURE_TRUE_VOID(success);
283 } else {
284 nsTArray<ContentParent*> parents;
285 ContentParent::GetAll(parents);
286 if (!parents.Length()) return;
288 for (uint32_t i = 0; i < parents.Length(); i++) {
289 DebugOnly<bool> success = parents[i]->SendRegisterChrome(
290 packages, resources, overrides, appLocale, true);
291 NS_WARNING_ASSERTION(success,
292 "couldn't reset a child's registered chrome");
297 /* static */
298 void nsChromeRegistryChrome::ChromePackageFromPackageEntry(
299 const nsACString& aPackageName, PackageEntry* aPackage,
300 ChromePackage* aChromePackage, const nsCString& aSelectedSkin) {
301 nsAutoCString appLocale;
302 LocaleService::GetInstance()->GetAppLocaleAsLangTag(appLocale);
304 SerializeURI(aPackage->baseURI, aChromePackage->contentBaseURI);
305 SerializeURI(aPackage->locales.GetBase(appLocale, nsProviderArray::LOCALE),
306 aChromePackage->localeBaseURI);
307 SerializeURI(aPackage->skins.GetBase(aSelectedSkin, nsProviderArray::ANY),
308 aChromePackage->skinBaseURI);
309 aChromePackage->package = aPackageName;
310 aChromePackage->flags = aPackage->flags;
313 static bool CanLoadResource(nsIURI* aResourceURI) {
314 bool isLocalResource = false;
315 (void)NS_URIChainHasFlags(aResourceURI,
316 nsIProtocolHandler::URI_IS_LOCAL_RESOURCE,
317 &isLocalResource);
318 return isLocalResource;
321 nsIURI* nsChromeRegistryChrome::GetBaseURIFromPackage(
322 const nsCString& aPackage, const nsCString& aProvider,
323 const nsCString& aPath) {
324 PackageEntry* entry;
325 if (!mPackagesHash.Get(aPackage, &entry)) {
326 if (!mInitialized) return nullptr;
328 LogMessage("No chrome package registered for chrome://%s/%s/%s",
329 aPackage.get(), aProvider.get(), aPath.get());
331 return nullptr;
334 if (aProvider.EqualsLiteral("locale")) {
335 nsAutoCString appLocale;
336 LocaleService::GetInstance()->GetAppLocaleAsLangTag(appLocale);
337 return entry->locales.GetBase(appLocale, nsProviderArray::LOCALE);
338 } else if (aProvider.EqualsLiteral("skin")) {
339 return entry->skins.GetBase(SKIN, nsProviderArray::ANY);
340 } else if (aProvider.EqualsLiteral("content")) {
341 return entry->baseURI;
343 return nullptr;
346 nsresult nsChromeRegistryChrome::GetFlagsFromPackage(const nsCString& aPackage,
347 uint32_t* aFlags) {
348 PackageEntry* entry;
349 if (!mPackagesHash.Get(aPackage, &entry)) return NS_ERROR_FILE_NOT_FOUND;
351 *aFlags = entry->flags;
352 return NS_OK;
355 nsChromeRegistryChrome::ProviderEntry*
356 nsChromeRegistryChrome::nsProviderArray::GetProvider(
357 const nsACString& aPreferred, MatchType aType) {
358 size_t i = mArray.Length();
359 if (!i) return nullptr;
361 ProviderEntry* found = nullptr; // Only set if we find a partial-match locale
362 ProviderEntry* entry = nullptr;
364 while (i--) {
365 entry = &mArray[i];
366 if (aPreferred.Equals(entry->provider)) return entry;
368 if (aType != LOCALE) continue;
370 if (LanguagesMatch(aPreferred, entry->provider)) {
371 found = entry;
372 continue;
375 if (!found && entry->provider.EqualsLiteral("en-US")) found = entry;
378 if (!found && aType != EXACT) return entry;
380 return found;
383 nsIURI* nsChromeRegistryChrome::nsProviderArray::GetBase(
384 const nsACString& aPreferred, MatchType aType) {
385 ProviderEntry* provider = GetProvider(aPreferred, aType);
387 if (!provider) return nullptr;
389 return provider->baseURI;
392 const nsACString& nsChromeRegistryChrome::nsProviderArray::GetSelected(
393 const nsACString& aPreferred, MatchType aType) {
394 ProviderEntry* entry = GetProvider(aPreferred, aType);
396 if (entry) return entry->provider;
398 return EmptyCString();
401 void nsChromeRegistryChrome::nsProviderArray::SetBase(
402 const nsACString& aProvider, nsIURI* aBaseURL) {
403 ProviderEntry* provider = GetProvider(aProvider, EXACT);
405 if (provider) {
406 provider->baseURI = aBaseURL;
407 return;
410 // no existing entries, add a new one
411 mArray.AppendElement(ProviderEntry(aProvider, aBaseURL));
414 void nsChromeRegistryChrome::nsProviderArray::EnumerateToArray(
415 nsTArray<nsCString>* a) {
416 int32_t i = mArray.Length();
417 while (i--) {
418 a->AppendElement(mArray[i].provider);
422 nsIURI* nsChromeRegistry::ManifestProcessingContext::GetManifestURI() {
423 if (!mManifestURI) {
424 nsCString uri;
425 mFile.GetURIString(uri);
426 NS_NewURI(getter_AddRefs(mManifestURI), uri);
428 return mManifestURI;
431 already_AddRefed<nsIURI>
432 nsChromeRegistry::ManifestProcessingContext::ResolveURI(const char* uri) {
433 nsIURI* baseuri = GetManifestURI();
434 if (!baseuri) return nullptr;
436 nsCOMPtr<nsIURI> resolved;
437 nsresult rv = NS_NewURI(getter_AddRefs(resolved), uri, baseuri);
438 if (NS_FAILED(rv)) return nullptr;
440 return resolved.forget();
443 static void EnsureLowerCase(char* aBuf) {
444 for (; *aBuf; ++aBuf) {
445 char ch = *aBuf;
446 if (ch >= 'A' && ch <= 'Z') *aBuf = ch + 'a' - 'A';
450 static void SendManifestEntry(const ChromeRegistryItem& aItem) {
451 nsTArray<ContentParent*> parents;
452 ContentParent::GetAll(parents);
453 if (!parents.Length()) return;
455 for (uint32_t i = 0; i < parents.Length(); i++) {
456 Unused << parents[i]->SendRegisterChromeItem(aItem);
460 void nsChromeRegistryChrome::ManifestContent(ManifestProcessingContext& cx,
461 int lineno, char* const* argv,
462 int flags) {
463 char* package = argv[0];
464 char* uri = argv[1];
466 EnsureLowerCase(package);
468 nsCOMPtr<nsIURI> resolved = cx.ResolveURI(uri);
469 if (!resolved) {
470 LogMessageWithContext(
471 cx.GetManifestURI(), lineno, nsIScriptError::warningFlag,
472 "During chrome registration, unable to create URI '%s'.", uri);
473 return;
476 if (!CanLoadResource(resolved)) {
477 LogMessageWithContext(resolved, lineno, nsIScriptError::warningFlag,
478 "During chrome registration, cannot register "
479 "non-local URI '%s' as content.",
480 uri);
481 return;
484 nsDependentCString packageName(package);
485 PackageEntry* entry = mPackagesHash.LookupOrAdd(packageName);
486 entry->baseURI = resolved;
487 entry->flags = flags;
489 if (mDynamicRegistration) {
490 ChromePackage chromePackage;
491 ChromePackageFromPackageEntry(packageName, entry, &chromePackage, SKIN);
492 SendManifestEntry(chromePackage);
496 void nsChromeRegistryChrome::ManifestLocale(ManifestProcessingContext& cx,
497 int lineno, char* const* argv,
498 int flags) {
499 char* package = argv[0];
500 char* provider = argv[1];
501 char* uri = argv[2];
503 EnsureLowerCase(package);
505 nsCOMPtr<nsIURI> resolved = cx.ResolveURI(uri);
506 if (!resolved) {
507 LogMessageWithContext(
508 cx.GetManifestURI(), lineno, nsIScriptError::warningFlag,
509 "During chrome registration, unable to create URI '%s'.", uri);
510 return;
513 if (!CanLoadResource(resolved)) {
514 LogMessageWithContext(resolved, lineno, nsIScriptError::warningFlag,
515 "During chrome registration, cannot register "
516 "non-local URI '%s' as content.",
517 uri);
518 return;
521 nsDependentCString packageName(package);
522 PackageEntry* entry = mPackagesHash.LookupOrAdd(packageName);
523 entry->locales.SetBase(nsDependentCString(provider), resolved);
525 if (mDynamicRegistration) {
526 ChromePackage chromePackage;
527 ChromePackageFromPackageEntry(packageName, entry, &chromePackage, SKIN);
528 SendManifestEntry(chromePackage);
531 // We use mainPackage as the package we track for reporting new locales being
532 // registered. For most cases it will be "global", but for Fennec it will be
533 // "browser".
534 nsAutoCString mainPackage;
535 nsresult rv =
536 OverrideLocalePackage(NS_LITERAL_CSTRING("global"), mainPackage);
537 if (NS_FAILED(rv)) {
538 return;
542 void nsChromeRegistryChrome::ManifestSkin(ManifestProcessingContext& cx,
543 int lineno, char* const* argv,
544 int flags) {
545 char* package = argv[0];
546 char* provider = argv[1];
547 char* uri = argv[2];
549 EnsureLowerCase(package);
551 nsCOMPtr<nsIURI> resolved = cx.ResolveURI(uri);
552 if (!resolved) {
553 LogMessageWithContext(
554 cx.GetManifestURI(), lineno, nsIScriptError::warningFlag,
555 "During chrome registration, unable to create URI '%s'.", uri);
556 return;
559 if (!CanLoadResource(resolved)) {
560 LogMessageWithContext(resolved, lineno, nsIScriptError::warningFlag,
561 "During chrome registration, cannot register "
562 "non-local URI '%s' as content.",
563 uri);
564 return;
567 nsDependentCString packageName(package);
568 PackageEntry* entry = mPackagesHash.LookupOrAdd(packageName);
569 entry->skins.SetBase(nsDependentCString(provider), resolved);
571 if (mDynamicRegistration) {
572 ChromePackage chromePackage;
573 ChromePackageFromPackageEntry(packageName, entry, &chromePackage, SKIN);
574 SendManifestEntry(chromePackage);
578 void nsChromeRegistryChrome::ManifestOverride(ManifestProcessingContext& cx,
579 int lineno, char* const* argv,
580 int flags) {
581 char* chrome = argv[0];
582 char* resolved = argv[1];
584 nsCOMPtr<nsIURI> chromeuri = cx.ResolveURI(chrome);
585 nsCOMPtr<nsIURI> resolveduri = cx.ResolveURI(resolved);
586 if (!chromeuri || !resolveduri) {
587 LogMessageWithContext(cx.GetManifestURI(), lineno,
588 nsIScriptError::warningFlag,
589 "During chrome registration, unable to create URI.");
590 return;
593 if (cx.mType == NS_SKIN_LOCATION) {
594 bool chromeSkinOnly =
595 chromeuri->SchemeIs("chrome") && resolveduri->SchemeIs("chrome");
596 if (chromeSkinOnly) {
597 nsAutoCString chromePath, resolvedPath;
598 chromeuri->GetPathQueryRef(chromePath);
599 resolveduri->GetPathQueryRef(resolvedPath);
600 chromeSkinOnly =
601 StringBeginsWith(chromePath, NS_LITERAL_CSTRING("/skin/")) &&
602 StringBeginsWith(resolvedPath, NS_LITERAL_CSTRING("/skin/"));
604 if (!chromeSkinOnly) {
605 LogMessageWithContext(
606 cx.GetManifestURI(), lineno, nsIScriptError::warningFlag,
607 "Cannot register non-chrome://.../skin/ URIs '%s' and '%s' as "
608 "overrides and/or to be overridden from a skin manifest.",
609 chrome, resolved);
610 return;
614 if (!CanLoadResource(resolveduri)) {
615 LogMessageWithContext(
616 cx.GetManifestURI(), lineno, nsIScriptError::warningFlag,
617 "Cannot register non-local URI '%s' for an override.", resolved);
618 return;
620 mOverrideTable.Put(chromeuri, resolveduri);
622 if (mDynamicRegistration) {
623 SerializedURI serializedChrome;
624 SerializedURI serializedOverride;
626 SerializeURI(chromeuri, serializedChrome);
627 SerializeURI(resolveduri, serializedOverride);
629 OverrideMapping override = {serializedChrome, serializedOverride};
630 SendManifestEntry(override);
634 void nsChromeRegistryChrome::ManifestResource(ManifestProcessingContext& cx,
635 int lineno, char* const* argv,
636 int flags) {
637 char* package = argv[0];
638 char* uri = argv[1];
640 EnsureLowerCase(package);
641 nsDependentCString host(package);
643 nsCOMPtr<nsIIOService> io = mozilla::services::GetIOService();
644 if (!io) {
645 NS_WARNING("No IO service trying to process chrome manifests");
646 return;
649 nsCOMPtr<nsIProtocolHandler> ph;
650 nsresult rv = io->GetProtocolHandler("resource", getter_AddRefs(ph));
651 if (NS_FAILED(rv)) return;
653 nsCOMPtr<nsIResProtocolHandler> rph = do_QueryInterface(ph);
655 nsCOMPtr<nsIURI> resolved = cx.ResolveURI(uri);
656 if (!resolved) {
657 LogMessageWithContext(
658 cx.GetManifestURI(), lineno, nsIScriptError::warningFlag,
659 "During chrome registration, unable to create URI '%s'.", uri);
660 return;
663 if (!CanLoadResource(resolved)) {
664 LogMessageWithContext(
665 cx.GetManifestURI(), lineno, nsIScriptError::warningFlag,
666 "Warning: cannot register non-local URI '%s' as a resource.", uri);
667 return;
670 // By default, Firefox resources are not content-accessible unless the
671 // manifests opts in.
672 bool contentAccessible = (flags & nsChromeRegistry::CONTENT_ACCESSIBLE);
674 uint32_t substitutionFlags = 0;
675 if (contentAccessible) {
676 substitutionFlags |= nsIResProtocolHandler::ALLOW_CONTENT_ACCESS;
678 rv = rph->SetSubstitutionWithFlags(host, resolved, substitutionFlags);
679 if (NS_FAILED(rv)) {
680 LogMessageWithContext(cx.GetManifestURI(), lineno,
681 nsIScriptError::warningFlag,
682 "Warning: cannot set substitution for '%s'.", uri);