Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/toolkit/mozapps/extensions/AddonManagerStartup.cpp
Line
Count
Source (jump to first uncovered line)
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/JSON.h"
12
#include "js/TracingAPI.h"
13
#include "xpcpublic.h"
14
15
#include "mozilla/ClearOnShutdown.h"
16
#include "mozilla/EndianUtils.h"
17
#include "mozilla/Compression.h"
18
#include "mozilla/LinkedList.h"
19
#include "mozilla/Preferences.h"
20
#include "mozilla/ResultExtensions.h"
21
#include "mozilla/ScopeExit.h"
22
#include "mozilla/Services.h"
23
#include "mozilla/URLPreloader.h"
24
#include "mozilla/Unused.h"
25
#include "mozilla/ErrorResult.h"
26
#include "mozilla/dom/ipc/StructuredCloneData.h"
27
28
#include "nsAppDirectoryServiceDefs.h"
29
#include "nsAppRunner.h"
30
#include "nsContentUtils.h"
31
#include "nsChromeRegistry.h"
32
#include "nsIDOMWindowUtils.h" // for nsIJSRAIIHelper
33
#include "nsIFileURL.h"
34
#include "nsIIOService.h"
35
#include "nsIJARProtocolHandler.h"
36
#include "nsIJARURI.h"
37
#include "nsIStringEnumerator.h"
38
#include "nsIZipReader.h"
39
#include "nsJSUtils.h"
40
#include "nsReadableUtils.h"
41
#include "nsXULAppAPI.h"
42
43
#include <stdlib.h>
44
45
namespace mozilla {
46
47
using Compression::LZ4;
48
using dom::ipc::StructuredCloneData;
49
50
#ifdef XP_WIN
51
#  define READ_BINARYMODE "rb"
52
#else
53
#  define READ_BINARYMODE "r"
54
#endif
55
56
AddonManagerStartup&
57
AddonManagerStartup::GetSingleton()
58
0
{
59
0
  static RefPtr<AddonManagerStartup> singleton;
60
0
  if (!singleton) {
61
0
    singleton = new AddonManagerStartup();
62
0
    ClearOnShutdown(&singleton);
63
0
  }
64
0
  return *singleton;
65
0
}
66
67
AddonManagerStartup::AddonManagerStartup()
68
0
{}
69
70
71
nsIFile*
72
AddonManagerStartup::ProfileDir()
73
0
{
74
0
  if (!mProfileDir) {
75
0
    nsresult rv;
76
0
77
0
    rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(mProfileDir));
78
0
    MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
79
0
  }
80
0
81
0
  return mProfileDir;
82
0
}
83
84
NS_IMPL_ISUPPORTS(AddonManagerStartup, amIAddonManagerStartup, nsIObserver)
85
86
87
/*****************************************************************************
88
 * File utils
89
 *****************************************************************************/
