Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/modules/libjar/nsJARChannel.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
/* vim: set sw=4 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/. */
6
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 "nsIPrefService.h"
14
#include "nsIPrefBranch.h"
15
#include "nsIViewSourceChannel.h"
16
#include "nsContentUtils.h"
17
#include "nsProxyRelease.h"
18
#include "nsContentSecurityManager.h"
19
20
#include "nsIScriptSecurityManager.h"
21
#include "nsIPrincipal.h"
22
#include "nsIFileURL.h"
23
24
#include "mozilla/IntegerPrintfMacros.h"
25
#include "mozilla/Preferences.h"
26
#include "nsITabChild.h"
27
#include "private/pprio.h"
28
#include "nsInputStreamPump.h"
29
#include "nsThreadUtils.h"
30
31
using namespace mozilla;
32
using namespace mozilla::net;
33
34
static NS_DEFINE_CID(kZipReaderCID, NS_ZIPREADER_CID);
35
36
// the entry for a directory will either be empty (in the case of the
37
// top-level directory) or will end with a slash
38
#define ENTRY_IS_DIRECTORY(_entry) \
39
5
  ((_entry).IsEmpty() || '/' == (_entry).Last())
40
41
//-----------------------------------------------------------------------------
42
43
// Ignore any LOG macro that we inherit from arbitrary headers. (We define our
44
// own LOG macro below.)
45
#ifdef LOG
46
#undef LOG
47
#endif
48
49
//
50
// set NSPR_LOG_MODULES=nsJarProtocol:5
51
//
52
static LazyLogModule gJarProtocolLog("nsJarProtocol");
53
54
35
#define LOG(args)     MOZ_LOG(gJarProtocolLog, mozilla::LogLevel::Debug, args)
55
#define LOG_ENABLED() MOZ_LOG_TEST(gJarProtocolLog, mozilla::LogLevel::Debug)
56
57
//-----------------------------------------------------------------------------
58
// nsJARInputThunk
59
//
60
// this class allows us to do some extra work on the stream transport thread.
61
//-----------------------------------------------------------------------------
62
63
class nsJARInputThunk : public nsIInputStream
64
{
65
public:
66
    NS_DECL_THREADSAFE_ISUPPORTS
67
    NS_DECL_NSIINPUTSTREAM
68
69
    nsJARInputThunk(nsIZipReader *zipReader,
70
                    nsIURI* fullJarURI,
71
                    const nsACString &jarEntry,
72
                    bool usingJarCache)
73
        : mUsingJarCache(usingJarCache)
74
        , mJarReader(zipReader)
75
        , mJarEntry(jarEntry)
76
        , mContentLength(-1)
77
5
    {
78
5
        if (fullJarURI) {
79
#ifdef DEBUG
80
            nsresult rv =
81
#endif
82
                fullJarURI->GetAsciiSpec(mJarDirSpec);
83
5
            NS_ASSERTION(NS_SUCCEEDED(rv), "this shouldn't fail");
84
5
        }
85
5
    }
86
87
    int64_t GetContentLength()
88
5
    {
89
5
        return mContentLength;
90
5
    }
91
92
    nsresult Init();
93
94
private:
95
96
    virtual ~nsJARInputThunk()
97
5
    {
98
5
        Close();
99
5
    }
100
101
    bool                        mUsingJarCache;
102
    nsCOMPtr<nsIZipReader>      mJarReader;
103
    nsCString                   mJarDirSpec;
104
    nsCOMPtr<nsIInputStream>    mJarStream;
105
    nsCString                   mJarEntry;
106
    int64_t                     mContentLength;
107
};
108
109
NS_IMPL_ISUPPORTS(nsJARInputThunk, nsIInputStream)
110
111
nsresult
112
nsJARInputThunk::Init()
113
5
{
114
5
    nsresult rv;
115
5
    if (ENTRY_IS_DIRECTORY(mJarEntry)) {
116
0
        // A directory stream also needs the Spec of the FullJarURI
117
0
        // because is included in the stream data itself.
118
0
119
0
        NS_ENSURE_STATE(!mJarDirSpec.IsEmpty());
120
0
121
0
        rv = mJarReader->GetInputStreamWithSpec(mJarDirSpec,
122
0
                                                mJarEntry,
123
0
                                                getter_AddRefs(mJarStream));
124
0
    }
125
5
    else {
126
5
        rv = mJarReader->GetInputStream(mJarEntry,
127
5
                                        getter_AddRefs(mJarStream));
128
5
    }
129
5
    if (NS_FAILED(rv)) {
130
0
        // convert to the proper result if the entry wasn't found
131
0
        // so that error pages work
132
0
        if (rv == NS_ERROR_FILE_TARGET_DOES_NOT_EXIST)
133
0
            rv = NS_ERROR_FILE_NOT_FOUND;
134
0
        return rv;
135
0
    }
136
5
137
5
    // ask the JarStream for the content length
138
5
    uint64_t avail;
139
5
    rv = mJarStream->Available((uint64_t *) &avail);
140
5
    if (NS_FAILED(rv)) return rv;
141
5
142
5
    mContentLength = avail < INT64_MAX ? (int64_t) avail : -1;
143
5
144
5
    return NS_OK;
145
5
}
146
147
NS_IMETHODIMP
148
nsJARInputThunk::Close()
149
5
{
150
5
    nsresult rv = NS_OK;
151
5
152
5
    if (mJarStream)
153
5
        rv = mJarStream->Close();
154
5
155
5
    if (!mUsingJarCache && mJarReader)
156
0
        mJarReader->Close();
157
5
158
5
    mJarReader = nullptr;
159
5
160
5
    return rv;
161
5
}
162
163
NS_IMETHODIMP
164
nsJARInputThunk::Available(uint64_t *avail)
165
5
{
166
5
    return mJarStream->Available(avail);
167
5
}
168
169
NS_IMETHODIMP
170
nsJARInputThunk::Read(char *buf, uint32_t count, uint32_t *countRead)
171
5
{
172
5
    return mJarStream->Read(buf, count, countRead);
173
5
}
174
175
NS_IMETHODIMP
176
nsJARInputThunk::ReadSegments(nsWriteSegmentFun writer, void *closure,
177
                              uint32_t count, uint32_t *countRead)
