Line data Source code
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/. */
5 :
6 : #include "AddonManagerStartup.h"
7 : #include "AddonManagerStartup-inlines.h"
8 :
9 : #include "jsapi.h"
10 : #include "jsfriendapi.h"
11 : #include "js/TracingAPI.h"
12 : #include "xpcpublic.h"
13 :
14 : #include "mozilla/ClearOnShutdown.h"
15 : #include "mozilla/EndianUtils.h"
16 : #include "mozilla/Compression.h"
17 : #include "mozilla/Preferences.h"
18 : #include "mozilla/ScopeExit.h"
19 : #include "mozilla/Services.h"
20 : #include "mozilla/Unused.h"
21 : #include "mozilla/ErrorResult.h"
22 : #include "mozilla/dom/ipc/StructuredCloneData.h"
23 :
24 : #include "nsAppDirectoryServiceDefs.h"
25 : #include "nsAppRunner.h"
26 : #include "nsContentUtils.h"
27 : #include "nsIAddonInterposition.h"
28 : #include "nsXULAppAPI.h"
29 :
30 : #include <stdlib.h>
31 :
32 : namespace mozilla {
33 :
34 : template <>
35 : class MOZ_MUST_USE_TYPE GenericErrorResult<nsresult>
36 : {
37 : nsresult mErrorValue;
38 :
39 : template<typename V, typename E2> friend class Result;
40 :
41 : public:
42 0 : explicit GenericErrorResult(nsresult aErrorValue) : mErrorValue(aErrorValue) {}
43 :
44 0 : operator nsresult() { return mErrorValue; }
45 : };
46 :
47 : static inline Result<Ok, nsresult>
48 : WrapNSResult(PRStatus aRv)
49 : {
50 : if (aRv != PR_SUCCESS) {
51 : return Err(NS_ERROR_FAILURE);
52 : }
53 : return Ok();
54 : }
55 :
56 : static inline Result<Ok, nsresult>
57 12 : WrapNSResult(nsresult aRv)
58 : {
59 12 : if (NS_FAILED(aRv)) {
60 0 : return Err(aRv);
61 : }
62 12 : return Ok();
63 : }
64 :
65 : #define NS_TRY(expr) MOZ_TRY(WrapNSResult(expr))
66 :
67 :
68 : using Compression::LZ4;
69 : using dom::ipc::StructuredCloneData;
70 :
71 : #ifdef XP_WIN
72 : # define READ_BINARYMODE "rb"
73 : #else
74 : # define READ_BINARYMODE "r"
75 : #endif
76 :
77 : AddonManagerStartup&
78 17 : AddonManagerStartup::GetSingleton()
79 : {
80 17 : static RefPtr<AddonManagerStartup> singleton;
81 17 : if (!singleton) {
82 3 : singleton = new AddonManagerStartup();
83 3 : ClearOnShutdown(&singleton);
84 : }
85 17 : return *singleton;
86 : }
87 :
88 3 : AddonManagerStartup::AddonManagerStartup()
89 3 : : mInitialized(false)
90 3 : {}
91 :
92 :
93 : nsIFile*
94 1 : AddonManagerStartup::ProfileDir()
95 : {
96 1 : if (!mProfileDir) {
97 : nsresult rv;
98 :
99 1 : rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(mProfileDir));
100 1 : MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
101 : }
102 :
103 1 : return mProfileDir;
104 : }
105 :
106 51 : NS_IMPL_ISUPPORTS(AddonManagerStartup, amIAddonManagerStartup)
107 :
108 :
109 : /*****************************************************************************
110 : * File utils
111 : *****************************************************************************/
112 :
113 : static already_AddRefed<nsIFile>
114 5 : CloneAndAppend(nsIFile* aFile, const char* name)
115 : {
116 10 : nsCOMPtr<nsIFile> file;
117 5 : aFile->Clone(getter_AddRefs(file));
118 5 : file->AppendNative(nsDependentCString(name));
119 10 : return file.forget();
120 : }
121 :
122 : static bool
123 2 : IsNormalFile(nsIFile* file)
124 : {
125 : bool result;
126 2 : return NS_SUCCEEDED(file->IsFile(&result)) && result;
127 : }
128 :
129 : static nsCString
130 1 : ReadFile(const char* path)
131 : {
132 1 : nsCString result;
133 :
134 1 : FILE* fd = fopen(path, READ_BINARYMODE);
135 1 : if (!fd) {
136 0 : return result;
137 : }
138 1 : auto cleanup = MakeScopeExit([&] () {
139 1 : fclose(fd);
140 3 : });
141 :
142 1 : if (fseek(fd, 0, SEEK_END) != 0) {
143 0 : return result;
144 : }
145 1 : size_t len = ftell(fd);
146 1 : if (len <= 0 || fseek(fd, 0, SEEK_SET) != 0) {
147 0 : return result;
148 : }
149 :
150 1 : result.SetLength(len);
151 1 : size_t rd = fread(result.BeginWriting(), sizeof(char), len, fd);
152 1 : if (rd != len) {
153 0 : result.Truncate();
154 : }
155 :
156 1 : return result;
157 : }
158 :
159 : static const char STRUCTURED_CLONE_MAGIC[] = "mozJSSCLz40v001";
160 :
161 : template <typename T>
162 : static Result<nsCString, nsresult>
163 1 : DecodeLZ4(const nsACString& lz4, const T& magicNumber)
164 : {
165 1 : constexpr auto HEADER_SIZE = sizeof(magicNumber) + 4;
166 :
167 : // Note: We want to include the null terminator here.
168 2 : nsDependentCSubstring magic(magicNumber, sizeof(magicNumber));
169 :
170 1 : if (lz4.Length() < HEADER_SIZE || StringHead(lz4, magic.Length()) != magic) {
171 0 : return Err(NS_ERROR_UNEXPECTED);
172 : }
173 :
174 1 : auto data = lz4.BeginReading() + magic.Length();
175 1 : auto size = LittleEndian::readUint32(data);
176 1 : data += 4;
177 :
178 2 : nsCString result;
179 2 : if (!result.SetLength(size, fallible) ||
180 1 : !LZ4::decompress(data, result.BeginWriting(), size)) {
181 0 : return Err(NS_ERROR_UNEXPECTED);
182 : }
183 :
184 1 : return result;
185 : }
186 :
187 : // Our zlib headers redefine this to MOZ_Z_compress, which breaks LZ4::compress
188 : #undef compress
189 :
190 : template <typename T>
191 : static Result<nsCString, nsresult>
192 0 : EncodeLZ4(const nsACString& data, const T& magicNumber)
193 : {
194 : // Note: We want to include the null terminator here.
195 0 : nsDependentCSubstring magic(magicNumber, sizeof(magicNumber));
196 :
197 0 : nsAutoCString result;
198 0 : result.Append(magic);
199 :
200 0 : auto off = result.Length();
201 0 : result.SetLength(off + 4);
202 :
203 0 : LittleEndian::writeUint32(result.BeginWriting() + off, data.Length());
204 0 : off += 4;
205 :
206 0 : auto size = LZ4::maxCompressedSize(data.Length());
207 0 : result.SetLength(off + size);
208 :
209 0 : size = LZ4::compress(data.BeginReading(), data.Length(),
210 0 : result.BeginWriting() + off);
211 :
212 0 : result.SetLength(off + size);
213 0 : return result;
214 : }
215 :
216 : static_assert(sizeof STRUCTURED_CLONE_MAGIC % 8 == 0,
217 : "Magic number should be an array of uint64_t");
218 :
219 : /**
220 : * Reads the contents of a LZ4-compressed file, as stored by the OS.File
221 : * module, and returns the decompressed contents on success.
222 : *
223 : * A nonexistent or empty file is treated as success. A corrupt or non-LZ4
224 : * file is treated as failure.
225 : */
226 : static Result<nsCString, nsresult>
227 1 : ReadFileLZ4(const char* path)
228 : {
229 : static const char MAGIC_NUMBER[] = "mozLz40";
230 :
231 2 : nsCString result;
232 :
233 2 : nsCString lz4 = ReadFile(path);
234 1 : if (lz4.IsEmpty()) {
235 0 : return result;
236 : }
237 :
238 1 : return DecodeLZ4(lz4, MAGIC_NUMBER);
239 : }
240 :
241 : static bool
242 1 : ParseJSON(JSContext* cx, nsACString& jsonData, JS::MutableHandleValue result)
243 : {
244 2 : NS_ConvertUTF8toUTF16 str(jsonData);
245 1 : jsonData.Truncate();
246 :
247 2 : return JS_ParseJSON(cx, str.Data(), str.Length(), result);
248 : }
249 :
250 :
251 : /*****************************************************************************
252 : * JSON data handling
253 : *****************************************************************************/
254 :
255 24 : class MOZ_STACK_CLASS WrapperBase {
256 : protected:
257 : WrapperBase(JSContext* cx, JSObject* object)
258 : : mCx(cx)
259 : , mObject(cx, object)
260 : {}
261 :
262 24 : WrapperBase(JSContext* cx, const JS::Value& value)
263 24 : : mCx(cx)
264 24 : , mObject(cx)
265 : {
266 24 : if (value.isObject()) {
267 24 : mObject = &value.toObject();
268 : } else {
269 0 : mObject = JS_NewPlainObject(cx);
270 : }
271 24 : }
272 :
273 : protected:
274 : JSContext* mCx;
275 : JS::RootedObject mObject;
276 :
277 : bool GetBool(const char* name, bool defVal = false);
278 :
279 : double GetNumber(const char* name, double defVal = 0);
280 :
281 : nsString GetString(const char* name, const char* defVal = "");
282 :
283 : JSObject* GetObject(const char* name);
284 : };
285 :
286 : bool
287 41 : WrapperBase::GetBool(const char* name, bool defVal)
288 : {
289 82 : JS::RootedObject obj(mCx, mObject);
290 :
291 82 : JS::RootedValue val(mCx, JS::UndefinedValue());
292 41 : if (!JS_GetProperty(mCx, obj, name, &val)) {
293 0 : JS_ClearPendingException(mCx);
294 : }
295 :
296 41 : if (val.isBoolean()) {
297 33 : return val.toBoolean();
298 : }
299 8 : return defVal;
300 : }
301 :
302 : double
303 1 : WrapperBase::GetNumber(const char* name, double defVal)
304 : {
305 2 : JS::RootedObject obj(mCx, mObject);
306 :
307 2 : JS::RootedValue val(mCx, JS::UndefinedValue());
308 1 : if (!JS_GetProperty(mCx, obj, name, &val)) {
309 0 : JS_ClearPendingException(mCx);
310 : }
311 :
312 1 : if (val.isNumber()) {
313 1 : return val.toNumber();
314 : }
315 0 : return defVal;
316 : }
317 :
318 : nsString
319 11 : WrapperBase::GetString(const char* name, const char* defVal)
320 : {
321 22 : JS::RootedObject obj(mCx, mObject);
322 :
323 22 : JS::RootedValue val(mCx, JS::UndefinedValue());
324 11 : if (!JS_GetProperty(mCx, obj, name, &val)) {
325 0 : JS_ClearPendingException(mCx);
326 : }
327 :
328 11 : nsString res;
329 11 : if (val.isString()) {
330 9 : AssignJSString(mCx, res, val.toString());
331 : } else {
332 2 : res.AppendASCII(defVal);
333 : }
334 22 : return res;
335 : }
336 :
337 : JSObject*
338 6 : WrapperBase::GetObject(const char* name)
339 : {
340 12 : JS::RootedObject obj(mCx, mObject);
341 :
342 12 : JS::RootedValue val(mCx, JS::UndefinedValue());
343 6 : if (!JS_GetProperty(mCx, obj, name, &val)) {
344 0 : JS_ClearPendingException(mCx);
345 : }
346 :
347 6 : if (val.isObject()) {
348 6 : return &val.toObject();
349 : }
350 0 : return nullptr;
351 : }
352 :
353 :
354 6 : class MOZ_STACK_CLASS InstallLocation : public WrapperBase {
355 : public:
356 : InstallLocation(JSContext* cx, const JS::Value& value);
357 :
358 6 : MOZ_IMPLICIT InstallLocation(PropertyIterElem& iter)
359 6 : : InstallLocation(iter.Cx(), iter.Value())
360 6 : {}
361 :
362 : InstallLocation(const InstallLocation& other)
363 : : InstallLocation(other.mCx, JS::ObjectValue(*other.mObject))
364 : {}
365 :
366 0 : void SetChanged(bool changed)
367 : {
368 0 : JS::RootedObject obj(mCx, mObject);
369 :
370 0 : JS::RootedValue val(mCx, JS::BooleanValue(changed));
371 0 : if (!JS_SetProperty(mCx, obj, "changed", val)) {
372 0 : JS_ClearPendingException(mCx);
373 : }
374 0 : }
375 :
376 4 : PropertyIter& Addons() { return mAddonsIter.ref(); }
377 :
378 4 : nsString Path() { return GetString("path"); }
379 :
380 3 : bool ShouldCheckStartupModifications() { return GetBool("checkStartupModifications"); }
381 :
382 :
383 : private:
384 : JS::RootedObject mAddonsObj;
385 : Maybe<PropertyIter> mAddonsIter;
386 : };
387 :
388 :
389 18 : class MOZ_STACK_CLASS Addon : public WrapperBase {
390 : public:
391 : Addon(JSContext* cx, InstallLocation& location, const nsAString& id, JSObject* object)
392 : : WrapperBase(cx, object)
393 : , mId(id)
394 : , mLocation(location)
395 : {}
396 :
397 18 : MOZ_IMPLICIT Addon(PropertyIterElem& iter)
398 36 : : WrapperBase(iter.Cx(), iter.Value())
399 18 : , mId(iter.Name())
400 36 : , mLocation(*static_cast<InstallLocation*>(iter.Context()))
401 18 : {}
402 :
403 : Addon(const Addon& other)
404 : : WrapperBase(other.mCx, other.mObject)
405 : , mId(other.mId)
406 : , mLocation(other.mLocation)
407 : {}
408 :
409 0 : const nsString& Id() { return mId; }
410 :
411 4 : nsString Path() { return GetString("path"); }
412 :
413 17 : bool Bootstrapped() { return GetBool("bootstrapped"); }
414 :
415 18 : bool Enabled() { return GetBool("enabled"); }
416 :
417 3 : bool ShimsEnabled() { return GetBool("enableShims"); }
418 :
419 1 : double LastModifiedTime() { return GetNumber("lastModifiedTime"); }
420 :
421 :
422 : Result<nsCOMPtr<nsIFile>, nsresult> FullPath();
423 :
424 : NSLocationType LocationType();
425 :
426 : Result<bool, nsresult> UpdateLastModifiedTime();
427 :
428 :
429 : private:
430 : nsString mId;
431 : InstallLocation& mLocation;
432 : };
433 :
434 : Result<nsCOMPtr<nsIFile>, nsresult>
435 4 : Addon::FullPath()
436 : {
437 8 : nsString path = Path();
438 :
439 : // First check for an absolute path, in case we have a proxy file.
440 8 : nsCOMPtr<nsIFile> file;
441 4 : if (NS_SUCCEEDED(NS_NewLocalFile(path, false, getter_AddRefs(file)))) {
442 0 : return Move(file);
443 : }
444 :
445 : // If not an absolute path, fall back to a relative path from the location.
446 4 : NS_TRY(NS_NewLocalFile(mLocation.Path(), false, getter_AddRefs(file)));
447 :
448 4 : NS_TRY(file->AppendRelativePath(path));
449 4 : return Move(file);
450 : }
451 :
452 : NSLocationType
453 3 : Addon::LocationType()
454 : {
455 6 : nsString type = GetString("type", "extension");
456 3 : if (type.LowerCaseEqualsLiteral("theme")) {
457 1 : return NS_SKIN_LOCATION;
458 : }
459 2 : return NS_EXTENSION_LOCATION;
460 : }
461 :
462 : Result<bool, nsresult>
463 1 : Addon::UpdateLastModifiedTime()
464 : {
465 2 : nsCOMPtr<nsIFile> file;
466 1 : MOZ_TRY_VAR(file, FullPath());
467 :
468 : bool result;
469 1 : if (NS_FAILED(file->Exists(&result)) || !result) {
470 0 : return true;
471 : }
472 :
473 : PRTime time;
474 :
475 2 : nsCOMPtr<nsIFile> manifest = file;
476 1 : if (!IsNormalFile(manifest)) {
477 1 : manifest = CloneAndAppend(file, "install.rdf");
478 1 : if (!IsNormalFile(manifest)) {
479 0 : manifest = CloneAndAppend(file, "manifest.json");
480 0 : if (!IsNormalFile(manifest)) {
481 0 : return true;
482 : }
483 : }
484 : }
485 :
486 1 : if (NS_FAILED(manifest->GetLastModifiedTime(&time))) {
487 0 : return true;
488 : }
489 :
490 2 : JS::RootedObject obj(mCx, mObject);
491 :
492 1 : double lastModified = time;
493 2 : JS::RootedValue value(mCx, JS::NumberValue(lastModified));
494 1 : if (!JS_SetProperty(mCx, obj, "currentModifiedTime", value)) {
495 0 : JS_ClearPendingException(mCx);
496 : }
497 :
498 1 : return lastModified != LastModifiedTime();;
499 : }
500 :
501 :
502 6 : InstallLocation::InstallLocation(JSContext* cx, const JS::Value& value)
503 : : WrapperBase(cx, value)
504 : , mAddonsObj(cx)
505 6 : , mAddonsIter()
506 : {
507 6 : mAddonsObj = GetObject("addons");
508 6 : if (!mAddonsObj) {
509 0 : mAddonsObj = JS_NewPlainObject(cx);
510 : }
511 6 : mAddonsIter.emplace(cx, mAddonsObj, this);
512 6 : }
513 :
514 :
515 : /*****************************************************************************
516 : * XPC interfacing
517 : *****************************************************************************/
518 :
519 : static void
520 0 : EnableShims(const nsAString& addonId)
521 : {
522 0 : NS_ConvertUTF16toUTF8 id(addonId);
523 :
524 : nsCOMPtr<nsIAddonInterposition> interposition =
525 0 : do_GetService("@mozilla.org/addons/multiprocess-shims;1");
526 :
527 0 : if (!interposition || !xpc::SetAddonInterposition(id, interposition)) {
528 0 : return;
529 : }
530 :
531 0 : Unused << xpc::AllowCPOWsInAddon(id, true);
532 : }
533 :
534 : Result<Ok, nsresult>
535 3 : AddonManagerStartup::AddInstallLocation(Addon& addon)
536 : {
537 6 : nsCOMPtr<nsIFile> file;
538 3 : MOZ_TRY_VAR(file, addon.FullPath());
539 :
540 6 : nsString path;
541 3 : NS_TRY(file->GetPath(path));
542 :
543 3 : auto type = addon.LocationType();
544 :
545 3 : if (type == NS_SKIN_LOCATION) {
546 1 : mThemePaths.AppendElement(file);
547 : } else {
548 2 : mExtensionPaths.AppendElement(file);
549 : }
550 :
551 3 : if (StringTail(path, 4).LowerCaseEqualsLiteral(".xpi")) {
552 0 : XRE_AddJarManifestLocation(type, file);
553 : } else {
554 6 : nsCOMPtr<nsIFile> manifest = CloneAndAppend(file, "chrome.manifest");
555 3 : XRE_AddManifestLocation(type, manifest);
556 : }
557 3 : return Ok();
558 : }
559 :
560 : nsresult
561 1 : AddonManagerStartup::ReadStartupData(JSContext* cx, JS::MutableHandleValue locations)
562 : {
563 1 : locations.set(JS::UndefinedValue());
564 :
565 2 : nsCOMPtr<nsIFile> file = CloneAndAppend(ProfileDir(), "addonStartup.json.lz4");
566 :
567 2 : nsCString path;
568 1 : NS_TRY(file->GetNativePath(path));
569 :
570 2 : nsCString data;
571 1 : MOZ_TRY_VAR(data, ReadFileLZ4(path.get()));
572 :
573 1 : if (data.IsEmpty() || !ParseJSON(cx, data, locations)) {
574 0 : return NS_OK;
575 : }
576 :
577 1 : if (!locations.isObject()) {
578 0 : return NS_ERROR_UNEXPECTED;
579 : }
580 :
581 2 : JS::RootedObject locs(cx, &locations.toObject());
582 2 : for (auto e1 : PropertyIter(cx, locs)) {
583 4 : InstallLocation loc(e1);
584 :
585 3 : if (!loc.ShouldCheckStartupModifications()) {
586 2 : continue;
587 : }
588 :
589 2 : for (auto e2 : loc.Addons()) {
590 2 : Addon addon(e2);
591 :
592 1 : if (addon.Enabled()) {
593 : bool changed;
594 1 : MOZ_TRY_VAR(changed, addon.UpdateLastModifiedTime());
595 1 : if (changed) {
596 0 : loc.SetChanged(true);
597 : }
598 : }
599 : }
600 : }
601 :
602 1 : return NS_OK;
603 : }
604 :
605 : nsresult
606 1 : AddonManagerStartup::InitializeExtensions(JS::HandleValue locations, JSContext* cx)
607 : {
608 1 : NS_ENSURE_FALSE(mInitialized, NS_ERROR_UNEXPECTED);
609 1 : NS_ENSURE_TRUE(locations.isObject(), NS_ERROR_INVALID_ARG);
610 :
611 1 : mInitialized = true;
612 :
613 1 : if (!Preferences::GetBool("extensions.defaultProviders.enabled", true)) {
614 0 : return NS_OK;
615 : }
616 :
617 1 : bool enableInterpositions = Preferences::GetBool("extensions.interposition.enabled", false);
618 :
619 2 : JS::RootedObject locs(cx, &locations.toObject());
620 4 : for (auto e1 : PropertyIter(cx, locs)) {
621 6 : InstallLocation loc(e1);
622 :
623 20 : for (auto e2 : loc.Addons()) {
624 34 : Addon addon(e2);
625 :
626 17 : if (addon.Enabled() && !addon.Bootstrapped()) {
627 3 : Unused << AddInstallLocation(addon);
628 :
629 3 : if (enableInterpositions && addon.ShimsEnabled()) {
630 0 : EnableShims(addon.Id());
631 : }
632 : }
633 : }
634 : }
635 :
636 1 : return NS_OK;
637 : }
638 :
639 : nsresult
640 0 : AddonManagerStartup::EncodeBlob(JS::HandleValue value, JSContext* cx, JS::MutableHandleValue result)
641 : {
642 0 : StructuredCloneData holder;
643 :
644 0 : ErrorResult rv;
645 0 : holder.Write(cx, value, rv);
646 0 : if (rv.Failed()) {
647 0 : return rv.StealNSResult();
648 : }
649 :
650 0 : nsAutoCString scData;
651 :
652 0 : auto& data = holder.Data();
653 0 : auto iter = data.Iter();
654 0 : while (!iter.Done()) {
655 0 : scData.Append(nsDependentCSubstring(iter.Data(), iter.RemainingInSegment()));
656 0 : iter.Advance(data, iter.RemainingInSegment());
657 : }
658 :
659 0 : nsCString lz4;
660 0 : MOZ_TRY_VAR(lz4, EncodeLZ4(scData, STRUCTURED_CLONE_MAGIC));
661 :
662 0 : JS::RootedObject obj(cx);
663 0 : NS_TRY(nsContentUtils::CreateArrayBuffer(cx, lz4, &obj.get()));
664 :
665 0 : result.set(JS::ObjectValue(*obj));
666 0 : return NS_OK;
667 : }
668 :
669 : nsresult
670 0 : AddonManagerStartup::DecodeBlob(JS::HandleValue value, JSContext* cx, JS::MutableHandleValue result)
671 : {
672 0 : NS_ENSURE_TRUE(value.isObject() &&
673 : JS_IsArrayBufferObject(&value.toObject()) &&
674 : JS_ArrayBufferHasData(&value.toObject()),
675 : NS_ERROR_INVALID_ARG);
676 :
677 0 : StructuredCloneData holder;
678 :
679 0 : nsCString data;
680 : {
681 0 : JS::AutoCheckCannotGC nogc;
682 :
683 0 : auto obj = &value.toObject();
684 : bool isShared;
685 :
686 : nsDependentCSubstring lz4(
687 0 : reinterpret_cast<char*>(JS_GetArrayBufferData(obj, &isShared, nogc)),
688 0 : JS_GetArrayBufferByteLength(obj));
689 :
690 0 : MOZ_TRY_VAR(data, DecodeLZ4(lz4, STRUCTURED_CLONE_MAGIC));
691 : }
692 :
693 0 : bool ok = holder.CopyExternalData(data.get(), data.Length());
694 0 : NS_ENSURE_TRUE(ok, NS_ERROR_OUT_OF_MEMORY);
695 :
696 0 : ErrorResult rv;
697 0 : holder.Read(cx, result, rv);
698 0 : return rv.StealNSResult();;
699 : }
700 :
701 : nsresult
702 0 : AddonManagerStartup::Reset()
703 : {
704 0 : MOZ_RELEASE_ASSERT(xpc::IsInAutomation());
705 :
706 0 : mInitialized = false;
707 :
708 0 : mExtensionPaths.Clear();
709 0 : mThemePaths.Clear();
710 :
711 0 : return NS_OK;
712 : }
713 :
714 : } // namespace mozilla
|