90
91
static already_AddRefed<nsIFile>
92
CloneAndAppend(nsIFile* aFile, const char* name)
93
0
{
94
0
  nsCOMPtr<nsIFile> file;
95
0
  aFile->Clone(getter_AddRefs(file));
96
0
  file->AppendNative(nsDependentCString(name));
97
0
  return file.forget();
98
0
}
99
100
static bool
101
IsNormalFile(nsIFile* file)
102
0
{
103
0
  bool result;
104
0
  return NS_SUCCEEDED(file->IsFile(&result)) && result;
105
0
}
106
107
static const char STRUCTURED_CLONE_MAGIC[] = "mozJSSCLz40v001";
108
109
template <typename T>
110
static Result<nsCString, nsresult>
111
DecodeLZ4(const nsACString& lz4, const T& magicNumber)
112
0
{
113
0
  constexpr auto HEADER_SIZE = sizeof(magicNumber) + 4;
114
0
115
0
  // Note: We want to include the null terminator here.
116
0
  nsDependentCSubstring magic(magicNumber, sizeof(magicNumber));
117
0
118
0
  if (lz4.Length() < HEADER_SIZE || StringHead(lz4, magic.Length()) != magic) {
119
0
    return Err(NS_ERROR_UNEXPECTED);
120
0
  }
121
0
122
0
  auto data = lz4.BeginReading() + magic.Length();
123
0
  auto size = LittleEndian::readUint32(data);
124
0
  data += 4;
125
0
126
0
  nsCString result;
127
0
  if (!result.SetLength(size, fallible) ||
128
0
      !LZ4::decompress(data, result.BeginWriting(), size)) {
129
0
    return Err(NS_ERROR_UNEXPECTED);
130
0
  }
131
0
132
0
  return result;
133
0
}
Unexecuted instantiation: Unified_cpp_mozapps_extensions0.cpp:mozilla::Result<nsTString<char>, nsresult> mozilla::DecodeLZ4<char [8]>(nsTSubstring<char> const&, char const (&) [8])
Unexecuted instantiation: Unified_cpp_mozapps_extensions0.cpp:mozilla::Result<nsTString<char>, nsresult> mozilla::DecodeLZ4<char [16]>(nsTSubstring<char> const&, char const (&) [16])
134
135
// Our zlib headers redefine this to MOZ_Z_compress, which breaks LZ4::compress
136
#undef compress
137
138
template <typename T>
139
static Result<nsCString, nsresult>
140
EncodeLZ4(const nsACString& data, const T& magicNumber)
141
0
{
142
0
  // Note: We want to include the null terminator here.
143
0
  nsDependentCSubstring magic(magicNumber, sizeof(magicNumber));
144
0
145
0
  nsAutoCString result;
146
0
  result.Append(magic);
147
0
148
0
  auto off = result.Length();
149
0
  if (!result.SetLength(off + 4, fallible)) {
150
0
    return Err(NS_ERROR_OUT_OF_MEMORY);
151
0
  }
152
0
153
0
  LittleEndian::writeUint32(result.BeginWriting() + off, data.Length());
154
0
  off += 4;
155
0
156
0
  auto size = LZ4::maxCompressedSize(data.Length());
157
0
  if (!result.SetLength(off + size, fallible)) {
158
0
    return Err(NS_ERROR_OUT_OF_MEMORY);
159
0
  }
160
0
161
0
  size = LZ4::compress(data.BeginReading(), data.Length(),
162
0
                       result.BeginWriting() + off);
163
0
164
0
  if (!result.SetLength(off + size, fallible)) {
165
0
    return Err(NS_ERROR_OUT_OF_MEMORY);
166
0
  }
167
0
  return result;
168
0
}
169
170
static_assert(sizeof STRUCTURED_CLONE_MAGIC % 8 == 0,
171
              "Magic number should be an array of uint64_t");
172
173
/**
174
 * Reads the contents of a LZ4-compressed file, as stored by the OS.File
175
 * module, and returns the decompressed contents on success.
176
 */
177
static Result<nsCString, nsresult>
178
ReadFileLZ4(nsIFile* file)
179
0
{
180
0
  static const char MAGIC_NUMBER[] = "mozLz40";
181
0
182
0
  nsCString lz4;
183
0
  MOZ_TRY_VAR(lz4, URLPreloader::ReadFile(file));
184
0
185
0
  if (lz4.IsEmpty()) {
186
0
    return lz4;
187
0
  }
188
0
189
0
  return DecodeLZ4(lz4, MAGIC_NUMBER);
190
0
}
191
192
static bool
193
ParseJSON(JSContext* cx, nsACString& jsonData, JS::MutableHandleValue result)
194
0
{
195
0
  NS_ConvertUTF8toUTF16 str(jsonData);
196
0
  jsonData.Truncate();
197
0
198
0
  return JS_ParseJSON(cx, str.Data(), str.Length(), result);
199
0
}
200
201
static Result<nsCOMPtr<nsIZipReaderCache>, nsresult>
202
GetJarCache()
203
0
{
204
0
  nsCOMPtr<nsIIOService> ios = services::GetIOService();
205
0
  NS_ENSURE_TRUE(ios, Err(NS_ERROR_FAILURE));
206
0
207
0
  nsCOMPtr<nsIProtocolHandler> jarProto;
208
0
  MOZ_TRY(ios->GetProtocolHandler("jar", getter_AddRefs(jarProto)));
209
0
210
0
  nsCOMPtr<nsIJARProtocolHandler> jar = do_QueryInterface(jarProto);
211
0
  MOZ_ASSERT(jar);
212
0
213
0
  nsCOMPtr<nsIZipReaderCache> zipCache;
214
0
  MOZ_TRY(jar->GetJARCache(getter_AddRefs(zipCache)));
215
0
216
0
  return std::move(zipCache);
217
0
}
218
219
static Result<FileLocation, nsresult>
220
GetFileLocation(nsIURI* uri)
221
0
{
222
0
  FileLocation location;
223
0
224
0
  nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(uri);
225
0
  nsCOMPtr<nsIFile> file;
226
0
  if (fileURL) {
227
0
    MOZ_TRY(fileURL->GetFile(getter_AddRefs(file)));
228
0
    location.Init(file);
229
0
  } else {
230
0
    nsCOMPtr<nsIJARURI> jarURI = do_QueryInterface(uri);
231
0
    NS_ENSURE_TRUE(jarURI, Err(NS_ERROR_INVALID_ARG));
232
0
233
0
    nsCOMPtr<nsIURI> fileURI;
234
0
    MOZ_TRY(jarURI->GetJARFile(getter_AddRefs(fileURI)));
235
0
236
0
    fileURL = do_QueryInterface(fileURI);
237
0
    NS_ENSURE_TRUE(fileURL, Err(NS_ERROR_INVALID_ARG));
238
0
239
0
    MOZ_TRY(fileURL->GetFile(getter_AddRefs(file)));
240
0
241
0
    nsCString entry;
242
0
    MOZ_TRY(jarURI->GetJAREntry(entry));
243
0
244
0
    location.Init(file, entry.get());
245
0
  }
246
0
247
0
  return std::move(location);
248
0
}
249
250
251
/*****************************************************************************
252
 * JSON data handling
253
 *****************************************************************************/