178
0
{
179
0
    // stream transport does only calls Read()
180
0
    return NS_ERROR_NOT_IMPLEMENTED;
181
0
}
182
183
NS_IMETHODIMP
184
nsJARInputThunk::IsNonBlocking(bool *nonBlocking)
185
0
{
186
0
    *nonBlocking = false;
187
0
    return NS_OK;
188
0
}
189
190
//-----------------------------------------------------------------------------
191
// nsJARChannel
192
//-----------------------------------------------------------------------------
193
194
195
nsJARChannel::nsJARChannel()
196
    : mOpened(false)
197
    , mContentLength(-1)
198
    , mLoadFlags(LOAD_NORMAL)
199
    , mStatus(NS_OK)
200
    , mIsPending(false)
201
    , mEnableOMT(true)
202
    , mPendingEvent()
203
5
{
204
5
    LOG(("nsJARChannel::nsJARChannel [this=%p]\n", this));
205
5
    // hold an owning reference to the jar handler
206
5
    mJarHandler = gJarHandler;
207
5
}
208
209
nsJARChannel::~nsJARChannel()
210
5
{
211
5
    LOG(("nsJARChannel::~nsJARChannel [this=%p]\n", this));
212
5
    NS_ReleaseOnMainThreadSystemGroup("nsJARChannel::mLoadInfo",
213
5
                                      mLoadInfo.forget());
214
5
}
215
216
NS_IMPL_ISUPPORTS_INHERITED(nsJARChannel,
217
                            nsHashPropertyBag,
218
                            nsIRequest,
219
                            nsIChannel,
220
                            nsIStreamListener,
221
                            nsIRequestObserver,
222
                            nsIThreadRetargetableRequest,
223
                            nsIThreadRetargetableStreamListener,
224
                            nsIJARChannel)
225
226
nsresult
227
nsJARChannel::Init(nsIURI *uri)
228
5
{
229
5
    LOG(("nsJARChannel::Init [this=%p]\n", this));
230
5
    nsresult rv;
231
5
232
5
    mWorker = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID, &rv);
233
5
    if (NS_FAILED(rv)) {
234
0
        return rv;
235
0
    }
236
5
237
5
    mJarURI = do_QueryInterface(uri, &rv);
238
5
    if (NS_FAILED(rv))
239
5
        return rv;
240
5
241
5
    mOriginalURI = mJarURI;
242
5
243
5
    // Prevent loading jar:javascript URIs (see bug 290982).
244
5
    nsCOMPtr<nsIURI> innerURI;
245
5
    rv = mJarURI->GetJARFile(getter_AddRefs(innerURI));
246
5
    if (NS_FAILED(rv))
247
5
        return rv;
248
5
    bool isJS;
249
5
    rv = innerURI->SchemeIs("javascript", &isJS);
250
5
    if (NS_FAILED(rv))
251
5
        return rv;
252
5
    if (isJS) {
253
0
        NS_WARNING("blocking jar:javascript:");
254
0
        return NS_ERROR_INVALID_ARG;
255
0
    }
256
5
257
5
    mJarURI->GetSpec(mSpec);
258
5
    return rv;
259
5
}
260
261
nsresult
262
nsJARChannel::CreateJarInput(nsIZipReaderCache *jarCache, nsJARInputThunk **resultInput)
263
5
{
264
5
    LOG(("nsJARChannel::CreateJarInput [this=%p]\n", this));
265
5
    MOZ_ASSERT(resultInput);
266
5
    MOZ_ASSERT(mJarFile);
267
5
268
5
    // important to pass a clone of the file since the nsIFile impl is not
269
5
    // necessarily MT-safe
270
5
    nsCOMPtr<nsIFile> clonedFile;
271
5
    nsresult rv = NS_OK;
272
5
    if (mJarFile) {
273
5
        rv = mJarFile->Clone(getter_AddRefs(clonedFile));
274
5
        if (NS_FAILED(rv))
275
5
            return rv;
276
5
    }
277
5
278
5
    nsCOMPtr<nsIZipReader> reader;
279
5
    if (mPreCachedJarReader) {
280
0
        reader = mPreCachedJarReader;
281
5
    } else if (jarCache) {
282
5
        if (mInnerJarEntry.IsEmpty())
283
5
            rv = jarCache->GetZip(clonedFile, getter_AddRefs(reader));
284
0
        else
285
0
            rv = jarCache->GetInnerZip(clonedFile, mInnerJarEntry,
286
0
                                       getter_AddRefs(reader));
287
5
    } else {
288
0
        // create an uncached jar reader
289
0
        nsCOMPtr<nsIZipReader> outerReader = do_CreateInstance(kZipReaderCID, &rv);
290
0
        if (NS_FAILED(rv))
291
0
            return rv;
292
0
293
0
        rv = outerReader->Open(clonedFile);
294
0
        if (NS_FAILED(rv))
295
0
            return rv;
296
0
297
0
        if (mInnerJarEntry.IsEmpty())
298
0
            reader = outerReader;
299
0
        else {
300
0
            reader = do_CreateInstance(kZipReaderCID, &rv);
301
0
            if (NS_FAILED(rv))
302
0
                return rv;
303
0
304
0
            rv = reader->OpenInner(outerReader, mInnerJarEntry);
305
0
        }
306
0
    }
307
5
    if (NS_FAILED(rv))
308
5
        return rv;
309
5
310
5
    RefPtr<nsJARInputThunk> input = new nsJARInputThunk(reader,
311
5
                                                        mJarURI,
312
5
                                                        mJarEntry,
313
5
                                                        jarCache != nullptr);
314
5
    rv = input->Init();
315
5
    if (NS_FAILED(rv))
316
5
        return rv;
317
5
318
5
    // Make GetContentLength meaningful
319
5
    mContentLength = input->GetContentLength();
320
5
321
5
    input.forget(resultInput);
322
5
    return NS_OK;
323
5
}
324
325
nsresult
326
nsJARChannel::LookupFile()
327
5
{
328
5
    LOG(("nsJARChannel::LookupFile [this=%p %s]\n", this, mSpec.get()));
329
5
330
5
    if (mJarFile)
331
0
        return NS_OK;
332
5
333
5
    nsresult rv;
334
5
335
5
    rv = mJarURI->GetJARFile(getter_AddRefs(mJarBaseURI));
336
5
    if (NS_FAILED(rv))
337
5
        return rv;
338
5
339
5
    rv = mJarURI->GetJAREntry(mJarEntry);
340
5
    if (NS_FAILED(rv))
341
5
        return rv;
342
5
343
5
    // The name of the JAR entry must not contain URL-escaped characters:
344
5
    // we're moving from URL domain to a filename domain here. nsStandardURL
345
5
    // does basic escaping by default, which breaks reading zipped files which
346
5
    // have e.g. spaces in their filenames.
347
5
    NS_UnescapeURL(mJarEntry);
348
5
349
5
    if (mJarFileOverride) {
350
0
        mJarFile = mJarFileOverride;
351
0
        LOG(("nsJARChannel::LookupFile [this=%p] Overriding mJarFile\n", this));
352
0
        return NS_OK;
353
0
    }
354
5
355
5
    // try to get a nsIFile directly from the url, which will often succeed.
356
5
    {
357
5
        nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(mJarBaseURI);
358
5
        if (fileURL)
359
5
            fileURL->GetFile(getter_AddRefs(mJarFile));
360
5
    }
361
5
362
5
    // try to handle a nested jar
363
5
    if (!mJarFile) {
364
0
        nsCOMPtr<nsIJARURI> jarURI = do_QueryInterface(mJarBaseURI);
365
0
        if (jarURI) {
366
0
            nsCOMPtr<nsIFileURL> fileURL;
367
0
            nsCOMPtr<nsIURI> innerJarURI;
368
0
            rv = jarURI->GetJARFile(getter_AddRefs(innerJarURI));
369
0
            if (NS_SUCCEEDED(rv))
370
0
                fileURL = do_QueryInterface(innerJarURI);
371
0
            if (fileURL) {
372
0
                fileURL->GetFile(getter_AddRefs(mJarFile));
373
0
                jarURI->GetJAREntry(mInnerJarEntry);
374
0
            }
375
0
        }
376
0
    }
377
5
378
5
    return rv;
379
5
}
380
381
nsresult
382
CreateLocalJarInput(nsIZipReaderCache* aJarCache,
383
                    nsIFile* aFile,
384
                    const nsACString& aInnerJarEntry,
385
                    nsIJARURI* aJarURI,
386
                    const nsACString& aJarEntry,
387
                    nsJARInputThunk** aResultInput)
