Bug 1782261 [wpt PR 35272] - Create toggles on elements when specified by the 'toggle...
[gecko.git] / chrome / nsChromeRegistryChrome.cpp
bloba7546be3c8a791e81d3ed577afa650895aec8c98
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/Components.h"
33 #include "mozilla/Preferences.h"
34 #include "nsIResProtocolHandler.h"
35 #include "nsIScriptError.h"
36 #include "nsIXULRuntime.h"
38 #define PACKAGE_OVERRIDE_BRANCH "chrome.override_package."
39 #define SKIN "classic/1.0"_ns
41 using namespace mozilla;
42 using mozilla::dom::ContentParent;
43 using mozilla::dom::PContentParent;
44 using mozilla::intl::LocaleService;
46 // We use a "best-fit" algorithm for matching locales and themes.
47 // 1) the exact selected locale/theme
48 // 2) (locales only) same language, different country
49 // e.g. en-GB is the selected locale, only en-US is available
50 // 3) any available locale/theme
52 /**
53 * Match the language-part of two lang-COUNTRY codes, hopefully but
54 * not guaranteed to be in the form ab-CD or abz-CD. "ab" should also
55 * work, any other garbage-in will produce undefined results as long
56 * as it does not crash.
58 static bool LanguagesMatch(const nsACString& a, const nsACString& b) {
59 if (a.Length() < 2 || b.Length() < 2) return false;
61 nsACString::const_iterator as, ae, bs, be;
62 a.BeginReading(as);
63 a.EndReading(ae);
64 b.BeginReading(bs);
65 b.EndReading(be);
67 while (*as == *bs) {
68 if (*as == '-') return true;
70 ++as;
71 ++bs;
73 // reached the end
74 if (as == ae && bs == be) return true;
76 // "a" is short
77 if (as == ae) return (*bs == '-');
79 // "b" is short
80 if (bs == be) return (*as == '-');
83 return false;
86 nsChromeRegistryChrome::nsChromeRegistryChrome()
87 : mProfileLoaded(false), mDynamicRegistration(true) {}
89 nsChromeRegistryChrome::~nsChromeRegistryChrome() {}
91 nsresult nsChromeRegistryChrome::Init() {
92 nsresult rv = nsChromeRegistry::Init();
93 if (NS_FAILED(rv)) return rv;
95 bool safeMode = false;
96 nsCOMPtr<nsIXULRuntime> xulrun(do_GetService(XULAPPINFO_SERVICE_CONTRACTID));
97 if (xulrun) xulrun->GetInSafeMode(&safeMode);
99 nsCOMPtr<nsIObserverService> obsService =
100 mozilla::services::GetObserverService();
101 if (obsService) {
102 obsService->AddObserver(this, "profile-initial-state", true);
103 obsService->AddObserver(this, "intl:app-locales-changed", true);
106 return NS_OK;
109 NS_IMETHODIMP
110 nsChromeRegistryChrome::GetLocalesForPackage(
111 const nsACString& aPackage, nsIUTF8StringEnumerator** aResult) {
112 nsCString realpackage;
113 nsresult rv = OverrideLocalePackage(aPackage, realpackage);
114 if (NS_FAILED(rv)) return rv;
116 nsTArray<nsCString>* a = new nsTArray<nsCString>;
117 if (!a) return NS_ERROR_OUT_OF_MEMORY;
119 PackageEntry* entry;
120 if (mPackagesHash.Get(realpackage, &entry)) {
121 entry->locales.EnumerateToArray(a);
124 rv = NS_NewAdoptingUTF8StringEnumerator(aResult, a);
125 if (NS_FAILED(rv)) delete a;
127 return rv;
130 NS_IMETHODIMP
131 nsChromeRegistryChrome::IsLocaleRTL(const nsACString& package, bool* aResult) {
132 *aResult = false;
134 nsAutoCString locale;
135 GetSelectedLocale(package, locale);
136 if (locale.Length() < 2) return NS_OK;
138 *aResult = LocaleService::IsLocaleRTL(locale);
139 return NS_OK;
143 * This method negotiates only between the app locale and the available
144 * chrome packages.
146 * If you want to get the current application's UI locale, please use
147 * LocaleService::GetAppLocaleAsBCP47.
149 nsresult nsChromeRegistryChrome::GetSelectedLocale(const nsACString& aPackage,
150 nsACString& aLocale) {
151 nsAutoCString reqLocale;
152 if (aPackage.EqualsLiteral("global")) {
153 LocaleService::GetInstance()->GetAppLocaleAsBCP47(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 return NS_OK;
172 nsresult nsChromeRegistryChrome::OverrideLocalePackage(
173 const nsACString& aPackage, nsACString& aOverride) {
174 const nsACString& pref = nsLiteralCString(PACKAGE_OVERRIDE_BRANCH) + aPackage;
175 nsAutoCString override;
176 nsresult rv = mozilla::Preferences::GetCString(PromiseFlatCString(pref).get(),
177 override);
178 if (NS_SUCCEEDED(rv)) {
179 aOverride = override;
180 } else {
181 aOverride = aPackage;
183 return NS_OK;
186 NS_IMETHODIMP
187 nsChromeRegistryChrome::Observe(nsISupports* aSubject, const char* aTopic,
188 const char16_t* someData) {
189 nsresult rv = NS_OK;
191 if (!strcmp("profile-initial-state", aTopic)) {
192 mProfileLoaded = true;
193 } else if (!strcmp("intl:app-locales-changed", aTopic)) {
194 if (mProfileLoaded) {
195 FlushAllCaches();
197 } else {
198 NS_ERROR("Unexpected observer topic!");
201 return rv;
204 NS_IMETHODIMP
205 nsChromeRegistryChrome::CheckForNewChrome() {
206 nsCOMPtr<nsIAppStartup> appStartup = components::AppStartup::Service();
207 if (appStartup->GetShuttingDown()) {
208 MOZ_ASSERT(false, "checking for new chrome during shutdown");
209 return NS_ERROR_UNEXPECTED;
212 mPackagesHash.Clear();
213 mOverrideTable.Clear();
215 mDynamicRegistration = false;
217 nsComponentManagerImpl::gComponentManager->RereadChromeManifests();
219 mDynamicRegistration = true;
221 SendRegisteredChrome(nullptr);
222 return NS_OK;
225 static void SerializeURI(nsIURI* aURI, SerializedURI& aSerializedURI) {
226 if (!aURI) return;
228 aURI->GetSpec(aSerializedURI.spec);
231 void nsChromeRegistryChrome::SendRegisteredChrome(
232 mozilla::dom::PContentParent* aParent) {
233 nsTArray<ChromePackage> packages;
234 nsTArray<SubstitutionMapping> resources;
235 nsTArray<OverrideMapping> overrides;
237 for (const auto& entry : mPackagesHash) {
238 ChromePackage chromePackage;
239 ChromePackageFromPackageEntry(entry.GetKey(), entry.GetWeak(),
240 &chromePackage, SKIN);
241 packages.AppendElement(chromePackage);
244 // If we were passed a parent then a new child process has been created and
245 // has requested all of the chrome so send it the resources too. Otherwise
246 // resource mappings are sent by the resource protocol handler dynamically.
247 if (aParent) {
248 nsCOMPtr<nsIIOService> io(do_GetIOService());
249 NS_ENSURE_TRUE_VOID(io);
251 nsCOMPtr<nsIProtocolHandler> ph;
252 nsresult rv = io->GetProtocolHandler("resource", getter_AddRefs(ph));
253 NS_ENSURE_SUCCESS_VOID(rv);
255 nsCOMPtr<nsIResProtocolHandler> irph(do_QueryInterface(ph));
256 nsResProtocolHandler* rph = static_cast<nsResProtocolHandler*>(irph.get());
257 rv = rph->CollectSubstitutions(resources);
258 NS_ENSURE_SUCCESS_VOID(rv);
261 for (const auto& entry : mOverrideTable) {
262 SerializedURI chromeURI, overrideURI;
264 SerializeURI(entry.GetKey(), chromeURI);
265 SerializeURI(entry.GetWeak(), overrideURI);
267 overrides.AppendElement(
268 OverrideMapping{std::move(chromeURI), std::move(overrideURI)});
271 nsAutoCString appLocale;
272 LocaleService::GetInstance()->GetAppLocaleAsBCP47(appLocale);
274 if (aParent) {
275 bool success = aParent->SendRegisterChrome(packages, resources, overrides,
276 appLocale, false);
277 NS_ENSURE_TRUE_VOID(success);
278 } else {
279 nsTArray<ContentParent*> parents;
280 ContentParent::GetAll(parents);
281 if (!parents.Length()) return;
283 for (uint32_t i = 0; i < parents.Length(); i++) {
284 DebugOnly<bool> success = parents[i]->SendRegisterChrome(
285 packages, resources, overrides, appLocale, true);
286 NS_WARNING_ASSERTION(success,
287 "couldn't reset a child's registered chrome");
292 /* static */
293 void nsChromeRegistryChrome::ChromePackageFromPackageEntry(
294 const nsACString& aPackageName, PackageEntry* aPackage,
295 ChromePackage* aChromePackage, const nsCString& aSelectedSkin) {
296 nsAutoCString appLocale;
297 LocaleService::GetInstance()->GetAppLocaleAsBCP47(appLocale);
299 SerializeURI(aPackage->baseURI, aChromePackage->contentBaseURI);
300 SerializeURI(aPackage->locales.GetBase(appLocale, nsProviderArray::LOCALE),
301 aChromePackage->localeBaseURI);
302 SerializeURI(aPackage->skins.GetBase(aSelectedSkin, nsProviderArray::ANY),
303 aChromePackage->skinBaseURI);
304 aChromePackage->package = aPackageName;
305 aChromePackage->flags = aPackage->flags;
308 static bool CanLoadResource(nsIURI* aResourceURI) {
309 bool isLocalResource = false;
310 (void)NS_URIChainHasFlags(aResourceURI,
311 nsIProtocolHandler::URI_IS_LOCAL_RESOURCE,
312 &isLocalResource);
313 return isLocalResource;
316 nsIURI* nsChromeRegistryChrome::GetBaseURIFromPackage(
317 const nsCString& aPackage, const nsCString& aProvider,
318 const nsCString& aPath) {
319 PackageEntry* entry;
320 if (!mPackagesHash.Get(aPackage, &entry)) {
321 if (!mInitialized) return nullptr;
323 LogMessage("No chrome package registered for chrome://%s/%s/%s",
324 aPackage.get(), aProvider.get(), aPath.get());
326 return nullptr;
329 if (aProvider.EqualsLiteral("locale")) {
330 nsAutoCString appLocale;
331 LocaleService::GetInstance()->GetAppLocaleAsLangTag(appLocale);
332 return entry->locales.GetBase(appLocale, nsProviderArray::LOCALE);
333 } else if (aProvider.EqualsLiteral("skin")) {
334 return entry->skins.GetBase(SKIN, nsProviderArray::ANY);
335 } else if (aProvider.EqualsLiteral("content")) {
336 return entry->baseURI;
338 return nullptr;
341 nsresult nsChromeRegistryChrome::GetFlagsFromPackage(const nsCString& aPackage,
342 uint32_t* aFlags) {
343 PackageEntry* entry;
344 if (!mPackagesHash.Get(aPackage, &entry)) return NS_ERROR_FILE_NOT_FOUND;
346 *aFlags = entry->flags;
347 return NS_OK;
350 nsChromeRegistryChrome::ProviderEntry*
351 nsChromeRegistryChrome::nsProviderArray::GetProvider(
352 const nsACString& aPreferred, MatchType aType) {
353 size_t i = mArray.Length();
354 if (!i) return nullptr;
356 ProviderEntry* found = nullptr; // Only set if we find a partial-match locale
357 ProviderEntry* entry = nullptr;
359 while (i--) {
360 entry = &mArray[i];
361 if (aPreferred.Equals(entry->provider)) return entry;
363 if (aType != LOCALE) continue;
365 if (LanguagesMatch(aPreferred, entry->provider)) {
366 found = entry;
367 continue;
370 if (!found && entry->provider.EqualsLiteral("en-US")) found = entry;
373 if (!found && aType != EXACT) return entry;
375 return found;
378 nsIURI* nsChromeRegistryChrome::nsProviderArray::GetBase(
379 const nsACString& aPreferred, MatchType aType) {
380 ProviderEntry* provider = GetProvider(aPreferred, aType);
382 if (!provider) return nullptr;
384 return provider->baseURI;
387 const nsACString& nsChromeRegistryChrome::nsProviderArray::GetSelected(
388 const nsACString& aPreferred, MatchType aType) {
389 ProviderEntry* entry = GetProvider(aPreferred, aType);
391 if (entry) return entry->provider;
393 return EmptyCString();
396 void nsChromeRegistryChrome::nsProviderArray::SetBase(
397 const nsACString& aProvider, nsIURI* aBaseURL) {
398 ProviderEntry* provider = GetProvider(aProvider, EXACT);
400 if (provider) {
401 provider->baseURI = aBaseURL;
402 return;
405 // no existing entries, add a new one
406 mArray.AppendElement(ProviderEntry(aProvider, aBaseURL));
409 void nsChromeRegistryChrome::nsProviderArray::EnumerateToArray(
410 nsTArray<nsCString>* a) {
411 int32_t i = mArray.Length();
412 while (i--) {
413 a->AppendElement(mArray[i].provider);
417 nsIURI* nsChromeRegistry::ManifestProcessingContext::GetManifestURI() {
418 if (!mManifestURI) {
419 nsCString uri;
420 mFile.GetURIString(uri);
421 NS_NewURI(getter_AddRefs(mManifestURI), uri);
423 return mManifestURI;
426 already_AddRefed<nsIURI>
427 nsChromeRegistry::ManifestProcessingContext::ResolveURI(const char* uri) {
428 nsIURI* baseuri = GetManifestURI();
429 if (!baseuri) return nullptr;
431 nsCOMPtr<nsIURI> resolved;
432 nsresult rv = NS_NewURI(getter_AddRefs(resolved), uri, baseuri);
433 if (NS_FAILED(rv)) return nullptr;
435 return resolved.forget();
438 static void EnsureLowerCase(char* aBuf) {
439 for (; *aBuf; ++aBuf) {
440 char ch = *aBuf;
441 if (ch >= 'A' && ch <= 'Z') *aBuf = ch + 'a' - 'A';
445 static void SendManifestEntry(const ChromeRegistryItem& aItem) {
446 nsTArray<ContentParent*> parents;
447 ContentParent::GetAll(parents);
448 if (!parents.Length()) return;
450 for (uint32_t i = 0; i < parents.Length(); i++) {
451 Unused << parents[i]->SendRegisterChromeItem(aItem);
455 void nsChromeRegistryChrome::ManifestContent(ManifestProcessingContext& cx,
456 int lineno, char* const* argv,
457 int flags) {
458 char* package = argv[0];
459 char* uri = argv[1];
461 EnsureLowerCase(package);
463 nsCOMPtr<nsIURI> resolved = cx.ResolveURI(uri);
464 if (!resolved) {
465 LogMessageWithContext(
466 cx.GetManifestURI(), lineno, nsIScriptError::warningFlag,
467 "During chrome registration, unable to create URI '%s'.", uri);
468 return;
471 if (!CanLoadResource(resolved)) {
472 LogMessageWithContext(resolved, lineno, nsIScriptError::warningFlag,
473 "During chrome registration, cannot register "
474 "non-local URI '%s' as content.",
475 uri);
476 return;
479 nsDependentCString packageName(package);
480 PackageEntry* entry = mPackagesHash.GetOrInsertNew(packageName);
481 entry->baseURI = resolved;
482 entry->flags = flags;
484 if (mDynamicRegistration) {
485 ChromePackage chromePackage;
486 ChromePackageFromPackageEntry(packageName, entry, &chromePackage, SKIN);
487 SendManifestEntry(chromePackage);
491 void nsChromeRegistryChrome::ManifestLocale(ManifestProcessingContext& cx,
492 int lineno, char* const* argv,
493 int flags) {
494 char* package = argv[0];
495 char* provider = argv[1];
496 char* uri = argv[2];
498 EnsureLowerCase(package);
500 nsCOMPtr<nsIURI> resolved = cx.ResolveURI(uri);
501 if (!resolved) {
502 LogMessageWithContext(
503 cx.GetManifestURI(), lineno, nsIScriptError::warningFlag,
504 "During chrome registration, unable to create URI '%s'.", uri);
505 return;
508 if (!CanLoadResource(resolved)) {
509 LogMessageWithContext(resolved, lineno, nsIScriptError::warningFlag,
510 "During chrome registration, cannot register "
511 "non-local URI '%s' as content.",
512 uri);
513 return;
516 nsDependentCString packageName(package);
517 PackageEntry* entry = mPackagesHash.GetOrInsertNew(packageName);
518 entry->locales.SetBase(nsDependentCString(provider), resolved);
520 if (mDynamicRegistration) {
521 ChromePackage chromePackage;
522 ChromePackageFromPackageEntry(packageName, entry, &chromePackage, SKIN);
523 SendManifestEntry(chromePackage);
526 // We use mainPackage as the package we track for reporting new locales being
527 // registered. For most cases it will be "global", but for Fennec it will be
528 // "browser".
529 nsAutoCString mainPackage;
530 nsresult rv = OverrideLocalePackage("global"_ns, mainPackage);
531 if (NS_FAILED(rv)) {
532 return;
536 void nsChromeRegistryChrome::ManifestSkin(ManifestProcessingContext& cx,
537 int lineno, char* const* argv,
538 int flags) {
539 char* package = argv[0];
540 char* provider = argv[1];
541 char* uri = argv[2];
543 EnsureLowerCase(package);
545 nsCOMPtr<nsIURI> resolved = cx.ResolveURI(uri);
546 if (!resolved) {
547 LogMessageWithContext(
548 cx.GetManifestURI(), lineno, nsIScriptError::warningFlag,
549 "During chrome registration, unable to create URI '%s'.", uri);
550 return;
553 if (!CanLoadResource(resolved)) {
554 LogMessageWithContext(resolved, lineno, nsIScriptError::warningFlag,
555 "During chrome registration, cannot register "
556 "non-local URI '%s' as content.",
557 uri);
558 return;
561 nsDependentCString packageName(package);
562 PackageEntry* entry = mPackagesHash.GetOrInsertNew(packageName);
563 entry->skins.SetBase(nsDependentCString(provider), resolved);
565 if (mDynamicRegistration) {
566 ChromePackage chromePackage;
567 ChromePackageFromPackageEntry(packageName, entry, &chromePackage, SKIN);
568 SendManifestEntry(chromePackage);
572 void nsChromeRegistryChrome::ManifestOverride(ManifestProcessingContext& cx,
573 int lineno, char* const* argv,
574 int flags) {
575 char* chrome = argv[0];
576 char* resolved = argv[1];
578 nsCOMPtr<nsIURI> chromeuri = cx.ResolveURI(chrome);
579 nsCOMPtr<nsIURI> resolveduri = cx.ResolveURI(resolved);
580 if (!chromeuri || !resolveduri) {
581 LogMessageWithContext(cx.GetManifestURI(), lineno,
582 nsIScriptError::warningFlag,
583 "During chrome registration, unable to create URI.");
584 return;
587 if (cx.mType == NS_SKIN_LOCATION) {
588 bool chromeSkinOnly =
589 chromeuri->SchemeIs("chrome") && resolveduri->SchemeIs("chrome");
590 if (chromeSkinOnly) {
591 nsAutoCString chromePath, resolvedPath;
592 chromeuri->GetPathQueryRef(chromePath);
593 resolveduri->GetPathQueryRef(resolvedPath);
594 chromeSkinOnly = StringBeginsWith(chromePath, "/skin/"_ns) &&
595 StringBeginsWith(resolvedPath, "/skin/"_ns);
597 if (!chromeSkinOnly) {
598 LogMessageWithContext(
599 cx.GetManifestURI(), lineno, nsIScriptError::warningFlag,
600 "Cannot register non-chrome://.../skin/ URIs '%s' and '%s' as "
601 "overrides and/or to be overridden from a skin manifest.",
602 chrome, resolved);
603 return;
607 if (!CanLoadResource(resolveduri)) {
608 LogMessageWithContext(
609 cx.GetManifestURI(), lineno, nsIScriptError::warningFlag,
610 "Cannot register non-local URI '%s' for an override.", resolved);
611 return;
613 mOverrideTable.InsertOrUpdate(chromeuri, resolveduri);
615 if (mDynamicRegistration) {
616 SerializedURI serializedChrome;
617 SerializedURI serializedOverride;
619 SerializeURI(chromeuri, serializedChrome);
620 SerializeURI(resolveduri, serializedOverride);
622 OverrideMapping override = {serializedChrome, serializedOverride};
623 SendManifestEntry(override);
627 void nsChromeRegistryChrome::ManifestResource(ManifestProcessingContext& cx,
628 int lineno, char* const* argv,
629 int flags) {
630 char* package = argv[0];
631 char* uri = argv[1];
633 EnsureLowerCase(package);
634 nsDependentCString host(package);
636 nsCOMPtr<nsIIOService> io = mozilla::components::IO::Service();
637 if (!io) {
638 NS_WARNING("No IO service trying to process chrome manifests");
639 return;
642 nsCOMPtr<nsIProtocolHandler> ph;
643 nsresult rv = io->GetProtocolHandler("resource", getter_AddRefs(ph));
644 if (NS_FAILED(rv)) return;
646 nsCOMPtr<nsIResProtocolHandler> rph = do_QueryInterface(ph);
648 nsCOMPtr<nsIURI> resolved = cx.ResolveURI(uri);
649 if (!resolved) {
650 LogMessageWithContext(
651 cx.GetManifestURI(), lineno, nsIScriptError::warningFlag,
652 "During chrome registration, unable to create URI '%s'.", uri);
653 return;
656 if (!CanLoadResource(resolved)) {
657 LogMessageWithContext(
658 cx.GetManifestURI(), lineno, nsIScriptError::warningFlag,
659 "Warning: cannot register non-local URI '%s' as a resource.", uri);
660 return;
663 // By default, Firefox resources are not content-accessible unless the
664 // manifests opts in.
665 bool contentAccessible = (flags & nsChromeRegistry::CONTENT_ACCESSIBLE);
667 uint32_t substitutionFlags = 0;
668 if (contentAccessible) {
669 substitutionFlags |= nsIResProtocolHandler::ALLOW_CONTENT_ACCESS;
671 rv = rph->SetSubstitutionWithFlags(host, resolved, substitutionFlags);
672 if (NS_FAILED(rv)) {
673 LogMessageWithContext(cx.GetManifestURI(), lineno,
674 nsIScriptError::warningFlag,
675 "Warning: cannot set substitution for '%s'.", uri);