1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "AddonManagerStartup.h"
7 #include "AddonManagerStartup-inlines.h"
10 #include "jsfriendapi.h"
11 #include "js/Array.h" // JS::IsArrayObject
12 #include "js/ArrayBuffer.h"
13 #include "js/Exception.h"
15 #include "js/PropertyAndElement.h" // JS_GetProperty, JS_SetProperty
16 #include "js/TracingAPI.h"
17 #include "xpcpublic.h"
19 #include "mozilla/AppShutdown.h"
20 #include "mozilla/ClearOnShutdown.h"
21 #include "mozilla/EndianUtils.h"
22 #include "mozilla/Components.h"
23 #include "mozilla/Compression.h"
24 #include "mozilla/LinkedList.h"
25 #include "mozilla/Preferences.h"
26 #include "mozilla/ResultExtensions.h"
27 #include "mozilla/URLPreloader.h"
28 #include "mozilla/Unused.h"
29 #include "mozilla/ErrorResult.h"
30 #include "mozilla/Services.h"
31 #include "mozilla/Try.h"
32 #include "mozilla/dom/ipc/StructuredCloneData.h"
33 #include "mozilla/dom/TypedArray.h"
35 #include "nsAppDirectoryServiceDefs.h"
36 #include "nsAppRunner.h"
37 #include "nsChromeRegistry.h"
38 #include "nsIDOMWindowUtils.h" // for nsIJSRAIIHelper
39 #include "nsIFileURL.h"
40 #include "nsIIOService.h"
41 #include "nsIJARURI.h"
42 #include "nsIStringEnumerator.h"
43 #include "nsIZipReader.h"
44 #include "nsJARProtocolHandler.h"
45 #include "nsJSUtils.h"
46 #include "nsIObserverService.h"
47 #include "nsReadableUtils.h"
48 #include "nsXULAppAPI.h"
54 using Compression::LZ4
;
55 using dom::ipc::StructuredCloneData
;
57 AddonManagerStartup
& AddonManagerStartup::GetSingleton() {
58 static RefPtr
<AddonManagerStartup
> singleton
;
60 singleton
= new AddonManagerStartup();
61 ClearOnShutdown(&singleton
);
66 AddonManagerStartup::AddonManagerStartup() = default;
68 nsIFile
* AddonManagerStartup::ProfileDir() {
72 rv
= NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR
,
73 getter_AddRefs(mProfileDir
));
74 MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv
));
80 NS_IMPL_ISUPPORTS(AddonManagerStartup
, amIAddonManagerStartup
, nsIObserver
)
82 /*****************************************************************************
84 *****************************************************************************/
86 static nsresult
ParseJARURI(nsIJARURI
* uri
, nsIURI
** jarFile
,
88 MOZ_TRY(uri
->GetJARFile(jarFile
));
89 MOZ_TRY(uri
->GetJAREntry(entry
));
91 // The entry portion of a jar: URI is required to begin with a '/', but for
92 // nested JAR URIs, the leading / of the outer entry is currently stripped.
93 // This is a bug which should be fixed in the JAR URI code, but...
94 if (entry
.IsEmpty() || entry
[0] != '/') {
100 static nsresult
ParseJARURI(nsIURI
* uri
, nsIURI
** jarFile
, nsCString
& entry
) {
102 nsCOMPtr
<nsIJARURI
> jarURI
= do_QueryInterface(uri
, &rv
);
105 return ParseJARURI(jarURI
, jarFile
, entry
);
108 static Result
<nsCOMPtr
<nsIFile
>, nsresult
> GetFile(nsIURI
* uri
) {
110 nsCOMPtr
<nsIFileURL
> fileURL
= do_QueryInterface(uri
, &rv
);
113 nsCOMPtr
<nsIFile
> file
;
114 MOZ_TRY(fileURL
->GetFile(getter_AddRefs(file
)));
117 return std::move(file
);
120 /*****************************************************************************
122 *****************************************************************************/
124 static already_AddRefed
<nsIFile
> CloneAndAppend(nsIFile
* aFile
,
126 nsCOMPtr
<nsIFile
> file
;
127 aFile
->Clone(getter_AddRefs(file
));
128 file
->AppendNative(nsDependentCString(name
));
129 return file
.forget();
132 static bool IsNormalFile(nsIFile
* file
) {
134 return NS_SUCCEEDED(file
->IsFile(&result
)) && result
;
137 static const char STRUCTURED_CLONE_MAGIC
[] = "mozJSSCLz40v001";
139 template <typename T
>
140 static Result
<nsCString
, nsresult
> DecodeLZ4(const nsACString
& lz4
,
141 const T
& magicNumber
) {
142 constexpr auto HEADER_SIZE
= sizeof(magicNumber
) + 4;
144 // Note: We want to include the null terminator here.
145 nsDependentCSubstring
magic(magicNumber
, sizeof(magicNumber
));
147 if (lz4
.Length() < HEADER_SIZE
|| StringHead(lz4
, magic
.Length()) != magic
) {
148 return Err(NS_ERROR_UNEXPECTED
);
151 auto data
= lz4
.BeginReading() + magic
.Length();
152 auto size
= LittleEndian::readUint32(data
);
155 size_t dataLen
= lz4
.EndReading() - data
;
159 if (!result
.SetLength(size
, fallible
) ||
160 !LZ4::decompress(data
, dataLen
, result
.BeginWriting(), size
,
162 return Err(NS_ERROR_UNEXPECTED
);
165 MOZ_DIAGNOSTIC_ASSERT(size
== outputSize
);
167 return std::move(result
);
170 // Our zlib headers redefine this to MOZ_Z_compress, which breaks LZ4::compress
173 template <typename T
>
174 static Result
<nsCString
, nsresult
> EncodeLZ4(const nsACString
& data
,
175 const T
& magicNumber
) {
176 // Note: We want to include the null terminator here.
177 nsDependentCSubstring
magic(magicNumber
, sizeof(magicNumber
));
179 nsAutoCString result
;
180 result
.Append(magic
);
182 auto off
= result
.Length();
183 if (!result
.SetLength(off
+ 4, fallible
)) {
184 return Err(NS_ERROR_OUT_OF_MEMORY
);
187 LittleEndian::writeUint32(result
.BeginWriting() + off
, data
.Length());
190 auto size
= LZ4::maxCompressedSize(data
.Length());
191 if (!result
.SetLength(off
+ size
, fallible
)) {
192 return Err(NS_ERROR_OUT_OF_MEMORY
);
195 size
= LZ4::compress(data
.BeginReading(), data
.Length(),
196 result
.BeginWriting() + off
);
198 if (!result
.SetLength(off
+ size
, fallible
)) {
199 return Err(NS_ERROR_OUT_OF_MEMORY
);
201 return std::move(result
);
204 static_assert(sizeof STRUCTURED_CLONE_MAGIC
% 8 == 0,
205 "Magic number should be an array of uint64_t");
208 * Reads the contents of a LZ4-compressed file, as stored by the IOUtils
209 * module, and returns the decompressed contents on success.
211 static Result
<nsCString
, nsresult
> ReadFileLZ4(nsIFile
* file
) {
212 static const char MAGIC_NUMBER
[] = "mozLz40";
215 MOZ_TRY_VAR(lz4
, URLPreloader::ReadFile(file
));
221 return DecodeLZ4(lz4
, MAGIC_NUMBER
);
224 static bool ParseJSON(JSContext
* cx
, nsACString
& jsonData
,
225 JS::MutableHandle
<JS::Value
> result
) {
226 NS_ConvertUTF8toUTF16
str(jsonData
);
229 return JS_ParseJSON(cx
, str
.Data(), str
.Length(), result
);
232 static Result
<nsCOMPtr
<nsIZipReaderCache
>, nsresult
> GetJarCache() {
233 nsCOMPtr
<nsIIOService
> ios
= components::IO::Service();
234 NS_ENSURE_TRUE(ios
, Err(NS_ERROR_FAILURE
));
236 nsCOMPtr
<nsIProtocolHandler
> jarProto
;
237 MOZ_TRY(ios
->GetProtocolHandler("jar", getter_AddRefs(jarProto
)));
239 auto jar
= static_cast<nsJARProtocolHandler
*>(jarProto
.get());
242 nsCOMPtr
<nsIZipReaderCache
> zipCache
= jar
->JarCache();
243 return std::move(zipCache
);
246 static Result
<FileLocation
, nsresult
> GetFileLocation(nsIURI
* uri
) {
247 FileLocation location
;
249 nsCOMPtr
<nsIFileURL
> fileURL
= do_QueryInterface(uri
);
250 nsCOMPtr
<nsIFile
> file
;
252 MOZ_TRY(fileURL
->GetFile(getter_AddRefs(file
)));
255 nsCOMPtr
<nsIURI
> fileURI
;
257 MOZ_TRY(ParseJARURI(uri
, getter_AddRefs(fileURI
), entry
));
259 MOZ_TRY_VAR(file
, GetFile(fileURI
));
261 location
.Init(file
, entry
.get());
264 return std::move(location
);
267 /*****************************************************************************
269 *****************************************************************************/
271 class MOZ_STACK_CLASS WrapperBase
{
273 WrapperBase(JSContext
* cx
, JSObject
* object
) : mCx(cx
), mObject(cx
, object
) {}
275 WrapperBase(JSContext
* cx
, const JS::Value
& value
) : mCx(cx
), mObject(cx
) {
276 if (value
.isObject()) {
277 mObject
= &value
.toObject();
279 mObject
= JS_NewPlainObject(cx
);
285 JS::Rooted
<JSObject
*> mObject
;
287 bool GetBool(const char* name
, bool defVal
= false);
289 double GetNumber(const char* name
, double defVal
= 0);
291 nsString
GetString(const char* name
, const char* defVal
= "");
293 JSObject
* GetObject(const char* name
);
296 bool WrapperBase::GetBool(const char* name
, bool defVal
) {
297 JS::Rooted
<JSObject
*> obj(mCx
, mObject
);
299 JS::Rooted
<JS::Value
> val(mCx
, JS::UndefinedValue());
300 if (!JS_GetProperty(mCx
, obj
, name
, &val
)) {
301 JS_ClearPendingException(mCx
);
304 if (val
.isBoolean()) {
305 return val
.toBoolean();
310 double WrapperBase::GetNumber(const char* name
, double defVal
) {
311 JS::Rooted
<JSObject
*> obj(mCx
, mObject
);
313 JS::Rooted
<JS::Value
> val(mCx
, JS::UndefinedValue());
314 if (!JS_GetProperty(mCx
, obj
, name
, &val
)) {
315 JS_ClearPendingException(mCx
);
318 if (val
.isNumber()) {
319 return val
.toNumber();
324 nsString
WrapperBase::GetString(const char* name
, const char* defVal
) {
325 JS::Rooted
<JSObject
*> obj(mCx
, mObject
);
327 JS::Rooted
<JS::Value
> val(mCx
, JS::UndefinedValue());
328 if (!JS_GetProperty(mCx
, obj
, name
, &val
)) {
329 JS_ClearPendingException(mCx
);
333 if (val
.isString()) {
334 AssignJSString(mCx
, res
, val
.toString());
336 res
.AppendASCII(defVal
);
341 JSObject
* WrapperBase::GetObject(const char* name
) {
342 JS::Rooted
<JSObject
*> obj(mCx
, mObject
);
344 JS::Rooted
<JS::Value
> val(mCx
, JS::UndefinedValue());
345 if (!JS_GetProperty(mCx
, obj
, name
, &val
)) {
346 JS_ClearPendingException(mCx
);
349 if (val
.isObject()) {
350 return &val
.toObject();
355 class MOZ_STACK_CLASS InstallLocation
: public WrapperBase
{
357 InstallLocation(JSContext
* cx
, const JS::Value
& value
);
359 MOZ_IMPLICIT
InstallLocation(PropertyIterElem
& iter
)
360 : InstallLocation(iter
.Cx(), iter
.Value()) {}
362 InstallLocation(const InstallLocation
& other
)
363 : InstallLocation(other
.mCx
, JS::ObjectValue(*other
.mObject
)) {}
365 void SetChanged(bool changed
) {
366 JS::Rooted
<JSObject
*> obj(mCx
, mObject
);
368 JS::Rooted
<JS::Value
> val(mCx
, JS::BooleanValue(changed
));
369 if (!JS_SetProperty(mCx
, obj
, "changed", val
)) {
370 JS_ClearPendingException(mCx
);
374 PropertyIter
& Addons() { return mAddonsIter
.ref(); }
376 nsString
Path() { return GetString("path"); }
378 bool ShouldCheckStartupModifications() {
379 return GetBool("checkStartupModifications");
383 JS::Rooted
<JSObject
*> mAddonsObj
;
384 Maybe
<PropertyIter
> mAddonsIter
;
387 class MOZ_STACK_CLASS Addon
: public WrapperBase
{
389 Addon(JSContext
* cx
, InstallLocation
& location
, const nsAString
& id
,
391 : WrapperBase(cx
, object
), mId(id
), mLocation(location
) {}
393 MOZ_IMPLICIT
Addon(PropertyIterElem
& iter
)
394 : WrapperBase(iter
.Cx(), iter
.Value()),
396 mLocation(*static_cast<InstallLocation
*>(iter
.Context())) {}
398 Addon(const Addon
& other
)
399 : WrapperBase(other
.mCx
, other
.mObject
),
401 mLocation(other
.mLocation
) {}
403 const nsString
& Id() { return mId
; }
405 nsString
Path() { return GetString("path"); }
407 nsString
Type() { return GetString("type", "extension"); }
409 bool Enabled() { return GetBool("enabled"); }
411 double LastModifiedTime() { return GetNumber("lastModifiedTime"); }
413 bool ShouldCheckStartupModifications() {
414 return Type().EqualsLiteral("locale");
417 Result
<nsCOMPtr
<nsIFile
>, nsresult
> FullPath();
419 Result
<bool, nsresult
> UpdateLastModifiedTime();
423 InstallLocation
& mLocation
;
426 Result
<nsCOMPtr
<nsIFile
>, nsresult
> Addon::FullPath() {
427 nsString path
= Path();
429 // First check for an absolute path, in case we have a proxy file.
430 nsCOMPtr
<nsIFile
> file
;
431 if (NS_SUCCEEDED(NS_NewLocalFile(path
, false, getter_AddRefs(file
)))) {
432 return std::move(file
);
435 // If not an absolute path, fall back to a relative path from the location.
436 MOZ_TRY(NS_NewLocalFile(mLocation
.Path(), false, getter_AddRefs(file
)));
438 MOZ_TRY(file
->AppendRelativePath(path
));
439 return std::move(file
);
442 Result
<bool, nsresult
> Addon::UpdateLastModifiedTime() {
443 nsCOMPtr
<nsIFile
> file
;
444 MOZ_TRY_VAR(file
, FullPath());
446 JS::Rooted
<JSObject
*> obj(mCx
, mObject
);
449 if (NS_FAILED(file
->Exists(&result
)) || !result
) {
450 JS::Rooted
<JS::Value
> value(mCx
, JS::NullValue());
451 if (!JS_SetProperty(mCx
, obj
, "currentModifiedTime", value
)) {
452 JS_ClearPendingException(mCx
);
460 nsCOMPtr
<nsIFile
> manifest
= file
;
461 if (!IsNormalFile(manifest
)) {
462 manifest
= CloneAndAppend(file
, "manifest.json");
463 if (!IsNormalFile(manifest
)) {
468 if (NS_FAILED(manifest
->GetLastModifiedTime(&time
))) {
472 double lastModified
= time
;
473 JS::Rooted
<JS::Value
> value(mCx
, JS::NumberValue(lastModified
));
474 if (!JS_SetProperty(mCx
, obj
, "currentModifiedTime", value
)) {
475 JS_ClearPendingException(mCx
);
478 return lastModified
!= LastModifiedTime();
481 InstallLocation::InstallLocation(JSContext
* cx
, const JS::Value
& value
)
482 : WrapperBase(cx
, value
), mAddonsObj(cx
) {
483 mAddonsObj
= GetObject("addons");
485 mAddonsObj
= JS_NewPlainObject(cx
);
487 mAddonsIter
.emplace(cx
, mAddonsObj
, this);
490 /*****************************************************************************
492 *****************************************************************************/
494 nsresult
AddonManagerStartup::ReadStartupData(
495 JSContext
* cx
, JS::MutableHandle
<JS::Value
> locations
) {
496 locations
.set(JS::UndefinedValue());
498 nsCOMPtr
<nsIFile
> file
=
499 CloneAndAppend(ProfileDir(), "addonStartup.json.lz4");
502 auto res
= ReadFileLZ4(file
);
505 } else if (res
.inspectErr() != NS_ERROR_FILE_NOT_FOUND
) {
506 return res
.unwrapErr();
509 if (data
.IsEmpty() || !ParseJSON(cx
, data
, locations
)) {
513 if (!locations
.isObject()) {
514 return NS_ERROR_UNEXPECTED
;
517 JS::Rooted
<JSObject
*> locs(cx
, &locations
.toObject());
518 for (auto e1
: PropertyIter(cx
, locs
)) {
519 InstallLocation
loc(e1
);
521 bool shouldCheck
= loc
.ShouldCheckStartupModifications();
523 for (auto e2
: loc
.Addons()) {
526 if (addon
.Enabled() &&
527 (shouldCheck
|| addon
.ShouldCheckStartupModifications())) {
529 MOZ_TRY_VAR(changed
, addon
.UpdateLastModifiedTime());
531 loc
.SetChanged(true);
540 nsresult
AddonManagerStartup::EncodeBlob(JS::Handle
<JS::Value
> value
,
542 JS::MutableHandle
<JS::Value
> result
) {
543 StructuredCloneData holder
;
546 holder
.Write(cx
, value
, rv
);
548 return rv
.StealNSResult();
551 nsAutoCString scData
;
554 holder
.Data().ForEachDataChunk([&](const char* aData
, size_t aSize
) {
555 return scData
.Append(nsDependentCSubstring(aData
, aSize
),
558 NS_ENSURE_TRUE(ok
, NS_ERROR_OUT_OF_MEMORY
);
561 MOZ_TRY_VAR(lz4
, EncodeLZ4(scData
, STRUCTURED_CLONE_MAGIC
));
563 JS::Rooted
<JSObject
*> obj(cx
, dom::ArrayBuffer::Create(cx
, lz4
, rv
));
564 ENSURE_SUCCESS(rv
, rv
.StealNSResult());
566 result
.set(JS::ObjectValue(*obj
));
570 nsresult
AddonManagerStartup::DecodeBlob(JS::Handle
<JS::Value
> value
,
572 JS::MutableHandle
<JS::Value
> result
) {
573 NS_ENSURE_TRUE(value
.isObject() &&
574 JS::IsArrayBufferObject(&value
.toObject()) &&
575 JS::ArrayBufferHasData(&value
.toObject()),
576 NS_ERROR_INVALID_ARG
);
578 StructuredCloneData holder
;
582 JS::AutoCheckCannotGC nogc
;
584 auto obj
= &value
.toObject();
587 size_t len
= JS::GetArrayBufferByteLength(obj
);
588 NS_ENSURE_TRUE(len
<= INT32_MAX
, NS_ERROR_INVALID_ARG
);
589 nsDependentCSubstring
lz4(
590 reinterpret_cast<char*>(JS::GetArrayBufferData(obj
, &isShared
, nogc
)),
593 MOZ_TRY_VAR(data
, DecodeLZ4(lz4
, STRUCTURED_CLONE_MAGIC
));
596 bool ok
= holder
.CopyExternalData(data
.get(), data
.Length());
597 NS_ENSURE_TRUE(ok
, NS_ERROR_OUT_OF_MEMORY
);
600 holder
.Read(cx
, result
, rv
);
601 return rv
.StealNSResult();
604 static nsresult
EnumerateZip(nsIZipReader
* zip
, const nsACString
& pattern
,
605 nsTArray
<nsString
>& results
) {
606 nsCOMPtr
<nsIUTF8StringEnumerator
> entries
;
607 MOZ_TRY(zip
->FindEntries(pattern
, getter_AddRefs(entries
)));
610 while (NS_SUCCEEDED(entries
->HasMore(&hasMore
)) && hasMore
) {
612 MOZ_TRY(entries
->GetNext(name
));
614 results
.AppendElement(NS_ConvertUTF8toUTF16(name
));
620 nsresult
AddonManagerStartup::EnumerateJAR(nsIURI
* uri
,
621 const nsACString
& pattern
,
622 nsTArray
<nsString
>& results
) {
623 nsCOMPtr
<nsIZipReaderCache
> zipCache
;
624 MOZ_TRY_VAR(zipCache
, GetJarCache());
626 nsCOMPtr
<nsIZipReader
> zip
;
627 nsCOMPtr
<nsIFile
> file
;
628 if (nsCOMPtr
<nsIJARURI
> jarURI
= do_QueryInterface(uri
)) {
629 nsCOMPtr
<nsIURI
> fileURI
;
631 MOZ_TRY(ParseJARURI(jarURI
, getter_AddRefs(fileURI
), entry
));
633 MOZ_TRY_VAR(file
, GetFile(fileURI
));
635 zipCache
->GetInnerZip(file
, Substring(entry
, 1), getter_AddRefs(zip
)));
637 MOZ_TRY_VAR(file
, GetFile(uri
));
638 MOZ_TRY(zipCache
->GetZip(file
, getter_AddRefs(zip
)));
642 return EnumerateZip(zip
, pattern
, results
);
645 nsresult
AddonManagerStartup::EnumerateJARSubtree(nsIURI
* uri
,
646 nsTArray
<nsString
>& results
) {
647 nsCOMPtr
<nsIURI
> fileURI
;
649 MOZ_TRY(ParseJARURI(uri
, getter_AddRefs(fileURI
), entry
));
651 // Mangle the path into a pattern to match all child entries by escaping any
652 // existing pattern matching metacharacters it contains and appending "/*".
653 constexpr auto metaChars
= "[]()?*~|$\\"_ns
;
656 pattern
.SetCapacity(entry
.Length());
658 // The first character of the entry name is "/", which we want to skip.
659 for (auto chr
: Span(Substring(entry
, 1))) {
660 if (metaChars
.FindChar(chr
) >= 0) {
661 pattern
.Append('\\');
665 if (!pattern
.IsEmpty() && !StringEndsWith(pattern
, "/"_ns
)) {
670 return EnumerateJAR(fileURI
, pattern
, results
);
673 nsresult
AddonManagerStartup::InitializeURLPreloader() {
674 MOZ_RELEASE_ASSERT(xpc::IsInAutomation());
676 URLPreloader::ReInitialize();
681 /******************************************************************************
683 ******************************************************************************/
686 static bool sObserverRegistered
;
688 struct ContentEntry final
{
689 explicit ContentEntry(nsTArray
<nsCString
>&& aArgs
, uint8_t aFlags
= 0)
690 : mArgs(std::move(aArgs
)), mFlags(aFlags
) {}
692 AutoTArray
<nsCString
, 2> mArgs
;
696 }; // anonymous namespace
697 }; // namespace mozilla
699 MOZ_DECLARE_RELOCATE_USING_MOVE_CONSTRUCTOR(mozilla::ContentEntry
);
704 class RegistryEntries final
: public nsIJSRAIIHelper
,
705 public LinkedListElement
<RegistryEntries
> {
708 NS_DECL_NSIJSRAIIHELPER
710 using Override
= AutoTArray
<nsCString
, 2>;
711 using Locale
= AutoTArray
<nsCString
, 3>;
713 RegistryEntries(FileLocation
& location
, nsTArray
<Override
>&& overrides
,
714 nsTArray
<ContentEntry
>&& content
, nsTArray
<Locale
>&& locales
)
715 : mLocation(location
),
716 mOverrides(std::move(overrides
)),
717 mContent(std::move(content
)),
718 mLocales(std::move(locales
)) {}
723 virtual ~RegistryEntries() { Unused
<< Destruct(); }
726 FileLocation mLocation
;
727 const nsTArray
<Override
> mOverrides
;
728 const nsTArray
<ContentEntry
> mContent
;
729 const nsTArray
<Locale
> mLocales
;
732 NS_IMPL_ISUPPORTS(RegistryEntries
, nsIJSRAIIHelper
)
734 void RegistryEntries::Register() {
735 RefPtr
<nsChromeRegistry
> cr
= nsChromeRegistry::GetSingleton();
737 nsChromeRegistry::ManifestProcessingContext
context(NS_EXTENSION_LOCATION
,
740 for (auto& override
: mOverrides
) {
741 const char* args
[] = {override
[0].get(), override
[1].get()};
742 cr
->ManifestOverride(context
, 0, const_cast<char**>(args
), 0);
745 for (auto& content
: mContent
) {
746 const char* args
[] = {content
.mArgs
[0].get(), content
.mArgs
[1].get()};
747 cr
->ManifestContent(context
, 0, const_cast<char**>(args
), content
.mFlags
);
750 for (auto& locale
: mLocales
) {
751 const char* args
[] = {locale
[0].get(), locale
[1].get(), locale
[2].get()};
752 cr
->ManifestLocale(context
, 0, const_cast<char**>(args
), 0);
757 RegistryEntries::Destruct() {
761 // No point in doing I/O to check for new chrome during shutdown, return
762 // early in that case.
763 if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed
)) {
767 // When we remove dynamic entries from the registry, we need to rebuild it
768 // in order to ensure a consistent state. See comments in Observe().
769 RefPtr
<nsChromeRegistry
> cr
= nsChromeRegistry::GetSingleton();
770 return cr
->CheckForNewChrome();
775 static LinkedList
<RegistryEntries
>& GetRegistryEntries() {
776 static LinkedList
<RegistryEntries
> sEntries
;
779 }; // anonymous namespace
782 AddonManagerStartup::RegisterChrome(nsIURI
* manifestURI
,
783 JS::Handle
<JS::Value
> locations
,
784 JSContext
* cx
, nsIJSRAIIHelper
** result
) {
785 auto IsArray
= [cx
](JS::Handle
<JS::Value
> val
) -> bool {
787 return JS::IsArrayObject(cx
, val
, &isArray
) && isArray
;
790 NS_ENSURE_ARG_POINTER(manifestURI
);
791 NS_ENSURE_TRUE(IsArray(locations
), NS_ERROR_INVALID_ARG
);
793 FileLocation location
;
794 MOZ_TRY_VAR(location
, GetFileLocation(manifestURI
));
796 nsTArray
<RegistryEntries::Locale
> locales
;
797 nsTArray
<ContentEntry
> content
;
798 nsTArray
<RegistryEntries::Override
> overrides
;
800 JS::Rooted
<JSObject
*> locs(cx
, &locations
.toObject());
801 JS::Rooted
<JS::Value
> arrayVal(cx
);
802 JS::Rooted
<JSObject
*> array(cx
);
804 for (auto elem
: ArrayIter(cx
, locs
)) {
805 arrayVal
= elem
.Value();
806 NS_ENSURE_TRUE(IsArray(arrayVal
), NS_ERROR_INVALID_ARG
);
808 array
= &arrayVal
.toObject();
810 AutoTArray
<nsCString
, 4> vals
;
811 for (auto val
: ArrayIter(cx
, array
)) {
813 NS_ENSURE_TRUE(str
.init(cx
, val
.Value()), NS_ERROR_OUT_OF_MEMORY
);
815 vals
.AppendElement(NS_ConvertUTF16toUTF8(str
));
817 NS_ENSURE_TRUE(vals
.Length() > 0, NS_ERROR_INVALID_ARG
);
819 nsCString type
= vals
[0];
820 vals
.RemoveElementAt(0);
822 if (type
.EqualsLiteral("override")) {
823 NS_ENSURE_TRUE(vals
.Length() == 2, NS_ERROR_INVALID_ARG
);
824 overrides
.AppendElement(std::move(vals
));
825 } else if (type
.EqualsLiteral("content")) {
826 if (vals
.Length() == 3 &&
827 vals
[2].EqualsLiteral("contentaccessible=yes")) {
828 NS_ENSURE_TRUE(xpc::IsInAutomation(), NS_ERROR_INVALID_ARG
);
829 vals
.RemoveElementAt(2);
830 content
.AppendElement(ContentEntry(
831 std::move(vals
), nsChromeRegistry::CONTENT_ACCESSIBLE
));
833 NS_ENSURE_TRUE(vals
.Length() == 2, NS_ERROR_INVALID_ARG
);
834 content
.AppendElement(ContentEntry(std::move(vals
)));
836 } else if (type
.EqualsLiteral("locale")) {
837 NS_ENSURE_TRUE(vals
.Length() == 3, NS_ERROR_INVALID_ARG
);
838 locales
.AppendElement(std::move(vals
));
840 return NS_ERROR_INVALID_ARG
;
844 if (!sObserverRegistered
) {
845 nsCOMPtr
<nsIObserverService
> obs
= services::GetObserverService();
846 NS_ENSURE_TRUE(obs
, NS_ERROR_UNEXPECTED
);
847 obs
->AddObserver(this, "chrome-manifests-loaded", false);
849 sObserverRegistered
= true;
852 auto entry
= MakeRefPtr
<RegistryEntries
>(
853 location
, std::move(overrides
), std::move(content
), std::move(locales
));
856 GetRegistryEntries().insertBack(entry
);
858 entry
.forget(result
);
863 AddonManagerStartup::Observe(nsISupports
* subject
, const char* topic
,
864 const char16_t
* data
) {
865 // The chrome registry is maintained as a set of global resource mappings
866 // generated mainly from manifest files, on-the-fly, as they're parsed.
867 // Entries added later override entries added earlier, and no record is kept
868 // of the former state.
870 // As a result, if we remove a dynamically-added manifest file, or a set of
871 // dynamic entries, the registry needs to be rebuilt from scratch, from the
872 // manifests and dynamic entries that remain. The chrome registry itself
873 // takes care of re-parsing manifes files. This observer notification lets
874 // us know when we need to re-register our dynamic entries.
875 if (!strcmp(topic
, "chrome-manifests-loaded")) {
876 for (auto entry
: GetRegistryEntries()) {
884 } // namespace mozilla