388
0
{
389
0
    LOG(("nsJARChannel::CreateLocalJarInput [aJarCache=%p, %s, %s]\n",
390
0
         aJarCache,
391
0
         PromiseFlatCString(aInnerJarEntry).get(),
392
0
         PromiseFlatCString(aJarEntry).get()));
393
0
394
0
    MOZ_ASSERT(!NS_IsMainThread());
395
0
    MOZ_ASSERT(aJarCache);
396
0
    MOZ_ASSERT(aResultInput);
397
0
398
0
    nsresult rv;
399
0
400
0
    nsCOMPtr<nsIZipReader> reader;
401
0
    if (aInnerJarEntry.IsEmpty()) {
402
0
        rv = aJarCache->GetZip(aFile, getter_AddRefs(reader));
403
0
    } else {
404
0
        rv = aJarCache->GetInnerZip(aFile,
405
0
                                    aInnerJarEntry,
406
0
                                    getter_AddRefs(reader));
407
0
    }
408
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
409
0
        return rv;
410
0
    }
411
0
412
0
    RefPtr<nsJARInputThunk> input = new nsJARInputThunk(reader,
413
0
                                                        aJarURI,
414
0
                                                        aJarEntry,
415
0
                                                        aJarCache != nullptr);
416
0
    rv = input->Init();
417
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
418
0
        return rv;
419
0
    }
420
0
421
0
    input.forget(aResultInput);
422
0
    return NS_OK;
423
0
}
424
425
nsresult
426
nsJARChannel::OpenLocalFile()
427
0
{
428
0
    LOG(("nsJARChannel::OpenLocalFile [this=%p]\n", this));
429
0
430
0
    MOZ_ASSERT(NS_IsMainThread());
431
0
432
0
    MOZ_ASSERT(mWorker);
433
0
    MOZ_ASSERT(mIsPending);
434
0
    MOZ_ASSERT(mJarFile);
435
0
436
0
    nsresult rv;
437
0
438
0
    // Set mLoadGroup and mOpened before AsyncOpen return, and set back if
439
0
    // if failed when callback.
440
0
    if (mLoadGroup) {
441
0
        mLoadGroup->AddRequest(this, nullptr);
442
0
    }
443
0
    mOpened = true;
444
0
445
0
    if (mPreCachedJarReader || !mEnableOMT) {
446
0
        RefPtr<nsJARInputThunk> input;
447
0
        rv = CreateJarInput(gJarHandler->JarCache(),
448
0
                            getter_AddRefs(input));
449
0
        if (NS_WARN_IF(NS_FAILED(rv))) {
450
0
            return OnOpenLocalFileComplete(rv, true);
451
0
        }
452
0
        return ContinueOpenLocalFile(input, true);
453
0
    }
454
0
455
0
456
0
    nsCOMPtr<nsIZipReaderCache> jarCache = gJarHandler->JarCache();
457
0
    if (NS_WARN_IF(!jarCache)) {
458
0
        return NS_ERROR_UNEXPECTED;
459
0
    }
460
0
461
0
    nsCOMPtr<nsIFile> clonedFile;
462
0
    rv = mJarFile->Clone(getter_AddRefs(clonedFile));
463
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
464
0
        return rv;
465
0
    }
466
0
467
0
    nsCOMPtr<nsIJARURI> localJARURI = mJarURI;
468
0
469
0
    nsAutoCString jarEntry(mJarEntry);
470
0
    nsAutoCString innerJarEntry(mInnerJarEntry);
471
0
472
0
    RefPtr<nsJARChannel> self = this;