254
255
class MOZ_STACK_CLASS WrapperBase {
256
protected:
257
  WrapperBase(JSContext* cx, JSObject* object)
258
    : mCx(cx)
259
    , mObject(cx, object)
260
0
  {}
261
262
  WrapperBase(JSContext* cx, const JS::Value& value)
263
    : mCx(cx)
264
    , mObject(cx)
265
0
  {
266
0
    if (value.isObject()) {
267
0
      mObject = &value.toObject();
268
0
    } else {
269
0
      mObject = JS_NewPlainObject(cx);
270
0
    }
271
0
  }
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
WrapperBase::GetBool(const char* name, bool defVal)
288
0
{
289
0
  JS::RootedObject obj(mCx, mObject);
290
0
291
0
  JS::RootedValue val(mCx, JS::UndefinedValue());
292
0
  if (!JS_GetProperty(mCx, obj, name, &val)) {
293
0
    JS_ClearPendingException(mCx);
294
0
  }
295
0
296
0
  if (val.isBoolean()) {
297
0
    return val.toBoolean();
298
0
  }
299
0
  return defVal;
300
0
}
301
302
double
303
WrapperBase::GetNumber(const char* name, double defVal)
304
0
{
305
0
  JS::RootedObject obj(mCx, mObject);
306
0
307
0
  JS::RootedValue val(mCx, JS::UndefinedValue());
308
0
  if (!JS_GetProperty(mCx, obj, name, &val)) {
309
0
    JS_ClearPendingException(mCx);
310
0
  }
311
0
312
0
  if (val.isNumber()) {
313
0
    return val.toNumber();
314
0
  }
315
0
  return defVal;
316
0
}
317
318
nsString
319
WrapperBase::GetString(const char* name, const char* defVal)
320
0
{
321
0
  JS::RootedObject obj(mCx, mObject);
322
0
323
0
  JS::RootedValue val(mCx, JS::UndefinedValue());
324
0
  if (!JS_GetProperty(mCx, obj, name, &val)) {
325
0
    JS_ClearPendingException(mCx);
326
0
  }
327
0
328
0
  nsString res;
329
0
  if (val.isString()) {
330
0
    AssignJSString(mCx, res, val.toString());
331
0
  } else {
332
0
    res.AppendASCII(defVal);
333
0
  }
334
0
  return res;
335
0
}
336
337
JSObject*
338
WrapperBase::GetObject(const char* name)
339
0
{
340
0
  JS::RootedObject obj(mCx, mObject);
341
0
342
0
  JS::RootedValue val(mCx, JS::UndefinedValue());
343
0
  if (!JS_GetProperty(mCx, obj, name, &val)) {
344
0
    JS_ClearPendingException(mCx);
345
0
  }
346
0
347
0
  if (val.isObject()) {
348
0
    return &val.toObject();
349
0
  }
350
0
  return nullptr;
351
0
}
352
353
354
class MOZ_STACK_CLASS InstallLocation : public WrapperBase {
355
public:
356
  InstallLocation(JSContext* cx, const JS::Value& value);
357
358
  MOZ_IMPLICIT InstallLocation(PropertyIterElem& iter)
359
    : InstallLocation(iter.Cx(), iter.Value())
360
0
  {}
361
362
  InstallLocation(const InstallLocation& other)
363
    : InstallLocation(other.mCx, JS::ObjectValue(*other.mObject))
364
0
  {}
365
366
  void SetChanged(bool changed)
367
0
  {
368
0
    JS::RootedObject obj(mCx, mObject);
369
0
370
0
    JS::RootedValue val(mCx, JS::BooleanValue(changed));
371
0
    if (!JS_SetProperty(mCx, obj, "changed", val)) {
372
0
      JS_ClearPendingException(mCx);
373
0
    }
374
0
  }
375
376
0
  PropertyIter& Addons() { return mAddonsIter.ref(); }
377
378
0
  nsString Path() { return GetString("path"); }
379
380
0
  bool ShouldCheckStartupModifications() { return GetBool("checkStartupModifications"); }
381
382
383
private:
384
  JS::RootedObject mAddonsObj;
385
  Maybe<PropertyIter> mAddonsIter;
386
};
387
388
389
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
0
  {}
396
397
  MOZ_IMPLICIT Addon(PropertyIterElem& iter)
398
    : WrapperBase(iter.Cx(), iter.Value())
399
    , mId(iter.Name())
400
    , mLocation(*static_cast<InstallLocation*>(iter.Context()))
401
0
  {}
402
403
  Addon(const Addon& other)
404
    : WrapperBase(other.mCx, other.mObject)
405
    , mId(other.mId)
406
    , mLocation(other.mLocation)
407
0
  {}
408
409
0
  const nsString& Id() { return mId; }
410
411
0
  nsString Path() { return GetString("path"); }
412
413
0
  nsString Type() { return GetString("type", "extension"); }
414
415
0
  bool Enabled() { return GetBool("enabled"); }
416
417
0
  double LastModifiedTime() { return GetNumber("lastModifiedTime"); }
418
419
  bool ShouldCheckStartupModifications()
420
0
  {
421
0
    return Type().EqualsLiteral("webextension-langpack");
422
0
  }
423
424
425
  Result<nsCOMPtr<nsIFile>, nsresult> FullPath();
426
427
  Result<bool, nsresult> UpdateLastModifiedTime();
428
429
430
private:
431
  nsString mId;
432
  InstallLocation& mLocation;
433
};
434
435
Result<nsCOMPtr<nsIFile>, nsresult>
436
Addon::FullPath()
437
0
{
438
0
  nsString path = Path();
439
0
440
0
  // First check for an absolute path, in case we have a proxy file.
441
0
  nsCOMPtr<nsIFile> file;
442
0
  if (NS_SUCCEEDED(NS_NewLocalFile(path, false, getter_AddRefs(file)))) {
443
0
    return std::move(file);
444
0
  }
445
0
446
0
  // If not an absolute path, fall back to a relative path from the location.
447
0
  MOZ_TRY(NS_NewLocalFile(mLocation.Path(), false, getter_AddRefs(file)));
448
0
449
0
  MOZ_TRY(file->AppendRelativePath(path));
450
0
  return std::move(file);
451
0
}
452
453
Result<bool, nsresult>
454
Addon::UpdateLastModifiedTime()
455
0
{
456
0
  nsCOMPtr<nsIFile> file;
457
0
  MOZ_TRY_VAR(file, FullPath());
458
0
459
0
  JS::RootedObject obj(mCx, mObject);
460
0
461
0
  bool result;
462
0
  if (NS_FAILED(file->Exists(&result)) || !result) {
463
0
    JS::RootedValue value(mCx, JS::NullValue());
464
0
    if (!JS_SetProperty(mCx, obj, "currentModifiedTime", value)) {
465
0
      JS_ClearPendingException(mCx);
466
0
    }
467
0
468
0
    return true;
469
0
  }
470
0
471
0
  PRTime time;
472
0
473
0
  nsCOMPtr<nsIFile> manifest = file;
474
0
  if (!IsNormalFile(manifest)) {
475
0
    manifest = CloneAndAppend(file, "install.rdf");
476
0
    if (!IsNormalFile(manifest)) {
477
0
      manifest = CloneAndAppend(file, "manifest.json");
478
0
      if (!IsNormalFile(manifest)) {
479
0
        return true;
480
0
      }
481
0
    }
482
0
  }
483
0
484
0
  if (NS_FAILED(manifest->GetLastModifiedTime(&time))) {
485
0
    return true;
486
0
  }
487
0
488
0
  double lastModified = time;
489
0
  JS::RootedValue value(mCx, JS::NumberValue(lastModified));
490
0
  if (!JS_SetProperty(mCx, obj, "currentModifiedTime", value)) {
491
0
    JS_ClearPendingException(mCx);
492
0
  }
493
0
494
0
  return lastModified != LastModifiedTime();;
495
0
}
496
497
498
InstallLocation::InstallLocation(JSContext* cx, const JS::Value& value)
499
  : WrapperBase(cx, value)
