Bug 1804798 - Explicitly set auto page name (and corresponding debug flag) when inser...
[gecko.git] / netwerk / base / nsDirectoryIndexStream.cpp
blob22a3b79bee4bc8c4595e91a12864827740ff18d2
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:set sw=2 sts=2 et cin: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 /*
9 The converts a filesystem directory into an "HTTP index" stream per
10 Lou Montulli's original spec:
12 http://www.mozilla.org/projects/netlib/dirindexformat.html
16 #include "nsEscape.h"
17 #include "nsDirectoryIndexStream.h"
18 #include "mozilla/Logging.h"
19 #include "prtime.h"
20 #include "nsIFile.h"
21 #include "nsURLHelper.h"
22 #include "nsNativeCharsetUtils.h"
24 // NOTE: This runs on the _file transport_ thread.
25 // The problem is that now that we're actually doing something with the data,
26 // we want to do stuff like i18n sorting. However, none of the collation stuff
27 // is threadsafe.
28 // So THIS CODE IS ASCII ONLY!!!!!!!! This is no worse than the current
29 // behaviour, though. See bug 99382.
31 using namespace mozilla;
32 static LazyLogModule gLog("nsDirectoryIndexStream");
34 nsDirectoryIndexStream::nsDirectoryIndexStream() {
35 MOZ_LOG(gLog, LogLevel::Debug, ("nsDirectoryIndexStream[%p]: created", this));
38 static int compare(nsIFile* aElement1, nsIFile* aElement2, void* aData) {
39 if (!NS_IsNativeUTF8()) {
40 // don't check for errors, because we can't report them anyway
41 nsAutoString name1, name2;
42 aElement1->GetLeafName(name1);
43 aElement2->GetLeafName(name2);
45 // Note - we should do the collation to do sorting. Why don't we?
46 // Because that is _slow_. Using TestProtocols to list file:///dev/
47 // goes from 3 seconds to 22. (This may be why nsXULSortService is
48 // so slow as well).
49 // Does this have bad effects? Probably, but since nsXULTree appears
50 // to use the raw RDF literal value as the sort key (which ammounts to an
51 // strcmp), it won't be any worse, I think.
52 // This could be made faster, by creating the keys once,
53 // but CompareString could still be smarter - see bug 99383 - bbaetz
54 // NB - 99393 has been WONTFIXed. So if the I18N code is ever made
55 // threadsafe so that this matters, we'd have to pass through a
56 // struct { nsIFile*, uint8_t* } with the pre-calculated key.
57 return Compare(name1, name2);
60 nsAutoCString name1, name2;
61 aElement1->GetNativeLeafName(name1);
62 aElement2->GetNativeLeafName(name2);
64 return Compare(name1, name2);
67 nsresult nsDirectoryIndexStream::Init(nsIFile* aDir) {
68 nsresult rv;
69 bool isDir;
70 rv = aDir->IsDirectory(&isDir);
71 if (NS_FAILED(rv)) return rv;
72 MOZ_ASSERT(isDir, "not a directory");
73 if (!isDir) return NS_ERROR_ILLEGAL_VALUE;
75 if (MOZ_LOG_TEST(gLog, LogLevel::Debug)) {
76 MOZ_LOG(gLog, LogLevel::Debug,
77 ("nsDirectoryIndexStream[%p]: initialized on %s", this,
78 aDir->HumanReadablePath().get()));
81 // Sigh. We have to allocate on the heap because there are no
82 // assignment operators defined.
83 nsCOMPtr<nsIDirectoryEnumerator> iter;
84 rv = aDir->GetDirectoryEntries(getter_AddRefs(iter));
85 if (NS_FAILED(rv)) return rv;
87 // Now lets sort, because clients expect it that way
88 // XXX - should we do so here, or when the first item is requested?
89 // XXX - use insertion sort instead?
91 nsCOMPtr<nsIFile> file;
92 while (NS_SUCCEEDED(iter->GetNextFile(getter_AddRefs(file))) && file) {
93 mArray.AppendObject(file); // addrefs
96 mArray.Sort(compare, nullptr);
98 mBuf.AppendLiteral("300: ");
99 nsAutoCString url;
100 rv = net_GetURLSpecFromFile(aDir, url);
101 if (NS_FAILED(rv)) return rv;
102 mBuf.Append(url);
103 mBuf.Append('\n');
105 mBuf.AppendLiteral("200: filename content-length last-modified file-type\n");
107 return NS_OK;
110 nsDirectoryIndexStream::~nsDirectoryIndexStream() {
111 MOZ_LOG(gLog, LogLevel::Debug,
112 ("nsDirectoryIndexStream[%p]: destroyed", this));
115 nsresult nsDirectoryIndexStream::Create(nsIFile* aDir,
116 nsIInputStream** aResult) {
117 RefPtr<nsDirectoryIndexStream> result = new nsDirectoryIndexStream();
118 if (!result) return NS_ERROR_OUT_OF_MEMORY;
120 nsresult rv = result->Init(aDir);
121 if (NS_FAILED(rv)) {
122 return rv;
125 result.forget(aResult);
126 return NS_OK;
129 NS_IMPL_ISUPPORTS(nsDirectoryIndexStream, nsIInputStream)
131 // The below routines are proxied to the UI thread!
132 NS_IMETHODIMP
133 nsDirectoryIndexStream::Close() {
134 mStatus = NS_BASE_STREAM_CLOSED;
135 return NS_OK;
138 NS_IMETHODIMP
139 nsDirectoryIndexStream::Available(uint64_t* aLength) {
140 if (NS_FAILED(mStatus)) return mStatus;
142 // If there's data in our buffer, use that
143 if (mOffset < (int32_t)mBuf.Length()) {
144 *aLength = mBuf.Length() - mOffset;
145 return NS_OK;
148 // Returning one byte is not ideal, but good enough
149 *aLength = (mPos < mArray.Count()) ? 1 : 0;
150 return NS_OK;
153 NS_IMETHODIMP
154 nsDirectoryIndexStream::Read(char* aBuf, uint32_t aCount,
155 uint32_t* aReadCount) {
156 if (mStatus == NS_BASE_STREAM_CLOSED) {
157 *aReadCount = 0;
158 return NS_OK;
160 if (NS_FAILED(mStatus)) return mStatus;
162 uint32_t nread = 0;
164 // If anything is enqueued (or left-over) in mBuf, then feed it to
165 // the reader first.
166 while (mOffset < (int32_t)mBuf.Length() && aCount != 0) {
167 *(aBuf++) = char(mBuf.CharAt(mOffset++));
168 --aCount;
169 ++nread;
172 // Room left?
173 if (aCount > 0) {
174 mOffset = 0;
175 mBuf.Truncate();
177 // Okay, now we'll suck stuff off of our iterator into the mBuf...
178 while (uint32_t(mBuf.Length()) < aCount) {
179 bool more = mPos < mArray.Count();
180 if (!more) break;
182 // don't addref, for speed - an addref happened when it
183 // was placed in the array, so it's not going to go stale
184 nsIFile* current = mArray.ObjectAt(mPos);
185 ++mPos;
187 if (MOZ_LOG_TEST(gLog, LogLevel::Debug)) {
188 MOZ_LOG(gLog, LogLevel::Debug,
189 ("nsDirectoryIndexStream[%p]: iterated %s", this,
190 current->HumanReadablePath().get()));
193 // rjc: don't return hidden files/directories!
194 // bbaetz: why not?
195 nsresult rv;
196 #ifndef XP_UNIX
197 bool hidden = false;
198 current->IsHidden(&hidden);
199 if (hidden) {
200 MOZ_LOG(gLog, LogLevel::Debug,
201 ("nsDirectoryIndexStream[%p]: skipping hidden file/directory",
202 this));
203 continue;
205 #endif
207 int64_t fileSize = 0;
208 current->GetFileSize(&fileSize);
210 PRTime fileInfoModifyTime = 0;
211 current->GetLastModifiedTime(&fileInfoModifyTime);
212 fileInfoModifyTime *= PR_USEC_PER_MSEC;
214 mBuf.AppendLiteral("201: ");
216 // The "filename" field
217 if (!NS_IsNativeUTF8()) {
218 nsAutoString leafname;
219 rv = current->GetLeafName(leafname);
220 if (NS_FAILED(rv)) return rv;
222 nsAutoCString escaped;
223 if (!leafname.IsEmpty() &&
224 NS_Escape(NS_ConvertUTF16toUTF8(leafname), escaped, url_Path)) {
225 mBuf.Append(escaped);
226 mBuf.Append(' ');
228 } else {
229 nsAutoCString leafname;
230 rv = current->GetNativeLeafName(leafname);
231 if (NS_FAILED(rv)) return rv;
233 nsAutoCString escaped;
234 if (!leafname.IsEmpty() && NS_Escape(leafname, escaped, url_Path)) {
235 mBuf.Append(escaped);
236 mBuf.Append(' ');
240 // The "content-length" field
241 mBuf.AppendInt(fileSize, 10);
242 mBuf.Append(' ');
244 // The "last-modified" field
245 PRExplodedTime tm;
246 PR_ExplodeTime(fileInfoModifyTime, PR_GMTParameters, &tm);
248 char buf[64];
249 PR_FormatTimeUSEnglish(
250 buf, sizeof(buf), "%a,%%20%d%%20%b%%20%Y%%20%H:%M:%S%%20GMT ", &tm);
251 mBuf.Append(buf);
254 // The "file-type" field
255 bool isFile = true;
256 current->IsFile(&isFile);
257 if (isFile) {
258 mBuf.AppendLiteral("FILE ");
259 } else {
260 bool isDir;
261 rv = current->IsDirectory(&isDir);
262 if (NS_FAILED(rv)) return rv;
263 if (isDir) {
264 mBuf.AppendLiteral("DIRECTORY ");
265 } else {
266 bool isLink;
267 rv = current->IsSymlink(&isLink);
268 if (NS_FAILED(rv)) return rv;
269 if (isLink) {
270 mBuf.AppendLiteral("SYMBOLIC-LINK ");
275 mBuf.Append('\n');
278 // ...and once we've either run out of directory entries, or
279 // filled up the buffer, then we'll push it to the reader.
280 while (mOffset < (int32_t)mBuf.Length() && aCount != 0) {
281 *(aBuf++) = char(mBuf.CharAt(mOffset++));
282 --aCount;
283 ++nread;
287 *aReadCount = nread;
288 return NS_OK;
291 NS_IMETHODIMP
292 nsDirectoryIndexStream::ReadSegments(nsWriteSegmentFun writer, void* closure,
293 uint32_t count, uint32_t* _retval) {
294 return NS_ERROR_NOT_IMPLEMENTED;
297 NS_IMETHODIMP
298 nsDirectoryIndexStream::IsNonBlocking(bool* aNonBlocking) {
299 *aNonBlocking = false;
300 return NS_OK;