Source code

Revision control

Other Tools

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 "nsNetUtil.h"
8
#include "nsIFileURL.h"
9
#include "nsIJARURI.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"
16
17
namespace mozilla {
18
namespace scache {
19
20
nsresult NewObjectInputStreamFromBuffer(const char* buffer, uint32_t len,
21
nsIObjectInputStream** stream) {
22
nsCOMPtr<nsIInputStream> stringStream;
23
nsresult rv =
24
NS_NewByteInputStream(getter_AddRefs(stringStream), MakeSpan(buffer, len),
25
NS_ASSIGNMENT_DEPEND);
26
MOZ_ALWAYS_SUCCEEDS(rv);
27
28
nsCOMPtr<nsIObjectInputStream> objectInput =
29
NS_NewObjectInputStream(stringStream);
30
31
objectInput.forget(stream);
32
return NS_OK;
33
}
34
35
nsresult NewObjectOutputWrappedStorageStream(
36
nsIObjectOutputStream** wrapperStream, nsIStorageStream** stream,
37
bool wantDebugStream) {
38
nsCOMPtr<nsIStorageStream> storageStream;
39
40
nsresult rv =
41
NS_NewStorageStream(256, UINT32_MAX, getter_AddRefs(storageStream));
42
NS_ENSURE_SUCCESS(rv, rv);
43
44
nsCOMPtr<nsIOutputStream> outputStream = do_QueryInterface(storageStream);
45
46
nsCOMPtr<nsIObjectOutputStream> objectOutput =
47
NS_NewObjectOutputStream(outputStream);
48
49
#ifdef DEBUG
50
if (wantDebugStream) {
51
// Wrap in debug stream to detect unsupported writes of
52
// multiply-referenced non-singleton objects
53
StartupCache* sc = StartupCache::GetSingleton();
54
NS_ENSURE_TRUE(sc, NS_ERROR_UNEXPECTED);
55
nsCOMPtr<nsIObjectOutputStream> debugStream;
56
sc->GetDebugObjectOutputStream(objectOutput, getter_AddRefs(debugStream));
57
debugStream.forget(wrapperStream);
58
} else {
59
objectOutput.forget(wrapperStream);
60
}
61
#else
62
objectOutput.forget(wrapperStream);
63
#endif
64
65
storageStream.forget(stream);
66
return NS_OK;
67
}
68
69
nsresult NewBufferFromStorageStream(nsIStorageStream* storageStream,
70
UniquePtr<char[]>* buffer, uint32_t* len) {
71
nsresult rv;
72
nsCOMPtr<nsIInputStream> inputStream;
73
rv = storageStream->NewInputStream(0, getter_AddRefs(inputStream));
74
NS_ENSURE_SUCCESS(rv, rv);
75
76
uint64_t avail64;
77
rv = inputStream->Available(&avail64);
78
NS_ENSURE_SUCCESS(rv, rv);
79
NS_ENSURE_TRUE(avail64 <= UINT32_MAX, NS_ERROR_FILE_TOO_BIG);
80
81
uint32_t avail = (uint32_t)avail64;
82
auto temp = MakeUnique<char[]>(avail);
83
uint32_t read;
84
rv = inputStream->Read(temp.get(), avail, &read);
85
if (NS_SUCCEEDED(rv) && avail != read) rv = NS_ERROR_UNEXPECTED;
86
87
if (NS_FAILED(rv)) {
88
return rv;
89
}
90
91
*len = avail;
92
*buffer = std::move(temp);
93
return NS_OK;
94
}
95
96
static const char baseName[2][5] = {"gre/", "app/"};
97
98
static inline bool canonicalizeBase(nsAutoCString& spec, nsACString& out) {
99
nsAutoCString greBase, appBase;
100
nsresult rv = mozilla::Omnijar::GetURIString(mozilla::Omnijar::GRE, greBase);
101
if (NS_FAILED(rv) || !greBase.Length()) return false;
102
103
rv = mozilla::Omnijar::GetURIString(mozilla::Omnijar::APP, appBase);
104
if (NS_FAILED(rv)) return false;
105
106
bool underGre = !greBase.Compare(spec.get(), false, greBase.Length());
107
bool underApp =
108
appBase.Length() && !appBase.Compare(spec.get(), false, appBase.Length());
109
110
if (!underGre && !underApp) return false;
111
112
/**
113
* At this point, if both underGre and underApp are true, it can be one
114
* of the two following cases:
115
* - the GRE directory points to a subdirectory of the APP directory,
116
* meaning spec points under GRE.
117
* - the APP directory points to a subdirectory of the GRE directory,
118
* meaning spec points under APP.
119
* Checking the GRE and APP path length is enough to know in which case
120
* we are.
121
*/
122
if (underGre && underApp && greBase.Length() < appBase.Length())
123
underGre = false;
124
125
out.AppendLiteral("/resource/");
126
out.Append(
127
baseName[underGre ? mozilla::Omnijar::GRE : mozilla::Omnijar::APP]);
128
out.Append(Substring(spec, underGre ? greBase.Length() : appBase.Length()));
129
return true;
130
}
131
132
/**
133
* ResolveURI transforms a chrome: or resource: URI into the URI for its
134
* underlying resource, or returns any other URI unchanged.
135
*/
136
nsresult ResolveURI(nsIURI* in, nsIURI** out) {
137
nsresult rv;
138
139
// Resolve resource:// URIs. At the end of this if/else block, we
140
// have both spec and uri variables identifying the same URI.
141
if (in->SchemeIs("resource")) {
142
nsCOMPtr<nsIIOService> ioService = do_GetIOService(&rv);
143
NS_ENSURE_SUCCESS(rv, rv);
144
145
nsCOMPtr<nsIProtocolHandler> ph;
146
rv = ioService->GetProtocolHandler("resource", getter_AddRefs(ph));
147
NS_ENSURE_SUCCESS(rv, rv);
148
149
nsCOMPtr<nsIResProtocolHandler> irph(do_QueryInterface(ph, &rv));
150
NS_ENSURE_SUCCESS(rv, rv);
151
152
nsAutoCString spec;
153
rv = irph->ResolveURI(in, spec);
154
NS_ENSURE_SUCCESS(rv, rv);
155
156
return ioService->NewURI(spec, nullptr, nullptr, out);
157
} else if (in->SchemeIs("chrome")) {
158
nsCOMPtr<nsIChromeRegistry> chromeReg =
159
mozilla::services::GetChromeRegistryService();
160
if (!chromeReg) return NS_ERROR_UNEXPECTED;
161
162
return chromeReg->ConvertChromeURL(in, out);
163
}
164
165
*out = do_AddRef(in).take();
166
return NS_OK;
167
}
168
169
/**
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
180
*
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.
183
*
184
* For example, in the js loader (string is prefixed with jsloader by caller):
188
* jsloader/resource/gre/modules/XPCOMUtils.jsm
190
* jsloader/$PROFILE_DIR/extensions/%7Buuid%7D/components/component.js
192
* jsloader/$PROFILE_DIR/extensions/some.xpi/components/component.js
193
*/
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);
198
199
nsAutoCString spec;
200
rv = uri->GetSpec(spec);
201
NS_ENSURE_SUCCESS(rv, rv);
202
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);
208
209
nsAutoCString path;
210
rv = baseFileURL->GetPathQueryRef(path);
211
NS_ENSURE_SUCCESS(rv, rv);
212
213
out.Append(path);
214
} else if (uri->SchemeIs("jar")) {
215
nsCOMPtr<nsIJARURI> jarURI = do_QueryInterface(uri, &rv);
216
NS_ENSURE_SUCCESS(rv, rv);
217
218
nsCOMPtr<nsIURI> jarFileURI;
219
rv = jarURI->GetJARFile(getter_AddRefs(jarFileURI));
220
NS_ENSURE_SUCCESS(rv, rv);
221
222
rv = PathifyURI(jarFileURI, out);
223
NS_ENSURE_SUCCESS(rv, rv);
224
225
nsAutoCString path;
226
rv = jarURI->GetJAREntry(path);
227
NS_ENSURE_SUCCESS(rv, rv);
228
out.Append('/');
229
out.Append(path);
230
} else { // Very unlikely
231
rv = uri->GetSpec(spec);
232
NS_ENSURE_SUCCESS(rv, rv);
233
234
out.Append('/');
235
out.Append(spec);
236
}
237
}
238
return NS_OK;
239
}
240
241
} // namespace scache
242
} // namespace mozilla