500
  , mAddonsObj(cx)
501
  , mAddonsIter()
502
0
{
503
0
  mAddonsObj = GetObject("addons");
504
0
  if (!mAddonsObj) {
505
0
    mAddonsObj = JS_NewPlainObject(cx);
506
0
  }
507
0
  mAddonsIter.emplace(cx, mAddonsObj, this);
508
0
}
509
510
511
/*****************************************************************************
512
 * XPC interfacing
513
 *****************************************************************************/
514
515
nsresult
516
AddonManagerStartup::ReadStartupData(JSContext* cx, JS::MutableHandleValue locations)
517
0
{
518
0
  locations.set(JS::UndefinedValue());
519
0
520
0
  nsCOMPtr<nsIFile> file = CloneAndAppend(ProfileDir(), "addonStartup.json.lz4");
521
0
522
0
  nsCString data;
523
0
  auto res = ReadFileLZ4(file);
524
0
  if (res.isOk()) {
525
0
    data = res.unwrap();
526
0
  } else if (res.unwrapErr() != NS_ERROR_FILE_NOT_FOUND) {
527
0
    return res.unwrapErr();
528
0
  }
529
0
530
0
  if (data.IsEmpty() || !ParseJSON(cx, data, locations)) {
531
0
    return NS_OK;
532
0
  }
533
0
534
0
  if (!locations.isObject()) {
535
0
    return NS_ERROR_UNEXPECTED;
536
0
  }
537
0
538
0
  JS::RootedObject locs(cx, &locations.toObject());
539
0
  for (auto e1 : PropertyIter(cx, locs)) {
540
0
    InstallLocation loc(e1);
541
0
542
0
    bool shouldCheck = loc.ShouldCheckStartupModifications();
543
0
544
0
    for (auto e2 : loc.Addons()) {
545
0
      Addon addon(e2);
546
0
547
0
      if (addon.Enabled() && (shouldCheck || addon.ShouldCheckStartupModifications())) {
548
0
        bool changed;
549
0
        MOZ_TRY_VAR(changed, addon.UpdateLastModifiedTime());
550
0
        if (changed) {
551
0
          loc.SetChanged(true);
552
0
        }
553
0
      }
554
0
    }
555
0
  }
556
0
557
0
  return NS_OK;
558
0
}
559
560
nsresult
561
AddonManagerStartup::EncodeBlob(JS::HandleValue value, JSContext* cx, JS::MutableHandleValue result)
562
0
{
563
0
  StructuredCloneData holder;
564
0
565
0
  ErrorResult rv;
566
0
  holder.Write(cx, value, rv);
567
0
  if (rv.Failed()) {
568
0
    return rv.StealNSResult();
569
0
  }
570
0
571
0
  nsAutoCString scData;
572
0
573
0
  holder.Data().ForEachDataChunk([&](const char* aData, size_t aSize) {
574
0
      scData.Append(nsDependentCSubstring(aData, aSize));
575
0
      return true;
576
0
  });
577
0
578
0
  nsCString lz4;
579
0
  MOZ_TRY_VAR(lz4, EncodeLZ4(scData, STRUCTURED_CLONE_MAGIC));
580
0
581
0
  JS::RootedObject obj(cx);
582
0
  MOZ_TRY(nsContentUtils::CreateArrayBuffer(cx, lz4, &obj.get()));
583
0
584
0
  result.set(JS::ObjectValue(*obj));
585
0
  return NS_OK;
586
0
}
587
588
nsresult
589
AddonManagerStartup::DecodeBlob(JS::HandleValue value, JSContext* cx, JS::MutableHandleValue result)
590
0
{
591
0
  NS_ENSURE_TRUE(value.isObject() &&
592
0
                 JS_IsArrayBufferObject(&value.toObject()) &&
593
0
                 JS_ArrayBufferHasData(&value.toObject()),
594
0
                 NS_ERROR_INVALID_ARG);
595
0
596
0
  StructuredCloneData holder;
597
0
598
0
  nsCString data;
599
0
  {
600
0
    JS::AutoCheckCannotGC nogc;
601
0
602
0
    auto obj = &value.toObject();
603
0
    bool isShared;
604
0
605
0
    nsDependentCSubstring lz4(
606
0
      reinterpret_cast<char*>(JS_GetArrayBufferData(obj, &isShared, nogc)),
607
0
      JS_GetArrayBufferByteLength(obj));
608
0
609
0
    MOZ_TRY_VAR(data, DecodeLZ4(lz4, STRUCTURED_CLONE_MAGIC));
610
0
  }
611
0
612
0
  bool ok = holder.CopyExternalData(data.get(), data.Length());
613
0
  NS_ENSURE_TRUE(ok, NS_ERROR_OUT_OF_MEMORY);
614
0
615
0
  ErrorResult rv;
616
0
  holder.Read(cx, result, rv);
617
0
  return rv.StealNSResult();;
618
0
}
619
620
nsresult
621
AddonManagerStartup::EnumerateZipFile(nsIFile* file, const nsACString& pattern,
622
                                      uint32_t* countOut, char16_t*** entriesOut)