473
0
    return mWorker->Dispatch(
474
0
            NS_NewRunnableFunction("nsJARChannel::OpenLocalFile",
475
0
                                   [self,
476
0
                                   jarCache,
477
0
                                   clonedFile,
478
0
                                   localJARURI,
479
0
                                   jarEntry,
480
0
                                   innerJarEntry] () mutable {
481
0
482
0
        RefPtr<nsJARInputThunk> input;
483
0
        nsresult rv = CreateLocalJarInput(jarCache,
484
0
                                          clonedFile,
485
0
                                          innerJarEntry,
486
0
                                          localJARURI,
487
0
                                          jarEntry,
488
0
                                          getter_AddRefs(input));
489
0
490
0
        nsCOMPtr<nsIRunnable> target;
491
0
        if (NS_SUCCEEDED(rv)) {
492
0
            target = NewRunnableMethod<RefPtr<nsJARInputThunk>, bool>(
493
0
                "nsJARChannel::ContinueOpenLocalFile",
494
0
                self,
495
0
                &nsJARChannel::ContinueOpenLocalFile,
496
0
                input,
497
0
                false);
498
0
        } else {
499
0
            target = NewRunnableMethod<nsresult, bool>(
500
0
                "nsJARChannel::OnOpenLocalFileComplete",
501
0
                self,
502
0
                &nsJARChannel::OnOpenLocalFileComplete,
503
0
                rv,
504
0
                false);
505
0
        }
506
0
507
0
        // nsJARChannel must be release on main thread, and sometimes
508
0
        // this still hold nsJARChannel after dispatched.
509
0
        self = nullptr;
510
0
511
0
        NS_DispatchToMainThread(target.forget());
512
0
    }));
