Source code

Revision control

Other Tools

1
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode:nil; c-basic-offset: 2 -*- */
2
/* vim:set ts=2 sts=2 sw=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/. */
6
7
#include "nsURILoader.h"
8
#include "nsAutoPtr.h"
9
#include "nsIURIContentListener.h"
10
#include "nsIContentHandler.h"
11
#include "nsILoadGroup.h"
12
#include "nsIDocumentLoader.h"
13
#include "nsIWebProgress.h"
14
#include "nsIWebProgressListener.h"
15
#include "nsIIOService.h"
16
#include "nsIServiceManager.h"
17
#include "nsIStreamListener.h"
18
#include "nsIURI.h"
19
#include "nsIChannel.h"
20
#include "nsIInterfaceRequestor.h"
21
#include "nsIInterfaceRequestorUtils.h"
22
#include "nsIProgressEventSink.h"
23
#include "nsIInputStream.h"
24
#include "nsIStreamConverterService.h"
25
#include "nsIWeakReferenceUtils.h"
26
#include "nsIHttpChannel.h"
27
#include "nsIMultiPartChannel.h"
28
#include "netCore.h"
29
#include "nsCRT.h"
30
#include "nsIDocShell.h"
31
#include "nsIDocShellTreeItem.h"
32
#include "nsIDocShellTreeOwner.h"
33
#include "nsIThreadRetargetableStreamListener.h"
34
#include "nsIChildChannel.h"
35
36
#include "nsString.h"
37
#include "nsThreadUtils.h"
38
#include "nsReadableUtils.h"
39
#include "nsError.h"
40
41
#include "nsICategoryManager.h"
42
#include "nsCExternalHandlerService.h"
43
44
#include "nsIMIMEHeaderParam.h"
45
#include "nsNetCID.h"
46
47
#include "nsMimeTypes.h"
48
49
#include "nsDocLoader.h"
50
#include "mozilla/Attributes.h"
51
#include "mozilla/IntegerPrintfMacros.h"
52
#include "mozilla/Preferences.h"
53
#include "mozilla/Unused.h"
54
#include "mozilla/StaticPrefs_dom.h"
55
#include "nsContentUtils.h"
56
57
mozilla::LazyLogModule nsURILoader::mLog("URILoader");
58
59
#define LOG(args) MOZ_LOG(nsURILoader::mLog, mozilla::LogLevel::Debug, args)
60
#define LOG_ERROR(args) \
61
MOZ_LOG(nsURILoader::mLog, mozilla::LogLevel::Error, args)
62
#define LOG_ENABLED() MOZ_LOG_TEST(nsURILoader::mLog, mozilla::LogLevel::Debug)
63
64
#define NS_PREF_DISABLE_BACKGROUND_HANDLING \
65
"security.exthelperapp.disable_background_handling"
66
67
static uint32_t sConvertDataLimit = 20;
68
69
static bool InitPreferences() {
70
mozilla::Preferences::AddUintVarCache(
71
&sConvertDataLimit, "general.document_open_conversion_depth_limit", 20);
72
return true;
73
}
74
75
/**
76
* The nsDocumentOpenInfo contains the state required when a single
77
* document is being opened in order to discover the content type...
78
* Each instance remains alive until its target URL has been loaded
79
* (or aborted).
80
*/
81
class nsDocumentOpenInfo final : public nsIStreamListener,
82
public nsIThreadRetargetableStreamListener {
83
public:
84
// Real constructor
85
// aFlags is a combination of the flags on nsIURILoader
86
nsDocumentOpenInfo(nsIInterfaceRequestor* aWindowContext, uint32_t aFlags,
87
nsURILoader* aURILoader);
88
89
NS_DECL_THREADSAFE_ISUPPORTS
90
91
/**
92
* Prepares this object for receiving data. The stream
93
* listener methods of this class must not be called before calling this
94
* method.
95
*/
96
nsresult Prepare();
97
98
// Call this (from OnStartRequest) to attempt to find an nsIStreamListener to
99
// take the data off our hands.
100
nsresult DispatchContent(nsIRequest* request, nsISupports* aCtxt);
101
102
// Call this if we need to insert a stream converter from aSrcContentType to
103
// aOutContentType into the StreamListener chain. DO NOT call it if the two
104
// types are the same, since no conversion is needed in that case.
105
nsresult ConvertData(nsIRequest* request, nsIURIContentListener* aListener,
106
const nsACString& aSrcContentType,
107
const nsACString& aOutContentType);
108
109
/**
110
* Function to attempt to use aListener to handle the load. If
111
* true is returned, nothing else needs to be done; if false
112
* is returned, then a different way of handling the load should be
113
* tried.
114
*/
115
bool TryContentListener(nsIURIContentListener* aListener,
116
nsIChannel* aChannel);
117
118
// nsIRequestObserver methods:
119
NS_DECL_NSIREQUESTOBSERVER
120
121
// nsIStreamListener methods:
122
NS_DECL_NSISTREAMLISTENER
123
124
// nsIThreadRetargetableStreamListener
125
NS_DECL_NSITHREADRETARGETABLESTREAMLISTENER
126
protected:
127
~nsDocumentOpenInfo();
128
129
protected:
130
/**
131
* The first content listener to try dispatching data to. Typically
132
* the listener associated with the entity that originated the load.
133
*/
134
nsCOMPtr<nsIURIContentListener> m_contentListener;
135
136
/**
137
* The stream listener to forward nsIStreamListener notifications
138
* to. This is set once the load is dispatched.
139
*/
140
nsCOMPtr<nsIStreamListener> m_targetStreamListener;
141
142
/**
143
* A pointer to the entity that originated the load. We depend on getting
144
* things like nsIURIContentListeners, nsIDOMWindows, etc off of it.
145
*/
146
nsCOMPtr<nsIInterfaceRequestor> m_originalContext;
147
148
/**
149
* IS_CONTENT_PREFERRED is used for the boolean to pass to CanHandleContent
150
* (also determines whether we use CanHandleContent or IsPreferred).
151
* DONT_RETARGET means that we will only try m_originalContext, no other
152
* listeners.
153
*/
154
uint32_t mFlags;
155
156
/**
157
* The type of the data we will be trying to dispatch.
158
*/
159
nsCString mContentType;
160
161
/**
162
* Reference to the URILoader service so we can access its list of
163
* nsIURIContentListeners.
164
*/
165
RefPtr<nsURILoader> mURILoader;
166
167
/**
168
* Limit of data conversion depth to prevent infinite conversion loops
169
*/
170
uint32_t mDataConversionDepthLimit;
171
};
172
173
NS_IMPL_ADDREF(nsDocumentOpenInfo)
174
NS_IMPL_RELEASE(nsDocumentOpenInfo)
175
176
NS_INTERFACE_MAP_BEGIN(nsDocumentOpenInfo)
177
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIRequestObserver)
178
NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
179
NS_INTERFACE_MAP_ENTRY(nsIStreamListener)
180
NS_INTERFACE_MAP_ENTRY(nsIThreadRetargetableStreamListener)
181
NS_INTERFACE_MAP_END
182
183
nsDocumentOpenInfo::nsDocumentOpenInfo(nsIInterfaceRequestor* aWindowContext,
184
uint32_t aFlags, nsURILoader* aURILoader)
185
: m_originalContext(aWindowContext),
186
mFlags(aFlags),
187
mURILoader(aURILoader),
188
mDataConversionDepthLimit(sConvertDataLimit) {}
189
190
nsDocumentOpenInfo::~nsDocumentOpenInfo() {}
191
192
nsresult nsDocumentOpenInfo::Prepare() {
193
LOG(("[0x%p] nsDocumentOpenInfo::Prepare", this));
194
195
nsresult rv;
196
197
// ask our window context if it has a uri content listener...
198
m_contentListener = do_GetInterface(m_originalContext, &rv);
199
return rv;
200
}
201
202
NS_IMETHODIMP nsDocumentOpenInfo::OnStartRequest(nsIRequest* request) {
203
LOG(("[0x%p] nsDocumentOpenInfo::OnStartRequest", this));
204
MOZ_ASSERT(request);
205
if (!request) {
206
return NS_ERROR_UNEXPECTED;
207
}
208
209
nsresult rv = NS_OK;
210
211
//
212
// Deal with "special" HTTP responses:
213
//
214
// - In the case of a 204 (No Content) or 205 (Reset Content) response, do
215
// not try to find a content handler. Return NS_BINDING_ABORTED to cancel
216
// the request. This has the effect of ensuring that the DocLoader does
217
// not try to interpret this as a real request.
218
//
219
nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(request, &rv));
220
221
if (NS_SUCCEEDED(rv)) {
222
uint32_t responseCode = 0;
223
224
rv = httpChannel->GetResponseStatus(&responseCode);
225
226
if (NS_FAILED(rv)) {
227
LOG_ERROR((" Failed to get HTTP response status"));
228
229
// behave as in the canceled case
230
return NS_OK;
231
}
232
233
LOG((" HTTP response status: %d", responseCode));
234
235
if (204 == responseCode || 205 == responseCode) {
236
return NS_BINDING_ABORTED;
237
}
238
239
static bool sLargeAllocationHeaderEnabled = false;
240
static bool sCachedLargeAllocationPref = false;
241
if (!sCachedLargeAllocationPref) {
242
sCachedLargeAllocationPref = true;
243
mozilla::Preferences::AddBoolVarCache(
244
&sLargeAllocationHeaderEnabled, "dom.largeAllocationHeader.enabled");
245
}
246
247
if (sLargeAllocationHeaderEnabled) {
248
if (StaticPrefs::dom_largeAllocation_testing_allHttpLoads()) {
249
nsCOMPtr<nsIURI> uri;
250
rv = httpChannel->GetURI(getter_AddRefs(uri));
251
if (NS_SUCCEEDED(rv) && uri) {
252
if ((uri->SchemeIs("http") || uri->SchemeIs("https")) &&
253
nsContentUtils::AttemptLargeAllocationLoad(httpChannel)) {
254
return NS_BINDING_ABORTED;
255
}
256
}
257
}
258
259
// If we have a Large-Allocation header, let's check if we should perform
260
// a process switch.
261
nsAutoCString largeAllocationHeader;
262
rv = httpChannel->GetResponseHeader(
263
NS_LITERAL_CSTRING("Large-Allocation"), largeAllocationHeader);
264
if (NS_SUCCEEDED(rv) &&
265
nsContentUtils::AttemptLargeAllocationLoad(httpChannel)) {
266
return NS_BINDING_ABORTED;
267
}
268
}
269
}
270
271
//
272
// Make sure that the transaction has succeeded, so far...
273
//
274
nsresult status;
275
276
rv = request->GetStatus(&status);
277
278
NS_ASSERTION(NS_SUCCEEDED(rv), "Unable to get request status!");
279
if (NS_FAILED(rv)) return rv;
280
281
if (NS_FAILED(status)) {
282
LOG_ERROR(
283
(" Request failed, status: 0x%08" PRIX32, static_cast<uint32_t>(rv)));
284
285
//
286
// The transaction has already reported an error - so it will be torn
287
// down. Therefore, it is not necessary to return an error code...
288
//
289
return NS_OK;
290
}
291
292
rv = DispatchContent(request, nullptr);
293
294
LOG((" After dispatch, m_targetStreamListener: 0x%p, rv: 0x%08" PRIX32,
295
m_targetStreamListener.get(), static_cast<uint32_t>(rv)));
296
297
NS_ASSERTION(
298
NS_SUCCEEDED(rv) || !m_targetStreamListener,
299
"Must not have an m_targetStreamListener with a failure return!");
300
301
NS_ENSURE_SUCCESS(rv, rv);
302
303
if (m_targetStreamListener)
304
rv = m_targetStreamListener->OnStartRequest(request);
305
306
LOG((" OnStartRequest returning: 0x%08" PRIX32, static_cast<uint32_t>(rv)));
307
308
return rv;
309
}
310
311
NS_IMETHODIMP
312
nsDocumentOpenInfo::CheckListenerChain() {
313
NS_ASSERTION(NS_IsMainThread(), "Should be on the main thread!");
314
nsresult rv = NS_OK;
315
nsCOMPtr<nsIThreadRetargetableStreamListener> retargetableListener =
316
do_QueryInterface(m_targetStreamListener, &rv);
317
if (retargetableListener) {
318
rv = retargetableListener->CheckListenerChain();
319
}
320
LOG(
321
("[0x%p] nsDocumentOpenInfo::CheckListenerChain %s listener %p rv "
322
"%" PRIx32,
323
this, (NS_SUCCEEDED(rv) ? "success" : "failure"),
324
(nsIStreamListener*)m_targetStreamListener, static_cast<uint32_t>(rv)));
325
return rv;
326
}
327
328
NS_IMETHODIMP
329
nsDocumentOpenInfo::OnDataAvailable(nsIRequest* request, nsIInputStream* inStr,
330
uint64_t sourceOffset, uint32_t count) {
331
// if we have retarged to the end stream listener, then forward the call....
332
// otherwise, don't do anything
333
334
nsresult rv = NS_OK;
335
336
if (m_targetStreamListener)
337
rv = m_targetStreamListener->OnDataAvailable(request, inStr, sourceOffset,
338
count);
339
return rv;
340
}
341
342
NS_IMETHODIMP nsDocumentOpenInfo::OnStopRequest(nsIRequest* request,
343
nsresult aStatus) {
344
LOG(("[0x%p] nsDocumentOpenInfo::OnStopRequest", this));
345
346
if (m_targetStreamListener) {
347
nsCOMPtr<nsIStreamListener> listener(m_targetStreamListener);
348
349
// If this is a multipart stream, we could get another
350
// OnStartRequest after this... reset state.
351
m_targetStreamListener = nullptr;
352
mContentType.Truncate();
353
listener->OnStopRequest(request, aStatus);
354
}
355
356
// Remember...
357
// In the case of multiplexed streams (such as multipart/x-mixed-replace)
358
// these stream listener methods could be called again :-)
359
//
360
return NS_OK;
361
}
362
363
nsresult nsDocumentOpenInfo::DispatchContent(nsIRequest* request,
364
nsISupports* aCtxt) {
365
LOG(("[0x%p] nsDocumentOpenInfo::DispatchContent for type '%s'", this,
366
mContentType.get()));
367
368
MOZ_ASSERT(!m_targetStreamListener,
369
"Why do we already have a target stream listener?");
370
371
nsresult rv;
372
nsCOMPtr<nsIChannel> aChannel = do_QueryInterface(request);
373
if (!aChannel) {
374
LOG_ERROR((" Request is not a channel. Bailing."));
375
return NS_ERROR_FAILURE;
376
}
377
378
NS_NAMED_LITERAL_CSTRING(anyType, "*/*");
379
if (mContentType.IsEmpty() || mContentType == anyType) {
380
rv = aChannel->GetContentType(mContentType);
381
if (NS_FAILED(rv)) return rv;
382
LOG((" Got type from channel: '%s'", mContentType.get()));
383
}
384
385
bool isGuessFromExt =
386
mContentType.LowerCaseEqualsASCII(APPLICATION_GUESS_FROM_EXT);
387
if (isGuessFromExt) {
388
// Reset to application/octet-stream for now; no one other than the
389
// external helper app service should see APPLICATION_GUESS_FROM_EXT.
390
mContentType = APPLICATION_OCTET_STREAM;
391
aChannel->SetContentType(NS_LITERAL_CSTRING(APPLICATION_OCTET_STREAM));
392
}
393
394
// Check whether the data should be forced to be handled externally. This
395
// could happen because the Content-Disposition header is set so, or, in the
396
// future, because the user has specified external handling for the MIME
397
// type.
398
bool forceExternalHandling = false;
399
uint32_t disposition;
400
rv = aChannel->GetContentDisposition(&disposition);
401
402
if (NS_SUCCEEDED(rv) && disposition == nsIChannel::DISPOSITION_ATTACHMENT) {
403
forceExternalHandling = true;
404
}
405
406
LOG((" forceExternalHandling: %s", forceExternalHandling ? "yes" : "no"));
407
408
if (!forceExternalHandling) {
409
//
410
// First step: See whether m_contentListener wants to handle this
411
// content type.
412
//
413
if (m_contentListener && TryContentListener(m_contentListener, aChannel)) {
414
LOG((" Success! Our default listener likes this type"));
415
// All done here
416
return NS_OK;
417
}
418
419
// If we aren't allowed to try other listeners, just skip through to
420
// trying to convert the data.
421
if (!(mFlags & nsIURILoader::DONT_RETARGET)) {
422
//
423
// Second step: See whether some other registered listener wants
424
// to handle this content type.
425
//
426
int32_t count = mURILoader->m_listeners.Count();
427
nsCOMPtr<nsIURIContentListener> listener;
428
for (int32_t i = 0; i < count; i++) {
429
listener = do_QueryReferent(mURILoader->m_listeners[i]);
430
if (listener) {
431
if (TryContentListener(listener, aChannel)) {
432
LOG((" Found listener registered on the URILoader"));
433
return NS_OK;
434
}
435
} else {
436
// remove from the listener list, reset i and update count
437
mURILoader->m_listeners.RemoveObjectAt(i--);
438
--count;
439
}
440
}
441
442
//
443
// Third step: Try to find a content listener that has not yet had
444
// the chance to register, as it is contained in a not-yet-loaded
445
// module, but which has registered a contract ID.
446
//
447
nsCOMPtr<nsICategoryManager> catman =
448
do_GetService(NS_CATEGORYMANAGER_CONTRACTID);
449
if (catman) {
450
nsCString contractidString;
451
rv = catman->GetCategoryEntry(NS_CONTENT_LISTENER_CATEGORYMANAGER_ENTRY,
452
mContentType, contractidString);
453
if (NS_SUCCEEDED(rv) && !contractidString.IsEmpty()) {
454
LOG((" Listener contractid for '%s' is '%s'", mContentType.get(),
455
contractidString.get()));
456
457
listener = do_CreateInstance(contractidString.get());
458
LOG((" Listener from category manager: 0x%p", listener.get()));
459
460
if (listener && TryContentListener(listener, aChannel)) {
461
LOG((" Listener from category manager likes this type"));
462
return NS_OK;
463
}
464
}
465
}
466
467
//
468
// Fourth step: try to find an nsIContentHandler for our type.
469
//
470
nsAutoCString handlerContractID(NS_CONTENT_HANDLER_CONTRACTID_PREFIX);
471
handlerContractID += mContentType;
472
473
nsCOMPtr<nsIContentHandler> contentHandler =
474
do_CreateInstance(handlerContractID.get());
475
if (contentHandler) {
476
LOG((" Content handler found"));
477
rv = contentHandler->HandleContent(mContentType.get(),
478
m_originalContext, request);
479
// XXXbz returning an error code to represent handling the
480
// content is just bizarre!
481
if (rv != NS_ERROR_WONT_HANDLE_CONTENT) {
482
if (NS_FAILED(rv)) {
483
// The content handler has unexpectedly failed. Cancel the request
484
// just in case the handler didn't...
485
LOG((" Content handler failed. Aborting load"));
486
request->Cancel(rv);
487
} else {
488
LOG((" Content handler taking over load"));
489
}
490
491
return rv;
492
}
493
}
494
} else {
495
LOG(
496
(" DONT_RETARGET flag set, so skipped over random other content "
497
"listeners and content handlers"));
498
}
499
500
//
501
// Fifth step: If no listener prefers this type, see if any stream
502
// converters exist to transform this content type into
503
// some other.
504
//
505
// Don't do this if the server sent us a MIME type of "*/*" because they saw
506
// it in our Accept header and got confused.
507
// XXXbz have to be careful here; may end up in some sort of bizarre
508
// infinite decoding loop.
509
if (mContentType != anyType) {
510
rv = ConvertData(request, m_contentListener, mContentType, anyType);
511
if (NS_FAILED(rv)) {
512
m_targetStreamListener = nullptr;
513
} else if (m_targetStreamListener) {
514
// We found a converter for this MIME type. We'll just pump data into
515
// it and let the downstream nsDocumentOpenInfo handle things.
516
LOG((" Converter taking over now"));
517
return NS_OK;
518
}
519
}
520
}
521
522
NS_ASSERTION(!m_targetStreamListener,
523
"If we found a listener, why are we not using it?");
524
525
if (mFlags & nsIURILoader::DONT_RETARGET) {
526
LOG(
527
(" External handling forced or (listener not interested and no "
528
"stream converter exists), and retargeting disallowed -> aborting"));
529
return NS_ERROR_WONT_HANDLE_CONTENT;
530
}
531
532
// Before dispatching to the external helper app service, check for an HTTP
533
// error page. If we got one, we don't want to handle it with a helper app,
534
// really.
535
nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(request));
536
if (httpChannel) {
537
bool requestSucceeded;
538
rv = httpChannel->GetRequestSucceeded(&requestSucceeded);
539
if (NS_FAILED(rv) || !requestSucceeded) {
540
// returning error from OnStartRequest will cancel the channel
541
return NS_ERROR_FILE_NOT_FOUND;
542
}
543
}
544
545
// Sixth step:
546
//
547
// All attempts to dispatch this content have failed. Just pass it off to
548
// the helper app service.
549
//
550
551
//
552
// Optionally, we may want to disable background handling by the external
553
// helper application service.
554
//
555
if (mozilla::Preferences::GetBool(NS_PREF_DISABLE_BACKGROUND_HANDLING,
556
false)) {
557
// First, we will ensure that the parent docshell is in an active
558
// state as we will disallow all external application handling unless it is
559
// in the foreground.
560
nsCOMPtr<nsIDocShell> docShell(do_GetInterface(m_originalContext));
561
if (!docShell) {
562
// If we can't perform our security check we definitely don't want to go
563
// any further!
564
LOG(
565
("Failed to get DocShell to ensure it is active before anding off to "
566
"helper app service. Aborting."));
567
return NS_ERROR_FAILURE;
568
}
569
570
// Ensure the DocShell is active before continuing.
571
bool isActive = false;
572
docShell->GetIsActive(&isActive);
573
if (!isActive) {
574
LOG(
575
(" Check for active DocShell returned false. Aborting hand off to "
576
"helper app service."));
577
return NS_ERROR_DOM_SECURITY_ERR;
578
}
579
}
580
581
nsCOMPtr<nsIExternalHelperAppService> helperAppService =
582
do_GetService(NS_EXTERNALHELPERAPPSERVICE_CONTRACTID, &rv);
583
if (helperAppService) {
584
LOG((" Passing load off to helper app service"));
585
586
// Set these flags to indicate that the channel has been targeted and that
587
// we are not using the original consumer.
588
nsLoadFlags loadFlags = 0;
589
request->GetLoadFlags(&loadFlags);
590
request->SetLoadFlags(loadFlags | nsIChannel::LOAD_RETARGETED_DOCUMENT_URI |
591
nsIChannel::LOAD_TARGETED);
592
593
if (isGuessFromExt) {
594
mContentType = APPLICATION_GUESS_FROM_EXT;
595
aChannel->SetContentType(NS_LITERAL_CSTRING(APPLICATION_GUESS_FROM_EXT));
596
}
597
598
rv = helperAppService->DoContent(mContentType, request, m_originalContext,
599
false, nullptr,
600
getter_AddRefs(m_targetStreamListener));
601
if (NS_FAILED(rv)) {
602
request->SetLoadFlags(loadFlags);
603
m_targetStreamListener = nullptr;
604
}
605
}
606
607
NS_ASSERTION(m_targetStreamListener || NS_FAILED(rv),
608
"There is no way we should be successful at this point without "
609
"a m_targetStreamListener");
610
return rv;
611
}
612
613
nsresult nsDocumentOpenInfo::ConvertData(nsIRequest* request,
614
nsIURIContentListener* aListener,
615
const nsACString& aSrcContentType,
616
const nsACString& aOutContentType) {
617
LOG(("[0x%p] nsDocumentOpenInfo::ConvertData from '%s' to '%s'", this,
618
PromiseFlatCString(aSrcContentType).get(),
619
PromiseFlatCString(aOutContentType).get()));
620
621
if (mDataConversionDepthLimit == 0) {
622
LOG((
623
"[0x%p] nsDocumentOpenInfo::ConvertData - reached the recursion limit!",
624
this));
625
// This will fall back to external helper app handling.
626
return NS_ERROR_ABORT;
627
}
628
629
MOZ_ASSERT(aSrcContentType != aOutContentType,
630
"ConvertData called when the two types are the same!");
631
632
nsresult rv = NS_OK;
633
634
nsCOMPtr<nsIStreamConverterService> StreamConvService =
635
do_GetService(NS_STREAMCONVERTERSERVICE_CONTRACTID, &rv);
636
if (NS_FAILED(rv)) return rv;
637
638
LOG((" Got converter service"));
639
640
// When applying stream decoders, it is necessary to "insert" an
641
// intermediate nsDocumentOpenInfo instance to handle the targeting of
642
// the "final" stream or streams.
643
//
644
// For certain content types (ie. multi-part/x-mixed-replace) the input
645
// stream is split up into multiple destination streams. This
646
// intermediate instance is used to target these "decoded" streams...
647
//
648
RefPtr<nsDocumentOpenInfo> nextLink =
649
new nsDocumentOpenInfo(m_originalContext, mFlags, mURILoader);
650
651
LOG((" Downstream DocumentOpenInfo would be: 0x%p", nextLink.get()));
652
653
// Decrease the conversion recursion limit by one to prevent infinite loops.
654
nextLink->mDataConversionDepthLimit = mDataConversionDepthLimit - 1;
655
656
// Make sure nextLink starts with the contentListener that said it wanted the
657
// results of this decode.
658
nextLink->m_contentListener = aListener;
659
// Also make sure it has to look for a stream listener to pump data into.
660
nextLink->m_targetStreamListener = nullptr;
661
662
// Make sure that nextLink treats the data as aOutContentType when
663
// dispatching; that way even if the stream converters don't change the type
664
// on the channel we will still do the right thing. If aOutContentType is
665
// */*, that's OK -- that will just indicate to nextLink that it should get
666
// the type off the channel.
667
nextLink->mContentType = aOutContentType;
668
669
// The following call sets m_targetStreamListener to the input end of the
670
// stream converter and sets the output end of the stream converter to
671
// nextLink. As we pump data into m_targetStreamListener the stream
672
// converter will convert it and pass the converted data to nextLink.
673
return StreamConvService->AsyncConvertData(
674
PromiseFlatCString(aSrcContentType).get(),
675
PromiseFlatCString(aOutContentType).get(), nextLink, request,
676
getter_AddRefs(m_targetStreamListener));
677
}
678
679
bool nsDocumentOpenInfo::TryContentListener(nsIURIContentListener* aListener,
680
nsIChannel* aChannel) {
681
LOG(("[0x%p] nsDocumentOpenInfo::TryContentListener; mFlags = 0x%x", this,
682
mFlags));
683
684
MOZ_ASSERT(aListener, "Must have a non-null listener");
685
MOZ_ASSERT(aChannel, "Must have a channel");
686
687
bool listenerWantsContent = false;
688
nsCString typeToUse;
689
690
if (mFlags & nsIURILoader::IS_CONTENT_PREFERRED) {
691
aListener->IsPreferred(mContentType.get(), getter_Copies(typeToUse),
692
&listenerWantsContent);
693
} else {
694
aListener->CanHandleContent(mContentType.get(), false,
695
getter_Copies(typeToUse),
696
&listenerWantsContent);
697
}
698
if (!listenerWantsContent) {
699
LOG((" Listener is not interested"));
700
return false;
701
}
702
703
if (!typeToUse.IsEmpty() && typeToUse != mContentType) {
704
// Need to do a conversion here.
705
706
nsresult rv = ConvertData(aChannel, aListener, mContentType, typeToUse);
707
708
if (NS_FAILED(rv)) {
709
// No conversion path -- we don't want this listener, if we got one
710
m_targetStreamListener = nullptr;
711
}
712
713
LOG((" Found conversion: %s", m_targetStreamListener ? "yes" : "no"));
714
715
// m_targetStreamListener is now the input end of the converter, and we can
716
// just pump the data in there, if it exists. If it does not, we need to
717
// try other nsIURIContentListeners.
718
return m_targetStreamListener != nullptr;
719
}
720
721
// At this point, aListener wants data of type mContentType. Let 'em have
722
// it. But first, if we are retargeting, set an appropriate flag on the
723
// channel
724
nsLoadFlags loadFlags = 0;
725
aChannel->GetLoadFlags(&loadFlags);
726
727
// Set this flag to indicate that the channel has been targeted at a final
728
// consumer. This load flag is tested in nsDocLoader::OnProgress.
729
nsLoadFlags newLoadFlags = nsIChannel::LOAD_TARGETED;
730
731
nsCOMPtr<nsIURIContentListener> originalListener =
732
do_GetInterface(m_originalContext);
733
if (originalListener != aListener) {
734
newLoadFlags |= nsIChannel::LOAD_RETARGETED_DOCUMENT_URI;
735
}
736
aChannel->SetLoadFlags(loadFlags | newLoadFlags);
737
738
bool abort = false;
739
bool isPreferred = (mFlags & nsIURILoader::IS_CONTENT_PREFERRED) != 0;
740
nsresult rv =
741
aListener->DoContent(mContentType, isPreferred, aChannel,
742
getter_AddRefs(m_targetStreamListener), &abort);
743
744
if (NS_FAILED(rv)) {
745
LOG_ERROR((" DoContent failed"));
746
747
// Unset the RETARGETED_DOCUMENT_URI flag if we set it...
748
aChannel->SetLoadFlags(loadFlags);
749
m_targetStreamListener = nullptr;
750
return false;
751
}
752
753
if (abort) {
754
// Nothing else to do here -- aListener is handling it all. Make
755
// sure m_targetStreamListener is null so we don't do anything
756
// after this point.
757
LOG((" Listener has aborted the load"));
758
m_targetStreamListener = nullptr;
759
}
760
761
NS_ASSERTION(abort || m_targetStreamListener,
762
"DoContent returned no listener?");
763
764
// aListener is handling the load from this point on.
765
return true;
766
}
767
768
///////////////////////////////////////////////////////////////////////////////////////////////
769
// Implementation of nsURILoader
770
///////////////////////////////////////////////////////////////////////////////////////////////
771
772
nsURILoader::nsURILoader() {}
773
774
nsURILoader::~nsURILoader() {}
775
776
NS_IMPL_ADDREF(nsURILoader)
777
NS_IMPL_RELEASE(nsURILoader)
778
779
NS_INTERFACE_MAP_BEGIN(nsURILoader)
780
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIURILoader)
781
NS_INTERFACE_MAP_ENTRY(nsIURILoader)
782
NS_INTERFACE_MAP_END
783
784
NS_IMETHODIMP nsURILoader::RegisterContentListener(
785
nsIURIContentListener* aContentListener) {
786
nsresult rv = NS_OK;
787
788
nsWeakPtr weakListener = do_GetWeakReference(aContentListener);
789
NS_ASSERTION(weakListener,
790
"your URIContentListener must support weak refs!\n");
791
792
if (weakListener) m_listeners.AppendObject(weakListener);
793
794
return rv;
795
}
796
797
NS_IMETHODIMP nsURILoader::UnRegisterContentListener(
798
nsIURIContentListener* aContentListener) {
799
nsWeakPtr weakListener = do_GetWeakReference(aContentListener);
800
if (weakListener) m_listeners.RemoveObject(weakListener);
801
802
return NS_OK;
803
}
804
805
NS_IMETHODIMP nsURILoader::OpenURI(nsIChannel* channel, uint32_t aFlags,
806
nsIInterfaceRequestor* aWindowContext) {
807
NS_ENSURE_ARG_POINTER(channel);
808
809
if (LOG_ENABLED()) {
810
nsCOMPtr<nsIURI> uri;
811
channel->GetURI(getter_AddRefs(uri));
812
nsAutoCString spec;
813
uri->GetAsciiSpec(spec);
814
LOG(("nsURILoader::OpenURI for %s", spec.get()));
815
}
816
817
nsCOMPtr<nsIStreamListener> loader;
818
nsresult rv = OpenChannel(channel, aFlags, aWindowContext, false,
819
getter_AddRefs(loader));
820
821
if (NS_SUCCEEDED(rv)) {
822
if (aFlags & nsIURILoader::REDIRECTED_CHANNEL) {
823
// Our channel was redirected from another process, so doesn't need to be
824
// opened again. However, it does need its listener hooked up correctly.
825
nsCOMPtr<nsIChildChannel> childChannel = do_QueryInterface(channel);
826
MOZ_ASSERT(childChannel);
827
if (!childChannel) {
828
return NS_ERROR_UNEXPECTED;
829
}
830
831
return childChannel->CompleteRedirectSetup(loader, nullptr);
832
}
833
834
// this method is not complete!!! Eventually, we should first go
835
// to the content listener and ask them for a protocol handler...
836
// if they don't give us one, we need to go to the registry and get
837
// the preferred protocol handler.
838
839
// But for now, I'm going to let necko do the work for us....
840
rv = channel->AsyncOpen(loader);
841
842
// no content from this load - that's OK.
843
if (rv == NS_ERROR_NO_CONTENT) {
844
LOG((" rv is NS_ERROR_NO_CONTENT -- doing nothing"));
845
rv = NS_OK;
846
}
847
} else if (rv == NS_ERROR_WONT_HANDLE_CONTENT) {
848
// Not really an error, from this method's point of view
849
rv = NS_OK;
850
}
851
return rv;
852
}
853
854
nsresult nsURILoader::OpenChannel(nsIChannel* channel, uint32_t aFlags,
855
nsIInterfaceRequestor* aWindowContext,
856
bool aChannelIsOpen,
857
nsIStreamListener** aListener) {
858
NS_ASSERTION(channel, "Trying to open a null channel!");
859
NS_ASSERTION(aWindowContext, "Window context must not be null");
860
861
if (LOG_ENABLED()) {
862
nsCOMPtr<nsIURI> uri;
863
channel->GetURI(getter_AddRefs(uri));
864
nsAutoCString spec;
865
uri->GetAsciiSpec(spec);
866
LOG(("nsURILoader::OpenChannel for %s", spec.get()));
867
}
868
869
// Let the window context's uriListener know that the open is starting. This
870
// gives that window a chance to abort the load process.
871
nsCOMPtr<nsIURIContentListener> winContextListener(
872
do_GetInterface(aWindowContext));
873
if (winContextListener) {
874
nsCOMPtr<nsIURI> uri;
875
channel->GetURI(getter_AddRefs(uri));
876
if (uri) {
877
bool doAbort = false;
878
winContextListener->OnStartURIOpen(uri, &doAbort);
879
880
if (doAbort) {
881
LOG((" OnStartURIOpen aborted load"));
882
return NS_ERROR_WONT_HANDLE_CONTENT;
883
}
884
}
885
}
886
887
static bool once = InitPreferences();
888
mozilla::Unused << once;
889
890
// we need to create a DocumentOpenInfo object which will go ahead and open
891
// the url and discover the content type....
892
RefPtr<nsDocumentOpenInfo> loader =
893
new nsDocumentOpenInfo(aWindowContext, aFlags, this);
894
895
// Set the correct loadgroup on the channel
896
nsCOMPtr<nsILoadGroup> loadGroup(do_GetInterface(aWindowContext));
897
898
if (!loadGroup) {
899
// XXXbz This context is violating what we'd like to be the new uriloader
900
// api.... Set up a nsDocLoader to handle the loadgroup for this context.
901
// This really needs to go away!
902
nsCOMPtr<nsIURIContentListener> listener(do_GetInterface(aWindowContext));
903
if (listener) {
904
nsCOMPtr<nsISupports> cookie;
905
listener->GetLoadCookie(getter_AddRefs(cookie));
906
if (!cookie) {
907
RefPtr<nsDocLoader> newDocLoader = new nsDocLoader();
908
nsresult rv = newDocLoader->Init();
909
if (NS_FAILED(rv)) return rv;
910
rv = nsDocLoader::AddDocLoaderAsChildOfRoot(newDocLoader);
911
if (NS_FAILED(rv)) return rv;
912
cookie = nsDocLoader::GetAsSupports(newDocLoader);
913
listener->SetLoadCookie(cookie);
914
}
915
loadGroup = do_GetInterface(cookie);
916
}
917
}
918
919
// If the channel is pending, then we need to remove it from its current
920
// loadgroup
921
nsCOMPtr<nsILoadGroup> oldGroup;
922
channel->GetLoadGroup(getter_AddRefs(oldGroup));
923
if (aChannelIsOpen && !SameCOMIdentity(oldGroup, loadGroup)) {
924
// It is important to add the channel to the new group before
925
// removing it from the old one, so that the load isn't considered
926
// done as soon as the request is removed.
927
loadGroup->AddRequest(channel, nullptr);
928
929
if (oldGroup) {
930
oldGroup->RemoveRequest(channel, nullptr, NS_BINDING_RETARGETED);
931
}
932
}
933
934
channel->SetLoadGroup(loadGroup);
935
936
// prepare the loader for receiving data
937
nsresult rv = loader->Prepare();
938
if (NS_SUCCEEDED(rv)) NS_ADDREF(*aListener = loader);
939
return rv;
940
}
941
942
NS_IMETHODIMP nsURILoader::OpenChannel(nsIChannel* channel, uint32_t aFlags,
943
nsIInterfaceRequestor* aWindowContext,
944
nsIStreamListener** aListener) {
945
bool pending;
946
if (NS_FAILED(channel->IsPending(&pending))) {
947
pending = false;
948
}
949
950
return OpenChannel(channel, aFlags, aWindowContext, pending, aListener);
951
}
952
953
NS_IMETHODIMP nsURILoader::Stop(nsISupports* aLoadCookie) {
954
nsresult rv;
955
nsCOMPtr<nsIDocumentLoader> docLoader;
956
957
NS_ENSURE_ARG_POINTER(aLoadCookie);
958
959
docLoader = do_GetInterface(aLoadCookie, &rv);
960
if (docLoader) {
961
rv = docLoader->Stop();
962
}
963
return rv;
964
}