Merge mozilla-central to autoland. CLOSED TREE
[gecko.git] / modules / libjar / nsJARChannel.cpp
blob8557327f206a5c02996c8f97f0e11049f58474fe
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set sw=2 ts=8 et tw=80 : */
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 #include "nsJAR.h"
8 #include "nsJARChannel.h"
9 #include "nsJARProtocolHandler.h"
10 #include "nsMimeTypes.h"
11 #include "nsNetUtil.h"
12 #include "nsEscape.h"
13 #include "nsContentUtils.h"
14 #include "nsProxyRelease.h"
15 #include "nsContentSecurityManager.h"
16 #include "nsComponentManagerUtils.h"
18 #include "nsIFileURL.h"
19 #include "nsIURIMutator.h"
21 #include "mozilla/BasePrincipal.h"
22 #include "mozilla/ErrorNames.h"
23 #include "mozilla/IntegerPrintfMacros.h"
24 #include "mozilla/Preferences.h"
25 #include "mozilla/ScopeExit.h"
26 #include "mozilla/StaticPrefs_network.h"
27 #include "mozilla/Telemetry.h"
28 #include "mozilla/TelemetryComms.h"
29 #include "private/pprio.h"
30 #include "nsInputStreamPump.h"
31 #include "nsThreadUtils.h"
32 #include "nsJARProtocolHandler.h"
34 using namespace mozilla;
35 using namespace mozilla::net;
37 static NS_DEFINE_CID(kZipReaderCID, NS_ZIPREADER_CID);
39 // the entry for a directory will either be empty (in the case of the
40 // top-level directory) or will end with a slash
41 #define ENTRY_IS_DIRECTORY(_entry) \
42 ((_entry).IsEmpty() || '/' == (_entry).Last())
44 //-----------------------------------------------------------------------------
47 // set MOZ_LOG=nsJarProtocol:5
49 static LazyLogModule gJarProtocolLog("nsJarProtocol");
51 // Ignore any LOG macro that we inherit from arbitrary headers. (We define our
52 // own LOG macro below.)
53 #ifdef LOG
54 # undef LOG
55 #endif
56 #ifdef LOG_ENABLED
57 # undef LOG_ENABLED
58 #endif
60 #define LOG(args) MOZ_LOG(gJarProtocolLog, mozilla::LogLevel::Debug, args)
61 #define LOG_ENABLED() MOZ_LOG_TEST(gJarProtocolLog, mozilla::LogLevel::Debug)
63 //-----------------------------------------------------------------------------
64 // nsJARInputThunk
66 // this class allows us to do some extra work on the stream transport thread.
67 //-----------------------------------------------------------------------------
69 class nsJARInputThunk : public nsIInputStream {
70 public:
71 NS_DECL_THREADSAFE_ISUPPORTS
72 NS_DECL_NSIINPUTSTREAM
74 nsJARInputThunk(nsIZipReader* zipReader, const nsACString& jarEntry,
75 bool usingJarCache)
76 : mUsingJarCache(usingJarCache),
77 mJarReader(zipReader),
78 mJarEntry(jarEntry),
79 mContentLength(-1) {
80 MOZ_DIAGNOSTIC_ASSERT(zipReader, "zipReader must not be null");
83 int64_t GetContentLength() { return mContentLength; }
85 nsresult Init();
87 private:
88 virtual ~nsJARInputThunk() { Close(); }
90 bool mUsingJarCache;
91 nsCOMPtr<nsIZipReader> mJarReader;
92 nsCOMPtr<nsIInputStream> mJarStream;
93 nsCString mJarEntry;
94 int64_t mContentLength;
97 NS_IMPL_ISUPPORTS(nsJARInputThunk, nsIInputStream)
99 nsresult nsJARInputThunk::Init() {
100 if (!mJarReader) {
101 return NS_ERROR_INVALID_ARG;
103 nsresult rv =
104 mJarReader->GetInputStream(mJarEntry, getter_AddRefs(mJarStream));
105 if (NS_FAILED(rv)) {
106 return rv;
109 // ask the JarStream for the content length
110 uint64_t avail;
111 rv = mJarStream->Available((uint64_t*)&avail);
112 if (NS_FAILED(rv)) return rv;
114 mContentLength = avail < INT64_MAX ? (int64_t)avail : -1;
116 return NS_OK;
119 NS_IMETHODIMP
120 nsJARInputThunk::Close() {
121 nsresult rv = NS_OK;
123 if (mJarStream) rv = mJarStream->Close();
125 if (!mUsingJarCache && mJarReader) mJarReader->Close();
127 mJarReader = nullptr;
129 return rv;
132 NS_IMETHODIMP
133 nsJARInputThunk::Available(uint64_t* avail) {
134 return mJarStream->Available(avail);
137 NS_IMETHODIMP
138 nsJARInputThunk::StreamStatus() { return mJarStream->StreamStatus(); }
140 NS_IMETHODIMP
141 nsJARInputThunk::Read(char* buf, uint32_t count, uint32_t* countRead) {
142 return mJarStream->Read(buf, count, countRead);
145 NS_IMETHODIMP
146 nsJARInputThunk::ReadSegments(nsWriteSegmentFun writer, void* closure,
147 uint32_t count, uint32_t* countRead) {
148 // stream transport does only calls Read()
149 return NS_ERROR_NOT_IMPLEMENTED;
152 NS_IMETHODIMP
153 nsJARInputThunk::IsNonBlocking(bool* nonBlocking) {
154 *nonBlocking = false;
155 return NS_OK;
158 //-----------------------------------------------------------------------------
159 // nsJARChannel
160 //-----------------------------------------------------------------------------
162 nsJARChannel::nsJARChannel()
163 : mOpened(false),
164 mCanceled(false),
165 mContentLength(-1),
166 mLoadFlags(LOAD_NORMAL),
167 mStatus(NS_OK),
168 mIsPending(false),
169 mEnableOMT(true),
170 mPendingEvent() {
171 LOG(("nsJARChannel::nsJARChannel [this=%p]\n", this));
172 // hold an owning reference to the jar handler
173 mJarHandler = gJarHandler;
176 nsJARChannel::~nsJARChannel() {
177 LOG(("nsJARChannel::~nsJARChannel [this=%p]\n", this));
178 if (NS_IsMainThread()) {
179 return;
182 // Proxy release the following members to main thread.
183 NS_ReleaseOnMainThread("nsJARChannel::mLoadInfo", mLoadInfo.forget());
184 NS_ReleaseOnMainThread("nsJARChannel::mCallbacks", mCallbacks.forget());
185 NS_ReleaseOnMainThread("nsJARChannel::mProgressSink", mProgressSink.forget());
186 NS_ReleaseOnMainThread("nsJARChannel::mLoadGroup", mLoadGroup.forget());
187 NS_ReleaseOnMainThread("nsJARChannel::mListener", mListener.forget());
190 NS_IMPL_ISUPPORTS_INHERITED(nsJARChannel, nsHashPropertyBag, nsIRequest,
191 nsIChannel, nsIStreamListener, nsIRequestObserver,
192 nsIThreadRetargetableRequest,
193 nsIThreadRetargetableStreamListener, nsIJARChannel)
195 nsresult nsJARChannel::Init(nsIURI* uri) {
196 LOG(("nsJARChannel::Init [this=%p]\n", this));
197 nsresult rv;
199 mWorker = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID, &rv);
200 if (NS_FAILED(rv)) {
201 return rv;
204 mJarURI = do_QueryInterface(uri, &rv);
205 if (NS_FAILED(rv)) return rv;
207 mOriginalURI = mJarURI;
209 // Prevent loading jar:javascript URIs (see bug 290982).
210 nsCOMPtr<nsIURI> innerURI;
211 rv = mJarURI->GetJARFile(getter_AddRefs(innerURI));
212 if (NS_FAILED(rv)) {
213 return rv;
216 if (innerURI->SchemeIs("javascript")) {
217 NS_WARNING("blocking jar:javascript:");
218 return NS_ERROR_INVALID_ARG;
221 mJarURI->GetSpec(mSpec);
222 return rv;
225 nsresult nsJARChannel::CreateJarInput(nsIZipReaderCache* jarCache,
226 nsJARInputThunk** resultInput) {
227 LOG(("nsJARChannel::CreateJarInput [this=%p]\n", this));
228 MOZ_ASSERT(resultInput);
229 MOZ_ASSERT(mJarFile);
231 // important to pass a clone of the file since the nsIFile impl is not
232 // necessarily MT-safe
233 nsCOMPtr<nsIFile> clonedFile;
234 nsresult rv = NS_OK;
235 if (mJarFile) {
236 rv = mJarFile->Clone(getter_AddRefs(clonedFile));
237 if (NS_FAILED(rv)) return rv;
240 nsCOMPtr<nsIZipReader> reader;
241 if (mPreCachedJarReader) {
242 reader = mPreCachedJarReader;
243 } else if (jarCache) {
244 if (mInnerJarEntry.IsEmpty())
245 rv = jarCache->GetZip(clonedFile, getter_AddRefs(reader));
246 else
247 rv = jarCache->GetInnerZip(clonedFile, mInnerJarEntry,
248 getter_AddRefs(reader));
249 } else {
250 // create an uncached jar reader
251 nsCOMPtr<nsIZipReader> outerReader = do_CreateInstance(kZipReaderCID, &rv);
252 if (NS_FAILED(rv)) return rv;
254 rv = outerReader->Open(clonedFile);
255 if (NS_FAILED(rv)) return rv;
257 if (mInnerJarEntry.IsEmpty())
258 reader = outerReader;
259 else {
260 reader = do_CreateInstance(kZipReaderCID, &rv);
261 if (NS_FAILED(rv)) return rv;
263 rv = reader->OpenInner(outerReader, mInnerJarEntry);
266 if (NS_FAILED(rv)) return rv;
268 RefPtr<nsJARInputThunk> input =
269 new nsJARInputThunk(reader, mJarEntry, jarCache != nullptr);
270 rv = input->Init();
271 if (NS_FAILED(rv)) {
272 return rv;
275 // Make GetContentLength meaningful
276 mContentLength = input->GetContentLength();
278 input.forget(resultInput);
279 return NS_OK;
282 nsresult nsJARChannel::LookupFile() {
283 LOG(("nsJARChannel::LookupFile [this=%p %s]\n", this, mSpec.get()));
285 if (mJarFile) return NS_OK;
287 nsresult rv;
289 rv = mJarURI->GetJARFile(getter_AddRefs(mJarBaseURI));
290 if (NS_FAILED(rv)) return rv;
292 rv = mJarURI->GetJAREntry(mJarEntry);
293 if (NS_FAILED(rv)) return rv;
295 // The name of the JAR entry must not contain URL-escaped characters:
296 // we're moving from URL domain to a filename domain here. nsStandardURL
297 // does basic escaping by default, which breaks reading zipped files which
298 // have e.g. spaces in their filenames.
299 NS_UnescapeURL(mJarEntry);
301 if (mJarFileOverride) {
302 mJarFile = mJarFileOverride;
303 LOG(("nsJARChannel::LookupFile [this=%p] Overriding mJarFile\n", this));
304 return NS_OK;
307 // try to get a nsIFile directly from the url, which will often succeed.
309 nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(mJarBaseURI);
310 if (fileURL) fileURL->GetFile(getter_AddRefs(mJarFile));
313 // try to handle a nested jar
314 if (!mJarFile) {
315 nsCOMPtr<nsIJARURI> jarURI = do_QueryInterface(mJarBaseURI);
316 if (jarURI) {
317 nsCOMPtr<nsIFileURL> fileURL;
318 nsCOMPtr<nsIURI> innerJarURI;
319 rv = jarURI->GetJARFile(getter_AddRefs(innerJarURI));
320 if (NS_SUCCEEDED(rv)) fileURL = do_QueryInterface(innerJarURI);
321 if (fileURL) {
322 fileURL->GetFile(getter_AddRefs(mJarFile));
323 jarURI->GetJAREntry(mInnerJarEntry);
328 return rv;
331 nsresult CreateLocalJarInput(nsIZipReaderCache* aJarCache, nsIFile* aFile,
332 const nsACString& aInnerJarEntry,
333 const nsACString& aJarEntry,
334 nsJARInputThunk** aResultInput) {
335 LOG(("nsJARChannel::CreateLocalJarInput [aJarCache=%p, %s, %s]\n", aJarCache,
336 PromiseFlatCString(aInnerJarEntry).get(),
337 PromiseFlatCString(aJarEntry).get()));
339 MOZ_ASSERT(!NS_IsMainThread());
340 MOZ_ASSERT(aJarCache);
341 MOZ_ASSERT(aResultInput);
343 nsresult rv;
345 nsCOMPtr<nsIZipReader> reader;
346 if (aInnerJarEntry.IsEmpty()) {
347 rv = aJarCache->GetZip(aFile, getter_AddRefs(reader));
348 } else {
349 rv = aJarCache->GetInnerZip(aFile, aInnerJarEntry, getter_AddRefs(reader));
351 if (NS_WARN_IF(NS_FAILED(rv))) {
352 return rv;
355 RefPtr<nsJARInputThunk> input =
356 new nsJARInputThunk(reader, aJarEntry, aJarCache != nullptr);
357 rv = input->Init();
358 if (NS_FAILED(rv)) {
359 return rv;
362 input.forget(aResultInput);
363 return NS_OK;
366 nsresult nsJARChannel::OpenLocalFile() {
367 LOG(("nsJARChannel::OpenLocalFile [this=%p]\n", this));
369 MOZ_ASSERT(NS_IsMainThread());
371 MOZ_ASSERT(mWorker);
372 MOZ_ASSERT(mIsPending);
373 MOZ_ASSERT(mJarFile);
375 nsresult rv;
377 // Set mLoadGroup and mOpened before AsyncOpen return, and set back if
378 // if failed when callback.
379 if (mLoadGroup) {
380 mLoadGroup->AddRequest(this, nullptr);
382 SetOpened();
384 if (mPreCachedJarReader || !mEnableOMT) {
385 RefPtr<nsJARInputThunk> input;
386 rv = CreateJarInput(gJarHandler->JarCache(), getter_AddRefs(input));
387 if (NS_WARN_IF(NS_FAILED(rv))) {
388 return OnOpenLocalFileComplete(rv, true);
390 return ContinueOpenLocalFile(input, true);
393 nsCOMPtr<nsIZipReaderCache> jarCache = gJarHandler->JarCache();
394 if (NS_WARN_IF(!jarCache)) {
395 return NS_ERROR_UNEXPECTED;
398 nsCOMPtr<nsIFile> clonedFile;
399 rv = mJarFile->Clone(getter_AddRefs(clonedFile));
400 if (NS_WARN_IF(NS_FAILED(rv))) {
401 return rv;
404 nsAutoCString jarEntry(mJarEntry);
405 nsAutoCString innerJarEntry(mInnerJarEntry);
407 RefPtr<nsJARChannel> self = this;
408 return mWorker->Dispatch(NS_NewRunnableFunction(
409 "nsJARChannel::OpenLocalFile",
410 [self, jarCache, clonedFile, jarEntry, innerJarEntry]() mutable {
411 RefPtr<nsJARInputThunk> input;
412 nsresult rv = CreateLocalJarInput(jarCache, clonedFile, innerJarEntry,
413 jarEntry, getter_AddRefs(input));
415 nsCOMPtr<nsIRunnable> target;
416 if (NS_SUCCEEDED(rv)) {
417 target = NewRunnableMethod<RefPtr<nsJARInputThunk>, bool>(
418 "nsJARChannel::ContinueOpenLocalFile", self,
419 &nsJARChannel::ContinueOpenLocalFile, input, false);
420 } else {
421 target = NewRunnableMethod<nsresult, bool>(
422 "nsJARChannel::OnOpenLocalFileComplete", self,
423 &nsJARChannel::OnOpenLocalFileComplete, rv, false);
426 // nsJARChannel must be release on main thread, and sometimes
427 // this still hold nsJARChannel after dispatched.
428 self = nullptr;
430 NS_DispatchToMainThread(target.forget());
431 }));
434 nsresult nsJARChannel::ContinueOpenLocalFile(nsJARInputThunk* aInput,
435 bool aIsSyncCall) {
436 LOG(("nsJARChannel::ContinueOpenLocalFile [this=%p %p]\n", this, aInput));
438 MOZ_ASSERT(NS_IsMainThread());
439 MOZ_ASSERT(mIsPending);
441 // Make GetContentLength meaningful
442 mContentLength = aInput->GetContentLength();
444 nsresult rv;
445 RefPtr<nsJARInputThunk> input = aInput;
446 // Create input stream pump and call AsyncRead as a block.
447 rv = NS_NewInputStreamPump(getter_AddRefs(mPump), input.forget());
448 if (NS_SUCCEEDED(rv)) {
449 rv = mPump->AsyncRead(this);
452 if (NS_SUCCEEDED(rv)) {
453 rv = CheckPendingEvents();
456 return OnOpenLocalFileComplete(rv, aIsSyncCall);
459 nsresult nsJARChannel::OnOpenLocalFileComplete(nsresult aResult,
460 bool aIsSyncCall) {
461 LOG(("nsJARChannel::OnOpenLocalFileComplete [this=%p %08x]\n", this,
462 static_cast<uint32_t>(aResult)));
464 MOZ_ASSERT(NS_IsMainThread());
465 MOZ_ASSERT(mIsPending);
467 if (NS_FAILED(aResult)) {
468 if (aResult == NS_ERROR_FILE_NOT_FOUND) {
469 CheckForBrokenChromeURL(mLoadInfo, mOriginalURI);
471 if (!aIsSyncCall) {
472 NotifyError(aResult);
475 if (mLoadGroup) {
476 mLoadGroup->RemoveRequest(this, nullptr, aResult);
479 mOpened = false;
480 mIsPending = false;
481 mListener = nullptr;
482 mCallbacks = nullptr;
483 mProgressSink = nullptr;
485 return aResult;
488 return NS_OK;
491 nsresult nsJARChannel::CheckPendingEvents() {
492 MOZ_ASSERT(NS_IsMainThread());
493 MOZ_ASSERT(mIsPending);
494 MOZ_ASSERT(mPump);
496 nsresult rv;
498 uint32_t suspendCount = mPendingEvent.suspendCount;
499 while (suspendCount--) {
500 if (NS_WARN_IF(NS_FAILED(rv = mPump->Suspend()))) {
501 return rv;
505 if (mPendingEvent.isCanceled) {
506 if (NS_WARN_IF(NS_FAILED(rv = mPump->Cancel(mStatus)))) {
507 return rv;
509 mPendingEvent.isCanceled = false;
512 return NS_OK;
515 void nsJARChannel::NotifyError(nsresult aError) {
516 MOZ_ASSERT(NS_FAILED(aError));
518 mStatus = aError;
520 OnStartRequest(nullptr);
521 OnStopRequest(nullptr, aError);
524 void nsJARChannel::FireOnProgress(uint64_t aProgress) {
525 MOZ_ASSERT(NS_IsMainThread());
526 MOZ_ASSERT(mProgressSink);
528 mProgressSink->OnProgress(this, aProgress, mContentLength);
531 //-----------------------------------------------------------------------------
532 // nsIRequest
533 //-----------------------------------------------------------------------------
535 NS_IMETHODIMP
536 nsJARChannel::GetName(nsACString& result) { return mJarURI->GetSpec(result); }
538 NS_IMETHODIMP
539 nsJARChannel::IsPending(bool* result) {
540 *result = mIsPending;
541 return NS_OK;
544 NS_IMETHODIMP
545 nsJARChannel::GetStatus(nsresult* status) {
546 if (mPump && NS_SUCCEEDED(mStatus))
547 mPump->GetStatus(status);
548 else
549 *status = mStatus;
550 return NS_OK;
553 NS_IMETHODIMP nsJARChannel::SetCanceledReason(const nsACString& aReason) {
554 return SetCanceledReasonImpl(aReason);
557 NS_IMETHODIMP nsJARChannel::GetCanceledReason(nsACString& aReason) {
558 return GetCanceledReasonImpl(aReason);
561 NS_IMETHODIMP nsJARChannel::CancelWithReason(nsresult aStatus,
562 const nsACString& aReason) {
563 return CancelWithReasonImpl(aStatus, aReason);
566 NS_IMETHODIMP
567 nsJARChannel::Cancel(nsresult status) {
568 mCanceled = true;
569 mStatus = status;
570 if (mPump) {
571 return mPump->Cancel(status);
574 if (mIsPending) {
575 mPendingEvent.isCanceled = true;
578 return NS_OK;
581 NS_IMETHODIMP
582 nsJARChannel::GetCanceled(bool* aCanceled) {
583 *aCanceled = mCanceled;
584 return NS_OK;
587 NS_IMETHODIMP
588 nsJARChannel::Suspend() {
589 ++mPendingEvent.suspendCount;
591 if (mPump) {
592 return mPump->Suspend();
595 return NS_OK;
598 NS_IMETHODIMP
599 nsJARChannel::Resume() {
600 if (NS_WARN_IF(mPendingEvent.suspendCount == 0)) {
601 return NS_ERROR_UNEXPECTED;
603 --mPendingEvent.suspendCount;
605 if (mPump) {
606 return mPump->Resume();
609 return NS_OK;
612 NS_IMETHODIMP
613 nsJARChannel::GetLoadFlags(nsLoadFlags* aLoadFlags) {
614 *aLoadFlags = mLoadFlags;
615 return NS_OK;
618 NS_IMETHODIMP
619 nsJARChannel::SetLoadFlags(nsLoadFlags aLoadFlags) {
620 mLoadFlags = aLoadFlags;
621 return NS_OK;
624 NS_IMETHODIMP
625 nsJARChannel::GetTRRMode(nsIRequest::TRRMode* aTRRMode) {
626 return GetTRRModeImpl(aTRRMode);
629 NS_IMETHODIMP
630 nsJARChannel::SetTRRMode(nsIRequest::TRRMode aTRRMode) {
631 return SetTRRModeImpl(aTRRMode);
634 NS_IMETHODIMP
635 nsJARChannel::GetIsDocument(bool* aIsDocument) {
636 return NS_GetIsDocumentChannel(this, aIsDocument);
639 NS_IMETHODIMP
640 nsJARChannel::GetLoadGroup(nsILoadGroup** aLoadGroup) {
641 NS_IF_ADDREF(*aLoadGroup = mLoadGroup);
642 return NS_OK;
645 NS_IMETHODIMP
646 nsJARChannel::SetLoadGroup(nsILoadGroup* aLoadGroup) {
647 mLoadGroup = aLoadGroup;
648 return NS_OK;
651 //-----------------------------------------------------------------------------
652 // nsIChannel
653 //-----------------------------------------------------------------------------
655 NS_IMETHODIMP
656 nsJARChannel::GetOriginalURI(nsIURI** aURI) {
657 *aURI = mOriginalURI;
658 NS_ADDREF(*aURI);
659 return NS_OK;
662 NS_IMETHODIMP
663 nsJARChannel::SetOriginalURI(nsIURI* aURI) {
664 NS_ENSURE_ARG_POINTER(aURI);
665 mOriginalURI = aURI;
666 return NS_OK;
669 NS_IMETHODIMP
670 nsJARChannel::GetURI(nsIURI** aURI) {
671 NS_IF_ADDREF(*aURI = mJarURI);
673 return NS_OK;
676 NS_IMETHODIMP
677 nsJARChannel::GetOwner(nsISupports** aOwner) {
678 // JAR signatures are not processed to avoid main-thread network I/O (bug
679 // 726125)
680 *aOwner = mOwner;
681 NS_IF_ADDREF(*aOwner);
682 return NS_OK;
685 NS_IMETHODIMP
686 nsJARChannel::SetOwner(nsISupports* aOwner) {
687 mOwner = aOwner;
688 return NS_OK;
691 NS_IMETHODIMP
692 nsJARChannel::GetLoadInfo(nsILoadInfo** aLoadInfo) {
693 NS_IF_ADDREF(*aLoadInfo = mLoadInfo);
694 return NS_OK;
697 NS_IMETHODIMP
698 nsJARChannel::SetLoadInfo(nsILoadInfo* aLoadInfo) {
699 MOZ_RELEASE_ASSERT(aLoadInfo, "loadinfo can't be null");
700 mLoadInfo = aLoadInfo;
701 return NS_OK;
704 NS_IMETHODIMP
705 nsJARChannel::GetNotificationCallbacks(nsIInterfaceRequestor** aCallbacks) {
706 NS_IF_ADDREF(*aCallbacks = mCallbacks);
707 return NS_OK;
710 NS_IMETHODIMP
711 nsJARChannel::SetNotificationCallbacks(nsIInterfaceRequestor* aCallbacks) {
712 mCallbacks = aCallbacks;
713 return NS_OK;
716 NS_IMETHODIMP
717 nsJARChannel::GetSecurityInfo(nsITransportSecurityInfo** aSecurityInfo) {
718 MOZ_ASSERT(aSecurityInfo, "Null out param");
719 *aSecurityInfo = nullptr;
720 return NS_OK;
723 bool nsJARChannel::GetContentTypeGuess(nsACString& aResult) const {
724 const char *ext = nullptr, *fileName = mJarEntry.get();
725 int32_t len = mJarEntry.Length();
727 // check if we're displaying a directory
728 // mJarEntry will be empty if we're trying to display
729 // the topmost directory in a zip, e.g. jar:foo.zip!/
730 if (ENTRY_IS_DIRECTORY(mJarEntry)) {
731 aResult.AssignLiteral(APPLICATION_HTTP_INDEX_FORMAT);
732 return true;
735 // Not a directory, take a guess by its extension
736 for (int32_t i = len - 1; i >= 0; i--) {
737 if (fileName[i] == '.') {
738 ext = &fileName[i + 1];
739 break;
742 if (!ext) {
743 return false;
745 nsIMIMEService* mimeServ = gJarHandler->MimeService();
746 if (!mimeServ) {
747 return false;
749 mimeServ->GetTypeFromExtension(nsDependentCString(ext), aResult);
750 return !aResult.IsEmpty();
753 NS_IMETHODIMP
754 nsJARChannel::GetContentType(nsACString& aResult) {
755 // If the Jar file has not been open yet,
756 // We return application/x-unknown-content-type
757 if (!mOpened) {
758 aResult.AssignLiteral(UNKNOWN_CONTENT_TYPE);
759 return NS_OK;
762 aResult = mContentType;
763 return NS_OK;
766 NS_IMETHODIMP
767 nsJARChannel::SetContentType(const nsACString& aContentType) {
768 // We behave like HTTP channels (treat this as a hint if called before open,
769 // and override the charset if called after open).
770 // mContentCharset is unchanged if not parsed
771 NS_ParseResponseContentType(aContentType, mContentType, mContentCharset);
772 return NS_OK;
775 NS_IMETHODIMP
776 nsJARChannel::GetContentCharset(nsACString& aContentCharset) {
777 // If someone gives us a charset hint we should just use that charset.
778 // So we don't care when this is being called.
779 aContentCharset = mContentCharset;
780 return NS_OK;
783 NS_IMETHODIMP
784 nsJARChannel::SetContentCharset(const nsACString& aContentCharset) {
785 mContentCharset = aContentCharset;
786 return NS_OK;
789 NS_IMETHODIMP
790 nsJARChannel::GetContentDisposition(uint32_t* aContentDisposition) {
791 return NS_ERROR_NOT_AVAILABLE;
794 NS_IMETHODIMP
795 nsJARChannel::SetContentDisposition(uint32_t aContentDisposition) {
796 return NS_ERROR_NOT_AVAILABLE;
799 NS_IMETHODIMP
800 nsJARChannel::GetContentDispositionFilename(
801 nsAString& aContentDispositionFilename) {
802 return NS_ERROR_NOT_AVAILABLE;
805 NS_IMETHODIMP
806 nsJARChannel::SetContentDispositionFilename(
807 const nsAString& aContentDispositionFilename) {
808 return NS_ERROR_NOT_AVAILABLE;
811 NS_IMETHODIMP
812 nsJARChannel::GetContentDispositionHeader(
813 nsACString& aContentDispositionHeader) {
814 return NS_ERROR_NOT_AVAILABLE;
817 NS_IMETHODIMP
818 nsJARChannel::GetContentLength(int64_t* result) {
819 *result = mContentLength;
820 return NS_OK;
823 NS_IMETHODIMP
824 nsJARChannel::SetContentLength(int64_t aContentLength) {
825 // XXX does this really make any sense at all?
826 mContentLength = aContentLength;
827 return NS_OK;
830 static void RecordZeroLengthEvent(bool aIsSync, const nsCString& aSpec,
831 nsresult aStatus, bool aCanceled,
832 nsILoadInfo* aLoadInfo) {
833 if (!StaticPrefs::network_jar_record_failure_reason()) {
834 return;
837 if (aLoadInfo) {
838 bool shouldSkipCheckForBrokenURLOrZeroSized;
839 MOZ_ALWAYS_SUCCEEDS(aLoadInfo->GetShouldSkipCheckForBrokenURLOrZeroSized(
840 &shouldSkipCheckForBrokenURLOrZeroSized));
841 if (shouldSkipCheckForBrokenURLOrZeroSized) {
842 return;
846 // The event can only hold 80 characters.
847 // We only save the file name and path inside the jar.
848 auto findFilenameStart = [](const nsCString& aSpec) -> uint32_t {
849 int32_t pos = aSpec.Find("!/");
850 if (pos == kNotFound) {
851 MOZ_ASSERT(false, "This should not happen");
852 return 0;
854 int32_t from = aSpec.RFindChar('/', pos);
855 if (from == kNotFound) {
856 MOZ_ASSERT(false, "This should not happen");
857 return 0;
859 // Skip over the slash
860 from++;
861 return from;
864 // If for some reason we are unable to extract the filename we report the
865 // entire string, or 80 characters of it, to make sure we don't miss any
866 // events.
867 uint32_t from = findFilenameStart(aSpec);
868 const auto fileName = Substring(aSpec, from);
870 nsAutoCString errorCString;
871 mozilla::GetErrorName(aStatus, errorCString);
873 // To test this telemetry we use a zip file and we want to make
874 // sure don't filter it out.
875 bool isTest = fileName.Find("test_empty_file.zip!") != -1;
876 bool isOmniJa = StringBeginsWith(fileName, "omni.ja!"_ns);
878 Telemetry::SetEventRecordingEnabled("zero_byte_load"_ns, true);
879 Telemetry::EventID eventType = Telemetry::EventID::Zero_byte_load_Load_Others;
880 if (StringEndsWith(fileName, ".ftl"_ns)) {
881 eventType = Telemetry::EventID::Zero_byte_load_Load_Ftl;
882 } else if (StringEndsWith(fileName, ".dtd"_ns)) {
883 // We're going to skip reporting telemetry on res DTDs.
884 // See Bug 1693711 for investigation into those empty loads.
885 if (!isTest && StringBeginsWith(fileName, "omni.ja!/res/dtd"_ns)) {
886 return;
889 eventType = Telemetry::EventID::Zero_byte_load_Load_Dtd;
890 } else if (StringEndsWith(fileName, ".properties"_ns)) {
891 eventType = Telemetry::EventID::Zero_byte_load_Load_Properties;
892 } else if (StringEndsWith(fileName, ".js"_ns) ||
893 StringEndsWith(fileName, ".jsm"_ns) ||
894 StringEndsWith(fileName, ".mjs"_ns)) {
895 // We're going to skip reporting telemetry on JS loads
896 // coming not from omni.ja.
897 // See Bug 1693711 for investigation into those empty loads.
898 if (!isTest && !isOmniJa) {
899 return;
901 eventType = Telemetry::EventID::Zero_byte_load_Load_Js;
902 } else if (StringEndsWith(fileName, ".xml"_ns)) {
903 eventType = Telemetry::EventID::Zero_byte_load_Load_Xml;
904 } else if (StringEndsWith(fileName, ".xhtml"_ns)) {
905 // This error seems to be very common and is not strongly
906 // correlated to YSOD.
907 if (aStatus == NS_ERROR_PARSED_DATA_CACHED) {
908 return;
911 // We're not investigating YSODs from extensions for now.
912 if (!isOmniJa) {
913 return;
916 eventType = Telemetry::EventID::Zero_byte_load_Load_Xhtml;
917 } else if (StringEndsWith(fileName, ".css"_ns)) {
918 // Bug 1702937: Filter out svg+'css'+'png'/NS_BINDING_ABORTED combo.
919 if (aStatus == NS_BINDING_ABORTED) {
920 return;
923 // Bug 1702937: Filter css/NS_ERROR_CORRUPTED_CONTENT that is coming from
924 // outside of omni.ja.
925 if (!isOmniJa && aStatus == NS_ERROR_CORRUPTED_CONTENT) {
926 return;
928 eventType = Telemetry::EventID::Zero_byte_load_Load_Css;
929 } else if (StringEndsWith(fileName, ".json"_ns)) {
930 eventType = Telemetry::EventID::Zero_byte_load_Load_Json;
931 } else if (StringEndsWith(fileName, ".html"_ns)) {
932 eventType = Telemetry::EventID::Zero_byte_load_Load_Html;
933 // See bug 1695560. Filter out non-omni.ja HTML.
934 if (!isOmniJa) {
935 return;
938 // See bug 1695560. "activity-stream-noscripts.html" with NS_ERROR_FAILURE
939 // is filtered out.
940 if (fileName.EqualsLiteral("omni.ja!/chrome/browser/res/activity-stream/"
941 "prerendered/activity-stream-noscripts.html") &&
942 aStatus == NS_ERROR_FAILURE) {
943 return;
945 } else if (StringEndsWith(fileName, ".png"_ns)) {
946 eventType = Telemetry::EventID::Zero_byte_load_Load_Png;
947 // See bug 1695560.
948 // Bug 1702937: Filter out svg+'css'+'png'/NS_BINDING_ABORTED combo.
949 if (!isOmniJa || aStatus == NS_BINDING_ABORTED) {
950 return;
952 } else if (StringEndsWith(fileName, ".svg"_ns)) {
953 eventType = Telemetry::EventID::Zero_byte_load_Load_Svg;
954 // See bug 1695560.
955 // Bug 1702937: Filter out svg+'css'+'png'/NS_BINDING_ABORTED combo.
956 if (!isOmniJa || aStatus == NS_BINDING_ABORTED) {
957 return;
961 // We're going to, for now, filter out `other` category.
962 // See Bug 1693711 for investigation into those empty loads.
963 // Bug 1702937: Filter other/*.ico/NS_BINDING_ABORTED.
964 if (!isTest && eventType == Telemetry::EventID::Zero_byte_load_Load_Others &&
965 (!isOmniJa || (aStatus == NS_BINDING_ABORTED &&
966 StringEndsWith(fileName, ".ico"_ns)))) {
967 return;
970 // FTL uses I/O to test for file presence, so we get
971 // a high volume of events from it, but it is not erronous.
972 // Also, Fluent is resilient to empty loads, so even if any
973 // of the errors are real errors, they don't cause YSOD.
974 // We can investigate them separately.
975 if (!isTest &&
976 (eventType == Telemetry::EventID::Zero_byte_load_Load_Ftl ||
977 eventType == Telemetry::EventID::Zero_byte_load_Load_Json) &&
978 aStatus == NS_ERROR_FILE_NOT_FOUND) {
979 return;
982 // See bug 1695560. "search-extensions/google/favicon.ico" with
983 // NS_BINDING_ABORTED is filtered out.
984 if (fileName.EqualsLiteral(
985 "omni.ja!/chrome/browser/search-extensions/google/favicon.ico") &&
986 aStatus == NS_BINDING_ABORTED) {
987 return;
990 // See bug 1695560. "update.locale" with
991 // NS_ERROR_FILE_NOT_FOUND is filtered out.
992 if (fileName.EqualsLiteral("omni.ja!/update.locale") &&
993 aStatus == NS_ERROR_FILE_NOT_FOUND) {
994 return;
997 auto res = CopyableTArray<Telemetry::EventExtraEntry>{};
998 res.SetCapacity(4);
999 res.AppendElement(
1000 Telemetry::EventExtraEntry{"sync"_ns, aIsSync ? "true"_ns : "false"_ns});
1001 res.AppendElement(
1002 Telemetry::EventExtraEntry{"file_name"_ns, nsCString(fileName)});
1003 res.AppendElement(Telemetry::EventExtraEntry{"status"_ns, errorCString});
1004 res.AppendElement(Telemetry::EventExtraEntry{
1005 "cancelled"_ns, aCanceled ? "true"_ns : "false"_ns});
1006 Telemetry::RecordEvent(eventType, Nothing{}, Some(res));
1009 NS_IMETHODIMP
1010 nsJARChannel::Open(nsIInputStream** aStream) {
1011 LOG(("nsJARChannel::Open [this=%p]\n", this));
1012 nsCOMPtr<nsIStreamListener> listener;
1013 nsresult rv =
1014 nsContentSecurityManager::doContentSecurityCheck(this, listener);
1015 NS_ENSURE_SUCCESS(rv, rv);
1017 auto recordEvent = MakeScopeExit([&] {
1018 if (mContentLength <= 0 || NS_FAILED(rv)) {
1019 RecordZeroLengthEvent(true, mSpec, rv, mCanceled, mLoadInfo);
1023 LOG(("nsJARChannel::Open [this=%p]\n", this));
1025 NS_ENSURE_TRUE(!mOpened, NS_ERROR_IN_PROGRESS);
1026 NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS);
1028 mJarFile = nullptr;
1030 rv = LookupFile();
1031 if (NS_FAILED(rv)) return rv;
1033 // If mJarFile was not set by LookupFile, we can't open a channel.
1034 if (!mJarFile) {
1035 MOZ_ASSERT_UNREACHABLE("only file-backed jars are supported");
1036 return NS_ERROR_NOT_IMPLEMENTED;
1039 RefPtr<nsJARInputThunk> input;
1040 rv = CreateJarInput(gJarHandler->JarCache(), getter_AddRefs(input));
1041 if (NS_FAILED(rv)) return rv;
1043 input.forget(aStream);
1044 SetOpened();
1046 return NS_OK;
1049 void nsJARChannel::SetOpened() {
1050 MOZ_ASSERT(!mOpened, "Opening channel twice?");
1051 mOpened = true;
1052 // Compute the content type now.
1053 if (!GetContentTypeGuess(mContentType)) {
1054 mContentType.Assign(UNKNOWN_CONTENT_TYPE);
1058 NS_IMETHODIMP
1059 nsJARChannel::AsyncOpen(nsIStreamListener* aListener) {
1060 LOG(("nsJARChannel::AsyncOpen [this=%p]\n", this));
1061 nsCOMPtr<nsIStreamListener> listener = aListener;
1062 nsresult rv =
1063 nsContentSecurityManager::doContentSecurityCheck(this, listener);
1064 if (NS_FAILED(rv)) {
1065 mIsPending = false;
1066 mListener = nullptr;
1067 mCallbacks = nullptr;
1068 mProgressSink = nullptr;
1069 return rv;
1072 LOG(("nsJARChannel::AsyncOpen [this=%p]\n", this));
1073 MOZ_ASSERT(
1074 mLoadInfo->GetSecurityMode() == 0 ||
1075 mLoadInfo->GetInitialSecurityCheckDone() ||
1076 (mLoadInfo->GetSecurityMode() ==
1077 nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL &&
1078 mLoadInfo->GetLoadingPrincipal() &&
1079 mLoadInfo->GetLoadingPrincipal()->IsSystemPrincipal()),
1080 "security flags in loadInfo but doContentSecurityCheck() not called");
1082 NS_ENSURE_ARG_POINTER(listener);
1083 NS_ENSURE_TRUE(!mOpened, NS_ERROR_IN_PROGRESS);
1084 NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS);
1086 mJarFile = nullptr;
1088 // Initialize mProgressSink
1089 NS_QueryNotificationCallbacks(mCallbacks, mLoadGroup, mProgressSink);
1091 mListener = listener;
1092 mIsPending = true;
1094 rv = LookupFile();
1095 if (NS_FAILED(rv) || !mJarFile) {
1096 // Not a local file...
1097 mIsPending = false;
1098 mListener = nullptr;
1099 mCallbacks = nullptr;
1100 mProgressSink = nullptr;
1101 return mJarFile ? rv : NS_ERROR_UNSAFE_CONTENT_TYPE;
1104 rv = OpenLocalFile();
1105 if (NS_FAILED(rv)) {
1106 mIsPending = false;
1107 mListener = nullptr;
1108 mCallbacks = nullptr;
1109 mProgressSink = nullptr;
1110 return rv;
1113 return NS_OK;
1116 //-----------------------------------------------------------------------------
1117 // nsIJARChannel
1118 //-----------------------------------------------------------------------------
1119 NS_IMETHODIMP
1120 nsJARChannel::GetJarFile(nsIFile** aFile) {
1121 NS_IF_ADDREF(*aFile = mJarFile);
1122 return NS_OK;
1125 NS_IMETHODIMP
1126 nsJARChannel::SetJarFile(nsIFile* aFile) {
1127 if (mOpened) {
1128 return NS_ERROR_IN_PROGRESS;
1130 mJarFileOverride = aFile;
1131 return NS_OK;
1134 NS_IMETHODIMP
1135 nsJARChannel::EnsureCached(bool* aIsCached) {
1136 nsresult rv;
1137 *aIsCached = false;
1139 if (mOpened) {
1140 return NS_ERROR_ALREADY_OPENED;
1143 if (mPreCachedJarReader) {
1144 // We've already been called and found the JAR is cached
1145 *aIsCached = true;
1146 return NS_OK;
1149 nsCOMPtr<nsIURI> innerFileURI;
1150 rv = mJarURI->GetJARFile(getter_AddRefs(innerFileURI));
1151 NS_ENSURE_SUCCESS(rv, rv);
1153 nsCOMPtr<nsIFileURL> innerFileURL = do_QueryInterface(innerFileURI, &rv);
1154 NS_ENSURE_SUCCESS(rv, rv);
1156 nsCOMPtr<nsIFile> jarFile;
1157 rv = innerFileURL->GetFile(getter_AddRefs(jarFile));
1158 NS_ENSURE_SUCCESS(rv, rv);
1160 nsCOMPtr<nsIIOService> ioService = do_GetIOService(&rv);
1161 NS_ENSURE_SUCCESS(rv, rv);
1163 nsCOMPtr<nsIProtocolHandler> handler;
1164 rv = ioService->GetProtocolHandler("jar", getter_AddRefs(handler));
1165 NS_ENSURE_SUCCESS(rv, rv);
1167 auto jarHandler = static_cast<nsJARProtocolHandler*>(handler.get());
1168 MOZ_ASSERT(jarHandler);
1170 nsIZipReaderCache* jarCache = jarHandler->JarCache();
1172 rv = jarCache->GetZipIfCached(jarFile, getter_AddRefs(mPreCachedJarReader));
1173 if (rv == NS_ERROR_CACHE_KEY_NOT_FOUND) {
1174 return NS_OK;
1176 NS_ENSURE_SUCCESS(rv, rv);
1178 *aIsCached = true;
1179 return NS_OK;
1182 NS_IMETHODIMP
1183 nsJARChannel::GetZipEntry(nsIZipEntry** aZipEntry) {
1184 nsresult rv = LookupFile();
1185 if (NS_FAILED(rv)) return rv;
1187 if (!mJarFile) return NS_ERROR_NOT_AVAILABLE;
1189 nsCOMPtr<nsIZipReader> reader;
1190 rv = gJarHandler->JarCache()->GetZip(mJarFile, getter_AddRefs(reader));
1191 if (NS_FAILED(rv)) return rv;
1193 return reader->GetEntry(mJarEntry, aZipEntry);
1196 //-----------------------------------------------------------------------------
1197 // nsIStreamListener
1198 //-----------------------------------------------------------------------------
1200 NS_IMETHODIMP
1201 nsJARChannel::OnStartRequest(nsIRequest* req) {
1202 LOG(("nsJARChannel::OnStartRequest [this=%p %s]\n", this, mSpec.get()));
1204 mRequest = req;
1205 nsresult rv = mListener->OnStartRequest(this);
1206 if (NS_FAILED(rv)) {
1207 return rv;
1210 // Restrict loadable content types.
1211 nsAutoCString contentType;
1212 GetContentType(contentType);
1213 auto contentPolicyType = mLoadInfo->GetExternalContentPolicyType();
1214 if (contentType.Equals(APPLICATION_HTTP_INDEX_FORMAT) &&
1215 contentPolicyType != ExtContentPolicy::TYPE_DOCUMENT &&
1216 contentPolicyType != ExtContentPolicy::TYPE_FETCH) {
1217 return NS_ERROR_CORRUPTED_CONTENT;
1219 if (contentPolicyType == ExtContentPolicy::TYPE_STYLESHEET &&
1220 !contentType.EqualsLiteral(TEXT_CSS)) {
1221 return NS_ERROR_CORRUPTED_CONTENT;
1223 if (contentPolicyType == ExtContentPolicy::TYPE_SCRIPT &&
1224 !nsContentUtils::IsJavascriptMIMEType(
1225 NS_ConvertUTF8toUTF16(contentType))) {
1226 return NS_ERROR_CORRUPTED_CONTENT;
1229 return rv;
1232 NS_IMETHODIMP
1233 nsJARChannel::OnStopRequest(nsIRequest* req, nsresult status) {
1234 LOG(("nsJARChannel::OnStopRequest [this=%p %s status=%" PRIx32 "]\n", this,
1235 mSpec.get(), static_cast<uint32_t>(status)));
1237 if (NS_SUCCEEDED(mStatus)) mStatus = status;
1239 if (mListener) {
1240 if (!mOnDataCalled || NS_FAILED(status)) {
1241 RecordZeroLengthEvent(false, mSpec, status, mCanceled, mLoadInfo);
1244 mListener->OnStopRequest(this, status);
1245 mListener = nullptr;
1248 if (mLoadGroup) mLoadGroup->RemoveRequest(this, nullptr, status);
1250 mRequest = nullptr;
1251 mPump = nullptr;
1252 mIsPending = false;
1254 // Drop notification callbacks to prevent cycles.
1255 mCallbacks = nullptr;
1256 mProgressSink = nullptr;
1258 #if defined(XP_WIN) || defined(MOZ_WIDGET_COCOA)
1259 #else
1260 // To deallocate file descriptor by RemoteOpenFileChild destructor.
1261 mJarFile = nullptr;
1262 #endif
1264 return NS_OK;
1267 NS_IMETHODIMP
1268 nsJARChannel::OnDataAvailable(nsIRequest* req, nsIInputStream* stream,
1269 uint64_t offset, uint32_t count) {
1270 LOG(("nsJARChannel::OnDataAvailable [this=%p %s]\n", this, mSpec.get()));
1272 nsresult rv;
1274 // don't send out OnDataAvailable notifications if we've been canceled.
1275 if (mCanceled) {
1276 return mStatus;
1279 mOnDataCalled = true;
1280 rv = mListener->OnDataAvailable(this, stream, offset, count);
1282 // simply report progress here instead of hooking ourselves up as a
1283 // nsITransportEventSink implementation.
1284 // XXX do the 64-bit stuff for real
1285 if (mProgressSink && NS_SUCCEEDED(rv)) {
1286 if (NS_IsMainThread()) {
1287 FireOnProgress(offset + count);
1288 } else {
1289 NS_DispatchToMainThread(NewRunnableMethod<uint64_t>(
1290 "nsJARChannel::FireOnProgress", this, &nsJARChannel::FireOnProgress,
1291 offset + count));
1295 return rv; // let the pump cancel on failure
1298 NS_IMETHODIMP
1299 nsJARChannel::RetargetDeliveryTo(nsISerialEventTarget* aEventTarget) {
1300 MOZ_ASSERT(NS_IsMainThread());
1302 nsCOMPtr<nsIThreadRetargetableRequest> request = do_QueryInterface(mRequest);
1303 if (!request) {
1304 return NS_ERROR_NO_INTERFACE;
1307 return request->RetargetDeliveryTo(aEventTarget);
1310 NS_IMETHODIMP
1311 nsJARChannel::GetDeliveryTarget(nsISerialEventTarget** aEventTarget) {
1312 MOZ_ASSERT(NS_IsMainThread());
1314 nsCOMPtr<nsIThreadRetargetableRequest> request = do_QueryInterface(mRequest);
1315 if (!request) {
1316 return NS_ERROR_NO_INTERFACE;
1319 return request->GetDeliveryTarget(aEventTarget);
1322 NS_IMETHODIMP
1323 nsJARChannel::CheckListenerChain() {
1324 MOZ_ASSERT(NS_IsMainThread());
1326 nsCOMPtr<nsIThreadRetargetableStreamListener> listener =
1327 do_QueryInterface(mListener);
1328 if (!listener) {
1329 return NS_ERROR_NO_INTERFACE;
1332 return listener->CheckListenerChain();
1335 NS_IMETHODIMP
1336 nsJARChannel::OnDataFinished(nsresult aStatus) {
1337 nsCOMPtr<nsIThreadRetargetableStreamListener> listener =
1338 do_QueryInterface(mListener);
1339 if (listener) {
1340 return listener->OnDataFinished(aStatus);
1343 return NS_OK;