1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "nsIInputStream.h"
8 #include "nsIFileURL.h"
10 #include "nsIResProtocolHandler.h"
11 #include "nsIChromeRegistry.h"
12 #include "nsStringStream.h"
13 #include "StartupCacheUtils.h"
14 #include "mozilla/scache/StartupCache.h"
15 #include "mozilla/Omnijar.h"
20 nsresult
NewObjectInputStreamFromBuffer(const char* buffer
, uint32_t len
,
21 nsIObjectInputStream
** stream
) {
22 nsCOMPtr
<nsIInputStream
> stringStream
;
23 nsresult rv
= NS_NewByteInputStream(getter_AddRefs(stringStream
),
24 Span(buffer
, len
), NS_ASSIGNMENT_DEPEND
);
25 MOZ_ALWAYS_SUCCEEDS(rv
);
27 nsCOMPtr
<nsIObjectInputStream
> objectInput
=
28 NS_NewObjectInputStream(stringStream
);
30 objectInput
.forget(stream
);
34 nsresult
NewObjectOutputWrappedStorageStream(
35 nsIObjectOutputStream
** wrapperStream
, nsIStorageStream
** stream
,
36 bool wantDebugStream
) {
37 nsCOMPtr
<nsIStorageStream
> storageStream
;
40 NS_NewStorageStream(256, UINT32_MAX
, getter_AddRefs(storageStream
));
41 NS_ENSURE_SUCCESS(rv
, rv
);
43 nsCOMPtr
<nsIOutputStream
> outputStream
= do_QueryInterface(storageStream
);
45 nsCOMPtr
<nsIObjectOutputStream
> objectOutput
=
46 NS_NewObjectOutputStream(outputStream
);
49 if (wantDebugStream
) {
50 // Wrap in debug stream to detect unsupported writes of
51 // multiply-referenced non-singleton objects
52 StartupCache
* sc
= StartupCache::GetSingleton();
53 NS_ENSURE_TRUE(sc
, NS_ERROR_UNEXPECTED
);
54 nsCOMPtr
<nsIObjectOutputStream
> debugStream
;
55 sc
->GetDebugObjectOutputStream(objectOutput
, getter_AddRefs(debugStream
));
56 debugStream
.forget(wrapperStream
);
58 objectOutput
.forget(wrapperStream
);
61 objectOutput
.forget(wrapperStream
);
64 storageStream
.forget(stream
);
68 nsresult
NewBufferFromStorageStream(nsIStorageStream
* storageStream
,
69 UniquePtr
<char[]>* buffer
, uint32_t* len
) {
71 nsCOMPtr
<nsIInputStream
> inputStream
;
72 rv
= storageStream
->NewInputStream(0, getter_AddRefs(inputStream
));
73 NS_ENSURE_SUCCESS(rv
, rv
);
76 rv
= inputStream
->Available(&avail64
);
77 NS_ENSURE_SUCCESS(rv
, rv
);
78 NS_ENSURE_TRUE(avail64
<= UINT32_MAX
, NS_ERROR_FILE_TOO_BIG
);
80 uint32_t avail
= (uint32_t)avail64
;
81 auto temp
= MakeUnique
<char[]>(avail
);
83 rv
= inputStream
->Read(temp
.get(), avail
, &read
);
84 if (NS_SUCCEEDED(rv
) && avail
!= read
) rv
= NS_ERROR_UNEXPECTED
;
91 *buffer
= std::move(temp
);
95 static const char baseName
[2][5] = {"gre/", "app/"};
97 static inline bool canonicalizeBase(nsAutoCString
& spec
, nsACString
& out
) {
98 nsAutoCString greBase
, appBase
;
99 nsresult rv
= mozilla::Omnijar::GetURIString(mozilla::Omnijar::GRE
, greBase
);
100 if (NS_FAILED(rv
) || !greBase
.Length()) return false;
102 rv
= mozilla::Omnijar::GetURIString(mozilla::Omnijar::APP
, appBase
);
103 if (NS_FAILED(rv
)) return false;
105 bool underGre
= !greBase
.Compare(spec
.get(), false, greBase
.Length());
107 appBase
.Length() && !appBase
.Compare(spec
.get(), false, appBase
.Length());
109 if (!underGre
&& !underApp
) return false;
112 * At this point, if both underGre and underApp are true, it can be one
113 * of the two following cases:
114 * - the GRE directory points to a subdirectory of the APP directory,
115 * meaning spec points under GRE.
116 * - the APP directory points to a subdirectory of the GRE directory,
117 * meaning spec points under APP.
118 * Checking the GRE and APP path length is enough to know in which case
121 if (underGre
&& underApp
&& greBase
.Length() < appBase
.Length())
124 out
.AppendLiteral("/resource/");
126 baseName
[underGre
? mozilla::Omnijar::GRE
: mozilla::Omnijar::APP
]);
127 out
.Append(Substring(spec
, underGre
? greBase
.Length() : appBase
.Length()));
132 * ResolveURI transforms a chrome: or resource: URI into the URI for its
133 * underlying resource, or returns any other URI unchanged.
135 nsresult
ResolveURI(nsIURI
* in
, nsIURI
** out
) {
138 // Resolve resource:// URIs. At the end of this if/else block, we
139 // have both spec and uri variables identifying the same URI.
140 if (in
->SchemeIs("resource")) {
141 nsCOMPtr
<nsIIOService
> ioService
= do_GetIOService(&rv
);
142 NS_ENSURE_SUCCESS(rv
, rv
);
144 nsCOMPtr
<nsIProtocolHandler
> ph
;
145 rv
= ioService
->GetProtocolHandler("resource", getter_AddRefs(ph
));
146 NS_ENSURE_SUCCESS(rv
, rv
);
148 nsCOMPtr
<nsIResProtocolHandler
> irph(do_QueryInterface(ph
, &rv
));
149 NS_ENSURE_SUCCESS(rv
, rv
);
152 rv
= irph
->ResolveURI(in
, spec
);
153 NS_ENSURE_SUCCESS(rv
, rv
);
155 return ioService
->NewURI(spec
, nullptr, nullptr, out
);
157 if (in
->SchemeIs("chrome")) {
158 nsCOMPtr
<nsIChromeRegistry
> chromeReg
=
159 mozilla::services::GetChromeRegistry();
160 if (!chromeReg
) return NS_ERROR_UNEXPECTED
;
162 return chromeReg
->ConvertChromeURL(in
, out
);
165 *out
= do_AddRef(in
).take();
170 * PathifyURI transforms uris into useful zip paths
171 * to make it easier to manipulate startup cache entries
172 * using standard zip tools.
173 * Transformations applied:
174 * * resource:// URIs are resolved to their corresponding file/jar URI to
175 * canonicalize resources URIs other than gre and app.
176 * * Paths under GRE or APP directory have their base path replaced with
177 * resource/gre or resource/app to avoid depending on install location.
178 * * jar:file:///path/to/file.jar!/sub/path urls are replaced with
179 * /path/to/file.jar/sub/path
181 * The result is appended to the string passed in. Adding a prefix before
182 * calling is recommended to avoid colliding with other cache users.
184 * For example, in the js loader (string is prefixed with jsloader by caller):
185 * resource://gre/modules/XPCOMUtils.jsm or
186 * file://$GRE_DIR/modules/XPCOMUtils.jsm or
187 * jar:file://$GRE_DIR/omni.jar!/modules/XPCOMUtils.jsm becomes
188 * jsloader/resource/gre/modules/XPCOMUtils.jsm
189 * file://$PROFILE_DIR/extensions/{uuid}/components/component.js becomes
190 * jsloader/$PROFILE_DIR/extensions/%7Buuid%7D/components/component.js
191 * jar:file://$PROFILE_DIR/extensions/some.xpi!/components/component.js becomes
192 * jsloader/$PROFILE_DIR/extensions/some.xpi/components/component.js
194 nsresult
PathifyURI(nsIURI
* in
, nsACString
& out
) {
195 nsCOMPtr
<nsIURI
> uri
;
196 nsresult rv
= ResolveURI(in
, getter_AddRefs(uri
));
197 NS_ENSURE_SUCCESS(rv
, rv
);
200 rv
= uri
->GetSpec(spec
);
201 NS_ENSURE_SUCCESS(rv
, rv
);
203 if (!canonicalizeBase(spec
, out
)) {
204 if (uri
->SchemeIs("file")) {
205 nsCOMPtr
<nsIFileURL
> baseFileURL
;
206 baseFileURL
= do_QueryInterface(uri
, &rv
);
207 NS_ENSURE_SUCCESS(rv
, rv
);
210 rv
= baseFileURL
->GetPathQueryRef(path
);
211 NS_ENSURE_SUCCESS(rv
, rv
);
214 } else if (uri
->SchemeIs("jar")) {
215 nsCOMPtr
<nsIJARURI
> jarURI
= do_QueryInterface(uri
, &rv
);
216 NS_ENSURE_SUCCESS(rv
, rv
);
218 nsCOMPtr
<nsIURI
> jarFileURI
;
219 rv
= jarURI
->GetJARFile(getter_AddRefs(jarFileURI
));
220 NS_ENSURE_SUCCESS(rv
, rv
);
222 rv
= PathifyURI(jarFileURI
, out
);
223 NS_ENSURE_SUCCESS(rv
, rv
);
226 rv
= jarURI
->GetJAREntry(path
);
227 NS_ENSURE_SUCCESS(rv
, rv
);
230 } else { // Very unlikely
231 rv
= uri
->GetSpec(spec
);
232 NS_ENSURE_SUCCESS(rv
, rv
);
241 } // namespace scache
242 } // namespace mozilla