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