623
0
{
624
0
  NS_ENSURE_ARG_POINTER(file);
625
0
  NS_ENSURE_ARG_POINTER(countOut);
626
0
  NS_ENSURE_ARG_POINTER(entriesOut);
627
0
628
0
  nsCOMPtr<nsIZipReaderCache> zipCache;
629
0
  MOZ_TRY_VAR(zipCache, GetJarCache());
630
0
631
0
  nsCOMPtr<nsIZipReader> zip;
632
0
  MOZ_TRY(zipCache->GetZip(file, getter_AddRefs(zip)));
633
0
634
0
  nsCOMPtr<nsIUTF8StringEnumerator> entries;
635
0
  MOZ_TRY(zip->FindEntries(pattern, getter_AddRefs(entries)));
636
0
637
0
  nsTArray<nsString> results;
638
0
  bool hasMore;
639
0
  while (NS_SUCCEEDED(entries->HasMore(&hasMore)) && hasMore) {
640
0
    nsAutoCString name;
641
0
    MOZ_TRY(entries->GetNext(name));
642
0
643
0
    results.AppendElement(NS_ConvertUTF8toUTF16(name));
644
0
  }
645
0
646
0
  auto strResults = MakeUnique<char16_t*[]>(results.Length());
647
0
  for (uint32_t i = 0; i < results.Length(); i++) {
648
0
    strResults[i] = ToNewUnicode(results[i]);
649
0
  }
650
0
651
0
  *countOut = results.Length();
652
0
  *entriesOut = strResults.release();
653
0
654
0
  return NS_OK;
655
0
}
656
657
nsresult
658
AddonManagerStartup::InitializeURLPreloader()
659
0
{
660
0
  MOZ_RELEASE_ASSERT(xpc::IsInAutomation());
661
0
662
0
  URLPreloader::ReInitialize();
663
0
664
0
  return NS_OK;
665
0
}
666
667
/******************************************************************************
668
 * RegisterChrome
669
 ******************************************************************************/
