|
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
|
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 "nsAboutCache.h" |
|
7 #include "nsIInputStream.h" |
|
8 #include "nsIStorageStream.h" |
|
9 #include "nsIURI.h" |
|
10 #include "nsCOMPtr.h" |
|
11 #include "nsNetUtil.h" |
|
12 #include "nsEscape.h" |
|
13 #include "nsAboutProtocolUtils.h" |
|
14 |
|
15 #include "nsICacheService.h" |
|
16 |
|
17 NS_IMPL_ISUPPORTS(nsAboutCache, nsIAboutModule, nsICacheVisitor) |
|
18 |
|
19 NS_IMETHODIMP |
|
20 nsAboutCache::NewChannel(nsIURI *aURI, nsIChannel **result) |
|
21 { |
|
22 NS_ENSURE_ARG_POINTER(aURI); |
|
23 nsresult rv; |
|
24 uint32_t bytesWritten; |
|
25 |
|
26 *result = nullptr; |
|
27 // Get the cache manager service |
|
28 nsCOMPtr<nsICacheService> cacheService = |
|
29 do_GetService(NS_CACHESERVICE_CONTRACTID, &rv); |
|
30 if (NS_FAILED(rv)) return rv; |
|
31 |
|
32 nsCOMPtr<nsIStorageStream> storageStream; |
|
33 nsCOMPtr<nsIOutputStream> outputStream; |
|
34 |
|
35 // Init: (block size, maximum length) |
|
36 rv = NS_NewStorageStream(256, (uint32_t)-1, getter_AddRefs(storageStream)); |
|
37 if (NS_FAILED(rv)) return rv; |
|
38 |
|
39 rv = storageStream->GetOutputStream(0, getter_AddRefs(outputStream)); |
|
40 if (NS_FAILED(rv)) return rv; |
|
41 |
|
42 mBuffer.AssignLiteral( |
|
43 "<!DOCTYPE html>\n" |
|
44 "<html>\n" |
|
45 "<head>\n" |
|
46 " <title>Information about the Cache Service</title>\n" |
|
47 " <link rel=\"stylesheet\" " |
|
48 "href=\"chrome://global/skin/about.css\" type=\"text/css\"/>\n" |
|
49 " <link rel=\"stylesheet\" " |
|
50 "href=\"chrome://global/skin/aboutCache.css\" type=\"text/css\"/>\n" |
|
51 "</head>\n" |
|
52 "<body class=\"aboutPageWideContainer\">\n" |
|
53 "<h1>Information about the Cache Service</h1>\n"); |
|
54 |
|
55 outputStream->Write(mBuffer.get(), mBuffer.Length(), &bytesWritten); |
|
56 |
|
57 rv = ParseURI(aURI, mDeviceID); |
|
58 if (NS_FAILED(rv)) return rv; |
|
59 |
|
60 mStream = outputStream; |
|
61 |
|
62 // nsCacheService::VisitEntries calls nsMemoryCacheDevice::Visit, |
|
63 // nsDiskCacheDevice::Visit and nsOfflineCacheDevice::Visit, |
|
64 // each of which call |
|
65 // 1. VisitDevice (for about:cache), |
|
66 // 2. VisitEntry in a loop (for about:cache?device=disk etc.) |
|
67 rv = cacheService->VisitEntries(this); |
|
68 mBuffer.Truncate(); |
|
69 if (rv == NS_ERROR_NOT_AVAILABLE) { |
|
70 mBuffer.AppendLiteral("<h2>The cache is disabled.</h2>\n"); |
|
71 } |
|
72 else if (NS_FAILED(rv)) { |
|
73 return rv; |
|
74 } |
|
75 |
|
76 if (!mDeviceID.IsEmpty()) { |
|
77 mBuffer.AppendLiteral("</table>\n"); |
|
78 } |
|
79 mBuffer.AppendLiteral("</body>\n" |
|
80 "</html>\n"); |
|
81 outputStream->Write(mBuffer.get(), mBuffer.Length(), &bytesWritten); |
|
82 |
|
83 nsCOMPtr<nsIInputStream> inStr; |
|
84 |
|
85 rv = storageStream->NewInputStream(0, getter_AddRefs(inStr)); |
|
86 if (NS_FAILED(rv)) return rv; |
|
87 |
|
88 nsCOMPtr<nsIChannel> channel; |
|
89 rv = NS_NewInputStreamChannel(getter_AddRefs(channel), aURI, inStr, |
|
90 NS_LITERAL_CSTRING("text/html"), |
|
91 NS_LITERAL_CSTRING("utf-8")); |
|
92 if (NS_FAILED(rv)) return rv; |
|
93 |
|
94 channel.forget(result); |
|
95 return rv; |
|
96 } |
|
97 |
|
98 NS_IMETHODIMP |
|
99 nsAboutCache::GetURIFlags(nsIURI *aURI, uint32_t *result) |
|
100 { |
|
101 *result = 0; |
|
102 return NS_OK; |
|
103 } |
|
104 |
|
105 NS_IMETHODIMP |
|
106 nsAboutCache::VisitDevice(const char *deviceID, |
|
107 nsICacheDeviceInfo *deviceInfo, |
|
108 bool *visitEntries) |
|
109 { |
|
110 uint32_t bytesWritten, value, entryCount; |
|
111 nsXPIDLCString str; |
|
112 |
|
113 *visitEntries = false; |
|
114 |
|
115 if (mDeviceID.IsEmpty() || mDeviceID.Equals(deviceID)) { |
|
116 |
|
117 // We need mStream for this |
|
118 if (!mStream) |
|
119 return NS_ERROR_FAILURE; |
|
120 |
|
121 // Write out the Cache Name |
|
122 deviceInfo->GetDescription(getter_Copies(str)); |
|
123 |
|
124 mBuffer.AssignLiteral("<h2>"); |
|
125 mBuffer.Append(str); |
|
126 mBuffer.AppendLiteral("</h2>\n" |
|
127 "<table id=\""); |
|
128 mBuffer.Append(deviceID); |
|
129 mBuffer.AppendLiteral("\">\n"); |
|
130 |
|
131 // Write out cache info |
|
132 // Number of entries |
|
133 mBuffer.AppendLiteral(" <tr>\n" |
|
134 " <th>Number of entries:</th>\n" |
|
135 " <td>"); |
|
136 entryCount = 0; |
|
137 deviceInfo->GetEntryCount(&entryCount); |
|
138 mBuffer.AppendInt(entryCount); |
|
139 mBuffer.AppendLiteral("</td>\n" |
|
140 " </tr>\n"); |
|
141 |
|
142 // Maximum storage size |
|
143 mBuffer.AppendLiteral(" <tr>\n" |
|
144 " <th>Maximum storage size:</th>\n" |
|
145 " <td>"); |
|
146 value = 0; |
|
147 deviceInfo->GetMaximumSize(&value); |
|
148 mBuffer.AppendInt(value/1024); |
|
149 mBuffer.AppendLiteral(" KiB</td>\n" |
|
150 " </tr>\n"); |
|
151 |
|
152 // Storage in use |
|
153 mBuffer.AppendLiteral(" <tr>\n" |
|
154 " <th>Storage in use:</th>\n" |
|
155 " <td>"); |
|
156 value = 0; |
|
157 deviceInfo->GetTotalSize(&value); |
|
158 mBuffer.AppendInt(value/1024); |
|
159 mBuffer.AppendLiteral(" KiB</td>\n" |
|
160 " </tr>\n"); |
|
161 |
|
162 deviceInfo->GetUsageReport(getter_Copies(str)); |
|
163 mBuffer.Append(str); |
|
164 |
|
165 if (mDeviceID.IsEmpty()) { // The about:cache case |
|
166 if (entryCount != 0) { // Add the "List Cache Entries" link |
|
167 mBuffer.AppendLiteral(" <tr>\n" |
|
168 " <th><a href=\"about:cache?device="); |
|
169 mBuffer.Append(deviceID); |
|
170 mBuffer.AppendLiteral("\">List Cache Entries</a></th>\n" |
|
171 " </tr>\n"); |
|
172 } |
|
173 mBuffer.AppendLiteral("</table>\n"); |
|
174 } else { // The about:cache?device=disk etc. case |
|
175 mBuffer.AppendLiteral("</table>\n"); |
|
176 if (entryCount != 0) { |
|
177 *visitEntries = true; |
|
178 mBuffer.AppendLiteral("<hr/>\n" |
|
179 "<table id=\"entries\">\n" |
|
180 " <colgroup>\n" |
|
181 " <col id=\"col-key\">\n" |
|
182 " <col id=\"col-dataSize\">\n" |
|
183 " <col id=\"col-fetchCount\">\n" |
|
184 " <col id=\"col-lastModified\">\n" |
|
185 " <col id=\"col-expires\">\n" |
|
186 " </colgroup>\n" |
|
187 " <thead>\n" |
|
188 " <tr>\n" |
|
189 " <th>Key</th>\n" |
|
190 " <th>Data size</th>\n" |
|
191 " <th>Fetch count</th>\n" |
|
192 " <th>Last modified</th>\n" |
|
193 " <th>Expires</th>\n" |
|
194 " </tr>\n" |
|
195 " </thead>\n"); |
|
196 } |
|
197 } |
|
198 |
|
199 mStream->Write(mBuffer.get(), mBuffer.Length(), &bytesWritten); |
|
200 } |
|
201 |
|
202 return NS_OK; |
|
203 } |
|
204 |
|
205 NS_IMETHODIMP |
|
206 nsAboutCache::VisitEntry(const char *deviceID, |
|
207 nsICacheEntryInfo *entryInfo, |
|
208 bool *visitNext) |
|
209 { |
|
210 // We need mStream for this |
|
211 if (!mStream) |
|
212 return NS_ERROR_FAILURE; |
|
213 |
|
214 nsresult rv; |
|
215 uint32_t bytesWritten; |
|
216 nsAutoCString key; |
|
217 nsXPIDLCString clientID; |
|
218 bool streamBased; |
|
219 |
|
220 rv = entryInfo->GetKey(key); |
|
221 if (NS_FAILED(rv)) return rv; |
|
222 |
|
223 rv = entryInfo->GetClientID(getter_Copies(clientID)); |
|
224 if (NS_FAILED(rv)) return rv; |
|
225 |
|
226 rv = entryInfo->IsStreamBased(&streamBased); |
|
227 if (NS_FAILED(rv)) return rv; |
|
228 |
|
229 // Generate a about:cache-entry URL for this entry... |
|
230 nsAutoCString url; |
|
231 url.AssignLiteral("about:cache-entry?client="); |
|
232 url += clientID; |
|
233 url.AppendLiteral("&sb="); |
|
234 url += streamBased ? '1' : '0'; |
|
235 url.AppendLiteral("&key="); |
|
236 char* escapedKey = nsEscapeHTML(key.get()); |
|
237 url += escapedKey; // key |
|
238 |
|
239 // Entry start... |
|
240 mBuffer.AssignLiteral(" <tr>\n"); |
|
241 |
|
242 // URI |
|
243 mBuffer.AppendLiteral(" <td><a href=\""); |
|
244 mBuffer.Append(url); |
|
245 mBuffer.AppendLiteral("\">"); |
|
246 mBuffer.Append(escapedKey); |
|
247 nsMemory::Free(escapedKey); |
|
248 mBuffer.AppendLiteral("</a></td>\n"); |
|
249 |
|
250 // Content length |
|
251 uint32_t length = 0; |
|
252 entryInfo->GetDataSize(&length); |
|
253 mBuffer.AppendLiteral(" <td>"); |
|
254 mBuffer.AppendInt(length); |
|
255 mBuffer.AppendLiteral(" bytes</td>\n"); |
|
256 |
|
257 // Number of accesses |
|
258 int32_t fetchCount = 0; |
|
259 entryInfo->GetFetchCount(&fetchCount); |
|
260 mBuffer.AppendLiteral(" <td>"); |
|
261 mBuffer.AppendInt(fetchCount); |
|
262 mBuffer.AppendLiteral("</td>\n"); |
|
263 |
|
264 // vars for reporting time |
|
265 char buf[255]; |
|
266 uint32_t t; |
|
267 |
|
268 // Last modified time |
|
269 mBuffer.AppendLiteral(" <td>"); |
|
270 entryInfo->GetLastModified(&t); |
|
271 if (t) { |
|
272 PrintTimeString(buf, sizeof(buf), t); |
|
273 mBuffer.Append(buf); |
|
274 } else |
|
275 mBuffer.AppendLiteral("No last modified time"); |
|
276 mBuffer.AppendLiteral("</td>\n"); |
|
277 |
|
278 // Expires time |
|
279 mBuffer.AppendLiteral(" <td>"); |
|
280 entryInfo->GetExpirationTime(&t); |
|
281 if (t < 0xFFFFFFFF) { |
|
282 PrintTimeString(buf, sizeof(buf), t); |
|
283 mBuffer.Append(buf); |
|
284 } else { |
|
285 mBuffer.AppendLiteral("No expiration time"); |
|
286 } |
|
287 mBuffer.AppendLiteral("</td>\n"); |
|
288 |
|
289 // Entry is done... |
|
290 mBuffer.AppendLiteral(" </tr>\n"); |
|
291 |
|
292 mStream->Write(mBuffer.get(), mBuffer.Length(), &bytesWritten); |
|
293 |
|
294 *visitNext = true; |
|
295 return NS_OK; |
|
296 } |
|
297 |
|
298 |
|
299 nsresult |
|
300 nsAboutCache::ParseURI(nsIURI * uri, nsCString &deviceID) |
|
301 { |
|
302 // |
|
303 // about:cache[?device=string] |
|
304 // |
|
305 nsresult rv; |
|
306 |
|
307 deviceID.Truncate(); |
|
308 |
|
309 nsAutoCString path; |
|
310 rv = uri->GetPath(path); |
|
311 if (NS_FAILED(rv)) return rv; |
|
312 |
|
313 nsACString::const_iterator start, valueStart, end; |
|
314 path.BeginReading(start); |
|
315 path.EndReading(end); |
|
316 |
|
317 valueStart = end; |
|
318 if (!FindInReadable(NS_LITERAL_CSTRING("?device="), start, valueStart)) |
|
319 return NS_OK; |
|
320 |
|
321 deviceID.Assign(Substring(valueStart, end)); |
|
322 return NS_OK; |
|
323 } |
|
324 |
|
325 |
|
326 nsresult |
|
327 nsAboutCache::Create(nsISupports *aOuter, REFNSIID aIID, void **aResult) |
|
328 { |
|
329 nsAboutCache* about = new nsAboutCache(); |
|
330 if (about == nullptr) |
|
331 return NS_ERROR_OUT_OF_MEMORY; |
|
332 NS_ADDREF(about); |
|
333 nsresult rv = about->QueryInterface(aIID, aResult); |
|
334 NS_RELEASE(about); |
|
335 return rv; |
|
336 } |
|
337 |
|
338 |
|
339 |
|
340 //////////////////////////////////////////////////////////////////////////////// |