/src/mozilla-central/startupcache/StartupCacheUtils.cpp
Line | Count | Source (jump to first uncovered line) |
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/. */ |
4 | | |
5 | | #include "nsCOMPtr.h" |
6 | | #include "nsIInputStream.h" |
7 | | #include "nsIStringStream.h" |
8 | | #include "nsNetUtil.h" |
9 | | #include "nsIFileURL.h" |
10 | | #include "nsIJARURI.h" |
11 | | #include "nsIResProtocolHandler.h" |
12 | | #include "nsIChromeRegistry.h" |
13 | | #include "nsAutoPtr.h" |
14 | | #include "nsStringStream.h" |
15 | | #include "StartupCacheUtils.h" |
16 | | #include "mozilla/scache/StartupCache.h" |
17 | | #include "mozilla/Omnijar.h" |
18 | | |
19 | | namespace mozilla { |
20 | | namespace scache { |
21 | | |
22 | | nsresult |
23 | | NewObjectInputStreamFromBuffer(UniquePtr<char[]> buffer, uint32_t len, |
24 | | nsIObjectInputStream** stream) |
25 | 0 | { |
26 | 0 | nsCOMPtr<nsIInputStream> stringStream; |
27 | 0 | nsresult rv = NS_NewByteInputStream(getter_AddRefs(stringStream), |
28 | 0 | buffer.release(), len, |
29 | 0 | NS_ASSIGNMENT_ADOPT); |
30 | 0 | MOZ_ALWAYS_SUCCEEDS(rv); |
31 | 0 |
|
32 | 0 | nsCOMPtr<nsIObjectInputStream> objectInput = |
33 | 0 | NS_NewObjectInputStream(stringStream); |
34 | 0 |
|
35 | 0 | objectInput.forget(stream); |
36 | 0 | return NS_OK; |
37 | 0 | } |
38 | | |
39 | | nsresult |
40 | | NewObjectOutputWrappedStorageStream(nsIObjectOutputStream **wrapperStream, |
41 | | nsIStorageStream** stream, |
42 | | bool wantDebugStream) |
43 | 0 | { |
44 | 0 | nsCOMPtr<nsIStorageStream> storageStream; |
45 | 0 |
|
46 | 0 | nsresult rv = NS_NewStorageStream(256, UINT32_MAX, getter_AddRefs(storageStream)); |
47 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
48 | 0 |
|
49 | 0 | nsCOMPtr<nsIOutputStream> outputStream |
50 | 0 | = do_QueryInterface(storageStream); |
51 | 0 |
|
52 | 0 | nsCOMPtr<nsIObjectOutputStream> objectOutput |
53 | 0 | = NS_NewObjectOutputStream(outputStream); |
54 | 0 |
|
55 | | #ifdef DEBUG |
56 | | if (wantDebugStream) { |
57 | | // Wrap in debug stream to detect unsupported writes of |
58 | | // multiply-referenced non-singleton objects |
59 | | StartupCache* sc = StartupCache::GetSingleton(); |
60 | | NS_ENSURE_TRUE(sc, NS_ERROR_UNEXPECTED); |
61 | | nsCOMPtr<nsIObjectOutputStream> debugStream; |
62 | | sc->GetDebugObjectOutputStream(objectOutput, getter_AddRefs(debugStream)); |
63 | | debugStream.forget(wrapperStream); |
64 | | } else { |
65 | | objectOutput.forget(wrapperStream); |
66 | | } |
67 | | #else |
68 | | objectOutput.forget(wrapperStream); |
69 | 0 | #endif |
70 | 0 |
|
71 | 0 | storageStream.forget(stream); |
72 | 0 | return NS_OK; |
73 | 0 | } |
74 | | |
75 | | nsresult |
76 | | NewBufferFromStorageStream(nsIStorageStream *storageStream, |
77 | | UniquePtr<char[]>* buffer, uint32_t* len) |
78 | 0 | { |
79 | 0 | nsresult rv; |
80 | 0 | nsCOMPtr<nsIInputStream> inputStream; |
81 | 0 | rv = storageStream->NewInputStream(0, getter_AddRefs(inputStream)); |
82 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
83 | 0 |
|
84 | 0 | uint64_t avail64; |
85 | 0 | rv = inputStream->Available(&avail64); |
86 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
87 | 0 | NS_ENSURE_TRUE(avail64 <= UINT32_MAX, NS_ERROR_FILE_TOO_BIG); |
88 | 0 |
|
89 | 0 | uint32_t avail = (uint32_t)avail64; |
90 | 0 | auto temp = MakeUnique<char[]>(avail); |
91 | 0 | uint32_t read; |
92 | 0 | rv = inputStream->Read(temp.get(), avail, &read); |
93 | 0 | if (NS_SUCCEEDED(rv) && avail != read) |
94 | 0 | rv = NS_ERROR_UNEXPECTED; |
95 | 0 |
|
96 | 0 | if (NS_FAILED(rv)) { |
97 | 0 | return rv; |
98 | 0 | } |
99 | 0 | |
100 | 0 | *len = avail; |
101 | 0 | *buffer = std::move(temp); |
102 | 0 | return NS_OK; |
103 | 0 | } |
104 | | |
105 | | static const char baseName[2][5] = { "gre/", "app/" }; |
106 | | |
107 | | static inline bool |
108 | | canonicalizeBase(nsAutoCString &spec, |
109 | | nsACString &out) |
110 | 5 | { |
111 | 5 | nsAutoCString greBase, appBase; |
112 | 5 | nsresult rv = mozilla::Omnijar::GetURIString(mozilla::Omnijar::GRE, greBase); |
113 | 5 | if (NS_FAILED(rv) || !greBase.Length()) |
114 | 0 | return false; |
115 | 5 | |
116 | 5 | rv = mozilla::Omnijar::GetURIString(mozilla::Omnijar::APP, appBase); |
117 | 5 | if (NS_FAILED(rv)) |
118 | 5 | return false; |
119 | 5 | |
120 | 5 | bool underGre = !greBase.Compare(spec.get(), false, greBase.Length()); |
121 | 5 | bool underApp = appBase.Length() && |
122 | 5 | !appBase.Compare(spec.get(), false, appBase.Length()); |
123 | 5 | |
124 | 5 | if (!underGre && !underApp) |
125 | 0 | return false; |
126 | 5 | |
127 | 5 | /** |
128 | 5 | * At this point, if both underGre and underApp are true, it can be one |
129 | 5 | * of the two following cases: |
130 | 5 | * - the GRE directory points to a subdirectory of the APP directory, |
131 | 5 | * meaning spec points under GRE. |
132 | 5 | * - the APP directory points to a subdirectory of the GRE directory, |
133 | 5 | * meaning spec points under APP. |
134 | 5 | * Checking the GRE and APP path length is enough to know in which case |
135 | 5 | * we are. |
136 | 5 | */ |
137 | 5 | if (underGre && underApp && greBase.Length() < appBase.Length()) |
138 | 0 | underGre = false; |
139 | 5 | |
140 | 5 | out.AppendLiteral("/resource/"); |
141 | 5 | out.Append(baseName[underGre ? mozilla::Omnijar::GRE : mozilla::Omnijar::APP]); |
142 | 5 | out.Append(Substring(spec, underGre ? greBase.Length() : appBase.Length())); |
143 | 5 | return true; |
144 | 5 | } |
145 | | |
146 | | /** |
147 | | * ResolveURI transforms a chrome: or resource: URI into the URI for its |
148 | | * underlying resource, or returns any other URI unchanged. |
149 | | */ |
150 | | nsresult |
151 | | ResolveURI(nsIURI *in, nsIURI **out) |
152 | 10 | { |
153 | 10 | bool equals; |
154 | 10 | nsresult rv; |
155 | 10 | |
156 | 10 | // Resolve resource:// URIs. At the end of this if/else block, we |
157 | 10 | // have both spec and uri variables identifying the same URI. |
158 | 10 | if (NS_SUCCEEDED(in->SchemeIs("resource", &equals)) && equals) { |
159 | 4 | nsCOMPtr<nsIIOService> ioService = do_GetIOService(&rv); |
160 | 4 | NS_ENSURE_SUCCESS(rv, rv); |
161 | 4 | |
162 | 4 | nsCOMPtr<nsIProtocolHandler> ph; |
163 | 4 | rv = ioService->GetProtocolHandler("resource", getter_AddRefs(ph)); |
164 | 4 | NS_ENSURE_SUCCESS(rv, rv); |
165 | 4 | |
166 | 4 | nsCOMPtr<nsIResProtocolHandler> irph(do_QueryInterface(ph, &rv)); |
167 | 4 | NS_ENSURE_SUCCESS(rv, rv); |
168 | 4 | |
169 | 4 | nsAutoCString spec; |
170 | 4 | rv = irph->ResolveURI(in, spec); |
171 | 4 | NS_ENSURE_SUCCESS(rv, rv); |
172 | 4 | |
173 | 4 | return ioService->NewURI(spec, nullptr, nullptr, out); |
174 | 6 | } else if (NS_SUCCEEDED(in->SchemeIs("chrome", &equals)) && equals) { |
175 | 0 | nsCOMPtr<nsIChromeRegistry> chromeReg = |
176 | 0 | mozilla::services::GetChromeRegistryService(); |
177 | 0 | if (!chromeReg) |
178 | 0 | return NS_ERROR_UNEXPECTED; |
179 | 0 | |
180 | 0 | return chromeReg->ConvertChromeURL(in, out); |
181 | 0 | } |
182 | 6 | |
183 | 6 | *out = do_AddRef(in).take(); |
184 | 6 | return NS_OK; |
185 | 6 | } |
186 | | |
187 | | /** |
188 | | * PathifyURI transforms uris into useful zip paths |
189 | | * to make it easier to manipulate startup cache entries |
190 | | * using standard zip tools. |
191 | | * Transformations applied: |
192 | | * * resource:// URIs are resolved to their corresponding file/jar URI to |
193 | | * canonicalize resources URIs other than gre and app. |
194 | | * * Paths under GRE or APP directory have their base path replaced with |
195 | | * resource/gre or resource/app to avoid depending on install location. |
196 | | * * jar:file:///path/to/file.jar!/sub/path urls are replaced with |
197 | | * /path/to/file.jar/sub/path |
198 | | * |
199 | | * The result is appended to the string passed in. Adding a prefix before |
200 | | * calling is recommended to avoid colliding with other cache users. |
201 | | * |
202 | | * For example, in the js loader (string is prefixed with jsloader by caller): |
203 | | * resource://gre/modules/XPCOMUtils.jsm or |
204 | | * file://$GRE_DIR/modules/XPCOMUtils.jsm or |
205 | | * jar:file://$GRE_DIR/omni.jar!/modules/XPCOMUtils.jsm becomes |
206 | | * jsloader/resource/gre/modules/XPCOMUtils.jsm |
207 | | * file://$PROFILE_DIR/extensions/{uuid}/components/component.js becomes |
208 | | * jsloader/$PROFILE_DIR/extensions/%7Buuid%7D/components/component.js |
209 | | * jar:file://$PROFILE_DIR/extensions/some.xpi!/components/component.js becomes |
210 | | * jsloader/$PROFILE_DIR/extensions/some.xpi/components/component.js |
211 | | */ |
212 | | nsresult |
213 | | PathifyURI(nsIURI *in, nsACString &out) |
214 | 5 | { |
215 | 5 | bool equals; |
216 | 5 | nsresult rv; |
217 | 5 | |
218 | 5 | nsCOMPtr<nsIURI> uri; |
219 | 5 | rv = ResolveURI(in, getter_AddRefs(uri)); |
220 | 5 | NS_ENSURE_SUCCESS(rv, rv); |
221 | 5 | |
222 | 5 | nsAutoCString spec; |
223 | 5 | rv = uri->GetSpec(spec); |
224 | 5 | NS_ENSURE_SUCCESS(rv, rv); |
225 | 5 | |
226 | 5 | if (!canonicalizeBase(spec, out)) { |
227 | 0 | if (NS_SUCCEEDED(uri->SchemeIs("file", &equals)) && equals) { |
228 | 0 | nsCOMPtr<nsIFileURL> baseFileURL; |
229 | 0 | baseFileURL = do_QueryInterface(uri, &rv); |
230 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
231 | 0 |
|
232 | 0 | nsAutoCString path; |
233 | 0 | rv = baseFileURL->GetPathQueryRef(path); |
234 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
235 | 0 |
|
236 | 0 | out.Append(path); |
237 | 0 | } else if (NS_SUCCEEDED(uri->SchemeIs("jar", &equals)) && equals) { |
238 | 0 | nsCOMPtr<nsIJARURI> jarURI = do_QueryInterface(uri, &rv); |
239 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
240 | 0 |
|
241 | 0 | nsCOMPtr<nsIURI> jarFileURI; |
242 | 0 | rv = jarURI->GetJARFile(getter_AddRefs(jarFileURI)); |
243 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
244 | 0 |
|
245 | 0 | rv = PathifyURI(jarFileURI, out); |
246 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
247 | 0 |
|
248 | 0 | nsAutoCString path; |
249 | 0 | rv = jarURI->GetJAREntry(path); |
250 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
251 | 0 | out.Append('/'); |
252 | 0 | out.Append(path); |
253 | 0 | } else { // Very unlikely |
254 | 0 | rv = uri->GetSpec(spec); |
255 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
256 | 0 |
|
257 | 0 | out.Append('/'); |
258 | 0 | out.Append(spec); |
259 | 0 | } |
260 | 0 | } |
261 | 5 | return NS_OK; |
262 | 5 | } |
263 | | |
264 | | } // namespace scache |
265 | | } // namespace mozilla |