670
671
namespace {
672
static bool sObserverRegistered;
673
674
struct ContentEntry final
675
{
676
  explicit ContentEntry(nsTArray<nsCString>& aArgs, uint8_t aFlags=0)
677
    : mArgs(aArgs)
678
    , mFlags(aFlags)
679
0
  {}
680
681
  ContentEntry(const ContentEntry& other)
682
    : mArgs(other.mArgs)
683
    , mFlags(other.mFlags)
684
0
  {}
685
686
  AutoTArray<nsCString, 2> mArgs;
687
  uint8_t mFlags;
688
};
689
690
}; // anonymous namespace
691
}; // namespace mozilla
692
693
DECLARE_USE_COPY_CONSTRUCTORS(mozilla::ContentEntry);
694
695
namespace mozilla {
696
namespace {
697
698
class RegistryEntries final : public nsIJSRAIIHelper
699
                            , public LinkedListElement<RegistryEntries>
700
{
701
public:
702
  NS_DECL_ISUPPORTS
703
  NS_DECL_NSIJSRAIIHELPER
704
705
  using Override = AutoTArray<nsCString, 2>;
706
  using Locale = AutoTArray<nsCString, 3>;
707
708
  RegistryEntries(FileLocation& location, nsTArray<Override>&& overrides, nsTArray<ContentEntry>&& content, nsTArray<Locale>&& locales)
709
    : mLocation(location)
710
    , mOverrides(std::move(overrides))
711
    , mContent(std::move(content))
712
    , mLocales(std::move(locales))
713
0
  {}
714
715
  void Register();
716
717
protected:
718
  virtual ~RegistryEntries()
719
0
  {
720
0
    Unused << Destruct();
721
0
  }
722
723
private:
724
  FileLocation mLocation;
725
  const nsTArray<Override> mOverrides;
726
  const nsTArray<ContentEntry> mContent;
727
  const nsTArray<Locale> mLocales;
728
};
729
730
NS_IMPL_ISUPPORTS(RegistryEntries, nsIJSRAIIHelper)
731
732
void
733
RegistryEntries::Register()
734
0
{
735
0
  RefPtr<nsChromeRegistry> cr = nsChromeRegistry::GetSingleton();
736
0
737
0
  nsChromeRegistry::ManifestProcessingContext context(NS_EXTENSION_LOCATION, mLocation);
738
0
739
0
  for (auto& override : mOverrides) {
740
0
    const char* args[] = {override[0].get(), override[1].get()};
741
0
    cr->ManifestOverride(context, 0, const_cast<char**>(args), 0);
742
0
  }
743
0
744
0
  for (auto& content : mContent) {
745
0
    const char* args[] = {content.mArgs[0].get(), content.mArgs[1].get()};
746
0
    cr->ManifestContent(context, 0, const_cast<char**>(args), content.mFlags);
747
0
  }
748
0
749
0
  for (auto& locale : mLocales) {
750
0
    const char* args[] = {locale[0].get(), locale[1].get(), locale[2].get()};
751
0
    cr->ManifestLocale(context, 0, const_cast<char**>(args), 0);
752
0
  }
753
0
}
754
755
NS_IMETHODIMP
756
RegistryEntries::Destruct()
757
0
{
758
0
  if (isInList()) {
759
0
    remove();
760
0
761
0
    // When we remove dynamic entries from the registry, we need to rebuild it
762
0
    // in order to ensure a consistent state. See comments in Observe().
763
0
    RefPtr<nsChromeRegistry> cr = nsChromeRegistry::GetSingleton();
764
0
    return cr->CheckForNewChrome();
765
0
  }
766
0
  return NS_OK;
767
0
}
768
769
static LinkedList<RegistryEntries>&
770
GetRegistryEntries()
771
0
{
772
0
  static LinkedList<RegistryEntries> sEntries;
773
0
  return sEntries;
774
0
}
775
}; // anonymous namespace
776
777
NS_IMETHODIMP
778
AddonManagerStartup::RegisterChrome(nsIURI* manifestURI, JS::HandleValue locations,
779
                                    JSContext* cx, nsIJSRAIIHelper** result)
780
0
{
781
0
  auto IsArray = [cx] (JS::HandleValue val) -> bool {
782
0
    bool isArray;
783
0
    return JS_IsArrayObject(cx, val, &isArray) && isArray;
784
0
  };
785
0
786
0
  NS_ENSURE_ARG_POINTER(manifestURI);
787
0
  NS_ENSURE_TRUE(IsArray(locations), NS_ERROR_INVALID_ARG);
788
0
789
0
  FileLocation location;
790
0
  MOZ_TRY_VAR(location, GetFileLocation(manifestURI));
791
0
792
0
793
0
  nsTArray<RegistryEntries::Locale> locales;
794
0
  nsTArray<ContentEntry> content;
795
0
  nsTArray<RegistryEntries::Override> overrides;
796
0
797
0
  JS::RootedObject locs(cx, &locations.toObject());
798
0
  JS::RootedValue arrayVal(cx);
799
0
  JS::RootedObject array(cx);
800
0
801
0
  for (auto elem : ArrayIter(cx, locs)) {
802
0
    arrayVal = elem.Value();
803
0
    NS_ENSURE_TRUE(IsArray(arrayVal), NS_ERROR_INVALID_ARG);
804
0
805
0
    array = &arrayVal.toObject();
806
0
807
0
    AutoTArray<nsCString, 4> vals;
808
0
    for (auto val : ArrayIter(cx, array)) {
809
0
      nsAutoJSString str;
810
0
      NS_ENSURE_TRUE(str.init(cx, val.Value()), NS_ERROR_OUT_OF_MEMORY);
811
0
812
0
      vals.AppendElement(NS_ConvertUTF16toUTF8(str));
813
0
    }
814
0
    NS_ENSURE_TRUE(vals.Length() > 0, NS_ERROR_INVALID_ARG);
815
0
816
0
    nsCString type = vals[0];
817
0
    vals.RemoveElementAt(0);
818
0
819
0
    if (type.EqualsLiteral("override")) {
820
0
      NS_ENSURE_TRUE(vals.Length() == 2, NS_ERROR_INVALID_ARG);
821
0
      overrides.AppendElement(vals);
822
0
    } else if (type.EqualsLiteral("content")) {
823
0
      if (vals.Length() == 3 && vals[2].EqualsLiteral("contentaccessible=yes")) {
824
0
        NS_ENSURE_TRUE(xpc::IsInAutomation(), NS_ERROR_INVALID_ARG);
825
0
        vals.RemoveElementAt(2);
826
0
        content.AppendElement(ContentEntry(vals, nsChromeRegistry::CONTENT_ACCESSIBLE));
827
0
      } else {
828
0
        NS_ENSURE_TRUE(vals.Length() == 2, NS_ERROR_INVALID_ARG);
829
0
        content.AppendElement(ContentEntry(vals));
830
0
      }
831
0
    } else if (type.EqualsLiteral("locale")) {
832
0
      NS_ENSURE_TRUE(vals.Length() == 3, NS_ERROR_INVALID_ARG);
833
0
      locales.AppendElement(vals);
834
0
    } else {
835
0
      return NS_ERROR_INVALID_ARG;
836
0
    }
837
0
  }
838
0
839
0
  if (!sObserverRegistered) {
840
0
    nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
841
0
    NS_ENSURE_TRUE(obs, NS_ERROR_UNEXPECTED);
842
0
    obs->AddObserver(this, "chrome-manifests-loaded", false);
843
0
844
0
    sObserverRegistered = true;
845
0
  }
846
0
847
0
  auto entry = MakeRefPtr<RegistryEntries>(location,
848
0
                                           std::move(overrides),
849
0
                                           std::move(content),
850
0
                                           std::move(locales));
851
0
852
0
  entry->Register();
853
0
  GetRegistryEntries().insertBack(entry);
854
0
855
0
  entry.forget(result);
856
0
  return NS_OK;
857
0
}
858
859
NS_IMETHODIMP
860
AddonManagerStartup::Observe(nsISupports* subject, const char* topic, const char16_t* data)
861
0
{
862
0
  // The chrome registry is maintained as a set of global resource mappings
863
0
  // generated mainly from manifest files, on-the-fly, as they're parsed.
864
0
  // Entries added later override entries added earlier, and no record is kept
865
0
  // of the former state.
866
0
  //
867
0
  // As a result, if we remove a dynamically-added manifest file, or a set of
868
0
  // dynamic entries, the registry needs to be rebuilt from scratch, from the
869
0
  // manifests and dynamic entries that remain. The chrome registry itself
870
0
  // takes care of re-parsing manifes files. This observer notification lets
871
0
  // us know when we need to re-register our dynamic entries.
872
0
  if (!strcmp(topic, "chrome-manifests-loaded")) {
873
0
    for (auto entry : GetRegistryEntries()) {
874
0
      entry->Register();
875
0
    }
876
0
  }
877
0
878
0
  return NS_OK;
879
0
}
880
881
} // namespace mozilla