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