513
0
}
514
515
nsresult
516
nsJARChannel::ContinueOpenLocalFile(nsJARInputThunk* aInput, bool aIsSyncCall)
517
0
{
518
0
    LOG(("nsJARChannel::ContinueOpenLocalFile [this=%p %p]\n", this, aInput));
519
0
520
0
    MOZ_ASSERT(NS_IsMainThread());
521
0
    MOZ_ASSERT(mIsPending);
522
0
523
0
    // Make GetContentLength meaningful
524
0
    mContentLength = aInput->GetContentLength();
525
0
526
0
    nsresult rv;
527
0
    RefPtr<nsJARInputThunk> input = aInput;
528
0
    // Create input stream pump and call AsyncRead as a block.
529
0
    rv = NS_NewInputStreamPump(getter_AddRefs(mPump), input.forget());
530
0
    if (NS_SUCCEEDED(rv)) {
531
0
        rv = mPump->AsyncRead(this, nullptr);
532
0
    }
533
0
534
0
    if (NS_SUCCEEDED(rv)) {
535
0
        rv = CheckPendingEvents();
536
0
    }
537
0
538
0
    return OnOpenLocalFileComplete(rv, aIsSyncCall);
539
0
}
540
541
nsresult
542
nsJARChannel::OnOpenLocalFileComplete(nsresult aResult, bool aIsSyncCall)
543
0
{
544
0
    LOG(("nsJARChannel::OnOpenLocalFileComplete [this=%p %08x]\n",
545
0
         this,
546
0
         static_cast<uint32_t>(aResult)));
547
0
548
0
    MOZ_ASSERT(NS_IsMainThread());
549
0
    MOZ_ASSERT(mIsPending);
550
0
551
0
    if (NS_FAILED(aResult)) {
552
0
        if (!aIsSyncCall) {
553
0
            NotifyError(aResult);
554
0
        }
555
0
556
0
        if (mLoadGroup) {
557
0
            mLoadGroup->RemoveRequest(this, nullptr, aResult);
558
0
        }
559
0
560
0
        mOpened = false;
561
0
        mIsPending = false;
562
0
        mListenerContext = nullptr;
563
0
        mListener = nullptr;
564
0
        mCallbacks = nullptr;
565
0
        mProgressSink = nullptr;
566
0
567
0
        return aResult;
568
0
    }
569
0
570
0
    return NS_OK;
571
0
}
572
573
nsresult nsJARChannel::CheckPendingEvents()
574
0
{
575
0
    MOZ_ASSERT(NS_IsMainThread());
576
0
    MOZ_ASSERT(mIsPending);
577
0
    MOZ_ASSERT(mPump);
578
0
579
0
    nsresult rv;
580
0
581
0
    auto suspendCount = mPendingEvent.suspendCount;
582
0
    while (suspendCount--) {
583
0
        if (NS_WARN_IF(NS_FAILED(rv = mPump->Suspend()))) {
584
0
            return rv;
585
0
        }
586
0
    }
587
0
588
0
    if (mPendingEvent.isCanceled) {
589
0
        if (NS_WARN_IF(NS_FAILED(rv = mPump->Cancel(mStatus)))) {
590
0
            return rv;
591
0
        }
592
0
        mPendingEvent.isCanceled = false;
593
0
    }
594
0
595
0
    return NS_OK;
596
0
}
597
598
void
599
nsJARChannel::NotifyError(nsresult aError)
600
0
{
601
0
    MOZ_ASSERT(NS_FAILED(aError));
602
0
603
0
    mStatus = aError;
604
0
605
0
    OnStartRequest(nullptr, nullptr);
606
0
    OnStopRequest(nullptr, nullptr, aError);
607
0
}
608
609
void
610
nsJARChannel::FireOnProgress(uint64_t aProgress)
611
0
{
612
0
  MOZ_ASSERT(NS_IsMainThread());
613
0
  MOZ_ASSERT(mProgressSink);
614
0
615
0
  mProgressSink->OnProgress(this, nullptr, aProgress, mContentLength);
616
0
}
617
618
//-----------------------------------------------------------------------------
619
// nsIRequest
620
//-----------------------------------------------------------------------------
621
622
NS_IMETHODIMP
623
nsJARChannel::GetName(nsACString &result)
624
0
{
625
0
    return mJarURI->GetSpec(result);
626
0
}
627
628
NS_IMETHODIMP
629
nsJARChannel::IsPending(bool *result)
630
0
{
631
0
    *result = mIsPending;
632
0
    return NS_OK;
633
0
}
634
635
NS_IMETHODIMP
636
nsJARChannel::GetStatus(nsresult *status)
637
0
{
638
0
    if (mPump && NS_SUCCEEDED(mStatus))
639
0
        mPump->GetStatus(status);
640
0
    else
641
0
        *status = mStatus;
642
0
    return NS_OK;
643
0
}
644
645
NS_IMETHODIMP
646
nsJARChannel::Cancel(nsresult status)
647
0
{
648
0
    mStatus = status;
649
0
    if (mPump) {
650
0
        return mPump->Cancel(status);
651
0
    }
652
0
653
0
    if (mIsPending) {
654
0
        mPendingEvent.isCanceled = true;
655
0
    }
656
0
657
0
    return NS_OK;
658
0
}
659
660
NS_IMETHODIMP
661
nsJARChannel::Suspend()
662
0
{
663
0
    ++mPendingEvent.suspendCount;
664
0
665
0
    if (mPump) {
666
0
        return mPump->Suspend();
667
0
    }
668
0
669
0
    return NS_OK;
670
0
}
671
672
NS_IMETHODIMP
673
nsJARChannel::Resume()
674
0
{
675
0
    if (NS_WARN_IF(mPendingEvent.suspendCount == 0)) {
676
0
        return NS_ERROR_UNEXPECTED;
677
0
    }
678
0
    --mPendingEvent.suspendCount;
679
0
680
0
    if (mPump) {
681
0
        return mPump->Resume();
682
0
    }
683
0
684
0
    return NS_OK;
685
0
}
686
687
NS_IMETHODIMP
688
nsJARChannel::GetLoadFlags(nsLoadFlags *aLoadFlags)
689
0
{
690
0
    *aLoadFlags = mLoadFlags;
691
0
    return NS_OK;
692
0
}
693
694
NS_IMETHODIMP
695
nsJARChannel::SetLoadFlags(nsLoadFlags aLoadFlags)
696
0
{
697
0
    mLoadFlags = aLoadFlags;
698
0
    return NS_OK;
699
0
}
700
701
NS_IMETHODIMP
702
nsJARChannel::GetIsDocument(bool *aIsDocument)
703
0
{
704
0
    return NS_GetIsDocumentChannel(this, aIsDocument);
705
0
}
706
707
NS_IMETHODIMP
708
nsJARChannel::GetLoadGroup(nsILoadGroup **aLoadGroup)
709
0
{
710
0
    NS_IF_ADDREF(*aLoadGroup = mLoadGroup);
711
0
    return NS_OK;
712
0
}
713
714
NS_IMETHODIMP
715
nsJARChannel::SetLoadGroup(nsILoadGroup *aLoadGroup)
716
0
{
717
0
    mLoadGroup = aLoadGroup;
718
0
    return NS_OK;
719
0
}
720
721
//-----------------------------------------------------------------------------
722
// nsIChannel
723
//-----------------------------------------------------------------------------
724
725
NS_IMETHODIMP
726
nsJARChannel::GetOriginalURI(nsIURI **aURI)
727
10
{
728
10
    *aURI = mOriginalURI;
729
10
    NS_ADDREF(*aURI);
730
10
    return NS_OK;
731
10
}
732
733
NS_IMETHODIMP
734
nsJARChannel::SetOriginalURI(nsIURI *aURI)
735
4
{
736
4
    NS_ENSURE_ARG_POINTER(aURI);
737
4
    mOriginalURI = aURI;
738
4
    return NS_OK;
739
4
}
740
741
NS_IMETHODIMP
742
nsJARChannel::GetURI(nsIURI **aURI)
743
0
{
744
0
    NS_IF_ADDREF(*aURI = mJarURI);
745
0
746
0
    return NS_OK;
747
0
}
748
749
NS_IMETHODIMP
750
nsJARChannel::GetOwner(nsISupports **aOwner)
751
0
{
752
0
    // JAR signatures are not processed to avoid main-thread network I/O (bug 726125)
753
0
    *aOwner = mOwner;
754
0
    NS_IF_ADDREF(*aOwner);
755
0
    return NS_OK;
756
0
}
757
758
NS_IMETHODIMP
759
nsJARChannel::SetOwner(nsISupports *aOwner)
760
0
{
761
0
    mOwner = aOwner;
762
0
    return NS_OK;
763
0
}
764
765
NS_IMETHODIMP
766
nsJARChannel::GetLoadInfo(nsILoadInfo **aLoadInfo)
767
39
{
768
39
  NS_IF_ADDREF(*aLoadInfo = mLoadInfo);
769
39
  return NS_OK;
770
39
}
771
772
NS_IMETHODIMP
773
nsJARChannel::SetLoadInfo(nsILoadInfo* aLoadInfo)
774
5
{
775
5
  mLoadInfo = aLoadInfo;
776
5
  return NS_OK;
777
5
}
778
779
NS_IMETHODIMP
780
nsJARChannel::GetNotificationCallbacks(nsIInterfaceRequestor **aCallbacks)
781
0
{
782
0
    NS_IF_ADDREF(*aCallbacks = mCallbacks);
783
0
    return NS_OK;
784
0
}
785
786
NS_IMETHODIMP
787
nsJARChannel::SetNotificationCallbacks(nsIInterfaceRequestor *aCallbacks)
788
0
{
789
0
    mCallbacks = aCallbacks;
790
0
    return NS_OK;
791
0
}
792
793
NS_IMETHODIMP
794
nsJARChannel::GetSecurityInfo(nsISupports **aSecurityInfo)
795
0
{
796
0
    MOZ_ASSERT(aSecurityInfo, "Null out param");
797
0
    NS_IF_ADDREF(*aSecurityInfo = mSecurityInfo);
798
0
    return NS_OK;
799
0
}
800
801
NS_IMETHODIMP
802
nsJARChannel::GetContentType(nsACString &result)
803
0
{
804
0
    // If the Jar file has not been open yet,
805
0
    // We return application/x-unknown-content-type
806
0
    if (!mOpened) {
807
0
      result.AssignLiteral(UNKNOWN_CONTENT_TYPE);
808
0
      return NS_OK;
809
0
    }
810
0
811
0
    if (mContentType.IsEmpty()) {
812
0
813
0
        //
814
0
        // generate content type and set it
815
0
        //
816
0
        const char *ext = nullptr, *fileName = mJarEntry.get();
817
0
        int32_t len = mJarEntry.Length();
818
0
819
0
        // check if we're displaying a directory
820
0
        // mJarEntry will be empty if we're trying to display
821
0
        // the topmost directory in a zip, e.g. jar:foo.zip!/
822
0
        if (ENTRY_IS_DIRECTORY(mJarEntry)) {
823
0
            mContentType.AssignLiteral(APPLICATION_HTTP_INDEX_FORMAT);
824
0
        }
825
0
        else {
826
0
            // not a directory, take a guess by its extension
827
0
            for (int32_t i = len-1; i >= 0; i--) {
828
0
                if (fileName[i] == '.') {
829
0
                    ext = &fileName[i + 1];
830
0
                    break;
831
0
                }
832
0
            }
833
0
            if (ext) {
834
0
                nsIMIMEService *mimeServ = gJarHandler->MimeService();
835
0
                if (mimeServ)
836
0
                    mimeServ->GetTypeFromExtension(nsDependentCString(ext), mContentType);
837
0
            }
838
0
            if (mContentType.IsEmpty())
839
0
                mContentType.AssignLiteral(UNKNOWN_CONTENT_TYPE);
840
0
        }
841
0
    }
842
0
    result = mContentType;
843
0
    return NS_OK;
844
0
}
845
846
NS_IMETHODIMP
847
nsJARChannel::SetContentType(const nsACString &aContentType)
848
0
{
849
0
    // If someone gives us a type hint we should just use that type instead of
850
0
    // doing our guessing.  So we don't care when this is being called.
851
0
852
0
    // mContentCharset is unchanged if not parsed
853
0
    NS_ParseResponseContentType(aContentType, mContentType, mContentCharset);
854
0
    return NS_OK;
855
0
}
856
857
NS_IMETHODIMP
858
nsJARChannel::GetContentCharset(nsACString &aContentCharset)
859
0
{
860
0
    // If someone gives us a charset hint we should just use that charset.
861
0
    // So we don't care when this is being called.
862
0
    aContentCharset = mContentCharset;
863
0
    return NS_OK;
864
0
}
865
866
NS_IMETHODIMP
867
nsJARChannel::SetContentCharset(const nsACString &aContentCharset)
868
0
{
869
0
    mContentCharset = aContentCharset;
870
0
    return NS_OK;
871
0
}
872
873
NS_IMETHODIMP
874
nsJARChannel::GetContentDisposition(uint32_t *aContentDisposition)
875
0
{
876
0
    return NS_ERROR_NOT_AVAILABLE;
877
0
}
878
879
NS_IMETHODIMP
880
nsJARChannel::SetContentDisposition(uint32_t aContentDisposition)
881
0
{
882
0
    return NS_ERROR_NOT_AVAILABLE;
883
0
}
884
885
NS_IMETHODIMP
886
nsJARChannel::GetContentDispositionFilename(nsAString &aContentDispositionFilename)
887
0
{
888
0
    return NS_ERROR_NOT_AVAILABLE;
889
0
}
890
891
NS_IMETHODIMP
892
nsJARChannel::SetContentDispositionFilename(const nsAString &aContentDispositionFilename)
893
0
{
894
0
    return NS_ERROR_NOT_AVAILABLE;
895
0
}
896
897
NS_IMETHODIMP
898
nsJARChannel::GetContentDispositionHeader(nsACString &aContentDispositionHeader)
899
0
{
900
0
    return NS_ERROR_NOT_AVAILABLE;
901
0
}
902
903
NS_IMETHODIMP
904
nsJARChannel::GetContentLength(int64_t *result)
905
0
{
906
0
    *result = mContentLength;
907
0
    return NS_OK;
908
0
}
909
910
NS_IMETHODIMP
911
nsJARChannel::SetContentLength(int64_t aContentLength)
912
0
{
913
0
    // XXX does this really make any sense at all?
914
0
    mContentLength = aContentLength;
915
0
    return NS_OK;
916
0
}
917
918
NS_IMETHODIMP
919
nsJARChannel::Open(nsIInputStream **stream)
920
5
{
921
5
    LOG(("nsJARChannel::Open [this=%p]\n", this));
922
5
923
5
    NS_ENSURE_TRUE(!mOpened, NS_ERROR_IN_PROGRESS);
924
5
    NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS);
