/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 |