925
5
926
5
    mJarFile = nullptr;
927
5
928
5
    nsresult rv = LookupFile();
929
5
    if (NS_FAILED(rv))
930
5
        return rv;
931
5
932
5
    // If mJarFile was not set by LookupFile, we can't open a channel.
933
5
    if (!mJarFile) {
934
0
        MOZ_ASSERT_UNREACHABLE("only file-backed jars are supported");
935
0
        return NS_ERROR_NOT_IMPLEMENTED;
936
0
    }
937
5
938
5
    RefPtr<nsJARInputThunk> input;
939
5
    rv = CreateJarInput(gJarHandler->JarCache(), getter_AddRefs(input));
940
5
    if (NS_FAILED(rv))
941
5
        return rv;
942
5
943
5
    input.forget(stream);
944
5
    mOpened = true;
945
5
    return NS_OK;
946
5
}
947
948
NS_IMETHODIMP
949
nsJARChannel::Open2(nsIInputStream** aStream)
950
5
{
951
5
    LOG(("nsJARChannel::Open2 [this=%p]\n", this));
952
5
    nsCOMPtr<nsIStreamListener> listener;
953
5
    nsresult rv = nsContentSecurityManager::doContentSecurityCheck(this, listener);
954
5
    NS_ENSURE_SUCCESS(rv, rv);
955
5
    return Open(aStream);
956
5
}
957
958
NS_IMETHODIMP
959
nsJARChannel::AsyncOpen(nsIStreamListener *listener, nsISupports *ctx)
960
0
{
961
0
    LOG(("nsJARChannel::AsyncOpen [this=%p]\n", this));
962
0
    MOZ_ASSERT(!mLoadInfo ||
963
0
               mLoadInfo->GetSecurityMode() == 0 ||
964
0
               mLoadInfo->GetInitialSecurityCheckDone() ||
965
0
               (mLoadInfo->GetSecurityMode() == nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL &&
966
0
                nsContentUtils::IsSystemPrincipal(mLoadInfo->LoadingPrincipal())),
967
0
               "security flags in loadInfo but asyncOpen2() not called");
968
0
969
0
    NS_ENSURE_ARG_POINTER(listener);
970
0
    NS_ENSURE_TRUE(!mOpened, NS_ERROR_IN_PROGRESS);
971
0
    NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS);
972
0
973
0
    mJarFile = nullptr;
974
0
975
0
    // Initialize mProgressSink
976
0
    NS_QueryNotificationCallbacks(mCallbacks, mLoadGroup, mProgressSink);
977
0
978
0
    mListener = listener;
979
0
    mListenerContext = ctx;
980
0
    mIsPending = true;
981
0
982
0
    nsresult rv = LookupFile();
983
0
    if (NS_FAILED(rv) || !mJarFile) {
984
0
        // Not a local file...
985
0
        mIsPending = false;
986
0
        mListenerContext = nullptr;
987
0
        mListener = nullptr;
988
0
        mCallbacks = nullptr;
989
0
        mProgressSink = nullptr;
990
0
        return mJarFile ? rv : NS_ERROR_UNSAFE_CONTENT_TYPE;
991
0
    }
992
0
993
0
    rv = OpenLocalFile();
994
0
    if (NS_FAILED(rv)) {
995
0
        mIsPending = false;
996
0
        mListenerContext = nullptr;
997
0
        mListener = nullptr;
998
0
        mCallbacks = nullptr;
999
0
        mProgressSink = nullptr;
1000
0
        return rv;
1001
0
    }
1002
0
1003
0
    return NS_OK;
1004
0
}
1005
1006
NS_IMETHODIMP
1007
nsJARChannel::AsyncOpen2(nsIStreamListener *aListener)
1008
0
{
1009
0
    LOG(("nsJARChannel::AsyncOpen2 [this=%p]\n", this));
1010
0
    nsCOMPtr<nsIStreamListener> listener = aListener;
1011
0
    nsresult rv = nsContentSecurityManager::doContentSecurityCheck(this, listener);
1012
0
    if (NS_FAILED(rv)) {
1013
0
        mIsPending = false;
1014
0
        mListenerContext = nullptr;
1015
0
        mListener = nullptr;
1016
0
        mCallbacks = nullptr;
1017
0
        mProgressSink = nullptr;
1018
0
        return rv;
1019
0
    }
1020
0
1021
0
    return AsyncOpen(listener, nullptr);
1022
0
}
1023
1024
//-----------------------------------------------------------------------------
1025
// nsIJARChannel
1026
//-----------------------------------------------------------------------------
1027
NS_IMETHODIMP
1028
nsJARChannel::GetJarFile(nsIFile **aFile)
1029
0
{
1030
0
    NS_IF_ADDREF(*aFile = mJarFile);
1031
0
    return NS_OK;
1032
0
}
1033
1034
NS_IMETHODIMP
1035
nsJARChannel::SetJarFile(nsIFile *aFile)
1036
0
{
1037
0
    if (mOpened) {
1038
0
        return NS_ERROR_IN_PROGRESS;
1039
0
    }
1040
0
    mJarFileOverride = aFile;
1041
0
    return NS_OK;
1042
0
}
1043
1044
NS_IMETHODIMP
1045
nsJARChannel::EnsureCached(bool *aIsCached)
1046
0
{
1047
0
    nsresult rv;
1048
0
    *aIsCached = false;
1049
0
1050
0
    if (mOpened) {
1051
0
        return NS_ERROR_ALREADY_OPENED;
1052
0
    }
1053
0
1054
0
    if (mPreCachedJarReader) {
1055
0
        // We've already been called and found the JAR is cached
1056
0
        *aIsCached = true;
1057
0
        return NS_OK;
1058
0
    }
1059
0
1060
0
    nsCOMPtr<nsIURI> innerFileURI;
1061
0
    rv = mJarURI->GetJARFile(getter_AddRefs(innerFileURI));
1062
0
    NS_ENSURE_SUCCESS(rv, rv);
1063
0
1064
0
    nsCOMPtr<nsIFileURL> innerFileURL = do_QueryInterface(innerFileURI, &rv);
1065
0
    NS_ENSURE_SUCCESS(rv, rv);
1066
0
1067
0
    nsCOMPtr<nsIFile> jarFile;
1068
0
    rv = innerFileURL->GetFile(getter_AddRefs(jarFile));
1069
0
    NS_ENSURE_SUCCESS(rv, rv);
1070
0
1071
0
    nsCOMPtr<nsIIOService> ioService = do_GetIOService(&rv);
1072
0
    NS_ENSURE_SUCCESS(rv, rv);
1073
0
1074
0
    nsCOMPtr<nsIProtocolHandler> handler;
1075
0
    rv = ioService->GetProtocolHandler("jar", getter_AddRefs(handler));
1076
0
    NS_ENSURE_SUCCESS(rv, rv);
1077
0
1078
0
    nsCOMPtr<nsIJARProtocolHandler> jarHandler = do_QueryInterface(handler);
1079
0
    MOZ_ASSERT(jarHandler);
1080
0
1081
0
    nsCOMPtr<nsIZipReaderCache> jarCache;
1082
0
    rv = jarHandler->GetJARCache(getter_AddRefs(jarCache));
1083
0
    NS_ENSURE_SUCCESS(rv, rv);
1084
0
1085
0
    rv = jarCache->GetZipIfCached(jarFile, getter_AddRefs(mPreCachedJarReader));
1086
0
    if (rv == NS_ERROR_CACHE_KEY_NOT_FOUND) {
1087
0
        return NS_OK;
1088
0
    }
1089
0
    NS_ENSURE_SUCCESS(rv, rv);
1090
0
1091
0
    *aIsCached = true;
1092
0
    return NS_OK;
1093
0
}
1094
1095
NS_IMETHODIMP
1096
nsJARChannel::GetZipEntry(nsIZipEntry **aZipEntry)
1097
0
{
1098
0
    nsresult rv = LookupFile();
1099
0
    if (NS_FAILED(rv))
1100
0
        return rv;
1101
0
1102
0
    if (!mJarFile)
1103
0
        return NS_ERROR_NOT_AVAILABLE;
1104
0
1105
0
    nsCOMPtr<nsIZipReader> reader;
1106
0
    rv = gJarHandler->JarCache()->GetZip(mJarFile, getter_AddRefs(reader));
1107
0
    if (NS_FAILED(rv))
1108
0
        return rv;
1109
0
1110
0
    return reader->GetEntry(mJarEntry, aZipEntry);
1111
0
}
1112
1113
//-----------------------------------------------------------------------------
1114
// nsIStreamListener
1115
//-----------------------------------------------------------------------------
1116
1117
NS_IMETHODIMP
1118
nsJARChannel::OnStartRequest(nsIRequest *req, nsISupports *ctx)
1119
0
{
1120
0
    LOG(("nsJARChannel::OnStartRequest [this=%p %s]\n", this, mSpec.get()));
1121
0
1122
0
    mRequest = req;
1123
0
    nsresult rv = mListener->OnStartRequest(this, mListenerContext);
1124
0
    mRequest = nullptr;
1125
0
    NS_ENSURE_SUCCESS(rv, rv);
1126
0
1127
0
    // Restrict loadable content types.
1128
0
    nsAutoCString contentType;
1129
0
    GetContentType(contentType);
1130
0
    auto contentPolicyType = mLoadInfo->GetExternalContentPolicyType();
1131
0
    if (contentType.Equals(APPLICATION_HTTP_INDEX_FORMAT) &&
1132
0
        contentPolicyType != nsIContentPolicy::TYPE_DOCUMENT &&
1133
0
        contentPolicyType != nsIContentPolicy::TYPE_FETCH) {
1134
0
      return NS_ERROR_CORRUPTED_CONTENT;
1135
0
    }
1136
0
    if (contentPolicyType == nsIContentPolicy::TYPE_STYLESHEET &&
1137
0
        !contentType.EqualsLiteral(TEXT_CSS)) {
1138
0
      return NS_ERROR_CORRUPTED_CONTENT;
1139
0
    }
1140
0
    if (contentPolicyType == nsIContentPolicy::TYPE_SCRIPT &&
1141
0
        !nsContentUtils::IsJavascriptMIMEType(NS_ConvertUTF8toUTF16(contentType))) {
1142
0
      return NS_ERROR_CORRUPTED_CONTENT;
1143
0
    }
1144
0
1145
0
    return rv;
1146
0
}
1147
1148
NS_IMETHODIMP
1149
nsJARChannel::OnStopRequest(nsIRequest *req, nsISupports *ctx, nsresult status)
1150
0
{
1151
0
    LOG(("nsJARChannel::OnStopRequest [this=%p %s status=%" PRIx32 "]\n",
1152
0
         this, mSpec.get(), static_cast<uint32_t>(status)));
1153
0
1154
0
    if (NS_SUCCEEDED(mStatus))
1155
0
        mStatus = status;
1156
0
1157
0
    if (mListener) {
1158
0
        mListener->OnStopRequest(this, mListenerContext, status);
1159
0
        mListener = nullptr;
1160
0
        mListenerContext = nullptr;
1161
0
    }
1162
0
1163
0
    if (mLoadGroup)
1164
0
        mLoadGroup->RemoveRequest(this, nullptr, status);
1165
0
1166
0
    mPump = nullptr;
1167
0
    mIsPending = false;
1168
0
1169
0
    // Drop notification callbacks to prevent cycles.
1170
0
    mCallbacks = nullptr;
1171
0
    mProgressSink = nullptr;
1172
0
1173
    #if defined(XP_WIN) || defined(MOZ_WIDGET_COCOA)
1174
    #else
1175
    // To deallocate file descriptor by RemoteOpenFileChild destructor.
1176
0
    mJarFile = nullptr;
1177
0
    #endif
1178
0
1179
0
    return NS_OK;
1180
0
}
1181
1182
NS_IMETHODIMP
1183
nsJARChannel::OnDataAvailable(nsIRequest *req, nsISupports *ctx,
1184
                               nsIInputStream *stream,
1185
                               uint64_t offset, uint32_t count)
1186
0
{
1187
0
    LOG(("nsJARChannel::OnDataAvailable [this=%p %s]\n", this, mSpec.get()));
1188
0
1189
0
    nsresult rv;
1190
0
1191
0
    rv = mListener->OnDataAvailable(this, mListenerContext, stream, offset, count);
1192
0
1193
0
    // simply report progress here instead of hooking ourselves up as a
1194
0
    // nsITransportEventSink implementation.
1195
0
    // XXX do the 64-bit stuff for real
1196
0
    if (mProgressSink && NS_SUCCEEDED(rv)) {
1197
0
        if (NS_IsMainThread()) {
1198
0
            FireOnProgress(offset + count);
1199
0
        } else {
1200
0
            NS_DispatchToMainThread(NewRunnableMethod
1201
0
                                    <uint64_t>("nsJARChannel::FireOnProgress",
1202
0
                                               this,
1203
0
                                               &nsJARChannel::FireOnProgress,
1204
0
                                               offset + count));
1205
0
        }
1206
0
    }
1207
0
1208
0
    return rv; // let the pump cancel on failure
1209
0
}
1210
1211
NS_IMETHODIMP
1212
nsJARChannel::RetargetDeliveryTo(nsIEventTarget* aEventTarget)
1213
0
{
1214
0
  MOZ_ASSERT(NS_IsMainThread());
1215
0
1216
0
  nsCOMPtr<nsIThreadRetargetableRequest> request = do_QueryInterface(mRequest);
1217
0
  if (!request) {
1218
0
    return NS_ERROR_NO_INTERFACE;
1219
0
  }
1220
0
1221
0
  return request->RetargetDeliveryTo(aEventTarget);
1222
0
}
1223
1224
NS_IMETHODIMP
1225
nsJARChannel::GetDeliveryTarget(nsIEventTarget** aEventTarget)
1226
0
{
1227
0
  MOZ_ASSERT(NS_IsMainThread());
1228
0
1229
0
  nsCOMPtr<nsIThreadRetargetableRequest> request = do_QueryInterface(mRequest);
1230
0
  if (!request) {
1231
0
    return NS_ERROR_NO_INTERFACE;
1232
0
  }
1233
0
1234
0
  return request->GetDeliveryTarget(aEventTarget);
1235
0
}
1236
1237
NS_IMETHODIMP
1238
nsJARChannel::CheckListenerChain()
1239
0
{
1240
0
  MOZ_ASSERT(NS_IsMainThread());
1241
0
1242
0
  nsCOMPtr<nsIThreadRetargetableStreamListener> listener =
1243
0
    do_QueryInterface(mListener);
1244
0
  if (!listener) {
1245
0
    return NS_ERROR_NO_INTERFACE;
1246
0
  }
1247
0
1248
0
  return listener->CheckListenerChain();
1249
0
}